/******************************************************************************* Copyright (c) 2022, Rockchip Corporation. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include UNI_NOR_FLASH_PROTOCOL *SpiFlashProtocol; CONST CHAR16 gShellSpiFlashFileName[] = L"ShellCommand"; EFI_HANDLE gShellSfHiiHandle = NULL; STATIC CONST SHELL_PARAM_ITEM ParamList[] = { {L"read", TypeFlag}, {L"readfile", TypeFlag}, {L"write", TypeFlag}, {L"writefile", TypeFlag}, {L"erase", TypeFlag}, {L"update", TypeFlag}, {L"updatefile", TypeFlag}, {L"help", TypeFlag}, {NULL , TypeMax} }; typedef enum { READ = 2, READ_FILE = 4, WRITE = 8, WRITE_FILE = 16, ERASE = 32, UPDATE = 64, UPDATE_FILE = 128, } Flags; /** Return the file name of the help text file if not using HII. @return The string pointer to the file name. **/ CONST CHAR16* EFIAPI ShellCommandGetManFileNameSpiFlash ( VOID ) { return gShellSpiFlashFileName; } VOID SfUsage ( VOID ) { Print (L"\nBasic SPI command\n" "sf [read | readfile | write | writefile | erase |" "update | updatefile]" "[
| ] \n\n" "Address - Address in RAM to store/load data\n" "FilePath - Path to file to read/write data from/to\n" "Offset - Offset from beginning of SPI flash to store/load data\n" "Length - Number of bytes to send\n" "Examples:\n" "Check if there is response from SPI flash\n" "Read 32 bytes from 0xe00000 of SPI flash into RAM at address 0x100000\n" " sf read 0x100000 0xe00000 32\n" "Read 0x20 bytes from 0x200000 of SPI flash into RAM at address 0x300000\n" " sf read 0x300000 0x200000 0x20\n" "Erase 0x10000 bytes from offset 0x100000 of SPI flash\n" " sf erase 0x100000 0x100000\n" "Write 16 bytes from 0x200000 at RAM into SPI flash at address 0x4000000\n" " sf write 0x200000 0x4000000 16\n" "Update 100 bytes from 0x100000 at RAM in SPI flash at address 0xe00000\n" " sf update 0x100000 0xe00000 100\n" "Read 0x3000 bytes from 0x0 of SPI flash into file fs2:file.bin\n" " sf readfile fs2:file.bin 0x0 0x3000 \n" "Update data in SPI flash at 0x3000000 from file Linux.efi\n" " sf updatefile Linux.efi 0x3000000\n" ); } STATIC EFI_STATUS OpenAndPrepareFile ( IN CHAR16 *FilePath, SHELL_FILE_HANDLE *FileHandle ) { EFI_STATUS Status; UINT64 OpenMode; OpenMode = EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE; Status = ShellOpenFileByName (FilePath, FileHandle, OpenMode, 0); if (EFI_ERROR (Status)) { Print (L"sf: Cannot open file\n"); return Status; } Status = FileHandleSetPosition(*FileHandle, 0); if (EFI_ERROR(Status)) { Print (L"sf: Cannot set file position to first byte\n"); ShellCloseFile (FileHandle); return Status; } return EFI_SUCCESS; } SHELL_STATUS EFIAPI ShellCommandRunSpiFlash ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; LIST_ENTRY *CheckPackage; EFI_PHYSICAL_ADDRESS Address = 0, Offset = 0; SHELL_FILE_HANDLE FileHandle = NULL; UINTN ByteCount, I; UINT64 FileSize; UINT8 *Buffer = NULL, *FileBuffer = NULL; CHAR16 *ProblemParam, *FilePath; CONST CHAR16 *AddressStr = NULL, *OffsetStr = NULL; CONST CHAR16 *LengthStr = NULL, *FileStr = NULL; BOOLEAN AddrFlag = FALSE, LengthFlag = TRUE, FileFlag = FALSE; UINT8 Flag = 0, CheckFlag = 0; Status = gBS->LocateProtocol ( &gUniNorFlashProtocolGuid, NULL, (VOID **)&SpiFlashProtocol ); if (EFI_ERROR(Status)) { Print (L"sf: Cannot locate SpiFlash protocol\n"); return SHELL_ABORTED; } // Parse Shell command line Status = ShellInitialize (); if (EFI_ERROR (Status)) { Print (L"sf: Cannot initialize Shell\n"); ASSERT_EFI_ERROR (Status); return SHELL_ABORTED; } Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE); if (EFI_ERROR (Status)) { Print (L"sf: Error while parsing command line\n"); return SHELL_ABORTED; } if (ShellCommandLineGetFlag (CheckPackage, L"help")) { SfUsage(); return EFI_SUCCESS; } // Check flags provided by user Flag |= (ShellCommandLineGetFlag (CheckPackage, L"read") << 1); Flag |= (ShellCommandLineGetFlag (CheckPackage, L"readfile") << 2); Flag |= (ShellCommandLineGetFlag (CheckPackage, L"write") << 3); Flag |= (ShellCommandLineGetFlag (CheckPackage, L"writefile") << 4); Flag |= (ShellCommandLineGetFlag (CheckPackage, L"erase") << 5); Flag |= (ShellCommandLineGetFlag (CheckPackage, L"update") << 6); Flag |= (ShellCommandLineGetFlag (CheckPackage, L"updatefile") << 7); CheckFlag = Flag; for (I = 0; CheckFlag; CheckFlag >>= 1) { I += CheckFlag & 1; if (I > 1) { Print (L"sf: Too many flags\n"); SfUsage(); return SHELL_ABORTED; } } switch (Flag) { case READ: case WRITE: case UPDATE: AddressStr = ShellCommandLineGetRawValue (CheckPackage, 1); OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 2); LengthStr = ShellCommandLineGetRawValue (CheckPackage, 3); AddrFlag = TRUE; break; case ERASE: OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 1); LengthStr = ShellCommandLineGetRawValue (CheckPackage, 2); break; case READ_FILE: FileStr = ShellCommandLineGetRawValue (CheckPackage, 1); OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 2); LengthStr = ShellCommandLineGetRawValue (CheckPackage, 3); FileFlag = TRUE; break; case WRITE_FILE: case UPDATE_FILE: FileStr = ShellCommandLineGetRawValue (CheckPackage, 1); OffsetStr = ShellCommandLineGetRawValue (CheckPackage, 2); LengthFlag = FALSE; FileFlag = TRUE; break; } // Read address parameter if ((AddressStr == NULL) & AddrFlag) { Print (L"sf: No address parameter!\n"); return SHELL_ABORTED; } else if (AddrFlag) { Address = ShellHexStrToUintn (AddressStr); if (Address == (UINTN)(-1)) { Print (L"sf: Wrong address parameter\n"); return SHELL_ABORTED; } } // Read offset parameter if (OffsetStr == NULL) { Print (L"sf: No offset Parameter!\n"); return SHELL_ABORTED; } else { Offset = ShellHexStrToUintn (OffsetStr); if (Offset < 0) { Print (L"sf: Wrong offset parameter: %s", OffsetStr); return SHELL_ABORTED; } } // Read length parameter if ((LengthStr == NULL) & LengthFlag) { Print (L"sf: No lenght parameter!\n"); return SHELL_ABORTED; } else if (LengthFlag) { ByteCount = ShellStrToUintn (LengthStr); if (ByteCount < 0) { Print (L"sf: Wrong length parameter %s!\n", LengthStr); return SHELL_ABORTED; } } if (FileFlag) { // Read FilePath parameter if (FileStr == NULL) { Print (L"sf: No FilePath parameter!\n"); return SHELL_ABORTED; } else { FilePath = (CHAR16 *) FileStr; Status = ShellIsFile (FilePath); // When read file into flash, file doesn't have to exist if (EFI_ERROR (Status) && !(Flag & READ_FILE)) { Print (L"sf: Wrong FilePath parameter!\n"); return SHELL_ABORTED; } } Status = OpenAndPrepareFile (FilePath, &FileHandle); if (EFI_ERROR(Status)) { Print (L"sf: Error while preparing file\n"); return SHELL_ABORTED; } // Get file size in order to check correctness at the end of transfer if (Flag & (WRITE_FILE | UPDATE_FILE)) { Status = FileHandleGetSize (FileHandle, &FileSize); if (EFI_ERROR (Status)) { Print (L"sf: Cannot get file size\n"); } ByteCount = (UINTN) FileSize; } FileBuffer = AllocateZeroPool ((UINTN) ByteCount); if (FileBuffer == NULL) { Print (L"sf: Cannot allocate memory\n"); goto Error_Close_File; } // Read file content and store it in FileBuffer if (Flag & (WRITE_FILE | UPDATE_FILE)) { Status = FileHandleRead (FileHandle, &ByteCount, FileBuffer); if (EFI_ERROR (Status)) { Print (L"sf: Read from file error\n"); goto Error_Free_Buffer; } else if (ByteCount != (UINTN) FileSize) { Print (L"sf: Not whole file read. Abort\n"); goto Error_Free_Buffer; } } } Buffer = (UINT8 *)(UINTN)Address; if (FileFlag) { Buffer = FileBuffer; } switch (Flag) { case READ: case READ_FILE: Status = SpiFlashProtocol->Read (SpiFlashProtocol, Offset, Buffer, ByteCount); break; case ERASE: Status = SpiFlashProtocol->Erase (SpiFlashProtocol, Offset, ByteCount); break; case WRITE: case WRITE_FILE: Status = SpiFlashProtocol->Write (SpiFlashProtocol, Offset, Buffer, ByteCount); break; case UPDATE: case UPDATE_FILE: Status = SpiFlashProtocol->Update (SpiFlashProtocol, Offset, Buffer, ByteCount); break; } if (EFI_ERROR (Status)) { Print (L"sf: Error while performing spi transfer\n"); return SHELL_ABORTED; } switch (Flag) { case ERASE: Print (L"sf: %d bytes succesfully erased at offset 0x%x\n", ByteCount, Offset); break; case WRITE: case WRITE_FILE: Print (L"sf: Write %d bytes at offset 0x%x\n", ByteCount, Offset); break; case UPDATE: case UPDATE_FILE: Print (L"sf: Update %d bytes at offset 0x%x\n", ByteCount, Offset); break; case READ: Print (L"sf: Read %d bytes from offset 0x%x\n", ByteCount, Offset); break; case READ_FILE: Status = FileHandleWrite (FileHandle, &ByteCount, FileBuffer); if (EFI_ERROR(Status)) { Print (L"sf: Error while writing into file\n"); goto Error_Free_Buffer; } break; } if (FileFlag) { FreePool (FileBuffer); if (FileHandle != NULL) { ShellCloseFile (&FileHandle); } } return EFI_SUCCESS; Error_Free_Buffer: FreePool (FileBuffer); Error_Close_File: ShellCloseFile (&FileHandle); return SHELL_ABORTED; } EFI_STATUS EFIAPI ShellSpiFlashLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { gShellSfHiiHandle = NULL; gShellSfHiiHandle = HiiAddPackages ( &gShellSfHiiGuid, gImageHandle, UefiShellSpiFlashLibStrings, NULL ); if (gShellSfHiiHandle == NULL) { return EFI_DEVICE_ERROR; } ShellCommandRegisterCommandName ( L"sf", ShellCommandRunSpiFlash, ShellCommandGetManFileNameSpiFlash, 0, L"sf", TRUE , gShellSfHiiHandle, STRING_TOKEN (STR_GET_HELP_SF) ); return EFI_SUCCESS; } EFI_STATUS EFIAPI ShellSpiFlashLibDestructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { if (gShellSfHiiHandle != NULL) { HiiRemovePackages (gShellSfHiiHandle); } return EFI_SUCCESS; }