/******************************************************************************* | |
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 <ShellBase.h> | |
#include <Uefi.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/FileHandleLib.h> | |
#include <Library/HiiLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/PrintLib.h> | |
#include <Library/ShellCEntryLib.h> | |
#include <Library/ShellCommandLib.h> | |
#include <Library/ShellLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiLib.h> | |
#include <Protocol/Spi.h> | |
#include <Protocol/SpiFlash.h> | |
#define CMD_NAME_STRING L"fupdate" | |
#define MAIN_HDR_MAGIC 0xB105B002 | |
STATIC MARVELL_SPI_FLASH_PROTOCOL *SpiFlashProtocol; | |
STATIC MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol; | |
STATIC CONST CHAR16 gShellFUpdateFileName[] = L"ShellCommands"; | |
STATIC EFI_HANDLE gShellFUpdateHiiHandle = NULL; | |
STATIC CONST SHELL_PARAM_ITEM ParamList[] = { | |
{L"help", TypeFlag}, | |
{NULL , TypeMax} | |
}; | |
typedef struct { // Bytes | |
UINT32 Magic; // 0-3 | |
UINT32 PrologSize; // 4-7 | |
UINT32 PrologChecksum; // 8-11 | |
UINT32 BootImageSize; // 12-15 | |
UINT32 BootImageChecksum; // 16-19 | |
UINT32 Reserved0; // 20-23 | |
UINT32 LoadAddr; // 24-27 | |
UINT32 ExecAddr; // 28-31 | |
UINT8 UartConfig; // 32 | |
UINT8 Baudrate; // 33 | |
UINT8 ExtCount; // 34 | |
UINT8 AuxFlags; // 35 | |
UINT32 IoArg0; // 36-39 | |
UINT32 IoArg1; // 40-43 | |
UINT32 IoArg2; // 43-47 | |
UINT32 IoArg3; // 48-51 | |
UINT32 Reserved1; // 52-55 | |
UINT32 Reserved2; // 56-59 | |
UINT32 Reserved3; // 60-63 | |
} MV_FIRMWARE_IMAGE_HEADER; | |
STATIC | |
EFI_STATUS | |
SpiFlashProbe ( | |
IN SPI_DEVICE *Slave | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 IdBuffer, Id, RefId; | |
Id = PcdGet32 (PcdSpiFlashId); | |
IdBuffer = CMD_READ_ID & 0xff; | |
// Read SPI flash ID | |
SpiFlashProtocol->ReadId (Slave, sizeof (UINT32), (UINT8 *)&IdBuffer); | |
// Swap and extract 3 bytes of the ID | |
RefId = SwapBytes32 (IdBuffer) >> 8; | |
if (RefId == 0) { | |
Print (L"%s: No SPI flash detected"); | |
return EFI_DEVICE_ERROR; | |
} else if (RefId != Id) { | |
Print (L"%s: Unsupported SPI flash detected with ID=%2x\n", CMD_NAME_STRING, RefId); | |
return EFI_DEVICE_ERROR; | |
} | |
Print (L"%s: Detected supported SPI flash with ID=%3x\n", CMD_NAME_STRING, RefId); | |
Status = SpiFlashProtocol->Init (SpiFlashProtocol, Slave); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: Cannot initialize flash device\n", CMD_NAME_STRING); | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
STATIC | |
EFI_STATUS | |
CheckImageHeader ( | |
IN OUT UINTN *ImageHeader | |
) | |
{ | |
MV_FIRMWARE_IMAGE_HEADER *Header; | |
UINT32 HeaderLength, Checksum, ChecksumBackup; | |
Header = (MV_FIRMWARE_IMAGE_HEADER *)ImageHeader; | |
HeaderLength = Header->PrologSize; | |
ChecksumBackup = Header->PrologChecksum; | |
// Compare magic number | |
if (Header->Magic != MAIN_HDR_MAGIC) { | |
Print (L"%s: Bad Image magic 0x%08x != 0x%08x\n", CMD_NAME_STRING, Header->Magic, MAIN_HDR_MAGIC); | |
return EFI_DEVICE_ERROR; | |
} | |
// The checksum field is discarded from calculation | |
Header->PrologChecksum = 0; | |
Checksum = CalculateSum32 ((UINT32 *)Header, HeaderLength); | |
if (Checksum != ChecksumBackup) { | |
Print (L"%s: Bad Image checksum. 0x%x != 0x%x\n", CMD_NAME_STRING, Checksum, ChecksumBackup); | |
return EFI_DEVICE_ERROR; | |
} | |
// Restore checksum backup | |
Header->PrologChecksum = ChecksumBackup; | |
return 0; | |
} | |
STATIC | |
EFI_STATUS | |
PrepareFirmwareImage ( | |
IN LIST_ENTRY *CheckPackage, | |
IN OUT SHELL_FILE_HANDLE *FileHandle, | |
IN OUT UINTN **FileBuffer, | |
IN OUT UINTN *FileSize | |
) | |
{ | |
CONST CHAR16 *FileStr; | |
EFI_STATUS Status; | |
UINT64 OpenMode; | |
UINTN *Buffer; | |
// Parse string from commandline | |
FileStr = ShellCommandLineGetRawValue (CheckPackage, 1); | |
if (FileStr == NULL) { | |
Print (L"%s: No image specified\n", CMD_NAME_STRING); | |
return EFI_INVALID_PARAMETER; | |
} else { | |
Status = ShellIsFile (FileStr); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: File not found\n", CMD_NAME_STRING); | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
// Obtain file size | |
OpenMode = EFI_FILE_MODE_READ; | |
Status = ShellOpenFileByName (FileStr, FileHandle, OpenMode, 0); | |
if (EFI_ERROR (Status)) { | |
Print (L"%s: Cannot open Image file\n", CMD_NAME_STRING); | |
return EFI_DEVICE_ERROR; | |
} | |
Status = FileHandleGetSize (*FileHandle, FileSize); | |
if (EFI_ERROR (Status)) { | |
Print (L"%s: Cannot get Image file size\n", CMD_NAME_STRING); | |
} | |
// Read Image header into buffer | |
Buffer = AllocateZeroPool (*FileSize); | |
Status = FileHandleRead (*FileHandle, FileSize, Buffer); | |
if (EFI_ERROR (Status)) { | |
Print (L"%s: Cannot read Image file header\n", CMD_NAME_STRING); | |
ShellCloseFile (FileHandle); | |
FreePool (Buffer); | |
return EFI_DEVICE_ERROR; | |
} | |
*FileBuffer = Buffer; | |
return EFI_SUCCESS; | |
} | |
/** | |
Return the file name of the help text file if not using HII. | |
@return The string pointer to the file name. | |
**/ | |
STATIC | |
CONST CHAR16* | |
EFIAPI | |
ShellCommandGetManFileNameFUpdate ( | |
VOID | |
) | |
{ | |
return gShellFUpdateFileName; | |
} | |
STATIC | |
VOID | |
FUpdateUsage ( | |
VOID | |
) | |
{ | |
Print (L"\nFirmware update command\n" | |
"fupdate <LocalFilePath>\n\n" | |
"LocalFilePath - path to local firmware image file\n" | |
"Example:\n" | |
"Update firmware from file fs2:flash-image.bin\n" | |
" fupdate fs2:flash-image.bin\n" | |
); | |
} | |
STATIC | |
SHELL_STATUS | |
EFIAPI | |
ShellCommandRunFUpdate ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
IN SHELL_FILE_HANDLE FileHandle; | |
SPI_DEVICE *Slave; | |
UINTN FileSize; | |
UINTN *FileBuffer = NULL; | |
CHAR16 *ProblemParam; | |
LIST_ENTRY *CheckPackage; | |
EFI_STATUS Status; | |
// Locate SPI protocols | |
Status = gBS->LocateProtocol ( | |
&gMarvellSpiFlashProtocolGuid, | |
NULL, | |
(VOID **)&SpiFlashProtocol | |
); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: Cannot locate SpiFlash protocol\n", CMD_NAME_STRING); | |
return SHELL_ABORTED; | |
} | |
Status = gBS->LocateProtocol ( | |
&gMarvellSpiMasterProtocolGuid, | |
NULL, | |
(VOID **)&SpiMasterProtocol | |
); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: Cannot locate SpiMaster protocol\n", CMD_NAME_STRING); | |
return SHELL_ABORTED; | |
} | |
// Parse command line | |
Status = ShellInitialize (); | |
if (EFI_ERROR (Status)) { | |
Print (L"%s: Error while initializing Shell\n", CMD_NAME_STRING); | |
ASSERT_EFI_ERROR (Status); | |
return SHELL_ABORTED; | |
} | |
Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE); | |
if (EFI_ERROR (Status)) { | |
Print (L"%s: Invalid parameter\n", CMD_NAME_STRING); | |
return SHELL_ABORTED; | |
} | |
if (ShellCommandLineGetFlag (CheckPackage, L"help")) { | |
FUpdateUsage(); | |
return EFI_SUCCESS; | |
} | |
// Prepare local file to be burned into flash | |
Status = PrepareFirmwareImage (CheckPackage, &FileHandle, &FileBuffer, &FileSize); | |
if (EFI_ERROR(Status)) { | |
return SHELL_ABORTED; | |
} | |
// Check image checksum and magic | |
Status = CheckImageHeader (FileBuffer); | |
if (EFI_ERROR(Status)) { | |
goto HeaderError; | |
} | |
// Setup and probe SPI flash | |
Slave = SpiMasterProtocol->SetupDevice (SpiMasterProtocol, 0, 0); | |
if (Slave == NULL) { | |
Print(L"%s: Cannot allocate SPI device!\n", CMD_NAME_STRING); | |
goto HeaderError; | |
} | |
Status = SpiFlashProbe (Slave); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: Error while performing SPI flash probe\n", CMD_NAME_STRING); | |
goto FlashProbeError; | |
} | |
// Update firmware image in flash at offset 0x0 | |
Status = SpiFlashProtocol->Update (Slave, 0, FileSize, (UINT8 *)FileBuffer); | |
// Release resources | |
SpiMasterProtocol->FreeDevice(Slave); | |
FreePool (FileBuffer); | |
ShellCloseFile (&FileHandle); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: Error while performing flash update\n", CMD_NAME_STRING); | |
return SHELL_ABORTED; | |
} | |
Print (L"%s: Update %d bytes at offset 0x0 succeeded!\n", CMD_NAME_STRING, FileSize); | |
return EFI_SUCCESS; | |
FlashProbeError: | |
SpiMasterProtocol->FreeDevice(Slave); | |
HeaderError: | |
FreePool (FileBuffer); | |
ShellCloseFile (&FileHandle); | |
return SHELL_ABORTED; | |
} | |
EFI_STATUS | |
EFIAPI | |
ShellFUpdateCommandConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
gShellFUpdateHiiHandle = NULL; | |
gShellFUpdateHiiHandle = HiiAddPackages ( | |
&gShellFUpdateHiiGuid, | |
gImageHandle, | |
UefiShellFUpdateCommandLibStrings, | |
NULL | |
); | |
if (gShellFUpdateHiiHandle == NULL) { | |
Print (L"%s: Cannot add Hii package\n", CMD_NAME_STRING); | |
return EFI_DEVICE_ERROR; | |
} | |
Status = ShellCommandRegisterCommandName ( | |
CMD_NAME_STRING, | |
ShellCommandRunFUpdate, | |
ShellCommandGetManFileNameFUpdate, | |
0, | |
CMD_NAME_STRING, | |
TRUE, | |
gShellFUpdateHiiHandle, | |
STRING_TOKEN (STR_GET_HELP_FUPDATE) | |
); | |
if (EFI_ERROR(Status)) { | |
Print (L"%s: Error while registering command\n", CMD_NAME_STRING); | |
return SHELL_ABORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
ShellFUpdateCommandDestructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
if (gShellFUpdateHiiHandle != NULL) { | |
HiiRemovePackages (gShellFUpdateHiiHandle); | |
} | |
return EFI_SUCCESS; | |
} |