summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Marinho <jose.marinho@arm.com>2020-07-05 13:54:40 +0100
committerjmarinho <jose.marinho@arm.com>2021-01-29 16:51:45 +0000
commitf1bed4857ce7274c41ff02f373a53ae3e22deaeb (patch)
tree4c634d6930e79d4cff3c2a08c7275463dd40132f
parent3cbc88e66b0a32bf6951596f1aadc55a5aa0bcaf (diff)
Implement FW Update backendfwu_update
This commit implements the ABI specified in DEN0118 at Alpha quality[1], used to communicate FW images from the Normal World to an entity responsible for writing these to flash. [1] https://developer.arm.com/-/media/Files/pdf/PlatformSecurityArchitecture/Architect/FWU-PSA-A_DEN0118_1.0ALP2.pdf Signed-off-by: jmarinho <jose.marinho@arm.com>
-rw-r--r--Drivers/FWUpdate/FWUpdate.c1096
-rw-r--r--Drivers/FWUpdate/FWUpdate.inf137
-rw-r--r--Drivers/FWUpdate/SmmFirmwareUpdate.h146
3 files changed, 1379 insertions, 0 deletions
diff --git a/Drivers/FWUpdate/FWUpdate.c b/Drivers/FWUpdate/FWUpdate.c
new file mode 100644
index 00000000..8866dc7d
--- /dev/null
+++ b/Drivers/FWUpdate/FWUpdate.c
@@ -0,0 +1,1096 @@
+/** @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"
+
+static EFI_BLOCK_IO_PROTOCOL *BlockIoProtocol;
+static const EFI_GUID temp_tfa_fip_guid = TEMP_TFA_FIP_GUID;
+
+BOOLEAN Staging_state = FALSE;
+BOOLEAN mAtRuntime = FALSE;
+UINT8 *mVariableBufferPayload = NULL;
+
+//static EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *VolumeProtocol;
+
+struct fwu_metadata *fwu_metadata = NULL;
+
+#define FWUPDATE_BLOCK_SIZE (256*1024)
+
+inline static UINT32 compute_metadata_crc32(struct fwu_metadata *metadata)
+{
+ ASSERT(metadata);
+
+ return CalculateCrc32(&metadata->metadata_version, FWU_METADATA_SIZE-4);
+}
+
+
+EFI_STATUS WriteBlock ( UINT8 *Buff, UINT32 BuffSize, UINT32 Offset);
+
+/* TODO: move to a platform specific library. */
+EFI_STATUS plat_write_metadata_region(struct fwu_metadata *metadata, UINT32 active_index)
+{
+ VOID *Buffer = NULL;
+ EFI_STATUS Status;
+ ASSERT(metadata);
+
+ UINT32 metadata_main_offset = PcdGet32 (PcdMetadataMainOffset);
+ UINT32 metadata_fallback_offset = PcdGet32 (PcdMetadataFallbackOffset);
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ FWUPDATE_BLOCK_SIZE,
+ (VOID **)&Buffer
+ );
+
+
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG((EFI_D_ERROR, "Failed to allocate buffer to write metadata.\n"));
+ return Status;
+ }
+
+ SetMem32(Buffer, FWUPDATE_BLOCK_SIZE, 0xbadbadba);
+
+ metadata->active_index = active_index % 2;
+ metadata->update_index = (active_index + 1) % 2;
+
+ metadata->header_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, "update index %lX\n", metadata->update_index));
+ DEBUG((EFI_D_INFO, "crc32 %lX\n", metadata->header_crc_32));
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+
+ CopyMem(Buffer, metadata, FWU_METADATA_SIZE);
+
+ Status = WriteBlock (
+ Buffer,
+ FWUPDATE_BLOCK_SIZE,
+ metadata_main_offset
+ );
+
+ if (Status != EFI_SUCCESS)
+ {
+ DEBUG((EFI_D_ERROR, "Failed to write main metadata.\n"));
+ goto out;
+ }
+
+ Status = WriteBlock (
+ Buffer,
+ FWUPDATE_BLOCK_SIZE,
+ 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;
+
+ UINT32 ctr_offset = PcdGet32 (PcdFWUpdateSWdNvCtrOffset);
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ FWUPDATE_BLOCK_SIZE,
+ (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, FWUPDATE_BLOCK_SIZE, 0xdeadbeef);
+ *(UINT32 *)Buffer = ctr;
+
+ Status = WriteBlock (
+ Buffer,
+ FWUPDATE_BLOCK_SIZE,
+ ctr_offset
+ );
+
+out:
+ gMmst->MmFreePool (Buffer);
+
+ return Status;
+
+}
+BOOLEAN CheckMetadata (EFI_PHYSICAL_ADDRESS metadata_address)
+{
+ /* TODO: compute whole block CRC and compare against CRC metadata field. */
+ struct fwu_metadata *metadata = (struct fwu_metadata *)metadata_address;
+
+ /*
+ * XXX: hack for prototype purpose,
+ * assumes that if A_B_selector is greater than zero then metadata is properly initialized.
+ */
+ return metadata->active_index != 0;
+}
+
+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->header_crc_32;
+
+ if (!intact)
+ DEBUG((EFI_D_INFO, "Metadata region is corrupted\n"));
+ else
+ DEBUG((EFI_D_INFO, "Metadata region is intact\n"));
+
+ return intact;
+}
+
+void bootstrap_metadata(struct fwu_metadata *metadata)
+{
+ UINT32 a_img_offset = PcdGet32 (PcdFWUpdateAImageOffset);
+ UINT32 b_img_offset = PcdGet32 (PcdFWUpdateBImageOffset);
+
+ DEBUG((EFI_D_INFO, "----metadata bootstrap---------\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"));
+
+ metadata->metadata_version = 1;
+ metadata->active_index = 0;
+ metadata->update_index = 1;
+
+ // 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->image[0].image_guid, &temp_tfa_fip_guid);
+ metadata->image[0].image_start[0] = a_img_offset;
+ metadata->image[0].image_start[1] = b_img_offset;
+
+ /*
+ * 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.
+ *
+ */
+ metadata->image[0].maximum_image_size = 0x240000;
+
+
+ metadata->header_crc_32 = compute_metadata_crc32(metadata);
+}
+
+void print_metadata(struct fwu_metadata *metadata)
+{
+ DEBUG((EFI_D_INFO, "---metadata--------------------\n"));
+ DEBUG((EFI_D_INFO, "header_crc_32 %lX\n", metadata->header_crc_32));
+ DEBUG((EFI_D_INFO, "metadata_version %lX\n", metadata->metadata_version ));
+ DEBUG((EFI_D_INFO, "active_index %lX\n", metadata->active_index ));
+ DEBUG((EFI_D_INFO, "update_index %lX\n", metadata->update_index ));
+ DEBUG((EFI_D_INFO, "image[0].image_guid %g\n", &metadata->image[0].image_guid));
+ DEBUG((EFI_D_INFO, "image[0].image_start[0] %lX\n", metadata->image[0].image_start[0]));
+ DEBUG((EFI_D_INFO, "image[0].image_start[1] %lX\n", metadata->image[0].image_start[1]));
+ DEBUG((EFI_D_INFO, "image[0].maximum_image_size %lX\n", metadata->image[0].maximum_image_size));
+ DEBUG((EFI_D_INFO, "-------------------------------\n"));
+}
+
+/*
+ * Read the metadata from the storage device.
+ * TODO: move to a platform specific library.
+ */
+EFI_STATUS refresh_metadata(void *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,
+ FWUPDATE_BLOCK_SIZE,
+ (VOID **)&Buffer
+ );
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to allocate buffer\n"));
+ return Status;
+ }
+
+ EFI_STATUS ReadBlock (UINT8 *Buff, UINT32 BuffSize, UINT32 Offset);
+ EFI_STATUS WriteBlock ( UINT8 *Buff, UINT32 BuffSize, UINT32 Offset);
+
+ Status = ReadBlock(Buffer, FWUPDATE_BLOCK_SIZE, metadata_main_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);
+ main_intact = TRUE;
+ }
+
+ Status = ReadBlock(Buffer, FWUPDATE_BLOCK_SIZE, 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)
+ {
+ Status = WriteBlock(metadata, FWUPDATE_BLOCK_SIZE, metadata_main_offset);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to correct fallbak metadata\n"));
+ return Status;
+ }
+ }
+
+ if(!fallback_intact)
+ {
+ Status = WriteBlock(metadata, FWUPDATE_BLOCK_SIZE, metadata_fallback_offset);
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to correct main metadata\n"));
+ return Status;
+ }
+ }
+
+ print_metadata(metadata);
+ 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"));
+
+ Status = gMmst->MmLocateProtocol (
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ (VOID **) &BlockIoProtocol
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed to locate Fvb instance\n"));
+ return Status;
+ }
+
+ 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;
+ }
+
+ return Status;
+}
+
+/**
+ * Read from the NorFlash.
+ */
+EFI_STATUS
+ReadBlock (
+ UINT8 *Buff,
+ UINT32 BuffSize,
+ UINT32 Offset
+ )
+{
+
+ EFI_STATUS Status;
+ UINT32 BlockSize = 256*1024;
+ 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->ReadBlocks (
+ BlockIoProtocol,
+ 0,
+ Lba,
+ BuffSize,
+ Buff
+ );
+
+ if (Status != EFI_SUCCESS) {
+ DEBUG((EFI_D_ERROR, "Failed read block\n"));
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * Write into the NorFlash.
+ */
+EFI_STATUS
+WriteBlock (
+ UINT8 *Buff,
+ UINT32 BuffSize,
+ UINT32 Offset
+ )
+{
+
+ EFI_STATUS Status;
+ UINT32 BlockSize = 256*1024;
+ 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;
+ }
+ 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;
+
+} 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_N;
+ DEBUG((DEBUG_INFO, "Open capsule guid: %g\n", image_guid));
+
+ for (UINT32 index = 0; index < FWU_N; ++index)
+ {
+ if (CompareGuid(image_guid, &fwu_metadata->image[index].image_guid))
+ {
+ img_idx = index;
+ DEBUG((DEBUG_INFO, "found image in metadata at index %d\n", index));
+ }
+ }
+ if (!(img_idx < FWU_N))
+ {
+ DEBUG((DEBUG_ERROR, "Could not find image in metadata: %g\n", image_guid));
+ print_metadata(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(fwu_metadata->update_index < FWU_K);
+ outstanding_capsules[index].next_offset =
+ fwu_metadata->image[img_idx].image_start[fwu_metadata->update_index];
+
+ CopyGuid(&outstanding_capsules[index].image_guid, image_guid);
+
+ 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 = 0;
+ CopyGuid(&state->image_guid, &nil_guid);
+}
+
+EFI_STATUS handle_read_stream(UINT8 *payload)
+{
+ struct fwu_read_stream_hdr *bulkin_hdr = (struct fwu_read_stream_hdr *)payload;
+ struct fw_image_info *img_info = (struct fw_image_info *)bulkin_hdr->payload;
+
+ if(bulkin_hdr->handle != IMG_INFO_HANDLE)
+ {
+ /* XXX: Only reads to the image directory are implemented. */
+ return EFI_INVALID_PARAMETER;
+ }
+
+ img_info->num_images = FWU_N;
+
+ for (UINT32 index = 0; index < img_info->num_images; index++)
+ {
+
+ DEBUG((DEBUG_INFO, "Returned GUID: %g\n", &fwu_metadata->image[index].image_guid));
+ CopyGuid (&img_info->entries[index].image_guid, &fwu_metadata->image[index].image_guid);
+ }
+
+ bulkin_hdr->total_size = sizeof(struct fw_image_info) + img_info->num_images * sizeof(struct fw_image_info_entry);
+ bulkin_hdr->fragment_size = bulkin_hdr->total_size;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_open(UINT8 *payload)
+{
+ UINT8 *UpdateBuff = NULL;
+ EFI_STATUS Status;
+
+ struct mm_fw_update_open *open_hdr =
+ (struct mm_fw_update_open *) payload;
+
+ if (CompareGuid(&open_hdr->image_guid, &fwu_directory_uuid)) {
+ DEBUG((EFI_D_INFO, "open FW directory\n"));
+ open_hdr->ret_handle = IMG_INFO_HANDLE;
+ return EFI_SUCCESS;
+ }
+
+ struct CapsuleState* state = open_capsule(&open_hdr->image_guid, &open_hdr->ret_handle);
+
+ if(!state) {
+ DEBUG((EFI_D_ERROR, "FWUpdate open: Too many outstanding capsules open\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gMmst->MmAllocatePool (
+ EfiRuntimeServicesData,
+ FWUPDATE_BLOCK_SIZE*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, FWUPDATE_BLOCK_SIZE*2, 0xdeadbeef);
+
+ state->Buffer = UpdateBuff;
+
+ 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, FWUPDATE_BLOCK_SIZE - 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 %X %X \n",
+ src_buff, state->Buffer, dst_bytes_filled, write_size,
+ ((UINT32 *)src_buff)[(src_size/4)-2],
+ ((UINT32 *)src_buff)[(src_size/4)-1])
+ );
+
+ CopyMem(dst_buff, src_buff, write_size);
+
+ src_size -= write_size;
+ dst_bytes_filled += write_size;
+ src_buff += write_size;
+
+ if (dst_bytes_filled == FWUPDATE_BLOCK_SIZE) {
+ // 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,
+ FWUPDATE_BLOCK_SIZE,
+ state->next_offset
+ );
+
+ if (Status!=EFI_SUCCESS)
+ {
+ return Status;
+ }
+
+ dst_bytes_filled = 0;
+ write_size = MIN (src_size, FWUPDATE_BLOCK_SIZE);
+ state->next_offset += FWUPDATE_BLOCK_SIZE;
+ }
+ }
+
+ state->bytes_filled = dst_bytes_filled;
+
+ 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 = FWUPDATE_BLOCK_SIZE - 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,
+ FWUPDATE_BLOCK_SIZE,
+ 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_complete_trial_run(UINT32 trial_run, UINT32 nvc)
+{
+ EFI_STATUS Status;
+
+ if (!trial_run)
+ {
+ /* system is not in trial run, hence image accept is not defined. */
+
+ DEBUG((EFI_D_ERROR, "System is not in trial run\n"));
+ return EFI_NOT_STARTED;
+ }
+
+ 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);
+ 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);
+
+ /* Trial run value set to the maximum consecurtive retries in the Trial state. */
+ /* TODO: move to a platform specific library. */
+ *(UINT32 *)QEMU_TRIAL_RUN_ADDR = 0x0;
+
+ DEBUG((EFI_D_INFO, "FW images accepted\n"));
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_write(UINT8 *payload)
+{
+ struct mm_fw_update_write *write_hdr =
+ (struct mm_fw_update_write *) payload;
+ struct CapsuleState* state = find_capsule(write_hdr->handle);
+
+ UINT32 offset = write_hdr->offset;
+ UINT32 data_size = write_hdr->data_size;
+ VOID *src_buffer = write_hdr->fw_fragment;
+
+ DEBUG((EFI_D_INFO, "FWUpdate Write: offset %d, size %d\n",
+ offset, data_size));
+
+ if (!state)
+ {
+ DEBUG((EFI_D_ERROR, "FWUpdate Write: Cannot find capsule\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ /*
+ * 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);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS handle_close(UINT8 *payload)
+{
+ struct mm_fw_update_close *close_hdr =
+ (struct mm_fw_update_close *) payload;
+ struct CapsuleState* state = find_capsule(close_hdr->handle);
+
+ if (!state)
+ {
+ DEBUG((EFI_D_ERROR, "FWUpdate Write: Cannot find capsule\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+ close_buffer (state);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS handle_trial_run_check(UINT8 *payload, UINT32 trial_run)
+{
+ struct fwu_trial_run_check_ret *trial_run_check = (struct fwu_trial_run_check_ret *)payload;
+
+ trial_run_check->trial_status = !!trial_run;
+
+ return EFI_SUCCESS;
+}
+
+
+
+EFI_STATUS handle_begin_staging(UINT32 trial_run)
+{
+ if (trial_run) {
+ 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(UINT32 trial_run)
+{
+
+ if (!Staging_state) {
+ DEBUG((EFI_D_ERROR, "FWU: begin staging called outside of Staging state\n"));
+ return FWU_UNAVAILABLE;
+ }
+
+ ASSERT(!trial_run);
+
+ /* Trial run value set to the maximum consecurtive 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(UINT32 trial_run)
+{
+ 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;
+}
+
+/**
+ Communication service SMI Handler entry.
+
+ This SMI handler provides services for the variable wrapper driver.
+
+ Caution: This function may receive untrusted input.
+ This variable data and communicate buffer are external input, so this function will do basic validation.
+ Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
+ VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
+ SmmVariableGetStatistics() should also do validation based on its own knowledge.
+
+ @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
+ @param[in] RegisterContext Points to an optional handler context which was specified when the
+ handler was registered.
+ @param[in, out] CommBuffer A pointer to a collection of data in memory that will
+ be conveyed from a non-SMM environment into an SMM environment.
+ @param[in, out] CommBufferSize The size of the CommBuffer.
+
+ @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
+ should still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
+ still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
+ be called.
+ @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
+**/
+
+//extern UINT32 FWU_NV_COUNTER;
+
+EFI_STATUS
+EFIAPI
+SmmFWBlobHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *RegisterContext,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ EFI_STATUS Status;
+ struct mm_fw_update_header *SmmFwUpdateFunctionHeader;
+ UINTN TempCommBufferSize;
+
+ struct ArmInitData {
+ UINT32 trial_run;
+ UINT32 nvc;
+ } armInitDataP;
+
+ armInitDataP.trial_run = *(UINT32 *)QEMU_TRIAL_RUN_ADDR;
+ armInitDataP.nvc = *(UINT32 *)QEMU_TRIAL_NVC_ADDR;
+
+ DEBUG((EFI_D_INFO, "armInitDataP %X TRIAL_RUN %d NVC %d\n", &armInitDataP,
+ armInitDataP.trial_run, armInitDataP.nvc));
+
+
+ // 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 mm_fw_update_header *)CommBuffer;
+ switch (SmmFwUpdateFunctionHeader->function) {
+ case FWU_OPEN:
+ DEBUG((EFI_D_INFO, "FWU_OPEN:\n"));
+
+ (void) outstanding_capsules;
+
+
+
+ Status = handle_open(SmmFwUpdateFunctionHeader->payload);
+
+ 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(SmmFwUpdateFunctionHeader->payload);
+
+ break;
+
+ case FWU_CLOSE:
+ DEBUG((EFI_D_INFO, "FWU_CLOSE:\n"));
+
+ Status = handle_close(SmmFwUpdateFunctionHeader->payload);
+
+ break;
+
+ case FWU_COMPLETE_TRIAL_RUN:
+ DEBUG((EFI_D_INFO, "FWU_COMPLETE_TRIAL_RUN: trial_run %d, nv_ctr %d\n", armInitDataP.trial_run, armInitDataP.nvc));
+
+ Status = handle_complete_trial_run(armInitDataP.trial_run, armInitDataP.nvc);
+
+ break;
+
+ case FWU_READ_STREAM:
+ DEBUG((EFI_D_INFO, "FWU_READ_STREAM:\n"));
+
+ Status = handle_read_stream(SmmFwUpdateFunctionHeader->payload);
+ break;
+
+ case FWU_TRIAL_RUN_CHECK:
+ DEBUG((EFI_D_INFO, "FWU_TRIAL_RUN_CHECK:\n"));
+
+ Status = handle_trial_run_check(SmmFwUpdateFunctionHeader->payload, armInitDataP.trial_run);
+
+ break;
+
+ case FWU_BEGIN_STAGING:
+ DEBUG((EFI_D_INFO, "FWU_BEGIN_STAGING:\n"));
+
+ Status = handle_begin_staging(armInitDataP.trial_run);
+
+ break;
+
+ case FWU_END_STAGING:
+ DEBUG((EFI_D_INFO, "FWU_END_STAGING:\n"));
+
+ Status = handle_end_staging(armInitDataP.trial_run);
+
+ break;
+
+ case FWU_CANCEL_STAGING:
+ DEBUG((EFI_D_INFO, "FWU_CANCEL_STAGING:\n"));
+
+ Status = handle_cancel_staging(armInitDataP.trial_run);
+ break;
+
+ default:
+ DEBUG((EFI_D_ERROR, "FWU function %d unsupported\n", SmmFwUpdateFunctionHeader->function));
+ Status = EFI_UNSUPPORTED;
+ }
+
+ SmmFwUpdateFunctionHeader->ret_status = Status;
+
+ 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;
+
+ DEBUG((EFI_D_ERROR, "FWUpdate Init =================================\n"));
+ Status = InitFWUpdateStore();
+
+ Status = gMmst->MmiHandlerRegister (SmmFWBlobHandler, &new_guid, &FWUpdateHandle);
+ ASSERT_EFI_ERROR (Status);
+
+
+ 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..b0a1721e
--- /dev/null
+++ b/Drivers/FWUpdate/SmmFirmwareUpdate.h
@@ -0,0 +1,146 @@
+/** @file
+ EFI SMM Firmware Update
+**/
+
+
+#ifndef __SMM_FW_UPDATE_H__
+#define __SMM_FW_UPDATE_H__
+
+#include <stdint.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 */
+#if 1
+#define TEMP_TFA_FIP_GUID { \
+ 0xFB90808A, 0xBA9A, 0x4D42, {0xA2, 0xB9, 0xEE, 0xA4, 0x14, 0x37, 0xA9, 0xA7} \
+}
+#else
+#define TEMP_TFA_FIP_GUID { \
+ 0xAE13ff2D, 0x9ad4, 0x4e25, {0x9a, 0xc8, 0x6D, 0x80, 0xb3, 0xb2, 0x21, 0x47} \
+}
+#endif
+
+#define FWU_DIRECTORY_UUID { \
+ 0xdeee58d9, 0x5147, 0x4ad3, {0x90, 0xa2, 0xa5, 0x41, 0x23, 0x6e, 0x66, 0x77} \
+}
+
+#define NIL_GUID { \
+ 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} \
+}
+static const EFI_GUID nil_guid = NIL_GUID;
+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_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_CLOSE 7
+#define FWU_COMPLETE_TRIAL_RUN 8
+
+
+#define FWU_TRIAL_RUN_CHECK 9
+
+
+struct mm_fw_update_open {
+ EFI_GUID image_guid;
+ UINT32 ret_handle;
+};
+
+struct mm_fw_update_close {
+ UINT32 handle;
+};
+
+#define MM_FW_UPDATE_OPEN_HEADER_SIZE \
+ (sizeof(struct mm_fw_update_open))
+
+struct mm_fw_update_write {
+ EFI_GUID image_guid;
+ UINT32 handle;
+ UINT32 offset;
+ UINT32 data_size;
+ UINT8 fw_fragment[1];
+}__packed;
+
+#define MM_FW_UPDATE_WRITE_HEADER_SIZE \
+ (offsetof(struct mm_fw_update_write, fw_fragment))
+
+struct mm_fw_update_header {
+ UINTN function;
+ EFI_STATUS ret_status;
+ UINT8 payload[1];
+};
+
+#define MM_FW_UPDATE_SIZE \
+ (offsetof(struct mm_fw_update_header, payload))
+
+/* Number of images per-set */
+#define FWU_N 1
+/* Number of sets */
+#define FWU_K 2
+
+struct fw_image_metadata_entry {
+ EFI_GUID image_guid;
+
+ /* Address of image A and image B */
+ EFI_PHYSICAL_ADDRESS image_start[FWU_K];
+
+ UINT64 maximum_image_size;
+};
+
+struct fwu_metadata {
+
+ UINT32 header_crc_32;
+ UINT32 metadata_version;
+
+ UINT32 active_index;
+ UINT32 update_index;
+
+ struct fw_image_metadata_entry image[FWU_N];
+};
+#define FWU_METADATA_SIZE sizeof(struct fwu_metadata)
+
+
+struct fw_image_info_entry {
+ EFI_GUID image_guid;
+};
+
+struct fw_image_info {
+ UINT32 num_images;
+ UINT32 reserved;
+ struct fw_image_info_entry entries[1];
+};
+
+struct fwu_read_stream_hdr {
+
+ UINT32 handle;
+ UINT32 total_size;
+ UINT32 fragment_size;
+ UINT8 payload[1];
+};
+
+struct fwu_trial_run_check_ret {
+
+ UINT32 trial_status;
+};
+
+#endif // __SMM_FW_UPDATE_H__