1/*******************************************************************************
2Copyright (C) 2016 Marvell International Ltd.
3
4Marvell BSD License Option
5
6If you received this File from Marvell, you may opt to use, redistribute and/or
7modify this File under the following licensing terms.
8Redistribution and use in source and binary forms, with or without modification,
9are 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
22THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29ANY 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
31SOFTWARE, 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
53MARVELL_SPI_FLASH_PROTOCOL *SpiFlashProtocol;
54MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol;
55
56CONST CHAR16 gShellSpiFlashFileName[] = L"ShellCommand";
57EFI_HANDLE gShellSfHiiHandle = NULL;
58
59BOOLEAN InitFlag = 1;
60
61STATIC 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
74typedef 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**/
90CONST CHAR16*
91EFIAPI
92ShellCommandGetManFileNameSpiFlash (
93  VOID
94  )
95{
96
97  return gShellSpiFlashFileName;
98}
99
100VOID
101SfUsage (
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
133STATIC
134EFI_STATUS
135OpenAndPrepareFile (
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
162STATIC
163EFI_STATUS
164FlashProbe (
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
202SHELL_STATUS
203EFIAPI
204ShellCommandRunSpiFlash (
205  IN EFI_HANDLE        ImageHandle,
206  IN EFI_SYSTEM_TABLE  *SystemTable
207  )
208{
209EFI_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
482Error_Free_Buffer:
483  FreePool (FileBuffer);
484Error_Close_File:
485  ShellCloseFile (&FileHandle);
486  return SHELL_ABORTED;
487}
488
489EFI_STATUS
490EFIAPI
491ShellSpiFlashLibConstructor (
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
514EFI_STATUS
515EFIAPI
516ShellSpiFlashLibDestructor (
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