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