public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Jagadeesh Ujja <jagadeesh.ujja@arm.com>
To: edk2-devel@lists.01.org, liming.gao@intel.com,
	chao.b.zhang@intel.com, leif.lindholm@linaro.org,
	ard.biesheuvel@linaro.org, Achin.Gupta@arm.com,
	Supreeth.Venkatesh@arm.com, jian.j.wang@intel.com
Subject: [PATCH v2 09/11] ArmPlatformPkg/NorFlashDxe: allow reusability as a MM driver
Date: Wed,  2 Jan 2019 18:43:46 +0530	[thread overview]
Message-ID: <1546434828-24405-10-git-send-email-jagadeesh.ujja@arm.com> (raw)
In-Reply-To: <1546434828-24405-1-git-send-email-jagadeesh.ujja@arm.com>

Adapt the NorFlashDxe driver to be used as a MM_STANDALONE driver to
allow access to NOR flash for code executing in MM_STANDALONE mode.
This allows storing of EFI variables on NOR flash which is accessible
only via the MM STANDALONE mode software.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jagadeesh Ujja <jagadeesh.ujja@arm.com>
---
 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.h            |    4 +-
 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf          |    2 +
 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c         |   96 +-
 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c   | 1339 ++++++++++++++++++++
 ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf |   76 ++
 5 files changed, 1470 insertions(+), 47 deletions(-)

diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.h b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.h
index c0563f6..d149652 100644
--- a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.h
+++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.h
@@ -1,6 +1,6 @@
 /** @file  NorFlashDxe.h
 
-  Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
+  Copyright (c) 2011 - 2019, ARM Ltd. All rights reserved.<BR>
 
   This program and the accompanying materials
   are licensed and made available under the terms and conditions of the BSD License
@@ -19,6 +19,7 @@
 #include <Base.h>
 #include <PiDxe.h>
 
+#include <PiMm.h>
 #include <Guid/EventGroup.h>
 
 #include <Protocol/BlockIo.h>
@@ -30,6 +31,7 @@
 #include <Library/NorFlashPlatformLib.h>
 #include <Library/UefiLib.h>
 #include <Library/UefiRuntimeLib.h>
+#include <Library/StandaloneMmServicesTableLib.h>
 
 #define NOR_FLASH_ERASE_RETRY                     10
 
diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf
index a59a21a..6bf0ff2 100644
--- a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf
+++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashDxe.inf
@@ -44,6 +44,7 @@
   UefiBootServicesTableLib
   UefiRuntimeLib
   DxeServicesTableLib
+  StandaloneMmServicesTableLib
 
 [Guids]
   gEfiSystemNvDataFvGuid
@@ -57,6 +58,7 @@
   gEfiDevicePathProtocolGuid
   gEfiFirmwareVolumeBlockProtocolGuid
   gEfiDiskIoProtocolGuid
+  gEfiSmmFirmwareVolumeBlockProtocolGuid
 
 [Pcd.common]
   gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c
index e62ffbb..024da28 100644
--- a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c
+++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashFvbDxe.c
@@ -1,6 +1,6 @@
 /*++ @file  NorFlashFvbDxe.c
 
- Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
+ Copyright (c) 2011 - 2019, ARM Ltd. All rights reserved.<BR>
 
  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
@@ -720,27 +720,29 @@ NorFlashFvbInitialize (
   DEBUG((DEBUG_BLKIO,"NorFlashFvbInitialize\n"));
   ASSERT((Instance != NULL));
 
-  //
-  // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
-  //
-
-  // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;
-  //       even if we only use the small block region at the top of the NOR Flash.
-  //       The reason is when the NOR Flash memory is set into program mode, the command
-  //       is written as the base of the flash region (ie: Instance->DeviceBaseAddress)
-  RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;
-
-  Status = gDS->AddMemorySpace (
-      EfiGcdMemoryTypeMemoryMappedIo,
-      Instance->DeviceBaseAddress, RuntimeMmioRegionSize,
-      EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
-      );
-  ASSERT_EFI_ERROR (Status);
-
-  Status = gDS->SetMemorySpaceAttributes (
-      Instance->DeviceBaseAddress, RuntimeMmioRegionSize,
-      EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
-  ASSERT_EFI_ERROR (Status);
+  if (!InMm ()) {
+    //
+    // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
+    //
+
+    // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;
+    //       even if we only use the small block region at the top of the NOR Flash.
+    //       The reason is when the NOR Flash memory is set into program mode, the command
+    //       is written as the base of the flash region (ie: Instance->DeviceBaseAddress)
+    RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;
+
+    Status = gDS->AddMemorySpace (
+        EfiGcdMemoryTypeMemoryMappedIo,
+        Instance->DeviceBaseAddress, RuntimeMmioRegionSize,
+        EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+        );
+    ASSERT_EFI_ERROR (Status);
+
+    Status = gDS->SetMemorySpaceAttributes (
+        Instance->DeviceBaseAddress, RuntimeMmioRegionSize,
+        EFI_MEMORY_UC | EFI_MEMORY_RUNTIME);
+    ASSERT_EFI_ERROR (Status);
+  }
 
   mFlashNvStorageVariableBase = FixedPcdGet32 (PcdFlashNvStorageVariableBase);
 
@@ -777,30 +779,32 @@ NorFlashFvbInitialize (
     }
   }
 
-  //
-  // The driver implementing the variable read service can now be dispatched;
-  // the varstore headers are in place.
-  //
-  Status = gBS->InstallProtocolInterface (
-                  &gImageHandle,
-                  &gEdkiiNvVarStoreFormattedGuid,
-                  EFI_NATIVE_INTERFACE,
-                  NULL
-                  );
-  ASSERT_EFI_ERROR (Status);
-
-  //
-  // Register for the virtual address change event
-  //
-  Status = gBS->CreateEventEx (
-                  EVT_NOTIFY_SIGNAL,
-                  TPL_NOTIFY,
-                  FvbVirtualNotifyEvent,
-                  NULL,
-                  &gEfiEventVirtualAddressChangeGuid,
-                  &mFvbVirtualAddrChangeEvent
-                  );
-  ASSERT_EFI_ERROR (Status);
+  if (!InMm ()) {
+    //
+    // The driver implementing the variable read service can now be dispatched;
+    // the varstore headers are in place.
+    //
+    Status = gBS->InstallProtocolInterface (
+                    &gImageHandle,
+                    &gEdkiiNvVarStoreFormattedGuid,
+                    EFI_NATIVE_INTERFACE,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Register for the virtual address change event
+    //
+    Status = gBS->CreateEventEx (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    FvbVirtualNotifyEvent,
+                    NULL,
+                    &gEfiEventVirtualAddressChangeGuid,
+                    &mFvbVirtualAddrChangeEvent
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
 
   return Status;
 }
diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c
new file mode 100644
index 0000000..0dbfc98
--- /dev/null
+++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c
@@ -0,0 +1,1339 @@
+/** @file  NorFlashDxe.c
+
+  Copyright (c) 2011 - 2019, ARM Ltd. All rights reserved.<BR>
+
+  This program and the accompanying materials
+  are licensed and made available under the terms and conditions of the BSD License
+  which 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 <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/PcdLib.h>
+
+#include "NorFlashDxe.h"
+
+
+//
+// Global variable declarations
+//
+NOR_FLASH_INSTANCE **mNorFlashInstances;
+UINT32               mNorFlashDeviceCount;
+
+NOR_FLASH_INSTANCE  mNorFlashInstanceTemplate = {
+  NOR_FLASH_SIGNATURE, // Signature
+  NULL, // Handle ... NEED TO BE FILLED
+
+  0, // DeviceBaseAddress ... NEED TO BE FILLED
+  0, // RegionBaseAddress ... NEED TO BE FILLED
+  0, // Size ... NEED TO BE FILLED
+  0, // StartLba
+
+  {
+    EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision
+    NULL, // Media ... NEED TO BE FILLED
+    NorFlashBlockIoReset, // Reset;
+    NorFlashBlockIoReadBlocks,          // ReadBlocks
+    NorFlashBlockIoWriteBlocks,         // WriteBlocks
+    NorFlashBlockIoFlushBlocks          // FlushBlocks
+  }, // BlockIoProtocol
+
+  {
+    0, // MediaId ... NEED TO BE FILLED
+    FALSE, // RemovableMedia
+    TRUE, // MediaPresent
+    FALSE, // LogicalPartition
+    FALSE, // ReadOnly
+    FALSE, // WriteCaching;
+    0, // BlockSize ... NEED TO BE FILLED
+    4, //  IoAlign
+    0, // LastBlock ... NEED TO BE FILLED
+    0, // LowestAlignedLba
+    1, // LogicalBlocksPerPhysicalBlock
+  }, //Media;
+
+  {
+    EFI_DISK_IO_PROTOCOL_REVISION, // Revision
+    NorFlashDiskIoReadDisk,        // ReadDisk
+    NorFlashDiskIoWriteDisk        // WriteDisk
+  },
+
+  {
+    FvbGetAttributes, // GetAttributes
+    FvbSetAttributes, // SetAttributes
+    FvbGetPhysicalAddress,  // GetPhysicalAddress
+    FvbGetBlockSize,  // GetBlockSize
+    FvbRead,  // Read
+    FvbWrite, // Write
+    FvbEraseBlocks, // EraseBlocks
+    NULL, //ParentHandle
+  }, //  FvbProtoccol;
+  NULL, // ShadowBuffer
+  {
+    {
+      {
+        HARDWARE_DEVICE_PATH,
+        HW_VENDOR_DP,
+        {
+          (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)),
+          (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8)
+        }
+      },
+      { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED
+    },
+    0, // Index
+    {
+      END_DEVICE_PATH_TYPE,
+      END_ENTIRE_DEVICE_PATH_SUBTYPE,
+      { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
+    }
+    } // DevicePath
+};
+
+EFI_STATUS
+NorFlashCreateInstance (
+  IN UINTN                  NorFlashDeviceBase,
+  IN UINTN                  NorFlashRegionBase,
+  IN UINTN                  NorFlashSize,
+  IN UINT32                 Index,
+  IN UINT32                 BlockSize,
+  IN BOOLEAN                SupportFvb,
+  OUT NOR_FLASH_INSTANCE**  NorFlashInstance
+  )
+{
+  EFI_STATUS Status;
+  NOR_FLASH_INSTANCE* Instance;
+
+  ASSERT(NorFlashInstance != NULL);
+
+  Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);
+  if (Instance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Instance->DeviceBaseAddress = NorFlashDeviceBase;
+  Instance->RegionBaseAddress = NorFlashRegionBase;
+  Instance->Size = NorFlashSize;
+
+  Instance->BlockIoProtocol.Media = &Instance->Media;
+  Instance->Media.MediaId = Index;
+  Instance->Media.BlockSize = BlockSize;
+  Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;
+
+  CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
+  Instance->DevicePath.Index = (UINT8)Index;
+
+  Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;
+  if (Instance->ShadowBuffer == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  if (SupportFvb) {
+    NorFlashFvbInitialize (Instance);
+
+    //Install DevicePath Protocol
+    Status = gMmst->MmInstallProtocolInterface (
+                      &Instance->Handle,
+                      &gEfiDevicePathProtocolGuid,
+                      EFI_NATIVE_INTERFACE,
+                      &Instance->DevicePath
+                      );
+    if (EFI_ERROR(Status)) {
+      FreePool (Instance);
+      return Status;
+    }
+    //Install BlockIo Protocol
+    Status = gMmst->MmInstallProtocolInterface (
+                      &Instance->Handle,
+                      &gEfiBlockIoProtocolGuid,
+                      EFI_NATIVE_INTERFACE,
+                      &Instance->BlockIoProtocol
+                      );
+    if (EFI_ERROR(Status)) {
+      FreePool (Instance);
+      return Status;
+    }
+    //Install FirmwareVolumeBlock Protocol
+    Status = gMmst->MmInstallProtocolInterface (
+                      &Instance->Handle,
+                      &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+                      EFI_NATIVE_INTERFACE,
+                      &Instance->FvbProtocol
+                      );
+    if (EFI_ERROR(Status)) {
+      FreePool (Instance);
+      return Status;
+    }
+  } else {
+    //Install DevicePath Protocol
+    Status = gMmst->MmInstallProtocolInterface (
+                      &Instance->Handle,
+                      &gEfiDevicePathProtocolGuid,
+                      EFI_NATIVE_INTERFACE,
+                      &Instance->DevicePath
+                      );
+    if (EFI_ERROR(Status)) {
+      FreePool (Instance);
+      return Status;
+    }
+    //Install BlockIo Protocol
+    Status = gMmst->MmInstallProtocolInterface (
+                      &Instance->Handle,
+                      &gEfiBlockIoProtocolGuid,
+                      EFI_NATIVE_INTERFACE,
+                      &Instance->BlockIoProtocol
+                      );
+    if (EFI_ERROR(Status)) {
+      FreePool (Instance);
+      return Status;
+    }
+    //Install DiskIO Protocol
+    Status = gMmst->MmInstallProtocolInterface (
+                      &Instance->Handle,
+                      &gEfiDiskIoProtocolGuid,
+                      EFI_NATIVE_INTERFACE,
+                      &Instance->DiskIoProtocol
+                      );
+    if (EFI_ERROR(Status)) {
+      FreePool (Instance);
+      return Status;
+    }
+  }
+
+  *NorFlashInstance = Instance;
+  return Status;
+}
+
+UINT32
+NorFlashReadStatusRegister (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  SR_Address
+  )
+{
+  // Prepare to read the status register
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);
+  return MmioRead32 (Instance->DeviceBaseAddress);
+}
+
+STATIC
+BOOLEAN
+NorFlashBlockIsLocked (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  BlockAddress
+  )
+{
+  UINT32                LockStatus;
+
+  // Send command for reading device id
+  SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
+
+  // Read block lock status
+  LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));
+
+  // Decode block lock status
+  LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);
+
+  if ((LockStatus & 0x2) != 0) {
+    DEBUG((EFI_D_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
+  }
+
+  return ((LockStatus & 0x1) != 0);
+}
+
+STATIC
+EFI_STATUS
+NorFlashUnlockSingleBlock (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  BlockAddress
+  )
+{
+  UINT32                LockStatus;
+
+  // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
+  // and to protect shared data structures.
+
+  if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) {
+    do {
+      // Request a lock setup
+      SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
+
+      // Request an unlock
+      SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
+
+      // Send command for reading device id
+      SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
+
+      // Read block lock status
+      LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));
+
+      // Decode block lock status
+      LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);
+    } while ((LockStatus & 0x1) == 1);
+  } else {
+    // Request a lock setup
+    SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
+
+    // Request an unlock
+    SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
+
+    // Wait until the status register gives us the all clear
+    do {
+      LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);
+    } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
+  }
+
+  // Put device back into Read Array mode
+  SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);
+
+  DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress));
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+NorFlashUnlockSingleBlockIfNecessary (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  BlockAddress
+  )
+{
+  EFI_STATUS Status;
+
+  Status = EFI_SUCCESS;
+
+  if (NorFlashBlockIsLocked (Instance, BlockAddress) == TRUE) {
+    Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);
+  }
+
+  return Status;
+}
+
+
+/**
+ * The following function presumes that the block has already been unlocked.
+ **/
+STATIC
+EFI_STATUS
+NorFlashEraseSingleBlock (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  BlockAddress
+  )
+{
+  EFI_STATUS            Status;
+  UINT32                StatusRegister;
+
+  Status = EFI_SUCCESS;
+
+  // Request a block erase and then confirm it
+  SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);
+  SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);
+
+  // Wait until the status register gives us the all clear
+  do {
+    StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);
+  } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
+
+  if (StatusRegister & P30_SR_BIT_VPP) {
+    DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {
+    DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (StatusRegister & P30_SR_BIT_ERASE) {
+    DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
+    // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
+    DEBUG((EFI_D_INFO,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));
+    Status = EFI_WRITE_PROTECTED;
+  }
+
+  if (EFI_ERROR(Status)) {
+    // Clear the Status Register
+    SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+  }
+
+  // Put device back into Read Array mode
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+
+  return Status;
+}
+
+/**
+ * This function unlock and erase an entire NOR Flash block.
+ **/
+EFI_STATUS
+NorFlashUnlockAndEraseSingleBlock (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  BlockAddress
+  )
+{
+  EFI_STATUS      Status;
+  UINTN           Index;
+
+  Index = 0;
+  // The block erase might fail a first time (SW bug ?). Retry it ...
+  do {
+    // Unlock the block if we have to
+    Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+    Status = NorFlashEraseSingleBlock (Instance, BlockAddress);
+    Index++;
+  } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));
+
+  if (Index == NOR_FLASH_ERASE_RETRY) {
+    DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index));
+  }
+
+
+  return Status;
+}
+
+
+STATIC
+EFI_STATUS
+NorFlashWriteSingleWord (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  WordAddress,
+  IN UINT32                 WriteData
+  )
+{
+  EFI_STATUS            Status;
+  UINT32                StatusRegister;
+
+  Status = EFI_SUCCESS;
+
+  // Request a write single word command
+  SEND_NOR_COMMAND(WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);
+
+  // Store the word into NOR Flash;
+  MmioWrite32 (WordAddress, WriteData);
+
+  // Wait for the write to complete and then check for any errors; i.e. check the Status Register
+  do {
+    // Prepare to read the status register
+    StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);
+    // The chip is busy while the WRITE bit is not asserted
+  } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
+
+
+  // Perform a full status check:
+  // Mask the relevant bits of Status Register.
+  // Everything should be zero, if not, we have a problem
+
+  if (StatusRegister & P30_SR_BIT_VPP) {
+    DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (StatusRegister & P30_SR_BIT_PROGRAM) {
+    DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
+    DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (!EFI_ERROR(Status)) {
+    // Clear the Status Register
+    SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+  }
+
+  // Put device back into Read Array mode
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+
+  return Status;
+}
+
+/*
+ * Writes data to the NOR Flash using the Buffered Programming method.
+ *
+ * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
+ * Therefore this function will only handle buffers up to 32 words or 128 bytes.
+ * To deal with larger buffers, call this function again.
+ *
+ * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
+ * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
+ *
+ * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
+ * then programming time is doubled and power consumption is increased.
+ * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
+ * i.e. the last 4 bits of the target start address must be zero: 0x......00
+ */
+EFI_STATUS
+NorFlashWriteBuffer (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN UINTN                  TargetAddress,
+  IN UINTN                  BufferSizeInBytes,
+  IN UINT32                 *Buffer
+  )
+{
+  EFI_STATUS            Status;
+  UINTN                 BufferSizeInWords;
+  UINTN                 Count;
+  volatile UINT32       *Data;
+  UINTN                 WaitForBuffer;
+  BOOLEAN               BufferAvailable;
+  UINT32                StatusRegister;
+
+  WaitForBuffer   = MAX_BUFFERED_PROG_ITERATIONS;
+  BufferAvailable = FALSE;
+
+  // Check that the target address does not cross a 32-word boundary.
+  if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Check there are some data to program
+  if (BufferSizeInBytes == 0) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
+  if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // Check that the buffer size is a multiple of 32-bit words
+  if ((BufferSizeInBytes % 4) != 0) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // Pre-programming conditions checked, now start the algorithm.
+
+  // Prepare the data destination address
+  Data = (UINT32 *)TargetAddress;
+
+  // Check the availability of the buffer
+  do {
+    // Issue the Buffered Program Setup command
+    SEND_NOR_COMMAND(TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);
+
+    // Read back the status register bit#7 from the same address
+    if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {
+      BufferAvailable = TRUE;
+    }
+
+    // Update the loop counter
+    WaitForBuffer--;
+
+  } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));
+
+  // The buffer was not available for writing
+  if (WaitForBuffer == 0) {
+    Status = EFI_DEVICE_ERROR;
+    goto EXIT;
+  }
+
+  // From now on we work in 32-bit words
+  BufferSizeInWords = BufferSizeInBytes / (UINTN)4;
+
+  // Write the word count, which is (buffer_size_in_words - 1),
+  // because word count 0 means one word.
+  SEND_NOR_COMMAND(TargetAddress, 0, (BufferSizeInWords - 1));
+
+  // Write the data to the NOR Flash, advancing each address by 4 bytes
+  for(Count=0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {
+    MmioWrite32 ((UINTN)Data, *Buffer);
+  }
+
+  // Issue the Buffered Program Confirm command, to start the programming operation
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);
+
+  // Wait for the write to complete and then check for any errors; i.e. check the Status Register
+  do {
+    StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);
+    // The chip is busy while the WRITE bit is not asserted
+  } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
+
+
+  // Perform a full status check:
+  // Mask the relevant bits of Status Register.
+  // Everything should be zero, if not, we have a problem
+
+  Status          = EFI_SUCCESS;
+
+  if (StatusRegister & P30_SR_BIT_VPP) {
+    DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (StatusRegister & P30_SR_BIT_PROGRAM) {
+    DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
+    DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  if (!EFI_ERROR(Status)) {
+    // Clear the Status Register
+    SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
+  }
+
+EXIT:
+  // Put device back into Read Array mode
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+NorFlashWriteFullBlock (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN EFI_LBA                Lba,
+  IN UINT32                 *DataBuffer,
+  IN UINT32                 BlockSizeInWords
+  )
+{
+  EFI_STATUS    Status;
+  UINTN         WordAddress;
+  UINT32        WordIndex;
+  UINTN         BufferIndex;
+  UINTN         BlockAddress;
+  UINTN         BuffersInBlock;
+  UINTN         RemainingWords;
+  UINTN         Cnt;
+
+  Status = EFI_SUCCESS;
+
+  // Get the physical address of the block
+  BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);
+
+  // Start writing from the first address at the start of the block
+  WordAddress = BlockAddress;
+
+  Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);
+  if (EFI_ERROR(Status)) {
+    DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));
+    goto EXIT;
+  }
+
+  // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
+
+  // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
+  if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
+
+    // First, break the entire block into buffer-sized chunks.
+    BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
+
+    // Then feed each buffer chunk to the NOR Flash
+    // If a buffer does not contain any data, don't write it.
+    for(BufferIndex=0;
+         BufferIndex < BuffersInBlock;
+         BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
+      ) {
+      // Check the buffer to see if it contains any data (not set all 1s).
+      for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
+        if (~DataBuffer[Cnt] != 0 ) {
+          // Some data found, write the buffer.
+          Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,
+                                        DataBuffer);
+          if (EFI_ERROR(Status)) {
+            goto EXIT;
+          }
+          break;
+        }
+      }
+    }
+
+    // Finally, finish off any remaining words that are less than the maximum size of the buffer
+    RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
+
+    if(RemainingWords != 0) {
+      Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
+      if (EFI_ERROR(Status)) {
+        goto EXIT;
+      }
+    }
+
+  } else {
+    // For now, use the single word programming algorithm
+    // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
+    // i.e. which ends in the range 0x......01 - 0x......7F.
+    for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
+      Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);
+      if (EFI_ERROR(Status)) {
+        goto EXIT;
+      }
+    }
+  }
+
+EXIT:
+  if (EFI_ERROR(Status)) {
+    DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
+  }
+  return Status;
+}
+
+
+EFI_STATUS
+NorFlashWriteBlocks (
+  IN NOR_FLASH_INSTANCE     *Instance,
+  IN EFI_LBA                Lba,
+  IN UINTN                  BufferSizeInBytes,
+  IN VOID                   *Buffer
+  )
+{
+  UINT32          *pWriteBuffer;
+  EFI_STATUS      Status = EFI_SUCCESS;
+  EFI_LBA         CurrentBlock;
+  UINT32          BlockSizeInWords;
+  UINT32          NumBlocks;
+  UINT32          BlockCount;
+
+  // The buffer must be valid
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if(Instance->Media.ReadOnly == TRUE) {
+    return EFI_WRITE_PROTECTED;
+  }
+
+  // We must have some bytes to read
+  DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));
+  if(BufferSizeInBytes == 0) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // The size of the buffer must be a multiple of the block size
+  DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize));
+  if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // All blocks must be within the device
+  NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
+
+  DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba));
+
+  if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
+    DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  BlockSizeInWords = Instance->Media.BlockSize / 4;
+
+  // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
+  // to a proper data type, so use *ReadBuffer
+  pWriteBuffer = (UINT32 *)Buffer;
+
+  CurrentBlock = Lba;
+  for (BlockCount=0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {
+
+    DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));
+
+    Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);
+
+    if (EFI_ERROR(Status)) {
+      break;
+    }
+  }
+
+  DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));
+  return Status;
+}
+
+#define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0)
+
+/**
+  Copy Length bytes from Source to Destination, using aligned accesses only.
+  Note that this implementation uses memcpy() semantics rather then memmove()
+  semantics, i.e., SourceBuffer and DestinationBuffer should not overlap.
+
+  @param  DestinationBuffer The target of the copy request.
+  @param  SourceBuffer      The place to copy from.
+  @param  Length            The number of bytes to copy.
+
+  @return Destination
+
+**/
+STATIC
+VOID *
+AlignedCopyMem (
+  OUT     VOID                      *DestinationBuffer,
+  IN      CONST VOID                *SourceBuffer,
+  IN      UINTN                     Length
+  )
+{
+  UINT8             *Destination8;
+  CONST UINT8       *Source8;
+  UINT32            *Destination32;
+  CONST UINT32      *Source32;
+  UINT64            *Destination64;
+  CONST UINT64      *Source64;
+
+  if (BOTH_ALIGNED(DestinationBuffer, SourceBuffer, 8) && Length >= 8) {
+    Destination64 = DestinationBuffer;
+    Source64 = SourceBuffer;
+    while (Length >= 8) {
+      *Destination64++ = *Source64++;
+      Length -= 8;
+    }
+
+    Destination8 = (UINT8 *)Destination64;
+    Source8 = (CONST UINT8 *)Source64;
+  } else if (BOTH_ALIGNED(DestinationBuffer, SourceBuffer, 4) && Length >= 4) {
+    Destination32 = DestinationBuffer;
+    Source32 = SourceBuffer;
+    while (Length >= 4) {
+      *Destination32++ = *Source32++;
+      Length -= 4;
+    }
+
+    Destination8 = (UINT8 *)Destination32;
+    Source8 = (CONST UINT8 *)Source32;
+  } else {
+    Destination8 = DestinationBuffer;
+    Source8 = SourceBuffer;
+  }
+  while (Length-- != 0) {
+    *Destination8++ = *Source8++;
+  }
+  return DestinationBuffer;
+}
+
+EFI_STATUS
+NorFlashReadBlocks (
+  IN NOR_FLASH_INSTANCE   *Instance,
+  IN EFI_LBA              Lba,
+  IN UINTN                BufferSizeInBytes,
+  OUT VOID                *Buffer
+  )
+{
+  UINT32              NumBlocks;
+  UINTN               StartAddress;
+
+  DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
+      BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba));
+
+  // The buffer must be valid
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Return if we have not any byte to read
+  if (BufferSizeInBytes == 0) {
+    return EFI_SUCCESS;
+  }
+
+  // The size of the buffer must be a multiple of the block size
+  if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // All blocks must be within the device
+  NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
+
+  if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
+    DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the address to start reading from
+  StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
+                                        Lba,
+                                        Instance->Media.BlockSize
+                                       );
+
+  // Put the device into Read Array mode
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+
+  // Readout the data
+  AlignedCopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+NorFlashRead (
+  IN NOR_FLASH_INSTANCE   *Instance,
+  IN EFI_LBA              Lba,
+  IN UINTN                Offset,
+  IN UINTN                BufferSizeInBytes,
+  OUT VOID                *Buffer
+  )
+{
+  UINTN  StartAddress;
+
+  // The buffer must be valid
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Return if we have not any byte to read
+  if (BufferSizeInBytes == 0) {
+    return EFI_SUCCESS;
+  }
+
+  if (((Lba * Instance->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) {
+    DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  // Get the address to start reading from
+  StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
+                                        Lba,
+                                        Instance->Media.BlockSize
+                                       );
+
+  // Put the device into Read Array mode
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+
+  // Readout the data
+  AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
+
+  return EFI_SUCCESS;
+}
+
+/*
+  Write a full or portion of a block. It must not span block boundaries; that is,
+  Offset + *NumBytes <= Instance->Media.BlockSize.
+*/
+EFI_STATUS
+NorFlashWriteSingleBlock (
+  IN        NOR_FLASH_INSTANCE   *Instance,
+  IN        EFI_LBA               Lba,
+  IN        UINTN                 Offset,
+  IN OUT    UINTN                *NumBytes,
+  IN        UINT8                *Buffer
+  )
+{
+  EFI_STATUS  TempStatus;
+  UINT32      Tmp;
+  UINT32      TmpBuf;
+  UINT32      WordToWrite;
+  UINT32      Mask;
+  BOOLEAN     DoErase;
+  UINTN       BytesToWrite;
+  UINTN       CurOffset;
+  UINTN       WordAddr;
+  UINTN       BlockSize;
+  UINTN       BlockAddress;
+  UINTN       PrevBlockAddress;
+
+  PrevBlockAddress = 0;
+
+  DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
+
+  // Detect WriteDisabled state
+  if (Instance->Media.ReadOnly == TRUE) {
+    DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
+    // It is in WriteDisabled state, return an error right away
+    return EFI_ACCESS_DENIED;
+  }
+
+  // Cache the block size to avoid de-referencing pointers all the time
+  BlockSize = Instance->Media.BlockSize;
+
+  // The write must not span block boundaries.
+  // We need to check each variable individually because adding two large values together overflows.
+  if ( ( Offset               >= BlockSize ) ||
+       ( *NumBytes            >  BlockSize ) ||
+       ( (Offset + *NumBytes) >  BlockSize )    ) {
+    DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // We must have some bytes to write
+  if (*NumBytes == 0) {
+    DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  // Pick 128bytes as a good start for word operations as opposed to erasing the
+  // block and writing the data regardless if an erase is really needed.
+  // It looks like most individual NV variable writes are smaller than 128bytes.
+  if (*NumBytes <= 128) {
+    // Check to see if we need to erase before programming the data into NOR.
+    // If the destination bits are only changing from 1s to 0s we can just write.
+    // After a block is erased all bits in the block is set to 1.
+    // If any byte requires us to erase we just give up and rewrite all of it.
+    DoErase      = FALSE;
+    BytesToWrite = *NumBytes;
+    CurOffset    = Offset;
+
+    while (BytesToWrite > 0) {
+      // Read full word from NOR, splice as required. A word is the smallest
+      // unit we can write.
+      TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof(Tmp), &Tmp);
+      if (EFI_ERROR (TempStatus)) {
+        return EFI_DEVICE_ERROR;
+      }
+
+      // Physical address of word in NOR to write.
+      WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
+                                                               Lba, BlockSize);
+      // The word of data that is to be written.
+      TmpBuf = *((UINT32*)(Buffer + (*NumBytes - BytesToWrite)));
+
+      // First do word aligned chunks.
+      if ((CurOffset & 0x3) == 0) {
+        if (BytesToWrite >= 4) {
+          // Is the destination still in 'erased' state?
+          if (~Tmp != 0) {
+            // Check to see if we are only changing bits to zero.
+            if ((Tmp ^ TmpBuf) & TmpBuf) {
+              DoErase = TRUE;
+              break;
+            }
+          }
+          // Write this word to NOR
+          WordToWrite = TmpBuf;
+          CurOffset += sizeof(TmpBuf);
+          BytesToWrite -= sizeof(TmpBuf);
+        } else {
+          // BytesToWrite < 4. Do small writes and left-overs
+          Mask = ~((~0) << (BytesToWrite * 8));
+          // Mask out the bytes we want.
+          TmpBuf &= Mask;
+          // Is the destination still in 'erased' state?
+          if ((Tmp & Mask) != Mask) {
+            // Check to see if we are only changing bits to zero.
+            if ((Tmp ^ TmpBuf) & TmpBuf) {
+              DoErase = TRUE;
+              break;
+            }
+          }
+          // Merge old and new data. Write merged word to NOR
+          WordToWrite = (Tmp & ~Mask) | TmpBuf;
+          CurOffset += BytesToWrite;
+          BytesToWrite = 0;
+        }
+      } else {
+        // Do multiple words, but starting unaligned.
+        if (BytesToWrite > (4 - (CurOffset & 0x3))) {
+          Mask = ((~0) << ((CurOffset & 0x3) * 8));
+          // Mask out the bytes we want.
+          TmpBuf &= Mask;
+          // Is the destination still in 'erased' state?
+          if ((Tmp & Mask) != Mask) {
+            // Check to see if we are only changing bits to zero.
+            if ((Tmp ^ TmpBuf) & TmpBuf) {
+              DoErase = TRUE;
+              break;
+            }
+          }
+          // Merge old and new data. Write merged word to NOR
+          WordToWrite = (Tmp & ~Mask) | TmpBuf;
+          BytesToWrite -= (4 - (CurOffset & 0x3));
+          CurOffset += (4 - (CurOffset & 0x3));
+        } else {
+          // Unaligned and fits in one word.
+          Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);
+          // Mask out the bytes we want.
+          TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;
+          // Is the destination still in 'erased' state?
+          if ((Tmp & Mask) != Mask) {
+            // Check to see if we are only changing bits to zero.
+            if ((Tmp ^ TmpBuf) & TmpBuf) {
+              DoErase = TRUE;
+              break;
+            }
+          }
+          // Merge old and new data. Write merged word to NOR
+          WordToWrite = (Tmp & ~Mask) | TmpBuf;
+          CurOffset += BytesToWrite;
+          BytesToWrite = 0;
+        }
+      }
+
+      //
+      // Write the word to NOR.
+      //
+
+      BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
+      if (BlockAddress != PrevBlockAddress) {
+        TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
+        if (EFI_ERROR (TempStatus)) {
+          return EFI_DEVICE_ERROR;
+        }
+        PrevBlockAddress = BlockAddress;
+      }
+      TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);
+      if (EFI_ERROR (TempStatus)) {
+        return EFI_DEVICE_ERROR;
+      }
+    }
+    // Exit if we got here and could write all the data. Otherwise do the
+    // Erase-Write cycle.
+    if (!DoErase) {
+      return EFI_SUCCESS;
+    }
+  }
+
+  // Check we did get some memory. Buffer is BlockSize.
+  if (Instance->ShadowBuffer == NULL) {
+    DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Read NOR Flash data into shadow buffer
+  TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
+  if (EFI_ERROR (TempStatus)) {
+    // Return one of the pre-approved error statuses
+    return EFI_DEVICE_ERROR;
+  }
+
+  // Put the data at the appropriate location inside the buffer area
+  CopyMem ((VOID*)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
+
+  // Write the modified buffer back to the NorFlash
+  TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
+  if (EFI_ERROR (TempStatus)) {
+    // Return one of the pre-approved error statuses
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/*
+  Although DiskIoDxe will automatically install the DiskIO protocol whenever
+  we install the BlockIO protocol, its implementation is sub-optimal as it reads
+  and writes entire blocks using the BlockIO protocol. In fact we can access
+  NOR flash with a finer granularity than that, so we can improve performance
+  by directly producing the DiskIO protocol.
+*/
+
+/**
+  Read BufferSize bytes from Offset into Buffer.
+
+  @param  This                  Protocol instance pointer.
+  @param  MediaId               Id of the media, changes every time the media is replaced.
+  @param  Offset                The starting byte offset to read from
+  @param  BufferSize            Size of Buffer
+  @param  Buffer                Buffer containing read data
+
+  @retval EFI_SUCCESS           The data was read correctly from the device.
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
+  @retval EFI_NO_MEDIA          There is no media in the device.
+  @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
+  @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
+                                valid for the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NorFlashDiskIoReadDisk (
+  IN EFI_DISK_IO_PROTOCOL         *This,
+  IN UINT32                       MediaId,
+  IN UINT64                       DiskOffset,
+  IN UINTN                        BufferSize,
+  OUT VOID                        *Buffer
+  )
+{
+  NOR_FLASH_INSTANCE *Instance;
+  UINT32              BlockSize;
+  UINT32              BlockOffset;
+  EFI_LBA             Lba;
+
+  Instance = INSTANCE_FROM_DISKIO_THIS(This);
+
+  if (MediaId != Instance->Media.MediaId) {
+    return EFI_MEDIA_CHANGED;
+  }
+
+  BlockSize = Instance->Media.BlockSize;
+  Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
+
+  return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);
+}
+
+/**
+  Writes a specified number of bytes to a device.
+
+  @param  This       Indicates a pointer to the calling context.
+  @param  MediaId    ID of the medium to be written.
+  @param  Offset     The starting byte offset on the logical block I/O device to write.
+  @param  BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
+  @param  Buffer     A pointer to the buffer containing the data to be written.
+
+  @retval EFI_SUCCESS           The data was written correctly to the device.
+  @retval EFI_WRITE_PROTECTED   The device can not be written to.
+  @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.
+  @retval EFI_NO_MEDIA          There is no media in the device.
+  @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
+  @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
+                                 valid for the device.
+
+**/
+EFI_STATUS
+EFIAPI
+NorFlashDiskIoWriteDisk (
+  IN EFI_DISK_IO_PROTOCOL         *This,
+  IN UINT32                       MediaId,
+  IN UINT64                       DiskOffset,
+  IN UINTN                        BufferSize,
+  IN VOID                         *Buffer
+  )
+{
+  NOR_FLASH_INSTANCE *Instance;
+  UINT32              BlockSize;
+  UINT32              BlockOffset;
+  EFI_LBA             Lba;
+  UINTN               RemainingBytes;
+  UINTN               WriteSize;
+  EFI_STATUS          Status;
+
+  Instance = INSTANCE_FROM_DISKIO_THIS(This);
+
+  if (MediaId != Instance->Media.MediaId) {
+    return EFI_MEDIA_CHANGED;
+  }
+
+  BlockSize = Instance->Media.BlockSize;
+  Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
+
+  RemainingBytes = BufferSize;
+
+  // Write either all the remaining bytes, or the number of bytes that bring
+  // us up to a block boundary, whichever is less.
+  // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
+  // block boundary (even if it is already on one).
+  WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);
+
+  do {
+    if (WriteSize == BlockSize) {
+      // Write a full block
+      Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));
+    } else {
+      // Write a partial block
+      Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);
+    }
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    // Now continue writing either all the remaining bytes or single blocks.
+    RemainingBytes -= WriteSize;
+    Buffer = (UINT8 *) Buffer + WriteSize;
+    Lba++;
+    BlockOffset = 0;
+    WriteSize = MIN (RemainingBytes, BlockSize);
+  } while (RemainingBytes);
+
+  return Status;
+}
+
+EFI_STATUS
+NorFlashReset (
+  IN  NOR_FLASH_INSTANCE *Instance
+  )
+{
+  // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
+  SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
+  return EFI_SUCCESS;
+}
+
+/**
+  Fixup internal data so that EFI can be call in virtual mode.
+  Call the passed in Child Notify event and convert any pointers in
+  lib to virtual mode.
+
+  @param[in]    Event   The Event that is being processed
+  @param[in]    Context Event Context
+**/
+VOID
+EFIAPI
+NorFlashVirtualNotifyEvent (
+  IN EFI_EVENT        Event,
+  IN VOID             *Context
+  )
+{
+  UINTN Index;
+
+  for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);
+
+    // Convert BlockIo protocol
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);
+
+    // Convert Fvb
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
+    EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);
+
+    if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
+      EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer);
+    }
+  }
+
+  return;
+}
+
+EFI_STATUS
+EFIAPI
+StandaloneMmNorFlashInitialise (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_MM_SYSTEM_TABLE   *SystemTable
+  )
+{
+  EFI_STATUS              Status;
+  UINT32                  Index;
+  NOR_FLASH_DESCRIPTION*  NorFlashDevices;
+  BOOLEAN                 ContainVariableStorage;
+
+  Status = NorFlashPlatformInitialization ();
+  if (EFI_ERROR(Status)) {
+    DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
+    return Status;
+  }
+
+  Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
+  if (EFI_ERROR(Status)) {
+    DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
+    return Status;
+  }
+
+  mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
+
+  for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
+    // Check if this NOR Flash device contain the variable storage region
+    ContainVariableStorage =
+        (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
+        (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
+
+    Status = NorFlashCreateInstance (
+      NorFlashDevices[Index].DeviceBaseAddress,
+      NorFlashDevices[Index].RegionBaseAddress,
+      NorFlashDevices[Index].Size,
+      Index,
+      NorFlashDevices[Index].BlockSize,
+      ContainVariableStorage,
+      &mNorFlashInstances[Index]
+    );
+    if (EFI_ERROR(Status)) {
+      DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));
+    }
+  }
+  return Status;
+}
diff --git a/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf
new file mode 100644
index 0000000..9f29bd4
--- /dev/null
+++ b/ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.inf
@@ -0,0 +1,76 @@
+#/** @file
+#
+#  Component description file for NorFlashDxe module
+#
+#  Copyright (c) 2018, ARM Limited. All rights reserved.
+#
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which 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                      = StandaloneMmNorFlash
+  FILE_GUID                      = 166F677B-DAC9-4AE4-AD34-2FF2504B0637
+  MODULE_TYPE                    = MM_STANDALONE
+  VERSION_STRING                 = 1.0
+  PI_SPECIFICATION_VERSION       = 0x00010032
+  ENTRY_POINT                    = StandaloneMmNorFlashInitialise
+
+[Sources.common]
+  NorFlashStandaloneMm.c
+  NorFlashFvbDxe.c
+  NorFlashBlockIoDxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  ArmPkg/ArmPkg.dec
+  StandaloneMmPkg/StandaloneMmPkg.dec
+
+[LibraryClasses]
+  StandaloneMmDriverEntryPoint
+  BaseMemoryLib
+  ArmSvcLib
+  ArmLib
+  IoLib
+  BaseLib
+  DebugLib
+  HobLib
+  MemoryAllocationLib
+  NorFlashPlatformLib
+  MmServicesTableLib
+
+[Guids]
+  gEfiSystemNvDataFvGuid
+  gEfiVariableGuid
+  gEfiAuthenticatedVariableGuid
+  gEfiEventVirtualAddressChangeGuid
+  gEdkiiNvVarStoreFormattedGuid     ## PRODUCES ## PROTOCOL
+
+[Protocols]
+  gEfiBlockIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiSmmFirmwareVolumeBlockProtocolGuid
+  gEfiDiskIoProtocolGuid
+
+[Pcd.common]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+  gArmPlatformTokenSpaceGuid.PcdNorFlashCheckBlockLocked
+
+[Depex]
+  TRUE
-- 
2.7.4



  parent reply	other threads:[~2019-01-02 13:14 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-02 13:13 [PATCH v2 00/11] Extend secure variable service to be usable from Standalone MM Jagadeesh Ujja
2019-01-02 13:13 ` [PATCH v2 01/11] StandaloneMmPkg: Remove MM_STANDALONE LIBRARY_CLASS from StandaloneMmCoreHobLib Jagadeesh Ujja
2019-01-02 13:13 ` [PATCH v2 02/11] StandaloneMmPkg: Adding the library packages used by MM_STANDALONE drivers Jagadeesh Ujja
2019-01-02 13:13 ` [PATCH v2 03/11] MdeModulePkg: Add a PCD to indicate Standalone MM supports secure variable Jagadeesh Ujja
2019-01-02 13:13 ` [PATCH v2 04/11] MdePkg/Include: Add StandaloneMmServicesTableLib library Jagadeesh Ujja
2019-01-03 11:03   ` Ard Biesheuvel
2019-01-03 16:14     ` Laszlo Ersek
2019-01-04 11:57       ` Ard Biesheuvel
2019-01-07 15:28         ` Laszlo Ersek
2019-01-07 17:33           ` Ard Biesheuvel
2019-01-07 18:50             ` Achin Gupta
2019-01-07 18:55               ` Ard Biesheuvel
2019-01-07 19:21                 ` Achin Gupta
2019-01-07 19:37                   ` Ard Biesheuvel
2019-01-08  1:11                     ` Laszlo Ersek
2019-01-08 13:27                       ` Ard Biesheuvel
2019-01-08 16:52                         ` Laszlo Ersek
2019-01-13 12:42                     ` Cohen, Eugene
2019-01-14 18:51                       ` Ard Biesheuvel
2019-01-02 13:13 ` [PATCH v2 05/11] MdeModulePkg/FaultTolerantWriteDxe: allow reusability as a MM driver Jagadeesh Ujja
2019-01-02 17:15   ` Ard Biesheuvel
2019-01-02 13:13 ` [PATCH v2 06/11] MdeModulePkg/Variable/RuntimeDxe: adapt for usability with MM Standalone Jagadeesh Ujja
2019-01-02 13:13 ` [PATCH v2 07/11] MdeModulePkg/Variable/RuntimeDxe: adapt as a MM Standalone driver Jagadeesh Ujja
2019-01-02 13:13 ` [PATCH v2 08/11] MdeModulePkg/VarCheckLib: allow MM_STANDALONE drivers to use this library Jagadeesh Ujja
2019-01-04 10:36   ` Ard Biesheuvel
2019-01-02 13:13 ` Jagadeesh Ujja [this message]
2019-01-02 13:13 ` [PATCH v2 10/11] SecurityPkg/AuthVariableLib: " Jagadeesh Ujja
2019-01-03  1:14   ` Zhang, Chao B
2019-01-03  6:15     ` Jagadeesh Ujja
2019-01-04 10:41       ` Ard Biesheuvel
2019-01-02 13:13 ` [PATCH v2 11/11] CryptoPkg/BaseCryptLib: " Jagadeesh Ujja
2019-01-04 10:35   ` Ard Biesheuvel

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=1546434828-24405-10-git-send-email-jagadeesh.ujja@arm.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