@@ -112,3 +112,9 @@
gAmdStyxTokenSpaceGuid.PcdParkingProtocolBase|0x8000E80000|UINT64|0x000b0001
gAmdStyxTokenSpaceGuid.PcdParkingProtocolSize|0x0000008000|UINT64|0x000b0002
+ # The original offset in memory of the NV store firmware volume, before
+ # relocating it to a dynamically allocated buffer. We need this to correlate
+ # flash accesses to the in-memory copy with LBAs in the actual SPI flash
+ gAmdStyxTokenSpaceGuid.PcdFlashNvStorageOriginalBase|0|UINT64|0x000c0000
+ # block size to use when invoking the ISCP FV methods
+ gAmdStyxTokenSpaceGuid.PcdFlashNvStorageBlockSize|0x1000|UINT32|0x000c0001
new file mode 100644
@@ -0,0 +1,503 @@
+/** @file
+
+ FV block I/O protocol driver for Styx SPI flash exposed via ISCP
+
+ Copyright (c) 2016, Linaro 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 <PiDxe.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/AmdIscpDxeProtocol.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+// TODO: replace with FixedPcdGet64 (PcdFdBaseAddress) once we have updated
+// the .FDF to let STYX_EFI.fd cover the entire SPI flash
+#define SPI_BASE 0x8000C80000UL
+#define BLOCK_SIZE (FixedPcdGet32 (PcdFlashNvStorageBlockSize))
+
+STATIC AMD_ISCP_DXE_PROTOCOL *mIscpDxeProtocol;
+STATIC EFI_HANDLE mStyxSpiFvHandle;
+
+STATIC EFI_EVENT mVirtualAddressChangeEvent;
+
+STATIC UINT64 mNvStorageBase;
+STATIC UINT64 mNvStorageLbaOffset;
+
+STATIC CONST UINT64 mNvStorageSize = FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
+ FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
+ FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize);
+
+
+/**
+ Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
+
+ This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
+ It convers pointer to new virtual address.
+
+ @param Event Event whose notification function is being invoked.
+ @param Context Pointer to the notification function's context.
+
+**/
+STATIC
+VOID
+EFIAPI
+VariableClassAddressChangeEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EfiConvertPointer (0x0, (VOID **)&mIscpDxeProtocol);
+ EfiConvertPointer (0x0, (VOID **)&mNvStorageBase);
+}
+
+/**
+ The GetAttributes() function retrieves the attributes and
+ current settings of the block.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the
+ attributes and current settings are
+ returned. Type EFI_FVB_ATTRIBUTES_2 is defined
+ in EFI_FIRMWARE_VOLUME_HEADER.
+
+ @retval EFI_SUCCESS The firmware volume attributes were
+ returned.
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeGetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ )
+{
+ *Attributes = EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled
+ EFI_FVB2_READ_STATUS | // Reads are currently enabled
+ EFI_FVB2_WRITE_STATUS | // Writes are currently enabled
+ EFI_FVB2_WRITE_ENABLED_CAP | // Writes may be enabled
+ EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY
+ EFI_FVB2_MEMORY_MAPPED | // It is memory mapped
+ EFI_FVB2_ERASE_POLARITY; // After erasure all bits take this value (i.e. '1')
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The SetAttributes() function sets configurable firmware volume
+ attributes and returns the new settings of the firmware volume.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Attributes On input, Attributes is a pointer to
+ EFI_FVB_ATTRIBUTES_2 that contains the
+ desired firmware volume settings. On
+ successful return, it contains the new
+ settings of the firmware volume. Type
+ EFI_FVB_ATTRIBUTES_2 is defined in
+ EFI_FIRMWARE_VOLUME_HEADER.
+
+ @retval EFI_SUCCESS The firmware volume attributes were returned.
+
+ @retval EFI_INVALID_PARAMETER The attributes requested are in
+ conflict with the capabilities
+ as declared in the firmware
+ volume header.
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeSetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
+ )
+{
+ return EFI_SUCCESS; // ignore for now
+}
+
+/**
+ The GetPhysicalAddress() function retrieves the base address of
+ a memory-mapped firmware volume. This function should be called
+ only for memory-mapped firmware volumes.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Address Pointer to a caller-allocated
+ EFI_PHYSICAL_ADDRESS that, on successful
+ return from GetPhysicalAddress(), contains the
+ base address of the firmware volume.
+
+ @retval EFI_SUCCESS The firmware volume base address was returned.
+
+ @retval EFI_UNSUPPORTED The firmware volume is not memory mapped.
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeGetPhysicalAddress (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ OUT EFI_PHYSICAL_ADDRESS *Address
+ )
+{
+ *Address = (EFI_PHYSICAL_ADDRESS)mNvStorageBase;
+ return EFI_SUCCESS;
+}
+
+/**
+ The GetBlockSize() function retrieves the size of the requested
+ block. It also returns the number of additional blocks with
+ the identical size. The GetBlockSize() function is used to
+ retrieve the block map (see EFI_FIRMWARE_VOLUME_HEADER).
+
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Lba Indicates the block for which to return the size.
+
+ @param BlockSize Pointer to a caller-allocated UINTN in which
+ the size of the block is returned.
+
+ @param NumberOfBlocks Pointer to a caller-allocated UINTN in
+ which the number of consecutive blocks,
+ starting with Lba, is returned. All
+ blocks in this range have a size of
+ BlockSize.
+
+
+ @retval EFI_SUCCESS The firmware volume base address was returned.
+
+ @retval EFI_INVALID_PARAMETER The requested LBA is out of range.
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeGetBlockSize (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ OUT UINTN *BlockSize,
+ OUT UINTN *NumberOfBlocks
+ )
+{
+ *BlockSize = BLOCK_SIZE;
+ *NumberOfBlocks = mNvStorageSize / BLOCK_SIZE - (UINTN)Lba;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the specified number of bytes into a buffer from the specified block.
+
+ The Read() function reads the requested number of bytes from the
+ requested block and stores them in the provided buffer.
+ Implementations should be mindful that the firmware volume
+ might be in the ReadDisabled state. If it is in this state,
+ the Read() function must return the status code
+ EFI_ACCESS_DENIED without modifying the contents of the
+ buffer. The Read() function must also prevent spanning block
+ boundaries. If a read is requested that would span a block
+ boundary, the read must read up to the boundary but not
+ beyond. The output parameter NumBytes must be set to correctly
+ indicate the number of bytes actually read. The caller must be
+ aware that a read may be partially completed.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Lba The starting logical block index
+ from which to read.
+
+ @param Offset Offset into the block at which to begin reading.
+
+ @param NumBytes Pointer to a UINTN. At entry, *NumBytes
+ contains the total size of the buffer. At
+ exit, *NumBytes contains the total number of
+ bytes read.
+
+ @param Buffer Pointer to a caller-allocated buffer that will
+ be used to hold the data that is read.
+
+ @retval EFI_SUCCESS The firmware volume was read successfully,
+ and contents are in Buffer.
+
+ @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA
+ boundary. On output, NumBytes
+ contains the total number of bytes
+ returned in Buffer.
+
+ @retval EFI_ACCESS_DENIED The firmware volume is in the
+ ReadDisabled state.
+
+ @retval EFI_DEVICE_ERROR The block device is not
+ functioning correctly and could
+ not be read.
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN OUT UINT8 *Buffer
+ )
+{
+ VOID *Base;
+
+ if (Offset + *NumBytes > BLOCK_SIZE) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Base = (VOID *)mNvStorageBase + Lba * BLOCK_SIZE + Offset;
+
+ //
+ // Copy the data from the in-memory image
+ //
+ CopyMem (Buffer, Base, *NumBytes);
+
+ DEBUG_CODE_BEGIN ();
+ EFI_STATUS Status;
+
+ if (!EfiAtRuntime ()) {
+ Lba += mNvStorageLbaOffset;
+ Status = mIscpDxeProtocol->AmdExecuteLoadFvBlockDxe (mIscpDxeProtocol,
+ Lba * BLOCK_SIZE + Offset, Buffer, *NumBytes);
+ ASSERT_EFI_ERROR (Status);
+
+ ASSERT (CompareMem (Base, Buffer, *NumBytes) == 0);
+ }
+ DEBUG_CODE_END ();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Writes the specified number of bytes from the input buffer to the block.
+
+ The Write() function writes the specified number of bytes from
+ the provided buffer to the specified block and offset. If the
+ firmware volume is sticky write, the caller must ensure that
+ all the bits of the specified range to write are in the
+ EFI_FVB_ERASE_POLARITY state before calling the Write()
+ function, or else the result will be unpredictable. This
+ unpredictability arises because, for a sticky-write firmware
+ volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY
+ state but cannot flip it back again. Before calling the
+ Write() function, it is recommended for the caller to first call
+ the EraseBlocks() function to erase the specified block to
+ write. A block erase cycle will transition bits from the
+ (NOT)EFI_FVB_ERASE_POLARITY state back to the
+ EFI_FVB_ERASE_POLARITY state. Implementations should be
+ mindful that the firmware volume might be in the WriteDisabled
+ state. If it is in this state, the Write() function must
+ return the status code EFI_ACCESS_DENIED without modifying the
+ contents of the firmware volume. The Write() function must
+ also prevent spanning block boundaries. If a write is
+ requested that spans a block boundary, the write must store up
+ to the boundary but not beyond. The output parameter NumBytes
+ must be set to correctly indicate the number of bytes actually
+ written. The caller must be aware that a write may be
+ partially completed. All writes, partial or otherwise, must be
+ fully flushed to the hardware before the Write() service
+ returns.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
+
+ @param Lba The starting logical block index to write to.
+
+ @param Offset Offset into the block at which to begin writing.
+
+ @param NumBytes The pointer to a UINTN. At entry, *NumBytes
+ contains the total size of the buffer. At
+ exit, *NumBytes contains the total number of
+ bytes actually written.
+
+ @param Buffer The pointer to a caller-allocated buffer that
+ contains the source for the write.
+
+ @retval EFI_SUCCESS The firmware volume was written successfully.
+
+ @retval EFI_BAD_BUFFER_SIZE The write was attempted across an
+ LBA boundary. On output, NumBytes
+ contains the total number of bytes
+ actually written.
+
+ @retval EFI_ACCESS_DENIED The firmware volume is in the
+ WriteDisabled state.
+
+ @retval EFI_DEVICE_ERROR The block device is malfunctioning
+ and could not be written.
+
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeWrite (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ VOID *Base;
+
+ if (Offset + *NumBytes > BLOCK_SIZE) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ Base = (VOID *)mNvStorageBase + Lba * BLOCK_SIZE + Offset;
+
+ Lba += mNvStorageLbaOffset;
+ Status = mIscpDxeProtocol->AmdExecuteUpdateFvBlockDxe (mIscpDxeProtocol,
+ Lba * BLOCK_SIZE + Offset, Buffer, *NumBytes);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Copy the data we just wrote to the in-memory copy of the
+ // firmware volume
+ //
+ CopyMem (Base, Buffer, *NumBytes);
+ }
+ return Status;
+}
+
+/**
+ Erases and initializes a firmware volume block.
+
+ The EraseBlocks() function erases one or more blocks as denoted
+ by the variable argument list. The entire parameter list of
+ blocks must be verified before erasing any blocks. If a block is
+ requested that does not exist within the associated firmware
+ volume (it has a larger index than the last block of the
+ firmware volume), the EraseBlocks() function must return the
+ status code EFI_INVALID_PARAMETER without modifying the contents
+ of the firmware volume. Implementations should be mindful that
+ the firmware volume might be in the WriteDisabled state. If it
+ is in this state, the EraseBlocks() function must return the
+ status code EFI_ACCESS_DENIED without modifying the contents of
+ the firmware volume. All calls to EraseBlocks() must be fully
+ flushed to the hardware before the EraseBlocks() service
+ returns.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL
+ instance.
+
+ @param ... The variable argument list is a list of tuples.
+ Each tuple describes a range of LBAs to erase
+ and consists of the following:
+ - An EFI_LBA that indicates the starting LBA
+ - A UINTN that indicates the number of blocks to
+ erase.
+
+ The list is terminated with an
+ EFI_LBA_LIST_TERMINATOR. For example, the
+ following indicates that two ranges of blocks
+ (5-7 and 10-11) are to be erased: EraseBlocks
+ (This, 5, 3, 10, 2, EFI_LBA_LIST_TERMINATOR);
+
+ @retval EFI_SUCCESS The erase request successfully
+ completed.
+
+ @retval EFI_ACCESS_DENIED The firmware volume is in the
+ WriteDisabled state.
+ @retval EFI_DEVICE_ERROR The block device is not functioning
+ correctly and could not be written.
+ The firmware device may have been
+ partially erased.
+ @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
+ in the variable argument list do
+ not exist in the firmware volume.
+
+**/
+STATIC
+EFI_STATUS
+StyxSpiFvDxeErase (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
+ ...
+ )
+{
+ VA_LIST Args;
+ EFI_LBA Start;
+ UINTN Length;
+ EFI_STATUS Status;
+
+ VA_START (Args, This);
+
+ for (Start = VA_ARG (Args, EFI_LBA);
+ Start != EFI_LBA_LIST_TERMINATOR;
+ Start = VA_ARG (Args, EFI_LBA)) {
+ Start += mNvStorageLbaOffset;
+ Length = VA_ARG (Args, UINTN);
+ Status = mIscpDxeProtocol->AmdExecuteEraseFvBlockDxe (mIscpDxeProtocol,
+ (Start + mNvStorageLbaOffset) * BLOCK_SIZE,
+ Length * BLOCK_SIZE);
+ if (!EFI_ERROR (Status)) {
+ SetMem64 ((VOID *)mNvStorageBase + Start * BLOCK_SIZE,
+ Length * BLOCK_SIZE, ~0UL);
+ }
+ }
+
+ VA_END (Args);
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL mStyxSpiFvProtocol = {
+ StyxSpiFvDxeGetAttributes,
+ StyxSpiFvDxeSetAttributes,
+ StyxSpiFvDxeGetPhysicalAddress,
+ StyxSpiFvDxeGetBlockSize,
+ StyxSpiFvDxeRead,
+ StyxSpiFvDxeWrite,
+ StyxSpiFvDxeErase
+};
+
+EFI_STATUS
+EFIAPI
+StyxSpiFvDxeInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ mNvStorageBase = PcdGet64 (PcdFlashNvStorageVariableBase64);
+ mNvStorageLbaOffset = (FixedPcdGet64 (PcdFlashNvStorageOriginalBase) -
+ SPI_BASE) / BLOCK_SIZE;
+
+ DEBUG ((EFI_D_INFO,
+ "%a: Using NV store FV in-memory copy at 0x%lx, LBA offset == 0x%lx\n",
+ __FUNCTION__, mNvStorageBase, mNvStorageLbaOffset));
+
+ Status = gBS->LocateProtocol (&gAmdIscpDxeProtocolGuid, NULL,
+ (VOID **)&mIscpDxeProtocol);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ VariableClassAddressChangeEvent, NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &mVirtualAddressChangeEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ return gBS->InstallMultipleProtocolInterfaces (&mStyxSpiFvHandle,
+ &gEfiFirmwareVolumeBlockProtocolGuid, &mStyxSpiFvProtocol,
+ NULL);
+}
new file mode 100644
@@ -0,0 +1,63 @@
+#/** @file
+#
+# Component description file for StyxSpiFvDxe module
+#
+# Copyright (c) 2016, Linaro 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.
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = StyxSpiFvDxe
+ FILE_GUID = F549FC67-C4A6-4E92-B9BA-297E1F82A1A8
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = StyxSpiFvDxeInitialize
+
+[Sources]
+ StyxSpiFvDxe.c
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ AmdModulePkg/AmdModulePkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OpenPlatformPkg/Platforms/AMD/Styx/AmdStyx.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiLib
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ UefiRuntimeLib
+ DxeServicesTableLib
+
+[FixedPcd]
+ gArmTokenSpaceGuid.PcdFdBaseAddress
+
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+ gAmdStyxTokenSpaceGuid.PcdFlashNvStorageOriginalBase
+ gAmdStyxTokenSpaceGuid.PcdFlashNvStorageBlockSize
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64
+
+[Protocols]
+ gAmdIscpDxeProtocolGuid ## CONSUMES
+ gEfiFirmwareVolumeBlockProtocolGuid ## PRODUCES
+
+[Depex]
+ gAmdIscpDxeProtocolGuid
This implements a firmware volume block I/O protocol driver on top of the SPI flash that is exposed by the SCP. Since the generic FTW and variable drivers cannot deal with non-memorymapped flash, this driver uses a RuntimeServicesData buffer to expose the varstore slice of the SPI flash, and keeps it in sync when the Write() or Erase() methods are called. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> --- Platforms/AMD/Styx/AmdStyx.dec | 6 + Platforms/AMD/Styx/Drivers/StyxSpiFvDxe/StyxSpiFvDxe.c | 503 ++++++++++++++++++++ Platforms/AMD/Styx/Drivers/StyxSpiFvDxe/StyxSpiFvDxe.inf | 63 +++ 3 files changed, 572 insertions(+)