/** @file Copyright (c) 2007 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "FirmwareUpdate.h" EFI_HII_HANDLE HiiHandle; // // MinnowMax Flash Layout // //Start (hex) End (hex) Length (hex) Area Name //----------- --------- ------------ --------- //00000000 007FFFFF 00800000 Flash Image // //00000000 00000FFF 00001000 Descriptor Region //00001000 003FFFFF 003FF000 TXE Region //00500000 007FFFFF 00400000 BIOS Region // FV_REGION_INFO mRegionInfo[] = { {FixedPcdGet32 (PcdFlashDescriptorBase), FixedPcdGet32 (PcdFlashDescriptorSize), TRUE}, {FixedPcdGet32 (PcdTxeRomBase), FixedPcdGet32 (PcdTxeRomSize), TRUE}, {FixedPcdGet32 (PcdBiosRomBase), FixedPcdGet32 (PcdBiosRomSize), TRUE} }; UINTN mRegionInfoCount = ARRAY_SIZE (mRegionInfo); FV_INPUT_DATA mInputData = {0}; EFI_SPI_PROTOCOL *mSpiProtocol; EFI_STATUS GetRegionIndex ( IN EFI_PHYSICAL_ADDRESS Address, OUT UINTN *RegionIndex ) { UINTN Index; for (Index = 0; Index < mRegionInfoCount; Index++) { if (Address >= mRegionInfo[Index].Base && Address < (mRegionInfo[Index].Base + mRegionInfo[Index].Size) ) { break; } } *RegionIndex = Index; if (Index >= mRegionInfoCount) { return EFI_NOT_FOUND; } return EFI_SUCCESS; } BOOLEAN UpdateBlock ( IN EFI_PHYSICAL_ADDRESS Address ) { EFI_STATUS Status; UINTN Index; if (mInputData.FullFlashUpdate) { return TRUE; } Status = GetRegionIndex (Address, &Index); if ((!EFI_ERROR(Status)) && mRegionInfo[Index].Update) { return TRUE; } return FALSE; } EFI_STATUS MarkRegionState ( IN EFI_PHYSICAL_ADDRESS Address, IN BOOLEAN Update ) { EFI_STATUS Status; UINTN Index; Status = GetRegionIndex (Address, &Index); if (!EFI_ERROR(Status)) { mRegionInfo[Index].Update = Update; } return Status; } UINTN InternalPrintToken ( IN CONST CHAR16 *Format, IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *Console, IN VA_LIST Marker ) { EFI_STATUS Status; UINTN Return; CHAR16 *Buffer; UINTN BufferSize; ASSERT (Format != NULL); ASSERT (((UINTN) Format & BIT0) == 0); ASSERT (Console != NULL); BufferSize = (PcdGet32 (PcdUefiLibMaxPrintBufferSize) + 1) * sizeof (CHAR16); Buffer = (CHAR16 *) AllocatePool(BufferSize); ASSERT (Buffer != NULL); Return = UnicodeVSPrint (Buffer, BufferSize, Format, Marker); if (Console != NULL && Return > 0) { // // To be extra safe make sure Console has been initialized. // Status = Console->OutputString (Console, Buffer); if (EFI_ERROR (Status)) { Return = 0; } } FreePool (Buffer); return Return; } UINTN EFIAPI PrintToken ( IN UINT16 Token, IN EFI_HII_HANDLE Handle, ... ) { VA_LIST Marker; UINTN Return; CHAR16 *Format; VA_START (Marker, Handle); Format = HiiGetString (Handle, Token, NULL); ASSERT (Format != NULL); Return = InternalPrintToken (Format, gST->ConOut, Marker); FreePool (Format); VA_END (Marker); return Return; } EFI_STATUS ParseCommandLine ( IN UINTN Argc, IN CHAR16 **Argv ) { EFI_STATUS Status; UINTN Index; // // Check to make sure that the command line has enough arguments for minimal // operation. The minimum is just the file name. // if (Argc < 2 || Argc > 4) { return EFI_INVALID_PARAMETER; } // // Loop through command line arguments. // for (Index = 1; Index < Argc; Index++) { // // Make sure the string is valid. // if (StrLen (Argv[Index]) == 0) {; PrintToken (STRING_TOKEN (STR_FWUPDATE_ZEROLENGTH_ARG), HiiHandle); return EFI_INVALID_PARAMETER; } // // Check to see if this is an option or the file name. // if ((Argv[Index])[0] == L'-' || (Argv[Index])[0] == L'/') { // // Parse the arguments. // if ((StrCmp (Argv[Index], L"-h") == 0) || (StrCmp (Argv[Index], L"--help") == 0) || (StrCmp (Argv[Index], L"/?") == 0) || (StrCmp (Argv[Index], L"/h") == 0)) { // // Print Help Information. // return EFI_INVALID_PARAMETER; } else if (StrCmp (Argv[Index], L"-m") == 0) { // // Parse the MAC address here. // Status = ConvertMac(Argv[Index+1]); if (EFI_ERROR(Status)) { PrintToken (STRING_TOKEN (STR_FWUPDATE_INVAILD_MAC), HiiHandle); return Status; } // // Save the MAC address to mInputData.MacValue. // mInputData.UpdateMac= TRUE; Index++; } else { // // Invalid option was provided. // return EFI_INVALID_PARAMETER; } } if ((Index == Argc - 1) && (StrCmp (Argv[Index - 1], L"-m") != 0)) { // // The only parameter that is not an option is the firmware image. Check // to make sure that the file exists. // Status = ShellIsFile (Argv[Index]); if (EFI_ERROR (Status)) { PrintToken (STRING_TOKEN (STR_FWUPDATE_FILE_NOT_FOUND_ERROR), HiiHandle, Argv[Index]); return EFI_INVALID_PARAMETER; } if (StrLen (Argv[Index]) > INPUT_STRING_LEN) { PrintToken (STRING_TOKEN (STR_FWUPDATE_PATH_ERROR), HiiHandle, Argv[Index]); return EFI_INVALID_PARAMETER; } StrCpyS (mInputData.FileName, sizeof (mInputData.FileName) / sizeof (CHAR16), Argv[Index]); mInputData.UpdateFromFile = TRUE; } } return EFI_SUCCESS; } INTN EFIAPI ShellAppMain ( IN UINTN Argc, IN CHAR16 **Argv ) { EFI_STATUS Status; UINTN Index; UINT32 FileSize; UINT32 BufferSize; UINT8 *FileBuffer; UINT8 *Buffer; EFI_PHYSICAL_ADDRESS Address; UINTN CountOfBlocks; EFI_TPL OldTpl; BOOLEAN ResetRequired; BOOLEAN FlashError; Index = 0; FileSize = 0; BufferSize = 0; FileBuffer = NULL; Buffer = NULL; Address = 0; CountOfBlocks = 0; ResetRequired = FALSE; FlashError = FALSE; Status = EFI_SUCCESS; mInputData.FullFlashUpdate = TRUE; // // Publish our HII data. // HiiHandle = HiiAddPackages ( &gEfiCallerIdGuid, NULL, FirmwareUpdateStrings, NULL ); if (HiiHandle == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } // // Locate the SPI protocol. // Status = gBS->LocateProtocol ( &gEfiSpiProtocolGuid, NULL, (VOID **)&mSpiProtocol ); if (EFI_ERROR (Status)) { PrintToken (STRING_TOKEN (STR_SPI_NOT_FOUND), HiiHandle); return EFI_DEVICE_ERROR; } // // Parse the command line. // Status = ParseCommandLine (Argc, Argv); if (EFI_ERROR (Status)) { PrintHelpInfo (); Status = EFI_SUCCESS; goto Done; } // // Display sign-on information. // PrintToken (STRING_TOKEN (STR_FWUPDATE_FIRMWARE_VOL_UPDATE), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_VERSION), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_COPYRIGHT), HiiHandle); // // Test to see if the firmware needs to be updated. // if (mInputData.UpdateFromFile) { // // Get the file to use in the update. // PrintToken (STRING_TOKEN (STR_FWUPDATE_READ_FILE), HiiHandle, mInputData.FileName); Status = ReadFileData (mInputData.FileName, &FileBuffer, &FileSize); if (EFI_ERROR (Status)) { PrintToken (STRING_TOKEN (STR_FWUPDATE_READ_FILE_ERROR), HiiHandle, mInputData.FileName); goto Done; } // // Check that the file and flash sizes match. // if (FileSize != PcdGet32 (PcdFlashChipSize)) { PrintToken (STRING_TOKEN (STR_FWUPDATE_SIZE), HiiHandle); Status = EFI_UNSUPPORTED; goto Done; } // // Display flash update information. // PrintToken (STRING_TOKEN (STR_FWUPDATE_UPDATING_FIRMWARE), HiiHandle); // // Update it. // Buffer = FileBuffer; BufferSize = FileSize; Address = PcdGet32 (PcdFlashChipBase); CountOfBlocks = (UINTN) (BufferSize / BLOCK_SIZE); // // Raise TPL to TPL_NOTIFY to block any event handler, // while still allowing RaiseTPL(TPL_NOTIFY) within // output driver during Print(). // OldTpl = gBS->RaiseTPL (TPL_NOTIFY); for (Index = 0; Index < CountOfBlocks; Index++) { // // Handle block based on address and contents. // if (!UpdateBlock (Address)) { DEBUG((EFI_D_INFO, "Skipping block at 0x%lx\n", Address)); } else if (!EFI_ERROR (InternalCompareBlock (Address, Buffer))) { DEBUG((EFI_D_INFO, "Skipping block at 0x%lx (already programmed)\n", Address)); } else { // // Display a dot for each block being updated. // Print (L"."); // // Flag that the flash image will be changed and the system must be rebooted // to use the change. // ResetRequired = TRUE; // // Make updating process uninterruptable, // so that the flash memory area is not accessed by other entities // which may interfere with the updating process. // Status = InternalEraseBlock (Address); ASSERT_EFI_ERROR(Status); if (EFI_ERROR (Status)) { gBS->RestoreTPL (OldTpl); FlashError = TRUE; goto Done; } Status = InternalWriteBlock ( Address, Buffer, (BufferSize > BLOCK_SIZE ? BLOCK_SIZE : BufferSize) ); if (EFI_ERROR (Status)) { gBS->RestoreTPL (OldTpl); FlashError = TRUE; goto Done; } } // // Move to next block to update. // Address += BLOCK_SIZE; Buffer += BLOCK_SIZE; if (BufferSize > BLOCK_SIZE) { BufferSize -= BLOCK_SIZE; } else { BufferSize = 0; } } gBS->RestoreTPL (OldTpl); // // Print result of update. // if (!FlashError) { if (ResetRequired) { Print (L"\n"); PrintToken (STRING_TOKEN (STR_FWUPDATE_UPDATE_SUCCESS), HiiHandle); } else { PrintToken (STRING_TOKEN (STR_FWUPDATE_NO_RESET), HiiHandle); } } else { goto Done; } } // // All flash updates are done so see if the system needs to be reset. // if (ResetRequired && !FlashError) { // // Update successful. // for (Index = 5; Index > 0; Index--) { PrintToken (STRING_TOKEN (STR_FWUPDATE_SHUTDOWN), HiiHandle, Index); gBS->Stall (1000000); } gRT->ResetSystem (EfiResetShutdown, EFI_SUCCESS, 0, NULL); PrintToken (STRING_TOKEN (STR_FWUPDATE_MANUAL_RESET), HiiHandle); CpuDeadLoop (); } Done: // // Print flash update failure message if error detected. // if (FlashError) { PrintToken (STRING_TOKEN (STR_FWUPDATE_UPDATE_FAILED), HiiHandle, Index); } // // Do cleanup. // if (HiiHandle != NULL) { HiiRemovePackages (HiiHandle); } if (FileBuffer) { gBS->FreePool (FileBuffer); } return Status; } STATIC EFI_STATUS InternalEraseBlock ( IN EFI_PHYSICAL_ADDRESS BaseAddress ) /*++ Routine Description: Erase the whole block. Arguments: BaseAddress - Base address of the block to be erased. Returns: EFI_SUCCESS - The command completed successfully. Other - Device error or wirte-locked, operation failed. --*/ { EFI_STATUS Status; UINTN NumBytes; NumBytes = BLOCK_SIZE; Status = SpiFlashBlockErase ((UINTN) BaseAddress, &NumBytes); return Status; } #if 0 STATIC EFI_STATUS InternalReadBlock ( IN EFI_PHYSICAL_ADDRESS BaseAddress, OUT VOID *ReadBuffer ) { EFI_STATUS Status; UINT32 BlockSize; BlockSize = BLOCK_SIZE; Status = SpiFlashRead ((UINTN) BaseAddress, &BlockSize, ReadBuffer); return Status; } #endif STATIC EFI_STATUS InternalCompareBlock ( IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT8 *Buffer ) { EFI_STATUS Status; VOID *CompareBuffer; UINT32 NumBytes; INTN CompareResult; NumBytes = BLOCK_SIZE; CompareBuffer = AllocatePool (NumBytes); if (CompareBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } Status = SpiFlashRead ((UINTN) BaseAddress, &NumBytes, CompareBuffer); if (EFI_ERROR (Status)) { goto Done; } CompareResult = CompareMem (CompareBuffer, Buffer, BLOCK_SIZE); if (CompareResult != 0) { Status = EFI_VOLUME_CORRUPTED; } Done: if (CompareBuffer != NULL) { FreePool (CompareBuffer); } return Status; } STATIC EFI_STATUS InternalWriteBlock ( IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT8 *Buffer, IN UINT32 BufferSize ) /*++ Routine Description: Write a block of data. Arguments: BaseAddress - Base address of the block. Buffer - Data buffer. BufferSize - Size of the buffer. Returns: EFI_SUCCESS - The command completed successfully. EFI_INVALID_PARAMETER - Invalid parameter, can not proceed. Other - Device error or wirte-locked, operation failed. --*/ { EFI_STATUS Status; Status = SpiFlashWrite ((UINTN) BaseAddress, &BufferSize, Buffer); ASSERT_EFI_ERROR(Status); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "\nFlash write error.")); return Status; } WriteBackInvalidateDataCacheRange ((VOID *) (UINTN) BaseAddress, BLOCK_SIZE); Status = InternalCompareBlock (BaseAddress, Buffer); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "\nError when writing to BaseAddress %lx with different at offset %x.", BaseAddress, Status)); } else { DEBUG((EFI_D_INFO, "\nVerified data written to Block at %lx is correct.", BaseAddress)); } return Status; } STATIC EFI_STATUS ReadFileData ( IN CHAR16 *FileName, OUT UINT8 **Buffer, OUT UINT32 *BufferSize ) { EFI_STATUS Status; SHELL_FILE_HANDLE FileHandle; UINT64 Size; VOID *NewBuffer; UINTN ReadSize; FileHandle = NULL; NewBuffer = NULL; Size = 0; Status = ShellOpenFileByName (FileName, &FileHandle, EFI_FILE_MODE_READ, 0); if (EFI_ERROR (Status)) { goto Done; } Status = FileHandleIsDirectory (FileHandle); if (!EFI_ERROR (Status)) { Status = EFI_NOT_FOUND; goto Done; } Status = FileHandleGetSize (FileHandle, &Size); if (EFI_ERROR (Status)) { goto Done; } NewBuffer = AllocatePool ((UINTN) Size); ReadSize = (UINTN) Size; Status = FileHandleRead (FileHandle, &ReadSize, NewBuffer); if (EFI_ERROR (Status)) { goto Done; } else if (ReadSize != (UINTN) Size) { Status = EFI_INVALID_PARAMETER; goto Done; } Done: if (FileHandle != NULL) { ShellCloseFile (&FileHandle); } if (EFI_ERROR (Status)) { if (NewBuffer != NULL) { FreePool (NewBuffer); } } else { *Buffer = NewBuffer; *BufferSize = (UINT32) Size; } return Status; } STATIC VOID PrintHelpInfo ( VOID ) /*++ Routine Description: Print out help information. Arguments: None. Returns: None. --*/ { PrintToken (STRING_TOKEN (STR_FWUPDATE_FIRMWARE_VOL_UPDATE), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_VERSION), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_COPYRIGHT), HiiHandle); Print (L"\n"); PrintToken (STRING_TOKEN (STR_FWUPDATE_USAGE), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_USAGE_1), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_USAGE_2), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_USAGE_3), HiiHandle); PrintToken (STRING_TOKEN (STR_FWUPDATE_USAGE_4), HiiHandle); Print (L"\n"); } /** Read NumBytes bytes of data from the address specified by PAddress into Buffer. @param[in] Address The starting physical address of the read. @param[in,out] NumBytes On input, the number of bytes to read. On output, the number of bytes actually read. @param[out] Buffer The destination data buffer for the read. @retval EFI_SUCCESS Opertion is successful. @retval EFI_DEVICE_ERROR If there is any device errors. **/ EFI_STATUS EFIAPI SpiFlashRead ( IN UINTN Address, IN OUT UINT32 *NumBytes, OUT UINT8 *Buffer ) { EFI_STATUS Status = EFI_SUCCESS; UINTN Offset = 0; ASSERT ((NumBytes != NULL) && (Buffer != NULL)); Offset = Address - (UINTN)PcdGet32 (PcdFlashChipBase); Status = mSpiProtocol->Execute ( mSpiProtocol, 1, //SPI_READ, 0, //SPI_WREN, TRUE, TRUE, FALSE, Offset, BLOCK_SIZE, Buffer, EnumSpiRegionAll ); return Status; } /** Write NumBytes bytes of data from Buffer to the address specified by PAddresss. @param[in] Address The starting physical address of the write. @param[in,out] NumBytes On input, the number of bytes to write. On output, the actual number of bytes written. @param[in] Buffer The source data buffer for the write. @retval EFI_SUCCESS Opertion is successful. @retval EFI_DEVICE_ERROR If there is any device errors. **/ EFI_STATUS EFIAPI SpiFlashWrite ( IN UINTN Address, IN OUT UINT32 *NumBytes, IN UINT8 *Buffer ) { EFI_STATUS Status; UINTN Offset; UINT32 Length; UINT32 RemainingBytes; ASSERT ((NumBytes != NULL) && (Buffer != NULL)); ASSERT (Address >= (UINTN)PcdGet32 (PcdFlashChipBase)); Offset = Address - (UINTN)PcdGet32 (PcdFlashChipBase); ASSERT ((*NumBytes + Offset) <= (UINTN)PcdGet32 (PcdFlashChipSize)); Status = EFI_SUCCESS; RemainingBytes = *NumBytes; while (RemainingBytes > 0) { if (RemainingBytes > SIZE_4KB) { Length = SIZE_4KB; } else { Length = RemainingBytes; } Status = mSpiProtocol->Execute ( mSpiProtocol, SPI_PROG, SPI_WREN, TRUE, TRUE, TRUE, (UINT32) Offset, Length, Buffer, EnumSpiRegionAll ); if (EFI_ERROR (Status)) { break; } RemainingBytes -= Length; Offset += Length; Buffer += Length; } // // Actual number of bytes written. // *NumBytes -= RemainingBytes; return Status; } /** Erase the block starting at Address. @param[in] Address The starting physical address of the block to be erased. This library assume that caller garantee that the PAddress is at the starting address of this block. @param[in] NumBytes On input, the number of bytes of the logical block to be erased. On output, the actual number of bytes erased. @retval EFI_SUCCESS. Opertion is successful. @retval EFI_DEVICE_ERROR If there is any device errors. **/ EFI_STATUS EFIAPI SpiFlashBlockErase ( IN UINTN Address, IN UINTN *NumBytes ) { EFI_STATUS Status; UINTN Offset; UINTN RemainingBytes; ASSERT (NumBytes != NULL); ASSERT (Address >= (UINTN)PcdGet32 (PcdFlashChipBase)); Offset = Address - (UINTN)PcdGet32 (PcdFlashChipBase); ASSERT ((*NumBytes % SIZE_4KB) == 0); ASSERT ((*NumBytes + Offset) <= (UINTN)PcdGet32 (PcdFlashChipSize)); Status = EFI_SUCCESS; RemainingBytes = *NumBytes; while (RemainingBytes > 0) { Status = mSpiProtocol->Execute ( mSpiProtocol, SPI_SERASE, SPI_WREN, FALSE, TRUE, FALSE, (UINT32) Offset, 0, NULL, EnumSpiRegionAll ); if (EFI_ERROR (Status)) { break; } RemainingBytes -= SIZE_4KB; Offset += SIZE_4KB; } // // Actual number of bytes erased. // *NumBytes -= RemainingBytes; return Status; } EFI_STATUS EFIAPI ConvertMac ( CHAR16 *Str ) { UINTN Index; UINT8 Temp[MAC_ADD_STR_LEN]; if (Str == NULL) return EFI_INVALID_PARAMETER; if (StrLen(Str) != MAC_ADD_STR_LEN) return EFI_INVALID_PARAMETER; for (Index = 0; Index < MAC_ADD_STR_LEN; Index++) { if (Str[Index] >= 0x30 && Str[Index] <= 0x39) { Temp[Index] = (UINT8)Str[Index] - 0x30; } else if (Str[Index] >= 0x41 && Str[Index] <= 0x46) { Temp[Index] = (UINT8)Str[Index] - 0x37; } else if (Str[Index] >= 0x61 && Str[Index] <= 0x66) { Temp[Index] = (UINT8)Str[Index] - 0x57; } else { return EFI_INVALID_PARAMETER; } } for (Index = 0; Index < MAC_ADD_BYTE_COUNT; Index++) { mInputData.MacValue[Index] = (Temp[2 * Index] << 4) + Temp[2 * Index + 1]; } return EFI_SUCCESS; }