1 /*******************************************************************************
2 Copyright (C) 2016 Marvell International Ltd.
3 
4 Marvell BSD License Option
5 
6 If you received this File from Marvell, you may opt to use, redistribute and/or
7 modify this File under the following licensing terms.
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10 
11 * Redistributions of source code must retain the above copyright notice,
12   this list of conditions and the following disclaimer.
13 
14 * Redistributions in binary form must reproduce the above copyright
15   notice, this list of conditions and the following disclaimer in the
16   documentation and/or other materials provided with the distribution.
17 
18 * Neither the name of Marvell nor the names of its contributors may be
19   used to endorse or promote products derived from this software without
20   specific prior written permission.
21 
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 *******************************************************************************/
34 #include <Uefi.h>
35 #include <ShellBase.h>
36 
37 #include <Library/BaseLib.h>
38 #include <Library/BaseMemoryLib.h>
39 #include <Library/DebugLib.h>
40 #include <Library/MemoryAllocationLib.h>
41 #include <Library/ShellCommandLib.h>
42 #include <Library/ShellLib.h>
43 #include <Library/UefiLib.h>
44 #include <Library/UefiBootServicesTableLib.h>
45 #include <Library/PrintLib.h>
46 #include <Library/ShellCEntryLib.h>
47 #include <Library/HiiLib.h>
48 #include <Library/FileHandleLib.h>
49 
50 #include <Protocol/Spi.h>
51 #include <Protocol/SpiFlash.h>
52 
53 MARVELL_SPI_FLASH_PROTOCOL *SpiFlashProtocol;
54 MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol;
55 
56 CONST CHAR16 gShellSpiFlashFileName[] = L"ShellCommand";
57 EFI_HANDLE gShellSfHiiHandle = NULL;
58 
59 BOOLEAN InitFlag = 1;
60 
61 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
62   {L"read", TypeFlag},
63   {L"readfile", TypeFlag},
64   {L"write", TypeFlag},
65   {L"writefile", TypeFlag},
66   {L"erase", TypeFlag},
67   {L"update", TypeFlag},
68   {L"updatefile", TypeFlag},
69   {L"probe", TypeFlag},
70   {L"help", TypeFlag},
71   {NULL , TypeMax}
72   };
73 
74 typedef enum {
75   PROBE       = 1,
76   READ        = 2,
77   READ_FILE   = 4,
78   WRITE       = 8,
79   WRITE_FILE  = 16,
80   ERASE       = 32,
81   UPDATE      = 64,
82   UPDATE_FILE = 128,
83 } Flags;
84 
85 /**
86   Return the file name of the help text file if not using HII.
87 
88   @return The string pointer to the file name.
89 **/
90 CONST CHAR16*
91 EFIAPI
ShellCommandGetManFileNameSpiFlash(VOID)92 ShellCommandGetManFileNameSpiFlash (
93   VOID
94   )
95 {
96 
97   return gShellSpiFlashFileName;
98 }
99 
100 VOID
SfUsage(VOID)101 SfUsage (
102   VOID
103   )
104 {
105   Print (L"\nBasic SPI command\n"
106          "sf [probe | read | readfile | write | writefile | erase |"
107          "update | updatefile]"
108          "[<Address> | <FilePath>] <Offset> <Length>\n\n"
109          "Length   - Number of bytes to send\n"
110          "Address  - Address in RAM to store/load data\n"
111          "FilePath - Path to file to read/write data from/to\n"
112          "Offset   - Offset from beggining of SPI flash to store/load data\n"
113          "Examples:\n"
114          "Check if there is response from SPI flash\n"
115          "  sf probe\n"
116          "Read 32 bytes from 0xe00000 of SPI flash into RAM at address 0x100000\n"
117          "  sf read 0x100000 0xe00000 32\n"
118          "Read 0x20 bytes from 0x200000 of SPI flash into RAM at address 0x300000\n"
119          "  sf read 0x300000 0x200000 0x20\n"
120          "Erase 0x10000 bytes from offset 0x100000 of SPI flash\n"
121          "  sf erase 0x100000 0x100000\n"
122          "Write 16 bytes from 0x200000 at RAM into SPI flash at address 0x4000000\n"
123          "  sf write 0x200000 0x4000000 16\n"
124          "Update 100 bytes from 0x100000 at RAM in SPI flash at address 0xe00000\n"
125          "  sf update 0x100000 0xe00000 100\n"
126          "Read 0x3000 bytes from 0x0 of SPI flash into file fs2:file.bin\n"
127          "  sf readfile fs2:file.bin 0x0 0x3000 \n"
128          "Update data in SPI flash at 0x3000000 from file Linux.efi\n"
129          "  sf updatefile Linux.efi 0x3000000\n"
130   );
131 }
132 
133 STATIC
134 EFI_STATUS
OpenAndPrepareFile(IN CHAR16 * FilePath,SHELL_FILE_HANDLE * FileHandle)135 OpenAndPrepareFile (
136   IN CHAR16 *FilePath,
137   SHELL_FILE_HANDLE *FileHandle
138   )
139 {
140   EFI_STATUS Status;
141   UINT64 OpenMode;
142 
143   OpenMode = EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE;
144 
145   Status = ShellOpenFileByName (FilePath, FileHandle, OpenMode, 0);
146   if (EFI_ERROR (Status)) {
147     Print (L"sf: Cannot open file\n");
148     return Status;
149   }
150 
151   Status = FileHandleSetPosition(*FileHandle, 0);
152 
153   if (EFI_ERROR(Status)) {
154     Print (L"sf: Cannot set file position to first byte\n");
155     ShellCloseFile (FileHandle);
156     return Status;
157   }
158 
159   return EFI_SUCCESS;
160 }
161 
162 STATIC
163 EFI_STATUS
FlashProbe(IN SPI_DEVICE * Slave)164 FlashProbe (
165   IN SPI_DEVICE       *Slave
166   )
167 {
168   EFI_STATUS Status;
169   UINT8  IdBuffer[4];
170   UINT32 Id, RefId;
171 
172   Id = PcdGet32 (PcdSpiFlashId);
173 
174   IdBuffer[0] = CMD_READ_ID;
175 
176   SpiFlashProtocol->ReadId (
177     Slave,
178     4,
179     IdBuffer
180     );
181 
182   RefId = (IdBuffer[0] << 16) + (IdBuffer[1] << 8) + IdBuffer[2];
183 
184   if (RefId == Id) {
185     Print (L"sf: Detected supported SPI flash with ID=%3x\n", RefId);
186     Status = SpiFlashProtocol->Init (SpiFlashProtocol, Slave);
187     if (EFI_ERROR(Status)) {
188       Print (L"sf: Cannot initialize flash device\n");
189       return SHELL_ABORTED;
190     }
191     InitFlag = 0;
192     return EFI_SUCCESS;
193   } else if (RefId != 0) {
194     Print (L"sf: Unsupported SPI flash detected with ID=%2x\n", RefId);
195     return SHELL_ABORTED;
196   }
197 
198   Print (L"sf: No SPI flash detected");
199   return SHELL_ABORTED;
200 }
201 
202 SHELL_STATUS
203 EFIAPI
ShellCommandRunSpiFlash(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)204 ShellCommandRunSpiFlash (
205   IN EFI_HANDLE        ImageHandle,
206   IN EFI_SYSTEM_TABLE  *SystemTable
207   )
208 {
209 EFI_STATUS              Status;
210   SPI_DEVICE            *Slave;
211   LIST_ENTRY            *CheckPackage;
212   EFI_PHYSICAL_ADDRESS  Address = 0, Offset = 0;
213   SHELL_FILE_HANDLE     FileHandle = NULL;
214   UINTN                 ByteCount, FileSize, I;
215   UINT8                 *Buffer = NULL, *FileBuffer = NULL;
216   CHAR16                *ProblemParam, *FilePath;
217   CONST CHAR16          *AddressStr = NULL, *OffsetStr = NULL;
218   CONST CHAR16          *LengthStr = NULL, *FileStr = NULL;
219   BOOLEAN               AddrFlag = FALSE, LengthFlag = TRUE, FileFlag = FALSE;
220   UINT8                 Flag = 0, CheckFlag = 0;
221 
222   Status = gBS->LocateProtocol (
223     &gMarvellSpiFlashProtocolGuid,
224     NULL,
225     (VOID **)&SpiFlashProtocol
226   );
227   if (EFI_ERROR(Status)) {
228     Print (L"sf: Cannot locate SpiFlash protocol\n");
229     return SHELL_ABORTED;
230   }
231 
232   Status = gBS->LocateProtocol (
233     &gMarvellSpiMasterProtocolGuid,
234     NULL,
235     (VOID **)&SpiMasterProtocol
236   );
237   if (EFI_ERROR(Status)) {
238     Print (L"sf: Cannot locate SpiMaster protocol\n");
239     return SHELL_ABORTED;
240   }
241 
242   // Parse Shell command line
243   Status = ShellInitialize ();
244   if (EFI_ERROR (Status)) {
245     Print (L"sf: Cannot initialize Shell\n");
246     ASSERT_EFI_ERROR (Status);
247     return SHELL_ABORTED;
248   }
249 
250   Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE);
251   if (EFI_ERROR (Status)) {
252     Print (L"sf: Error while parsing command line\n");
253     return SHELL_ABORTED;
254   }
255 
256   if (ShellCommandLineGetFlag (CheckPackage, L"help")) {
257     SfUsage();
258     return EFI_SUCCESS;
259   }
260 
261   // Check flags provided by user
262   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"probe") << 0);
263   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"read") << 1);
264   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"readfile") << 2);
265   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"write") << 3);
266   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"writefile") << 4);
267   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"erase") << 5);
268   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"update") << 6);
269   Flag |= (ShellCommandLineGetFlag (CheckPackage, L"updatefile") << 7);
270 
271   if (InitFlag && !(Flag & PROBE)) {
272     Print (L"Please run sf probe\n");
273     return EFI_SUCCESS;
274   }
275 
276   CheckFlag = Flag;
277   for (I = 0; CheckFlag; CheckFlag >>= 1) {
278     I += CheckFlag & 1;
279     if (I > 1) {
280       Print (L"sf: Too many flags\n");
281       SfUsage();
282       return SHELL_ABORTED;
283     }
284   }
285 
286   // Setup new spi device
287   Slave = SpiMasterProtocol->SetupDevice (SpiMasterProtocol, 0, 0);
288     if (Slave == NULL) {
289       Print(L"sf: Cannot allocate SPI device!\n");
290       return SHELL_ABORTED;
291     }
292 
293   switch (Flag) {
294   case PROBE:
295     // Probe spi bus
296     Status = FlashProbe (Slave);
297     if (EFI_ERROR(Status)) {
298       // No supported spi flash detected
299       return SHELL_ABORTED;
300     } else {
301       return Status;
302     }
303     break;
304   // Fall through
305   case READ:
306   case WRITE:
307   case UPDATE:
308     AddressStr = ShellCommandLineGetRawValue (CheckPackage, 1);
309     OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 2);
310     LengthStr = ShellCommandLineGetRawValue (CheckPackage, 3);
311     AddrFlag = TRUE;
312     break;
313   case ERASE:
314     OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 1);
315     LengthStr = ShellCommandLineGetRawValue (CheckPackage, 2);
316     break;
317   case READ_FILE:
318     FileStr = ShellCommandLineGetRawValue (CheckPackage, 1);
319     OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 2);
320     LengthStr = ShellCommandLineGetRawValue (CheckPackage, 3);
321     FileFlag = TRUE;
322     break;
323   case WRITE_FILE:
324   case UPDATE_FILE:
325     FileStr = ShellCommandLineGetRawValue (CheckPackage, 1);
326     OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 2);
327     LengthFlag = FALSE;
328     FileFlag = TRUE;
329     break;
330   }
331 
332   // Read address parameter
333   if ((AddressStr == NULL) & AddrFlag) {
334     Print (L"sf: No address parameter!\n");
335     return SHELL_ABORTED;
336   } else if (AddrFlag) {
337     Address = ShellHexStrToUintn (AddressStr);
338     if (Address == (UINTN)(-1)) {
339       Print (L"sf: Wrong address parameter\n");
340       return SHELL_ABORTED;
341     }
342   }
343 
344   // Read offset parameter
345   if (OffsetStr == NULL) {
346     Print (L"sf: No offset Parameter!\n");
347     return SHELL_ABORTED;
348   } else {
349     Offset = ShellHexStrToUintn (OffsetStr);
350     if (Offset < 0) {
351       Print (L"sf: Wrong offset parameter: %s", OffsetStr);
352       return SHELL_ABORTED;
353     }
354   }
355 
356   // Read length parameter
357   if ((LengthStr == NULL) & LengthFlag) {
358     Print (L"sf: No lenght parameter!\n");
359     return SHELL_ABORTED;
360   } else if (LengthFlag) {
361     ByteCount = ShellStrToUintn (LengthStr);
362     if (ByteCount < 0) {
363       Print (L"sf: Wrong length parameter %s!\n", LengthStr);
364       return SHELL_ABORTED;
365     }
366   }
367 
368   if (FileFlag) {
369     // Read FilePath parameter
370     if (FileStr == NULL) {
371       Print (L"sf: No FilePath parameter!\n");
372       return SHELL_ABORTED;
373     } else {
374       FilePath = (CHAR16 *) FileStr;
375       Status = ShellIsFile (FilePath);
376       // When read file into flash, file doesn't have to exist
377       if (EFI_ERROR(Status && !(Flag & READ_FILE))) {
378         Print (L"sf: Wrong FilePath parameter!\n");
379         return SHELL_ABORTED;
380       }
381     }
382 
383     Status = OpenAndPrepareFile (FilePath, &FileHandle);
384     if (EFI_ERROR(Status)) {
385       Print (L"sf: Error while preparing file\n");
386       return SHELL_ABORTED;
387     }
388 
389     // Get file size in order to check correctness at the end of transfer
390     if (Flag & (WRITE_FILE | UPDATE_FILE)) {
391       Status = FileHandleGetSize (FileHandle, &FileSize);
392       if (EFI_ERROR (Status)) {
393         Print (L"sf: Cannot get file size\n");
394       }
395       ByteCount = (UINTN) FileSize;
396     }
397 
398     FileBuffer = AllocateZeroPool ((UINTN) ByteCount);
399     if (FileBuffer == NULL) {
400       Print (L"sf: Cannot allocate memory\n");
401       goto Error_Close_File;
402     }
403 
404     // Read file content and store it in FileBuffer
405     if (Flag & (WRITE_FILE | UPDATE_FILE)) {
406       Status = FileHandleRead (FileHandle, &ByteCount, FileBuffer);
407       if (EFI_ERROR (Status)) {
408         Print (L"sf: Read from file error\n");
409         goto Error_Free_Buffer;
410       } else if (ByteCount != (UINTN) FileSize) {
411         Print (L"sf: Not whole file read. Abort\n");
412         goto Error_Free_Buffer;
413       }
414     }
415   }
416 
417   Buffer = (UINT8 *) Address;
418   if (FileFlag) {
419     Buffer = FileBuffer;
420   }
421 
422   switch (Flag) {
423   case READ:
424   case READ_FILE:
425     Status = SpiFlashProtocol->Read (Slave, Offset, ByteCount, Buffer);
426     break;
427   case ERASE:
428     Status = SpiFlashProtocol->Erase (Slave, Offset, ByteCount);
429     break;
430   case WRITE:
431   case WRITE_FILE:
432     Status = SpiFlashProtocol->Write (Slave, Offset, ByteCount, Buffer);
433     break;
434   case UPDATE:
435   case UPDATE_FILE:
436     Status = SpiFlashProtocol->Update (Slave, Offset, ByteCount, Buffer);
437     break;
438   }
439 
440   SpiMasterProtocol->FreeDevice(Slave);
441 
442   if (EFI_ERROR (Status)) {
443     Print (L"sf: Error while performing spi transfer\n");
444     return SHELL_ABORTED;
445   }
446 
447   switch (Flag) {
448   case ERASE:
449     Print (L"sf: %d bytes succesfully erased at offset 0x%x\n", ByteCount,
450       Offset);
451     break;
452   case WRITE:
453   case WRITE_FILE:
454     Print (L"sf: Write %d bytes at offset 0x%x\n", ByteCount, Offset);
455     break;
456   case UPDATE:
457   case UPDATE_FILE:
458     Print (L"sf: Update %d bytes at offset 0x%x\n", ByteCount, Offset);
459     break;
460   case READ:
461     Print (L"sf: Read %d bytes from offset 0x%x\n", ByteCount, Offset);
462     break;
463   case READ_FILE:
464     Status = FileHandleWrite (FileHandle, &ByteCount, FileBuffer);
465     if (EFI_ERROR(Status)) {
466       Print (L"sf: Error while writing into file\n");
467       goto Error_Free_Buffer;
468     }
469     break;
470   }
471 
472   if (FileFlag) {
473     FreePool (FileBuffer);
474 
475     if (FileHandle != NULL) {
476       ShellCloseFile (&FileHandle);
477     }
478   }
479 
480   return EFI_SUCCESS;
481 
482 Error_Free_Buffer:
483   FreePool (FileBuffer);
484 Error_Close_File:
485   ShellCloseFile (&FileHandle);
486   return SHELL_ABORTED;
487 }
488 
489 EFI_STATUS
490 EFIAPI
ShellSpiFlashLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)491 ShellSpiFlashLibConstructor (
492   IN EFI_HANDLE        ImageHandle,
493   IN EFI_SYSTEM_TABLE  *SystemTable
494   )
495 {
496   gShellSfHiiHandle = NULL;
497 
498   gShellSfHiiHandle = HiiAddPackages (
499                         &gShellSfHiiGuid, gImageHandle,
500                         UefiShellSpiFlashLibStrings, NULL
501                         );
502   if (gShellSfHiiHandle == NULL) {
503     return EFI_DEVICE_ERROR;
504   }
505 
506   ShellCommandRegisterCommandName (
507      L"sf", ShellCommandRunSpiFlash, ShellCommandGetManFileNameSpiFlash, 0,
508      L"sf", TRUE , gShellSfHiiHandle, STRING_TOKEN (STR_GET_HELP_SF)
509      );
510 
511   return EFI_SUCCESS;
512 }
513 
514 EFI_STATUS
515 EFIAPI
ShellSpiFlashLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)516 ShellSpiFlashLibDestructor (
517   IN EFI_HANDLE        ImageHandle,
518   IN EFI_SYSTEM_TABLE  *SystemTable
519   )
520 {
521 
522   if (gShellSfHiiHandle != NULL) {
523     HiiRemovePackages (gShellSfHiiHandle);
524   }
525   return EFI_SUCCESS;
526 }
527