summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Marinho <jose.marinho@arm.com>2020-07-05 13:54:40 +0100
committerJose Marinho <jose.marinho@arm.com>2021-08-07 00:10:51 +0100
commit0b7c4f0f6b7e8ad0f918e1438e4a8144af072aca (patch)
treeafee76534cb4277b52cf730962180cc0b077f957
parent56ba4de0b0e0f34fc4f834389c8d507059ace33c (diff)
[FWU proto] UpdateAgent DEN0118 BET0 compliant
This commit implements the ABI specified in DEN0118[1], used to communicate FW images from the FWU Client in the Normal World to an the Update Agent in the Secure World. [1] https://developer.arm.com/documentation/den0118/a/ Signed-off-by: jmarinho <jose.marinho@arm.com>
-rw-r--r--Drivers/FWUpdate/FWUpdate.c1148
-rw-r--r--Drivers/FWUpdate/FWUpdate.inf137
-rw-r--r--Drivers/FWUpdate/SmmFirmwareUpdate.h191
3 files changed, 1476 insertions, 0 deletions
diff --git a/Drivers/FWUpdate/FWUpdate.c b/Drivers/FWUpdate/FWUpdate.c
new file mode 100644
index 00000000..4ab784b7
--- /dev/null
+++ b/Drivers/FWUpdate/FWUpdate.c
@@ -0,0 +1,1148 @@
+/** @file
+
+ Secure FW update implementation.
+
+Copyright (c) 2021 Arm Ltd.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiMm.h>
+
+#include <Protocol/SmmVariable.h>
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+#include <Protocol/SmmFaultTolerantWrite.h>
+#include <Protocol/MmEndOfDxe.h>
+#include <Protocol/SmmVarCheck.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/FirmwareVolumeBlock.h>
+
+#include <Library/MmServicesTableLib.h>
+
+#include <Guid/SmmVariableCommon.h>
+#include <Protocol/SmmFirmwareVolumeBlock.h>
+#include <Protocol/BlockIo.h>
+
+#include <Library/MmServicesTableLib.h>
+
+
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+
+#include <Library/BaseMemoryLib.h>
+
+#include "SmmFirmwareUpdate.h"
+
+
+EFI_STATUS ReadBlock (UINT8 *Buff, UINTN BuffSize, UINT32 Offset);
+EFI_STATUS WriteBlock ( UINT8 *Buff, UINTN BuffSize, UINT32 Offset);
+
+UINT32 BlockSize;
+
+static UINT32 update_index;
+static BOOLEAN trial_state;
+
+__attribute__((unused)) static EFI_BLOCK_IO_PROTOCOL *BlockIoProtocol;
+__attribute__((unused)) static EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *FvbProtocol;
+
+static const EFI_GUID temp_tfa_fip_guid = TEMP_TFA_FIP_GUID;
+
+BOOLEAN Staging_state = FALSE;
+BOOLEAN mAtRuntime = FALSE;
+UINT8 *mVariableBufferPayload = NULL;
+
+// XXX: auxiliar datastructure while the prototype lacks GPT support.
+static UINT64 image_start[FWU_NUM_BANKS];
+
+struct fwu_metadata *fwu_metadata = NULL;
+
+inline static UINT32 compute_metadata_crc32(struct fwu_metadata *metadata)
+{
+ ASSERT(metadata);
+
+ return CalculateCrc32(&metadata->version, FWU_METADATA_SIZE-4);
+}
+
+/* TODO: move to a platform specific library. */
+EFI_STATUS plat_write_metadata_region(struct fwu_metadata *metadata, UINT32 active_index, UINT32 previous_active)
+{
+VOID *Buffer = NULL;
+ EFI_STATUS Status;
+ ASSERT(metadata);
+
+ UINT32 metadata_main_offset = PcdGet32 (PcdMetadataMainOffset);
+ UINT32 metadata_fallback_offset = PcdGet32 (PcdMetadataFallbackOffset);
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ BlockSize,
+ (VOID **)&Buffer
+ );
+
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG((EFI_D_ERROR, "Failed to allocate buffer to write metadata.\n"));
+ return Status;
+ }
+
+ SetMem32(Buffer, BlockSize, 0xbadbadba);
+
+ metadata->active_index = active_index % FWU_NUM_BANKS;
+ metadata->previous_active_index = previous_active % FWU_NUM_BANKS;
+
+ metadata->crc_32 = compute_metadata_crc32(metadata);
+
+ DEBUG((EFI_D_INFO, "----metadata update-----------\n"));
+ DEBUG((EFI_D_INFO, "active index %lX\n", metadata->active_index));
+ DEBUG((EFI_D_INFO, "previous active index %lX\n", metadata->previous_active_index));
+ DEBUG((EFI_D_INFO, "crc32 %lX\n", metadata->crc_32));
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+
+ CopyMem(Buffer, metadata, FWU_METADATA_SIZE);
+
+ DEBUG((EFI_D_INFO, "sync main metadata (size %x, offsett %x)\n", BlockSize, metadata_main_offset));
+ Status = WriteBlock (
+ Buffer,
+ BlockSize,
+ metadata_main_offset
+ );
+
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG((EFI_D_ERROR, "Failed to write main metadata.\n"));
+ goto out;
+ }
+
+ DEBUG((EFI_D_INFO, "sync fallback metadata\n"));
+ Status = WriteBlock (
+ Buffer,
+ BlockSize,
+ metadata_fallback_offset
+ );
+
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG((EFI_D_ERROR, "Failed to write fallback metadata.\n"));
+ goto out;
+ }
+
+out:
+ gMmst->MmFreePool (Buffer);
+
+ return Status;
+}
+
+UINT32 read_nv_ctr(void)
+{
+ UINT32 ctr_offset = PcdGet32 (PcdFWUpdateSWdNvCtrOffset);
+ EFI_PHYSICAL_ADDRESS flashBase = PcdGet32 (PcdFWUpdateBaseAddress);
+
+ return *(UINT32 *)(flashBase + ctr_offset);
+}
+
+EFI_STATUS write_nv_ctr(UINT32 ctr)
+{
+ VOID *Buffer = NULL;
+ UINT32 oldctr;
+ EFI_STATUS Status = EFI_SUCCESS;
+
+ // XXX: anti-rollback counter update disabled for the imx8mm platform prototype for now
+ return EFI_SUCCESS; //XXX XXX XXX
+ UINT32 ctr_offset = PcdGet32 (PcdFWUpdateSWdNvCtrOffset);
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ BlockSize,
+ (VOID **)&Buffer
+ );
+
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG((EFI_D_ERROR, "Failed to allocate buffer to write metadata.\n"));
+ goto out;
+ }
+
+ oldctr = read_nv_ctr();
+
+ if (ctr <= oldctr)
+ {
+ Status = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ SetMem32(Buffer, BlockSize, 0xdeadbeef);
+ *(UINT32 *)Buffer = ctr;
+
+ Status = WriteBlock (
+ Buffer,
+ BlockSize,
+ ctr_offset
+ );
+
+out:
+ gMmst->MmFreePool (Buffer);
+
+ return Status;
+}
+
+VOID PrintMetadata (struct fwu_metadata *metadata)
+{
+ DEBUG((EFI_D_INFO, "---------------- METADATA -----------------\n"));
+ DEBUG((EFI_D_INFO, "Crc32 %x\n", metadata->crc_32));
+ DEBUG((EFI_D_INFO, "metadata version %x\n", metadata->version));
+ DEBUG((EFI_D_INFO, "active idx %x\n", metadata->active_index));
+ DEBUG((EFI_D_INFO, "update index %x\n", metadata->previous_active_index));
+ DEBUG((EFI_D_INFO, "matadata size %x\n", sizeof(*metadata)));
+ DEBUG((EFI_D_INFO, "-------------------------------------------\n"));
+}
+
+BOOLEAN IsMetadataIntact(struct fwu_metadata *metadata)
+{
+ BOOLEAN intact = TRUE;
+ UINT32 MetadataCrc32;
+ ASSERT(metadata);
+
+ MetadataCrc32 = compute_metadata_crc32(metadata);
+ DEBUG((EFI_D_INFO, "Metadata Crc32 %x\n", MetadataCrc32));
+
+ intact = (MetadataCrc32 == metadata->crc_32);
+
+ if (!intact)
+ DEBUG((EFI_D_INFO, "Metadata region is corrupted %X!== %X\n", MetadataCrc32, metadata->crc_32));
+ else
+ DEBUG((EFI_D_INFO, "Metadata region is intact\n"));
+
+ return intact;
+}
+
+void bootstrap_metadata(struct fwu_metadata *metadata)
+{
+ static const UINT32 bootstrap_active = 0;
+ static const UINT32 bootstrap_previous = 1;
+
+
+ metadata->version = 1;
+ metadata->active_index = bootstrap_active;
+ metadata->previous_active_index = bootstrap_previous;
+
+ // FIP was generated with this guid: FB90808A-BA9A-4D42-B9A2-A7A937144AEE
+ // see SmmFirmwareUpdate.h
+ //
+ // Capsule generated with:
+ // ./edk2/BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o fip.capsule
+ // --fw-version 11 --lsv 12 --guid fb90808a-ba9a-4d42-b9a2-a7a937144aee
+ // --verbose --update-image-index 0 --verbose fip.bin
+ //
+ CopyGuid(&metadata->img_entry[0].image_uuid, &temp_tfa_fip_guid);
+
+ // XXX Location is hardcoded and hence not used at this time
+ // The location can be used to search through a set of GPTs.
+ CopyGuid(&metadata->img_entry[0].location_uuid, &nil_guid);
+
+
+ // XXX: img_uuid is not used yet in the prototype as the prototype lacks GPT support.
+ CopyGuid(&metadata->img_entry[0].img_bank_info[0].img_uuid, &nil_guid);
+ CopyGuid(&metadata->img_entry[0].img_bank_info[1].img_uuid, &nil_guid);
+
+ metadata->img_entry[0].img_bank_info[bootstrap_active].accepted = 1;
+ metadata->img_entry[0].img_bank_info[bootstrap_previous].accepted = 0;
+
+ metadata->crc_32 = compute_metadata_crc32(metadata);
+}
+
+BOOLEAN is_trial_state(struct fwu_metadata *metadata)
+{
+ UINT32 active_index;
+ BOOLEAN Trial = FALSE;
+
+ ASSERT(metadata);
+ active_index = fwu_metadata->active_index;
+
+ for (UINT32 idx=0; idx<FWU_NUM_IMAGES; idx++) {
+ Trial = Trial || !fwu_metadata->img_entry[idx].img_bank_info[active_index].accepted;
+ }
+
+ return Trial;
+}
+
+/*
+ * Read the metadata from the storage device.
+ * TODO: move to a platform specific library.
+ */
+EFI_STATUS refresh_metadata(struct fwu_metadata *metadata)
+{
+ ASSERT(metadata);
+ VOID *Buffer;
+ EFI_STATUS Status;
+ BOOLEAN main_intact = FALSE;
+ BOOLEAN fallback_intact = FALSE;
+
+ UINT32 metadata_main_offset = PcdGet32 (PcdMetadataMainOffset);
+ UINT32 metadata_fallback_offset = PcdGet32 (PcdMetadataFallbackOffset);
+
+ DEBUG((EFI_D_INFO, "---refresh local metadata------\n"));
+ DEBUG((EFI_D_INFO, "Main metadata offset %lX\n", metadata_main_offset));
+ DEBUG((EFI_D_INFO, "Fallback metadata offset %lX\n", metadata_fallback_offset));
+ DEBUG((EFI_D_INFO, "metadata size %lX\n", FWU_METADATA_SIZE));
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+
+ /* allocate buffer to place the contents read from flash. */
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ BlockSize,
+ (VOID **)&Buffer
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to allocate buffer\n"));
+ return Status;
+ }
+
+ Status = ReadBlock(Buffer, BlockSize, metadata_main_offset);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to read metadata from flash\n"));
+ return Status;
+ }
+
+ PrintMetadata (Buffer);
+
+ if (IsMetadataIntact(Buffer)) {
+ CopyMem(metadata, Buffer, FWU_METADATA_SIZE);
+ main_intact = TRUE;
+ }
+
+ Status = ReadBlock(Buffer, BlockSize, metadata_fallback_offset);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to read metadata from flash\n"));
+ return Status;
+ }
+
+ if (IsMetadataIntact(Buffer)) {
+ CopyMem(metadata, Buffer, FWU_METADATA_SIZE);
+ fallback_intact = TRUE;
+ }
+
+ if (!(main_intact && fallback_intact)) {
+ bootstrap_metadata(metadata);
+
+ /*
+ * XXX: The prototype NV rollback counter is kept in secure flash.
+ *
+ *
+ * XXX Proto: the NV ctr is set to 0
+ * when the flash is rebuilt.
+ * A real system would never reset the NV counter.
+ */
+ write_nv_ctr(0);
+ }
+
+ if(!main_intact)
+ {
+ DEBUG((EFI_D_INFO, "sync main metadata\n"));
+
+ Status = WriteBlock((UINT8 *)metadata, BlockSize, metadata_main_offset);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to correct fallbak metadata\n"));
+ return Status;
+ }
+ }
+
+ if(!fallback_intact)
+ {
+ DEBUG((EFI_D_INFO, "sync fallback metadata\n"));
+
+ Status = WriteBlock((UINT8 *)metadata, BlockSize, metadata_fallback_offset);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to correct main metadata\n"));
+ return Status;
+ }
+ }
+
+ PrintMetadata(metadata);
+
+ //set the update_index value for the current run
+ update_index = (metadata->active_index + 1) % FWU_NUM_BANKS;
+
+ gMmst->MmFreePool (Buffer);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * Verify that the FW Update header is present in the secure flash.
+ * If the header is not present it is initialized.
+ */
+EFI_STATUS
+InitFWUpdateStore (
+ VOID
+ )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ EFI_PHYSICAL_ADDRESS FWHeaderAddress = PcdGet32 (PcdFWUpdateBaseAddress);
+
+ UINT32 metadata_main_offset = PcdGet32 (PcdMetadataMainOffset);
+ UINT32 metadata_fallback_offset = PcdGet32 (PcdMetadataFallbackOffset);
+
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+ DEBUG((EFI_D_INFO, "FW Update address %lX\n", FWHeaderAddress));
+ DEBUG((EFI_D_INFO, "Main metadata offset %lX\n", metadata_main_offset));
+ DEBUG((EFI_D_INFO, "Fallback metadata offset %lX\n", metadata_fallback_offset));
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+
+ UINT32 a_img_offset = PcdGet32 (PcdFWUpdateAImageOffset);
+ UINT32 b_img_offset = PcdGet32 (PcdFWUpdateBImageOffset);
+
+ DEBUG((EFI_D_INFO, "----Init FIP offsets---------\n"));
+ DEBUG((EFI_D_INFO, "A image offset %lX\n", a_img_offset));
+ DEBUG((EFI_D_INFO, "B image offset %lX\n", b_img_offset));
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+
+ // XXX: auxiliar datastructure until the prototype has GPT support.
+ image_start[0] = a_img_offset;
+ image_start[1] = b_img_offset;
+
+ Status = gMmst->MmLocateProtocol (
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ (VOID **) &BlockIoProtocol
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to locate Fvb instance\n"));
+ return Status;
+ }
+
+ BlockSize = BlockIoProtocol->Media->BlockSize;
+
+ ASSERT(BlockSize);
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ FWU_METADATA_SIZE,
+ (VOID **)&fwu_metadata
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to allocate metadta representation\n"));
+ return Status;
+ }
+
+ Status = refresh_metadata(fwu_metadata);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to refresh metadata\n"));
+ return Status;
+ }
+
+ trial_state = is_trial_state(fwu_metadata);
+
+ return Status;
+}
+
+/**
+ * Read from the FW store.
+ * TODO: Move to a library.
+ */
+EFI_STATUS
+ReadBlock (
+ UINT8 *Buff,
+ UINTN BuffSize,
+ UINT32 Offset
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Lba = Offset/BlockSize;
+
+ // Offset must be at a BlockSize boundary.
+ DEBUG((EFI_D_INFO, "read at offset %lX\n", Offset));
+
+ // BuffSize must be a multiple of BlockSize.
+ DEBUG((EFI_D_INFO, "read size %lX\n", BuffSize));
+ ASSERT((BuffSize & (BlockSize - 1)) == 0);
+
+ ASSERT(BlockIoProtocol);
+
+ Status = BlockIoProtocol->ReadBlocks (
+ BlockIoProtocol,
+ 0,
+ Lba,
+ BuffSize,
+ Buff
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed read block\n"));
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * Write onto the FW Store.
+ * TODO: Move to a library.
+ */
+EFI_STATUS
+WriteBlock (
+ UINT8 *Buff,
+ UINTN BuffSize,
+ UINT32 Offset
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Lba = Offset/BlockSize;
+
+ // Offset must be at a BlockSize boundary.
+ DEBUG((EFI_D_INFO, "write at offset %lX\n", Offset));
+
+ // BuffSize must be a multiple of BlockSize.
+ DEBUG((EFI_D_INFO, "write size %lX\n", BuffSize));
+ ASSERT((BuffSize & (BlockSize - 1)) == 0);
+
+ ASSERT(BlockIoProtocol);
+
+ Status = BlockIoProtocol->WriteBlocks (
+ BlockIoProtocol,
+ 0,
+ Lba,
+ BuffSize,
+ Buff
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed write block\n"));
+ return Status;
+ }
+
+ if (BlockIoProtocol->FlushBlocks) {
+ Status = BlockIoProtocol->FlushBlocks(BlockIoProtocol);
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed flush block\n"));
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+#define MAX_CAPSULES 10
+#define IMG_INFO_HANDLE MAX_CAPSULES
+static struct CapsuleState {
+
+ VOID *Buffer;
+
+ EFI_GUID image_guid;
+
+ BOOLEAN OngoingTransfer;
+
+ UINT32 bytes_filled;
+ UINT32 next_offset;
+
+ UINT32 img_idx;
+
+} outstanding_capsules [MAX_CAPSULES];
+
+BOOLEAN any_image_open(void)
+{
+ for (UINT32 index = 0; index < MAX_CAPSULES; ++index) {
+ if(!CompareGuid(&outstanding_capsules[index].image_guid, &nil_guid))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+VOID print_capsule_guids ()
+{
+ for (UINT32 index = 0; index < MAX_CAPSULES; ++index) {
+ DEBUG((DEBUG_INFO, "Capsule id: %d guid: %g used %d\n",
+ index, &outstanding_capsules[index].image_guid, outstanding_capsules[index].OngoingTransfer));
+ }
+}
+
+struct CapsuleState* find_capsule (UINT32 handle)
+{
+ if (!outstanding_capsules[handle].OngoingTransfer) {
+ DEBUG((DEBUG_INFO, "found capsule guid: %g\n", &outstanding_capsules[handle].image_guid));
+ print_capsule_guids ();
+ return NULL;
+ }
+
+ DEBUG((DEBUG_INFO, "found capsule guid: %g\n", &outstanding_capsules[handle].image_guid));
+ return &outstanding_capsules[handle];
+}
+
+struct CapsuleState* open_capsule (EFI_GUID *image_guid, UINT32 *handle)
+{
+ UINT32 img_idx = FWU_NUM_IMAGES;
+ DEBUG((DEBUG_INFO, "Open capsule guid: %g\n", image_guid));
+
+ for (UINT32 index = 0; index < FWU_NUM_IMAGES; ++index) {
+ if (CompareGuid(image_guid, &fwu_metadata->img_entry[index].image_uuid)) {
+ img_idx = index;
+ DEBUG((DEBUG_INFO, "found image in metadata at index %d\n", index));
+ }
+ }
+
+ if (!(img_idx < FWU_NUM_IMAGES)) {
+ DEBUG((DEBUG_ERROR, "Could not find image in metadata: %g\n", image_guid));
+ PrintMetadata(fwu_metadata);
+ return NULL;
+ }
+
+ for (UINT32 index = 0; index < MAX_CAPSULES; ++index) {
+ if (outstanding_capsules[index].OngoingTransfer == FALSE) {
+ outstanding_capsules[index].OngoingTransfer = TRUE;
+ outstanding_capsules[index].bytes_filled = 0;
+
+ // TODO: move image offset retrival to a platform specific library.
+ ASSERT(update_index < FWU_NUM_BANKS);
+
+ // TODO: Obtain the image address from the
+ // 1) image_uuid and 2) the metadata and the update_index.
+ outstanding_capsules[index].next_offset = image_start[update_index];
+ DEBUG((DEBUG_INFO, "NextOffset set to %X\n, update_index=%u", image_start[update_index], update_index));
+
+ CopyGuid(&outstanding_capsules[index].image_guid, image_guid);
+
+ outstanding_capsules[index].img_idx = img_idx;
+ print_capsule_guids ();
+
+ *handle = index;
+ return &outstanding_capsules[index];
+ }
+ }
+
+ DEBUG((DEBUG_INFO, "Too many open capsule states. requested capsule: %g\n", image_guid));
+ print_capsule_guids ();
+ return NULL;
+}
+
+VOID drop_capsule_state (struct CapsuleState* state)
+{
+ state->OngoingTransfer = FALSE;
+
+ gMmst->MmFreePool (state->Buffer);
+
+ state->Buffer = NULL;
+ state->bytes_filled = 0;
+ state->next_offset = 0x123456;
+ state->img_idx = FWU_NUM_IMAGES;
+ CopyGuid(&state->image_guid, &nil_guid);
+}
+
+EFI_STATUS handle_read_stream(struct mm_fwu_read_arg *read_arg)
+{
+ struct mm_fwu_read_ret *read_ret = (struct mm_fwu_read_ret *)read_arg;
+ struct fw_image_info *img_info = (struct fw_image_info *)read_ret->payload;
+ UINT32 total_bytes;
+ UINT32 handle = read_arg->handle;
+ UINT32 active_index;
+
+ DEBUG((DEBUG_INFO, "fwu_read_arg-----------\n"));
+ DEBUG((DEBUG_INFO, "handle %d\n", handle));
+ DEBUG((DEBUG_INFO, "-----------------------\n"));
+
+ if(handle != IMG_INFO_HANDLE) {
+ /* XXX: Only reads to the image directory are implemented. */
+ DEBUG((DEBUG_ERROR, "Invalid parameter (handle=%d)\n", read_arg->handle));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ active_index = fwu_metadata->active_index;
+ img_info->num_images = FWU_NUM_IMAGES;
+ img_info->active_index = active_index;
+ img_info->boot_index = fwu_metadata->active_index; // TODO: obtain the boot_index from BL32
+
+ for (UINT32 index = 0; index < img_info->num_images; index++) {
+ DEBUG((DEBUG_INFO, "GUID: %g\n", &fwu_metadata->img_entry[index].image_uuid));
+ CopyGuid (&img_info->entries[index].image_guid, &fwu_metadata->img_entry[index].image_uuid);
+ img_info->entries[index].client_permissions = FWU_READ_PERM | FWU_WRITE_PERM;
+
+ /*
+ * maximum image size is determined by the minimum of the space allocated in flash
+ * for the set_0 and set_b images. On this prototype the value is 0x2400000.
+ */
+ img_info->entries[index].img_max_size = 240000; // TODO; get image max size from the GPT or PCD.
+ img_info->entries[index].lowest_acceptable_version = 0;
+ img_info->entries[index].img_version = 1; // TODO: Extract version information from the FIP.
+ img_info->entries[index].accepted = fwu_metadata->img_entry[index].img_bank_info[active_index].accepted;
+ }
+
+ // XXX total_bytes is assumed to be smaller than shared buffer size.
+ total_bytes = sizeof(struct fw_image_info) + img_info->num_images * sizeof(struct fw_image_info_entry);
+ read_ret->total_bytes = total_bytes;
+ read_ret->read_bytes = total_bytes;
+
+ DEBUG((DEBUG_INFO, "read result-----------\n"));
+ DEBUG((DEBUG_INFO, "handle %d\n",handle));
+ DEBUG((DEBUG_INFO, "total size %d\n",read_ret->total_bytes));
+ DEBUG((DEBUG_INFO, "read bytes %d\n",read_ret->read_bytes));
+
+ read_ret->status = EFI_SUCCESS;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_open(struct mm_fwu_open_arg *open_hdr)
+{
+ UINT8 *UpdateBuff = NULL;
+ EFI_STATUS Status;
+ struct mm_fwu_open_ret *RetBuf = (struct mm_fwu_open_ret *) open_hdr;
+ UINT32 handle;
+
+ if (CompareGuid(&open_hdr->image_guid, &fwu_directory_uuid)) {
+ DEBUG((EFI_D_INFO, "open FW directory\n"));
+
+ RetBuf->handle = IMG_INFO_HANDLE;
+
+ return EFI_SUCCESS;
+ }
+
+ struct CapsuleState* state = open_capsule(&open_hdr->image_guid, &handle);
+
+ if(!state) {
+ DEBUG((EFI_D_ERROR, "FWUpdate open: Too many outstanding capsules open\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ BlockSize*2,
+ (VOID **)&UpdateBuff
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "FWUpdate open: Failed to allocate buffer\n"));
+ drop_capsule_state (state);
+ return Status;
+ }
+
+ SetMem32(UpdateBuff, BlockSize*2, 0xdeadbeef);
+ state->Buffer = UpdateBuff;
+ RetBuf->handle = handle;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS write_to_buffer (struct CapsuleState* state,
+ UINT32 src_size, VOID *src_buff)
+{
+ EFI_STATUS Status;
+
+ UINT32 dst_bytes_filled = state->bytes_filled;
+ UINT32 write_size = MIN (src_size, BlockSize - dst_bytes_filled);
+
+ /* iterate until data_size is zero*/
+ while (src_size) {
+ VOID *dst_buff = state->Buffer + dst_bytes_filled;
+
+ DEBUG((EFI_D_INFO, "FWUpdate Write: from %X to %X copy at offset %X size %X\n",
+ src_buff, state->Buffer, dst_bytes_filled, write_size));
+
+ CopyMem(dst_buff, src_buff, write_size);
+
+ src_size -= write_size;
+ dst_bytes_filled += write_size;
+ src_buff += write_size;
+
+ if (dst_bytes_filled == BlockSize) {
+ // Buffer is full so write the buffer contents to NorFlash
+ DEBUG((EFI_D_INFO, "------------------------------\n"));
+ DEBUG((EFI_D_INFO, "FWUpdate Write: offset %X\n", state->next_offset));
+
+ Status = WriteBlock (
+ state->Buffer,
+ BlockSize,
+ state->next_offset
+ );
+
+ if (Status!=EFI_SUCCESS) {
+ return Status;
+ }
+
+ dst_bytes_filled = 0;
+ write_size = MIN (src_size, BlockSize);
+ state->next_offset += BlockSize;
+
+ DEBUG((EFI_D_INFO, " after write block --- src_size %d- write size %d--------------\n",
+ src_size, write_size));
+ }
+ }
+
+ state->bytes_filled = dst_bytes_filled;
+
+ DEBUG((EFI_D_INFO, "finish write buffer\n"));
+ return EFI_SUCCESS;
+}
+
+VOID close_buffer (struct CapsuleState* state)
+{
+ EFI_STATUS Status;
+ UINT32 dst_bytes_filled = state->bytes_filled;
+
+ /* return if no information is left in the buffer. */
+ if (!dst_bytes_filled)
+ goto out;
+
+ UINT32 write_size = BlockSize - dst_bytes_filled;
+
+ if (write_size) {
+ // Zero out the remainder of the buffer.
+ VOID *dst_buff = state->Buffer + dst_bytes_filled;
+
+ DEBUG((EFI_D_INFO, "FWUpdate close: zero out buffer remainder %X size %X\n", dst_buff, write_size));
+ SetMem(dst_buff, write_size, 0);
+ }
+
+ Status = WriteBlock (
+ state->Buffer,
+ BlockSize,
+ state->next_offset
+ );
+
+out:
+
+ drop_capsule_state (state);
+}
+
+/* TODO: move to plat specific library */
+#define QEMU_FWU_SECURE_BASE 0x40e19000
+#define QEMU_TRIAL_RUN_OFF 0x4
+#define QEMU_TRIAL_NVC_OFF 0x0
+#define QEMU_TRIAL_RUN_ADDR (QEMU_FWU_SECURE_BASE + QEMU_TRIAL_RUN_OFF)
+#define QEMU_TRIAL_NVC_ADDR (QEMU_FWU_SECURE_BASE + QEMU_TRIAL_NVC_OFF)
+
+EFI_STATUS handle_accept_image(struct mm_fwu_accept_arg *accept_arg)
+{
+ struct mm_fwu_accept_ret *accept_ret = (struct mm_fwu_accept_ret *)accept_arg;
+ EFI_GUID *accepted_uuid = &accept_arg->image_type_uuid;
+ ASSERT(fwu_metadata);
+
+ // TODO: Check if active_index == boot_index,
+ // return DENIED if not.
+
+ for (UINT32 idx=0; idx<FWU_NUM_IMAGES; idx++) {
+
+ if(!CompareGuid(&fwu_metadata->img_entry[idx].image_uuid, accepted_uuid)) {
+ UINT32 active_index = fwu_metadata->active_index;
+ fwu_metadata->img_entry[idx].img_bank_info[active_index].accepted = 1;
+ accept_ret->status = FWU_SUCCESS;
+ }
+ }
+
+ accept_ret->status = FWU_UNKNOWN;
+ return EFI_NOT_FOUND;
+}
+
+EFI_STATUS commit_metadata(INT32 nvc)
+{
+ EFI_STATUS Status;
+
+ if(any_image_open()) {
+ DEBUG((EFI_D_ERROR, "FWUpdate accept image: image instance open, cannot accept before closing\n"));
+ print_capsule_guids();
+ return EFI_NOT_READY;
+ }
+
+ Status = plat_write_metadata_region(fwu_metadata, fwu_metadata->active_index+1, fwu_metadata->active_index);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to write main metadata\n"));
+ return Status;
+ }
+
+ if (read_nv_ctr() < nvc)
+ write_nv_ctr(nvc);
+
+ DEBUG((EFI_D_INFO, "FW images accepted\n"));
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_write(struct mm_fwu_write_arg *write_hdr)
+{
+ EFI_STATUS ret = EFI_SUCCESS;
+ struct mm_fwu_write_ret *write_ret = (struct mm_fwu_write_ret *)write_hdr;
+ UINT32 handle = write_hdr->handle;
+ struct CapsuleState* state = find_capsule(handle);
+
+ UINT32 data_size = write_hdr->data_len;
+ VOID *src_buffer = write_hdr->payload;
+
+ DEBUG((EFI_D_INFO, "FWUpdate Write: size %d\n", data_size));
+
+ if (!state) {
+ DEBUG((EFI_D_ERROR, "FWUpdate Write: Cannot find context with handle %d\n", handle));
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /*
+ * Write payload to intermediate buffer. When the intermediate buffer
+ * is full it is written to flash.
+ *
+ * XXX NOTE: we could eventually allow unordered fw_update_writes.
+ * that requires either
+ * 1) tracking which fragments are updated
+ * 2) read sector->update fragment->write sector
+ */
+ write_to_buffer (state, data_size, src_buffer);
+
+out:
+
+ write_ret->status = ret;
+ return ret;
+}
+
+EFI_STATUS handle_discover(struct mm_fwu_discover_ret *discover_ret)
+{
+ discover_ret->status = FWU_SUCCESS;
+ discover_ret->version_major = FWU_MAJOR;
+ discover_ret->version_minor = FWU_MINOR;
+ discover_ret->num_func = FWU_N_FUNC;
+
+ discover_ret->function_presence[FWU_DISCOVER] = 1;
+ discover_ret->function_presence[FWU_BEGIN_STAGING] = 1;
+ discover_ret->function_presence[FWU_END_STAGING] = 1;
+ discover_ret->function_presence[FWU_CANCEL_STAGING] = 1;
+ discover_ret->function_presence[FWU_OPEN] = 1;
+ discover_ret->function_presence[FWU_WRITE_STREAM] = 1;
+ discover_ret->function_presence[FWU_READ_STREAM] = 1;
+ discover_ret->function_presence[FWU_COMMIT] = 1;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_commit(struct mm_fwu_commit_arg *commit_arg)
+{
+ struct mm_fwu_commit_ret *commit_ret = (struct mm_fwu_commit_ret *) commit_arg;
+ struct CapsuleState* state = find_capsule(commit_arg->handle);
+ BOOLEAN client_accept = commit_arg->acceptance_req;
+ UINT32 active_index;
+ UINT32 img_idx;
+
+ if (!state) {
+ DEBUG((EFI_D_ERROR, "FWUpdate Write: Cannot find capsule\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ active_index = fwu_metadata->active_index;
+ img_idx = state->img_idx;
+
+ if (!client_accept)
+ fwu_metadata->img_entry[img_idx].img_bank_info[active_index].accepted = 1;
+
+ close_buffer (state);
+
+ commit_ret->status = EFI_SUCCESS;
+ commit_ret->progress = 100;
+ commit_ret->total_work = 100;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_begin_staging()
+{
+ if (trial_state) {
+ DEBUG((EFI_D_ERROR, "FWU: begin staging called from a Trial state\n"));
+ return FWU_UNAVAILABLE;
+ }
+
+ if (Staging_state) {
+ DEBUG((EFI_D_WARN, "FWU: begin staging called from an ongoing staging state\n"));
+ /* TODO: reset any open file state. */
+
+ }
+
+ /*
+ * TODO: Call platform specific method to communicate to other entities in the system
+ * that the PE will access the NV storage.
+ */
+ Staging_state = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_end_staging()
+{
+ if (!Staging_state) {
+ DEBUG((EFI_D_ERROR, "FWU: end staging called outside of Staging state\n"));
+ return FWU_UNAVAILABLE;
+ }
+
+ trial_state = is_trial_state(fwu_metadata);
+
+ /* Trial run value set to the maximum consecutive retries in the Trial state. */
+ /* TODO: move to a platform specific library. */
+ /* TODO: define the trial_run value after accept from a PCD. */
+ // *(UINT32 *)QEMU_TRIAL_RUN_ADDR = 0x4;
+ Staging_state = FALSE;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_cancel_staging()
+{
+ if (!Staging_state) {
+ DEBUG((EFI_D_ERROR, "FWU: cancel staging called outside of Staging state\n"));
+ return FWU_UNAVAILABLE;
+ }
+
+ DEBUG((EFI_D_WARN, "FWU: Cancel staging\n"));
+ return EFI_SUCCESS;
+}
+
+VOID PrintInOutBuffer(const CHAR16 *Lbl, VOID *CommBuffer)
+{
+ DEBUG((DEBUG_INFO, "------ %s ------------------\n", Lbl));
+ for (int idx=0; idx<64; idx++) {
+ DEBUG((DEBUG_INFO, "%x ", ((UINT8 *)CommBuffer)[idx]));
+
+ if ((idx + 1) % 16 == 0)
+ DEBUG((DEBUG_INFO, "\n"));
+ }
+ DEBUG((DEBUG_INFO, "-----------------------------------\n"));
+}
+
+/**
+ * FWU SMI Handler entry.
+ */
+EFI_STATUS
+EFIAPI
+SmmFWBlobHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *RegisterContext,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ EFI_STATUS Status;
+ struct MmFwuHdr *SmmFwUpdateFunctionHeader;
+ UINTN TempCommBufferSize;
+
+ // If input is invalid, stop processing this SMI
+ //
+ if (CommBuffer == NULL || CommBufferSize == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ TempCommBufferSize = *CommBufferSize;
+
+ if (TempCommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
+ DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
+ return EFI_SUCCESS;
+ }
+
+ SmmFwUpdateFunctionHeader = (struct MmFwuHdr *)CommBuffer;
+
+ PrintInOutBuffer(L"FWU input", CommBuffer);
+
+ switch ((UINT32)SmmFwUpdateFunctionHeader->function) {
+ case FWU_DISCOVER:
+ Status = handle_discover((struct mm_fwu_discover_ret *) SmmFwUpdateFunctionHeader);
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "FWU: failed to perform protocol discovery\n"));
+ }
+
+ break;
+
+ case FWU_OPEN:
+ DEBUG((EFI_D_INFO, "FWU_OPEN:\n"));
+
+ Status = handle_open((struct mm_fwu_open_arg *) SmmFwUpdateFunctionHeader);
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "FWU: cannot allocate 256KB buffer\n"));
+ }
+
+ break;
+
+ case FWU_WRITE_STREAM:
+ DEBUG((EFI_D_INFO, "FWU_WRITE_STREAM\n"));
+
+ Status = handle_write((struct mm_fwu_write_arg *)SmmFwUpdateFunctionHeader);
+
+ break;
+
+ case FWU_COMMIT:
+ DEBUG((EFI_D_INFO, "FWU_COMMIT:\n"));
+
+ Status = handle_commit((struct mm_fwu_commit_arg *)SmmFwUpdateFunctionHeader);
+
+ break;
+
+ case FWU_ACCEPT_IMAGE:
+ DEBUG((EFI_D_INFO, "FWU_ACCEPT_IMAGE\n"));
+
+ Status = handle_accept_image((struct mm_fwu_accept_arg *)SmmFwUpdateFunctionHeader);
+
+ break;
+
+ case FWU_READ_STREAM:
+ DEBUG((EFI_D_INFO, "FWU_READ_STREAM:\n"));
+
+ Status = handle_read_stream((struct mm_fwu_read_arg *)SmmFwUpdateFunctionHeader);
+ break;
+
+ case FWU_BEGIN_STAGING:
+ DEBUG((EFI_D_INFO, "FWU_BEGIN_STAGING:\n"));
+
+ Status = handle_begin_staging();
+
+ break;
+
+ case FWU_END_STAGING:
+ DEBUG((EFI_D_INFO, "FWU_END_STAGING:\n"));
+ commit_metadata(0);
+ Status = handle_end_staging();
+
+ break;
+
+ case FWU_CANCEL_STAGING:
+ DEBUG((EFI_D_INFO, "FWU_CANCEL_STAGING:\n"));
+
+ Status = handle_cancel_staging();
+ break;
+
+ default:
+ DEBUG((EFI_D_ERROR, "FWU function %d unsupported\n", SmmFwUpdateFunctionHeader->function));
+ Status = EFI_UNSUPPORTED;
+ }
+
+ SmmFwUpdateFunctionHeader->status = Status;
+
+ PrintInOutBuffer(L"FWU output", CommBuffer);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Initialize the FWUpdate backend module.
+
+ @retval EFI_SUCCESS FWUpdate successfully initialized.
+ @retval otherwuse FWUpdate initialization failed.
+**/
+EFI_STATUS
+EFIAPI
+FWUpdateInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_MM_SYSTEM_TABLE *MmSystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE FWUpdateHandle;
+
+ static EFI_GUID new_guid = EFI_SMM_FW_UPDATE_PROTOCOL_GUID;
+
+ Status = InitFWUpdateStore();
+ if(Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to initialize FW store\n"));
+ }
+
+ Status = gMmst->MmiHandlerRegister (SmmFWBlobHandler, &new_guid, &FWUpdateHandle);
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG((EFI_D_INFO, "FWUpdate successful init\n"));
+ return Status;
+}
+
+
diff --git a/Drivers/FWUpdate/FWUpdate.inf b/Drivers/FWUpdate/FWUpdate.inf
new file mode 100644
index 00000000..46ed464a
--- /dev/null
+++ b/Drivers/FWUpdate/FWUpdate.inf
@@ -0,0 +1,137 @@
+## @file
+# Provides SMM variable service.
+#
+# This module installs SMM variable protocol into SMM protocol database,
+# which can be used by SMM driver, and installs SMM variable protocol
+# into BS protocol database, which can be used to notify the SMM Runtime
+# Dxe driver that the SMM variable service is ready.
+# This module should be used with SMM Runtime DXE module together. The
+# SMM Runtime DXE module would install variable arch protocol and variable
+# write arch protocol based on SMM variable module.
+#
+# Caution: This module requires additional review when modified.
+# This driver will have external input - variable data and communicate buffer in SMM mode.
+# This external input must be validated carefully to avoid security issues such as
+# buffer overflow or integer overflow.
+# The whole SMM authentication variable design relies on the integrity of flash part and SMM.
+# which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory
+# may not be modified without authorization. If platform fails to protect these resources,
+# the authentication service provided in this driver will be broken, and the behavior is undefined.
+#
+# Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = FWUpdateMm
+ FILE_GUID = 23A089B3-EED5-4ac5-B2AB-43E3298C2343
+ MODULE_TYPE = MM_STANDALONE
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x00010032
+ ENTRY_POINT = FWUpdateInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64
+#
+
+
+[Sources]
+ FWUpdate.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ StandaloneMmPkg/StandaloneMmPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ HobLib
+ MemoryAllocationLib
+ MmServicesTableLib
+ StandaloneMmDriverEntryPoint
+ SynchronizationLib
+
+[Protocols]
+ gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES
+ ## CONSUMES
+ ## NOTIFY
+ gEfiSmmFaultTolerantWriteProtocolGuid
+ ## PRODUCES
+ ## UNDEFINED # SmiHandlerRegister
+ gEfiSmmVariableProtocolGuid
+ gEfiMmEndOfDxeProtocolGuid ## NOTIFY
+ gEdkiiSmmVarCheckProtocolGuid ## PRODUCES
+
+ gEfiBlockIoProtocolGuid ## Consumes
+ gEfiFirmwareVolumeBlockProtocolGuid ## Consumes
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # Signature of Variable store header
+ ## SOMETIMES_PRODUCES ## GUID # Signature of Variable store header
+ ## SOMETIMES_CONSUMES ## HOB
+ ## SOMETIMES_PRODUCES ## SystemTable
+ gEfiAuthenticatedVariableGuid
+
+ ## SOMETIMES_CONSUMES ## GUID # Signature of Variable store header
+ ## SOMETIMES_PRODUCES ## GUID # Signature of Variable store header
+ ## SOMETIMES_CONSUMES ## HOB
+ ## SOMETIMES_PRODUCES ## SystemTable
+ gEfiVariableGuid
+
+ gArmInitData
+
+ ## SOMETIMES_CONSUMES ## Variable:L"PlatformLang"
+ ## SOMETIMES_PRODUCES ## Variable:L"PlatformLang"
+ ## SOMETIMES_CONSUMES ## Variable:L"Lang"
+ ## SOMETIMES_PRODUCES ## Variable:L"Lang"
+ gEfiGlobalVariableGuid
+
+ gEfiMemoryOverwriteControlDataGuid ## SOMETIMES_CONSUMES ## Variable:L"MemoryOverwriteRequestControl"
+ gEfiMemoryOverwriteRequestControlLockGuid ## SOMETIMES_PRODUCES ## Variable:L"MemoryOverwriteRequestControlLock"
+
+ gEfiSystemNvDataFvGuid ## CONSUMES ## GUID
+ gEdkiiFaultTolerantWriteGuid ## SOMETIMES_CONSUMES ## HOB
+
+ ## SOMETIMES_CONSUMES ## Variable:L"VarErrorFlag"
+ ## SOMETIMES_PRODUCES ## Variable:L"VarErrorFlag"
+ gEdkiiVarErrorFlagGuid
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 ## CONSUMES
+
+[FixedPcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFWUpdateBaseAddress ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFWUpdateAImageOffset ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFWUpdateBImageOffset ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMetadataMainOffset
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMetadataFallbackOffset
+
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFWUpdateSWdNvCtrOffset
+
+ gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVolatileVariableSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdHwErrStorageSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxUserNvVariableSpaceSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdBoottimeReservedNvVariableSpaceSize ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdReclaimVariableSpaceAtEndOfDxe ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvStoreReserved ## SOMETIMES_CONSUMES
+
+[FeaturePcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## CONSUMES # statistic the information of variable.
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangDeprecate ## CONSUMES # Auto update PlatformLang/Lang
+
+[Depex]
+ TRUE
diff --git a/Drivers/FWUpdate/SmmFirmwareUpdate.h b/Drivers/FWUpdate/SmmFirmwareUpdate.h
new file mode 100644
index 00000000..1b5d916b
--- /dev/null
+++ b/Drivers/FWUpdate/SmmFirmwareUpdate.h
@@ -0,0 +1,191 @@
+/** @file
+ EFI SMM Firmware Update
+
+Copyright (c) 2021 Arm Ltd.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __SMM_FW_UPDATE_H__
+#define __SMM_FW_UPDATE_H__
+
+#include <stdint.h>
+#include <Base.h>
+
+#define EFI_SMM_FW_UPDATE_PROTOCOL_GUID \
+ { \
+ 0xed32d533, 0x99e6, 0x4209, { 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa8} \
+ }
+
+/* temporary FIP GUID: FB90808A-BA9A-4D42-B9A2-A7A937144AEE */
+#define TEMP_TFA_FIP_GUID { \
+ 0xFB90808A, 0xBA9A, 0x4D42, {0xA2, 0xB9, 0xEE, 0xA4, 0x14, 0x37, 0xA9, 0xA7} \
+ }
+
+#define FWU_DIRECTORY_UUID { \
+ 0xdeee58d9, 0x5147, 0x4ad3, {0x90, 0xa2, 0xa5, 0x41, 0x23, 0x6e, 0x66, 0x77} \
+ }
+
+#define NIL_UUID { \
+ 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} \
+ }
+static const EFI_GUID nil_guid = NIL_UUID;
+static const EFI_GUID fwu_directory_uuid = FWU_DIRECTORY_UUID;
+
+extern EFI_GUID gEfiSmmFwUpdateProtocolGuid;
+
+#define FWU_SUCCESS INT32_C(0)
+#define FWU_UNKNOWN INT32_C(-1)
+#define FWU_UNAVAILABLE INT32_C(-2)
+#define FWU_OUT_OF_BOUNDS INT32_C(-3)
+#define FWU_AUTH_FAIL INT32_C(-4)
+#define FWU_NO_PERM INT32_C(-5)
+
+#define FWU_DISCOVER 0
+#define FWU_BEGIN_STAGING 1
+#define FWU_END_STAGING 2
+#define FWU_CANCEL_STAGING 3
+#define FWU_OPEN 4
+#define FWU_WRITE_STREAM 5
+#define FWU_READ_STREAM 6
+#define FWU_COMMIT 7
+#define FWU_ACCEPT_IMAGE 8
+#define FWU_N_FUNC 8
+
+#define FWU_MAJOR 1
+#define FWU_MINOR 0
+
+#define FWU_READ_PERM (1<<1)
+#define FWU_WRITE_PERM (1)
+
+struct MmFwuHdr {
+ union {
+ UINT32 function;
+ UINT32 status;
+ };
+};
+
+/* Number of images per-set */
+#define FWU_NUM_IMAGES 1
+/* Number of sets */
+#define FWU_NUM_BANKS 2
+
+struct img_bank_info_t {
+
+ EFI_GUID img_uuid;
+ UINT32 accepted;
+ UINT32 reserved;
+
+} PACKED;
+
+struct fw_image_metadata_entry {
+
+ EFI_GUID image_uuid;
+ EFI_GUID location_uuid;
+
+ /* Address of image A and image B */
+ struct img_bank_info_t img_bank_info[FWU_NUM_BANKS];
+
+} PACKED;
+
+struct fwu_metadata {
+
+ UINT32 crc_32;
+ UINT32 version;
+
+ UINT32 active_index;
+ UINT32 previous_active_index;
+
+ struct fw_image_metadata_entry img_entry[FWU_NUM_IMAGES];
+} PACKED;
+
+#define FWU_METADATA_SIZE sizeof(struct fwu_metadata)
+
+struct mm_fwu_open_arg {
+ UINT32 func_id;
+ EFI_GUID image_guid;
+} PACKED;
+
+struct mm_fwu_open_ret {
+ UINT32 status;
+ UINT32 handle;
+} PACKED;
+
+struct mm_fwu_read_arg {
+ UINT32 func_id;
+ UINT32 handle;
+} PACKED;
+
+struct mm_fwu_read_ret {
+ UINT32 status;
+ UINT32 read_bytes;
+ UINT32 total_bytes;
+ UINT8 payload[];
+} PACKED;
+
+struct mm_fwu_write_arg {
+ UINT32 func_id;
+ UINT32 handle;
+ UINT32 data_len;
+ UINT8 payload[];
+} PACKED;
+
+struct mm_fwu_write_ret {
+ UINT32 status;
+} PACKED;
+
+struct mm_fwu_commit_arg {
+ UINT32 func_id;
+ UINT32 handle;
+ UINT32 acceptance_req;
+ UINT32 max_atomic_len;
+} PACKED;
+
+struct mm_fwu_commit_ret {
+ UINT32 status;
+ UINT32 progress;
+ UINT32 total_work;
+} PACKED;
+
+struct mm_fwu_discover_arg {
+ UINT32 func_id;
+};
+
+struct mm_fwu_discover_ret {
+ UINT32 status;
+ UINT8 version_major;
+ UINT8 version_minor;
+ UINT16 num_func;
+ UINT8 function_presence[];
+} PACKED;
+
+struct mm_fwu_accept_arg {
+ UINT32 func_id;
+ UINT32 reserved;
+ EFI_GUID image_type_uuid;
+} PACKED;
+
+struct mm_fwu_accept_ret {
+ UINT32 status;
+};
+
+// FW Directory version 1
+struct fw_image_info_entry {
+ EFI_GUID image_guid;
+ UINT32 client_permissions;
+ UINT32 img_max_size;
+ UINT32 lowest_acceptable_version;
+ UINT32 img_version;
+ UINT32 accepted;
+ UINT32 reserved;
+}PACKED;
+
+struct fw_image_info {
+ UINT32 directory_version;
+ UINT32 num_images;
+ UINT32 active_index;
+ UINT32 boot_index;
+ struct fw_image_info_entry entries[];
+}PACKED;
+
+#endif // __SMM_FW_UPDATE_H__