/******************************************************************************* | |
Copyright (C) 2016 Marvell International Ltd. | |
Marvell BSD License Option | |
If you received this File from Marvell, you may opt to use, redistribute and/or | |
modify this File under the following licensing terms. | |
Redistribution and use in source and binary forms, with or without modification, | |
are permitted provided that the following conditions are met: | |
* Redistributions of source code must retain the above copyright notice, | |
this list of conditions and the following disclaimer. | |
* Redistributions in binary form must reproduce the above copyright | |
notice, this list of conditions and the following disclaimer in the | |
documentation and/or other materials provided with the distribution. | |
* Neither the name of Marvell nor the names of its contributors may be | |
used to endorse or promote products derived from this software without | |
specific prior written permission. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*******************************************************************************/ | |
#include <Uefi.h> | |
#include <ShellBase.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/ShellCommandLib.h> | |
#include <Library/ShellLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/PrintLib.h> | |
#include <Library/ShellCEntryLib.h> | |
#include <Library/HiiLib.h> | |
#include <Library/FileHandleLib.h> | |
#include <Protocol/Spi.h> | |
#include <Protocol/SpiFlash.h> | |
MARVELL_SPI_FLASH_PROTOCOL *SpiFlashProtocol; | |
MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol; | |
CONST CHAR16 gShellSpiFlashFileName[] = L"ShellCommand"; | |
EFI_HANDLE gShellSfHiiHandle = NULL; | |
BOOLEAN InitFlag = 1; | |
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"probe", TypeFlag}, | |
{L"help", TypeFlag}, | |
{NULL , TypeMax} | |
}; | |
typedef enum { | |
PROBE = 1, | |
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 [probe | read | readfile | write | writefile | erase |" | |
"update | updatefile]" | |
"[<Address> | <FilePath>] <Offset> <Length>\n\n" | |
"Length - Number of bytes to send\n" | |
"Address - Address in RAM to store/load data\n" | |
"FilePath - Path to file to read/write data from/to\n" | |
"Offset - Offset from beggining of SPI flash to store/load data\n" | |
"Examples:\n" | |
"Check if there is response from SPI flash\n" | |
" sf probe\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; | |
} | |
STATIC | |
EFI_STATUS | |
FlashProbe ( | |
IN SPI_DEVICE *Slave | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 IdBuffer[4]; | |
UINT32 Id, RefId; | |
Id = PcdGet32 (PcdSpiFlashId); | |
IdBuffer[0] = CMD_READ_ID; | |
SpiFlashProtocol->ReadId ( | |
Slave, | |
4, | |
IdBuffer | |
); | |
RefId = (IdBuffer[0] << 16) + (IdBuffer[1] << 8) + IdBuffer[2]; | |
if (RefId == Id) { | |
Print (L"sf: Detected supported SPI flash with ID=%3x\n", RefId); | |
Status = SpiFlashProtocol->Init (SpiFlashProtocol, Slave); | |
if (EFI_ERROR(Status)) { | |
Print (L"sf: Cannot initialize flash device\n"); | |
return SHELL_ABORTED; | |
} | |
InitFlag = 0; | |
return EFI_SUCCESS; | |
} else if (RefId != 0) { | |
Print (L"sf: Unsupported SPI flash detected with ID=%2x\n", RefId); | |
return SHELL_ABORTED; | |
} | |
Print (L"sf: No SPI flash detected"); | |
return SHELL_ABORTED; | |
} | |
SHELL_STATUS | |
EFIAPI | |
ShellCommandRunSpiFlash ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
SPI_DEVICE *Slave; | |
LIST_ENTRY *CheckPackage; | |
EFI_PHYSICAL_ADDRESS Address = 0, Offset = 0; | |
SHELL_FILE_HANDLE FileHandle = NULL; | |
UINTN ByteCount, FileSize, I; | |
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 ( | |
&gMarvellSpiFlashProtocolGuid, | |
NULL, | |
(VOID **)&SpiFlashProtocol | |
); | |
if (EFI_ERROR(Status)) { | |
Print (L"sf: Cannot locate SpiFlash protocol\n"); | |
return SHELL_ABORTED; | |
} | |
Status = gBS->LocateProtocol ( | |
&gMarvellSpiMasterProtocolGuid, | |
NULL, | |
(VOID **)&SpiMasterProtocol | |
); | |
if (EFI_ERROR(Status)) { | |
Print (L"sf: Cannot locate SpiMaster 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"probe") << 0); | |
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); | |
if (InitFlag && !(Flag & PROBE)) { | |
Print (L"Please run sf probe\n"); | |
return EFI_SUCCESS; | |
} | |
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; | |
} | |
} | |
// Setup new spi device | |
Slave = SpiMasterProtocol->SetupDevice (SpiMasterProtocol, 0, 0); | |
if (Slave == NULL) { | |
Print(L"sf: Cannot allocate SPI device!\n"); | |
return SHELL_ABORTED; | |
} | |
switch (Flag) { | |
case PROBE: | |
// Probe spi bus | |
Status = FlashProbe (Slave); | |
if (EFI_ERROR(Status)) { | |
// No supported spi flash detected | |
return SHELL_ABORTED; | |
} else { | |
return Status; | |
} | |
break; | |
// Fall through | |
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 *) Address; | |
if (FileFlag) { | |
Buffer = FileBuffer; | |
} | |
switch (Flag) { | |
case READ: | |
case READ_FILE: | |
Status = SpiFlashProtocol->Read (Slave, Offset, ByteCount, Buffer); | |
break; | |
case ERASE: | |
Status = SpiFlashProtocol->Erase (Slave, Offset, ByteCount); | |
break; | |
case WRITE: | |
case WRITE_FILE: | |
Status = SpiFlashProtocol->Write (Slave, Offset, ByteCount, Buffer); | |
break; | |
case UPDATE: | |
case UPDATE_FILE: | |
Status = SpiFlashProtocol->Update (Slave, Offset, ByteCount, Buffer); | |
break; | |
} | |
SpiMasterProtocol->FreeDevice(Slave); | |
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; | |
} |