diff --git a/ArmPkg/Include/Library/ArmMmuLib.h b/ArmPkg/Include/Library/ArmMmuLib.h
index 6aba78fa7b67ffbb65b6c51350def2710a7c0a41..ece136dd96e1a05535b9ba29975a9cf04afe0e95 100644
--- a/ArmPkg/Include/Library/ArmMmuLib.h
+++ b/ArmPkg/Include/Library/ArmMmuLib.h
@@ -64,11 +64,31 @@ ArmReplaceLiveTranslationEntry (
**/
EFI_STATUS
+EFIAPI
ArmSetMemoryAttributes (
IN EFI_PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN UINT64 Attributes,
IN UINT64 AttributeMask
);
-
+ /**
+ Set the attributes for the memory region.
+
+ @param[in] BaseAddress Start address of the memory region.
+ @param[in] Length Length memory region.
+ @param[in] Attributes Attributes to set for the memory region.
+ @param[in] BlockEntryMask Mask to be used for the block entry.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ **/
+ EFI_STATUS
+ EFIAPI
+ SetMemoryRegionAttribute (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN UINT64 BlockEntryMask
+ );
#endif // ARM_MMU_LIB_H_
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index 741785b2e6049db7baa3c951157f6cdf85c24998..09b1022ad7173f11a975e8a429528529e671f3ba 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -756,3 +756,17 @@ ArmMmuBaseLibConstructor (
return RETURN_SUCCESS;
}
+EFI_STATUS
+EFIAPI
+SetMemoryRegionAttribute (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN UINT64 BlockEntryMask
+ )
+{
+ DEBUG ((DEBUG_INFO, "SetMemoryRegionAttribute base 0x%llx, Length 0x%llx, set 0x%llx, clear 0x%llx\n",
+ BaseAddress, Length, Attributes, BlockEntryMask));
+ return UpdateRegionMapping (BaseAddress, Length, Attributes, BlockEntryMask,ArmGetTTBR0BaseAddress (),
+ TRUE);
+}
\ No newline at end of file
diff --git a/ArmPlatformPkg/Include/Library/ArmCcaInitPeiLib.h b/ArmPlatformPkg/Include/Library/ArmCcaInitPeiLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..9374fe81590d2240d7b1fe176b7ab4792df62564
--- /dev/null
+++ b/ArmPlatformPkg/Include/Library/ArmCcaInitPeiLib.h
@@ -0,0 +1,49 @@
+/** @file
+ Library that implements the Arm CCA helper functions.
+
+ Copyright (c) 2022 2023, Arm Ltd. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+**/
+
+#ifndef ARM_CCA_INIT_PEI_LIB_
+#define ARM_CCA_INIT_PEI_LIB_
+
+#include
+
+/**
+ Configure the System Memory region as Protected RAM.
+
+ When a VMM creates a Realm, a small amount of DRAM (which contains the
+ firmware image) and the initial content is configured as Protected RAM.
+ The remaining System Memory is in the Protected Empty state. The firmware
+ must then initialise the remaining System Memory as Protected RAM before
+ it can be accessed.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The execution context is not in a Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureSystemMemory (
+ VOID
+ );
+
+/**
+ Perform Arm CCA specific initialisations.
+
+ @retval RETURN_SUCCESS Success or execution context is not a Realm.
+ @retval RETURN_OUT_OF_RESOURCES Out of resources.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaInitialize (
+ VOID
+ );
+
+#endif // ARM_CCA_LIB_
diff --git a/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.c b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b664b18584b833a9463683dd152e4fcc6db2494
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.c
@@ -0,0 +1,813 @@
+/** @file
+ The protocol provides support to allocate, free, map and umap a DMA buffer
+ for bus master (e.g PciHostBridge). When the execution context is a Realm,
+ the DMA operations must be performed on buffers that are shared with the Host.
+ Hence the RAMP protocol is used to manage the sharing of the DMA buffers or
+ in some cases to bounce the buffers.
+
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ Copyright (c) 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "ArmCcaIoMmu.h"
+
+/** List of the MAP_INFO structures that have been set up by IoMmuMap() and not
+ yet torn down by IoMmuUnmap(). The list represents the full set of mappings
+ currently in effect.
+*/
+STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos);
+
+#if !defined (MDEPKG_NDEBUG)
+
+/** ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.
+*/
+STATIC CONST CHAR8 *CONST
+mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {
+ "Read",
+ "Write",
+ "CommonBuffer",
+ "Read64",
+ "Write64",
+ "CommonBuffer64"
+};
+#endif
+
+/** Pointer to the Realm Aperture Management Protocol
+*/
+extern EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL *mRamp;
+
+/**
+ Given the host address find a mapping node in the linked list.
+
+ @param [in] HostAddress Host address.
+
+ @return Pointer to the MapInfo node if found, otherwise NULL.
+**/
+STATIC
+MAP_INFO *
+EFIAPI
+FindMappingByHostAddress (
+ IN VOID *HostAddress
+ )
+{
+ LIST_ENTRY *Node;
+ LIST_ENTRY *NextNode;
+ MAP_INFO *MapInfo;
+
+ for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {
+ NextNode = GetNextNode (&mMapInfos, Node);
+ MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);
+ if (MapInfo->HostAddress == HostAddress) {
+ return MapInfo;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Map a shared buffer
+
+ @param [in] Operation IoMMU operation to perform.
+ @param [in] HostAddress Pointer to the Host buffer.
+ @param [in] NumberOfBytes Number of bytes to map.
+ @param [in] BbAddress Bounce buffer address.
+ @param [in] BbPages Number of pages covering the bounce buffer.
+ @param [out] Mapping Pointer to the MapInfo node.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+**/
+STATIC
+EFI_STATUS
+MapSharedBuffer (
+ IN EDKII_IOMMU_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN UINTN NumberOfBytes,
+ IN EFI_PHYSICAL_ADDRESS BbAddress,
+ IN UINTN BbPages,
+ OUT MAP_INFO **Mapping
+ )
+{
+ EFI_STATUS Status;
+ MAP_INFO *MapInfo;
+
+ if (BbPages != EFI_SIZE_TO_PAGES (NumberOfBytes)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
+ // called later.
+ MapInfo = AllocateZeroPool (sizeof (MAP_INFO));
+ if (MapInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&MapInfo->Link);
+
+ // Initialize the MAP_INFO structure, except the NonParAddress field
+ MapInfo->Signature = MAP_INFO_SIG;
+ MapInfo->Operation = Operation;
+ MapInfo->NumberOfBytes = NumberOfBytes;
+ MapInfo->NumberOfPages = BbPages;
+ MapInfo->HostAddress = HostAddress;
+ MapInfo->BbAddress = BbAddress;
+
+ // Open aperture here
+ Status = mRamp->OpenAperture (
+ BbAddress,
+ BbPages,
+ &MapInfo->ApertureRef
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto FreeMapInfo;
+ }
+
+ // Track all MAP_INFO structures.
+ InsertHeadList (&mMapInfos, &MapInfo->Link);
+ *Mapping = MapInfo;
+ return Status;
+
+FreeMapInfo:
+ FreePool (MapInfo);
+ return Status;
+}
+
+/**
+ Unmap a shared buffer.
+
+ @param [in] MapInfo Pointer to the MapInfo node.
+ @param [in] MemoryMapLocked The function is executing on the stack of
+ gBS->ExitBootServices(); changes to the UEFI
+ memory map are forbidden.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+UnMapSharedBuffer (
+ IN MAP_INFO *MapInfo,
+ IN BOOLEAN MemoryMapLocked
+ )
+{
+ EFI_STATUS Status;
+
+ if (MapInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: HostAddress = 0x%p, BbAddress = 0x%p\n",
+ __func__,
+ MapInfo->HostAddress,
+ MapInfo->BbAddress
+ ));
+ Status = mRamp->CloseAperture (MapInfo->ApertureRef);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Failed to close aperture. Status = %r\n",
+ Status
+ ));
+ }
+
+ RemoveEntryList (&MapInfo->Link);
+
+ if (!MemoryMapLocked) {
+ FreePool (MapInfo);
+ }
+
+ return Status;
+}
+
+/**
+ Provides the controller-specific addresses required to access system memory
+ from a DMA bus master. On guest Realms, the DMA operations must be performed
+ on shared buffer hence we allocate a bounce buffer to map the HostAddress to
+ a DeviceAddress. The Realm Aperture Management protocol is then involved to
+ open the aperture for sharing the buffer pages with the Host OS.
+
+ @param This The protocol instance pointer.
+ @param Operation Indicates if the bus master is going to read or
+ write to system memory.
+ @param HostAddress The system memory address to map to the PCI
+ controller.
+ @param NumberOfBytes On input the number of bytes to map. On output
+ the number of bytes that were mapped.
+ @param DeviceAddress The resulting map address for the bus master
+ PCI controller to use to access the hosts
+ HostAddress.
+ @param Mapping A resulting value to pass to Unmap().
+
+ @retval EFI_SUCCESS The range was mapped for the returned
+ NumberOfBytes.
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common
+ buffer.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a
+ lack of resources.
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested
+ address.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuMap (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN EDKII_IOMMU_OPERATION Operation,
+ IN VOID *HostAddress,
+ IN OUT UINTN *NumberOfBytes,
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,
+ OUT VOID **Mapping
+ )
+{
+ EFI_STATUS Status;
+ MAP_INFO *MapInfo;
+ EFI_PHYSICAL_ADDRESS BbAddress;
+ UINTN Pages;
+ EFI_ALLOCATE_TYPE AllocateType;
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Operation=%a Host=0x%p Bytes=0x%lx\n",
+ __func__,
+ ((Operation >= 0 &&
+ Operation < ARRAY_SIZE (mBusMasterOperationName)) ?
+ mBusMasterOperationName[Operation] :
+ "Invalid"),
+ HostAddress,
+ (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes)
+ ));
+
+ if ((HostAddress == NULL) ||
+ (NumberOfBytes == NULL) ||
+ (DeviceAddress == NULL) ||
+ (Mapping == NULL) ||
+ (Operation >= EdkiiIoMmuOperationMaximum) ||
+ (Operation < EdkiiIoMmuOperationBusMasterRead))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BbAddress = MAX_ADDRESS;
+ Pages = EFI_SIZE_TO_PAGES (*NumberOfBytes);
+ AllocateType = AllocateAnyPages;
+ switch (Operation) {
+ // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer
+ // is necessary as the original buffer may not meet the page start/end and
+ // page size alignment requirements. Also we need to consider the case where
+ // the original buffer crosses the 4GB limit.
+ case EdkiiIoMmuOperationBusMasterRead:
+ case EdkiiIoMmuOperationBusMasterWrite:
+ BbAddress = BASE_4GB - 1;
+ AllocateType = AllocateMaxAddress;
+ // fall through
+ case EdkiiIoMmuOperationBusMasterRead64:
+ case EdkiiIoMmuOperationBusMasterWrite64:
+ // Allocate a bounce buffer.
+ Status = gBS->AllocatePages (
+ AllocateType,
+ EfiBootServicesData,
+ Pages,
+ &BbAddress
+ );
+ if (EFI_ERROR (Status)) {
+ goto Failed;
+ }
+
+ // Open aperture here
+ Status = MapSharedBuffer (
+ Operation,
+ HostAddress,
+ *NumberOfBytes,
+ BbAddress,
+ Pages,
+ &MapInfo
+ );
+ if (EFI_ERROR (Status)) {
+ goto FreeBounceBuffer;
+ }
+
+ break;
+
+ // For BusMasterCommonBuffer[64] operations, the buffer is already allocated
+ // and mapped in a call to AllocateBuffer(). So, we only need to return the
+ // device address and the mapping info
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:
+ // fall through
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+ MapInfo = FindMappingByHostAddress (HostAddress);
+ if (MapInfo == NULL) {
+ ASSERT (MapInfo == NULL);
+ goto Failed;
+ }
+
+ BbAddress = MapInfo->BbAddress;
+ break;
+
+ default:
+ // Operation is invalid
+ Status = EFI_INVALID_PARAMETER;
+ goto Failed;
+ } // switch
+
+ // If this is a read operation from the Bus Master's point of view,
+ // then copy the contents of the real buffer into the mapped buffer
+ // so the Bus Master can read the contents of the real buffer.
+ // No special action is needed for BusMasterCommonBuffer[64] operations.
+ if ((Operation == EdkiiIoMmuOperationBusMasterRead) ||
+ (Operation == EdkiiIoMmuOperationBusMasterRead64))
+ {
+ CopyMem (
+ (VOID *)(UINTN)BbAddress,
+ (VOID *)(UINTN)HostAddress,
+ MapInfo->NumberOfBytes
+ );
+ }
+
+ // Populate output parameters.
+ *DeviceAddress = BbAddress;
+ *Mapping = MapInfo;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Mapping=0x%p HostAddress = 0x%p BBAddress = 0x%Lx Pages=0x%Lx\n",
+ __func__,
+ MapInfo,
+ HostAddress,
+ MapInfo->BbAddress,
+ MapInfo->NumberOfPages
+ ));
+
+ return EFI_SUCCESS;
+
+FreeBounceBuffer:
+ gBS->FreePages (BbAddress, Pages);
+
+Failed:
+ *NumberOfBytes = 0;
+ return Status;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ This is an internal worker function that only extends the Map() API with
+ the MemoryMapLocked parameter.
+
+ @param This The protocol instance pointer.
+ @param MapInfo The mapping value returned from Map().
+ @param MemoryMapLocked The function is executing on the stack of
+ gBS->ExitBootServices(); changes to the UEFI
+ memory map are forbidden.
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by
+ Map().
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system
+ memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMmuUnmapWorker (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN MAP_INFO *MapInfo,
+ IN BOOLEAN MemoryMapLocked
+ )
+{
+ EFI_STATUS Status;
+ PHYSICAL_ADDRESS BbAddress;
+ UINTN Pages;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: MapInfo=0x%p MemoryMapLocked=%d\n",
+ __func__,
+ MapInfo,
+ MemoryMapLocked
+ ));
+
+ if (MapInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BbAddress = MapInfo->BbAddress;
+ Pages = MapInfo->NumberOfPages;
+
+ // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations
+ // we have to copy the results, ultimately to the original place (i.e.,
+ // "MapInfo->HostAddress").
+ // No special operaton is needed for BusMasterCommonBuffer[64] operations.
+ switch (MapInfo->Operation) {
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+ ASSERT (BbAddress == (PHYSICAL_ADDRESS)MapInfo->HostAddress);
+ break;
+ case EdkiiIoMmuOperationBusMasterWrite:
+ case EdkiiIoMmuOperationBusMasterWrite64:
+ CopyMem (
+ (VOID *)(UINTN)MapInfo->HostAddress,
+ (VOID *)(UINTN)BbAddress,
+ MapInfo->NumberOfBytes
+ );
+ break;
+
+ default:
+ // nothing to do for BusMasterRead[64] operations
+ break;
+ }
+
+ // For all other operations, fill the late bounce buffer with zeros, and
+ // then release it (unless the UEFI memory map is locked).
+ if ((MapInfo->Operation != EdkiiIoMmuOperationBusMasterCommonBuffer) &&
+ (MapInfo->Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64))
+ {
+ ZeroMem (
+ (VOID *)(UINTN)BbAddress,
+ EFI_PAGES_TO_SIZE (Pages)
+ );
+
+ // UnMapSharedPages
+ Status = UnMapSharedBuffer (MapInfo, MemoryMapLocked);
+ ASSERT_EFI_ERROR (Status);
+
+ if (!MemoryMapLocked) {
+ gBS->FreePages (BbAddress, Pages);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Completes the Map() operation and releases any corresponding resources.
+
+ @param This The protocol instance pointer.
+ @param Mapping The mapping value returned from Map().
+
+ @retval EFI_SUCCESS The range was unmapped.
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by
+ Map().
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system
+ memory.
+**/
+EFI_STATUS
+EFIAPI
+IoMmuUnmap (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN VOID *Mapping
+ )
+{
+ return IoMmuUnmapWorker (
+ This,
+ (MAP_INFO *)Mapping,
+ FALSE // MemoryMapLocked
+ );
+}
+
+/**
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+ OperationBusMasterCommonBuffer64 mapping.
+
+ @param This The protocol instance pointer.
+ @param Type This parameter is not used and must be ignored.
+ @param MemoryType The type of memory to allocate,
+ EfiBootServicesData or EfiRuntimeServicesData.
+ @param Pages The number of pages to allocate.
+ @param HostAddress A pointer to store the base system memory
+ address of the allocated range.
+ @param Attributes The requested bit mask of attributes for the
+ allocated range.
+
+ @retval EFI_SUCCESS The requested memory pages were allocated.
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal
+ attribute bits are MEMORY_WRITE_COMBINE and
+ MEMORY_CACHED.
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuAllocateBuffer (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN EFI_ALLOCATE_TYPE Type,
+ IN EFI_MEMORY_TYPE MemoryType,
+ IN UINTN Pages,
+ IN OUT VOID **HostAddress,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS BbAddress;
+ MAP_INFO *MapInfo;
+
+ // Validate Attributes
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ // Check for invalid inputs
+ if (HostAddress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // The only valid memory types are EfiBootServicesData
+ if (MemoryType != EfiBootServicesData) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Pages >= MAX_UINTN) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ BbAddress = (UINTN)-1;
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
+ // Limit allocations to memory below 4GB
+ BbAddress = SIZE_4GB - 1;
+ }
+
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ MemoryType,
+ Pages,
+ &BbAddress
+ );
+ if (EFI_ERROR (Status)) {
+ // Set the host address to NULL in case of error
+ *HostAddress = NULL;
+ } else {
+ *HostAddress = (VOID *)(UINTN)BbAddress;
+ Status = MapSharedBuffer (
+ EdkiiIoMmuOperationBusMasterCommonBuffer,
+ *HostAddress,
+ EFI_PAGES_TO_SIZE (Pages),
+ BbAddress,
+ Pages,
+ &MapInfo
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
+
+/**
+ Frees memory that was allocated with AllocateBuffer().
+
+ @param This The protocol instance pointer.
+ @param Pages The number of pages to free.
+ @param HostAddress The base system memory address of the allocated
+ range.
+
+ @retval EFI_SUCCESS The requested memory pages were freed.
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and
+ Pages was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuFreeBuffer (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN UINTN Pages,
+ IN VOID *HostAddress
+ )
+{
+ EFI_STATUS Status;
+ MAP_INFO *MapInfo;
+
+ // Release the common buffer itself. Unmap() has re-encrypted it in-place, so
+ // no need to zero it.
+ MapInfo = FindMappingByHostAddress (HostAddress);
+ if (MapInfo == NULL) {
+ ASSERT (0);
+ return EFI_NOT_FOUND;
+ } else {
+ // UnMapSharedPages
+ Status = UnMapSharedBuffer (MapInfo, FALSE);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return gBS->FreePages ((UINTN)HostAddress, Pages);
+}
+
+/**
+ Set IOMMU attribute for a system memory.
+
+ If the IOMMU protocol exists, the system memory cannot be used
+ for DMA by default.
+
+ When a device requests a DMA access to system memory,
+ the device driver need use SetAttribute() to update the IOMMU
+ attribute to request DMA access (read and/or write).
+
+ The DeviceHandle is used to identify which device submits the request.
+ The IOMMU implementation need to translate the device path to an IOMMU device
+ ID, and set the IOMMU hardware register accordingly.
+ 1) DeviceHandle can be a standard PCI device.
+ The memory for BusMasterRead needs EDKII_IOMMU_ACCESS_READ set.
+ The memory for BusMasterWrite needs EDKII_IOMMU_ACCESS_WRITE set.
+ The memory for BusMasterCommonBuffer needs
+ EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE set.
+ After the memory is used, the memory need set 0 to keep it being
+ protected.
+ 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
+ The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or
+ EDKII_IOMMU_ACCESS_WRITE.
+
+ @param[in] This The protocol instance pointer.
+ @param[in] DeviceHandle The device initiating the DMA access
+ request.
+ @param[in] Mapping The mapping value returned from Map().
+ @param[in] IoMmuAccess The IOMMU access.
+
+ @retval EFI_UNSUPPORTED Operation not supported by IOMMU.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuSetAttribute (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN VOID *Mapping,
+ IN UINT64 IoMmuAccess
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/** Arm CCA IoMMU protocol
+*/
+EDKII_IOMMU_PROTOCOL mArmCcaIoMmu = {
+ EDKII_IOMMU_PROTOCOL_REVISION,
+ IoMmuSetAttribute,
+ IoMmuMap,
+ IoMmuUnmap,
+ IoMmuAllocateBuffer,
+ IoMmuFreeBuffer,
+};
+
+/**
+ Notification function that is queued when gBS->ExitBootServices() signals the
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another
+ event, received as Context, and returns.
+
+ Signaling an event in this context is safe. The UEFI spec allows
+ gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not
+ listed, hence memory is not allocated. The edk2 implementation also does not
+ release memory (and we only have to care about the edk2 implementation
+ because EDKII_IOMMU_PROTOCOL is edk2-specific anyway).
+
+ @param[in] Event Event whose notification function is being invoked.
+ Event is permitted to request the queueing of this
+ function at TPL_CALLBACK or TPL_NOTIFY task
+ priority level.
+
+ @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal
+ is permitted to request the queueing of its
+ notification function only at TPL_CALLBACK level.
+**/
+STATIC
+VOID
+EFIAPI
+ArmCcaIoMmuExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *EventToSignal
+ )
+{
+ // (1) The NotifyFunctions of all the events in
+ // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before
+ // ArmCcaIoMmuExitBoot() is entered.
+ //
+ // (2) ArmCcaIoMmuExitBoot() is executing minimally at TPL_CALLBACK.
+ //
+ // (3) ArmCcaIoMmuExitBoot() has been queued in unspecified order relative
+ // to the NotifyFunctions of all the other events in
+ // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as
+ // Event's.
+ //
+ // Consequences:
+ //
+ // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions
+ // queued at TPL_CALLBACK may be invoked after ArmCcaIoMmuExitBoot()
+ // returns.
+ //
+ // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions
+ // queued at TPL_NOTIFY may be invoked after ArmCcaIoMmuExitBoot() returns;
+ // plus *all* NotifyFunctions queued at TPL_CALLBACK will be invoked
+ // strictly after all NotifyFunctions queued at TPL_NOTIFY, including
+ // ArmCcaIoMmuExitBoot(), have been invoked.
+ //
+ // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we
+ // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*
+ // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.
+ gBS->SignalEvent (EventToSignal);
+}
+
+/**
+ Notification function that is queued after the notification functions of all
+ events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory
+ map restrictions apply.
+
+ This function unmaps all currently existing IOMMU mappings.
+
+ @param[in] Event Event whose notification function is being invoked. Event
+ is permitted to request the queueing of this function
+ only at TPL_CALLBACK task priority level.
+
+ @param[in] Context Ignored.
+**/
+STATIC
+VOID
+EFIAPI
+ArmCcaIoMmuUnmapAllMappings (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Node;
+ LIST_ENTRY *NextNode;
+ MAP_INFO *MapInfo;
+
+ // All drivers that had set up IOMMU mappings have halted their respective
+ // controllers by now; tear down the mappings.
+ for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {
+ NextNode = GetNextNode (&mMapInfos, Node);
+ MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);
+ IoMmuUnmapWorker (
+ &mArmCcaIoMmu, // This
+ MapInfo, // Mapping
+ TRUE // MemoryMapLocked
+ );
+ }
+}
+
+/**
+ Initialize and install the ArmCca IoMmu Protocol.
+
+ @return RETURN_SUCCESS if successful, otherwise any other error.
+**/
+EFI_STATUS
+EFIAPI
+ArmCcaInstallIoMmuProtocol (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_EVENT UnmapAllMappingsEvent;
+ EFI_EVENT ExitBootEvent;
+ EFI_HANDLE Handle;
+
+ // Create the "late" event whose notification function will tear down all
+ // left-over IOMMU mappings.
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL, // Type
+ TPL_CALLBACK, // NotifyTpl
+ ArmCcaIoMmuUnmapAllMappings, // NotifyFunction
+ NULL, // NotifyContext
+ &UnmapAllMappingsEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // Create the event whose notification function will be queued by
+ // gBS->ExitBootServices() and will signal the event created above.
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
+ TPL_CALLBACK, // NotifyTpl
+ ArmCcaIoMmuExitBoot, // NotifyFunction
+ UnmapAllMappingsEvent, // NotifyContext
+ &ExitBootEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseUnmapAllMappingsEvent;
+ }
+
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEdkiiIoMmuProtocolGuid,
+ &mArmCcaIoMmu,
+ NULL
+ );
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // cleanup on error
+ gBS->CloseEvent (ExitBootEvent);
+
+CloseUnmapAllMappingsEvent:
+ gBS->CloseEvent (UnmapAllMappingsEvent);
+
+ return Status;
+}
diff --git a/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.h b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.h
new file mode 100644
index 0000000000000000000000000000000000000000..a12ca775d213c0ce2aee5cb46fe67e707b0c5794
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.h
@@ -0,0 +1,66 @@
+/** @file
+ The protocol provides support to allocate, free, map and umap a DMA buffer
+ for bus master (e.g PciHostBridge). When the execution context is a Realm,
+ the DMA operations must be performed on buffers that are shared with the HOST,
+ hence the RAMP protocol is used to manage the sharing of the DMA buffers or in
+ some cases bounce the buffers.
+
+ Copyright (c) 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ (C) Copyright 2017 Hewlett Packard Enterprise Development LP
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef ARM_CCA_IOMMU_H_
+#define ARM_CCA_IOMMU_H_
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ A macro defning the signature for the MAP_INFO structure.
+*/
+#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O')
+
+/** A structure describing the mapping for the buffers shared with the host.
+*/
+typedef struct {
+ /// Signature.
+ UINT64 Signature;
+ /// Linked List node entry.
+ LIST_ENTRY Link;
+ /// IoMMU operation.
+ EDKII_IOMMU_OPERATION Operation;
+ /// Number of bytes.
+ UINTN NumberOfBytes;
+ /// Number of pages.
+ UINTN NumberOfPages;
+ /// Address of the Host buffer.
+ VOID *HostAddress;
+
+ /// Address for the Bounce Buffer.
+ EFI_PHYSICAL_ADDRESS BbAddress;
+ /// Handle to the Aperture.
+ EFI_HANDLE ApertureRef;
+} MAP_INFO;
+
+/**
+ Install IOMMU protocol to provide the DMA support for PciHostBridge and
+ RAMP.
+
+**/
+EFI_STATUS
+EFIAPI
+ArmCcaInstallIoMmuProtocol (
+ VOID
+ );
+
+#endif
diff --git a/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.c b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.c
new file mode 100644
index 0000000000000000000000000000000000000000..01e4e6fa10a684f0d8e3335d59ae529a74ad42fc
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.c
@@ -0,0 +1,60 @@
+/** @file
+
+ IoMmuArmBowDxe driver installs EDKII_IOMMU_PROTOCOL to support
+ DMA operations when the execution context is a Realm.
+
+ Copyright (c) 2017, AMD Inc. All rights reserved.
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "ArmCcaIoMmu.h"
+
+/** Pointer to the Realm Aperture Management Protocol
+*/
+EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL *mRamp = NULL;
+
+/** Entrypoint of Arm CCA IoMMU Dxe.
+
+ @param [in] ImageHandle Image handle of this driver.
+ @param [in] SystemTable Pointer to the EFI System Table.
+
+ @return RETURN_SUCCESS if successful, otherwise any other error.
+**/
+EFI_STATUS
+EFIAPI
+ArmCcaIoMmuDxeEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ DEBUG ((DEBUG_INFO, "ArmCcaIoMmuDxeEntryPoint 1.\n"));
+ // When the execution context is a Realm, install ArmCcaIoMmu protocol
+ // otherwise install the placeholder protocol so that other dependent
+ // module can run.
+ Status = gBS->LocateProtocol (
+ &gEfiRealmApertureManagementProtocolGuid,
+ NULL,
+ (VOID **)&mRamp
+ );
+ if (!EFI_ERROR (Status)) {
+ // If the Realm Aperture Management Protocol is present
+ // then the execution context is a Realm.
+ Status = ArmCcaInstallIoMmuProtocol ();
+ DEBUG ((DEBUG_INFO, "ArmCcaIoMmuDxeEntryPoint 1.1.\n"));
+ } else {
+ DEBUG ((DEBUG_INFO, "Execution context is not a Realm.\n"));
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gIoMmuAbsentProtocolGuid,
+ NULL,
+ NULL
+ );
+ }
+ DEBUG ((DEBUG_INFO, "ArmCcaIoMmuDxeEntryPoint 2.\n"));
+ return Status;
+}
diff --git a/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
new file mode 100644
index 0000000000000000000000000000000000000000..59fa1abd1eb73ccd10c659baeb29fc80353d7022
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
@@ -0,0 +1,45 @@
+## @file
+# Driver provides the IOMMU protcol support for PciHostBridgeIo and others
+# drivers.
+#
+# Copyright (c) 2017, AMD Inc. All rights reserved.
+# Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = IoMmuDxe
+ FILE_GUID = AA6C1A48-A341-439C-950E-CC394FDFE144
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = ArmCcaIoMmuDxeEntryPoint
+
+[Sources]
+ ArmCcaIoMmu.c
+ ArmCcaIoMmu.h
+ ArmCcaIoMmuDxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEdkiiIoMmuProtocolGuid ## SOMETIME_PRODUCES
+ gIoMmuAbsentProtocolGuid ## SOMETIME_PRODUCES
+ gEfiRealmApertureManagementProtocolGuid
+
+[Depex]
+ gEfiRealmApertureManagementProtocolGuid
diff --git a/ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.c b/ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.c
new file mode 100644
index 0000000000000000000000000000000000000000..ed552c0a7e9bc835f308035fbbe71a0e8eb6838e
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.c
@@ -0,0 +1,2233 @@
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#define PERF_ID_CC_TCG2_DXE 0x3130
+
+#define CC_EVENT_LOG_AREA_COUNT_MAX 1
+#define CC_MR_INDEX_0_RIM 0
+#define CC_MR_INDEX_1_REM0 1
+#define CC_MR_INDEX_2_REM1 2
+#define CC_MR_INDEX_3_REM2 3
+#define CC_MR_INDEX_INVALID 4
+
+typedef struct {
+ CHAR16 *VariableName;
+ EFI_GUID *VendorGuid;
+} VARIABLE_TYPE;
+
+typedef struct {
+ EFI_CC_EVENT_LOG_FORMAT EventLogFormat;
+ EFI_PHYSICAL_ADDRESS Lasa;
+ UINT64 Laml;
+ UINTN EventLogSize;
+ UINT8 *LastEvent;
+ BOOLEAN EventLogStarted;
+ BOOLEAN EventLogTruncated;
+ UINTN Next800155EventOffset;
+} CC_EVENT_LOG_AREA_STRUCT;
+
+typedef struct _CCA_DXE_DATA {
+ EFI_CC_BOOT_SERVICE_CAPABILITY BsCap;
+ CC_EVENT_LOG_AREA_STRUCT EventLogAreaStruct[CC_EVENT_LOG_AREA_COUNT_MAX];
+ BOOLEAN GetEventLogCalled[CC_EVENT_LOG_AREA_COUNT_MAX];
+ CC_EVENT_LOG_AREA_STRUCT FinalEventLogAreaStruct[CC_EVENT_LOG_AREA_COUNT_MAX];
+ EFI_CC_FINAL_EVENTS_TABLE *FinalEventsTable[CC_EVENT_LOG_AREA_COUNT_MAX];
+} CCA_DXE_DATA;
+
+typedef struct {
+ TPMI_ALG_HASH HashAlgo;
+ UINT16 HashSize;
+ UINT32 HashMask;
+} CCA_HASH_INFO;
+
+CCA_DXE_DATA mCcaDxeData = {
+ {
+ sizeof (EFI_CC_BOOT_SERVICE_CAPABILITY), // Size
+ { 1, 1 }, // StructureVersion
+ { 1, 1 }, // ProtocolVersion
+ EFI_CC_BOOT_HASH_ALG_SHA256, // HashAlgorithmBitmap
+ EFI_CC_EVENT_LOG_FORMAT_TCG_2, // SupportedEventLogs
+ { 2, 0 } // {CC_TYPE, CC_SUBTYPE}
+ },
+};
+
+UINTN mBootAttempts = 0;
+CHAR16 mBootVarName[] = L"BootOrder";
+
+VARIABLE_TYPE mVariableType[] = {
+ { EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid },
+ { EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid },
+ { EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid },
+ { EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid },
+ { EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid },
+};
+
+EFI_CC_EVENTLOG_ACPI_TABLE mCcaEventlogAcpiTemplate = {
+ {
+ EFI_CC_EVENTLOG_ACPI_TABLE_SIGNATURE,
+ sizeof (mCcaEventlogAcpiTemplate),
+ EFI_CC_EVENTLOG_ACPI_TABLE_REVISION,
+ //
+ // Compiler initializes the remaining bytes to 0
+ // These fields should be filled in production
+ //
+ },
+ { EFI_CC_TYPE_CCA, 0 }, // CcType
+ 0, // rsvd
+ 0, // laml
+ 0, // lasa
+};
+
+//
+// Currently SHA256 is supported.
+//
+CCA_HASH_INFO mHashInfo[] = {
+ { TPM_ALG_SHA256, SHA256_DIGEST_SIZE, HASH_ALG_SHA256 }
+};
+
+/**
+ Get hash size based on Algo
+
+ @param[in] HashAlgo Hash Algorithm Id.
+
+ @return Size of the hash.
+**/
+UINT16
+GetHashSizeFromAlgo (
+ IN TPMI_ALG_HASH HashAlgo
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < sizeof (mHashInfo)/sizeof (mHashInfo[0]); Index++) {
+ if (mHashInfo[Index].HashAlgo == HashAlgo) {
+ return mHashInfo[Index].HashSize;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ Copy TPML_DIGEST_VALUES into a buffer
+
+ @param[in,out] Buffer Buffer to hold copied TPML_DIGEST_VALUES compact binary.
+ @param[in] DigestList TPML_DIGEST_VALUES to be copied.
+ @param[in] HashAlgorithmMask HASH bits corresponding to the desired digests to copy.
+
+ @return The end of buffer to hold TPML_DIGEST_VALUES.
+**/
+VOID *
+CopyDigestListToBuffer (
+ IN OUT VOID *Buffer,
+ IN TPML_DIGEST_VALUES *DigestList,
+ IN UINT32 HashAlgorithmMask
+ )
+{
+ UINTN Index;
+ UINT16 DigestSize;
+ UINT32 DigestListCount;
+ UINT32 *DigestListCountPtr;
+
+ DigestListCountPtr = (UINT32 *)Buffer;
+ DigestListCount = 0;
+ Buffer = (UINT8 *)Buffer + sizeof (DigestList->count);
+ for (Index = 0; Index < DigestList->count; Index++) {
+ if ((DigestList->digests[Index].hashAlg & HashAlgorithmMask) == 0) {
+ DEBUG ((DEBUG_ERROR, "WARNING: CC Event log has HashAlg unsupported (0x%x)\n", DigestList->digests[Index].hashAlg));
+ continue;
+ }
+
+ CopyMem (Buffer, &DigestList->digests[Index].hashAlg, sizeof (DigestList->digests[Index].hashAlg));
+ Buffer = (UINT8 *)Buffer + sizeof (DigestList->digests[Index].hashAlg);
+ DigestSize = GetHashSizeFromAlgo (DigestList->digests[Index].hashAlg);
+ CopyMem (Buffer, &DigestList->digests[Index].digest, DigestSize);
+ Buffer = (UINT8 *)Buffer + DigestSize;
+ DigestListCount++;
+ }
+
+ WriteUnaligned32 (DigestListCountPtr, DigestListCount);
+
+ return Buffer;
+}
+
+EFI_HANDLE mImageHandle;
+
+/**
+ Measure PE image into TPM log based on the authenticode image hashing in
+ PE/COFF Specification 8.0 Appendix A.
+
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this function will validate its data structure
+ within this image buffer before use.
+
+ Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo().
+
+ @param[in] RemIndex REM index
+ @param[in] ImageAddress Start address of image buffer.
+ @param[in] ImageSize Image size
+ @param[out] DigestList Digest list of this image.
+
+ @retval EFI_SUCCESS Successfully measure image.
+ @retval EFI_OUT_OF_RESOURCES No enough resource to measure image.
+ @retval other error value
+**/
+EFI_STATUS
+MeasurePeImageAndExtend (
+ IN UINT32 RemIndex,
+ IN EFI_PHYSICAL_ADDRESS ImageAddress,
+ IN UINTN ImageSize,
+ OUT TPML_DIGEST_VALUES *DigestList
+ );
+
+#define COLUME_SIZE (16 * 2)
+
+/**
+
+ This function dump raw data.
+
+ @param Data raw data
+ @param Size raw data size
+
+**/
+VOID
+InternalDumpData (
+ IN UINT8 *Data,
+ IN UINTN Size
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Size; Index++) {
+ DEBUG ((DEBUG_INFO, Index == COLUME_SIZE/2 ? " | %02x" : " %02x", (UINTN)Data[Index]));
+ }
+}
+
+/**
+
+ This function dump raw data with colume format.
+
+ @param Data raw data
+ @param Size raw data size
+
+**/
+VOID
+InternalDumpHex (
+ IN UINT8 *Data,
+ IN UINTN Size
+ )
+{
+ UINTN Index;
+ UINTN Count;
+ UINTN Left;
+
+ Count = Size / COLUME_SIZE;
+ Left = Size % COLUME_SIZE;
+ for (Index = 0; Index < Count; Index++) {
+ DEBUG ((DEBUG_INFO, "%04x: ", Index * COLUME_SIZE));
+ InternalDumpData (Data + Index * COLUME_SIZE, COLUME_SIZE);
+ DEBUG ((DEBUG_INFO, "\n"));
+ }
+
+ if (Left != 0) {
+ DEBUG ((DEBUG_INFO, "%04x: ", Index * COLUME_SIZE));
+ InternalDumpData (Data + Index * COLUME_SIZE, Left);
+ DEBUG ((DEBUG_INFO, "\n"));
+ }
+}
+
+/**
+
+ This function initialize CC_EVENT_HDR for EV_NO_ACTION
+ Event Type other than EFI Specification ID event. The behavior is defined
+ by TCG PC Client PFP Spec. Section 9.3.4 EV_NO_ACTION Event Types
+
+ @param[in, out] NoActionEvent Event Header of EV_NO_ACTION Event
+ @param[in] EventSize Event Size of the EV_NO_ACTION Event
+
+**/
+VOID
+InitNoActionEvent (
+ IN OUT CC_EVENT_HDR *NoActionEvent,
+ IN UINT32 EventSize
+ )
+{
+ UINT32 DigestListCount;
+ TPMI_ALG_HASH HashAlgId;
+ UINT8 *DigestBuffer;
+
+ DigestBuffer = (UINT8 *)NoActionEvent->Digests.digests;
+ DigestListCount = 0;
+
+ NoActionEvent->MrIndex = 0;
+ NoActionEvent->EventType = EV_NO_ACTION;
+
+ //
+ // Set Hash count & hashAlg accordingly, while Digest.digests[n].digest to all 0
+ //
+ ZeroMem (&NoActionEvent->Digests, sizeof (NoActionEvent->Digests));
+
+ if ((mCcaDxeData.BsCap.HashAlgorithmBitmap & EFI_CC_BOOT_HASH_ALG_SHA256) != 0) {
+ HashAlgId = TPM_ALG_SHA256;
+ CopyMem (DigestBuffer, &HashAlgId, sizeof (TPMI_ALG_HASH));
+ DigestBuffer += sizeof (TPMI_ALG_HASH) + GetHashSizeFromAlgo (HashAlgId);
+ DigestListCount++;
+ }
+
+ //
+ // Set Digests Count
+ //
+ WriteUnaligned32 ((UINT32 *)&NoActionEvent->Digests.count, DigestListCount);
+
+ //
+ // Set Event Size
+ //
+ WriteUnaligned32 ((UINT32 *)DigestBuffer, EventSize);
+}
+
+/**
+ The EFI_CC_MEASUREMENT_PROTOCOL GetCapability function call provides protocol
+ capability information and state information.
+
+ @param[in] This Indicates the calling context
+ @param[in, out] ProtocolCapability The caller allocates memory for a EFI_CC_BOOT_SERVICE_CAPABILITY
+ structure and sets the size field to the size of the structure allocated.
+ The callee fills in the fields with the EFI protocol capability information
+ and the current EFI TCG2 state information up to the number of fields which
+ fit within the size of the structure passed in.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_DEVICE_ERROR The command was unsuccessful.
+ The ProtocolCapability variable will not be populated.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect.
+ The ProtocolCapability variable will not be populated.
+ @retval EFI_BUFFER_TOO_SMALL The ProtocolCapability variable is too small to hold the full response.
+ It will be partially populated (required Size field will be set).
+**/
+EFI_STATUS
+EFIAPI
+CcaGetCapability (
+ IN EFI_CC_MEASUREMENT_PROTOCOL *This,
+ IN OUT EFI_CC_BOOT_SERVICE_CAPABILITY *ProtocolCapability
+ )
+{
+ DEBUG ((DEBUG_VERBOSE, "CcaGetCapability\n"));
+
+ if ((This == NULL) || (ProtocolCapability == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (ProtocolCapability, &mCcaDxeData.BsCap, sizeof (EFI_CC_BOOT_SERVICE_CAPABILITY));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function dump PCR event.
+ CC Event log reuse the TCG PCR Event spec.
+ The first event in the event log is the SHA1 log format.
+ There is only ONE TCG_PCR_EVENT in CC Event log.
+
+ @param[in] EventHdr TCG PCR event structure.
+**/
+VOID
+DumpPcrEvent (
+ IN TCG_PCR_EVENT_HDR *EventHdr
+ )
+{
+ UINTN Index;
+
+ DEBUG ((DEBUG_INFO, " Event:\n"));
+ DEBUG ((DEBUG_INFO, " MrIndex - %d\n", EventHdr->PCRIndex));
+ DEBUG ((DEBUG_INFO, " EventType - 0x%08x\n", EventHdr->EventType));
+ DEBUG ((DEBUG_INFO, " Digest - "));
+ for (Index = 0; Index < sizeof (TCG_DIGEST); Index++) {
+ DEBUG ((DEBUG_INFO, "%02x ", EventHdr->Digest.digest[Index]));
+ }
+
+ DEBUG ((DEBUG_INFO, "\n"));
+ DEBUG ((DEBUG_INFO, " EventSize - 0x%08x\n", EventHdr->EventSize));
+ InternalDumpHex ((UINT8 *)(EventHdr + 1), EventHdr->EventSize);
+}
+
+/**
+ This function dump TCG_EfiSpecIDEventStruct.
+
+ @param[in] TcgEfiSpecIdEventStruct A pointer to TCG_EfiSpecIDEventStruct.
+**/
+VOID
+DumpTcgEfiSpecIdEventStruct (
+ IN TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct
+ )
+{
+ TCG_EfiSpecIdEventAlgorithmSize *DigestSize;
+ UINTN Index;
+ UINT8 *VendorInfoSize;
+ UINT8 *VendorInfo;
+ UINT32 NumberOfAlgorithms;
+
+ DEBUG ((DEBUG_INFO, " TCG_EfiSpecIDEventStruct:\n"));
+ DEBUG ((DEBUG_INFO, " signature - '"));
+ for (Index = 0; Index < sizeof (TcgEfiSpecIdEventStruct->signature); Index++) {
+ DEBUG ((DEBUG_INFO, "%c", TcgEfiSpecIdEventStruct->signature[Index]));
+ }
+
+ DEBUG ((DEBUG_INFO, "'\n"));
+ DEBUG ((DEBUG_INFO, " platformClass - 0x%08x\n", TcgEfiSpecIdEventStruct->platformClass));
+ DEBUG ((DEBUG_INFO, " specVersion - %d.%d%d\n", TcgEfiSpecIdEventStruct->specVersionMajor, TcgEfiSpecIdEventStruct->specVersionMinor, TcgEfiSpecIdEventStruct->specErrata));
+ DEBUG ((DEBUG_INFO, " uintnSize - 0x%02x\n", TcgEfiSpecIdEventStruct->uintnSize));
+
+ CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof (NumberOfAlgorithms));
+ DEBUG ((DEBUG_INFO, " NumberOfAlgorithms - 0x%08x\n", NumberOfAlgorithms));
+
+ DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof (*TcgEfiSpecIdEventStruct) + sizeof (NumberOfAlgorithms));
+ for (Index = 0; Index < NumberOfAlgorithms; Index++) {
+ DEBUG ((DEBUG_INFO, " digest(%d)\n", Index));
+ DEBUG ((DEBUG_INFO, " algorithmId - 0x%04x\n", DigestSize[Index].algorithmId));
+ DEBUG ((DEBUG_INFO, " digestSize - 0x%04x\n", DigestSize[Index].digestSize));
+ }
+
+ VendorInfoSize = (UINT8 *)&DigestSize[NumberOfAlgorithms];
+ DEBUG ((DEBUG_INFO, " VendorInfoSize - 0x%02x\n", *VendorInfoSize));
+ VendorInfo = VendorInfoSize + 1;
+ DEBUG ((DEBUG_INFO, " VendorInfo - "));
+ for (Index = 0; Index < *VendorInfoSize; Index++) {
+ DEBUG ((DEBUG_INFO, "%02x ", VendorInfo[Index]));
+ }
+
+ DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/**
+ This function get size of TCG_EfiSpecIDEventStruct.
+
+ @param[in] TcgEfiSpecIdEventStruct A pointer to TCG_EfiSpecIDEventStruct.
+**/
+UINTN
+GetTcgEfiSpecIdEventStructSize (
+ IN TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct
+ )
+{
+ TCG_EfiSpecIdEventAlgorithmSize *DigestSize;
+ UINT8 *VendorInfoSize;
+ UINT32 NumberOfAlgorithms;
+
+ CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof (NumberOfAlgorithms));
+
+ DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof (*TcgEfiSpecIdEventStruct) + sizeof (NumberOfAlgorithms));
+ VendorInfoSize = (UINT8 *)&DigestSize[NumberOfAlgorithms];
+ return sizeof (TCG_EfiSpecIDEventStruct) + sizeof (UINT32) + (NumberOfAlgorithms * sizeof (TCG_EfiSpecIdEventAlgorithmSize)) + sizeof (UINT8) + (*VendorInfoSize);
+}
+
+/**
+ This function dump CCA Event (including the Digests).
+
+ @param[in] CcEvent CCA Event structure.
+**/
+VOID
+DumpCcEvent (
+ IN CC_EVENT *CcEvent
+ )
+{
+ UINT32 DigestIndex;
+ UINT32 DigestCount;
+ TPMI_ALG_HASH HashAlgo;
+ UINT32 DigestSize;
+ UINT8 *DigestBuffer;
+ UINT32 EventSize;
+ UINT8 *EventBuffer;
+
+ DEBUG ((DEBUG_INFO, "Cc Event:\n"));
+ DEBUG ((DEBUG_INFO, " MrIndex - %d\n", CcEvent->MrIndex));
+ DEBUG ((DEBUG_INFO, " EventType - 0x%08x\n", CcEvent->EventType));
+ DEBUG ((DEBUG_INFO, " DigestCount: 0x%08x\n", CcEvent->Digests.count));
+
+ DigestCount = CcEvent->Digests.count;
+ HashAlgo = CcEvent->Digests.digests[0].hashAlg;
+ DigestBuffer = (UINT8 *)&CcEvent->Digests.digests[0].digest;
+ for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) {
+ DEBUG ((DEBUG_INFO, " HashAlgo : 0x%04x\n", HashAlgo));
+ DEBUG ((DEBUG_INFO, " Digest(%d): \n", DigestIndex));
+ DigestSize = GetHashSizeFromAlgo (HashAlgo);
+ InternalDumpHex (DigestBuffer, DigestSize);
+ //
+ // Prepare next
+ //
+ CopyMem (&HashAlgo, DigestBuffer + DigestSize, sizeof (TPMI_ALG_HASH));
+ DigestBuffer = DigestBuffer + DigestSize + sizeof (TPMI_ALG_HASH);
+ }
+
+ DigestBuffer = DigestBuffer - sizeof (TPMI_ALG_HASH);
+
+ CopyMem (&EventSize, DigestBuffer, sizeof (CcEvent->EventSize));
+ DEBUG ((DEBUG_INFO, " EventSize - 0x%08x\n", EventSize));
+ EventBuffer = DigestBuffer + sizeof (CcEvent->EventSize);
+ InternalDumpHex (EventBuffer, EventSize);
+ DEBUG ((DEBUG_INFO, "\n"));
+}
+
+/**
+ This function returns size of CC Table event.
+
+ @param[in] CcEvent CC Table event structure.
+
+ @return size of CC event.
+**/
+UINTN
+GetCcEventSize (
+ IN CC_EVENT *CcEvent
+ )
+{
+ UINT32 DigestIndex;
+ UINT32 DigestCount;
+ TPMI_ALG_HASH HashAlgo;
+ UINT32 DigestSize;
+ UINT8 *DigestBuffer;
+ UINT32 EventSize;
+ UINT8 *EventBuffer;
+
+ DigestCount = CcEvent->Digests.count;
+ HashAlgo = CcEvent->Digests.digests[0].hashAlg;
+ DigestBuffer = (UINT8 *)&CcEvent->Digests.digests[0].digest;
+ for (DigestIndex = 0; DigestIndex < DigestCount; DigestIndex++) {
+ DigestSize = GetHashSizeFromAlgo (HashAlgo);
+ //
+ // Prepare next
+ //
+ CopyMem (&HashAlgo, DigestBuffer + DigestSize, sizeof (TPMI_ALG_HASH));
+ DigestBuffer = DigestBuffer + DigestSize + sizeof (TPMI_ALG_HASH);
+ }
+
+ DigestBuffer = DigestBuffer - sizeof (TPMI_ALG_HASH);
+
+ CopyMem (&EventSize, DigestBuffer, sizeof (CcEvent->EventSize));
+ EventBuffer = DigestBuffer + sizeof (CcEvent->EventSize);
+
+ return (UINTN)EventBuffer + EventSize - (UINTN)CcEvent;
+}
+
+/**
+ This function dump CC event log.
+ Now only supports EFI_CC_EVENT_LOG_FORMAT_TCG_2
+
+ @param[in] EventLogFormat The type of the event log for which the information is requested.
+ @param[in] EventLogLocation A pointer to the memory address of the event log.
+ @param[in] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the
+ address of the start of the last entry in the event log in memory.
+ @param[in] FinalEventsTable A pointer to the memory address of the final event table.
+**/
+VOID
+DumpCcEventLog (
+ IN EFI_CC_EVENT_LOG_FORMAT EventLogFormat,
+ IN EFI_PHYSICAL_ADDRESS EventLogLocation,
+ IN EFI_PHYSICAL_ADDRESS EventLogLastEntry,
+ IN EFI_CC_FINAL_EVENTS_TABLE *FinalEventsTable
+ )
+{
+ TCG_PCR_EVENT_HDR *EventHdr;
+ CC_EVENT *CcEvent;
+ TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct;
+ UINTN NumberOfEvents;
+
+ DEBUG ((DEBUG_INFO, "EventLogFormat: (0x%x)\n", EventLogFormat));
+ ASSERT (EventLogFormat == EFI_CC_EVENT_LOG_FORMAT_TCG_2);
+
+ //
+ // Dump first event.
+ // The first event is always the TCG_PCR_EVENT_HDR
+ // After this event is a TCG_EfiSpecIDEventStruct
+ //
+ EventHdr = (TCG_PCR_EVENT_HDR *)(UINTN)EventLogLocation;
+ DumpPcrEvent (EventHdr);
+
+ TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *)(EventHdr + 1);
+ DumpTcgEfiSpecIdEventStruct (TcgEfiSpecIdEventStruct);
+
+ //
+ // Then the CcEvent (Its structure is similar to TCG_PCR_EVENT2)
+ //
+ CcEvent = (CC_EVENT *)((UINTN)TcgEfiSpecIdEventStruct + GetTcgEfiSpecIdEventStructSize (TcgEfiSpecIdEventStruct));
+ while ((UINTN)CcEvent <= EventLogLastEntry) {
+ DumpCcEvent (CcEvent);
+ CcEvent = (CC_EVENT *)((UINTN)CcEvent + GetCcEventSize (CcEvent));
+ }
+
+ if (FinalEventsTable == NULL) {
+ DEBUG ((DEBUG_INFO, "FinalEventsTable: NOT FOUND\n"));
+ } else {
+ DEBUG ((DEBUG_INFO, "FinalEventsTable: (0x%x)\n", FinalEventsTable));
+ DEBUG ((DEBUG_INFO, " Version: (0x%x)\n", FinalEventsTable->Version));
+ DEBUG ((DEBUG_INFO, " NumberOfEvents: (0x%x)\n", FinalEventsTable->NumberOfEvents));
+
+ CcEvent = (CC_EVENT *)(UINTN)(FinalEventsTable + 1);
+ for (NumberOfEvents = 0; NumberOfEvents < FinalEventsTable->NumberOfEvents; NumberOfEvents++) {
+ DumpCcEvent (CcEvent);
+ CcEvent = (CC_EVENT *)((UINTN)CcEvent + GetCcEventSize (CcEvent));
+ }
+ }
+
+ return;
+}
+
+/**
+ The EFI_CC_MEASUREMENT_PROTOCOL Get Event Log function call allows a caller to
+ retrieve the address of a given event log and its last entry.
+
+ @param[in] This Indicates the calling context
+ @param[in] EventLogFormat The type of the event log for which the information is requested.
+ @param[out] EventLogLocation A pointer to the memory address of the event log.
+ @param[out] EventLogLastEntry If the Event Log contains more than one entry, this is a pointer to the
+ address of the start of the last entry in the event log in memory.
+ @param[out] EventLogTruncated If the Event Log is missing at least one entry because an event would
+ have exceeded the area allocated for events, this value is set to TRUE.
+ Otherwise, the value will be FALSE and the Event Log will be complete.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect
+ (e.g. asking for an event log whose format is not supported).
+**/
+EFI_STATUS
+EFIAPI
+CcaGetEventLog (
+ IN EFI_CC_MEASUREMENT_PROTOCOL *This,
+ IN EFI_CC_EVENT_LOG_FORMAT EventLogFormat,
+ OUT EFI_PHYSICAL_ADDRESS *EventLogLocation,
+ OUT EFI_PHYSICAL_ADDRESS *EventLogLastEntry,
+ OUT BOOLEAN *EventLogTruncated
+ )
+{
+ UINTN Index = 0;
+
+ DEBUG ((DEBUG_INFO, "CcaGetEventLog ... (0x%x)\n", EventLogFormat));
+ ASSERT (EventLogFormat == EFI_CC_EVENT_LOG_FORMAT_TCG_2);
+
+ if (EventLogLocation != NULL) {
+ *EventLogLocation = mCcaDxeData.EventLogAreaStruct[Index].Lasa;
+ DEBUG ((DEBUG_INFO, "CcaGetEventLog (EventLogLocation - %x)\n", *EventLogLocation));
+ }
+
+ if (EventLogLastEntry != NULL) {
+ if (!mCcaDxeData.EventLogAreaStruct[Index].EventLogStarted) {
+ *EventLogLastEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)0;
+ } else {
+ *EventLogLastEntry = (EFI_PHYSICAL_ADDRESS)(UINTN)mCcaDxeData.EventLogAreaStruct[Index].LastEvent;
+ }
+
+ DEBUG ((DEBUG_INFO, "CcaGetEventLog (EventLogLastEntry - %x)\n", *EventLogLastEntry));
+ }
+
+ if (EventLogTruncated != NULL) {
+ *EventLogTruncated = mCcaDxeData.EventLogAreaStruct[Index].EventLogTruncated;
+ DEBUG ((DEBUG_INFO, "CcaGetEventLog (EventLogTruncated - %x)\n", *EventLogTruncated));
+ }
+
+ DEBUG ((DEBUG_INFO, "CcaGetEventLog - %r\n", EFI_SUCCESS));
+
+ // Dump Event Log for debug purpose
+ if ((EventLogLocation != NULL) && (EventLogLastEntry != NULL)) {
+ DumpCcEventLog (EventLogFormat, *EventLogLocation, *EventLogLastEntry, mCcaDxeData.FinalEventsTable[Index]);
+ }
+
+ //
+ // All events generated after the invocation of EFI_TCG2_GET_EVENT_LOG SHALL be stored
+ // in an instance of an EFI_CONFIGURATION_TABLE named by the VendorGuid of EFI_TCG2_FINAL_EVENTS_TABLE_GUID.
+ //
+ mCcaDxeData.GetEventLogCalled[Index] = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Return if this is a Tcg800155PlatformIdEvent.
+
+ @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure.
+ @param[in] NewEventHdrSize New event header size.
+ @param[in] NewEventData Pointer to the new event data.
+ @param[in] NewEventSize New event data size.
+
+ @retval TRUE This is a Tcg800155PlatformIdEvent.
+ @retval FALSE This is NOT a Tcg800155PlatformIdEvent.
+
+**/
+BOOLEAN
+Is800155Event (
+ IN VOID *NewEventHdr,
+ IN UINT32 NewEventHdrSize,
+ IN UINT8 *NewEventData,
+ IN UINT32 NewEventSize
+ )
+{
+ if ((((TCG_PCR_EVENT2_HDR *)NewEventHdr)->EventType == EV_NO_ACTION) &&
+ (NewEventSize >= sizeof (TCG_Sp800_155_PlatformId_Event2)) &&
+ ((CompareMem (
+ NewEventData,
+ TCG_Sp800_155_PlatformId_Event2_SIGNATURE,
+ sizeof (TCG_Sp800_155_PlatformId_Event2_SIGNATURE) - 1
+ ) == 0) ||
+ (CompareMem (
+ NewEventData,
+ TCG_Sp800_155_PlatformId_Event3_SIGNATURE,
+ sizeof (TCG_Sp800_155_PlatformId_Event3_SIGNATURE) - 1
+ ) == 0)))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Add a new entry to the Event Log.
+
+ @param[in, out] EventLogAreaStruct The event log area data structure
+ @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure.
+ @param[in] NewEventHdrSize New event header size.
+ @param[in] NewEventData Pointer to the new event data.
+ @param[in] NewEventSize New event data size.
+
+ @retval EFI_SUCCESS The new event log entry was added.
+ @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
+
+**/
+EFI_STATUS
+TcgCommLogEvent (
+ IN OUT CC_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct,
+ IN VOID *NewEventHdr,
+ IN UINT32 NewEventHdrSize,
+ IN UINT8 *NewEventData,
+ IN UINT32 NewEventSize
+ )
+{
+ UINTN NewLogSize;
+ BOOLEAN Record800155Event;
+ CC_EVENT_HDR *CcEventHdr;
+
+ CcEventHdr = (CC_EVENT_HDR *)NewEventHdr;
+ DEBUG ((DEBUG_INFO, "Cca: Try to log event. Index = %d, EventType = 0x%x\n", CcEventHdr->MrIndex, CcEventHdr->EventType));
+
+ if (NewEventSize > MAX_ADDRESS - NewEventHdrSize) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewLogSize = NewEventHdrSize + NewEventSize;
+
+ if (NewLogSize > MAX_ADDRESS - EventLogAreaStruct->EventLogSize) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (NewLogSize + EventLogAreaStruct->EventLogSize > EventLogAreaStruct->Laml) {
+ DEBUG ((DEBUG_INFO, " Laml - 0x%x\n", EventLogAreaStruct->Laml));
+ DEBUG ((DEBUG_INFO, " NewLogSize - 0x%x\n", NewLogSize));
+ DEBUG ((DEBUG_INFO, " LogSize - 0x%x\n", EventLogAreaStruct->EventLogSize));
+ DEBUG ((DEBUG_INFO, "TcgCommLogEvent - %r\n", EFI_OUT_OF_RESOURCES));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Check 800-155 event
+ // Record to 800-155 event offset only.
+ // If the offset is 0, no need to record.
+ //
+ Record800155Event = Is800155Event (NewEventHdr, NewEventHdrSize, NewEventData, NewEventSize);
+ if (Record800155Event) {
+ DEBUG ((DEBUG_INFO, "It is 800155Event.\n"));
+
+ if (EventLogAreaStruct->Next800155EventOffset != 0) {
+ CopyMem (
+ (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset + NewLogSize,
+ (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset,
+ EventLogAreaStruct->EventLogSize - EventLogAreaStruct->Next800155EventOffset
+ );
+
+ CopyMem (
+ (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset,
+ NewEventHdr,
+ NewEventHdrSize
+ );
+ CopyMem (
+ (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->Next800155EventOffset + NewEventHdrSize,
+ NewEventData,
+ NewEventSize
+ );
+
+ EventLogAreaStruct->Next800155EventOffset += NewLogSize;
+ EventLogAreaStruct->LastEvent += NewLogSize;
+ EventLogAreaStruct->EventLogSize += NewLogSize;
+ }
+
+ return EFI_SUCCESS;
+ }
+
+ EventLogAreaStruct->LastEvent = (UINT8 *)(UINTN)EventLogAreaStruct->Lasa + EventLogAreaStruct->EventLogSize;
+ EventLogAreaStruct->EventLogSize += NewLogSize;
+
+ CopyMem (EventLogAreaStruct->LastEvent, NewEventHdr, NewEventHdrSize);
+ CopyMem (
+ EventLogAreaStruct->LastEvent + NewEventHdrSize,
+ NewEventData,
+ NewEventSize
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ According to UEFI Spec 2.10 Section 38.4.1:
+ The following table shows the TPM PCR index mapping and CC event log measurement
+ register index interpretation for ARM CCA, where RIM means Realm Initial Measurement
+ and REM means Realm Extensive Measurement
+
+ // TPM PCR Index | CC Measurement Register Index | CCA-measurement register
+ // ------------------------------------------------------------------------
+ // 0 | 0 | RIM
+ // 1, 7 | 1 | REM[0]
+ // 2~6 | 2 | REM[1]
+ // 8~15 | 3 | REM[2]
+
+ @param[in] PCRIndex Index of the TPM PCR
+
+ @retval UINT32 Index of the CC Event Log Measurement Register Index
+ @retval CC_MR_INDEX_INVALID Invalid MR Index
+**/
+UINT32
+EFIAPI
+MapPcrToMrIndex (
+ IN UINT32 PCRIndex
+ )
+{
+ UINT32 MrIndex;
+
+ if (PCRIndex > 15) {
+ ASSERT (FALSE);
+ return CC_MR_INDEX_INVALID;
+ }
+
+ MrIndex = 0;
+ if (PCRIndex == 0) {
+ MrIndex = CC_MR_INDEX_0_RIM;
+ } else if ((PCRIndex == 1) || (PCRIndex == 7)) {
+ MrIndex = CC_MR_INDEX_1_REM0;
+ } else if ((PCRIndex >= 2) && (PCRIndex <= 6)) {
+ MrIndex = CC_MR_INDEX_2_REM1;
+ } else if ((PCRIndex >= 8) && (PCRIndex <= 15)) {
+ MrIndex = CC_MR_INDEX_3_REM2;
+ }
+
+ return MrIndex;
+}
+
+EFI_STATUS
+EFIAPI
+CcaMapPcrToMrIndex (
+ IN EFI_CC_MEASUREMENT_PROTOCOL *This,
+ IN UINT32 PCRIndex,
+ OUT UINT32 *MrIndex
+ )
+{
+ if (MrIndex == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *MrIndex = MapPcrToMrIndex (PCRIndex);
+
+ return *MrIndex == CC_MR_INDEX_INVALID ? EFI_INVALID_PARAMETER : EFI_SUCCESS;
+}
+
+/**
+ Add a new entry to the Event Log.
+
+ @param[in] EventLogFormat The type of the event log for which the information is requested.
+ @param[in] NewEventHdr Pointer to a TCG_PCR_EVENT_HDR/TCG_PCR_EVENT_EX data structure.
+ @param[in] NewEventHdrSize New event header size.
+ @param[in] NewEventData Pointer to the new event data.
+ @param[in] NewEventSize New event data size.
+
+ @retval EFI_SUCCESS The new event log entry was added.
+ @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
+
+**/
+EFI_STATUS
+CcaDxeLogEvent (
+ IN EFI_CC_EVENT_LOG_FORMAT EventLogFormat,
+ IN VOID *NewEventHdr,
+ IN UINT32 NewEventHdrSize,
+ IN UINT8 *NewEventData,
+ IN UINT32 NewEventSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ CC_EVENT_LOG_AREA_STRUCT *EventLogAreaStruct;
+
+ if (EventLogFormat != EFI_CC_EVENT_LOG_FORMAT_TCG_2) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Index = 0;
+
+ //
+ // Record to normal event log
+ //
+ EventLogAreaStruct = &mCcaDxeData.EventLogAreaStruct[Index];
+
+ if (EventLogAreaStruct->EventLogTruncated) {
+ return EFI_VOLUME_FULL;
+ }
+
+ Status = TcgCommLogEvent (
+ EventLogAreaStruct,
+ NewEventHdr,
+ NewEventHdrSize,
+ NewEventData,
+ NewEventSize
+ );
+
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ EventLogAreaStruct->EventLogTruncated = TRUE;
+ return EFI_VOLUME_FULL;
+ } else if (Status == EFI_SUCCESS) {
+ EventLogAreaStruct->EventLogStarted = TRUE;
+ }
+
+ //
+ // If GetEventLog is called, record to FinalEventsTable, too.
+ //
+ if (mCcaDxeData.GetEventLogCalled[Index]) {
+ if (mCcaDxeData.FinalEventsTable[Index] == NULL) {
+ //
+ // no need for FinalEventsTable
+ //
+ return EFI_SUCCESS;
+ }
+
+ EventLogAreaStruct = &mCcaDxeData.FinalEventLogAreaStruct[Index];
+
+ if (EventLogAreaStruct->EventLogTruncated) {
+ return EFI_VOLUME_FULL;
+ }
+
+ Status = TcgCommLogEvent (
+ EventLogAreaStruct,
+ NewEventHdr,
+ NewEventHdrSize,
+ NewEventData,
+ NewEventSize
+ );
+ if (Status == EFI_OUT_OF_RESOURCES) {
+ EventLogAreaStruct->EventLogTruncated = TRUE;
+ return EFI_VOLUME_FULL;
+ } else if (Status == EFI_SUCCESS) {
+ EventLogAreaStruct->EventLogStarted = TRUE;
+ //
+ // Increase the NumberOfEvents in FinalEventsTable
+ //
+ (mCcaDxeData.FinalEventsTable[Index])->NumberOfEvents++;
+ DEBUG ((DEBUG_INFO, "FinalEventsTable->NumberOfEvents - 0x%x\n", (mCcaDxeData.FinalEventsTable[Index])->NumberOfEvents));
+ DEBUG ((DEBUG_INFO, " Size - 0x%x\n", (UINTN)EventLogAreaStruct->EventLogSize));
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Get TPML_DIGEST_VALUES compact binary buffer size.
+
+ @param[in] DigestListBin TPML_DIGEST_VALUES compact binary buffer.
+
+ @return TPML_DIGEST_VALUES compact binary buffer size.
+**/
+UINT32
+GetDigestListBinSize (
+ IN VOID *DigestListBin
+ )
+{
+ UINTN Index;
+ UINT16 DigestSize;
+ UINT32 TotalSize;
+ UINT32 Count;
+ TPMI_ALG_HASH HashAlg;
+
+ Count = ReadUnaligned32 (DigestListBin);
+ TotalSize = sizeof (Count);
+ DigestListBin = (UINT8 *)DigestListBin + sizeof (Count);
+ for (Index = 0; Index < Count; Index++) {
+ HashAlg = ReadUnaligned16 (DigestListBin);
+ TotalSize += sizeof (HashAlg);
+ DigestListBin = (UINT8 *)DigestListBin + sizeof (HashAlg);
+
+ DigestSize = GetHashSizeFromAlgo (HashAlg);
+ TotalSize += DigestSize;
+ DigestListBin = (UINT8 *)DigestListBin + DigestSize;
+ }
+
+ return TotalSize;
+}
+
+/**
+ Add a new entry to the Event Log. The call chain is like below:
+ CcaDxeLogHashEvent -> CcaDxeLogEvent -> TcgCommonLogEvent
+
+ Before this function is called, the event information (including the digest)
+ is ready.
+
+ @param[in] DigestList A list of digest.
+ @param[in,out] NewEventHdr Pointer to a CC_EVENT_HDR data structure.
+ @param[in] NewEventData Pointer to the new event data.
+
+ @retval EFI_SUCCESS The new event log entry was added.
+ @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
+**/
+EFI_STATUS
+CcaDxeLogHashEvent (
+ IN TPML_DIGEST_VALUES *DigestList,
+ IN OUT CC_EVENT_HDR *NewEventHdr,
+ IN UINT8 *NewEventData
+ )
+{
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ EFI_STATUS RetStatus;
+ CC_EVENT CcEvent;
+ UINT8 *DigestBuffer;
+ UINT32 *EventSizePtr;
+ EFI_CC_EVENT_LOG_FORMAT LogFormat;
+
+ RetStatus = EFI_SUCCESS;
+ LogFormat = EFI_CC_EVENT_LOG_FORMAT_TCG_2;
+
+ ZeroMem (&CcEvent, sizeof (CcEvent));
+ CcEvent.MrIndex = NewEventHdr->MrIndex;
+ CcEvent.EventType = NewEventHdr->EventType;
+ DigestBuffer = (UINT8 *)&CcEvent.Digests;
+ EventSizePtr = CopyDigestListToBuffer (DigestBuffer, DigestList, HASH_ALG_SHA256);
+ CopyMem (EventSizePtr, &NewEventHdr->EventSize, sizeof (NewEventHdr->EventSize));
+
+ //
+ // Enter critical region
+ //
+ OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+ Status = CcaDxeLogEvent (
+ LogFormat,
+ &CcEvent,
+ sizeof (CcEvent.MrIndex) + sizeof (CcEvent.EventType) + GetDigestListBinSize (DigestBuffer) + sizeof (CcEvent.EventSize),
+ NewEventData,
+ NewEventHdr->EventSize
+ );
+ if (Status != EFI_SUCCESS) {
+ RetStatus = Status;
+ }
+
+ gBS->RestoreTPL (OldTpl);
+
+ return RetStatus;
+}
+
+/**
+ Do a hash operation on a data buffer, extend a specific REM with the hash result,
+ and add an entry to the Event Log.
+
+ @param[in] Flags Bitmap providing additional information.
+ @param[in] HashData Physical address of the start of the data buffer
+ to be hashed, extended, and logged.
+ @param[in] HashDataLen The length, in bytes, of the buffer referenced by HashData
+ @param[in, out] NewEventHdr Pointer to a CC_EVENT_HDR data structure.
+ @param[in] NewEventData Pointer to the new event data.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES No enough memory to log the new event.
+ @retval EFI_DEVICE_ERROR The command was unsuccessful.
+
+**/
+EFI_STATUS
+CcaDxeHashLogExtendEvent (
+ IN UINT64 Flags,
+ IN UINT8 *HashData,
+ IN UINT64 HashDataLen,
+ IN OUT CC_EVENT_HDR *NewEventHdr,
+ IN UINT8 *NewEventData
+ )
+{
+ EFI_STATUS Status;
+ TPML_DIGEST_VALUES DigestList;
+ CC_EVENT_HDR NoActionEvent;
+
+ if (NewEventHdr->EventType == EV_NO_ACTION) {
+ //
+ // Do not do REM extend for EV_NO_ACTION
+ //
+ Status = EFI_SUCCESS;
+ InitNoActionEvent (&NoActionEvent, NewEventHdr->EventSize);
+ if ((Flags & EFI_CC_FLAG_EXTEND_ONLY) == 0) {
+ Status = CcaDxeLogHashEvent (&(NoActionEvent.Digests), NewEventHdr, NewEventData);
+ }
+
+ return Status;
+ }
+
+ //
+ // According to UEFI Spec 2.10 Section 38.4.x the mapping between MrIndex and ARM
+ // CCA Measurement Register is:
+ // MrIndex 0 <--> RIM
+ // MrIndex 1-3 <--> REM[0-2]
+ // Only the REM registers can be extended in EDK2 by HashAndExtend.
+ //
+ Status = HashAndExtend (
+ NewEventHdr->MrIndex,
+ HashData,
+ (UINTN)HashDataLen,
+ &DigestList
+ );
+ if (!EFI_ERROR (Status)) {
+ if ((Flags & EFI_CC_FLAG_EXTEND_ONLY) == 0) {
+ Status = CcaDxeLogHashEvent (&DigestList, NewEventHdr, NewEventData);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ The EFI_CC_MEASUREMENT_PROTOCOL HashLogExtendEvent function call provides callers with
+ an opportunity to extend and optionally log events without requiring
+ knowledge of actual TPM commands.
+ The extend operation will occur even if this function cannot create an event
+ log entry (e.g. due to the event log being full).
+
+ @param[in] This Indicates the calling context
+ @param[in] Flags Bitmap providing additional information.
+ @param[in] DataToHash Physical address of the start of the data buffer to be hashed.
+ @param[in] DataToHashLen The length in bytes of the buffer referenced by DataToHash.
+ @param[in] Event Pointer to data buffer containing information about the event.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_DEVICE_ERROR The command was unsuccessful.
+ @retval EFI_VOLUME_FULL The extend operation occurred, but the event could not be written to one or more event logs.
+ @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect.
+ @retval EFI_UNSUPPORTED The PE/COFF image type is not supported.
+**/
+EFI_STATUS
+EFIAPI
+CcaHashLogExtendEvent (
+ IN EFI_CC_MEASUREMENT_PROTOCOL *This,
+ IN UINT64 Flags,
+ IN EFI_PHYSICAL_ADDRESS DataToHash,
+ IN UINT64 DataToHashLen,
+ IN EFI_CC_EVENT *CcEvent
+ )
+{
+ EFI_STATUS Status;
+ CC_EVENT_HDR NewEventHdr;
+ TPML_DIGEST_VALUES DigestList;
+
+ DEBUG ((DEBUG_VERBOSE, "CcaHashLogExtendEvent ...\n"));
+
+ if ((This == NULL) || (CcEvent == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Do not check hash data size for EV_NO_ACTION event.
+ //
+ if ((CcEvent->Header.EventType != EV_NO_ACTION) && (DataToHash == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CcEvent->Size < CcEvent->Header.HeaderSize + sizeof (UINT32)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CcEvent->Header.MrIndex == CC_MR_INDEX_0_RIM) {
+ DEBUG ((DEBUG_ERROR, "%a: RIM cannot be extended.\n", __func__));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CcEvent->Header.MrIndex >= CC_MR_INDEX_INVALID) {
+ DEBUG ((DEBUG_ERROR, "%a: MrIndex is invalid. (%d)\n", __func__, CcEvent->Header.MrIndex));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NewEventHdr.MrIndex = CcEvent->Header.MrIndex;
+ NewEventHdr.EventType = CcEvent->Header.EventType;
+ NewEventHdr.EventSize = CcEvent->Size - sizeof (UINT32) - CcEvent->Header.HeaderSize;
+ if ((Flags & EFI_CC_FLAG_PE_COFF_IMAGE) != 0) {
+ //
+ // According to UEFI Spec 2.10 Section 38.4.x the mapping between MrIndex and ARM
+ // CCA Measurement Register is:
+ // MrIndex 0 <--> RIM
+ // MrIndex 1-3 <--> REM[0-2]
+ // Only the REM registers can be extended in EDK2 by HashAndExtend.
+ //
+ Status = MeasurePeImageAndExtend (
+ NewEventHdr.MrIndex,
+ DataToHash,
+ (UINTN)DataToHashLen,
+ &DigestList
+ );
+ if (!EFI_ERROR (Status)) {
+ if ((Flags & EFI_CC_FLAG_EXTEND_ONLY) == 0) {
+ Status = CcaDxeLogHashEvent (&DigestList, &NewEventHdr, CcEvent->Event);
+ }
+ }
+ } else {
+ Status = CcaDxeHashLogExtendEvent (
+ Flags,
+ (UINT8 *)(UINTN)DataToHash,
+ DataToHashLen,
+ &NewEventHdr,
+ CcEvent->Event
+ );
+ }
+
+ DEBUG ((DEBUG_VERBOSE, "CcaHashLogExtendEvent - %r\n", Status));
+ return Status;
+}
+
+EFI_CC_MEASUREMENT_PROTOCOL mCcaProtocol = {
+ CcaGetCapability,
+ CcaGetEventLog,
+ CcaHashLogExtendEvent,
+ CcaMapPcrToMrIndex
+};
+
+#define CCA_HASH_COUNT 1
+#define TEMP_BUF_LEN (sizeof(TCG_EfiSpecIDEventStruct) + sizeof(UINT32) \
+ + (CCA_HASH_COUNT * sizeof(TCG_EfiSpecIdEventAlgorithmSize)) + sizeof(UINT8))
+
+/**
+ Initialize the CC Event Log and log events passed from the PEI phase.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+
+**/
+EFI_STATUS
+SetupCcEventLog (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Lasa;
+ UINTN Index;
+ TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct;
+ UINT8 TempBuf[TEMP_BUF_LEN];
+ TCG_PCR_EVENT_HDR SpecIdEvent;
+ TCG_EfiSpecIdEventAlgorithmSize *DigestSize;
+ TCG_EfiSpecIdEventAlgorithmSize *TempDigestSize;
+ UINT8 *VendorInfoSize;
+ UINT32 NumberOfAlgorithms;
+ EFI_CC_EVENT_LOG_FORMAT LogFormat;
+ EFI_PEI_HOB_POINTERS GuidHob;
+ CC_EVENT_HDR NoActionEvent;
+
+ Status = EFI_SUCCESS;
+ DEBUG ((DEBUG_INFO, "SetupCcEventLog\n"));
+
+ Index = 0;
+ LogFormat = EFI_CC_EVENT_LOG_FORMAT_TCG_2;
+
+ //
+ // 1. Create Log Area
+ //
+ mCcaDxeData.EventLogAreaStruct[Index].EventLogFormat = LogFormat;
+
+ // allocate pages for CC Event log
+ Status = gBS->AllocatePages (
+ AllocateAnyPages,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcgLogAreaMinLen)),
+ &Lasa
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ mCcaDxeData.EventLogAreaStruct[Index].Lasa = Lasa;
+ mCcaDxeData.EventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcgLogAreaMinLen);
+ mCcaDxeData.EventLogAreaStruct[Index].Next800155EventOffset = 0;
+
+ //
+ // Report CC event log address and length, so that they can be reported in
+ // CC ACPI table. Ignore the return status, because those fields are optional.
+ //
+ PcdSet32S (PcdCcEventlogAcpiTableLaml, (UINT32)mCcaDxeData.EventLogAreaStruct[Index].Laml);
+ PcdSet64S (PcdCcEventlogAcpiTableLasa, mCcaDxeData.EventLogAreaStruct[Index].Lasa);
+
+ //
+ // To initialize them as 0xFF is recommended
+ // because the OS can know the last entry for that.
+ //
+ SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcgLogAreaMinLen), 0xFF);
+
+ //
+ // Create first entry for Log Header Entry Data
+ //
+
+ //
+ // TcgEfiSpecIdEventStruct
+ //
+ TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *)TempBuf;
+ CopyMem (TcgEfiSpecIdEventStruct->signature, TCG_EfiSpecIDEventStruct_SIGNATURE_03, sizeof (TcgEfiSpecIdEventStruct->signature));
+
+ TcgEfiSpecIdEventStruct->platformClass = PcdGet8 (PcdTpmPlatformClass);
+
+ TcgEfiSpecIdEventStruct->specVersionMajor = TCG_EfiSpecIDEventStruct_SPEC_VERSION_MAJOR_TPM2;
+ TcgEfiSpecIdEventStruct->specVersionMinor = TCG_EfiSpecIDEventStruct_SPEC_VERSION_MINOR_TPM2;
+ TcgEfiSpecIdEventStruct->specErrata = TCG_EfiSpecIDEventStruct_SPEC_ERRATA_TPM2;
+ TcgEfiSpecIdEventStruct->uintnSize = sizeof (UINTN)/sizeof (UINT32);
+ NumberOfAlgorithms = 0;
+ DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct
+ + sizeof (*TcgEfiSpecIdEventStruct)
+ + sizeof (NumberOfAlgorithms));
+
+ TempDigestSize = DigestSize;
+ TempDigestSize += NumberOfAlgorithms;
+ TempDigestSize->algorithmId = TPM_ALG_SHA256;
+ TempDigestSize->digestSize = SHA256_DIGEST_SIZE;
+ NumberOfAlgorithms++;
+
+ CopyMem (TcgEfiSpecIdEventStruct + 1, &NumberOfAlgorithms, sizeof (NumberOfAlgorithms));
+ TempDigestSize = DigestSize;
+ TempDigestSize += NumberOfAlgorithms;
+ VendorInfoSize = (UINT8 *)TempDigestSize;
+ *VendorInfoSize = 0;
+
+ SpecIdEvent.PCRIndex = 1; // PCRIndex 0 maps to MrIndex 1
+ SpecIdEvent.EventType = EV_NO_ACTION;
+ ZeroMem (&SpecIdEvent.Digest, sizeof (SpecIdEvent.Digest));
+ SpecIdEvent.EventSize = (UINT32)GetTcgEfiSpecIdEventStructSize (TcgEfiSpecIdEventStruct);
+
+ //
+ // CC Event log re-use the spec of TCG2 Event log.
+ // Log TcgEfiSpecIdEventStruct as the first Event. Event format is TCG_PCR_EVENT.
+ // TCG EFI Protocol Spec. Section 5.3 Event Log Header
+ // TCG PC Client PFP spec. Section 9.2 Measurement Event Entries and Log
+ //
+ Status = CcaDxeLogEvent (
+ LogFormat,
+ &SpecIdEvent,
+ sizeof (SpecIdEvent),
+ (UINT8 *)TcgEfiSpecIdEventStruct,
+ SpecIdEvent.EventSize
+ );
+ //
+ // record the offset at the end of 800-155 event.
+ // the future 800-155 event can be inserted here.
+ //
+ mCcaDxeData.EventLogAreaStruct[Index].Next800155EventOffset = mCcaDxeData.EventLogAreaStruct[Index].EventLogSize;
+
+ //
+ // Tcg800155PlatformIdEvent. Event format is TCG_PCR_EVENT2
+ //
+ GuidHob.Guid = GetFirstGuidHob (&gTcg800155PlatformIdEventHobGuid);
+ while (GuidHob.Guid != NULL) {
+ InitNoActionEvent (&NoActionEvent, GET_GUID_HOB_DATA_SIZE (GuidHob.Guid));
+
+ Status = CcaDxeLogEvent (
+ LogFormat,
+ &NoActionEvent,
+ sizeof (NoActionEvent.MrIndex) + sizeof (NoActionEvent.EventType) + GetDigestListBinSize (&NoActionEvent.Digests) + sizeof (NoActionEvent.EventSize),
+ GET_GUID_HOB_DATA (GuidHob.Guid),
+ GET_GUID_HOB_DATA_SIZE (GuidHob.Guid)
+ );
+
+ GuidHob.Guid = GET_NEXT_HOB (GuidHob);
+ GuidHob.Guid = GetNextGuidHob (&gTcg800155PlatformIdEventHobGuid, GuidHob.Guid);
+ }
+
+ //
+ // 2. Create Final Log Area
+ //
+ Status = gBS->AllocatePages (
+ AllocateAnyPages,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (PcdGet32 (PcdTcg2FinalLogAreaLen)),
+ &Lasa
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ SetMem ((VOID *)(UINTN)Lasa, PcdGet32 (PcdTcg2FinalLogAreaLen), 0xFF);
+
+ //
+ // Initialize
+ //
+ mCcaDxeData.FinalEventsTable[Index] = (VOID *)(UINTN)Lasa;
+ (mCcaDxeData.FinalEventsTable[Index])->Version = EFI_TCG2_FINAL_EVENTS_TABLE_VERSION;
+ (mCcaDxeData.FinalEventsTable[Index])->NumberOfEvents = 0;
+
+ mCcaDxeData.FinalEventLogAreaStruct[Index].EventLogFormat = LogFormat;
+ mCcaDxeData.FinalEventLogAreaStruct[Index].Lasa = Lasa + sizeof (EFI_CC_FINAL_EVENTS_TABLE);
+ mCcaDxeData.FinalEventLogAreaStruct[Index].Laml = PcdGet32 (PcdTcg2FinalLogAreaLen) - sizeof (EFI_CC_FINAL_EVENTS_TABLE);
+ mCcaDxeData.FinalEventLogAreaStruct[Index].EventLogSize = 0;
+ mCcaDxeData.FinalEventLogAreaStruct[Index].LastEvent = (VOID *)(UINTN)mCcaDxeData.FinalEventLogAreaStruct[Index].Lasa;
+ mCcaDxeData.FinalEventLogAreaStruct[Index].EventLogStarted = FALSE;
+ mCcaDxeData.FinalEventLogAreaStruct[Index].EventLogTruncated = FALSE;
+ mCcaDxeData.FinalEventLogAreaStruct[Index].Next800155EventOffset = 0;
+
+ //
+ // Install to configuration table for EFI_CC_EVENT_LOG_FORMAT_TCG_2
+ //
+ Status = gBS->InstallConfigurationTable (&gEfiCcFinalEventsTableGuid, (VOID *)mCcaDxeData.FinalEventsTable[Index]);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return Status;
+}
+
+/**
+ Measure and log an action string, and extend the measurement result into REM.
+
+ @param[in] MrIndex MrIndex to extend
+ @param[in] String A specific string that indicates an Action event.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+CcaMeasureAction (
+ IN UINT32 MrIndex,
+ IN CHAR8 *String
+ )
+{
+ CC_EVENT_HDR CcEvent;
+
+ CcEvent.MrIndex = MrIndex;
+ CcEvent.EventType = EV_EFI_ACTION;
+ CcEvent.EventSize = (UINT32)AsciiStrLen (String);
+ return CcaDxeHashLogExtendEvent (
+ 0,
+ (UINT8 *)String,
+ CcEvent.EventSize,
+ &CcEvent,
+ (UINT8 *)String
+ );
+}
+
+/**
+ Measure and log EFI handoff tables, and extend the measurement result into PCR[1].
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+MeasureHandoffTables (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ Status = EFI_SUCCESS;
+
+ // TODO: may delete this function
+ if (PcdGet8 (PcdTpmPlatformClass) == TCG_PLATFORM_TYPE_SERVER) {
+ DEBUG ((DEBUG_ERROR, "MeasureHandoffTables: TCG_PLATFORM_TYPE_SERVER"));
+ }
+
+ return Status;
+}
+
+/**
+ Measure and log Separator event, and extend the measurement result into a specific PCR.
+
+ @param[in] MrIndex REM Index.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+MeasureSeparatorEvent (
+ IN UINT32 MrIndex
+ )
+{
+ CC_EVENT_HDR CcEvent;
+ UINT32 EventData;
+
+ DEBUG ((DEBUG_INFO, "MeasureSeparatorEvent to Rem - %d\n", MrIndex));
+
+ EventData = 0;
+ CcEvent.MrIndex = MrIndex;
+ CcEvent.EventType = EV_SEPARATOR;
+ CcEvent.EventSize = (UINT32)sizeof (EventData);
+
+ return CcaDxeHashLogExtendEvent (
+ 0,
+ (UINT8 *)&EventData,
+ sizeof (EventData),
+ &CcEvent,
+ (UINT8 *)&EventData
+ );
+}
+
+/**
+ Measure and log an EFI variable, and extend the measurement result into a specific REM.
+
+ @param[in] MrIndex REM Index.
+ @param[in] EventType Event type.
+ @param[in] VarName A Null-terminated string that is the name of the vendor's variable.
+ @param[in] VendorGuid A unique identifier for the vendor.
+ @param[in] VarData The content of the variable data.
+ @param[in] VarSize The size of the variable data.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+MeasureVariable (
+ IN UINT32 MrIndex,
+ IN TCG_EVENTTYPE EventType,
+ IN CHAR16 *VarName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *VarData,
+ IN UINTN VarSize
+ )
+{
+ EFI_STATUS Status;
+ CC_EVENT_HDR CcEvent;
+ UINTN VarNameLength;
+ UEFI_VARIABLE_DATA *VarLog;
+
+ DEBUG ((DEBUG_INFO, "CcaTcg2Dxe: MeasureVariable (REM - %x, EventType - %x, ", (UINTN)MrIndex, (UINTN)EventType));
+ DEBUG ((DEBUG_INFO, "VariableName - %s, VendorGuid - %g)\n", VarName, VendorGuid));
+
+ VarNameLength = StrLen (VarName);
+ CcEvent.MrIndex = MrIndex;
+ CcEvent.EventType = EventType;
+
+ CcEvent.EventSize = (UINT32)(sizeof (*VarLog) + VarNameLength * sizeof (*VarName) + VarSize
+ - sizeof (VarLog->UnicodeName) - sizeof (VarLog->VariableData));
+
+ VarLog = (UEFI_VARIABLE_DATA *)AllocatePool (CcEvent.EventSize);
+ if (VarLog == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ VarLog->VariableName = *VendorGuid;
+ VarLog->UnicodeNameLength = VarNameLength;
+ VarLog->VariableDataLength = VarSize;
+ CopyMem (
+ VarLog->UnicodeName,
+ VarName,
+ VarNameLength * sizeof (*VarName)
+ );
+ if ((VarSize != 0) && (VarData != NULL)) {
+ CopyMem (
+ (CHAR16 *)VarLog->UnicodeName + VarNameLength,
+ VarData,
+ VarSize
+ );
+ }
+
+ if (EventType == EV_EFI_VARIABLE_DRIVER_CONFIG) {
+ //
+ // Digest is the event data (UEFI_VARIABLE_DATA)
+ //
+ Status = CcaDxeHashLogExtendEvent (
+ 0,
+ (UINT8 *)VarLog,
+ CcEvent.EventSize,
+ &CcEvent,
+ (UINT8 *)VarLog
+ );
+ } else {
+ ASSERT (VarData != NULL);
+ Status = CcaDxeHashLogExtendEvent (
+ 0,
+ (UINT8 *)VarData,
+ VarSize,
+ &CcEvent,
+ (UINT8 *)VarLog
+ );
+ }
+
+ FreePool (VarLog);
+ return Status;
+}
+
+/**
+ Read then Measure and log an EFI variable, and extend the measurement result into a specific REM.
+
+ @param[in] MrIndex REM Index.
+ @param[in] EventType Event type.
+ @param[in] VarName A Null-terminated string that is the name of the vendor's variable.
+ @param[in] VendorGuid A unique identifier for the vendor.
+ @param[out] VarSize The size of the variable data.
+ @param[out] VarData Pointer to the content of the variable.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+ReadAndMeasureVariable (
+ IN UINT32 MrIndex,
+ IN TCG_EVENTTYPE EventType,
+ IN CHAR16 *VarName,
+ IN EFI_GUID *VendorGuid,
+ OUT UINTN *VarSize,
+ OUT VOID **VarData
+ )
+{
+ EFI_STATUS Status;
+
+ Status = GetVariable2 (VarName, VendorGuid, VarData, VarSize);
+ if (EventType == EV_EFI_VARIABLE_DRIVER_CONFIG) {
+ if (EFI_ERROR (Status)) {
+ //
+ // It is valid case, so we need handle it.
+ //
+ *VarData = NULL;
+ *VarSize = 0;
+ }
+ } else {
+ //
+ // if status error, VarData is freed and set NULL by GetVariable2
+ //
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ Status = MeasureVariable (
+ MrIndex,
+ EventType,
+ VarName,
+ VendorGuid,
+ *VarData,
+ *VarSize
+ );
+ return Status;
+}
+
+/**
+ Read then Measure and log an EFI boot variable, and extend the measurement result into PCR[1].
+according to TCG PC Client PFP spec 0021 Section 2.4.4.2
+
+ @param[in] VarName A Null-terminated string that is the name of the vendor's variable.
+ @param[in] VendorGuid A unique identifier for the vendor.
+ @param[out] VarSize The size of the variable data.
+ @param[out] VarData Pointer to the content of the variable.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+ReadAndMeasureBootVariable (
+ IN CHAR16 *VarName,
+ IN EFI_GUID *VendorGuid,
+ OUT UINTN *VarSize,
+ OUT VOID **VarData
+ )
+{
+ return ReadAndMeasureVariable (
+ MapPcrToMrIndex (1),
+ EV_EFI_VARIABLE_BOOT,
+ VarName,
+ VendorGuid,
+ VarSize,
+ VarData
+ );
+}
+
+/**
+ Read then Measure and log an EFI Secure variable, and extend the measurement result into PCR[7].
+
+ @param[in] VarName A Null-terminated string that is the name of the vendor's variable.
+ @param[in] VendorGuid A unique identifier for the vendor.
+ @param[out] VarSize The size of the variable data.
+ @param[out] VarData Pointer to the content of the variable.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+ReadAndMeasureSecureVariable (
+ IN CHAR16 *VarName,
+ IN EFI_GUID *VendorGuid,
+ OUT UINTN *VarSize,
+ OUT VOID **VarData
+ )
+{
+ return ReadAndMeasureVariable (
+ MapPcrToMrIndex (7),
+ EV_EFI_VARIABLE_DRIVER_CONFIG,
+ VarName,
+ VendorGuid,
+ VarSize,
+ VarData
+ );
+}
+
+/**
+ Measure and log all EFI boot variables, and extend the measurement result into a specific PCR.
+
+ The EFI boot variables are BootOrder and Boot#### variables.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+MeasureAllBootVariables (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT16 *BootOrder;
+ UINTN BootCount;
+ UINTN Index;
+ VOID *BootVarData;
+ UINTN Size;
+
+ Status = ReadAndMeasureBootVariable (
+ mBootVarName,
+ &gEfiGlobalVariableGuid,
+ &BootCount,
+ (VOID **)&BootOrder
+ );
+ if ((Status == EFI_NOT_FOUND) || (BootOrder == NULL)) {
+ return EFI_SUCCESS;
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // BootOrder can't be NULL if status is not EFI_NOT_FOUND
+ //
+ FreePool (BootOrder);
+ return Status;
+ }
+
+ BootCount /= sizeof (*BootOrder);
+ for (Index = 0; Index < BootCount; Index++) {
+ UnicodeSPrint (mBootVarName, sizeof (mBootVarName), L"Boot%04x", BootOrder[Index]);
+ Status = ReadAndMeasureBootVariable (
+ mBootVarName,
+ &gEfiGlobalVariableGuid,
+ &Size,
+ &BootVarData
+ );
+ if (!EFI_ERROR (Status)) {
+ FreePool (BootVarData);
+ }
+ }
+
+ FreePool (BootOrder);
+ return EFI_SUCCESS;
+}
+
+/**
+ Measure and log all EFI Secure variables, and extend the measurement result into a specific PCR.
+
+ The EFI boot variables are BootOrder and Boot#### variables.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+MeasureAllSecureVariables (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ VOID *Data;
+ UINTN DataSize;
+ UINTN Index;
+
+ Status = EFI_NOT_FOUND;
+ for (Index = 0; Index < sizeof (mVariableType)/sizeof (mVariableType[0]); Index++) {
+ Status = ReadAndMeasureSecureVariable (
+ mVariableType[Index].VariableName,
+ mVariableType[Index].VendorGuid,
+ &DataSize,
+ &Data
+ );
+ if (!EFI_ERROR (Status)) {
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+ }
+ }
+
+ //
+ // Measure DBT if present and not empty
+ //
+ Status = GetVariable2 (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid, &Data, &DataSize);
+ if (!EFI_ERROR (Status)) {
+ Status = MeasureVariable (
+ MapPcrToMrIndex (7),
+ EV_EFI_VARIABLE_DRIVER_CONFIG,
+ EFI_IMAGE_SECURITY_DATABASE2,
+ &gEfiImageSecurityDatabaseGuid,
+ Data,
+ DataSize
+ );
+ FreePool (Data);
+ } else {
+ DEBUG ((DEBUG_INFO, "Skip measuring variable %s since it's deleted\n", EFI_IMAGE_SECURITY_DATABASE2));
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Measure and log launch of FirmwareDebugger, and extend the measurement result into a specific PCR.
+
+ @retval EFI_SUCCESS Operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Out of memory.
+ @retval EFI_DEVICE_ERROR The operation was unsuccessful.
+
+**/
+EFI_STATUS
+MeasureLaunchOfFirmwareDebugger (
+ VOID
+ )
+{
+ CC_EVENT_HDR CcEvent;
+
+ CcEvent.MrIndex = MapPcrToMrIndex (7);
+ CcEvent.EventType = EV_EFI_ACTION;
+ CcEvent.EventSize = sizeof (FIRMWARE_DEBUGGER_EVENT_STRING) - 1;
+ return CcaDxeHashLogExtendEvent (
+ 0,
+ (UINT8 *)FIRMWARE_DEBUGGER_EVENT_STRING,
+ sizeof (FIRMWARE_DEBUGGER_EVENT_STRING) - 1,
+ &CcEvent,
+ (UINT8 *)FIRMWARE_DEBUGGER_EVENT_STRING
+ );
+}
+
+/**
+ Measure and log all Secure Boot Policy, and extend the measurement result into a specific PCR.
+
+ Platform firmware adhering to the policy must therefore measure the following values into PCR[7]: (in order listed)
+ - The contents of the SecureBoot variable
+ - The contents of the PK variable
+ - The contents of the KEK variable
+ - The contents of the EFI_IMAGE_SECURITY_DATABASE variable
+ - The contents of the EFI_IMAGE_SECURITY_DATABASE1 variable
+ - Separator
+ - Entries in the EFI_IMAGE_SECURITY_DATABASE that are used to validate EFI Drivers or EFI Boot Applications in the boot path
+
+ NOTE: Because of the above, UEFI variables PK, KEK, EFI_IMAGE_SECURITY_DATABASE,
+ EFI_IMAGE_SECURITY_DATABASE1 and SecureBoot SHALL NOT be measured into PCR[3].
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+MeasureSecureBootPolicy (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ VOID *Protocol;
+
+ Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, (VOID **)&Protocol);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ if (PcdGetBool (PcdFirmwareDebuggerInitialized)) {
+ Status = MeasureLaunchOfFirmwareDebugger ();
+ DEBUG ((DEBUG_INFO, "MeasureLaunchOfFirmwareDebugger - %r\n", Status));
+ }
+
+ Status = MeasureAllSecureVariables ();
+ DEBUG ((DEBUG_INFO, "MeasureAllSecureVariables - %r\n", Status));
+
+ //
+ // We need measure Separator(7) here, because this event must be between SecureBootPolicy (Configure)
+ // and ImageVerification (Authority)
+ // There might be a case that we need measure UEFI image from DriverOrder, besides BootOrder. So
+ // the Authority measurement happen before ReadToBoot event.
+ //
+ Status = MeasureSeparatorEvent (MapPcrToMrIndex (7));
+ DEBUG ((DEBUG_INFO, "MeasureSeparatorEvent - %r\n", Status));
+ return;
+}
+
+/**
+ Ready to Boot Event notification handler.
+
+ Sequence of OS boot events is measured in this event notification handler.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+
+**/
+VOID
+EFIAPI
+OnReadyToBoot (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ PERF_START_EX (mImageHandle, "EventRec", "CcaTcg2Dxe", 0, PERF_ID_CC_TCG2_DXE);
+ if (mBootAttempts == 0) {
+ //
+ // Measure handoff tables.
+ //
+ Status = MeasureHandoffTables ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "HOBs not Measured. Error!\n"));
+ }
+
+ //
+ // Measure BootOrder & Boot#### variables.
+ //
+ Status = MeasureAllBootVariables ();
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Boot Variables not Measured. Error!\n"));
+ }
+
+ //
+ // 1. This is the first boot attempt.
+ //
+ Status = CcaMeasureAction (
+ MapPcrToMrIndex (4),
+ EFI_CALLING_EFI_APPLICATION
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a not Measured. Error!\n", EFI_CALLING_EFI_APPLICATION));
+ }
+
+ //
+ // 2. Draw a line between pre-boot env and entering post-boot env.
+ //
+ // According to UEFI Spec 2.10 Section 38.4.x the mapping between MrIndex and ARM
+ // CCA Measurement Register is:
+ // MrIndex 0 <--> RIM
+ // MrIndex 1-3 <--> REM[0-2]
+ // REM[0] (i.e. MrIndex 1) is already done. So SepartorEvent shall be extended to
+ // REM[1] (i.e. MrIndex 2) as well.
+ //
+ Status = MeasureSeparatorEvent (CC_MR_INDEX_2_REM1);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Separator Event not Measured to REM[1]. Error!\n"));
+ }
+
+ //
+ // 3. Measure GPT. It would be done in SAP driver.
+ //
+
+ //
+ // 4. Measure PE/COFF OS loader. It would be done in SAP driver.
+ //
+
+ //
+ // 5. Read & Measure variable. BootOrder already measured.
+ //
+ } else {
+ //
+ // 6. Not first attempt, meaning a return from last attempt
+ //
+ Status = CcaMeasureAction (
+ MapPcrToMrIndex (4),
+ EFI_RETURNING_FROM_EFI_APPLICATION
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a not Measured. Error!\n", EFI_RETURNING_FROM_EFI_APPLICATION));
+ }
+ //
+ // 7. Next boot attempt, measure "Calling EFI Application from Boot Option" again
+ // TCG PC Client PFP spec Section 2.4.4.5 Step 4
+ //
+ Status = CcaMeasureAction (
+ MapPcrToMrIndex (4),
+ EFI_CALLING_EFI_APPLICATION
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a not Measured. Error!\n", EFI_CALLING_EFI_APPLICATION));
+ }
+ }
+
+ DEBUG ((DEBUG_INFO, "CcaTcg2Dxe Measure Data when ReadyToBoot\n"));
+ //
+ // Increase boot attempt counter.
+ //
+ mBootAttempts++;
+ PERF_END_EX (mImageHandle, "EventRec", "CcaTcg2Dxe", 0, PERF_ID_CC_TCG2_DXE + 1);
+}
+
+/**
+ Exit Boot Services Event notification handler.
+
+ Measure invocation and success of ExitBootServices.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+
+**/
+VOID
+EFIAPI
+OnExitBootServices (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Measure invocation of ExitBootServices,
+ //
+ Status = CcaMeasureAction (
+ MapPcrToMrIndex (5),
+ EFI_EXIT_BOOT_SERVICES_INVOCATION
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_INVOCATION));
+ }
+
+ //
+ // Measure success of ExitBootServices
+ //
+ Status = CcaMeasureAction (
+ MapPcrToMrIndex (5),
+ EFI_EXIT_BOOT_SERVICES_SUCCEEDED
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_SUCCEEDED));
+ }
+}
+
+/**
+ Exit Boot Services Failed Event notification handler.
+
+ Measure Failure of ExitBootServices.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+
+**/
+VOID
+EFIAPI
+OnExitBootServicesFailed (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Measure Failure of ExitBootServices,
+ //
+ Status = CcaMeasureAction (
+ MapPcrToMrIndex (5),
+ EFI_EXIT_BOOT_SERVICES_FAILED
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a not Measured. Error!\n", EFI_EXIT_BOOT_SERVICES_FAILED));
+ }
+}
+
+/**
+ Install ACPI Table when ACPI Table Protocol is available.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+InstallAcpiTable (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UINTN TableKey;
+ EFI_STATUS Status;
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTable;
+
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **)&AcpiTable);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "CCA: AcpiTableProtocol is not installed. %r\n", Status));
+ return;
+ }
+
+ mCcaEventlogAcpiTemplate.Laml = (UINT64)PcdGet32 (PcdCcEventlogAcpiTableLaml);
+ mCcaEventlogAcpiTemplate.Lasa = PcdGet64 (PcdCcEventlogAcpiTableLasa);
+ CopyMem (mCcaEventlogAcpiTemplate.Header.OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (mCcaEventlogAcpiTemplate.Header.OemId));
+ mCcaEventlogAcpiTemplate.Header.OemTableId = PcdGet64 (PcdAcpiDefaultOemTableId);
+ mCcaEventlogAcpiTemplate.Header.OemRevision = PcdGet32 (PcdAcpiDefaultOemRevision);
+ mCcaEventlogAcpiTemplate.Header.CreatorId = PcdGet32 (PcdAcpiDefaultCreatorId);
+ mCcaEventlogAcpiTemplate.Header.CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);
+
+ //
+ // Construct ACPI Table
+ Status = AcpiTable->InstallAcpiTable (
+ AcpiTable,
+ &mCcaEventlogAcpiTemplate,
+ mCcaEventlogAcpiTemplate.Header.Length,
+ &TableKey
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ DEBUG ((DEBUG_INFO, "CCA Eventlog ACPI Table is installed.\n"));
+}
+
+/**
+ The function install CcaTcg2 protocol.
+
+ @retval EFI_SUCCESS CcaTcg2 protocol is installed.
+ @retval other Some error occurs.
+**/
+EFI_STATUS
+InstallCcMeasurementProtocol (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEfiCcMeasurementProtocolGuid,
+ &mCcaProtocol,
+ NULL
+ );
+ DEBUG ((DEBUG_INFO, "CcProtocol: Install %r\n", Status));
+ return Status;
+}
+
+EFI_STATUS
+EFIAPI
+DriverEntry (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+)
+{
+ EFI_STATUS Status;
+ EFI_EVENT Event;
+ VOID *Registration;
+
+ if (!IsRealm ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ mImageHandle = ImageHandle;
+
+ //
+ // Fill information
+ //
+ mCcaDxeData.BsCap.Size = sizeof (EFI_CC_BOOT_SERVICE_CAPABILITY);
+ mCcaDxeData.BsCap.ProtocolVersion.Major = 1;
+ mCcaDxeData.BsCap.ProtocolVersion.Minor = 0;
+ mCcaDxeData.BsCap.StructureVersion.Major = 1;
+ mCcaDxeData.BsCap.StructureVersion.Minor = 0;
+
+ //
+ // Get supported PCR and current Active PCRs
+ //
+ mCcaDxeData.BsCap.HashAlgorithmBitmap = HASH_ALG_SHA256;
+
+ // Realm guest only supports EFI_TCG2_EVENT_LOG_FORMAT_TCG_2
+ mCcaDxeData.BsCap.SupportedEventLogs = EFI_CC_EVENT_LOG_FORMAT_TCG_2;
+
+ //
+ // Setup the log area and copy event log from hob list to it
+ //
+ Status = SetupCcEventLog ();
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Measure handoff tables, Boot#### variables etc.
+ //
+ Status = EfiCreateEventReadyToBootEx (
+ TPL_CALLBACK,
+ OnReadyToBoot,
+ NULL,
+ &Event
+ );
+
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ OnExitBootServices,
+ NULL,
+ &gEfiEventExitBootServicesGuid,
+ &Event
+ );
+
+ //
+ // Measure Exit Boot Service failed
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ OnExitBootServicesFailed,
+ NULL,
+ &gEventExitBootServicesFailedGuid,
+ &Event
+ );
+
+ //
+ // Create event callback, because we need access variable on SecureBootPolicyVariable
+ // We should use VariableWriteArch instead of VariableArch, because Variable driver
+ // may update SecureBoot value based on last setting.
+ //
+ EfiCreateProtocolNotifyEvent (&gEfiVariableWriteArchProtocolGuid, TPL_CALLBACK, MeasureSecureBootPolicy, NULL, &Registration);
+
+ //
+ // Install CcMeasurementProtocol
+ //
+ Status = InstallCcMeasurementProtocol ();
+ DEBUG ((DEBUG_INFO, "InstallCcMeasurementProtocol - %r\n", Status));
+
+ if (Status == EFI_SUCCESS) {
+ //
+ // Create event callback to install CC EventLog ACPI Table
+ EfiCreateProtocolNotifyEvent (&gEfiAcpiTableProtocolGuid, TPL_CALLBACK, InstallAcpiTable, NULL, &Registration);
+ } else {
+ //
+ // Cc measurement feature is crucial to a Realm and it shall stop running immediately
+ // when it is failed to be installed.
+ DEBUG ((DEBUG_ERROR, "%a: CcMeasurement protocol failed to be installed - %r\n", __func__, Status));
+ CpuDeadLoop ();
+ }
+
+ return Status;
+}
\ No newline at end of file
diff --git a/ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.inf b/ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.inf
new file mode 100644
index 0000000000000000000000000000000000000000..728ca951ca94d261ed24096cfdc804964a95a332
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.inf
@@ -0,0 +1,82 @@
+## @file
+#
+# Produces EFI_CC_MEASUREMENT_PROTOCOL and measure boot environment
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = CcaTcg2Dxe
+ FILE_GUID = 1788737c-83c4-47b9-899c-b741ab263964
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = DriverEntry
+
+[Sources]
+ CcaTcg2Dxe.c
+ MeasureBootPeCoff.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+
+[LibraryClasses]
+ MemoryAllocationLib
+ BaseLib
+ UefiBootServicesTableLib
+ HobLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ BaseMemoryLib
+ DebugLib
+ PrintLib
+ UefiLib
+ HashLib
+ PerformanceLib
+ PeCoffLib
+ ArmCcaLib
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot"
+ ## SOMETIMES_CONSUMES ## Variable:L"PK"
+ ## SOMETIMES_CONSUMES ## Variable:L"KEK"
+ ## SOMETIMES_CONSUMES ## Variable:L"BootXXXX"
+ gEfiGlobalVariableGuid
+
+ ## SOMETIMES_CONSUMES ## Variable:L"db"
+ ## SOMETIMES_CONSUMES ## Variable:L"dbx"
+ gEfiImageSecurityDatabaseGuid
+
+ # gTcgEventEntryHobGuid ## SOMETIMES_CONSUMES ## HOB
+ gEfiEventExitBootServicesGuid ## CONSUMES ## Event
+ gEventExitBootServicesFailedGuid ## SOMETIMES_CONSUMES ## Event
+
+ gCcEventEntryHobGuid ## SOMETIMES_CONSUMES ## HOB
+ gTcg800155PlatformIdEventHobGuid ## SOMETIMES_CONSUMES ## HOB
+ gEfiCcFinalEventsTableGuid ## PRODUCES
+
+[Protocols]
+ gEfiCcMeasurementProtocolGuid ## PRODUCES
+ gEfiVariableWriteArchProtocolGuid ## NOTIFY
+ gEfiAcpiTableProtocolGuid ## NOTIFY
+
+[Pcd]
+ gEfiSecurityPkgTokenSpaceGuid.PcdTpmPlatformClass ## SOMETIMES_CONSUMES
+ gEfiSecurityPkgTokenSpaceGuid.PcdFirmwareDebuggerInitialized ## SOMETIMES_CONSUMES
+ # gEfiSecurityPkgTokenSpaceGuid.PcdStatusCodeSubClassTpmDevice ## SOMETIMES_CONSUMES
+ # gEfiSecurityPkgTokenSpaceGuid.PcdTcg2HashAlgorithmBitmap ## CONSUMES
+ # gEfiSecurityPkgTokenSpaceGuid.PcdTcg2NumberOfPCRBanks ## CONSUMES
+ gEfiSecurityPkgTokenSpaceGuid.PcdTcgLogAreaMinLen ## CONSUMES
+ gEfiSecurityPkgTokenSpaceGuid.PcdTcg2FinalLogAreaLen ## CONSUMES
+ gEfiSecurityPkgTokenSpaceGuid.PcdCcEventlogAcpiTableLaml ## PRODUCES
+ gEfiSecurityPkgTokenSpaceGuid.PcdCcEventlogAcpiTableLasa ## PRODUCES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision
+
+[Depex]
+ gEfiVariableArchProtocolGuid AND gEfiAcpiTableProtocolGuid
\ No newline at end of file
diff --git a/ArmVirtPkg/ArmCcaTcg2Dxe/MeasureBootPeCoff.c b/ArmVirtPkg/ArmCcaTcg2Dxe/MeasureBootPeCoff.c
new file mode 100644
index 0000000000000000000000000000000000000000..af2d6f98c94ffe21b767c6b48031303d474105eb
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaTcg2Dxe/MeasureBootPeCoff.c
@@ -0,0 +1,407 @@
+/** @file
+ This module implements measuring PeCoff image for Tcg2 Protocol.
+
+ Caution: This file requires additional review when modified.
+ This driver will have external input - PE/COFF image.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+UINTN mTcg2DxeImageSize = 0;
+
+/**
+ Reads contents of a PE/COFF image in memory buffer.
+
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this function will make sure the PE/COFF image content
+ read is within the image buffer.
+
+ @param FileHandle Pointer to the file handle to read the PE/COFF image.
+ @param FileOffset Offset into the PE/COFF image to begin the read operation.
+ @param ReadSize On input, the size in bytes of the requested read operation.
+ On output, the number of bytes actually read.
+ @param Buffer Output buffer that contains the data read from the PE/COFF image.
+
+ @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size
+**/
+EFI_STATUS
+EFIAPI
+Tcg2DxeImageRead (
+ IN VOID *FileHandle,
+ IN UINTN FileOffset,
+ IN OUT UINTN *ReadSize,
+ OUT VOID *Buffer
+ )
+{
+ UINTN EndPosition;
+
+ if ((FileHandle == NULL) || (ReadSize == NULL) || (Buffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (MAX_ADDRESS - FileOffset < *ReadSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EndPosition = FileOffset + *ReadSize;
+ if (EndPosition > mTcg2DxeImageSize) {
+ *ReadSize = (UINT32)(mTcg2DxeImageSize - FileOffset);
+ }
+
+ if (FileOffset >= mTcg2DxeImageSize) {
+ *ReadSize = 0;
+ }
+
+ CopyMem (Buffer, (UINT8 *)((UINTN)FileHandle + FileOffset), *ReadSize);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Measure PE image into TPM log based on the authenticode image hashing in
+ PE/COFF Specification 8.0 Appendix A.
+
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this function will validate its data structure
+ within this image buffer before use.
+
+ Notes: PE/COFF image is checked by BasePeCoffLib PeCoffLoaderGetImageInfo().
+
+ @param[in] RemIndex Rem index
+ @param[in] ImageAddress Start address of image buffer.
+ @param[in] ImageSize Image size
+ @param[out] DigestList Digest list of this image.
+
+ @retval EFI_SUCCESS Successfully measure image.
+ @retval EFI_OUT_OF_RESOURCES No enough resource to measure image.
+ @retval other error value
+**/
+EFI_STATUS
+MeasurePeImageAndExtend (
+ IN UINT32 RemIndex,
+ IN EFI_PHYSICAL_ADDRESS ImageAddress,
+ IN UINTN ImageSize,
+ OUT TPML_DIGEST_VALUES *DigestList
+ )
+{
+ EFI_STATUS Status;
+ EFI_IMAGE_DOS_HEADER *DosHdr;
+ UINT32 PeCoffHeaderOffset;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ UINT8 *HashBase;
+ UINTN HashSize;
+ UINTN SumOfBytesHashed;
+ EFI_IMAGE_SECTION_HEADER *SectionHeader;
+ UINTN Index;
+ UINTN Pos;
+ EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
+ UINT32 NumberOfRvaAndSizes;
+ UINT32 CertSize;
+ HASH_HANDLE HashHandle;
+ PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
+
+ HashHandle = 0xFFFFFFFF; // Know bad value
+
+ Status = EFI_UNSUPPORTED;
+ SectionHeader = NULL;
+
+ //
+ // Check PE/COFF image
+ //
+ ZeroMem (&ImageContext, sizeof (ImageContext));
+ ImageContext.Handle = (VOID *)(UINTN)ImageAddress;
+ mTcg2DxeImageSize = ImageSize;
+ ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)Tcg2DxeImageRead;
+
+ //
+ // Get information about the image being loaded
+ //
+ Status = PeCoffLoaderGetImageInfo (&ImageContext);
+ if (EFI_ERROR (Status)) {
+ //
+ // The information can't be got from the invalid PeImage
+ //
+ DEBUG ((DEBUG_INFO, "Tcg2Dxe: PeImage invalid. Cannot retrieve image information.\n"));
+ goto Finish;
+ }
+
+ DosHdr = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;
+ PeCoffHeaderOffset = 0;
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
+ PeCoffHeaderOffset = DosHdr->e_lfanew;
+ }
+
+ Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);
+ if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
+ Status = EFI_UNSUPPORTED;
+ goto Finish;
+ }
+
+ //
+ // PE/COFF Image Measurement
+ //
+ // NOTE: The following codes/steps are based upon the authenticode image hashing in
+ // PE/COFF Specification 8.0 Appendix A.
+ //
+ //
+
+ // 1. Load the image header into memory.
+
+ // 2. Initialize a SHA hash context.
+
+ Status = HashStart (&HashHandle);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+
+ //
+ // Measuring PE/COFF Image Header;
+ // But CheckSum field and SECURITY data directory (certificate) are excluded
+ //
+
+ //
+ // 3. Calculate the distance from the base of the image header to the image checksum address.
+ // 4. Hash the image header from its base to beginning of the image checksum.
+ //
+ HashBase = (UINT8 *)(UINTN)ImageAddress;
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use PE32 offset
+ //
+ NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
+ HashSize = (UINTN)(&Hdr.Pe32->OptionalHeader.CheckSum) - (UINTN)HashBase;
+ } else {
+ //
+ // Use PE32+ offset
+ //
+ NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
+ HashSize = (UINTN)(&Hdr.Pe32Plus->OptionalHeader.CheckSum) - (UINTN)HashBase;
+ }
+
+ Status = HashUpdate (HashHandle, HashBase, HashSize);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+
+ //
+ // 5. Skip over the image checksum (it occupies a single ULONG).
+ //
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
+ //
+ // 6. Since there is no Cert Directory in optional header, hash everything
+ // from the end of the checksum to the end of image header.
+ //
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use PE32 offset.
+ //
+ HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
+ HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);
+ } else {
+ //
+ // Use PE32+ offset.
+ //
+ HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
+ HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);
+ }
+
+ if (HashSize != 0) {
+ Status = HashUpdate (HashHandle, HashBase, HashSize);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+ }
+ } else {
+ //
+ // 7. Hash everything from the end of the checksum to the start of the Cert Directory.
+ //
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use PE32 offset
+ //
+ HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32);
+ HashSize = (UINTN)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase;
+ } else {
+ //
+ // Use PE32+ offset
+ //
+ HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32);
+ HashSize = (UINTN)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase;
+ }
+
+ if (HashSize != 0) {
+ Status = HashUpdate (HashHandle, HashBase, HashSize);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+ }
+
+ //
+ // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.)
+ // 9. Hash everything from the end of the Cert Directory to the end of image header.
+ //
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use PE32 offset
+ //
+ HashBase = (UINT8 *)&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
+ HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);
+ } else {
+ //
+ // Use PE32+ offset
+ //
+ HashBase = (UINT8 *)&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1];
+ HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN)(HashBase - ImageAddress);
+ }
+
+ if (HashSize != 0) {
+ Status = HashUpdate (HashHandle, HashBase, HashSize);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+ }
+ }
+
+ //
+ // 10. Set the SUM_OF_BYTES_HASHED to the size of the header
+ //
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use PE32 offset
+ //
+ SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
+ } else {
+ //
+ // Use PE32+ offset
+ //
+ SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;
+ }
+
+ //
+ // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER
+ // structures in the image. The 'NumberOfSections' field of the image
+ // header indicates how big the table should be. Do not include any
+ // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero.
+ //
+ SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections);
+ if (SectionHeader == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Finish;
+ }
+
+ //
+ // 12. Using the 'PointerToRawData' in the referenced section headers as
+ // a key, arrange the elements in the table in ascending order. In other
+ // words, sort the section headers according to the disk-file offset of
+ // the section.
+ //
+ Section = (EFI_IMAGE_SECTION_HEADER *)(
+ (UINT8 *)(UINTN)ImageAddress +
+ PeCoffHeaderOffset +
+ sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER) +
+ Hdr.Pe32->FileHeader.SizeOfOptionalHeader
+ );
+ for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
+ Pos = Index;
+ while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) {
+ CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
+ Pos--;
+ }
+
+ CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
+ Section += 1;
+ }
+
+ //
+ // 13. Walk through the sorted table, bring the corresponding section
+ // into memory, and hash the entire section (using the 'SizeOfRawData'
+ // field in the section header to determine the amount of data to hash).
+ // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED .
+ // 15. Repeat steps 13 and 14 for all the sections in the sorted table.
+ //
+ for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
+ Section = (EFI_IMAGE_SECTION_HEADER *)&SectionHeader[Index];
+ if (Section->SizeOfRawData == 0) {
+ continue;
+ }
+
+ HashBase = (UINT8 *)(UINTN)ImageAddress + Section->PointerToRawData;
+ HashSize = (UINTN)Section->SizeOfRawData;
+
+ Status = HashUpdate (HashHandle, HashBase, HashSize);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+
+ SumOfBytesHashed += HashSize;
+ }
+
+ //
+ // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra
+ // data in the file that needs to be added to the hash. This data begins
+ // at file offset SUM_OF_BYTES_HASHED and its length is:
+ // FileSize - (CertDirectory->Size)
+ //
+ if (ImageSize > SumOfBytesHashed) {
+ HashBase = (UINT8 *)(UINTN)ImageAddress + SumOfBytesHashed;
+
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
+ CertSize = 0;
+ } else {
+ if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // Use PE32 offset.
+ //
+ CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
+ } else {
+ //
+ // Use PE32+ offset.
+ //
+ CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
+ }
+ }
+
+ if (ImageSize > CertSize + SumOfBytesHashed) {
+ HashSize = (UINTN)(ImageSize - CertSize - SumOfBytesHashed);
+
+ Status = HashUpdate (HashHandle, HashBase, HashSize);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+ } else if (ImageSize < CertSize + SumOfBytesHashed) {
+ Status = EFI_UNSUPPORTED;
+ goto Finish;
+ }
+ }
+
+ //
+ // 17. Finalize the SHA hash.
+ //
+ Status = HashCompleteAndExtend (HashHandle, RemIndex, NULL, 0, DigestList);
+ if (EFI_ERROR (Status)) {
+ goto Finish;
+ }
+
+Finish:
+ if (SectionHeader != NULL) {
+ FreePool (SectionHeader);
+ }
+
+ return Status;
+}
diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
index b5df14c720a7bde43d54a90e987409e349c0605b..58765e939ca6269422a5eb49a79df59676e5579e 100644
--- a/ArmVirtPkg/ArmVirtPkg.dec
+++ b/ArmVirtPkg/ArmVirtPkg.dec
@@ -26,6 +26,8 @@
Include # Root include for the package
[LibraryClasses]
+ ArmCcaLib|Include/Library/ArmCcaLib.h
+ ArmCcaRsiLib|Include/Library/ArmCcaRsiLib.h
ArmVirtMemInfoLib|Include/Library/ArmVirtMemInfoLib.h
[Guids.common]
@@ -33,7 +35,9 @@
gEarlyPL011BaseAddressGuid = { 0xB199DEA9, 0xFD5C, 0x4A84, { 0x80, 0x82, 0x2F, 0x41, 0x70, 0x78, 0x03, 0x05 } }
gEarly16550UartBaseAddressGuid = { 0xea67ca3e, 0x1f54, 0x436b, { 0x97, 0x88, 0xd4, 0xeb, 0x29, 0xc3, 0x42, 0x67 } }
gArmVirtSystemMemorySizeGuid = { 0x504eccb9, 0x1bf0, 0x4420, { 0x86, 0x5d, 0xdc, 0x66, 0x06, 0xd4, 0x13, 0xbf } }
-
+ gArmCcaIpaWidthGuid = { 0xbdb66787, 0xfc8a, 0x412e, { 0xa0, 0x9b, 0x84, 0x96, 0x61, 0x81, 0x72, 0xc0 } }
+[Protocols]
+ gEfiRealmApertureManagementProtocolGuid = { 0x585c00be, 0xcf7c, 0x4db8, { 0x8a, 0xa2, 0x49, 0xd, 0x67, 0xf5, 0xf6, 0xe6 } }
[PcdsFeatureFlag]
#
# Feature Flag PCD that defines whether TPM2 support is enabled
diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index ae342bcc9339c0b94f72c8da841ffa6b07f8d968..0d1e4345ce96ea8ded26324e03d19730017cc5be 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -32,6 +32,7 @@
DEFINE TPM2_ENABLE = FALSE
DEFINE TPM2_CONFIG_ENABLE = FALSE
DEFINE CAVIUM_ERRATUM_27456 = FALSE
+ DEFINE CC_MEASUREMENT_ENABLE = FALSE
#
# Network definition
@@ -85,21 +86,33 @@
!if $(TPM2_ENABLE) == TRUE
Tpm2CommandLib|SecurityPkg/Library/Tpm2CommandLib/Tpm2CommandLib.inf
Tcg2PhysicalPresenceLib|OvmfPkg/Library/Tcg2PhysicalPresenceLibQemu/DxeTcg2PhysicalPresenceLib.inf
- TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf
TpmPlatformHierarchyLib|SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLib/PeiDxeTpmPlatformHierarchyLib.inf
!else
- TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
TpmPlatformHierarchyLib|SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf
!endif
+!if $(TPM2_ENABLE) == TRUE || $(CC_MEASUREMENT_ENABLE) == TRUE
+ #
+ # DxeTpmMeasurementLib supports measurement functions for both TPM and Confidential Computing.
+ # It should be controlled by TPM2_ENABLE and CC_MEASUREMENT_ENABLE.
+ #
+ TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf
+!else
+ TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+!endif
+
[LibraryClasses.AARCH64]
ArmPlatformLib|ArmVirtPkg/Library/ArmPlatformLibQemu/ArmPlatformLibQemu.inf
+ ArmCcaLib|ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf
+ ArmCcaRsiLib|ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf
+ HashLib|ArmVirtPkg/Library/HashLibCca/HashLibCca.inf
[LibraryClasses.ARM]
ArmPlatformLib|ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.inf
[LibraryClasses.common.PEIM]
ArmVirtMemInfoLib|ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf
+ ArmCcaInitPeiLib|ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf
!if $(TPM2_ENABLE) == TRUE
BaseCryptLib|CryptoPkg/Library/BaseCryptLib/PeiCryptLib.inf
@@ -254,6 +267,10 @@
gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum|0x0
gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum|0x0
+ # Define PCD for emulating runtime variable storage when CFI flash is absent
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable|FALSE
+
+
#
# ARM General Interrupt Controller
#
@@ -391,18 +408,23 @@
# don't use unaligned CopyMem () on the UEFI varstore NOR flash region
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
}
-!if $(SECURE_BOOT_ENABLE) == TRUE
MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf {
+!if $(SECURE_BOOT_ENABLE) == TRUE
NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf
-!if $(TPM2_ENABLE) == TRUE
+!endif
+!if $(TPM2_ENABLE) == TRUE || $(CC_MEASUREMENT_ENABLE) == TRUE
+ #
+ # DxeTpm2MeasureBootLib provides security service of TPM2 measure boot and
+ # Confidential Computing (CC) measure boot. It should be controlled by
+ # TPM2_ENABLE and CC_MEASUREMENT_ENABLE
+ #
NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf
!endif
}
+!if $(SECURE_BOOT_ENABLE) == TRUE
SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf
OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf
-!else
- MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
!endif
MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
@@ -442,6 +464,10 @@
#
# Platform Driver
#
+ ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.inf {
+
+ NULL|ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf
+ }
OvmfPkg/Fdt/VirtioFdtDxe/VirtioFdtDxe.inf
EmbeddedPkg/Drivers/FdtClientDxe/FdtClientDxe.inf
OvmfPkg/Fdt/HighMemDxe/HighMemDxe.inf
@@ -585,3 +611,23 @@
NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
}
+ #
+ # Realm Aperture Management
+ #
+ ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf
+
+ #
+ # IoMMU support for Arm CCA
+ #
+ ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
+
+ #
+ # Cc Measurement Protocol for Cca guest
+ #
+!if $(CC_MEASUREMENT_ENABLE) == TRUE
+ ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.inf {
+
+ HashLib|ArmVirtPkg/Library/HashLibCca/HashLibCca.inf
+ NULL|SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.inf
+ }
+!endif
\ No newline at end of file
diff --git a/ArmVirtPkg/ArmVirtQemu.fdf b/ArmVirtPkg/ArmVirtQemu.fdf
index 0831906f8f6f1c06b183dd9ac331fedced85a018..1cc3f4e19b14c0fc737c8212c0d3612bb4c34f30 100644
--- a/ArmVirtPkg/ArmVirtQemu.fdf
+++ b/ArmVirtPkg/ArmVirtQemu.fdf
@@ -111,6 +111,10 @@ READ_LOCK_STATUS = TRUE
INF ArmPkg/Drivers/CpuPei/CpuPei.inf
INF MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+!if $(CC_MEASUREMENT_ENABLE) == TRUE
+ INF ArmVirtPkg/ArmCcaTcg2Dxe/CcaTcg2Dxe.inf
+!endif
+
!if $(TPM2_ENABLE) == TRUE
INF MdeModulePkg/Universal/PCD/Pei/Pcd.inf
INF MdeModulePkg/Universal/ResetSystemPei/ResetSystemPei.inf
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index e5d7f8d4c7d2919ea4308b3b3c0ec5daf64e0452..85009798d27afc4f4f8bd4a5d33fa0059b187654 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -42,6 +42,7 @@ READ_LOCK_STATUS = TRUE
INF MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
INF OvmfPkg/Fdt/VirtioFdtDxe/VirtioFdtDxe.inf
INF EmbeddedPkg/Drivers/FdtClientDxe/FdtClientDxe.inf
+ INF ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.inf
INF OvmfPkg/Fdt/HighMemDxe/HighMemDxe.inf
#
@@ -147,6 +148,16 @@ READ_LOCK_STATUS = TRUE
INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
INF MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
INF OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
+
+ #
+ # Realm Aperture Management
+ #
+ INF ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf
+
+ #
+ # IoMMU support for Arm CCA
+ #
+ INF ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
!endif
#
diff --git a/ArmVirtPkg/Include/Library/ArmCcaInitPeiLib.h b/ArmVirtPkg/Include/Library/ArmCcaInitPeiLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..9374fe81590d2240d7b1fe176b7ab4792df62564
--- /dev/null
+++ b/ArmVirtPkg/Include/Library/ArmCcaInitPeiLib.h
@@ -0,0 +1,49 @@
+/** @file
+ Library that implements the Arm CCA helper functions.
+
+ Copyright (c) 2022 2023, Arm Ltd. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+**/
+
+#ifndef ARM_CCA_INIT_PEI_LIB_
+#define ARM_CCA_INIT_PEI_LIB_
+
+#include
+
+/**
+ Configure the System Memory region as Protected RAM.
+
+ When a VMM creates a Realm, a small amount of DRAM (which contains the
+ firmware image) and the initial content is configured as Protected RAM.
+ The remaining System Memory is in the Protected Empty state. The firmware
+ must then initialise the remaining System Memory as Protected RAM before
+ it can be accessed.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The execution context is not in a Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureSystemMemory (
+ VOID
+ );
+
+/**
+ Perform Arm CCA specific initialisations.
+
+ @retval RETURN_SUCCESS Success or execution context is not a Realm.
+ @retval RETURN_OUT_OF_RESOURCES Out of resources.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaInitialize (
+ VOID
+ );
+
+#endif // ARM_CCA_LIB_
diff --git a/ArmVirtPkg/Include/Library/ArmCcaLib.h b/ArmVirtPkg/Include/Library/ArmCcaLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..322c237d74515c2993b76601eaebe2d591a67f2e
--- /dev/null
+++ b/ArmVirtPkg/Include/Library/ArmCcaLib.h
@@ -0,0 +1,80 @@
+/** @file
+ Library that implements the Arm CCA helper functions.
+
+ Copyright (c) 2022 - 2023, Arm Ltd. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+**/
+
+#ifndef ARM_CCA_LIB_
+#define ARM_CCA_LIB_
+
+#include
+#include
+
+/**
+ Check if running in a Realm.
+
+ @retval TRUE The execution is within the context of a Realm.
+ @retval FALSE The execution is not within the context of a Realm.
+**/
+BOOLEAN
+EFIAPI
+IsRealm (
+ VOID
+ );
+
+/**
+ Configure the protection attribute for the page tables
+ describing the memory region.
+
+ The IPA space of a Realm is divided into two halves:
+ - Protected IPA space and
+ - Unprotected IPA space.
+
+ Software in a Realm should treat the most significant bit of an
+ IPA as a protection attribute.
+
+ A Protected IPA is an address in the lower half of a Realms IPA
+ space. The most significant bit of a Protected IPA is 0.
+
+ An Unprotected IPA is an address in the upper half of a Realms
+ IPA space. The most significant bit of an Unprotected IPA is 1.
+
+ Note:
+ - Configuring the memory region as Unprotected IPA enables the
+ Realm to share the memory region with the Host.
+ - This function updates the page table entries to reflect the
+ protection attribute.
+ - A separate call to transition the memory range using the Realm
+ Service Interface (RSI) RSI_IPA_STATE_SET command is additionally
+ required and is expected to be done outside this function.
+
+ @param [in] BaseAddress Base address of the memory region.
+ @param [in] Length Length of the memory region.
+ @param [in] IpaWidth IPA width of the Realm.
+ @param [in] Share If TRUE, set the most significant
+ bit of the IPA to configure the memory
+ region as Unprotected IPA.
+ If FALSE, clear the most significant
+ bit of the IPA to configure the memory
+ region as Protected IPA.
+
+ @retval RETURN_SUCCESS IPA protection attribute updated.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The request is not initiated in a
+ Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaSetMemoryProtectAttribute (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 IpaWidth,
+ IN BOOLEAN Share
+ );
+
+#endif // ARM_CCA_LIB_
diff --git a/ArmVirtPkg/Include/Library/ArmCcaRsiLib.h b/ArmVirtPkg/Include/Library/ArmCcaRsiLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..746c5e3832c0595638055755753eb32fb763874a
--- /dev/null
+++ b/ArmVirtPkg/Include/Library/ArmCcaRsiLib.h
@@ -0,0 +1,84 @@
+/** @file
+ Library that implements the Arm CCA Realm Service Interface calls.
+
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+ - RIM - Realm Initial Measurement
+ - REM - Realm Extensible Measurement
+
+ @par Reference(s):
+ - Realm Management Monitor (RMM) Specification, version 1.0-eac5
+ (https://developer.arm.com/documentation/den0137/)
+**/
+
+#ifndef ARM_CCA_RSI_LIB_
+#define ARM_CCA_RSI_LIB_
+
+#include
+
+/**
+ A macro defining the size of a Realm Granule.
+ See Section A2.2, RMM Specification, version A-bet0
+ DNBXXX A Granule is a unit of physical memory whose size is 4KB.
+*/
+#define REALM_GRANULE_SIZE SIZE_4KB
+
+/* Maximum measurement data size in bytes.
+ See Section C1.11 RmmRealmMeasurement type, RMM Specification, version A-bet0
+ The width of the RmmRealmMeasurement type is 512 bits.
+*/
+#define MAX_MEASUREMENT_DATA_SIZE_BYTES 64
+
+/* Minimum and Maximum indices for REMs
+ See Section A2.1.3 Realm attributes, RMM Specification, version A-bet0
+ IFMPYL - Attributes of a Realm include an array of measurement values. The
+ first entry in this array is a RIM. The remaining entries in this array are
+ REMs.
+*/
+#define MIN_REM_INDEX 1
+#define MAX_REM_INDEX 4
+
+/**
+ Extends a measurement to a REM.
+
+ @param [in] MeasurementIndex Index of the REM.
+ @param [in] Measurement Pointer to the measurement buffer.
+ @param [in] MeasurementSize Size of the measurement data.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiExtendMeasurement (
+ IN UINTN MeasurementIndex,
+ IN CONST UINT8 *CONST Measurement,
+ IN UINTN MeasurementSize
+ );
+
+/**
+ Get the version of the RSI implementation.
+
+ @param [out] UefiImpl The version of the RSI specification
+ implemented by the UEFI firmware.
+ @param [out] RmmImpl The version of the RSI specification
+ implemented by the RMM.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_UNSUPPORTED The execution context is not a Realm.
+ @retval RETURN_INCOMPATIBLE_VERSION The Firmware and RMM specification
+ revisions are not compatible.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetVersion (
+ OUT UINT32 *CONST UefiImpl,
+ OUT UINT32 *CONST RmmImpl
+ );
+
+#endif // ARM_CCA_RSI_LIB_
diff --git a/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h b/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h
index 5e640912c6271d938b13b56a0f1bd9244a1fcc18..58b13b774a2d2336a7f43990f557f3da9f46c691 100644
--- a/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h
+++ b/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h
@@ -32,4 +32,20 @@ ArmVirtGetMemoryMap (
OUT ARM_MEMORY_REGION_DESCRIPTOR **VirtualMemoryMap
);
+/**
+ Configure the MMIO regions as shared with the VMM.
+
+ Set the protection attribute for the MMIO regions as Unprotected IPA.
+
+ @param[in] IpaWidth IPA width of the Realm.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The execution context is not in a Realm.
+**/
+EFI_STATUS
+EFIAPI
+ArmCcaConfigureMmio (
+ IN UINT64 IpaWidth
+ );
#endif
diff --git a/ArmVirtPkg/Include/Protocol/RealmApertureManagementProtocol.h b/ArmVirtPkg/Include/Protocol/RealmApertureManagementProtocol.h
new file mode 100644
index 0000000000000000000000000000000000000000..a3f200befa1620a11dce1f3c8b4a9f82a7ef9a5a
--- /dev/null
+++ b/ArmVirtPkg/Include/Protocol/RealmApertureManagementProtocol.h
@@ -0,0 +1,103 @@
+/** @file
+ Realm Aperture Management Protocol (RAMP)
+ On Arm CCA Systems the Realm protects access and visibility of Guest memory
+ and code execution from software outside the realm.
+
+ However, software executing in a Realm needs to interact with the external
+ world. This may be done using virtualised disk, network interfaces, etc.
+ The drivers for these virtualised devices need to share buffers with the host
+ OS to exchange information/data.
+
+ Since the Guest memory is protected by the Realm, the host cannot access these
+ buffers unless the IPA state of the buffers is changed to Protected EMPTY by
+ the software executing in the Realm.
+
+ By enabling the sharing of the buffers, we are essentially opening an
+ aperture so that the host OS can access the range of pages that are shared.
+
+ The virtual firmware (Guest firmware) needs a mechanism to manage the sharing
+ of buffers. The Realm Aperture Management Protocol provides an interface that
+ UEFI drivers/modules can use to enable/disable the sharing of buffers with the
+ Host. The protocol also tracks open apertures and ensures they are shut on
+ ExitBootServices.
+
+ Copyright (c) 2022 - 2023, ARM Ltd. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - RAMP - Realm Aperture Management Protocol
+**/
+
+#ifndef REALM_APERTURE_MANAGEMENT_PROTOCOL_H_
+#define REALM_APERTURE_MANAGEMENT_PROTOCOL_H_
+
+/** This macro defines the Realm Aperture Management Protocol GUID.
+
+ GUID: {585C00BE-CF7C-4DB8-8AA2-490D67F5F6E6}
+*/
+#define EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_GUID \
+ { 0x585c00be, 0xcf7c, 0x4db8, \
+ { 0x8a, 0xa2, 0x49, 0xd, 0x67, 0xf5, 0xf6, 0xe6 } \
+ };
+
+/** This macro defines the Realm Aperture Management Protocol Revision.
+*/
+#define EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_REVISION 0x00010000
+
+#pragma pack(1)
+
+/** Enables sharing of the memory buffers with the host.
+
+ @param [in] Memory Pointer to the page start address.
+ @param [in] Pages Number of pages to share.
+ @param [out] ApertureReference Reference to the opened aperture.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_ACCESS_DENIED Aperture already open over memory region.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_OPEN_APERTURE)(
+ IN CONST EFI_PHYSICAL_ADDRESS Memory,
+ IN CONST UINTN Pages,
+ OUT EFI_HANDLE *CONST ApertureReference
+ );
+
+/** Disables the sharing of the buffers.
+
+ @param [in] ApertureReference Reference to the aperture for closing.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required buffer information is not found.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_CLOSE_APERTURE)(
+ IN CONST EFI_HANDLE ApertureReference
+ );
+
+/** A structure describing the interface provided by the Realm Aperture
+ Management Protocol.
+*/
+typedef struct RealmApertureManagementProtocol {
+ /// The Realm Aperture Management Protocol revision.
+ UINT64 Revision;
+
+ /// Shares Realm Pages(s) with the Host.
+ EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_OPEN_APERTURE OpenAperture;
+
+ /// Makes the Realm Pages(s) private to the Realm.
+ EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_CLOSE_APERTURE CloseAperture;
+} EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL;
+
+/** The Realm Aperture Management Protocol GUID.
+*/
+extern EFI_GUID gEfiRealmApertureManagementProtocolGuid;
+
+#pragma pack()
+
+#endif // REALM_APERTURE_MANAGEMENT_PROTOCOL_H_
diff --git a/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.c b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.c
new file mode 100644
index 0000000000000000000000000000000000000000..dc3f55a83e4c7436af74af4c21c021aae63609d6
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.c
@@ -0,0 +1,60 @@
+/** @file
+ Library that implements the Arm CCA initialisation in PEI phase.
+
+ Copyright (c) 2022 2023, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+**/
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ Configure the System Memory region as Protected RAM.
+
+ When a VMM creates a Realm, a small amount of DRAM (which contains the
+ firmware image) and the initial content is configured as Protected RAM.
+ The remaining System Memory is in the Protected Empty state. The firmware
+ must then initialise the remaining System Memory as Protected RAM before
+ it can be accessed.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The execution context is not in a Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureSystemMemory (
+ VOID
+ )
+{
+ return RETURN_SUCCESS;
+}
+
+/**
+ Perform Arm CCA specific initialisations.
+
+ @retval RETURN_SUCCESS Success or execution context is not a Realm.
+ @retval RETURN_OUT_OF_RESOURCES Out of resources.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaInitialize (
+ VOID
+ )
+{
+ return ArmCcaConfigureMmio (48);
+}
diff --git a/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf
new file mode 100644
index 0000000000000000000000000000000000000000..b3c17a6ab00f74eba47c8b3793bfbda053438b18
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf
@@ -0,0 +1,38 @@
+## @file
+# Library that implements the Arm CCA initialisation in PEI phase.
+#
+# Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = ArmCcaInitPeiLib
+ FILE_GUID = 9A8C3768-79ED-487E-8155-BBF4DD638296
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmCcaInitPeiLib
+
+[Sources]
+ ArmCcaInitPeiLib.c
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmCcaLib
+ ArmLib
+ ArmMmuLib
+ ArmVirtMemInfoLib
+ BaseLib
+
+[Pcd]
+ gArmTokenSpaceGuid.PcdSystemMemoryBase
+ gArmTokenSpaceGuid.PcdSystemMemorySize
+
+[Guids]
+ gArmCcaIpaWidthGuid
diff --git a/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.c b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.c
new file mode 100644
index 0000000000000000000000000000000000000000..561a4148c7744f5ed34b52ad69f857948e9ec284
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.c
@@ -0,0 +1,132 @@
+/** @file
+ Library that implements the Arm CCA helper functions.
+
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+**/
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+/**
+ Check if running in a Realm.
+
+ @retval TRUE The execution is within the context of a Realm.
+ @retval FALSE The execution is not within the context of a Realm.
+**/
+BOOLEAN
+EFIAPI
+IsRealm (
+ VOID
+ )
+{
+ RETURN_STATUS Status;
+ UINT32 UefiImpl;
+ UINT32 RmmImpl;
+
+ Status = RsiGetVersion (
+ &UefiImpl,
+ &RmmImpl
+ );
+ if (!RETURN_ERROR (Status)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Configure the protection attribute for the page tables
+ describing the memory region.
+
+ The IPA space of a Realm is divided into two halves:
+ - Protected IPA space and
+ - Unprotected IPA space.
+
+ Software in a Realm should treat the most significant bit of an
+ IPA as a protection attribute.
+
+ A Protected IPA is an address in the lower half of a Realms IPA
+ space. The most significant bit of a Protected IPA is 0.
+
+ An Unprotected IPA is an address in the upper half of a Realms
+ IPA space. The most significant bit of an Unprotected IPA is 1.
+
+ Note:
+ - Configuring the memory region as Unprotected IPA enables the
+ Realm to share the memory region with the Host.
+ - This function updates the page table entries to reflect the
+ protection attribute.
+ - A separate call to transition the memory range using the Realm
+ Service Interface (RSI) RSI_IPA_STATE_SET command is additionally
+ required and is expected to be done outside this function.
+
+ @param [in] BaseAddress Base address of the memory region.
+ @param [in] Length Length of the memory region.
+ @param [in] IpaWidth IPA width of the Realm.
+ @param [in] Share If TRUE, set the most significant
+ bit of the IPA to configure the memory
+ region as Unprotected IPA.
+ If FALSE, clear the most significant
+ bit of the IPA to configure the memory
+ region as Protected IPA.
+
+ @retval RETURN_SUCCESS IPA protection attribute updated.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The request is not initiated in a
+ Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaSetMemoryProtectAttribute (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 IpaWidth,
+ IN BOOLEAN Share
+ )
+{
+ UINT64 Val;
+ UINT64 Mask;
+ UINT64 ProtectionAttributeMask;
+
+ if (!IsRealm ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ if (IpaWidth == 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ /* Software in a Realm should treat the most significant bit of an
+ IPA as a protection attribute.
+ */
+ ProtectionAttributeMask = 1ULL << 5;
+ if (Share) {
+ Val = ProtectionAttributeMask;
+ Mask = 0xFFFFFFFFFFFFFFFF;
+ } else {
+ Val = 0;
+ Mask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | ProtectionAttributeMask);
+ }
+ return SetMemoryRegionAttribute (
+ BaseAddress,
+ Length,
+ Val,
+ Mask
+ );
+}
+
diff --git a/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf
new file mode 100644
index 0000000000000000000000000000000000000000..2c96d2a56c60b0e43b44a9c8b5101e4babae45a7
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf
@@ -0,0 +1,35 @@
+## @file
+# Library that implements the Arm CCA helper functions.
+#
+# Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = ArmCcaLib
+ FILE_GUID = 11C18743-52F9-405E-B35B-D7BE91A26F9F
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmCcaLib
+
+[Sources]
+ ArmCcaLib.c
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+ ArmPlatformPkg/ArmPlatformPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmCcaRsiLib
+ ArmLib
+ ArmMmuLib
+ BaseLib
+ HobLib
+
+[Guids]
+ gArmCcaIpaWidthGuid
diff --git a/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsi.h b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsi.h
new file mode 100644
index 0000000000000000000000000000000000000000..87002cda48213f435f7d92e1142e2be085ce4ba9
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsi.h
@@ -0,0 +1,51 @@
+/** @file
+ Definitions for Arm CCA Realm Service Interface.
+
+ Copyright (c) 2022 - 2023, ARM Ltd. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+
+ @par Reference(s):
+ - Realm Management Monitor (RMM) Specification, version 1.0-eac5
+ (https://developer.arm.com/documentation/den0137/)
+**/
+
+#ifndef ARM_CCA_RSI_H_
+#define ARM_CCA_RSI_H_
+
+// FIDs for Realm Service Interface calls.
+#define FID_RSI_MEASUREMENT_EXTEND 0xC4000193
+#define FID_RSI_VERSION 0xC4000190
+
+/** RSI Command Return codes
+ See Section B4.4.1, RMM Specification, version A-bet0.
+ The width of the RsiCommandReturnCode enumeration is 64 bits.
+*/
+#define RSI_SUCCESS 0ULL
+#define RSI_ERROR_INPUT 1ULL
+#define RSI_ERROR_STATE 2ULL
+#define RSI_INCOMPLETE 3ULL
+
+/** RSI interface Version
+ See Section B4.4.3, RMM Specification, version A-bet0.
+ The width of the RsiInterfaceVersion fieldset is 64 bits.
+*/
+#define RSI_VER_MINOR_MASK 0x0000FFFFULL
+#define RSI_VER_MAJOR_MASK 0x7FFF0000ULL
+#define RSI_VER_MAJOR_SHIFT 16
+#define RSI_VERSION_MASK (RSI_VER_MAJOR_MASK | RSI_VER_MINOR_MASK)
+
+#define RMM_VERSION(Major, Minor) ((Minor & RSI_VER_MINOR_MASK) | \
+ ((Major << RSI_VER_MAJOR_SHIFT) & RSI_VER_MAJOR_MASK))
+
+#define GET_MAJOR_REVISION(Rev) \
+ ((Rev & RSI_VER_MAJOR_MASK) >> RSI_VER_MAJOR_SHIFT)
+
+#define GET_MINOR_REVISION(Rev) \
+ ((Rev & RSI_VER_MINOR_MASK))
+
+#endif // ARM_CCA_RSI_H_
diff --git a/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.c b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.c
new file mode 100644
index 0000000000000000000000000000000000000000..8a000f904e2d99694a426f9fcbf29fd84d2f6a55
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.c
@@ -0,0 +1,163 @@
+/** @file
+ Library that implements the Arm CCA Realm Service Interface calls.
+
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Rsi or RSI - Realm Service Interface
+ - IPA - Intermediate Physical Address
+ - RIPAS - Realm IPA state
+ - REM - Realm Extensible Measurement
+
+ @par Reference(s):
+ - Realm Management Monitor (RMM) Specification, version 1.0-eac5
+ (https://developer.arm.com/documentation/den0137/)
+
+**/
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ArmCcaRsi.h"
+
+/** The version of RSI specification implemented by this module.
+*/
+STATIC CONST UINT32 mRsiImplVersion = RMM_VERSION (1, 0);
+
+/**
+ Convert the RSI status code to EFI Status code.
+
+ @param [in] RsiCommandReturnCode RSI status code.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_ABORTED The operation was aborted as the state
+ of the Realm or REC does not match the
+ state expected by the command.
+ @retval RETURN_NOT_READY The operation requested by the command
+ is not complete.
+ **/
+STATIC
+RETURN_STATUS
+RsiCmdStatusToEfiStatus (
+ IN UINT64 RsiCommandReturnCode
+ )
+{
+ switch (RsiCommandReturnCode) {
+ /* TODO: when running in the host, RSI is not available. Find out how to probe
+ * this reliably.
+ */
+ case 0xFFFFFFFFFFFFFFFF:
+ return RETURN_ABORTED;
+ case RSI_SUCCESS:
+ return RETURN_SUCCESS;
+ case RSI_ERROR_INPUT:
+ return RETURN_INVALID_PARAMETER;
+ case RSI_ERROR_STATE:
+ return RETURN_ABORTED;
+ case RSI_INCOMPLETE:
+ return RETURN_NOT_READY;
+ default:
+ // Unknown error code.
+ ASSERT (0);
+ break;
+ } // switch
+
+ return RETURN_ABORTED;
+}
+
+/**
+ Extends a measurement to a REM.
+
+ @param [in] MeasurementIndex Index of the REM.
+ @param [in] Measurement Pointer to the measurement buffer.
+ @param [in] MeasurementSize Size of the measurement data.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiExtendMeasurement (
+ IN UINTN MeasurementIndex,
+ IN CONST UINT8 *CONST Measurement,
+ IN UINTN MeasurementSize
+ )
+{
+ ARM_SMC_ARGS SmcCmd;
+
+ if ((MeasurementIndex < MIN_REM_INDEX) ||
+ (MeasurementIndex > MAX_REM_INDEX) ||
+ (Measurement == NULL) ||
+ (MeasurementSize == 0) ||
+ (MeasurementSize > MAX_MEASUREMENT_DATA_SIZE_BYTES))
+ {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+
+ SmcCmd.Arg0 = FID_RSI_MEASUREMENT_EXTEND;
+ SmcCmd.Arg1 = MeasurementIndex;
+ SmcCmd.Arg2 = MeasurementSize;
+
+ SmcCmd.Arg3 = (UINTN) Measurement;
+
+ ArmCallSmc (&SmcCmd);
+ return RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+}
+
+/**
+ Get the version of the RSI implementation.
+
+ @param [out] UefiImpl The version of the RSI specification
+ implemented by the UEFI firmware.
+ @param [out] RmmImpl The version of the RSI specification
+ implemented by the RMM.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_UNSUPPORTED The execution context is not a Realm.
+ @retval RETURN_INCOMPATIBLE_VERSION The Firmware and RMM specification
+ revisions are not compatible.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetVersion (
+ OUT UINT32 *CONST UefiImpl,
+ OUT UINT32 *CONST RmmImpl
+ )
+{
+ ARM_SMC_ARGS SmcCmd;
+
+ if ((UefiImpl == NULL) || (RmmImpl == NULL)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_VERSION;
+ ArmCallSmc (&SmcCmd);
+ if (SmcCmd.Arg0 == MAX_UINT64) {
+ // This FID is not implemented, which means
+ // we are not running in a Realm, therefore
+ // return the error code as unsupported.
+ return RETURN_UNSUPPORTED;
+ }
+
+ *RmmImpl = (SmcCmd.Arg0 & RSI_VERSION_MASK);
+ *UefiImpl = mRsiImplVersion;
+ if (*RmmImpl != *UefiImpl) {
+ // The RSI version implemented by UEFI
+ // firmware and RMM dose not match, return
+ // the status code as RETURN_INCOMPATIBLE_VERSION.
+ return RETURN_INCOMPATIBLE_VERSION;
+ }
+
+ return RETURN_SUCCESS;
+}
+
diff --git a/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf
new file mode 100644
index 0000000000000000000000000000000000000000..24506baa5db93ba3e51f284ee5189986b6b564c5
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf
@@ -0,0 +1,29 @@
+## @file
+# Library that implements the Arm CCA Realm Service Interface calls.
+#
+# Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = ArmCcaRsiLib
+ FILE_GUID = 5EF34A0A-28B5-4E57-A999-CC1528FC629A
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmCcaRsiLib
+
+[Sources]
+ ArmCcaRsiLib.c
+ ArmCcaRsi.h
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmLib
+ ArmSmcLib
diff --git a/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.c b/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.c
index 08beeacac0c5e6237921cbc3f4a18870cacacc39..1a9a967181883f5ab4475d3e9df50e01d6d06066 100644
--- a/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.c
+++ b/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.c
@@ -9,6 +9,7 @@
#include
+#include
#include
#include
#include
@@ -106,6 +107,6 @@ MemoryPeim (
// Optional feature that helps prevent EFI memory map fragmentation.
BuildMemoryTypeInformationHob ();
}
-
+ ArmCcaInitialize();
return EFI_SUCCESS;
}
diff --git a/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.inf b/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.inf
index 34b9fbf89a372c9af871459ca7e94184b67c4e3e..edd2e61941031822437d7e6a25f7d37a0efca823 100644
--- a/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.inf
+++ b/ArmVirtPkg/Library/ArmVirtMemoryInitPeiLib/ArmVirtMemoryInitPeiLib.inf
@@ -26,6 +26,7 @@
ArmVirtPkg/ArmVirtPkg.dec
[LibraryClasses]
+ ArmCcaInitPeiLib
DebugLib
HobLib
ArmLib
diff --git a/ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.c b/ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.c
new file mode 100644
index 0000000000000000000000000000000000000000..efc03e2f25077263db101c8b971a99c92609436b
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.c
@@ -0,0 +1,119 @@
+/** @file
+ Arm Monitor Library.
+
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ An enum representing the PSCI conduits for issuing monitor calls.
+*/
+typedef enum PsciConduit {
+ PsciConduitHvc, // < HVC conduit
+ PsciConduitSmc, // < SMC conduit
+ PsciConduitMax
+} PSCI_CONDUIT;
+
+/**
+ A variable that stores the PSCI conduit to be used.
+*/
+STATIC PSCI_CONDUIT mArmPsciConduit = PsciConduitMax;
+
+/** Monitor call.
+
+ An HyperVisor Call (HVC) or System Monitor Call (SMC) will be issued
+ depending on the conduit. The library constructor for ArmVirtMonitorLib
+ determines the conduit by parsing the Device Tree handed off by the VMM
+ and initialising mArmPsciConduit.
+
+ @param [in,out] Args Arguments for the HVC/SMC.
+**/
+VOID
+EFIAPI
+ArmMonitorCall (
+ IN OUT ARM_MONITOR_ARGS *Args
+ )
+{
+ switch (mArmPsciConduit) {
+ case PsciConduitHvc:
+ ArmCallHvc ((ARM_HVC_ARGS *)Args);
+ break;
+ case PsciConduitSmc:
+ ArmCallSmc ((ARM_SMC_ARGS *)Args);
+ break;
+ default:
+ ASSERT (0);
+ CpuDeadLoop ();
+ }
+}
+
+/** Constructor for ArmVirtMonitorLib.
+
+ The library constructor for ArmVirtMonitorLib determines the conduit
+ by parsing the Device Tree handed off by the VMM and initialising
+ mArmPsciConduit, which can then be used to select the appropriate
+ conduit for invoking the monitor call.
+
+ @retval RETURN_SUCCESS The constructor always returns RETURN_SUCCESS.
+ @retval RETURN_NOT_FOUND An entry for the PSCI conduit was not found in
+ the platform device tree.
+**/
+RETURN_STATUS
+EFIAPI
+ArmVirtMonitorLibConstructor (
+ VOID
+ )
+{
+ RETURN_STATUS Status;
+ FDT_CLIENT_PROTOCOL *FdtClient;
+ CONST VOID *Prop;
+
+ Status = gBS->LocateProtocol (
+ &gFdtClientProtocolGuid,
+ NULL,
+ (VOID **)&FdtClient
+ );
+ if (RETURN_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ Status = FdtClient->FindCompatibleNodeProperty (
+ FdtClient,
+ "arm,psci-0.2",
+ "method",
+ &Prop,
+ NULL
+ );
+ if (RETURN_ERROR (Status)) {
+ return Status;
+ }
+
+ if (AsciiStrnCmp (Prop, "hvc", 3) == 0) {
+ mArmPsciConduit = PsciConduitHvc;
+ } else if (AsciiStrnCmp (Prop, "smc", 3) == 0) {
+ mArmPsciConduit = PsciConduitSmc;
+ } else {
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a: Unknown PSCI method \"%a\"\n",
+ __func__,
+ Prop
+ ));
+ return RETURN_NOT_FOUND;
+ }
+
+ return RETURN_SUCCESS;
+}
diff --git a/ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.inf b/ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.inf
new file mode 100644
index 0000000000000000000000000000000000000000..fbc219333ec1878b1d974284d5cdd7203de67f0f
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.inf
@@ -0,0 +1,36 @@
+## @file
+# Arm Virt Monitor Library
+#
+# Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = ArmVirtMonitorLib
+ FILE_GUID = 3E464134-890D-4C3F-A559-D0FE2803E332
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmMonitorLib|DXE_DRIVER DXE_RUNTIME_DRIVER
+ CONSTRUCTOR = ArmVirtMonitorLibConstructor
+
+[Sources]
+ ArmVirtMonitorLib.c
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmHvcLib
+ ArmSmcLib
+ DebugLib
+ UefiBootServicesTableLib
+
+[Protocols]
+ gFdtClientProtocolGuid ## CONSUMES
+
+[Depex]
+ gFdtClientProtocolGuid
diff --git a/ArmVirtPkg/Library/HashLibCca/HashLibCca.c b/ArmVirtPkg/Library/HashLibCca/HashLibCca.c
new file mode 100644
index 0000000000000000000000000000000000000000..44e7138032bda695b7d0da25c5c41af1848fc09c
--- /dev/null
+++ b/ArmVirtPkg/Library/HashLibCca/HashLibCca.c
@@ -0,0 +1,209 @@
+/** @file
+ This library is HashLib for Cca.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+EFI_GUID mSha256Guid = HASH_ALGORITHM_SHA256_GUID;
+
+//
+// Currently CCA supports SHA256.
+//
+HASH_INTERFACE mHashInterface = {
+ { 0 }, NULL, NULL, NULL
+};
+
+UINTN mHashInterfaceCount = 0;
+
+/**
+ Start hash sequence.
+
+ @param HashHandle Hash handle.
+
+ @retval EFI_SUCCESS Hash sequence start and HandleHandle returned.
+ @retval EFI_OUT_OF_RESOURCES No enough resource to start hash.
+**/
+EFI_STATUS
+EFIAPI
+HashStart (
+ OUT HASH_HANDLE *HashHandle
+ )
+{
+ HASH_HANDLE HashCtx;
+
+ if (mHashInterfaceCount == 0) {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ HashCtx = 0;
+ mHashInterface.HashInit (&HashCtx);
+
+ *HashHandle = HashCtx;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update hash sequence data.
+
+ @param HashHandle Hash handle.
+ @param DataToHash Data to be hashed.
+ @param DataToHashLen Data size.
+
+ @retval EFI_SUCCESS Hash sequence updated.
+**/
+EFI_STATUS
+EFIAPI
+HashUpdate (
+ IN HASH_HANDLE HashHandle,
+ IN VOID *DataToHash,
+ IN UINTN DataToHashLen
+ )
+{
+ if (mHashInterfaceCount == 0) {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ mHashInterface.HashUpdate (HashHandle, DataToHash, DataToHashLen);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Hash sequence complete and extend to PCR.
+
+ @param HashHandle Hash handle.
+ @param PcrIndex PCR to be extended.
+ @param DataToHash Data to be hashed.
+ @param DataToHashLen Data size.
+ @param DigestList Digest list.
+
+ @retval EFI_SUCCESS Hash sequence complete and DigestList is returned.
+**/
+EFI_STATUS
+EFIAPI
+HashCompleteAndExtend (
+ IN HASH_HANDLE HashHandle,
+ IN TPMI_DH_PCR PcrIndex,
+ IN VOID *DataToHash,
+ IN UINTN DataToHashLen,
+ OUT TPML_DIGEST_VALUES *DigestList
+ )
+{
+ TPML_DIGEST_VALUES Digest;
+ EFI_STATUS Status;
+
+ if (mHashInterfaceCount == 0) {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ ZeroMem (DigestList, sizeof (*DigestList));
+
+ mHashInterface.HashUpdate (HashHandle, DataToHash, DataToHashLen);
+ mHashInterface.HashFinal (HashHandle, &Digest);
+
+ CopyMem (
+ &DigestList->digests[0],
+ &Digest.digests[0],
+ sizeof (Digest.digests[0])
+ );
+ DigestList->count++;
+
+ ASSERT (DigestList->count == 1 && DigestList->digests[0].hashAlg == TPM_ALG_SHA256);
+
+ Status = RsiExtendMeasurement (
+ (UINTN)PcrIndex,
+ (UINT8 *)DigestList->digests[0].digest.sha256,
+ SHA256_DIGEST_SIZE
+ );
+
+ ASSERT (!EFI_ERROR (Status));
+ return Status;
+}
+
+/**
+ Hash data and extend to REM.
+
+ @param PcrIndex PCR to be extended.
+ @param DataToHash Data to be hashed.
+ @param DataToHashLen Data size.
+ @param DigestList Digest list.
+
+ @retval EFI_SUCCESS Hash data and DigestList is returned.
+**/
+EFI_STATUS
+EFIAPI
+HashAndExtend (
+ IN TPMI_DH_PCR PcrIndex,
+ IN VOID *DataToHash,
+ IN UINTN DataToHashLen,
+ OUT TPML_DIGEST_VALUES *DigestList
+ )
+{
+ HASH_HANDLE HashHandle;
+ EFI_STATUS Status;
+
+ if (mHashInterfaceCount == 0) {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ ASSERT (IsRealm ());
+
+ HashStart (&HashHandle);
+ HashUpdate (HashHandle, DataToHash, DataToHashLen);
+ Status = HashCompleteAndExtend (HashHandle, PcrIndex, NULL, 0, DigestList);
+
+ return Status;
+}
+
+/**
+ This service register Hash.
+
+ @param HashInterface Hash interface
+
+ @retval EFI_SUCCESS This hash interface is registered successfully.
+ @retval EFI_UNSUPPORTED System does not support register this interface.
+ @retval EFI_ALREADY_STARTED System already register this interface.
+**/
+EFI_STATUS
+EFIAPI
+RegisterHashInterfaceLib (
+ IN HASH_INTERFACE *HashInterface
+ )
+{
+ //
+ // HashLibCca is designed for Cca guest. So if it is not Cca guest,
+ // return EFI_UNSUPPORTED.
+ //
+ if (!IsRealm ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Only SHA256 is allowed.
+ //
+ if (!CompareGuid (&mSha256Guid, &HashInterface->HashGuid)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (mHashInterfaceCount != 0) {
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (&mHashInterface, HashInterface, sizeof (*HashInterface));
+ mHashInterfaceCount++;
+
+ return EFI_SUCCESS;
+}
diff --git a/ArmVirtPkg/Library/HashLibCca/HashLibCca.inf b/ArmVirtPkg/Library/HashLibCca/HashLibCca.inf
new file mode 100644
index 0000000000000000000000000000000000000000..124682949725487a4bce0ce5d813a28ae39fc215
--- /dev/null
+++ b/ArmVirtPkg/Library/HashLibCca/HashLibCca.inf
@@ -0,0 +1,35 @@
+## @file
+#
+# This library is HashLib for Cca. Currently only SHA256 is supported.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = HashLibCca
+ FILE_GUID = 60a6d467-4f75-4089-8c8e-52a74af1e068
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = HashLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = AARCH64
+#
+
+[Sources]
+ HashLibCca.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ PcdLib
+ ArmCcaLib
+ ArmCcaRsiLib
diff --git a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c
index 55b419c9f46298d77218cbcc67bb83e78272719b..2fbb483e0489d1d2c0a2f2142387c3b8df7f0924 100644
--- a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c
+++ b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.c
@@ -17,23 +17,71 @@
#define MAX_FLASH_BANKS 4
+STATIC VIRT_NOR_FLASH_DESCRIPTION mNorFlashDevices[MAX_FLASH_BANKS];
+STATIC UINTN mNorFlashDeviceCount = 0;
+STATIC INT32 mNorFlashNodes[MAX_FLASH_BANKS];
+STATIC UINTN mNorFlashNodeCount = 0;
+FDT_CLIENT_PROTOCOL *mFdtClient;
+
EFI_STATUS
VirtNorFlashPlatformInitialization (
VOID
)
{
+ EFI_STATUS Status;
+ UINTN Index;
+ //
+ // UEFI takes ownership of the NOR flash, and exposes its functionality
+ // through the UEFI Runtime Services GetVariable, SetVariable, etc. This
+ // means we need to disable it in the device tree to prevent the OS from
+ // attaching its device driver as well.
+ // Note that this also hides other flash banks, but the only other flash
+ // bank we expect to encounter is the one that carries the UEFI executable
+ // code, which is not intended to be guest updatable, and is usually backed
+ // in a readonly manner by QEMU anyway.
+ //
+ for (Index = 0; Index < mNorFlashNodeCount; Index++) {
+ Status = mFdtClient->SetNodeProperty (
+ mFdtClient,
+ mNorFlashNodes[Index],
+ "status",
+ "disabled",
+ sizeof ("disabled")
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "Failed to set NOR flash status to 'disabled'\n"));
+ }
+ }
return EFI_SUCCESS;
}
-STATIC VIRT_NOR_FLASH_DESCRIPTION mNorFlashDevices[MAX_FLASH_BANKS];
-
EFI_STATUS
VirtNorFlashPlatformGetDevices (
OUT VIRT_NOR_FLASH_DESCRIPTION **NorFlashDescriptions,
OUT UINT32 *Count
)
{
- FDT_CLIENT_PROTOCOL *FdtClient;
+
+ if (mNorFlashDeviceCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ *NorFlashDescriptions = mNorFlashDevices;
+ *Count = mNorFlashDeviceCount;
+
+ return EFI_SUCCESS;
+}
+
+
+
+EFI_STATUS
+EFIAPI
+NorFlashQemuLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+
+ )
+{
INT32 Node;
EFI_STATUS Status;
EFI_STATUS FindNodeStatus;
@@ -46,26 +94,26 @@ VirtNorFlashPlatformGetDevices (
Status = gBS->LocateProtocol (
&gFdtClientProtocolGuid,
NULL,
- (VOID **)&FdtClient
+ (VOID **)&mFdtClient
);
ASSERT_EFI_ERROR (Status);
Num = 0;
- for (FindNodeStatus = FdtClient->FindCompatibleNode (
- FdtClient,
+ for (FindNodeStatus = mFdtClient->FindCompatibleNode (
+ mFdtClient,
"cfi-flash",
&Node
);
!EFI_ERROR (FindNodeStatus) && Num < MAX_FLASH_BANKS;
- FindNodeStatus = FdtClient->FindNextCompatibleNode (
- FdtClient,
+ FindNodeStatus = mFdtClient->FindNextCompatibleNode (
+ mFdtClient,
"cfi-flash",
Node,
&Node
))
{
- Status = FdtClient->GetNodeProperty (
- FdtClient,
+ Status = mFdtClient->GetNodeProperty (
+ mFdtClient,
Node,
"reg",
(CONST VOID **)&Reg,
@@ -104,33 +152,20 @@ VirtNorFlashPlatformGetDevices (
mNorFlashDevices[Num].RegionBaseAddress = (UINTN)Base;
mNorFlashDevices[Num].Size = (UINTN)Size;
mNorFlashDevices[Num].BlockSize = QEMU_NOR_BLOCK_SIZE;
- Num++;
- }
-
- //
- // UEFI takes ownership of the NOR flash, and exposes its functionality
- // through the UEFI Runtime Services GetVariable, SetVariable, etc. This
- // means we need to disable it in the device tree to prevent the OS from
- // attaching its device driver as well.
- // Note that this also hides other flash banks, but the only other flash
- // bank we expect to encounter is the one that carries the UEFI executable
- // code, which is not intended to be guest updatable, and is usually backed
- // in a readonly manner by QEMU anyway.
- //
- Status = FdtClient->SetNodeProperty (
- FdtClient,
- Node,
- "status",
- "disabled",
- sizeof ("disabled")
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_WARN, "Failed to set NOR flash status to 'disabled'\n"));
+ mNorFlashDeviceCount = ++Num;
}
+ mNorFlashNodes[mNorFlashNodeCount++] = Node;
}
-
- *NorFlashDescriptions = mNorFlashDevices;
- *Count = Num;
-
- return EFI_SUCCESS;
-}
+ if (Num == 0) {
+ DEBUG ((DEBUG_INFO,
+ "No Flash device found, falling back to Runtime Variable Emulation\n"));
+
+ Status = PcdSetBoolS (PcdEmuVariableNvModeEnable, TRUE);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR,
+ "Failed to set PcdEmuVariableNvModeEnable, Status = %r\n",
+ Status));
+ }
+ }
+ return EFI_SUCCESS;
+}
\ No newline at end of file
diff --git a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf
index 8c77a18b91309c59ad77ba9654d024e39d7452ff..a5ff4be6030985c70eda0244df333bbf7a487b03 100644
--- a/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf
+++ b/ArmVirtPkg/Library/NorFlashQemuLib/NorFlashQemuLib.inf
@@ -15,12 +15,13 @@
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = VirtNorFlashPlatformLib
-
+ CONSTRUCTOR = NorFlashQemuLibConstructor
[Sources.common]
NorFlashQemuLib.c
[Packages]
MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
ArmPkg/ArmPkg.dec
ArmVirtPkg/ArmVirtPkg.dec
EmbeddedPkg/EmbeddedPkg.dec
@@ -40,3 +41,4 @@
[Pcd]
gArmTokenSpaceGuid.PcdFvBaseAddress
gArmTokenSpaceGuid.PcdFvSize
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable
diff --git a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
index e6362a69861425bf1458d295990dd4c2c0122953..5315177817ff6d4611e526f82224ff18564298a6 100644
--- a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
@@ -122,3 +123,23 @@ ArmVirtGetMemoryMap (
*VirtualMemoryMap = VirtualMemoryTable;
}
+
+/**
+ Configure the MMIO regions as shared with the VMM.
+
+ Set the protection attribute for the MMIO regions as Unprotected IPA.
+
+ @param[in] IpaWidth IPA width of the Realm.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The execution context is not in a Realm.
+**/
+EFI_STATUS
+EFIAPI
+ArmCcaConfigureMmio (
+ IN UINT64 IpaWidth
+ )
+{
+ return RETURN_SUCCESS;
+}
diff --git a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf
index f209b0381cc79cf4e8115418b4930ebe401ed960..69b8708dee1a1fe25e6635db89dc9094f165af13 100644
--- a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf
+++ b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf
@@ -27,6 +27,7 @@
MdePkg/MdePkg.dec
[LibraryClasses]
+ ArmCcaLib
ArmLib
BaseMemoryLib
DebugLib
diff --git a/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf b/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
index 9e2a273326ca1a2de8bdfd13307fad2fc66d0637..f2ddcc59fc1268b0d01c222279ff4a9f0daec675 100755
--- a/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
+++ b/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
@@ -37,6 +37,7 @@
ArmVirtPkg/ArmVirtPkg.dec
[LibraryClasses]
+ ArmCcaInitPeiLib
BaseLib
DebugLib
FdtLib
diff --git a/ArmVirtPkg/PrePi/PrePi.c b/ArmVirtPkg/PrePi/PrePi.c
index 31b1cf0a04a213779f774165c9a75500d5f6a64a..bdd20941599c75f0801cc8780008f3141a06f9d2 100755
--- a/ArmVirtPkg/PrePi/PrePi.c
+++ b/ArmVirtPkg/PrePi/PrePi.c
@@ -66,7 +66,8 @@ PrePiMain (
// Initialize MMU and Memory HOBs (Resource Descriptor HOBs)
Status = MemoryPeim (UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize));
ASSERT_EFI_ERROR (Status);
-
+ // Perform the Arm CCA specific initialisations.
+ ArmCcaInitialize ();
// Initialize the Serial Port
SerialPortInitialize ();
CharCount = AsciiSPrint (
diff --git a/ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.c b/ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.c
new file mode 100644
index 0000000000000000000000000000000000000000..cabda2363cdbe081ac8711112d33f6c88f0346f7
--- /dev/null
+++ b/ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.c
@@ -0,0 +1,38 @@
+/** @file
+
+ The QemuPlatformDxe performs platform specific initialization.
+
+ Copyright (c) 2018 - 2023, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include
+#include
+#include
+#include
+
+EFI_STATUS
+EFIAPI
+QemuPlatformDxeEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+
+ if (PcdGetBool (PcdEmuVariableNvModeEnable)) {
+
+ // The driver implementing the variable service can now be dispatched.
+ Status = gBS->InstallProtocolInterface (
+ &gImageHandle,
+ &gEdkiiNvVarStoreFormattedGuid,
+ EFI_NATIVE_INTERFACE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
diff --git a/ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.inf b/ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.inf
new file mode 100644
index 0000000000000000000000000000000000000000..b62f5274a25aea4fff21fd288e347f0d3b8f1200
--- /dev/null
+++ b/ArmVirtPkg/QemuPlatformDxe/QemuPlatformDxe.inf
@@ -0,0 +1,39 @@
+## @file
+#
+# Copyright (c) 2018 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = QemuPlatformDxe
+ FILE_GUID = 30E617DC-3EB6-4225-B990-A5C22C87AC27
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = QemuPlatformDxeEntryPoint
+
+[Sources]
+ QemuPlatformDxe.c
+
+[Packages]
+ OvmfPkg/OvmfPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Guids]
+ gEdkiiNvVarStoreFormattedGuid ## SOMETIMES_PRODUCES ## PROTOCOL
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvModeEnable
+
+[Depex]
+ TRUE
diff --git a/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.c b/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.c
new file mode 100644
index 0000000000000000000000000000000000000000..82ae9571f3dda33c96a896b861f30ffd05f931f0
--- /dev/null
+++ b/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.c
@@ -0,0 +1,592 @@
+/** @file
+ Realm Aperture Management Protocol Dxe
+
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - IPA - Intermediate Physical Address
+ - RAMP - Realm Aperture Management Protocol
+ - RIPAS - Realm IPA state
+ - RSI - Realm Service Interface
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ A macro defining the signature for the aperture information structure.
+*/
+#define APERTURE_INFO_SIG SIGNATURE_64 ('A', 'P', 'E', 'R', 'T', 'U', 'R', 'E')
+
+/**
+ A structure describing the aperture.
+*/
+typedef struct {
+ /// Signature for identifying this structure.
+ UINT64 Signature;
+
+ /// The linked list entry.
+ LIST_ENTRY Link;
+
+ /// The base address for the start of the aperture.
+ EFI_PHYSICAL_ADDRESS BaseAddress;
+
+ /// The number of pages covered by the aperture.
+ UINTN Pages;
+
+ /// The bit mask of attributes for the memory region. The
+ /// bit mask of available attributes is defined in GetMemoryMap().
+ UINT64 MemoryAttributes;
+} APERTURE_INFO;
+
+/**
+ List of the APERTURE_INFO structures that have been set up by OpenAperture()
+ and not yet torn down by CloseAperture(). The list represents the full set
+ of open apertures currently in effect.
+*/
+STATIC
+LIST_ENTRY mApertureInfos = INITIALIZE_LIST_HEAD_VARIABLE (mApertureInfos);
+
+/**
+ A local variable to store the IPA width of the Realm. The IPA width
+ of the Realm is required to configure the protection attribute of
+ memory regions.
+*/
+STATIC UINT64 mIpaWidth;
+
+/** Checks if an open aperture is overlapping the memory region.
+
+ @param [in] Memory Pointer to the page start address.
+ @param [in] Pages Number of pages to share.
+
+ @retval TRUE If memory region overlaps an open aperture.
+ @retval FALSE Memory region does not overlap any open apertures.
+**/
+STATIC
+BOOLEAN
+EFIAPI
+IsApertureOverlapping (
+ IN CONST EFI_PHYSICAL_ADDRESS MemStart,
+ IN CONST UINTN Pages
+ )
+{
+ LIST_ENTRY *Node;
+ LIST_ENTRY *NextNode;
+ APERTURE_INFO *ApertureInfo;
+ EFI_PHYSICAL_ADDRESS MemEnd;
+ EFI_PHYSICAL_ADDRESS ApertureStart;
+ EFI_PHYSICAL_ADDRESS ApertureEnd;
+
+ MemEnd = MemStart + (EFI_PAGE_SIZE * Pages) - 1;
+
+ // All drivers that had opened the apertures have halted their respective
+ // controllers by now; close all the apertures.
+ for (
+ Node = GetFirstNode (&mApertureInfos);
+ Node != &mApertureInfos;
+ Node = NextNode
+ )
+ {
+ NextNode = GetNextNode (&mApertureInfos, Node);
+ ApertureInfo = CR (Node, APERTURE_INFO, Link, APERTURE_INFO_SIG);
+ ApertureStart = ApertureInfo->BaseAddress;
+ ApertureEnd = ApertureStart + (EFI_PAGE_SIZE * ApertureInfo->Pages) - 1;
+
+ if (((ApertureStart >= MemStart) && (ApertureStart <= MemEnd)) ||
+ ((ApertureEnd >= MemStart) && (ApertureEnd <= MemEnd)) ||
+ ((MemStart >= ApertureStart) && (MemStart <= ApertureEnd)) ||
+ ((MemEnd >= ApertureStart) && (MemEnd <= ApertureEnd)))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/** Enables sharing of the memory buffers with the host.
+
+ @param [in] Memory Pointer to the page start address.
+ @param [in] Pages Number of pages to share.
+ @param [out] ApertureReference Reference to the opened aperture.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_ACCESS_DENIED Aperture already open over memory region.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+RampOpenAperture (
+ IN CONST EFI_PHYSICAL_ADDRESS Memory,
+ IN CONST UINTN Pages,
+ OUT EFI_HANDLE *CONST ApertureReference
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS Status1;
+ APERTURE_INFO *ApertInfo;
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
+ EFI_PHYSICAL_ADDRESS MemRangeAddr;
+ UINTN Index;
+
+ if ((Memory == 0) ||
+ (Pages == 0) ||
+ (ApertureReference == NULL) ||
+ ((Memory & (EFI_PAGE_SIZE - 1)) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // The pages size must be aligned to the Realm Granule size.
+ // STATIC_ASSERT ((EFI_PAGE_SIZE & (REALM_GRANULE_SIZE - 1)) == 0);
+
+ // Checks if we already have an open aperture that overlaps the
+ // memory region. If so return the request as invalid.
+ if (IsApertureOverlapping (Memory, Pages)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MemRangeAddr = Memory;
+ for (Index = 0; Index < Pages; Index++) {
+ Status = gDS->GetMemorySpaceDescriptor (MemRangeAddr, &GcdDescriptor);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: Memory = 0x%lx, MemType = %a\n",
+ __func__,
+ MemRangeAddr,
+ ((GcdDescriptor.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) ?
+ "Runtime Services Memory" : "Boot Services Memory"
+ ));
+
+ // We currently do not have a usecase where we would want to open apertures
+ // for runtime services memory
+ if ((GcdDescriptor.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) {
+ return EFI_UNSUPPORTED;
+ }
+
+ MemRangeAddr += EFI_PAGE_SIZE;
+ } // for
+
+ Status = ArmCcaSetMemoryProtectAttribute (
+ Memory,
+ EFI_PAGES_TO_SIZE (Pages),
+ mIpaWidth,
+ TRUE
+ );
+ if (RETURN_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to update page tables for Protected EMPTY page mapping, "
+ "Address = %p, Pages = 0x%lx, Status = %r\n",
+ Memory,
+ Pages,
+ Status
+ ));
+ return Status;
+ }
+
+ // Allocate a APERTURE_INFO structure to remember the apertures opened.
+ ApertInfo = AllocateZeroPool (sizeof (APERTURE_INFO));
+ if (ApertInfo == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto error_handler1;
+ }
+
+ InitializeListHead (&ApertInfo->Link);
+ ApertInfo->Signature = APERTURE_INFO_SIG;
+ ApertInfo->BaseAddress = Memory;
+ ApertInfo->Pages = Pages;
+ ApertInfo->MemoryAttributes = GcdDescriptor.Attributes;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: ApertRef = 0x%p, Memory = 0x%lx, Pages = 0x%x, "
+ "MemoryAttributes = 0x%x\n",
+ __func__,
+ ApertInfo,
+ ApertInfo->BaseAddress,
+ ApertInfo->Pages,
+ ApertInfo->MemoryAttributes
+ ));
+
+ InsertHeadList (&mApertureInfos, &ApertInfo->Link);
+ *ApertureReference = (EFI_HANDLE *)&ApertInfo->Link;
+
+ return Status;
+
+error_handler1:
+ Status1 = ArmCcaSetMemoryProtectAttribute (
+ Memory,
+ EFI_PAGES_TO_SIZE (Pages),
+ mIpaWidth,
+ TRUE
+ );
+ if (RETURN_ERROR (Status1)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to update page tables to Protected page mapping, "
+ "Address = %p, Pages = 0x%lx, Status = %r\n",
+ Memory,
+ Pages,
+ Status1
+ ));
+ }
+
+ *ApertureReference = NULL;
+ return Status;
+}
+
+/** Disables the sharing of the buffers.
+
+ @param [in] ApertureReference Reference to the aperture for closing.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND The required buffer information is not found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+RampCloseAperture (
+ IN CONST EFI_HANDLE ApertureReference
+ )
+{
+ EFI_STATUS Status;
+ APERTURE_INFO *ApertInfo = NULL;
+
+ if (ApertureReference == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ApertInfo = CR (ApertureReference, APERTURE_INFO, Link, APERTURE_INFO_SIG);
+ if (ApertInfo == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: ApertRef = 0x%p, Memory = 0x%lx, Pages = 0x%x, "
+ "MemoryAttributes = 0x%x\n",
+ __func__,
+ ApertInfo,
+ ApertInfo->BaseAddress,
+ ApertInfo->Pages,
+ ApertInfo->MemoryAttributes
+ ));
+
+ Status = ArmCcaSetMemoryProtectAttribute (
+ ApertInfo->BaseAddress,
+ EFI_PAGES_TO_SIZE (ApertInfo->Pages),
+ mIpaWidth,
+ FALSE
+ );
+ if (RETURN_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to update page tables for Protected RAM page mapping,"
+ "Address = %p, Pages = 0x%lx, Status = %r\n",
+ ApertInfo->BaseAddress,
+ ApertInfo->Pages,
+ Status
+ ));
+ }
+
+ RemoveEntryList (&ApertInfo->Link);
+ FreePool (ApertInfo);
+
+ return Status;
+}
+
+/** Closes all open apertures.
+
+**/
+STATIC
+VOID
+EFIAPI
+RampCloseAllApertures (
+ VOID
+ )
+{
+ LIST_ENTRY *Node;
+ LIST_ENTRY *NextNode;
+ APERTURE_INFO *ApertureInfo;
+
+ // All drivers that had opened the apertures have halted their respective
+ // controllers by now; close all the apertures.
+ for (
+ Node = GetFirstNode (&mApertureInfos);
+ Node != &mApertureInfos;
+ Node = NextNode
+ )
+ {
+ NextNode = GetNextNode (&mApertureInfos, Node);
+ ApertureInfo = CR (Node, APERTURE_INFO, Link, APERTURE_INFO_SIG);
+ RampCloseAperture (&ApertureInfo->Link);
+ }
+}
+
+/**
+ Notification function that is queued after the notification functions of all
+ events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group.
+
+ This function invokes the closing of all open apertures.
+
+ @param[in] Event Event whose notification function is being invoked. Event
+ is permitted to request the queueing of this function
+ only at TPL_CALLBACK task priority level.
+
+ @param[in] Context Ignored.
+**/
+STATIC
+VOID
+EFIAPI
+OnRampExitBootServicesEvent (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ RampCloseAllApertures ();
+}
+
+/**
+ Notification function that is queued when gBS->ExitBootServices() signals the
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another
+ event, received as Context, and returns.
+
+ Signaling an event in this context is safe. The UEFI spec allows
+ gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not
+ listed, hence memory is not allocated.
+
+ @param[in] Event Event whose notification function is being invoked.
+ Event is permitted to request the queueing of this
+ function at TPL_CALLBACK or TPL_NOTIFY task
+ priority level.
+
+ @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal
+ is permitted to request the queueing of its
+ notification function only at TPL_CALLBACK level.
+**/
+STATIC
+VOID
+EFIAPI
+RampExitBootServices (
+ IN EFI_EVENT Event,
+ IN VOID *EventToSignal
+ )
+{
+ /**
+ (1) The NotifyFunctions of all the events in
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before
+ RampExitBootServices() is entered.
+
+ (2) RampExitBootServices() is executing minimally at TPL_CALLBACK.
+
+ (3) RampExitBootServices() has been queued in unspecified order relative
+ to the NotifyFunctions of all the other events in
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as
+ Event's.
+
+ Consequences:
+
+ - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions
+ queued at TPL_CALLBACK may be invoked after RampExitBootServices()
+ returns.
+
+ - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions
+ queued at TPL_NOTIFY may be invoked after RampExitBootServices()
+ returns; plus *all* NotifyFunctions queued at TPL_CALLBACK will be
+ invoked strictly after all NotifyFunctions queued at TPL_NOTIFY,
+ including RampExitBootServices(), have been invoked.
+
+ - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we
+ queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*
+ events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.
+ */
+ gBS->SignalEvent (EventToSignal);
+}
+
+/** A structure describing the Realm Aperture Management protocol.
+*/
+STATIC
+CONST
+EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL Ramp = {
+ EDKII_REALM_APERTURE_MANAGEMENT_PROTOCOL_REVISION,
+ RampOpenAperture,
+ RampCloseAperture
+};
+
+/**
+ This routine is called to close all apertures before system reset.
+
+ @param[in] ResetType The type of reset to perform.
+ @param[in] ResetStatus The status code for the reset.
+ @param[in] DataSize The size, in bytes, of ResetData.
+ @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or
+ EfiResetShutdown the data buffer starts with a Null-
+ terminated string, optionally followed by additional
+ binary data. The string is a description that the
+ caller may use to further indicate the reason for
+ the system reset. ResetData is only valid if
+ ResetStatus is something other than EFI_SUCCESS
+ unless the ResetType is EfiResetPlatformSpecific
+ where a minimum amount of ResetData is always
+ required.
+ For a ResetType of EfiResetPlatformSpecific the data
+ buffer also starts with a Null-terminated string
+ that is followed by an EFI_GUID that describes the
+ specific type of reset to perform.
+**/
+VOID
+EFIAPI
+OnResetEvent (
+ IN EFI_RESET_TYPE ResetType,
+ IN EFI_STATUS ResetStatus,
+ IN UINTN DataSize,
+ IN VOID *ResetData OPTIONAL
+ )
+{
+ RampCloseAllApertures ();
+}
+
+/**
+ Hook the system reset to close all apertures.
+
+ @param[in] Event Event whose notification function is being invoked
+ @param[in] Context Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+OnResetNotificationInstall (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_RESET_NOTIFICATION_PROTOCOL *ResetNotify;
+
+ Status = gBS->LocateProtocol (
+ &gEfiResetNotificationProtocolGuid,
+ NULL,
+ (VOID **)&ResetNotify
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = ResetNotify->RegisterResetNotify (ResetNotify, OnResetEvent);
+ ASSERT_EFI_ERROR (Status);
+ DEBUG ((DEBUG_INFO, "RAMP: Hook system reset to close all apertures.\n"));
+ gBS->CloseEvent (Event);
+ }
+}
+
+/** Entry point for Realm Aperture Management Protocol Dxe
+
+ @param [in] ImageHandle Handle for this image.
+ @param [in] SystemTable Pointer to the EFI system table.
+
+ @retval EFI_SUCCESS When executing in a Realm the RAMP was
+ installed successfully.
+ When execution context is not a Realm, this
+ function returns success indicating nothing
+ needs to be done and allow other modules to
+ run.
+ @retval EFI_OUT_OF_RESOURCES There was not enough memory to install the
+ protocols.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+RealmApertureManagementProtocolDxeInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle = NULL;
+ EFI_EVENT CloseAllAperturesEvent;
+ EFI_EVENT ExitBootEvent;
+ VOID *Registration;
+
+ // When the execution context is a Realm, install the Realm Aperture
+ // Management protocol otherwise return success so that other modules
+ // can run.
+ if (!IsRealm ()) {
+ return EFI_SUCCESS;
+ }
+
+ mIpaWidth = 64;
+
+ /*
+ Create the "late" event whose notification function will close all
+ apertures.
+ */
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL, // Type
+ TPL_CALLBACK, // NotifyTpl
+ OnRampExitBootServicesEvent, // NotifyFunction
+ NULL, // NotifyContext
+ &CloseAllAperturesEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ /*
+ Create the event whose notification function will be queued by
+ gBS->ExitBootServices() and will signal the event created above.
+ */
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
+ TPL_CALLBACK, // NotifyTpl
+ RampExitBootServices, // NotifyFunction
+ CloseAllAperturesEvent, // NotifyContext
+ &ExitBootEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto error_handler1;
+ }
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEfiRealmApertureManagementProtocolGuid,
+ &Ramp,
+ NULL
+ );
+ if (!EFI_ERROR (Status)) {
+ // RAMP Protocol installed successfully
+ // Hook the system reset to close all apertures.
+ EfiCreateProtocolNotifyEvent (
+ &gEfiResetNotificationProtocolGuid,
+ TPL_CALLBACK,
+ OnResetNotificationInstall,
+ NULL,
+ &Registration
+ );
+
+ return Status;
+ }
+
+ // cleanup on error
+ gBS->CloseEvent (ExitBootEvent);
+
+error_handler1:
+ gBS->CloseEvent (CloseAllAperturesEvent);
+
+ return Status;
+}
diff --git a/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf b/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf
new file mode 100644
index 0000000000000000000000000000000000000000..8f8e758518892998f04b0aec79feb84fea74bc36
--- /dev/null
+++ b/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf
@@ -0,0 +1,48 @@
+## @file
+# Module to manage the sharing of buffers in a Realm with the Host.
+#
+# Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = RealmApertureManagementProtocolDxe
+ FILE_GUID = CEC2F7D5-2564-46D4-A23F-501623F7F56A
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = RealmApertureManagementProtocolDxeInitialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = AARCH64
+#
+
+[Sources]
+ RealmApertureManagementProtocolDxe.c
+
+[Packages]
+ ArmVirtPkg/ArmVirtPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmCcaLib
+ ArmCcaRsiLib
+ BaseLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ PrintLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+
+[Protocols]
+ gEfiRealmApertureManagementProtocolGuid ## SOMETIME_PRODUCES
+ gEfiResetNotificationProtocolGuid ## CONSUMES
+
+[Depex]
+ TRUE
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 2edcc2d47b1ea97facac18fc37e9a0c61bf68774..f61478a41eb245b9ecd8546538c08a14ce403bbc 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -1984,7 +1984,7 @@
## Default OEM ID for ACPI table creation, its length must be 0x6 bytes to follow ACPI specification.
# @Prompt Default OEM ID for ACPI table creation.
- gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId|"INTEL "|VOID*|0x30001034
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId|"Huawei "|VOID*|0x30001034
## Default OEM Table ID for ACPI table creation, it is "EDK2 ".
# According to ACPI specification, this field is particularly useful when
diff --git a/MdePkg/Include/IndustryStandard/UefiTcgPlatform.h b/MdePkg/Include/IndustryStandard/UefiTcgPlatform.h
index 77866bb2df6be3f02e7c7ea75ea32ac367ff4f1b..133e22d8d5ea01e54d3378f2bb7b18fccb8710a2 100644
--- a/MdePkg/Include/IndustryStandard/UefiTcgPlatform.h
+++ b/MdePkg/Include/IndustryStandard/UefiTcgPlatform.h
@@ -438,6 +438,7 @@ typedef struct tdTCG_PCClientTaggedEvent {
#define TCG_Sp800_155_PlatformId_Event_SIGNATURE "SP800-155 Event"
#define TCG_Sp800_155_PlatformId_Event2_SIGNATURE "SP800-155 Event2"
+#define TCG_Sp800_155_PlatformId_Event3_SIGNATURE "SP800-155 Event3"
typedef struct tdTCG_Sp800_155_PlatformId_Event2 {
UINT8 Signature[16];
diff --git a/MdePkg/Include/Protocol/CcMeasurement.h b/MdePkg/Include/Protocol/CcMeasurement.h
index 43d24036c5b8b16eb243bf195f9144280057242d..282b7b9b1c789f8e74e9d400b367c40a616b797e 100644
--- a/MdePkg/Include/Protocol/CcMeasurement.h
+++ b/MdePkg/Include/Protocol/CcMeasurement.h
@@ -36,6 +36,7 @@ typedef struct {
#define EFI_CC_TYPE_NONE 0
#define EFI_CC_TYPE_SEV 1
#define EFI_CC_TYPE_TDX 2
+#define EFI_CC_TYPE_CCA 3
typedef struct {
UINT8 Type;
@@ -57,7 +58,9 @@ typedef UINT32 EFI_CC_MR_INDEX;
#define TDX_MR_INDEX_RTMR3 4
#define EFI_CC_EVENT_LOG_FORMAT_TCG_2 0x00000002
-#define EFI_CC_BOOT_HASH_ALG_SHA384 0x00000004
+#define EFI_CC_BOOT_HASH_ALG_SHA256 0x00000002
+#define EFI_CC_BOOT_HASH_ALG_SHA384 0x00000004
+#define EFI_CC_BOOT_HASH_ALG_SHA512 0x00000008
//
// This bit is shall be set when an event shall be extended but not logged.
diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
index 59efb6f8e93ab657225e02123fbd0108a12c68d7..e1598bb632bff0fe150f59759355f50aad45355b 100644
--- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
+++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.c
@@ -22,7 +22,6 @@
STATIC UINTN mFwCfgSelectorAddress;
STATIC UINTN mFwCfgDataAddress;
-STATIC UINTN mFwCfgDmaAddress;
/**
Reads firmware configuration bytes into a buffer
@@ -67,9 +66,6 @@ VOID(EFIAPI SKIP_BYTES_FUNCTION)(
STATIC READ_BYTES_FUNCTION MmioReadBytes;
STATIC WRITE_BYTES_FUNCTION MmioWriteBytes;
STATIC SKIP_BYTES_FUNCTION MmioSkipBytes;
-STATIC READ_BYTES_FUNCTION DmaReadBytes;
-STATIC WRITE_BYTES_FUNCTION DmaWriteBytes;
-STATIC SKIP_BYTES_FUNCTION DmaSkipBytes;
//
// These correspond to the implementation we detect at runtime.
@@ -201,12 +197,6 @@ QemuFwCfgInitialize (
QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
Features = QemuFwCfgRead32 ();
- if ((Features & FW_CFG_F_DMA) != 0) {
- mFwCfgDmaAddress = FwCfgDmaAddress;
- InternalQemuFwCfgReadBytes = DmaReadBytes;
- InternalQemuFwCfgWriteBytes = DmaWriteBytes;
- InternalQemuFwCfgSkipBytes = DmaSkipBytes;
- }
}
} else {
mFwCfgSelectorAddress = 0;
@@ -291,91 +281,6 @@ MmioReadBytes (
}
}
-/**
- Transfer an array of bytes, or skip a number of bytes, using the DMA
- interface.
-
- @param[in] Size Size in bytes to transfer or skip.
-
- @param[in,out] Buffer Buffer to read data into or write data from. Ignored,
- and may be NULL, if Size is zero, or Control is
- FW_CFG_DMA_CTL_SKIP.
-
- @param[in] Control One of the following:
- FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
- FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer.
- FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg.
-**/
-STATIC
-VOID
-DmaTransferBytes (
- IN UINTN Size,
- IN OUT VOID *Buffer OPTIONAL,
- IN UINT32 Control
- )
-{
- volatile FW_CFG_DMA_ACCESS Access;
- UINT32 Status;
-
- ASSERT (
- Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ ||
- Control == FW_CFG_DMA_CTL_SKIP
- );
-
- if (Size == 0) {
- return;
- }
-
- ASSERT (Size <= MAX_UINT32);
-
- Access.Control = SwapBytes32 (Control);
- Access.Length = SwapBytes32 ((UINT32)Size);
- Access.Address = SwapBytes64 ((UINT64)(UINTN)Buffer);
-
- //
- // We shouldn't start the transfer before setting up Access.
- //
- MemoryFence ();
-
- //
- // This will fire off the transfer.
- //
- #if defined (MDE_CPU_AARCH64) || defined (MDE_CPU_RISCV64)
- MmioWrite64 (mFwCfgDmaAddress, SwapBytes64 ((UINT64)&Access));
- #else
- MmioWrite32 ((UINT32)(mFwCfgDmaAddress + 4), SwapBytes32 ((UINT32)&Access));
- #endif
-
- //
- // We shouldn't look at Access.Control before starting the transfer.
- //
- MemoryFence ();
-
- do {
- Status = SwapBytes32 (Access.Control);
- ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0);
- } while (Status != 0);
-
- //
- // The caller will want to access the transferred data.
- //
- MemoryFence ();
-}
-
-/**
- Fast READ_BYTES_FUNCTION.
-**/
-STATIC
-VOID
-EFIAPI
-DmaReadBytes (
- IN UINTN Size,
- IN VOID *Buffer OPTIONAL
- )
-{
- DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_READ);
-}
-
/**
Reads firmware configuration bytes into a buffer
@@ -418,20 +323,6 @@ MmioWriteBytes (
}
}
-/**
- Fast WRITE_BYTES_FUNCTION.
-**/
-STATIC
-VOID
-EFIAPI
-DmaWriteBytes (
- IN UINTN Size,
- IN VOID *Buffer OPTIONAL
- )
-{
- DmaTransferBytes (Size, Buffer, FW_CFG_DMA_CTL_WRITE);
-}
-
/**
Write firmware configuration bytes from a buffer
@@ -481,19 +372,6 @@ MmioSkipBytes (
}
}
-/**
- Fast SKIP_BYTES_FUNCTION.
-**/
-STATIC
-VOID
-EFIAPI
-DmaSkipBytes (
- IN UINTN Size
- )
-{
- DmaTransferBytes (Size, NULL, FW_CFG_DMA_CTL_SKIP);
-}
-
/**
Skip bytes in the firmware configuration item.
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 25c05a99ffec20d20a4d81d7cf31ccade08e2923..4285730ab75ea30006b9813b1f869e02249c7302 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -185,6 +185,7 @@
gEfiPeiMpInitLibUpDepPpiGuid = {0xb590774, 0xbc67, 0x49f4, { 0xa7, 0xdb, 0xe8, 0x2e, 0x89, 0xe6, 0xb5, 0xd6}}
[Protocols]
+ gEfiRealmApertureManagementProtocolGuid = { 0x585c00be, 0xcf7c, 0x4db8, { 0x8a, 0xa2, 0x49, 0xd, 0x67, 0xf5, 0xf6, 0xe6 } }
gVirtioDeviceProtocolGuid = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
gXenBusProtocolGuid = {0x3d3ca290, 0xb9a5, 0x11e3, {0xb7, 0x5d, 0xb8, 0xac, 0x6f, 0x7d, 0x65, 0xe6}}
gXenIoProtocolGuid = {0x6efac84f, 0x0ab0, 0x4747, {0x81, 0xbe, 0x85, 0x55, 0x62, 0x59, 0x04, 0x49}}
diff --git a/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.c b/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.c
index 2e341c680c77b903fc6ae24ea3a0c7398cef6d98..9e2779991d828575b0378fe4ca923120108b87f8 100644
--- a/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.c
+++ b/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.c
@@ -10,7 +10,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#include
#include
#include
-#include
#include
#include
#include
diff --git a/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.inf b/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.inf
index cecd7841ed442cd6d4aa9b54309fa3b2762ff2a2..c1002a8fc40397cec2e024957803e528991fd681 100644
--- a/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.inf
+++ b/SecurityPkg/Library/HashInstanceLibSha256/HashInstanceLibSha256.inf
@@ -36,6 +36,5 @@
BaseLib
BaseMemoryLib
DebugLib
- Tpm2CommandLib
MemoryAllocationLib
BaseCryptLib