public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: zwei4 <david.wei@intel.com>
To: edk2-devel@lists.01.org
Subject: [Patch][edk2-platforms/devel-MinnowBoard3] Add FirmwareUpdate application
Date: Mon, 13 Feb 2017 11:10:05 +0800	[thread overview]
Message-ID: <20170213031005.27728-1-david.wei@intel.com> (raw)

Add FirmwareUpdate application for Minnnowboard 3, which is for updating 8MB SPI NOR flash.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: zwei4 <david.wei@intel.com>
---
 .../Application/FirmwareUpdate/FirmwareUpdate.c    | 927 +++++++++++++++++++++
 .../Application/FirmwareUpdate/FirmwareUpdate.h    | 190 +++++
 .../Application/FirmwareUpdate/FirmwareUpdate.inf  |  83 ++
 .../FirmwareUpdate/FirmwareUpdateStrings.uni       | Bin 0 -> 7480 bytes
 4 files changed, 1200 insertions(+)
 create mode 100644 Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.inf
 create mode 100644 Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdateStrings.uni

diff --git a/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.c b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.c
new file mode 100644
index 000000000..b374b983d
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.c
@@ -0,0 +1,927 @@
+/** @file
+
+Copyright (c) 2007  - 2017, Intel Corporation. All rights reserved.<BR>
+                                                                                   
+  This program and the accompanying materials are licensed and made available under
+  the terms and conditions of the BSD License that accompanies this distribution.  
+  The full text of the license may be found at                                     
+  http://opensource.org/licenses/bsd-license.php.                                  
+                                                                                   
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,            
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.    
+                                                                                   
+
+**/
+
+#include "FirmwareUpdate.h"
+
+EFI_HII_HANDLE  HiiHandle;
+
+
+/*
+
+MinnowBoard 3 Flash Layout
+
+Start (hex)    End (hex)    Length (hex)    Area Name
+-----------    ---------    ------------    ---------
+
+00000000       007FFFFF     00800000        Full Flash Image
+00000014       00000017     00000004            FLMAP0 - Flash Map 0 Register
+00000018       0000001B     00000004            FLMAP1 - Flash Map 1 Register
+0000001C       0000001F     00000004            FLMAP2 - Flash Map 2 Register
+00000030       0000003B     0000000C            FCBA - Flash Component Registers
+00000040       00000043     00000004            FLREG0 - Flash Region 0 (Flash Descriptor) Register
+00000044       00000047     00000004            FLREG1 - Flash Region 1 (IFWI) Register
+00000048       0000004B     00000004            FLREG2 - Flash Region 2 (Intel(R) TXE) Register
+00000050       00000053     00000004            FLREG4 - Flash Region 4 (Platform Data) Register
+00000054       00000057     00000004            FLREG5 - Flash Region 5 (Device Expansion) Register
+00000060       00000063     00000004            FLREG8 - Flash Region 8 (Embedded Controller) Register
+00000080       00000083     00000004            FLMSTR1 - Flash Master 1 (Host CPU/BIOS)
+00000084       00000087     00000004            FLMSTR2 - Flash Master 2 (Intel(R) TXE)
+00000100       000002FF     00000200            FPSBA - SoC Straps (Including Padding)
+000003EC       0000046B     00000080            FPSBA - SoC Straps (Including Padding)
+00000DF0       00000EFF     00000110            VSCC Table
+00000DF0       00000DF7     00000008                VsccEntry0
+00001000       00241FFF     00241000        Boot Partition 1
+00001000       000F9FFF     000F9000            Primary Boot Partition
+00001200       0000120F     00000010                IFP Overrides Partition
+00001210       00001317     00000108                Unified Emulation Partition (UEP)
+00002000       00005FFF     00004000                OEM SMIP Partition
+00006000       0000FFFF     0000A000                CSE RBE Partition
+00010000       0001FFFF     00010000                PMCP
+00020000       0007BFFF     0005C000                CSE BUP Partition
+0007C000       0007FFFF     00004000                uCode Partition
+00080000       000F7FFF     00078000                IBB Partition
+000F8000       000F9FFF     00002000                Debug Token Partition
+000FA000       00241FFF     00148000            Secondary Boot Partition
+000FB000       0013AFFF     00040000                ISHC Partition
+0013B000       0023FFFF     00105000                CSE Main Partition
+00240000       00241FFF     00002000                IUnit Partition
+00242000       0065BFFF     0041A000            Secondary Boot Partition
+00242000       0065BFFF     0041A000        Boot Partition 2
+00381000       0065BFFF     002DB000                OBB Partition
+0065C000       006FF000     000A3001        TXE Data Region
+*/
+
+FV_REGION_INFO mRegionInfo[] = {
+  {0xFF800000, 0x00001000, TRUE},
+  {0xFF801000, 0x0037F000, TRUE},
+  {0xFFB80000, 0x0037F000, TRUE},
+  {0xFFEFF000, 0x00100000, TRUE},
+};
+
+UINTN mRegionInfoCount = sizeof (mRegionInfo) / sizeof (mRegionInfo[0]);
+
+FV_INPUT_DATA mInputData = {0};
+
+SC_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) {
+    //
+    // Apollo Lake Workaround: 0xFFFFF000 - 0xFFFFFFFF region (Top 4KB bwlow 4G) could not be access by SPI protocol. 
+    //
+    if (Address  >= 0xFFFFF000) {
+      return FALSE;
+    }
+    
+    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;
+      }
+      StrCpy (mInputData.FileName, 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 (
+                  &gScSpiProtocolGuid,
+                  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 (PcdFlashAreaSize)) {
+      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 (PcdFlashAreaBaseAddress);
+    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 is 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;
+}
+
+/*++
+  
+  Erase the whole block.
+
+  @param[in] BaseAddress  Base address of the block to be erased.
+
+  @retval    EFI_SUCCESS  The command completed successfully.
+  @retval    Other        Device error or wirte-locked, operation failed.
+
+--*/
+STATIC
+EFI_STATUS
+InternalEraseBlock (
+  IN  EFI_PHYSICAL_ADDRESS BaseAddress
+  )
+{
+  EFI_STATUS                              Status;
+  UINTN                                   NumBytes;
+
+  NumBytes = BLOCK_SIZE;
+
+  Status = SpiFlashBlockErase ((UINTN) BaseAddress, &NumBytes);
+
+  return Status;
+}
+
+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;
+}
+
+/*++
+  Write a block of data.
+
+  @param[in] BaseAddress  Base address of the block.
+  @param[in] Buffer       Data buffer.
+  @param[in] BufferSize   Size of the buffer.
+
+  @retval    EFI_SUCCESS           The command completed successfully.
+  @retval    EFI_INVALID_PARAMETER Invalid parameter, can not proceed.
+  @retval    Other                 Device error or wirte-locked, operation failed.
+
+--*/
+STATIC
+EFI_STATUS
+InternalWriteBlock (
+  IN  EFI_PHYSICAL_ADDRESS        BaseAddress,
+  IN  UINT8                       *Buffer,
+  IN  UINT32                      BufferSize
+  )
+{
+  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;
+  }
+
+  //Workaround: 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
+  )
+{
+  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;
+  UINT32                SectorSize;
+  UINT32                Offset;
+  UINT8                 TempBuffer[SIZE_4KB];
+  UINTN                 Count = 0;
+  UINT32                Length;
+    
+  Offset = (UINT32)Address - PcdGet32 (PcdFlashAreaBaseAddress);
+  SectorSize = SIZE_4KB;
+  Length = *NumBytes;
+  
+  while (Count < Length) {
+    Status = mSpiProtocol ->FlashRead (
+                              mSpiProtocol,
+                              FlashRegionAll,
+                              Offset,
+                              SIZE_4KB,
+                              TempBuffer
+                              );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "Read SPI ROM Failed [%08x]\n", Offset));
+      break;
+    }
+
+    CopyMem ((Buffer + Count), (VOID *)TempBuffer, SectorSize);
+    Offset += SectorSize;
+    Count += SectorSize;
+  }
+  
+  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;
+  UINT32                    Offset;
+  UINT32                    Length;
+  UINT32                    RemainingBytes;
+
+  ASSERT ((NumBytes != NULL) && (Buffer != NULL));
+  ASSERT (Address >= (UINTN)PcdGet32 (PcdFlashAreaBaseAddress));
+  
+  Offset    = (UINT32)Address - PcdGet32 (PcdFlashAreaBaseAddress);
+
+  ASSERT ((*NumBytes + Offset) <= (UINTN)PcdGet32 (PcdFlashAreaSize));
+
+  Status = EFI_SUCCESS;
+  RemainingBytes = *NumBytes;
+
+  while (RemainingBytes > 0) {
+
+    DEBUG ((EFI_D_ERROR, "SpiFlashWrite RemainingBytes 0x%x\n", RemainingBytes));
+    
+    if (RemainingBytes > SIZE_4KB) {
+      Length = SIZE_4KB;
+    } else {
+      Length = RemainingBytes;
+    }
+
+    Status = mSpiProtocol->FlashWrite(
+                           mSpiProtocol,
+                           FlashRegionAll,
+                           Offset,
+                           Length,
+                           Buffer
+                           );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "SPI Wirte failed. Status: %r.\n", 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;
+  UINT32               Offset;
+  UINTN               RemainingBytes;
+
+  ASSERT (NumBytes != NULL);
+  ASSERT (Address >= (UINTN)PcdGet32 (PcdFlashAreaBaseAddress));
+   
+  Offset = (UINT32)Address - PcdGet32 (PcdFlashAreaBaseAddress);
+
+  ASSERT ((*NumBytes % SIZE_4KB) == 0);
+  ASSERT ((*NumBytes + Offset) <= (UINTN)PcdGet32 (PcdFlashAreaSize));
+
+  Status = EFI_SUCCESS;
+  RemainingBytes = *NumBytes;
+
+  while (RemainingBytes > 0) {
+  
+    Status = mSpiProtocol->FlashErase (
+                              mSpiProtocol,
+                              FlashRegionAll,
+                              Offset,
+                              SIZE_4KB
+                              );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "Erase failed. Status: %r Offset = 0x%x\n", Status, Offset));
+      break;
+    }
+    
+    RemainingBytes -= SIZE_4KB;
+    Offset         += SIZE_4KB;
+  }
+
+  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;
+}
+
diff --git a/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.h b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.h
new file mode 100644
index 000000000..61624b58b
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.h
@@ -0,0 +1,190 @@
+/** @file
+
+Copyright (c) 1999  - 2014, Intel Corporation. All rights reserved.<BR>
+                                                                                   
+  This program and the accompanying materials are licensed and made available under
+  the terms and conditions of the BSD License that accompanies this distribution.  
+  The full text of the license may be found at                                     
+  http://opensource.org/licenses/bsd-license.php.                                  
+                                                                                   
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,            
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.    
+                                                                                   
+
+**/
+
+#ifndef _FIRMWARE_UPDATE_H_
+#define _FIRMWARE_UPDATE_H_
+
+#include <Uefi.h>
+
+#include <PiDxe.h>
+
+#include <Guid/FileInfo.h>
+
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/Spi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/HiiLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PrintLib.h>
+#include <Library/ShellLib.h>
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+//
+// Function Prototypes.
+//
+STATIC
+EFI_STATUS
+ReadFileData (
+  IN  CHAR16   *FileName,
+  OUT UINT8    **Buffer,
+  OUT UINT32   *BufferSize
+  );
+
+STATIC
+EFI_STATUS
+InternalEraseBlock (
+  IN  EFI_PHYSICAL_ADDRESS    BaseAddress
+  );
+
+#if 0
+STATIC
+EFI_STATUS
+InternalReadBlock (
+  IN  EFI_PHYSICAL_ADDRESS    BaseAddress,
+  OUT VOID                    *ReadBuffer
+  );
+#endif
+
+STATIC
+EFI_STATUS
+InternalCompareBlock (
+  IN  EFI_PHYSICAL_ADDRESS    BaseAddress,
+  IN  UINT8                   *Buffer
+  );
+
+STATIC
+EFI_STATUS
+InternalWriteBlock (
+  IN  EFI_PHYSICAL_ADDRESS    BaseAddress,
+  IN  UINT8                   *Buffer,
+  IN  UINT32                  BufferSize
+  );
+
+STATIC
+VOID
+PrintHelpInfo (
+  VOID
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+SpiFlashRead (
+  IN     UINTN     Address,
+  IN OUT UINT32    *NumBytes,
+     OUT UINT8     *Buffer
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+SpiFlashWrite (
+  IN     UINTN     Address,
+  IN OUT UINT32    *NumBytes,
+  IN     UINT8     *Buffer
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+SpiFlashBlockErase (
+  IN    UINTN    Address,
+  IN    UINTN    *NumBytes
+  );
+
+STATIC
+EFI_STATUS
+EFIAPI
+ConvertMac (
+  CHAR16 *Str
+  );
+
+EFI_STATUS
+InitializeFVUPDATE (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable
+  );
+
+//
+// Flash specific definitions.
+// - Should we use a PCD for this information?
+//
+#define BLOCK_SIZE          SIZE_4KB
+
+//
+// Flash region layout and update information.
+//
+typedef struct {
+  EFI_PHYSICAL_ADDRESS  Base;
+  UINTN                 Size;
+  BOOLEAN               Update;
+} FV_REGION_INFO;
+
+//
+// MAC Address information.
+//
+#define MAC_ADD_STR_LEN       12
+#define MAC_ADD_STR_SIZE      (MAC_ADD_STR_LEN + 1)
+#define MAC_ADD_BYTE_COUNT    6
+#define MAC_ADD_TMP_STR_LEN   2
+#define MAC_ADD_TMP_STR_SIZE  (MAC_ADD_TMP_STR_LEN + 1)
+
+//
+// Command Line Data.
+//
+#define INPUT_STRING_LEN    255
+#define INPUT_STRING_SIZE   (INPUT_STRING_LEN + 1)
+typedef struct {
+  BOOLEAN   UpdateFromFile;
+  CHAR16    FileName[INPUT_STRING_SIZE];
+  BOOLEAN   UpdateMac;
+  UINT8     MacValue[MAC_ADD_BYTE_COUNT];
+  BOOLEAN   FullFlashUpdate;
+} FV_INPUT_DATA;
+
+//
+// Prefix Opcode Index on the host SPI controller.
+//
+typedef enum {
+  SPI_WREN,             // Prefix Opcode 0: Write Enable.
+  SPI_EWSR,             // Prefix Opcode 1: Enable Write Status Register.
+} PREFIX_OPCODE_INDEX;
+
+//
+// Opcode Menu Index on the host SPI controller.
+//
+typedef enum {
+  SPI_READ_ID,        // Opcode 0: READ ID, Read cycle with address.
+  SPI_READ,           // Opcode 1: READ, Read cycle with address.
+  SPI_RDSR,           // Opcode 2: Read Status Register, No address.
+  SPI_WRDI_SFDP,      // Opcode 3: Write Disable or Discovery Parameters, No address.
+  SPI_SERASE,         // Opcode 4: Sector Erase (4KB), Write cycle with address.
+  SPI_BERASE,         // Opcode 5: Block Erase (32KB), Write cycle with address.
+  SPI_PROG,           // Opcode 6: Byte Program, Write cycle with address.
+  SPI_WRSR,           // Opcode 7: Write Status Register, No address.
+} SPI_OPCODE_INDEX;
+
+#endif
diff --git a/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.inf b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.inf
new file mode 100644
index 000000000..6215714f2
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdate.inf
@@ -0,0 +1,83 @@
+## @file
+# Implements a Tunnel Mountain specific flash update program.  This will allow
+# users to update all regions of the flash as needed in a given update.
+#
+# Copyright (c) 2006  - 2017, Intel Corporation. All rights reserved.<BR>
+#                                                                                  
+# This program and the accompanying materials are licensed and made available under
+# the terms and conditions of the BSD License that accompanies this distribution.  
+# The full text of the license may be found at                                     
+# http://opensource.org/licenses/bsd-license.php.                                  
+#                                                                                  
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,            
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.    
+#                                                                                  
+#
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = FirmwareUpdate
+  FILE_GUID                      = AEFAF26C-FB6D-4fef-AF7A-9D78FF201FCA
+  MODULE_TYPE                    = UEFI_APPLICATION
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = ShellCEntryLib
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = X64
+#
+
+[Sources]
+  FirmwareUpdateStrings.uni
+  FirmwareUpdate.c
+  FirmwareUpdate.h
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  ShellPkg/ShellPkg.dec
+  BroxtonPlatformPkg/PlatformPkg.dec
+  BroxtonSiPkg/BroxtonSiPkg.dec
+  
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  CacheMaintenanceLib
+  DebugLib
+  FileHandleLib
+  #FlashDeviceLib
+  #SpiFlashCommonLib
+  MemoryAllocationLib
+  PcdLib
+  ShellCEntryLib
+  ShellLib
+  UefiApplicationEntryPoint
+  UefiBootServicesTableLib
+  UefiLib
+  UefiRuntimeServicesTableLib
+
+[Protocols]
+  gEfiLoadedImageProtocolGuid                   # PROTOCOL ALWAYS_CONSUMED
+  gEfiFirmwareVolumeBlockProtocolGuid           # PROTOCOL ALWAYS_CONSUMED
+  gScSpiProtocolGuid
+
+[Pcd]
+  gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize   ## CONSUMES
+
+[FixedPcd]
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+
+  gPlatformModuleTokenSpaceGuid.PcdFlashAreaBaseAddress
+  gPlatformModuleTokenSpaceGuid.PcdFlashAreaSize
+
+[BuildOptions]
+  MSFT:*_*_X64_CC_FLAGS       = /Od 
+  INTEL:*_*_X64_CC_FLAGS       = /Od 
\ No newline at end of file
diff --git a/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdateStrings.uni b/Platform/BroxtonPlatformPkg/Common/Application/FirmwareUpdate/FirmwareUpdateStrings.uni
new file mode 100644
index 0000000000000000000000000000000000000000..fdd254dea64e70d6021a660be1f861338ed910d0
GIT binary patch
literal 7480
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

literal 0
HcmV?d00001

-- 
2.11.0.windows.1



                 reply	other threads:[~2017-02-13  3:10 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170213031005.27728-1-david.wei@intel.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox