summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlias Apalodimas <ilias.apalodimas@linaro.org>2020-04-09 19:37:24 +0300
committerIlias Apalodimas <ilias.apalodimas@linaro.org>2020-06-03 14:27:46 +0300
commit261215c4d46755869ec3df4ad1cbcd7dc1b4a712 (patch)
tree16e6add4681c93465cb3018f2f9c24fbc98bc392
parentf585872e60fefffe9eb825024bdb7413b2d83698 (diff)
add RPMB driver for OP-TEEstmm_reloc_combined
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
-rw-r--r--Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.c63
-rw-r--r--Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.inf44
-rw-r--r--Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.c781
-rw-r--r--Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.h52
-rw-r--r--Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.inf58
5 files changed, 998 insertions, 0 deletions
diff --git a/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.c b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.c
new file mode 100644
index 00000000..1a8cc2d3
--- /dev/null
+++ b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.c
@@ -0,0 +1,63 @@
+/** @file
+
+ Update the patched PCDs to their correct value
+
+ Copyright (c) 2020, Linaro Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+/**
+ * Patch the relevant PCDs of the RPMB driver with the correct address of the
+ * allocated memory
+ *
+**/
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MmServicesTableLib.h>
+#include <Library/PcdLib.h>
+
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+
+#include "OpTeeRpmbFv.h"
+
+EFI_STATUS
+EFIAPI
+FixPcdMemory (
+ VOID
+ )
+{
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol;
+ MEM_INSTANCE *Instance;
+ EFI_STATUS Status;
+ //
+ // Locate SmmFirmwareVolumeBlockProtocol
+ //
+
+ Status = gMmst->MmLocateProtocol (
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ NULL,
+ (VOID **) &FvbProtocol
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Instance = INSTANCE_FROM_FVB_THIS(FvbProtocol);
+ // Set the updated PCDs
+ PatchPcdSet32 (PcdFlashNvStorageVariableBase, Instance->MemBaseAddress);
+ PatchPcdSet32 (PcdFlashNvStorageFtwWorkingBase, Instance->MemBaseAddress +
+ PcdGet32 (PcdFlashNvStorageVariableSize));
+ PatchPcdSet32 (PcdFlashNvStorageFtwSpareBase, Instance->MemBaseAddress +
+ PcdGet32 (PcdFlashNvStorageVariableSize) +
+ PcdGet32 (PcdFlashNvStorageFtwWorkingSize));
+
+ DEBUG ((DEBUG_INFO, "%a: Fixup PcdFlashNvStorageVariableBase: 0x%lx\n",
+ __FUNCTION__, PcdGet32 (PcdFlashNvStorageVariableBase)));
+ DEBUG ((DEBUG_INFO, "%a: Fixup PcdFlashNvStorageFtwWorkingBase: 0x%lx\n",
+ __FUNCTION__, PcdGet32 (PcdFlashNvStorageFtwWorkingBase)));
+ DEBUG ((DEBUG_INFO, "%a: Fixup PcdFlashNvStorageFtwSpareBase: 0x%lx\n",
+ __FUNCTION__, PcdGet32 (PcdFlashNvStorageFtwSpareBase)));
+
+ return Status;
+}
diff --git a/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.inf b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.inf
new file mode 100644
index 00000000..318845fb
--- /dev/null
+++ b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/FixupPcd.inf
@@ -0,0 +1,44 @@
+## @file
+# Instance of Base Memory Library without assembly.
+#
+# Copyright (c) 2020, Linaro Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = FixupPcd
+ FILE_GUID = a827c337-a9c6-301b-aeb7-acbc95d8da22
+ MODULE_TYPE = BASE
+ VERSION_STRING = 0.1
+ LIBRARY_CLASS = RpmbPcdFixup|MM_STANDALONE
+ CONSTRUCTOR = FixPcdMemory
+
+[Sources]
+ FixupPcd.c
+ OpTeeRpmbFv.h
+
+[Packages]
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ MmServicesTableLib
+ PcdLib
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+
+[Protocols]
+ gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES
diff --git a/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.c b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.c
new file mode 100644
index 00000000..9cb8459c
--- /dev/null
+++ b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.c
@@ -0,0 +1,781 @@
+/** @file
+
+ FV block I/O protocol driver for RPMB eMMC accessed via OP-TEE
+
+ Copyright (c) 2020, Linaro Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/ArmSvcLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/MmServicesTableLib.h>
+#include <Library/PcdLib.h>
+
+#include <Protocol/FirmwareVolumeBlock.h>
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+#include <Guid/VariableFormat.h>
+
+#include "OpTeeRpmbFv.h"
+
+STATIC MEM_INSTANCE mInstance;
+/**
+ The SendSvc() function sends an svc call to OP-TEE
+
+ @param Act Stored in regs->x0
+
+ @param File Filename in OP-TEE
+ @param File Filename in OP-TEE
+
+ @retval EFI_SUCCESS The firmware volume attributes were
+ returned.
+
+**/
+
+STATIC
+UINTN
+SendSvc (
+ ARM_SVC_ARGS *SvcArgs
+ )
+{
+ ArmCallSvc (SvcArgs);
+ if (SvcArgs->Arg0) {
+ DEBUG ((DEBUG_ERROR, "%a: failed with 0x%x\n", __func__, SvcArgs->Arg0));
+ }
+
+ return SvcArgs->Arg0;
+}
+
+STATIC
+UINTN
+ReadWriteVariables (
+ UINTN SvcAct,
+ UINTN Addr,
+ UINTN NumBytes,
+ UINTN Offset
+ )
+{
+ ARM_SVC_ARGS SvcArgs;
+
+ ZeroMem (&SvcArgs, sizeof (SvcArgs));
+
+ SvcArgs.Arg0 = SvcAct;
+ SvcArgs.Arg1 = (UINTN) FILENAME;
+ SvcArgs.Arg2 = AsciiStrLen(FILENAME);
+ SvcArgs.Arg3 = Addr;
+ SvcArgs.Arg4 = NumBytes;
+ SvcArgs.Arg5 = Offset;
+
+ SendSvc (&SvcArgs);
+ if (SvcArgs.Arg0) {
+ DEBUG ((DEBUG_ERROR, "%a: Svc Call 0x%08x Addr: 0x%08x len: 0x%x Offset: 0x%x failed with 0x%x\n",
+ __func__, SvcAct, Addr, NumBytes, Offset, SvcArgs.Arg0));
+ }
+
+ return SvcArgs.Arg0;
+}
+
+/**
+ The GetAttributes() function retrieves the attributes and
+ current settings of the block.
+
+ @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK_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
+OpTeeRpmbFvGetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_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_BLOCK_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
+OpTeeRpmbFvSetAttributes (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_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_BLOCK_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
+OpTeeRpmbFvGetPhysicalAddress (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ OUT EFI_PHYSICAL_ADDRESS *Address
+ )
+{
+ MEM_INSTANCE *Instance;
+
+ Instance = INSTANCE_FROM_FVB_THIS(This);
+ *Address = Instance->MemBaseAddress;
+
+ 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_BLOCK_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
+OpTeeRpmbFvGetBlockSize (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ OUT UINTN *BlockSize,
+ OUT UINTN *NumberOfBlocks
+ )
+{
+ MEM_INSTANCE *Instance;
+
+ Instance = INSTANCE_FROM_FVB_THIS(This);
+ *NumberOfBlocks = Instance->NBlocks;
+ *BlockSize = Instance->BlockSize;
+
+ 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_BLOCK_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
+OpTeeRpmbFvRead (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN OUT UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ MEM_INSTANCE *Instance;
+ VOID *Base;
+
+ Instance = INSTANCE_FROM_FVB_THIS(This);
+ Base = (VOID *)Instance->MemBaseAddress + Lba * BLOCK_SIZE + Offset;
+ if (Instance->Initialized == FALSE) {
+ Instance->Initialize (Instance);
+ }
+
+ // Update the memory copy
+ CopyMem (Buffer, Base, *NumBytes);
+
+ // TODO read the actual hardware and compare memory
+ //ReadWriteVariables (SP_SVC_RPMB_READ, File, Addr, *NumBytes, RelativeOffset);
+
+ return Status;
+}
+
+/**
+ 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_BLOCK_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
+OpTeeRpmbFvWrite (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ IN EFI_LBA Lba,
+ IN UINTN Offset,
+ IN OUT UINTN *NumBytes,
+ IN UINT8 *Buffer
+ )
+{
+ MEM_INSTANCE *Instance;
+ EFI_STATUS Status = EFI_SUCCESS;
+ VOID *Base;
+ UINTN Ret;
+
+ Instance = INSTANCE_FROM_FVB_THIS(This);
+ if (Instance->Initialized == FALSE) {
+ Instance->Initialize (Instance);
+ }
+ Base = (VOID *)Instance->MemBaseAddress + Lba * BLOCK_SIZE + Offset;
+ // We can map OP-TEE errors to EFI exitcodes and return a more
+ // realistic error. Keep it simple for now
+ Ret = ReadWriteVariables (SP_SVC_RPMB_WRITE, (UINTN) Buffer, *NumBytes,
+ Lba * BLOCK_SIZE + Offset);
+ if (Ret) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ // Update the memory copy
+ 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_BLOCK_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
+OpTeeRpmbFvErase (
+ IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *This,
+ ...
+ )
+{
+ MEM_INSTANCE *Instance;
+ UINTN NumBytes;
+ UINTN Length;
+ EFI_LBA Start;
+ VOID *Base;
+ VA_LIST Args;
+ UINTN Ret;
+
+ Instance = INSTANCE_FROM_FVB_THIS(This);
+
+ VA_START (Args, This);
+ for (Start = VA_ARG (Args, EFI_LBA);
+ Start != EFI_LBA_LIST_TERMINATOR;
+ Start = VA_ARG (Args, EFI_LBA)) {
+ Length = VA_ARG (Args, UINTN);
+ NumBytes = Length * BLOCK_SIZE;
+ Base = (VOID *)Instance->MemBaseAddress + Start * BLOCK_SIZE;
+ // FIXME revert memory on fails?
+ SetMem64 (Base, Length * BLOCK_SIZE, ~0UL);
+ // Write the device
+ Ret = ReadWriteVariables (SP_SVC_RPMB_WRITE, (UINTN) Base, NumBytes,
+ Start * BLOCK_SIZE);
+ if (Ret) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ VA_END (Args);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Since we use a memory backed storage we need to restore the RPMB contents
+ into memory before we register the Fvb protocol.
+ The contiguous memory in EDK2 is ampped is 3 OP-TEE files
+
+ @param Addr Address to copy flash contents to
+
+ @retval 0 on success, OP-TEE error on failure
+**/
+STATIC
+UINTN
+ReadEntireFlash (
+ MEM_INSTANCE *Instance
+ )
+{
+ UINTN ReadAddr;
+ UINTN Ret;
+
+ UINTN StorageFtwWorkingSize = PcdGet32(PcdFlashNvStorageFtwWorkingSize);
+ UINTN StorageVariableSize = PcdGet32(PcdFlashNvStorageVariableSize);
+ UINTN StorageFtwSpareSize = PcdGet32(PcdFlashNvStorageFtwSpareSize);
+
+ ReadAddr = Instance->MemBaseAddress;
+ Ret = ReadWriteVariables(SP_SVC_RPMB_READ, ReadAddr, StorageVariableSize +
+ StorageFtwWorkingSize + StorageFtwSpareSize , 0);
+
+ return Ret;
+}
+
+
+STATIC
+EFI_STATUS
+EFIAPI
+ValidateFvHeader (
+ IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader
+ )
+{
+ UINT16 Checksum;
+ VARIABLE_STORE_HEADER *VariableStoreHeader;
+ UINTN VariableStoreLength;
+ UINTN FvLength;
+
+ FvLength = PcdGet32(PcdFlashNvStorageVariableSize) +
+ PcdGet32(PcdFlashNvStorageFtwWorkingSize) +
+ PcdGet32(PcdFlashNvStorageFtwSpareSize);
+
+ // Verify the header revision, header signature, length
+ // Length of FvBlock cannot be 2**64-1
+ // HeaderLength cannot be an odd number
+ //
+ if ( (FwVolHeader->Revision != EFI_FVH_REVISION)
+ || (FwVolHeader->Signature != EFI_FVH_SIGNATURE)
+ || (FwVolHeader->FvLength != FvLength)
+ )
+ {
+ DEBUG ((DEBUG_INFO, "%a: No Firmware Volume header present\n",
+ __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ // Check the Firmware Volume Guid
+ if (!CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid)) {
+ DEBUG ((DEBUG_INFO, "%a: Firmware Volume Guid non-compatible\n",
+ __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ // Verify the header checksum
+ Checksum = CalculateSum16((UINT16*)FwVolHeader, FwVolHeader->HeaderLength);
+ if (Checksum != 0) {
+ DEBUG ((DEBUG_INFO, "%a: FV checksum is invalid (Checksum:0x%X)\n",
+ __FUNCTION__, Checksum));
+ return EFI_NOT_FOUND;
+ }
+
+ VariableStoreHeader = (VOID *)((UINTN)FwVolHeader +
+ FwVolHeader->HeaderLength);
+
+ // Check the Variable Store Guid
+ if (!CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) &&
+ !CompareGuid (&VariableStoreHeader->Signature,
+ &gEfiAuthenticatedVariableGuid)) {
+ DEBUG ((DEBUG_INFO, "%a: Variable Store Guid non-compatible\n",
+ __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) -
+ FwVolHeader->HeaderLength;
+ if (VariableStoreHeader->Size != VariableStoreLength) {
+ DEBUG ((DEBUG_INFO, "%a: Variable Store Length does not match\n",
+ __FUNCTION__));
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+STATIC
+EFI_STATUS
+InitializeFvAndVariableStoreHeaders (
+ EFI_PHYSICAL_ADDRESS Addr
+ )
+{
+ EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumeHeader;
+ VARIABLE_STORE_HEADER *VariableStoreHeader;
+ EFI_STATUS Status = EFI_SUCCESS;
+ UINTN HeadersLength;
+ VOID* Headers;
+ UINTN Ret;
+
+ HeadersLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) +
+ sizeof(EFI_FV_BLOCK_MAP_ENTRY) +
+ sizeof(VARIABLE_STORE_HEADER);
+ Headers = AllocateZeroPool(HeadersLength);
+
+ //
+ // EFI_FIRMWARE_VOLUME_HEADER
+ //
+ FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER*)Headers;
+ CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid);
+ FirmwareVolumeHeader->FvLength =
+ PcdGet32(PcdFlashNvStorageVariableSize) +
+ PcdGet32(PcdFlashNvStorageFtwWorkingSize) +
+ PcdGet32(PcdFlashNvStorageFtwSpareSize);
+ FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE;
+ FirmwareVolumeHeader->Attributes = EFI_FVB2_READ_ENABLED_CAP |
+ EFI_FVB2_READ_STATUS |
+ EFI_FVB2_STICKY_WRITE |
+ EFI_FVB2_MEMORY_MAPPED |
+ EFI_FVB2_ERASE_POLARITY |
+ EFI_FVB2_WRITE_STATUS |
+ EFI_FVB2_WRITE_ENABLED_CAP;
+
+ FirmwareVolumeHeader->HeaderLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) +
+ sizeof(EFI_FV_BLOCK_MAP_ENTRY);
+ FirmwareVolumeHeader->Revision = EFI_FVH_REVISION;
+ FirmwareVolumeHeader->BlockMap[0].NumBlocks = NBLOCKS + 1;
+ FirmwareVolumeHeader->BlockMap[0].Length = BLOCK_SIZE;
+ FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0;
+ FirmwareVolumeHeader->BlockMap[1].Length = 0;
+ FirmwareVolumeHeader->Checksum = CalculateCheckSum16 (
+ (UINT16*)FirmwareVolumeHeader,
+ FirmwareVolumeHeader->HeaderLength);
+
+ //
+ // VARIABLE_STORE_HEADER
+ //
+ VariableStoreHeader = (VOID *)((UINTN)Headers +
+ FirmwareVolumeHeader->HeaderLength);
+ CopyGuid (&VariableStoreHeader->Signature, &gEfiAuthenticatedVariableGuid);
+ VariableStoreHeader->Size = PcdGet32(PcdFlashNvStorageVariableSize) -
+ FirmwareVolumeHeader->HeaderLength;
+ VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED;
+ VariableStoreHeader->State = VARIABLE_STORE_HEALTHY;
+
+ Ret = ReadWriteVariables(SP_SVC_RPMB_WRITE, (UINTN) Headers, HeadersLength, 0);
+ if (Ret) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+ // Install the combined super-header in memory
+ CopyMem ((VOID*) Addr, Headers, HeadersLength);
+
+Exit:
+ FreePool (Headers);
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+FvbInitialize (
+ MEM_INSTANCE *Instance
+ )
+{
+ UINTN mFlashNvStorageVariableBase;
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;
+ EFI_STATUS Status;
+ UINTN Ret;
+
+ if (Instance->Initialized == TRUE) {
+ return EFI_SUCCESS;
+ }
+
+ // FirmwareVolumeHeader->FvLength is declared to have the Variable area
+ // AND the FTW working area AND the FTW Spare contiguous.
+ ASSERT (PcdGet32 (PcdFlashNvStorageVariableBase) +
+ PcdGet32 (PcdFlashNvStorageVariableSize) ==
+ PcdGet32 (PcdFlashNvStorageFtwWorkingBase));
+ ASSERT (PcdGet32 (PcdFlashNvStorageFtwWorkingBase) +
+ PcdGet32 (PcdFlashNvStorageFtwWorkingSize) ==
+ PcdGet32 (PcdFlashNvStorageFtwSpareBase));
+
+ // Check if the size of the area is at least one block size
+ ASSERT ((PcdGet32 (PcdFlashNvStorageVariableSize) > 0) &&
+ (PcdGet32 (PcdFlashNvStorageVariableSize) / BLOCK_SIZE > 0));
+ ASSERT ((PcdGet32 (PcdFlashNvStorageFtwWorkingSize) > 0) &&
+ (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) / BLOCK_SIZE > 0));
+ ASSERT ((PcdGet32 (PcdFlashNvStorageFtwSpareSize) > 0) &&
+ (PcdGet32 (PcdFlashNvStorageFtwSpareSize) / BLOCK_SIZE > 0));
+
+ // Ensure the Variable areas are aligned on block size boundaries
+ ASSERT ((PcdGet32 (PcdFlashNvStorageVariableBase) % BLOCK_SIZE) == 0);
+ ASSERT ((PcdGet32 (PcdFlashNvStorageFtwWorkingBase) % BLOCK_SIZE) == 0);
+ ASSERT ((PcdGet32 (PcdFlashNvStorageFtwSpareBase) % BLOCK_SIZE) == 0);
+
+ mFlashNvStorageVariableBase = PcdGet32 (PcdFlashNvStorageVariableBase);
+ // Read the file from disk and copy it to memory
+ Ret = ReadEntireFlash (Instance);
+
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) mFlashNvStorageVariableBase;
+ Status = ValidateFvHeader(FwVolHeader);
+ if (EFI_ERROR (Status)) {
+ // There is no valid header, so time to install one.
+ DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__));
+
+ // Reset memory
+ SetMem64 ((VOID *)Instance->MemBaseAddress, NBLOCKS * BLOCK_SIZE, ~0UL);
+ DEBUG ((DEBUG_INFO, "%a: Erasing Flash.\n", __FUNCTION__));
+ Ret = ReadWriteVariables(SP_SVC_RPMB_WRITE, Instance->MemBaseAddress,
+ PcdGet32(PcdFlashNvStorageVariableSize) +
+ PcdGet32(PcdFlashNvStorageFtwWorkingSize) +
+ PcdGet32(PcdFlashNvStorageFtwSpareSize), 0);
+ if (Ret) {
+ return EFI_DEVICE_ERROR;
+ }
+ // Install all appropriate headers
+ DEBUG ((DEBUG_INFO, "%a: Installing a correct one for this volume.\n",
+ __FUNCTION__));
+ Status = InitializeFvAndVariableStoreHeaders (mFlashNvStorageVariableBase);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ } else {
+ DEBUG ((DEBUG_INFO, "%a: Found valid FVB Header.\n", __FUNCTION__));
+ }
+ Instance->Initialized = TRUE;
+
+ return Status;
+}
+
+EFI_STATUS
+EFIAPI
+OpTeeRpmbFvInit (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_MM_SYSTEM_TABLE *SystemTable
+ )
+{
+ //ARM_SVC_ARGS SvcArgs;
+ EFI_STATUS Status;
+ VOID *Addr;
+
+ //ZeroMem (&SvcArgs, sizeof (SvcArgs));
+ //SvcArgs.Arg0 = SP_SVC_GET_UART;
+ //SendSvc(&SvcArgs);
+ //PatchPcdSet32 (PcdSerialRegisterBase, SvcArgs.Arg1);
+
+ //mInstance.UartBaseAddress = (EFI_PHYSICAL_ADDRESS) SvcArgs.Arg1;
+ //DEBUG ((DEBUG_INFO, "%a: MAPPED UART 0x%lx\n", __FUNCTION__, mInstance.UartBaseAddress));
+ Addr = AllocatePages(NBLOCKS);
+ ASSERT (Addr != NULL);
+
+ SetMem (&mInstance, sizeof (mInstance), 0);
+
+ mInstance.FvbProtocol.GetPhysicalAddress = OpTeeRpmbFvGetPhysicalAddress;
+ mInstance.FvbProtocol.GetAttributes = OpTeeRpmbFvGetAttributes;
+ mInstance.FvbProtocol.SetAttributes = OpTeeRpmbFvSetAttributes;
+ mInstance.FvbProtocol.GetBlockSize = OpTeeRpmbFvGetBlockSize;
+ mInstance.FvbProtocol.EraseBlocks = OpTeeRpmbFvErase;
+ mInstance.FvbProtocol.Write = OpTeeRpmbFvWrite;
+ mInstance.FvbProtocol.Read = OpTeeRpmbFvRead;
+
+ mInstance.MemBaseAddress = (EFI_PHYSICAL_ADDRESS) Addr;
+ mInstance.Signature = FLASH_SIGNATURE;
+ mInstance.Initialize = FvbInitialize;
+ mInstance.BlockSize = BLOCK_SIZE;
+ mInstance.NBlocks = NBLOCKS;
+
+ // Update the defined PCDs related to Variable Storage
+ PatchPcdSet32 (PcdFlashNvStorageVariableBase, mInstance.MemBaseAddress);
+ PatchPcdSet32 (PcdFlashNvStorageFtwWorkingBase, mInstance.MemBaseAddress +
+ PcdGet32 (PcdFlashNvStorageVariableSize));
+ PatchPcdSet32 (PcdFlashNvStorageFtwSpareBase, mInstance.MemBaseAddress +
+ PcdGet32 (PcdFlashNvStorageVariableSize) +
+ PcdGet32 (PcdFlashNvStorageFtwWorkingSize));
+
+ Status = gMmst->MmInstallProtocolInterface (
+ &mInstance.Handle,
+ &gEfiSmmFirmwareVolumeBlockProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mInstance.FvbProtocol
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG ((DEBUG_INFO, "%a: Register OP-TEE RPMB Fvb\n", __FUNCTION__));
+ DEBUG ((DEBUG_INFO, "%a: Using NV store FV in-memory copy at 0x%lx\n",
+ __FUNCTION__, PatchPcdGet32 (PcdFlashNvStorageVariableBase)));
+
+ return Status;
+}
diff --git a/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.h b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.h
new file mode 100644
index 00000000..f6c3fe26
--- /dev/null
+++ b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.h
@@ -0,0 +1,52 @@
+/** @file
+
+ Copyright (c) 2020, Linaro Ltd. All rights reserved.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __OPTEE_RPMB_FV_
+#define __OPTEE_RPMB_FV_
+
+/* SVC Args */
+#define SP_SVC_RPMB_READ 0xC4000066
+#define SP_SVC_RPMB_WRITE 0xC4000067
+#define SP_SVC_GET_UART 0xC4000068
+
+#define FILENAME "EFI_VARS"
+
+#define NBLOCKS (3 * 16) // EFI Vars, FTW working, FTW spare
+#define BLOCK_SIZE SIZE_4KB
+#define FLASH_SIGNATURE SIGNATURE_32('r', 'p', 'm', 'b')
+#define INSTANCE_FROM_FVB_THIS(a) CR(a, MEM_INSTANCE, FvbProtocol, \
+ FLASH_SIGNATURE)
+enum _RPMB_FILE_MAP {
+ EFI_VARS,
+ FTW_WORK,
+ FTW_SPARE,
+};
+
+typedef enum _RPMB_FILE_MAP RPMB_FILE_MAP;
+
+struct _MAP_VAL_TO_FILE {
+ CHAR8 *Filename;
+ RPMB_FILE_MAP Map;
+};
+
+typedef struct _MAP_VAL_TO_FILE MAP_VAL_TO_FILE;
+
+typedef struct _MEM_INSTANCE MEM_INSTANCE;
+typedef EFI_STATUS (*MEM_INITIALIZE) (MEM_INSTANCE* Instance);
+struct _MEM_INSTANCE
+{
+ UINT32 Signature;
+ MEM_INITIALIZE Initialize;
+ BOOLEAN Initialized;
+ EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL FvbProtocol;
+ EFI_HANDLE Handle;
+ EFI_PHYSICAL_ADDRESS MemBaseAddress;
+ UINT16 BlockSize;
+ UINT16 NBlocks;
+};
+
+#endif
diff --git a/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.inf b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.inf
new file mode 100644
index 00000000..fcc9d100
--- /dev/null
+++ b/Silicon/QemuVirt/Drivers/OpTeeRpmbFv/OpTeeRpmbFv.inf
@@ -0,0 +1,58 @@
+## @file
+#
+# Component description file for OpTeeRpmbFv module
+#
+# Copyright (c) 2020, Linaro Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = OpTeeRpmbFv
+ FILE_GUID = 4803FC20-E583-3BCD-8C60-141E85C9A2CF
+ MODULE_TYPE = MM_STANDALONE
+ VERSION_STRING = 0.1
+ PI_SPECIFICATION_VERSION = 0x00010032
+ ENTRY_POINT = OpTeeRpmbFvInit
+
+[Sources]
+ OpTeeRpmbFv.c
+ OpTeeRpmbFv.h
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+ StandaloneMmPkg/StandaloneMmPkg.dec
+
+[LibraryClasses]
+ ArmSvcLib
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ MmServicesTableLib
+ PcdLib
+ StandaloneMmDriverEntryPoint
+
+[Guids]
+ gEfiAuthenticatedVariableGuid
+ gEfiSystemNvDataFvGuid
+ gEfiVariableGuid
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+
+[Protocols]
+ gEfiSmmFirmwareVolumeBlockProtocolGuid ## PRODUCES
+
+[Depex]
+ TRUE