diff --git a/ArmPkg/Include/Library/ArmMmuLib.h b/ArmPkg/Include/Library/ArmMmuLib.h
index 2ce948e8db1d..1cea71ee6e88 100644
--- a/ArmPkg/Include/Library/ArmMmuLib.h
+++ b/ArmPkg/Include/Library/ArmMmuLib.h
@@ -71,4 +71,59 @@ ArmSetMemoryAttributes (
IN UINT64 AttributeMask
);
+#ifdef MDE_CPU_AARCH64
+
+/**
+ 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.
+ - The caller must ensure that this function call is invoked by code
+ executing within the Realm.
+
+ @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 EFI_SUCCESS IPA protection attribute updated.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_UNSUPPORTED RME is not supported.
+**/
+EFI_STATUS
+EFIAPI
+SetMemoryProtectionAttribute (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 IpaWidth,
+ IN BOOLEAN Share
+ );
+
+#endif
+
#endif // ARM_MMU_LIB_H_
diff --git a/ArmPkg/Include/Library/ArmSmcLib.h b/ArmPkg/Include/Library/ArmSmcLib.h
index beef0175c35c..e80b74671a64 100644
--- a/ArmPkg/Include/Library/ArmSmcLib.h
+++ b/ArmPkg/Include/Library/ArmSmcLib.h
@@ -1,10 +1,13 @@
/** @file
*
* Copyright (c) 2021, NUVIA Inc. All rights reserved.
-* Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+* Copyright (c) 2012-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*
+* @par Reference(s):
+* - SMC Calling Convention (SMCCC), ARM DEN 0028E, EAC0, 1.4
+* (https://developer.arm.com/documentation/den0028/e/)
**/
#ifndef ARM_SMC_LIB_H_
@@ -13,6 +16,18 @@
/**
* The size of the SMC arguments are different between AArch64 and AArch32.
* The native size is used for the arguments.
+ * According to the SMCCC Section 2.6 SMC32/HVC32 argument passing
+ * When an SMC32/HVC32 call is made from AArch32:
+ * - Arguments are passed in registers R1-R7.
+ * - Results are returned in R0-R7.
+ * When an SMC32/HVC32 call is made from AArch64:
+ * - Arguments are passed in registers W1-W7.
+ * - Results are returned in W0-W7.
+ *
+ * According to SMCCC Section 2.7 SMC64/HVC64 argument passing
+ * When an SMC64/HVC64 call is made from AArch64:
+ * - Arguments are passed in registers X1-X17.
+ * - Results are returned in X0-X17.
*/
typedef struct {
UINTN Arg0;
@@ -23,13 +38,42 @@ typedef struct {
UINTN Arg5;
UINTN Arg6;
UINTN Arg7;
+ #ifdef MDE_CPU_AARCH64
+ UINTN Arg8;
+ UINTN Arg9;
+ UINTN Arg10;
+ UINTN Arg11;
+ #endif
} ARM_SMC_ARGS;
/**
Trigger an SMC call
- SMC calls can take up to 7 arguments and return up to 4 return values.
- Therefore, the 4 first fields in the ARM_SMC_ARGS structure are used
+ According to the SMCCC Section 2.6 SMC32/HVC32 argument passing
+ When an SMC32/HVC32 call is made from AArch32:
+ - Arguments are passed in registers R1-R7.
+ - Results are returned in R0-R7.
+ When an SMC32/HVC32 call is made from AArch64:
+ - Arguments are passed in registers W1-W7.
+ - Results are returned in W0-W7.
+
+ According to SMCCC Section 2.7 SMC64/HVC64 argument passing
+ When an SMC64/HVC64 call is made from AArch64:
+ - Arguments are passed in registers X1-X17.
+ - Results are returned in X0-X17.
+
+ This means SMC calls can take up to 7/17 arguments and return up
+ to 7/17 return values.
+
+ However, the current use-case:
+ - For SMC32/HVC32 calls made from AArch32/AArch64 up to 7 arguments
+ and 4 return values are required. Therefore, limit the maximum
+ arguments to 7 and return values to 4.
+ - For AMC64/HVC64 calls made from AArch64 up to 11 arguments and
+ return values are required. Therefore, limit the maximum arguments
+ and return values to 11.
+
+ The fields in the ARM_SMC_ARGS structure are used
for both input and output values.
**/
diff --git a/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.c b/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.c
index 53b202ff34a8..bed2ca24fe3e 100644
--- a/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.c
+++ b/ArmPkg/Library/ArmLib/AArch64/AArch64Lib.c
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
- Portions copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Portions copyright (c) 2011 - 2023, Arm Limited. All rights reserved.
Copyright (c) 2021, NUVIA Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -95,3 +95,17 @@ ArmHasEte (
// The ID_AA64DFR0_EL1.TraceVer field identifies the presence of FEAT_ETE.
return ((ArmReadIdAA64Dfr0 () & AARCH64_DFR0_TRACEVER) != 0);
}
+
+/** Checks if RME is implemented.
+
+ @retval TRUE RME is implemented.
+ @retval FALSE RME is not implemented.
+**/
+BOOLEAN
+EFIAPI
+ArmHasRme (
+ VOID
+ )
+{
+ return ((ArmReadIdAA64Pfr0 () & AARCH64_PFR0_RME) != 0);
+}
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index 45b7c8134165..ace042433409 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -7,6 +7,10 @@
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*
+* @par Glossary:
+* - Rsi or RSI - Realm Service Interface
+* - IPA - Intermediate Physical Address
+* - RIPAS - Realm IPA state
**/
#include
@@ -773,3 +777,89 @@ ArmMmuBaseLibConstructor (
return RETURN_SUCCESS;
}
+
+/**
+ 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.
+ - The caller must ensure that this function call is invoked by code
+ executing within the Realm.
+
+ @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 EFI_SUCCESS IPA protection attribute updated.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_UNSUPPORTED RME is not supported.
+**/
+EFI_STATUS
+EFIAPI
+SetMemoryProtectionAttribute (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 IpaWidth,
+ IN BOOLEAN Share
+ )
+{
+ UINT64 Attributes;
+ UINT64 Mask;
+ UINT64 ProtectionAttributeMask;
+
+ if ((Length == 0) || (IpaWidth == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!ArmHasRme ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ /* Software in a Realm should treat the most significant bit of an
+ IPA as a protection attribute.
+ */
+ ProtectionAttributeMask = 1ULL << (IpaWidth - 1);
+
+ if (Share) {
+ Attributes = ProtectionAttributeMask;
+ Mask = ~TT_ADDRESS_MASK_BLOCK_ENTRY;
+ } else {
+ Attributes = 0;
+ Mask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | ProtectionAttributeMask);
+ }
+
+ return UpdateRegionMapping (
+ BaseAddress,
+ Length,
+ Attributes,
+ Mask,
+ ArmGetTTBR0BaseAddress (),
+ TRUE
+ );
+}
diff --git a/ArmPkg/Library/ArmSmcLib/AArch64/ArmSmc.S b/ArmPkg/Library/ArmSmcLib/AArch64/ArmSmc.S
index a4443a41fe6c..da14f5021090 100644
--- a/ArmPkg/Library/ArmSmcLib/AArch64/ArmSmc.S
+++ b/ArmPkg/Library/ArmSmcLib/AArch64/ArmSmc.S
@@ -1,8 +1,11 @@
//
-// Copyright (c) 2012-2014, ARM Limited. All rights reserved.
+// Copyright (c) 2012-2023, Arm Limited. All rights reserved.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
+// @par Reference(s):
+// - SMC Calling Convention (SMCCC), ARM DEN 0028E, EAC0, 1.4
+// (https://developer.arm.com/documentation/den0028/e/)
//
#include
@@ -12,6 +15,8 @@ ASM_FUNC(ArmCallSmc)
str x0, [sp, #-16]!
// Load the SMC arguments values into the appropriate registers
+ ldp x10, x11, [x0, #80]
+ ldp x8, x9, [x0, #64]
ldp x6, x7, [x0, #48]
ldp x4, x5, [x0, #32]
ldp x2, x3, [x0, #16]
@@ -19,14 +24,17 @@ ASM_FUNC(ArmCallSmc)
smc #0
- // Pop the ARM_SMC_ARGS structure address from the stack into x9
- ldr x9, [sp], #16
+ // Pop the ARM_SMC_ARGS structure address from the stack into x13
+ ldr x13, [sp], #16
// Store the SMC returned values into the ARM_SMC_ARGS structure.
- // A SMC call can return up to 4 values - we do not need to store back x4-x7.
- stp x2, x3, [x9, #16]
- stp x0, x1, [x9, #0]
-
- mov x0, x9
+ stp x10, x11, [x13, #80]
+ stp x8, x9, [x13, #64]
+ stp x6, x7, [x13, #48]
+ stp x4, x5, [x13, #32]
+ stp x2, x3, [x13, #16]
+ stp x0, x1, [x13, #0]
+
+ mov x0, x13
ret
diff --git a/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.c b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.c
new file mode 100644
index 000000000000..8104bb4dbc10
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmu.c
@@ -0,0 +1,869 @@
+/** @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);
+
+/** ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.
+*/
+STATIC CONST CHAR8 *CONST
+mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {
+ "Read",
+ "Write",
+ "CommonBuffer",
+ "Read64",
+ "Write64",
+ "CommonBuffer64"
+};
+
+/** 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_INVALID_PARAMETER A parameter was invalid.
+ @retval EFI_UNSUPPORTED The requested operation is not supported.
+ @retval EFI_SUCCESS Success.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuSetAttribute (
+ IN EDKII_IOMMU_PROTOCOL *This,
+ IN EFI_HANDLE DeviceHandle,
+ IN VOID *Mapping,
+ IN UINT64 IoMmuAccess
+ )
+{
+ EFI_STATUS Status;
+ MAP_INFO *MapInfo;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Mapping=0x%p Access=%lu\n",
+ __func__,
+ Mapping,
+ IoMmuAccess
+ ));
+
+ if (Mapping == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+
+ // An IoMmuAccess value of 0 is always accepted,
+ // validate any non-zero value.
+ if (IoMmuAccess != 0) {
+ MapInfo = (MAP_INFO *)Mapping;
+
+ // The mapping operation already implied the access mode.
+ // Validate that the supplied access mode matches operation
+ // access mode.
+ switch (MapInfo->Operation) {
+ case EdkiiIoMmuOperationBusMasterRead:
+ case EdkiiIoMmuOperationBusMasterRead64:
+ if (IoMmuAccess != EDKII_IOMMU_ACCESS_READ) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case EdkiiIoMmuOperationBusMasterWrite:
+ case EdkiiIoMmuOperationBusMasterWrite64:
+ if (IoMmuAccess != EDKII_IOMMU_ACCESS_WRITE) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ break;
+
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+ if (IoMmuAccess !=
+ (EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE))
+ {
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+ Status = EFI_UNSUPPORTED;
+ } // switch
+ }
+
+ return Status;
+}
+
+/** 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 000000000000..070f7bebf5bf
--- /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 000000000000..deba9dd5e720
--- /dev/null
+++ b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.c
@@ -0,0 +1,59 @@
+/** @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;
+
+ // 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 ();
+ } else {
+ DEBUG ((DEBUG_INFO, "Execution context is not a Realm.\n"));
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gIoMmuAbsentProtocolGuid,
+ NULL,
+ NULL
+ );
+ }
+
+ return Status;
+}
diff --git a/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf b/ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
new file mode 100644
index 000000000000..b8e125296f4d
--- /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/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index cf0aba6f2b6f..784963617223 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2011 - 2022, ARM Limited. All rights reserved.
+# Copyright (c) 2011 - 2023, ARM Limited. All rights reserved.
# Copyright (c) 2014, Linaro Limited. All rights reserved.
# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
# Copyright (c) Microsoft Corporation.
@@ -168,6 +168,8 @@
ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf
+ ArmCcaLib|ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.inf
+
[LibraryClasses.common.SEC]
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
@@ -182,6 +184,8 @@
DebugLib|ArmVirtPkg/Library/DebugLibFdtPL011Uart/DebugLibFdtPL011UartFlash.inf
!endif
+ ArmCcaInitPeiLib|ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.inf
+
[LibraryClasses.common.PEI_CORE]
PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
diff --git a/ArmVirtPkg/ArmVirtKvmTool.dsc b/ArmVirtPkg/ArmVirtKvmTool.dsc
index 6565ea35c124..f0f94bc52c4d 100644
--- a/ArmVirtPkg/ArmVirtKvmTool.dsc
+++ b/ArmVirtPkg/ArmVirtKvmTool.dsc
@@ -87,6 +87,13 @@
ArmMonitorLib|ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.inf
ArmTrngLib|ArmPkg/Library/ArmTrngLib/ArmTrngLib.inf
+ ArmPlatformDeviceInfoLib|ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/ArmPlatformDeviceInfoLib.inf
+
+[LibraryClasses.AARCH64]
+ ArmCcaLib|ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf
+ ArmCcaRsiLib|ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf
+ RngLib|MdePkg/Library/BaseRngLib/BaseRngLib.inf
+
[LibraryClasses.common.SEC, LibraryClasses.common.PEI_CORE, LibraryClasses.common.PEIM]
PciExpressLib|MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf
PlatformHookLib|ArmVirtPkg/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
@@ -100,6 +107,9 @@
DebugLib|MdePkg/Library/DxeRuntimeDebugLibSerialPort/DxeRuntimeDebugLibSerialPort.inf
!endif
+[LibraryClasses.AARCH64.SEC, LibraryClasses.AARCH64.PEI_CORE, LibraryClasses.AARCH64.PEIM]
+ ArmCcaInitPeiLib|ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf
+
[LibraryClasses.common.UEFI_DRIVER]
UefiScsiLib|MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
@@ -243,6 +253,7 @@
#
ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf {
+ DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
ExtractGuidedSectionLib|EmbeddedPkg/Library/PrePiExtractGuidedSectionLib/PrePiExtractGuidedSectionLib.inf
NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
PrePiLib|EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
@@ -388,9 +399,18 @@
#
SecurityPkg/RandomNumberGenerator/RngDxe/RngDxe.inf
-!if $(ARCH) == AARCH64
+[Components.AARCH64]
#
# ACPI Support
#
ArmVirtPkg/KvmtoolCfgMgrDxe/ConfigurationManagerDxe.inf
-!endif
+
+ #
+ # Realm Aperture Management
+ #
+ ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf
+
+ #
+ # IoMMU support for Arm CCA
+ #
+ ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
diff --git a/ArmVirtPkg/ArmVirtKvmTool.fdf b/ArmVirtPkg/ArmVirtKvmTool.fdf
index cdf756c11289..07d86f649e01 100644
--- a/ArmVirtPkg/ArmVirtKvmTool.fdf
+++ b/ArmVirtPkg/ArmVirtKvmTool.fdf
@@ -212,6 +212,16 @@ READ_LOCK_STATUS = TRUE
!include DynamicTablesPkg/DynamicTables.fdf.inc
INF ArmVirtPkg/KvmtoolCfgMgrDxe/ConfigurationManagerDxe.inf
+
+ #
+ # Realm Aperture Management
+ #
+ INF ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.inf
+
+ #
+ # IoMMU support for Arm CCA
+ #
+ INF ArmVirtPkg/ArmCcaIoMmuDxe/ArmCcaIoMmuDxe.inf
!endif
#
diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
index 6aa5ea05f4e9..52b2dddbfcdb 100644
--- a/ArmVirtPkg/ArmVirtPkg.dec
+++ b/ArmVirtPkg/ArmVirtPkg.dec
@@ -26,6 +26,10 @@
Include # Root include for the package
[LibraryClasses]
+ ArmCcaInitPeiLib|Include/Library/ArmCcaInitPeiLib.h
+ ArmCcaLib|Include/Library/ArmCcaLib.h
+ ArmCcaRsiLib|Include/Library/ArmCcaRsiLib.h
+ ArmPlatformDeviceInfoLib|Include/Library/ArmPlatformDeviceInfoLib.h
ArmVirtMemInfoLib|Include/Library/ArmVirtMemInfoLib.h
[Guids.common]
@@ -33,6 +37,7 @@
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 } }
[PcdsFeatureFlag]
#
@@ -40,9 +45,18 @@
#
gArmVirtTokenSpaceGuid.PcdTpm2SupportEnabled|FALSE|BOOLEAN|0x00000004
+[Protocols]
+ gEfiRealmApertureManagementProtocolGuid = { 0x585c00be, 0xcf7c, 0x4db8, { 0x8a, 0xa2, 0x49, 0xd, 0x67, 0xf5, 0xf6, 0xe6 } }
+
[PcdsFixedAtBuild, PcdsPatchableInModule]
##
# This is the physical address of Rsdp which is the core struct of Acpi.
# Cloud Hypervisor has no other way to pass Rsdp address to the guest except use a PCD.
#
gArmVirtTokenSpaceGuid.PcdCloudHvAcpiRsdpBaseAddress|0x0|UINT64|0x00000005
+
+[PcdsFixedAtBuild]
+ #
+ # Maximum number of platform devices that can be discovered from a FDT.
+ #
+ gArmVirtTokenSpaceGuid.PcdMaxPlatDeviceCount|0x10|UINT64|0x00000006
diff --git a/ArmVirtPkg/Include/Library/ArmCcaInitPeiLib.h b/ArmVirtPkg/Include/Library/ArmCcaInitPeiLib.h
new file mode 100644
index 000000000000..439a70a54a21
--- /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 000000000000..32ed28495497
--- /dev/null
+++ b/ArmVirtPkg/Include/Library/ArmCcaLib.h
@@ -0,0 +1,136 @@
+/** @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
+ );
+
+/**
+ Return the IPA width of the Realm.
+
+ The IPA width of the Realm is used to configure the protection attribute
+ for memory regions, see ArmCcaSetMemoryProtectAttribute().
+
+ The IPA width of the Realm is present in the Realm config which is read
+ when the ArmCcaInitPeiLib library hook function ArmCcaInitialize () is
+ called in the PrePi phase. ArmCcaInitialize () stores the IPA width of
+ the Realm in a GUID HOB gArmCcaIpaWidthGuid.
+
+ This function searches the GUID HOB gArmCcaIpaWidthGuid and returns the
+ IPA width value stored therein.
+
+ Note:
+ - This function must only be called after ArmCcaInitialize () has setup
+ the GUID HOB gArmCcaIpaWidthGuid.
+
+ @param [out] IpaWidth IPA width of the Realm.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_NOT_FOUND The GUID HOB gArmCcaIpaWidthGuid is not
+ found and could mean that this function
+ was called before ArmCcaInitialize ()
+ has created and initialised the GUID
+ HOB gArmCcaIpaWidthGuid.
+**/
+RETURN_STATUS
+EFIAPI
+GetIpaWidth (
+ OUT UINT64 *IpaWidth
+ );
+
+/** Check if the address range is protected MMIO
+
+ @param [in] BaseAddress Base address of the device MMIO region.
+ @param [in] Length Length of the device MMIO region.
+ @param [out] IsProtectedMmio TRUE - if the RIPAS for the address range is
+ protected MMIO.
+ FALSE - if the RIPAS for the address range is
+ not protected MMIO.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The request is not initiated in a
+ Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaMemRangeIsProtectedMmio (
+ IN UINT64 BaseAddress,
+ IN UINT64 Length,
+ OUT BOOLEAN *IsProtectedMmio
+ );
+
+#endif // ARM_CCA_LIB_
diff --git a/ArmVirtPkg/Include/Library/ArmCcaRsiLib.h b/ArmVirtPkg/Include/Library/ArmCcaRsiLib.h
new file mode 100644
index 000000000000..3b389847eb42
--- /dev/null
+++ b/ArmVirtPkg/Include/Library/ArmCcaRsiLib.h
@@ -0,0 +1,407 @@
+/** @file
+ Library that implements the Arm CCA Realm Service Interface calls.
+
+ Copyright (c) 2022 - 2024, 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-rel0
+ (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
+
+/**
+ A macro defining the mask for the RSI RIPAS type.
+ See Section B4.4.5 RsiRipas type, RMM Specification, version A-bet0.
+*/
+#define RIPAS_TYPE_MASK 0xFF
+
+/* Maximum challenge data size in bits.
+*/
+#define MAX_CHALLENGE_DATA_SIZE_BITS 512
+
+/* Minimum recommended challenge data size in bits.
+*/
+#define MIN_CHALLENGE_DATA_SIZE_BITS 256
+
+/* 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
+
+/* The values of the RsiHashAlgorithm enumeration.
+ SHA-256 (Secure Hash Standard (SHS))
+*/
+#define RSI_HASH_SHA_256 0
+
+/* The values of the RsiHashAlgorithm enumeration.
+ SHA-512 (Secure Hash Standard (SHS))
+*/
+#define RSI_HASH_SHA_512 1
+
+/* The RsiRipasChangeFlags fieldset contains flags provided by
+ the Realm when requesting a RIPAS change.
+ See section B4.4.8 RsiRipasChangeFlags type in the
+ RMM Specification, version 1.0-eac2.
+ The following macros prefixed RIPAS_CHANGE_FLAGS_xxx
+ define the values of the RsiRipasChangeFlags fieldset.
+*/
+
+/* A RIPAS change from DESTROYED should not be permitted.
+ See section B4.4.7 RsiRipasChangeDestroyed type in the
+ RMM Specification, version 1.0-eac2
+*/
+#define RIPAS_CHANGE_FLAGS_RSI_NO_CHANGE_DESTROYED 0
+
+/* A RIPAS change from DESTROYED should be permitted.
+ See section B4.4.7 RsiRipasChangeDestroyed type in the
+ RMM Specification, version 1.0-eac2
+*/
+#define RIPAS_CHANGE_FLAGS_RSI_CHANGE_DESTROYED 1
+
+/* The RsiResponse type is a value returned by the
+ RSI_IPA_STATE_SET command and represents whether
+ the Host accepted or rejected a Realm request.
+ See section B4.4.6 RsiResponse type in the
+ RMM Specification, version 1.0-eac3.
+ The width of the RsiResponse enumeration is 1 bit
+ and the following macros prefixed RIPAS_CHANGE_RESPONSE_xxx
+ define the values of the RsiResponse type.
+*/
+
+/* The RIPAS change request to RAM was accepted
+ by the host.
+*/
+#define RIPAS_CHANGE_RESPONSE_ACCEPT 0
+
+/* The RIPAS change request to RAM was rejected
+ by the host.
+*/
+#define RIPAS_CHANGE_RESPONSE_REJECT 1
+
+/* A mask for the RSI Response bit */
+#define RSI_RESPONSE_MASK BIT0
+
+/** An enum describing the RSI RIPAS.
+ See Section A5.2.2 Realm IPA state, RMM Specification, version 1.0-rel0
+*/
+typedef enum Ripas {
+ RipasEmpty, ///< Unused IPA location.
+ RipasRam, ///< Private code or data owned by the Realm.
+ RipasDestroyed, ///< An address which is inaccessible to the Realm.
+ RipasDev, ///< MMIO address where an assigned Realm device is mapped.
+ RipasMax ///< A valid RIPAS type value is less than RipasMax.
+} RIPAS;
+
+/* The maximum length of the Realm Personalisation Value (RPV).
+*/
+#define REALM_CFG_RPV_SIZE 64
+
+/* The size of the Realm Config is 4KB.
+*/
+#define REALM_CFG_SIZE SIZE_4KB
+
+/* Helper macros to define the RealmConfig structure.
+*/
+#define REALM_CFG_OFFSET_IPA_WIDTH 0
+#define REALM_CFG_OFFSET_HASH_ALGO (REALM_CFG_OFFSET_IPA_WIDTH + sizeof (UINT64))
+#define REALM_CFG_OFFSET_RESERVED (REALM_CFG_OFFSET_HASH_ALGO + sizeof (UINT8))
+#define REALM_CFG_OFFSET_RPV 0x200
+#define REALM_CFG_OFFSET_RESERVED1 (REALM_CFG_OFFSET_RPV + REALM_CFG_RPV_SIZE)
+
+#pragma pack(1)
+
+/** A structure describing the Realm Configuration.
+ See Section B5.4.5 RsiRealmConfig type, RMM Specification, version 1.0-rel0
+ The width of the RsiRealmConfig structure is 4096 (0x1000) bytes.
+*/
+typedef struct RealmConfig {
+ /// Width of IPA in bits.
+ UINT64 IpaWidth;
+ /// Width of the RsiHashAlgorithm enumeration is 8 bits.
+ UINT8 HashAlgorithm;
+ /// Reserved
+ UINT8 Reserved[REALM_CFG_OFFSET_RPV - REALM_CFG_OFFSET_RESERVED];
+ /// Realm Personalisation Value
+ UINT8 Rpv[REALM_CFG_RPV_SIZE];
+ /// Unused bits of the RsiRealmConfig structure should be zero.
+ UINT8 Reserved1[REALM_CFG_SIZE - REALM_CFG_OFFSET_RESERVED1];
+} REALM_CONFIG;
+
+/** A structure describing the Host Call arguments
+ See Section 4.4.2 RsiHostCall type, RMM Specification, version 1.0-bet2
+*/
+typedef struct HostCallArgs {
+ UINT16 Imm;
+ UINT8 Reserved1[6];
+
+ UINT64 Gprs0;
+ UINT64 Gprs1;
+ UINT64 Gprs2;
+ UINT64 Gprs3;
+ UINT64 Gprs4;
+ UINT64 Gprs5;
+ UINT64 Gprs6;
+ UINT64 Gprs7;
+ UINT64 Gprs8;
+ UINT64 Gprs9;
+ UINT64 Gprs10;
+ UINT64 Gprs11;
+ UINT64 Gprs12;
+ UINT64 Gprs13;
+ UINT64 Gprs14;
+ UINT64 Gprs15;
+ UINT64 Gprs16;
+ UINT64 Gprs17;
+ UINT64 Gprs18;
+ UINT64 Gprs19;
+ UINT64 Gprs20;
+ UINT64 Gprs21;
+ UINT64 Gprs22;
+ UINT64 Gprs23;
+ UINT64 Gprs24;
+ UINT64 Gprs25;
+ UINT64 Gprs26;
+ UINT64 Gprs27;
+ UINT64 Gprs28;
+ UINT64 Gprs29;
+ UINT64 Gprs30;
+} HOST_CALL_ARGS;
+
+#pragma pack()
+
+/**
+ Retrieve an attestation token from the RMM.
+
+ @param [in] ChallengeData Pointer to the challenge data to be
+ included in the attestation token.
+ @param [in] ChallengeDataSizeBits Size of the challenge data in bits.
+ @param [out] TokenBuffer Pointer to a buffer to store the
+ retrieved attestation token.
+ @param [out] TokenBufferSize Length of token data returned.
+
+ Note: The TokenBuffer allocated must be freed by the caller
+ using RsiFreeAttestationToken().
+
+ @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.
+ Or the Token generation failed for an
+ unknown or IMPDEF reason.
+ @retval RETURN_NOT_READY The operation requested by the command
+ is not complete.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetAttestationToken (
+ IN CONST UINT8 *CONST ChallengeData,
+ IN UINT64 ChallengeDataSizeBits,
+ OUT UINT8 **CONST TokenBuffer,
+ OUT UINT64 *CONST TokenBufferSize
+ );
+
+/**
+ Free the attestation token buffer.
+
+ @param [in] TokenBuffer Pointer to the retrieved
+ attestation token.
+ @param [in] TokenBufferSize Size of the token buffer.
+**/
+VOID
+RsiFreeAttestationToken (
+ IN UINT8 *CONST TokenBuffer,
+ IN UINT64 CONST TokenBufferSize
+ );
+
+/**
+ Returns the IPA state for the page pointed by the address.
+
+ @param [in] Base Base of target IPA region.
+ @param [in, out] Top End of target IPA region on input.
+ Top of IPA region which has the
+ reported RIPAS value on return.
+ @param [out] State The RIPAS state for the address specified.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetIpaState (
+ IN UINT64 *Base,
+ IN OUT UINT64 **Top,
+ OUT RIPAS *State
+ );
+
+/**
+ Sets the IPA state for the pages pointed by the memory range.
+
+ @param [in] Address Address to the start of the memory range.
+ @param [in] Size Length of the memory range.
+ @param [in] State The RIPAS state to be configured.
+ @param [in] Flags The RIPAS change flags.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_ACCESS_DENIED RIPAS change request was rejected.
+**/
+RETURN_STATUS
+EFIAPI
+RsiSetIpaState (
+ IN UINT64 *Address,
+ IN UINT64 Size,
+ IN RIPAS State,
+ IN UINT64 Flags
+ );
+
+/**
+ 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
+ );
+
+/**
+ Read the measurement value from a REM.
+
+ @param [in] MeasurementIndex Index of the REM.
+ @param [out] MeasurementBuffer Pointer to store the measurement data.
+ @param [in] MeasurementBufferSize Size of the measurement buffer.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiReadMeasurement (
+ IN UINTN MeasurementIndex,
+ OUT UINT8 *CONST MeasurementBuffer,
+ IN UINTN MeasurementBufferSize
+ );
+
+/**
+ Read the Realm Configuration.
+
+ @param [out] Config Pointer to the address of the buffer to retrieve
+ the Realm configuration.
+
+ Note: The buffer to retrieve the Realm configuration must be aligned to the
+ Realm granule size.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetRealmConfig (
+ IN REALM_CONFIG *Config
+ );
+
+/**
+ Make a Host Call.
+
+ A Host call can be used by a Realm to make a hypercall.
+ On Realm execution of HVC, an Unknown exception is taken to the Realm.
+
+ @param [in] Args Pointer to the IPA of the Host call data
+ structure.
+
+ Note: The IPA of the Host call arguments data structure must be aligned
+ to the Realm granule size.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiHostCall (
+ IN HOST_CALL_ARGS *Args
+ );
+
+/**
+ Get the version of the RSI implementation.
+
+ @param [out] UefiImpl The version of the RSI specification
+ implemented by the UEFI firmware.
+ @param [out] RmmImplLow The low version of the RSI specification
+ implemented by the RMM.
+ @param [out] RmmImplHigh The high 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 RmmImplLow,
+ OUT UINT32 *CONST RmmImplHigh
+ );
+
+/**
+ Get the features supported by the RSI implementation.
+
+ RMM implementations across different CCA platforms may support
+ disparate features and may offer disparate configuration options
+ for Realms. The features supported by an RSI implementation are
+ discovered by reading feature pseudo-register values using the
+ RSI_FEATURES command.
+
+ @param [in] FeatureRegIndex The Feature Register Index.
+ @param [out] FeatureRegValue The Feature Register Value.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetFeatures (
+ IN UINT64 FeatureRegIndex,
+ OUT UINT64 *FeatureRegValue
+ );
+
+#endif // ARM_CCA_RSI_LIB_
diff --git a/ArmVirtPkg/Include/Library/ArmPlatformDeviceInfoLib.h b/ArmVirtPkg/Include/Library/ArmPlatformDeviceInfoLib.h
new file mode 100644
index 000000000000..ea713884ef51
--- /dev/null
+++ b/ArmVirtPkg/Include/Library/ArmPlatformDeviceInfoLib.h
@@ -0,0 +1,58 @@
+/** @file
+ Arm Platform Device Info Library
+
+ Copyright (c) 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef ARM_PLATFORM_DEVICE_INFO_LIB_H_
+#define ARM_PLATFORM_DEVICE_INFO_LIB_H_
+
+#include
+#include
+
+/* Maximum number of platform devices that can be discovered from a FDT.
+*/
+#define MAX_PLAT_DEVICE_COUNT FixedPcdGet64 (PcdMaxPlatDeviceCount)
+
+/** A DEVICE_INFO structure that contains a brief description of the
+ device and the Base address and range.
+**/
+typedef struct DeviceInfo {
+ /// Brief description of the device.
+ CHAR8 Desc[16];
+ /// The MMIO Base address.
+ UINT64 BaseAddress;
+ /// The base address range.
+ UINT64 Length;
+} DEVICE_INFO;
+
+/** A structure that contains the information about the devices available
+ on a platform.
+**/
+typedef struct PlatDevInfo {
+ /// An array containing the information about the platform devices.
+ DEVICE_INFO Dev[MAX_PLAT_DEVICE_COUNT];
+ /// The max count of devices.
+ UINTN MaxDevices;
+} PLATFROM_DEVICE_INFO;
+
+/** Parse the platform FDT and populate the platform device info.
+
+ @param [in] FdtBase Pointer to the platform FDT.
+ @param [in] PlatInfo Pointer to the platform device info to populate.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND Parser did not find any device information
+ in the FDT.
+**/
+EFI_STATUS
+EFIAPI
+ArmParsePlatformDeviceFdt (
+ IN VOID *FdtBase,
+ IN PLATFROM_DEVICE_INFO *PlatInfo
+ );
+
+#endif // ARM_PLATFORM_DEVICE_INFO_LIB_H_
diff --git a/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h b/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h
index 7812c2e28657..b70a96ed923e 100644
--- a/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h
+++ b/ArmVirtPkg/Include/Library/ArmVirtMemInfoLib.h
@@ -1,6 +1,6 @@
/** @file
- Copyright (c) 2011-2013, ARM Limited. All rights reserved.
+ Copyright (c) 2011-2023, Arm Limited. All rights reserved.
Copyright (c) 2017, Linaro, Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -32,4 +32,21 @@ 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 000000000000..0f45fd296fd5
--- /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/KvmtoolCfgMgrDxe/ConfigurationManager.c b/ArmVirtPkg/KvmtoolCfgMgrDxe/ConfigurationManager.c
index 96fbea33350d..1574979432d3 100644
--- a/ArmVirtPkg/KvmtoolCfgMgrDxe/ConfigurationManager.c
+++ b/ArmVirtPkg/KvmtoolCfgMgrDxe/ConfigurationManager.c
@@ -1,7 +1,7 @@
/** @file
Configuration Manager Dxe
- Copyright (c) 2021 - 2022, Arm Limited. All rights reserved.
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -699,6 +699,22 @@ GetStandardNameSpaceObject (
// IORT is only required for GicV3/4
//
AcpiTableCount -= 1;
+ } else {
+ //
+ // Check if we have support for ITS.
+ //
+ Status = DynamicPlatRepoGetObject (
+ PlatformRepo->DynamicPlatformRepo,
+ CREATE_CM_ARM_OBJECT_ID (EArmObjGicItsInfo),
+ CM_NULL_TOKEN,
+ &CmObjDesc
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // IORT is only required for GicV3/4 if ITS is present.
+ //
+ AcpiTableCount -= 1;
+ }
}
Status = HandleCmObject (
diff --git a/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.c b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.c
new file mode 100644
index 000000000000..e59a990da425
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.c
@@ -0,0 +1,117 @@
+/** @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
+#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_STATUS Status;
+
+ if (!IsRealm ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ Status = RsiSetIpaState (
+ (UINT64 *)PcdGet64 (PcdSystemMemoryBase),
+ PcdGet64 (PcdSystemMemorySize),
+ RipasRam,
+ RIPAS_CHANGE_FLAGS_RSI_NO_CHANGE_DESTROYED
+ );
+ if (RETURN_ERROR (Status)) {
+ // Panic
+ CpuDeadLoop ();
+ }
+
+ return Status;
+}
+
+/**
+ 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
+ )
+{
+ EFI_STATUS Status;
+ REALM_CONFIG *Config;
+ UINT64 *IpaWidthHobData;
+
+ if (!IsRealm ()) {
+ // Noting to do as the execution context is not a Realm.
+ return RETURN_SUCCESS;
+ }
+
+ // Read the Realm Config and store the IPA width in a GUID HOB.
+ Config = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (REALM_CONFIG)));
+ if (Config == NULL) {
+ ASSERT (0);
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (Config, sizeof (REALM_CONFIG));
+
+ Status = RsiGetRealmConfig (Config);
+ if (RETURN_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ IpaWidthHobData = BuildGuidHob (
+ &gArmCcaIpaWidthGuid,
+ sizeof (*IpaWidthHobData)
+ );
+ if (IpaWidthHobData == NULL) {
+ ASSERT (0);
+ FreePages (Config, EFI_SIZE_TO_PAGES (sizeof (REALM_CONFIG)));
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ *IpaWidthHobData = Config->IpaWidth;
+
+ FreePages (Config, EFI_SIZE_TO_PAGES (sizeof (REALM_CONFIG)));
+
+ // Configure the MMIO memory regions.
+ return ArmCcaConfigureMmio (*IpaWidthHobData);
+}
diff --git a/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf
new file mode 100644
index 000000000000..f2a321d9cdfc
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaInitPeiLib/ArmCcaInitPeiLib.inf
@@ -0,0 +1,39 @@
+## @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
+ ArmCcaRsiLib
+ ArmLib
+ ArmMmuLib
+ ArmVirtMemInfoLib
+ BaseLib
+
+[Pcd]
+ gArmTokenSpaceGuid.PcdSystemMemoryBase
+ gArmTokenSpaceGuid.PcdSystemMemorySize
+
+[Guids]
+ gArmCcaIpaWidthGuid
diff --git a/ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.c b/ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.c
new file mode 100644
index 000000000000..5b606208dbcf
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.c
@@ -0,0 +1,59 @@
+/** @file
+ Library that implements a NULL implementation of the ArmCcaInitPeiLib.
+
+ 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
+
+/**
+ 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_UNSUPPORTED;
+}
+
+/**
+ Perform Arm CCA specific initialisations.
+
+ @retval EFI_SUCCESS Success or execution context is not a Realm.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+EFI_STATUS
+EFIAPI
+ArmCcaInitialize (
+ VOID
+ )
+{
+ // Noting to do as the execution context is not a Realm.
+ return EFI_SUCCESS;
+}
diff --git a/ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.inf b/ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.inf
new file mode 100644
index 000000000000..f039c7abdb6d
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaInitPeiLibNull/ArmCcaInitPeiLibNull.inf
@@ -0,0 +1,27 @@
+## @file
+# Library that implements a NULL implementation of the ArmCcaInitPeiLib.
+#
+# 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 = 60686C60-8433-49EE-9F2C-DDC424A95652
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmCcaInitPeiLib
+
+[Sources]
+ ArmCcaInitPeiLibNull.c
+
+[Packages]
+ ArmPkg/ArmPkg.dec
+ ArmVirtPkg/ArmVirtPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
diff --git a/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.c b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.c
new file mode 100644
index 000000000000..e6ca1b889ea8
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.c
@@ -0,0 +1,243 @@
+/** @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 RmmImplLow;
+ UINT32 RmmImplHigh;
+ STATIC BOOLEAN RealmWorld = FALSE;
+ STATIC BOOLEAN FlagsInitialised = FALSE;
+
+ if (!FlagsInitialised) {
+ FlagsInitialised = TRUE;
+ if (ArmHasRme ()) {
+ Status = RsiGetVersion (
+ &UefiImpl,
+ &RmmImplLow,
+ &RmmImplHigh
+ );
+ if (!RETURN_ERROR (Status)) {
+ RealmWorld = TRUE;
+ }
+ }
+ }
+
+ return RealmWorld;
+}
+
+/**
+ 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
+ )
+{
+ if (!IsRealm ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ if ((Length == 0) || (IpaWidth == 0)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ return SetMemoryProtectionAttribute (
+ BaseAddress,
+ Length,
+ IpaWidth,
+ Share
+ );
+}
+
+/**
+ Return the IPA width of the Realm.
+
+ The IPA width of the Realm is used to configure the protection attribute
+ for memory regions, see ArmCcaSetMemoryProtectAttribute().
+
+ The IPA width of the Realm is present in the Realm config which is read
+ when the ArmCcaInitPeiLib library hook function ArmCcaInitialize () is
+ called in the PrePi phase. ArmCcaInitialize () stores the IPA width of
+ the Realm in a GUID HOB gArmCcaIpaWidthGuid.
+
+ This function searches the GUID HOB gArmCcaIpaWidthGuid and returns the
+ IPA width value stored therein.
+
+ Note:
+ - This function must only be called after ArmCcaInitialize () has setup
+ the GUID HOB gArmCcaIpaWidthGuid.
+
+ @param [out] IpaWidth IPA width of the Realm.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_NOT_FOUND The GUID HOB gArmCcaIpaWidthGuid is not
+ found and could mean that this function
+ was called before ArmCcaInitialize ()
+ has created and initialised the GUID
+ HOB gArmCcaIpaWidthGuid.
+**/
+RETURN_STATUS
+EFIAPI
+GetIpaWidth (
+ OUT UINT64 *IpaWidth
+ )
+{
+ VOID *Hob;
+ UINT64 *CcaIpaWidth;
+
+ if (IpaWidth == NULL) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ Hob = GetFirstGuidHob (&gArmCcaIpaWidthGuid);
+ if ((Hob == NULL) ||
+ (GET_GUID_HOB_DATA_SIZE (Hob) != sizeof (UINT64)))
+ {
+ return RETURN_NOT_FOUND;
+ }
+
+ CcaIpaWidth = GET_GUID_HOB_DATA (Hob);
+ if ((UINT64)*CcaIpaWidth == 0) {
+ return RETURN_NOT_FOUND;
+ }
+
+ *IpaWidth = *CcaIpaWidth;
+
+ return RETURN_SUCCESS;
+}
+
+/** Check if the address range is protected MMIO
+
+ @param [in] BaseAddress Base address of the device MMIO region.
+ @param [in] Length Length of the device MMIO region.
+ @param [out] IsProtectedMmio TRUE - if the RIPAS for the address range is
+ protected MMIO.
+ FALSE - if the RIPAS for the address range is
+ not protected MMIO.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The request is not initiated in a
+ Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaMemRangeIsProtectedMmio (
+ IN UINT64 BaseAddress,
+ IN UINT64 Length,
+ OUT BOOLEAN *IsProtectedMmio
+ )
+{
+ RETURN_STATUS RetStatus;
+ UINT64 *Base;
+ UINT64 *Top;
+ UINT64 *End;
+ RIPAS State;
+
+ if ((Length == 0) || (IsProtectedMmio == NULL)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ Base = (UINT64 *)BaseAddress;
+ End = (UINT64 *)(BaseAddress + Length);
+
+ while (Base < End) {
+ Top = End;
+ RetStatus = RsiGetIpaState (
+ Base,
+ &Top,
+ &State
+ );
+ if (RETURN_ERROR (RetStatus)) {
+ // An error occurred.
+ return RetStatus;
+ }
+
+ if ((Top <= Base) || (State != RipasDev)) {
+ // The address range is not protected MMIO.
+ *IsProtectedMmio = FALSE;
+ return RetStatus;
+ }
+
+ Base = Top;
+ } // while
+
+ *IsProtectedMmio = TRUE;
+ return RETURN_SUCCESS;
+}
diff --git a/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf
new file mode 100644
index 000000000000..7d90b4535d69
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaLib/ArmCcaLib.inf
@@ -0,0 +1,34 @@
+## @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
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ ArmCcaRsiLib
+ ArmLib
+ ArmMmuLib
+ BaseLib
+ HobLib
+
+[Guids]
+ gArmCcaIpaWidthGuid
diff --git a/ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.c b/ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.c
new file mode 100644
index 000000000000..907b6cd30f09
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.c
@@ -0,0 +1,142 @@
+/** @file
+ Null implemmentation of the ArmCcaLib library.
+
+ 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
+
+/**
+ 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 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
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
+/**
+ Return the IPA width of the Realm.
+
+ The IPA width of the Realm is used to configure the protection attribute
+ for memory regions, see ArmCcaSetMemoryProtectAttribute().
+
+ The IPA width of the Realm is present in the Realm config which is read
+ when the ArmCcaInitPeiLib library hook function ArmCcaInitialize () is
+ called in the PrePi phase. ArmCcaInitialize () stores the IPA width of
+ the Realm in a GUID HOB gArmCcaIpaWidthGuid.
+
+ This function searches the GUID HOB gArmCcaIpaWidthGuid and returns the
+ IPA width value stored therein.
+
+ Note:
+ - This function must only be called after ArmCcaInitialize () has setup
+ the GUID HOB gArmCcaIpaWidthGuid.
+
+ @param [out] IpaWidth IPA width of the Realm.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_NOT_FOUND The GUID HOB gArmCcaIpaWidthGuid is not
+ found and could mean that this function
+ was called before ArmCcaInitialize ()
+ has created and initialised the GUID
+ HOB gArmCcaIpaWidthGuid.
+**/
+RETURN_STATUS
+EFIAPI
+GetIpaWidth (
+ OUT UINT64 *IpaWidth
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
+
+/** Check if the address range is protected MMIO
+
+ @param [in] BaseAddress Base address of the device MMIO region.
+ @param [in] Length Length of the device MMIO region.
+ @param [out] IsProtectedMmio TRUE - if the RIPAS for the address range is
+ protected MMIO.
+ FALSE - if the RIPAS for the address range is
+ not protected MMIO.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_UNSUPPORTED The request is not initiated in a
+ Realm.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaMemRangeIsProtectedMmio (
+ IN UINT64 BaseAddress,
+ IN UINT64 Length,
+ OUT BOOLEAN *IsProtectedMmio
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
diff --git a/ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.inf b/ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.inf
new file mode 100644
index 000000000000..8f6c251afb14
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaLibNull/ArmCcaLibNull.inf
@@ -0,0 +1,28 @@
+## @file
+# Null implemmentation of the ArmCcaLib library.
+#
+# 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 = 9E3F7AAA-10A6-4513-A960-B87F4D7DCFC5
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmCcaLib
+
+[Sources]
+ ArmCcaLibNull.c
+
+
+[Packages]
+ ArmVirtPkg/ArmVirtPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+
diff --git a/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsi.h b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsi.h
new file mode 100644
index 000000000000..fdde88cc38c0
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsi.h
@@ -0,0 +1,60 @@
+/** @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-rel0
+ (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_ATTESTATION_TOKEN_CONTINUE 0xC4000195
+#define FID_RSI_ATTESTATION_TOKEN_INIT 0xC4000194
+#define FID_RSI_FEATURES 0xC4000191
+#define FID_RSI_HOST_CALL 0xC4000199
+#define FID_RSI_IPA_STATE_GET 0xC4000198
+#define FID_RSI_IPA_STATE_SET 0xC4000197
+#define FID_RSI_MEASUREMENT_EXTEND 0xC4000193
+#define FID_RSI_MEASUREMENT_READ 0xC4000192
+#define FID_RSI_REALM_CONFIG 0xC4000196
+#define FID_RSI_VERSION 0xC4000190
+
+/** RSI Command Return codes
+ See Section B5.4.1, RMM Specification, version 1.0-rel0.
+ 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
+#define RSI_ERROR_UNKNOWN 4ULL
+
+/** 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 000000000000..184709933faf
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.c
@@ -0,0 +1,777 @@
+/** @file
+ Library that implements the Arm CCA Realm Service Interface calls.
+
+ Copyright (c) 2022 - 2024, 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-rel0
+ (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) {
+ case RSI_SUCCESS:
+ return RETURN_SUCCESS;
+ case RSI_ERROR_INPUT:
+ return RETURN_INVALID_PARAMETER;
+ case RSI_ERROR_STATE:
+ case RSI_ERROR_UNKNOWN:
+ return RETURN_ABORTED;
+ case RSI_INCOMPLETE:
+ return RETURN_NOT_READY;
+ default:
+ // Unknown error code.
+ ASSERT (0);
+ break;
+ } // switch
+
+ return RETURN_ABORTED;
+}
+
+/**
+ Check if the address is aligned to the size of the Realm granule.
+
+ @param [in] Address Address to check granule alignment.
+
+ @retval TRUE Address is aligned to the Realm granule size.
+ @retval FALSE Address is not aligned to the Realm granule size.
+**/
+STATIC
+BOOLEAN
+EFIAPI
+AddrIsGranuleAligned (
+ IN UINT64 *Address
+ )
+{
+ if (((UINT64)Address & (REALM_GRANULE_SIZE - 1)) != 0) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Continue the operation to retrieve an attestation token.
+
+ @param [out] TokenBuffer Pointer to a buffer to store the
+ retrieved attestation token.
+ @param [in] Offset Offset within Token buffer granule
+ to start of buffer in bytes.
+ @param [in,out] TokenSize On input size of the token buffer,
+ and on output size of the token
+ returned if operation is successful,
+ otherwise 0.
+
+ @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.
+ Or the Token generation failed for an
+ unknown or IMPDEF reason.
+ @retval RETURN_NOT_READY The operation requested by the command
+ is not complete.
+ **/
+STATIC
+RETURN_STATUS
+EFIAPI
+RsiAttestationTokenContinue (
+ OUT UINT8 *CONST TokenBuffer,
+ IN UINT64 CONST Offset,
+ IN OUT UINT64 *CONST TokenSize
+ )
+{
+ RETURN_STATUS Status;
+ ARM_SMC_ARGS SmcCmd;
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_ATTESTATION_TOKEN_CONTINUE;
+ // Set the IPA of the Granule to which the token will be written.
+ SmcCmd.Arg1 = (UINTN)TokenBuffer;
+ // Set the Offset within Granule to start of buffer in bytes
+ SmcCmd.Arg2 = (UINTN)Offset;
+ // Set the size of the buffer in bytes
+ SmcCmd.Arg3 = (UINTN)*TokenSize;
+
+ ArmCallSmc (&SmcCmd);
+ Status = RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+ if (!RETURN_ERROR (Status)) {
+ // Update the token size
+ *TokenSize = SmcCmd.Arg1;
+ } else {
+ // Clear the TokenBuffer on error.
+ ZeroMem (TokenBuffer, *TokenSize);
+ *TokenSize = 0;
+ }
+
+ return Status;
+}
+
+/**
+ Initialize the operation to retrieve an attestation token.
+
+ @param [in] ChallengeData Pointer to the challenge data to be
+ included in the attestation token.
+ @param [in] ChallengeDataSizeBits Size of the challenge data in bits.
+ @param [out] MaxTokenSize Pointer to an integer to retrieve
+ the maximum attestation token size.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ **/
+STATIC
+RETURN_STATUS
+EFIAPI
+RsiAttestationTokenInit (
+ IN CONST UINT8 *CONST ChallengeData,
+ IN UINT64 ChallengeDataSizeBits,
+ OUT UINT64 *CONST MaxTokenSize
+ )
+{
+ RETURN_STATUS Status;
+ ARM_SMC_ARGS SmcCmd;
+ UINT8 *Buffer8;
+ CONST UINT8 *Data8;
+ UINT64 Count;
+ UINT8 TailBits;
+
+ /* See A7.2.2 Attestation token generation, RMM Specification, version A-bet0
+ IWTKDD - If the size of the challenge provided by the relying party is less
+ than 64 bytes, it should be zero-padded prior to calling
+ RSI_ATTESTATION_TOKEN_INIT.
+
+ Therefore, zero out the SmcCmd memory before coping the ChallengeData
+ bits.
+ */
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_ATTESTATION_TOKEN_INIT;
+ // Copy challenge data.
+ Buffer8 = (UINT8 *)&SmcCmd.Arg1;
+ Data8 = ChallengeData;
+
+ // First copy whole bytes
+ Count = ChallengeDataSizeBits >> 3;
+ CopyMem (Buffer8, Data8, Count);
+
+ // Now copy any remaining tail bits.
+ TailBits = ChallengeDataSizeBits & (8 - 1);
+ if (TailBits > 0) {
+ // Advance buffer pointers.
+ Buffer8 += Count;
+ Data8 += Count;
+
+ // Copy tail byte.
+ *Buffer8 = *Data8;
+
+ // Clear unused tail bits.
+ *Buffer8 &= ~(0xFF << TailBits);
+ }
+
+ ArmCallSmc (&SmcCmd);
+ Status = RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+ if (RETURN_ERROR (Status)) {
+ // Set the max token size to zero
+ *MaxTokenSize = 0;
+ } else {
+ *MaxTokenSize = SmcCmd.Arg1;
+ }
+
+ return Status;
+}
+
+/**
+ Free the attestation token buffer.
+
+ @param [in] TokenBuffer Pointer to the retrieved
+ attestation token.
+ @param [in] TokenBufferSize Size of the token buffer.
+**/
+VOID
+RsiFreeAttestationToken (
+ IN UINT8 *CONST TokenBuffer,
+ IN UINT64 CONST TokenBufferSize
+ )
+{
+ if (TokenBuffer != NULL) {
+ if (TokenBufferSize > 0) {
+ // Scrub the token buffer
+ ZeroMem (TokenBuffer, TokenBufferSize);
+ }
+
+ FreePool (TokenBuffer);
+ }
+}
+
+/**
+ Retrieve an attestation token from the RMM.
+
+ @param [in] ChallengeData Pointer to the challenge data to be
+ included in the attestation token.
+ @param [in] ChallengeDataSizeBits Size of the challenge data in bits.
+ @param [out] TokenBuffer Pointer to a buffer to store the
+ retrieved attestation token.
+ @param [out] TokenBufferSize Length of token data returned.
+
+ Note: The TokenBuffer allocated must be freed by the caller
+ using RsiFreeAttestationToken().
+
+ @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.
+ Or the Token generation failed for an
+ unknown or IMPDEF reason.
+ @retval RETURN_NOT_READY The operation requested by the command
+ is not complete.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetAttestationToken (
+ IN CONST UINT8 *CONST ChallengeData,
+ IN UINT64 ChallengeDataSizeBits,
+ OUT UINT8 **CONST TokenBuffer,
+ OUT UINT64 *CONST TokenBufferSize
+ )
+{
+ RETURN_STATUS Status;
+ UINT8 *Granule;
+ UINT64 GranuleSize;
+ UINT64 Offset;
+ UINT8 *Token;
+ UINT64 TokenSize;
+ UINT64 MaxTokenSize;
+
+ if ((TokenBuffer == NULL) ||
+ (TokenBufferSize == NULL) ||
+ (ChallengeData == NULL))
+ {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (ChallengeDataSizeBits > MAX_CHALLENGE_DATA_SIZE_BITS) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ /* See A7.2.2 Attestation token generation, RMM Specification, version A-bet0
+ IWTKDD - Arm recommends that the challenge should contain at least 32 bytes
+ of unique data.
+ */
+ if (ChallengeDataSizeBits < MIN_CHALLENGE_DATA_SIZE_BITS) {
+ DEBUG ((DEBUG_WARN, "Minimum Challenge data size should be 32 bytes\n"));
+ }
+
+ Status = RsiAttestationTokenInit (
+ ChallengeData,
+ ChallengeDataSizeBits,
+ &MaxTokenSize
+ );
+ if (RETURN_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Allocate a granule to retrieve the attestation token chunk.
+ Granule = (UINT8 *)AllocateAlignedPages (
+ EFI_SIZE_TO_PAGES (REALM_GRANULE_SIZE),
+ REALM_GRANULE_SIZE
+ );
+ if (Granule == NULL) {
+ ASSERT (0);
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ // Allocate a buffer to store the retrieved attestation token.
+ Token = AllocateZeroPool (MaxTokenSize);
+ if (Token == NULL) {
+ ASSERT (0);
+ Status = RETURN_OUT_OF_RESOURCES;
+ goto exit_handler;
+ }
+
+ TokenSize = 0;
+ do {
+ // Retrieve one Granule of data per loop iteration
+ ZeroMem (Granule, REALM_GRANULE_SIZE);
+ Offset = 0;
+ do {
+ // Retrieve sub-Granule chunk of data per loop iteration
+ GranuleSize = REALM_GRANULE_SIZE - Offset;
+ Status = RsiAttestationTokenContinue (
+ Granule,
+ Offset,
+ &GranuleSize
+ );
+ Offset += GranuleSize;
+ } while ((Status == RETURN_NOT_READY) && (Offset < REALM_GRANULE_SIZE));
+
+ if (RETURN_ERROR (Status) && (Status != RETURN_NOT_READY)) {
+ ASSERT (0);
+ goto exit_handler1;
+ }
+
+ // "Offset" bytes of data are now ready for consumption from "Granule"
+ // Copy the new token data from the Granule.
+ CopyMem (&Token[TokenSize], Granule, Offset);
+ TokenSize += Offset;
+ } while ((Status == RETURN_NOT_READY) && (TokenSize < MaxTokenSize));
+
+ *TokenBuffer = Token;
+ *TokenBufferSize = TokenSize;
+ goto exit_handler;
+
+exit_handler1:
+ if (Token != NULL) {
+ // Scrub the old Token
+ ZeroMem (Token, TokenSize);
+ FreePool (Token);
+ }
+
+ *TokenBuffer = NULL;
+ *TokenBufferSize = 0;
+
+exit_handler:
+ // Scrub the Granule buffer
+ ZeroMem (Granule, REALM_GRANULE_SIZE);
+ FreePages (Granule, EFI_SIZE_TO_PAGES (REALM_GRANULE_SIZE));
+
+ return Status;
+}
+
+/**
+ Returns the IPA state for the page pointed by the address.
+
+ @param [in] Base Base of target IPA region.
+ @param [in, out] Top End of target IPA region on input.
+ Top of IPA region which has the
+ reported RIPAS value on return.
+ @param [out] State The RIPAS state for the address specified.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetIpaState (
+ IN UINT64 *Base,
+ IN OUT UINT64 **Top,
+ OUT RIPAS *State
+ )
+{
+ RETURN_STATUS Status;
+ ARM_SMC_ARGS SmcCmd;
+
+ if ((State == NULL) ||
+ (!AddrIsGranuleAligned (Base)) ||
+ (!AddrIsGranuleAligned (*Top)) ||
+ (*Top < Base))
+ {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_IPA_STATE_GET;
+ SmcCmd.Arg1 = (UINTN)Base;
+ SmcCmd.Arg2 = (UINTN)*Top;
+
+ ArmCallSmc (&SmcCmd);
+ Status = RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+ if (!RETURN_ERROR (Status)) {
+ *Top = (UINT64 *)SmcCmd.Arg1;
+ *State = (RIPAS)(SmcCmd.Arg2 & RIPAS_TYPE_MASK);
+ }
+
+ return Status;
+}
+
+/**
+ Sets the IPA state for the pages pointed by the memory range.
+
+ @param [in] Address Address to the start of the memory range.
+ @param [in] Size Length of the memory range.
+ @param [in] State The RIPAS state to be configured.
+ @param [in] Flags The RIPAS change flags.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+ @retval RETURN_ACCESS_DENIED RIPAS change request was rejected.
+**/
+RETURN_STATUS
+EFIAPI
+RsiSetIpaState (
+ IN UINT64 *Address,
+ IN UINT64 Size,
+ IN RIPAS State,
+ IN UINT64 Flags
+ )
+{
+ RETURN_STATUS Status;
+ UINT64 *BaseAddress;
+ UINT64 *EndAddress;
+ ARM_SMC_ARGS SmcCmd;
+
+ if ((Size == 0) ||
+ ((Size & (REALM_GRANULE_SIZE - 1)) != 0) ||
+ (!AddrIsGranuleAligned (Address)))
+ {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ BaseAddress = Address;
+ // Divide Size by 8 for the pointer arithmetic
+ // to work, as we are adding to UINT64*.
+ EndAddress = Address + (Size >> 3);
+
+ while (Size > 0) {
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_IPA_STATE_SET;
+ SmcCmd.Arg1 = (UINTN)BaseAddress;
+ SmcCmd.Arg2 = (UINTN)EndAddress;
+ SmcCmd.Arg3 = (UINTN)State;
+ SmcCmd.Arg4 = Flags;
+
+ ArmCallSmc (&SmcCmd);
+ Status = RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+ if (RETURN_ERROR (Status)) {
+ break;
+ }
+
+ BaseAddress = (UINT64 *)SmcCmd.Arg1;
+ Size = EndAddress - BaseAddress;
+
+ if ((SmcCmd.Arg2 & RSI_RESPONSE_MASK) == RIPAS_CHANGE_RESPONSE_REJECT) {
+ Status = RETURN_ACCESS_DENIED;
+ break;
+ }
+ } // while
+
+ return Status;
+}
+
+/**
+ 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;
+ UINT64 *Data64;
+
+ 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;
+
+ Data64 = &SmcCmd.Arg3;
+ CopyMem (Data64, Measurement, MeasurementSize);
+
+ ArmCallSmc (&SmcCmd);
+ return RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+}
+
+/**
+ Read the measurement value from a REM.
+
+ @param [in] MeasurementIndex Index of the REM.
+ @param [out] MeasurementBuffer Pointer to store the measurement data.
+ @param [in] MeasurementBufferSize Size of the measurement buffer.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiReadMeasurement (
+ IN UINTN MeasurementIndex,
+ OUT UINT8 *CONST MeasurementBuffer,
+ IN UINTN MeasurementBufferSize
+ )
+{
+ RETURN_STATUS Status;
+ ARM_SMC_ARGS SmcCmd;
+ UINT64 *Data64;
+
+ if ((MeasurementIndex < MIN_REM_INDEX) ||
+ (MeasurementIndex > MAX_REM_INDEX) ||
+ (MeasurementBuffer == NULL))
+ {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ if (MeasurementBufferSize < MAX_MEASUREMENT_DATA_SIZE_BYTES) {
+ return RETURN_BUFFER_TOO_SMALL;
+ }
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_MEASUREMENT_READ;
+ SmcCmd.Arg1 = MeasurementIndex;
+
+ ArmCallSmc (&SmcCmd);
+ Status = RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+ if (!RETURN_ERROR (Status)) {
+ Data64 = &SmcCmd.Arg1;
+ CopyMem (MeasurementBuffer, Data64, MAX_MEASUREMENT_DATA_SIZE_BYTES);
+ }
+
+ return Status;
+}
+
+/**
+ Read the Realm Configuration.
+
+ @param [out] Config Pointer to the address of the buffer to retrieve
+ the Realm configuration.
+
+ Note: The buffer to retrieve the Realm configuration must be aligned to the
+ Realm granule size.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetRealmConfig (
+ OUT REALM_CONFIG *Config
+ )
+{
+ ARM_SMC_ARGS SmcCmd;
+
+ if ((Config == NULL) || (!AddrIsGranuleAligned ((UINT64 *)Config))) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ // Add static asserts to check that the Realm Config is as what we expect.
+ STATIC_ASSERT (sizeof (REALM_CONFIG) == SIZE_4KB);
+ STATIC_ASSERT (
+ OFFSET_OF (REALM_CONFIG, IpaWidth) == REALM_CFG_OFFSET_IPA_WIDTH
+ );
+ STATIC_ASSERT (
+ OFFSET_OF (REALM_CONFIG, HashAlgorithm) == REALM_CFG_OFFSET_HASH_ALGO
+ );
+ STATIC_ASSERT (
+ OFFSET_OF (REALM_CONFIG, Reserved) == REALM_CFG_OFFSET_RESERVED
+ );
+ STATIC_ASSERT (
+ OFFSET_OF (REALM_CONFIG, Rpv) == REALM_CFG_OFFSET_RPV
+ );
+ STATIC_ASSERT (
+ OFFSET_OF (REALM_CONFIG, Reserved1) == REALM_CFG_OFFSET_RESERVED1
+ );
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_REALM_CONFIG;
+ SmcCmd.Arg1 = (UINTN)Config;
+
+ ArmCallSmc (&SmcCmd);
+ return RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+}
+
+/**
+ Make a Host Call.
+
+ A Host call can be used by a Realm to make a hypercall.
+ On Realm execution of HVC, an Unknown exception is taken to the Realm.
+
+ @param [in] Args Pointer to the IPA of the Host call data
+ structure.
+
+ Note: The IPA of the Host call arguments data structure must be aligned
+ to the Realm granule size.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiHostCall (
+ IN HOST_CALL_ARGS *Args
+ )
+{
+ ARM_SMC_ARGS SmcCmd;
+
+ // The RMM specification, version 1.0-eac1, relaxes the alignment
+ // requirement for RSI_HOST_CALL from 4KB to 256B. Also see RMM
+ // specification, sections B4.3.3 RSI_HOST_CALL command and
+ // section B4.3.3.2 Failure conditions.
+ if ((Args == NULL) || (((UINT64)Args & (0x100 - 1)) != 0)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ // See RMM specification, version 1.0-bet1, Section B4.4.2 RsiHostCall type
+ // The width of the RsiHostCall structure is 256 (0x100) bytes.
+ STATIC_ASSERT (sizeof (HOST_CALL_ARGS) == 0x100);
+
+ // Clear the reserved fields
+ ZeroMem (&Args->Reserved1, sizeof (Args->Reserved1));
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_HOST_CALL;
+ SmcCmd.Arg1 = (UINTN)Args;
+
+ 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] RmmImplLow The low version of the RSI specification
+ implemented by the RMM.
+ @param [out] RmmImplHigh The high 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 RmmImplLow,
+ OUT UINT32 *CONST RmmImplHigh
+ )
+{
+ RETURN_STATUS Status;
+ ARM_SMC_ARGS SmcCmd;
+
+ if ((UefiImpl == NULL) || (RmmImplLow == NULL) || (RmmImplHigh == NULL)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_VERSION;
+ SmcCmd.Arg1 = mRsiImplVersion;
+ 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;
+ }
+
+ *RmmImplLow = (SmcCmd.Arg1 & RSI_VERSION_MASK);
+ *RmmImplHigh = (SmcCmd.Arg2 & RSI_VERSION_MASK);
+ *UefiImpl = mRsiImplVersion;
+
+ // The RSI_VERSION command does not have any failure
+ // conditions see section B5.3.10.2 Failure conditions
+ // Therefore the only defined return values are
+ // RSI_SUCCESS and RSI_ERROR_INPUT.
+ Status = RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+ if (Status == RETURN_INVALID_PARAMETER) {
+ // RSI_VERSION returns RSI_ERROR_INPUT when
+ // the RMM does not support an interface revision
+ // which is compatible with the requested revision.
+ // Since RSI_ERROR_INPUT is mapped to RETURN_INVALID_PARAMETER
+ // by RsiCmdStatusToEfiStatus(), return the status code as
+ // RETURN_INCOMPATIBLE_VERSION.
+ return RETURN_INCOMPATIBLE_VERSION;
+ }
+
+ // Add an assert in case RMM returns a different error code than expected.
+ ASSERT (Status == RETURN_SUCCESS);
+ return Status;
+}
+
+/**
+ Get the features supported by the RSI implementation.
+
+ RMM implementations across different CCA platforms may support
+ disparate features and may offer disparate configuration options
+ for Realms. The features supported by an RSI implementation are
+ discovered by reading feature pseudo-register values using the
+ RSI_FEATURES command.
+
+ @param [in] FeatureRegIndex The Feature Register Index.
+ @param [out] FeatureRegValue The Feature Register Value.
+
+ @retval RETURN_SUCCESS Success.
+ @retval RETURN_INVALID_PARAMETER A parameter is invalid.
+**/
+RETURN_STATUS
+EFIAPI
+RsiGetFeatures (
+ IN UINT64 FeatureRegIndex,
+ OUT UINT64 *FeatureRegValue
+ )
+{
+ ARM_SMC_ARGS SmcCmd;
+
+ if (FeatureRegValue == NULL) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ZeroMem (&SmcCmd, sizeof (SmcCmd));
+ SmcCmd.Arg0 = FID_RSI_FEATURES;
+ SmcCmd.Arg1 = FeatureRegIndex;
+
+ ArmCallSmc (&SmcCmd);
+ *FeatureRegValue = SmcCmd.Arg1;
+ return RsiCmdStatusToEfiStatus (SmcCmd.Arg0);
+}
diff --git a/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf b/ArmVirtPkg/Library/ArmCcaRsiLib/ArmCcaRsiLib.inf
new file mode 100644
index 000000000000..1e2b72f31258
--- /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/ArmPlatformDeviceInfoLib/ArmPlatformDeviceInfoLib.inf b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/ArmPlatformDeviceInfoLib.inf
new file mode 100644
index 000000000000..e3491abaa6ab
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/ArmPlatformDeviceInfoLib.inf
@@ -0,0 +1,50 @@
+## @file
+# Arm Platform Device Info library.
+#
+# Copyright (c) 2024, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001B
+ BASE_NAME = ArmPlatformDeviceInfoLib
+ FILE_GUID = 7f5f99e7-4022-4b56-8d68-c0dc8f41aab2
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = ArmPlatformDeviceInfoLib
+
+[Sources]
+ HwInfoParser/Arm/Gic/ArmGicCParser.c
+ HwInfoParser/Arm/Gic/ArmGicCParser.h
+ HwInfoParser/Arm/Gic/ArmGicDispatcher.c
+ HwInfoParser/Arm/Gic/ArmGicDispatcher.h
+ HwInfoParser/Arm/Gic/ArmGicDParser.c
+ HwInfoParser/Arm/Gic/ArmGicDParser.h
+ HwInfoParser/Arm/Gic/ArmGicRParser.c
+ HwInfoParser/Arm/Gic/ArmGicRParser.h
+ HwInfoParser/Common/DeviceParser.c
+ HwInfoParser/Common/DeviceParser.h
+ HwInfoParser/FdtInfoParser.c
+ HwInfoParser/FdtInfoParser.h
+ HwInfoParser/FdtUtility.c
+ HwInfoParser/FdtUtility.h
+ HwInfoParser/Pci/PciConfigSpaceParser.c
+ HwInfoParser/Pci/PciConfigSpaceParser.h
+ HwInfoParser/Serial/SerialPortParser.c
+ HwInfoParser/Serial/SerialPortParser.h
+ HwInfoParser/Rtc/RtcParser.c
+ HwInfoParser/Rtc/RtcParser.h
+
+[Packages]
+ ArmVirtPkg/ArmVirtPkg.dec
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+
+[Pcd]
+ gArmVirtTokenSpaceGuid.PcdMaxPlatDeviceCount
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicCParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicCParser.c
new file mode 100755
index 000000000000..b961a777d16b
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicCParser.c
@@ -0,0 +1,436 @@
+/** @file
+ Arm Gic cpu parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/arm/cpus.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+ - linux/Documentation/devicetree/bindings/arm/pmu.yaml
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "ArmGicCParser.h"
+#include "ArmGicDispatcher.h"
+
+/** Parse a "cpus" node and its children "cpu" nodes and return
+ the CPU node count.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
+ @param [out] CpuNodeCount The count of CPU nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpusNodeParser (
+ IN CONST VOID *Fdt,
+ OUT UINT32 *CpuNodeCount
+ )
+{
+ EFI_STATUS Status;
+ INT32 CpusNode;
+ INT32 AddressCells;
+
+ if (Fdt == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // The "cpus" node resides at the root of the DT. Fetch it.
+ CpusNode = fdt_path_offset (Fdt, "/cpus");
+ if (CpusNode < 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ AddressCells = fdt_address_cells (Fdt, CpusNode);
+ if (AddressCells < 0) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ // Count the number of "cpu" nodes under the "cpus" node.
+ Status = FdtCountNamedNodeInBranch (Fdt, CpusNode, "cpu", CpuNodeCount);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ if (*CpuNodeCount == 0) {
+ ASSERT (FALSE);
+ return EFI_NOT_FOUND;
+ }
+
+ return Status;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+ extracting GicCv2 information.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] Gicv2IntcNode Offset of a Gicv2 compatible
+ interrupt-controller node.
+ @param [in] CpuNodeCount The count of CPU nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCv2IntcNodeParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 Gicv2IntcNode,
+ IN UINT32 CpuNodeCount
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ INT32 AddressCells;
+ INT32 SizeCells;
+ VOID *Fdt;
+ CONST UINT8 *GicCValue;
+ CONST UINT8 *GicCRange;
+
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ UINT32 RegSize;
+ UINT32 RegCount;
+ UINT64 PhysicalBaseAddress;
+ UINT64 PhysicalBaseAddressRange;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ GicCValue = NULL;
+ GicCRange = NULL;
+
+ // Get the #address-cells and #size-cells property values.
+ Status = FdtGetParentAddressInfo (
+ Fdt,
+ Gicv2IntcNode,
+ &AddressCells,
+ &SizeCells
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Don't support more than 64 bits and less than 32 bits addresses.
+ if ((AddressCells < 1) ||
+ (AddressCells > 2) ||
+ (SizeCells < 1) ||
+ (SizeCells > 2))
+ {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+ Data = fdt_getprop (Fdt, Gicv2IntcNode, "reg", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize < 0) ||
+ ((DataSize % RegSize) != 0))
+ {
+ // If error or wrong size.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ RegCount = DataSize/RegSize;
+
+ switch (RegCount) {
+ case 2:
+ {
+ // GicC is at index 1 in the reg property. GicC is mandatory.
+ GicCValue = Data + (sizeof (UINT32) *
+ GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells));
+ GicCRange = Data + (sizeof (UINT32) *
+ GET_DT_REG_ADDRESS_OFFSET (2, AddressCells, SizeCells));
+ break;
+ }
+ default:
+ {
+ // Not enough or too much information.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ }
+
+ for (Index = 0; Index < CpuNodeCount; Index++) {
+ if (AddressCells == 2) {
+ PhysicalBaseAddress = FdtReadUnaligned64 ((UINT64 *)GicCValue);
+ PhysicalBaseAddressRange = FdtReadUnaligned64 ((UINT64 *)GicCRange);
+ } else {
+ PhysicalBaseAddress = FdtReadUnaligned32 ((UINT32 *)GicCValue);
+ PhysicalBaseAddressRange = FdtReadUnaligned32 ((UINT32 *)GicCRange);
+ }
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ "GICC GicV2",
+ PhysicalBaseAddress,
+ PhysicalBaseAddressRange
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ break;
+ }
+ }
+ } // for
+
+ return Status;
+}
+
+/** Parse a Gic compatible interrupt-controller node,
+ extracting GicCv3 information.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] Gicv3IntcNode Offset of a Gicv3 compatible
+ interrupt-controller node.
+ @param [in] CpuNodeCount The count of CPU nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicCv3IntcNodeParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 Gicv3IntcNode,
+ IN UINT32 CpuNodeCount
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ INT32 AddressCells;
+ INT32 SizeCells;
+ UINT32 AdditionalRedistReg;
+ UINT64 PhysicalBaseAddress;
+ UINT64 PhysicalBaseAddressRange;
+ VOID *Fdt;
+ CONST UINT8 *GicCValue;
+ CONST UINT8 *GicCRange;
+
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ UINT32 RegSize;
+ UINT32 RegCount;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ GicCValue = NULL;
+ GicCRange = NULL;
+
+ // Get the #address-cells and #size-cells property values.
+ Status = FdtGetParentAddressInfo (
+ Fdt,
+ Gicv3IntcNode,
+ &AddressCells,
+ &SizeCells
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Don't support more than 64 bits and less than 32 bits addresses.
+ if ((AddressCells < 1) ||
+ (AddressCells > 2) ||
+ (SizeCells < 1) ||
+ (SizeCells > 2))
+ {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ // The "#redistributor-regions" property is optional.
+ Data = fdt_getprop (Fdt, Gicv3IntcNode, "#redistributor-regions", &DataSize);
+ if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
+ ASSERT (FdtReadUnaligned32 ((UINT32 *)Data) > 1);
+ AdditionalRedistReg = FdtReadUnaligned32 ((UINT32 *)Data) - 1;
+ } else {
+ AdditionalRedistReg = 0;
+ }
+
+ RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+ /*
+ Ref: linux/blob/master/Documentation/devicetree/bindings/
+ interrupt-controller/arm%2Cgic-v3.yaml
+
+ reg:
+ description: |
+ Specifies base physical address(s) and size of the GIC
+ registers, in the following order:
+ - GIC Distributor interface (GICD)
+ - GIC Redistributors (GICR), one range per redistributor region
+ - GIC CPU interface (GICC)
+ - GIC Hypervisor interface (GICH)
+ - GIC Virtual CPU interface (GICV)
+ GICC, GICH and GICV are optional.
+ minItems: 2
+ maxItems: 4096
+ */
+ Data = fdt_getprop (Fdt, Gicv3IntcNode, "reg", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize < 0) ||
+ ((DataSize % RegSize) != 0))
+ {
+ // If error or wrong size.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ RegCount = (DataSize / RegSize) - AdditionalRedistReg;
+
+ // The GicD and GicR info is mandatory.
+ switch (RegCount) {
+ case 3:
+ {
+ // GicC is at index 2 in the reg property. GicC is optional.
+ // Even though GicC is optional, it is made mandatory in this parser.
+ GicCValue = Data + (sizeof (UINT32) *
+ GET_DT_REG_ADDRESS_OFFSET (
+ 2 + AdditionalRedistReg,
+ AddressCells,
+ SizeCells
+ ));
+ GicCRange = Data + (sizeof (UINT32) *
+ GET_DT_REG_ADDRESS_OFFSET (
+ 3 + AdditionalRedistReg,
+ AddressCells,
+ SizeCells
+ ));
+ // fall-through
+ }
+ case 2:
+ {
+ // GicR
+ // GicD
+ break;
+ }
+ default:
+ {
+ // Not enough or too much information.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ } // switch
+
+ if ((GicCValue != NULL) && (GicCRange != NULL)) {
+ for (Index = 0; Index < CpuNodeCount; Index++) {
+ if (AddressCells == 2) {
+ PhysicalBaseAddress = FdtReadUnaligned64 ((UINT64 *)GicCValue);
+ PhysicalBaseAddressRange = FdtReadUnaligned64 ((UINT64 *)GicCRange);
+ } else {
+ PhysicalBaseAddress = FdtReadUnaligned32 ((UINT32 *)GicCValue);
+ PhysicalBaseAddressRange = FdtReadUnaligned32 ((UINT32 *)GicCRange);
+ }
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ "GICC GicV3",
+ PhysicalBaseAddress,
+ PhysicalBaseAddressRange
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ break;
+ }
+ }
+ } // for
+ }
+
+ return Status;
+}
+
+/** GICC information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] IntcNode Offset of the interrupt-controller node.
+ @param [in] CpuNodeCount The count of CPU nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicCInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 IntcNode,
+ IN CONST UINT32 GicVersion
+ )
+{
+ EFI_STATUS Status;
+ VOID *Fdt;
+ UINT32 CpuNodeCount;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ // Parse the "cpus" nodes and its children "cpu" nodes, and
+ // return the CPU node count.
+ Status = CpusNodeParser (Fdt, &CpuNodeCount);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Parse the interrupt-controller node according to the Gic version.
+ switch (GicVersion) {
+ case 2:
+ {
+ Status = GicCv2IntcNodeParser (FdtParserHandle, IntcNode, CpuNodeCount);
+ break;
+ }
+ case 3:
+ {
+ Status = GicCv3IntcNodeParser (FdtParserHandle, IntcNode, CpuNodeCount);
+ break;
+ }
+ default:
+ {
+ // Unsupported Gic version.
+ ASSERT (FALSE);
+ Status = EFI_UNSUPPORTED;
+ }
+ }
+
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ goto exit_handler;
+ }
+
+exit_handler:
+ return Status;
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicCParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicCParser.h
new file mode 100755
index 000000000000..fd23be976585
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicCParser.h
@@ -0,0 +1,35 @@
+/** @file
+ Arm Gic cpu parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICC_PARSER_H_
+#define ARM_GICC_PARSER_H_
+
+/** GICC information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] IntcNode Offset of the interrupt-controller node.
+ @param [in] CpuNodeCount The count of CPU nodes.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicCInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 IntcNode,
+ IN CONST UINT32 GicVersion
+ );
+
+#endif // ARM_GICC_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDParser.c
new file mode 100755
index 000000000000..4872f5251b70
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDParser.c
@@ -0,0 +1,169 @@
+/** @file
+ Arm Gic Distributor Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "ArmGicDispatcher.h"
+#include "ArmGicDParser.h"
+
+/** Parse a Gic compatible interrupt-controller node,
+ extracting GicD information.
+
+ This parser is valid for Gic v2 and v3.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] GicIntcNode Offset of a Gic compatible
+ interrupt-controller node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicDIntcNodeParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 GicIntcNode
+ )
+{
+ EFI_STATUS Status;
+ INT32 AddressCells;
+ INT32 SizeCells;
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ UINT32 RegSize;
+ UINT64 PhysicalBaseAddress;
+ UINT64 PhysicalBaseAddressRange;
+ VOID *Fdt;
+ CONST UINT8 *GicDValue;
+ CONST UINT8 *GicDRange;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ Status = FdtGetParentAddressInfo (
+ Fdt,
+ GicIntcNode,
+ &AddressCells,
+ &SizeCells
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Don't support more than 64 bits and less than 32 bits addresses.
+ if ((AddressCells < 1) ||
+ (AddressCells > 2) ||
+ (SizeCells < 1) ||
+ (SizeCells > 2))
+ {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+
+ Data = fdt_getprop (Fdt, GicIntcNode, "reg", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize < 0) ||
+ ((DataSize % RegSize) != 0))
+ {
+ // If error or wrong size.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ GicDValue = Data + (sizeof (UINT32) *
+ GET_DT_REG_ADDRESS_OFFSET (0, AddressCells, SizeCells));
+ GicDRange = Data + sizeof (UINT32) * AddressCells;
+
+ if (AddressCells == 2) {
+ PhysicalBaseAddress = FdtReadUnaligned64 ((UINT64 *)GicDValue);
+ PhysicalBaseAddressRange = FdtReadUnaligned64 ((UINT64 *)GicDRange);
+ } else {
+ PhysicalBaseAddress = FdtReadUnaligned32 ((UINT32 *)GicDValue);
+ PhysicalBaseAddressRange = FdtReadUnaligned32 ((UINT32 *)GicDRange);
+ }
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ "GICD",
+ PhysicalBaseAddress,
+ PhysicalBaseAddressRange
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
+
+/** GICD information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+ @param [in] GicVersion The version of the GIC.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch,
+ IN CONST UINT32 GicVersion
+ )
+{
+ EFI_STATUS Status;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ if (!FdtNodeHasProperty (Fdt, FdtBranch, "interrupt-controller")) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Parse the interrupt-controller depending on its Gic version.
+ switch (GicVersion) {
+ case 2:
+ case 3:
+ {
+ // Set the Gic version, then parse the GicD information.
+ Status = GicDIntcNodeParser (FdtParserHandle, FdtBranch);
+ break;
+ }
+ default:
+ {
+ // Unsupported Gic version.
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return Status;
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDParser.h
new file mode 100755
index 000000000000..fd464f631cfb
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDParser.h
@@ -0,0 +1,36 @@
+/** @file
+ Arm Gic Distributor Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICD_PARSER_H_
+#define ARM_GICD_PARSER_H_
+
+/** GICD information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+ @param [in] GicVersion The version of the GIC.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch,
+ IN CONST UINT32 GicVersion
+ );
+
+#endif // ARM_GICD_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDispatcher.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDispatcher.c
new file mode 100755
index 000000000000..fe7eecbee7e5
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDispatcher.c
@@ -0,0 +1,189 @@
+/** @file
+ Arm Gic dispatcher.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "ArmGicCParser.h"
+#include "ArmGicDispatcher.h"
+#include "ArmGicDParser.h"
+#include "ArmGicRParser.h"
+
+/** List of "compatible" property values for GicV2 interrupt nodes.
+
+ Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR GicV2CompatibleStr[] = {
+ { "arm,cortex-a15-gic" }
+};
+
+/** COMPATIBILITY_INFO structure for the GICv2.
+*/
+CONST COMPATIBILITY_INFO GicV2CompatibleInfo = {
+ ARRAY_SIZE (GicV2CompatibleStr),
+ GicV2CompatibleStr
+};
+
+/** List of "compatible" property values for GicV3 interrupt nodes.
+
+ Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR GicV3CompatibleStr[] = {
+ { "arm,gic-v3" }
+};
+
+/** COMPATIBILITY_INFO structure for the GICv3.
+*/
+CONST COMPATIBILITY_INFO GicV3CompatibleInfo = {
+ ARRAY_SIZE (GicV3CompatibleStr),
+ GicV3CompatibleStr
+};
+
+/** Get the Gic version of an interrupt-controller node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
+ @param [in] IntcNode Interrupt-controller node.
+ @param [out] GicVersion If success, contains the Gic version of the
+ interrupt-controller node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetGicVersion (
+ IN CONST VOID *Fdt,
+ IN INT32 IntcNode,
+ OUT UINT32 *GicVersion
+ )
+{
+ if ((Fdt == NULL) ||
+ (GicVersion == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (FdtNodeIsCompatible (Fdt, IntcNode, &GicV2CompatibleInfo)) {
+ *GicVersion = 2;
+ } else if (FdtNodeIsCompatible (Fdt, IntcNode, &GicV3CompatibleInfo)) {
+ *GicVersion = 3;
+ } else {
+ // Unsupported Gic version.
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Gic dispatcher.
+
+ This disptacher parses the Device tree for the following infromation:
+ - GICC
+ - GICD
+ - GICR
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ )
+{
+ EFI_STATUS Status;
+ INT32 CpusNode;
+ INT32 IntcNode;
+ UINT32 GicVersion;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ // The "cpus" node resides at the root of the DT. Fetch it.
+ CpusNode = fdt_path_offset (Fdt, "/cpus");
+ if (CpusNode < 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ // Get the interrupt-controller node associated to the "cpus" node.
+ Status = FdtGetIntcParentNode (Fdt, CpusNode, &IntcNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ if (Status == EFI_NOT_FOUND) {
+ // Should have found the node.
+ Status = EFI_ABORTED;
+ }
+
+ return Status;
+ }
+
+ Status = GetGicVersion (Fdt, IntcNode, &GicVersion);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Parse the GicC information.
+ Status = ArmGicCInfoParser (FdtParserHandle, IntcNode, GicVersion);
+ if (EFI_ERROR (Status)) {
+ // Don't try to parse GicD and GicMsiFrame information
+ // if no GicC information is found. Return.
+ ASSERT (Status == EFI_NOT_FOUND);
+ return Status;
+ }
+
+ // Parse the GicD information of the "cpus" interrupt-controller node.
+ Status = ArmGicDInfoParser (FdtParserHandle, IntcNode, GicVersion);
+ if (EFI_ERROR (Status)) {
+ // EFI_NOT_FOUND is not tolerated at this point.
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ switch (GicVersion) {
+ case 4:
+ case 3:
+ {
+ // Parse the GicR information of the interrupt-controller node.
+ Status = ArmGicRInfoParser (FdtParserHandle, IntcNode, GicVersion);
+ if (EFI_ERROR (Status)) {
+ // EFI_NOT_FOUND is not tolerated at this point.
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ break;
+ }
+ default:
+ {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDispatcher.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDispatcher.h
new file mode 100755
index 000000000000..f09400bed1be
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicDispatcher.h
@@ -0,0 +1,39 @@
+/** @file
+ Arm Gic dispatcher.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic.yaml
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GIC_DISPATCHER_H_
+#define ARM_GIC_DISPATCHER_H_
+
+/** Gic dispatcher.
+
+ This dispatcher parses the Device tree for the following information:
+ - GICC
+ - GICD
+ - GICR
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ );
+
+#endif // ARM_GIC_DISPATCHER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicRParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicRParser.c
new file mode 100755
index 000000000000..cee42f7ddcb0
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicRParser.c
@@ -0,0 +1,217 @@
+/** @file
+ Arm Gic Redistributor Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "ArmGicDispatcher.h"
+#include "ArmGicRParser.h"
+
+/** Parse a Gic compatible interrupt-controller node,
+ extracting GicR information.
+
+ This parser is valid for Gic v3 and higher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] GicIntcNode Offset of a Gic compatible
+ interrupt-controller node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GicRIntcNodeParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 GicIntcNode
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT32 RedistReg;
+ UINT32 RegSize;
+ INT32 AddressCells;
+ INT32 SizeCells;
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ VOID *Fdt;
+ UINT64 DiscoveryRangeBaseAddress;
+ UINT64 DiscoveryRangeLength;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ Status = FdtGetParentAddressInfo (
+ Fdt,
+ GicIntcNode,
+ &AddressCells,
+ &SizeCells
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Don't support more than 64 bits and less than 32 bits addresses.
+ if ((AddressCells < 1) ||
+ (AddressCells > 2) ||
+ (SizeCells < 1) ||
+ (SizeCells > 2))
+ {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ // The "#redistributor-regions" property is optional.
+ // It indicates the number of GicR.
+ Data = fdt_getprop (Fdt, GicIntcNode, "#redistributor-regions", &DataSize);
+ if ((Data != NULL) && (DataSize == sizeof (UINT32))) {
+ // If available, must be on one cell.
+ RedistReg = FdtReadUnaligned32 ((UINT32 *)Data);
+ } else {
+ // The DT Spec says GicR is mandatory so we will
+ // always have one.
+ RedistReg = 1;
+ }
+
+ /*
+ Ref: linux/blob/master/Documentation/devicetree/bindings/
+ interrupt-controller/arm%2Cgic-v3.yaml
+
+ reg:
+ description: |
+ Specifies base physical address(s) and size of the GIC
+ registers, in the following order:
+ - GIC Distributor interface (GICD)
+ - GIC Redistributors (GICR), one range per redistributor region
+ - GIC CPU interface (GICC)
+ - GIC Hypervisor interface (GICH)
+ - GIC Virtual CPU interface (GICV)
+ GICC, GICH and GICV are optional.
+ minItems: 2
+ maxItems: 4096
+
+ Example:
+ interrupt-controller@2c010000 {
+ compatible = "arm,gic-v3";
+ #interrupt-cells = <4>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ interrupt-controller;
+ redistributor-stride = <0x0 0x40000>; // 256kB stride
+ #redistributor-regions = <2>;
+ reg = <0x2c010000 0x10000>, // GICD
+ <0x2d000000 0x800000>, // GICR 1: CPUs 0-31
+ <0x2e000000 0x800000>, // GICR 2: CPUs 32-63
+ <0x2c040000 0x2000>, // GICC
+ <0x2c060000 0x2000>, // GICH
+ <0x2c080000 0x2000>; // GICV
+ interrupts = <1 9 4>;
+ ...
+ }
+ */
+ RegSize = (AddressCells + SizeCells) * sizeof (UINT32);
+ Data = fdt_getprop (Fdt, GicIntcNode, "reg", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize < 0) ||
+ ((DataSize % RegSize) != 0))
+ {
+ // If error or wrong size.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ Data += GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)
+ * sizeof (UINT32);
+ for (Index = 0; Index < RedistReg; Index++) {
+ if (AddressCells == 2) {
+ DiscoveryRangeBaseAddress = FdtReadUnaligned64 ((UINT64 *)Data);
+ } else {
+ DiscoveryRangeBaseAddress = FdtReadUnaligned32 ((UINT32 *)Data);
+ }
+
+ Data += sizeof (UINT32) * AddressCells;
+
+ if (SizeCells == 2) {
+ DiscoveryRangeLength = FdtReadUnaligned64 ((UINT64 *)Data);
+ } else {
+ DiscoveryRangeLength = FdtReadUnaligned32 ((UINT32 *)Data);
+ }
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ "GICR",
+ DiscoveryRangeBaseAddress,
+ DiscoveryRangeLength
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ break;
+ }
+ }
+
+ Data += sizeof (UINT32) * SizeCells;
+ } // for
+
+ return Status;
+}
+
+/** GICR information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+ @param [in] GicVersion The version of the GIC.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicRInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch,
+ IN CONST UINT32 GicVersion
+ )
+{
+ EFI_STATUS Status;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ if (!FdtNodeHasProperty (Fdt, FdtBranch, "interrupt-controller")) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (GicVersion < 3) {
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = GicRIntcNodeParser (FdtParserHandle, FdtBranch);
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicRParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicRParser.h
new file mode 100755
index 000000000000..ef7f8f38d4c0
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Arm/Gic/ArmGicRParser.h
@@ -0,0 +1,35 @@
+/** @file
+ Arm Gic Redistributor Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+**/
+
+#ifndef ARM_GICR_PARSER_H_
+#define ARM_GICR_PARSER_H_
+
+/** GICR information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+ @param [in] GicVersion The version of the GIC.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+ArmGicRInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch,
+ IN CONST UINT32 GicVersion
+ );
+
+#endif // ARM_GICR_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Common/DeviceParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Common/DeviceParser.c
new file mode 100755
index 000000000000..eee243c6317b
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Common/DeviceParser.c
@@ -0,0 +1,203 @@
+/** @file
+ Device Node Address Range Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+
+/** Parse a Device node.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] DeviceNode Offset of a device node.
+ @param [in] Index Index of the device if there are multiple
+ instances of the same device type.
+ @param [in] DevDesc A NULL terminated string containing a short
+ description of the device.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DeviceNodeParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 DeviceNode,
+ IN CONST UINT32 Index,
+ IN CONST CHAR8 *DevDesc
+ )
+{
+ EFI_STATUS Status;
+ CONST UINT8 *SizeValue;
+
+ INT32 AddressCells;
+ INT32 SizeCells;
+
+ CONST UINT8 *Data;
+ INT32 DataSize;
+
+ // The physical base address for the device
+ UINT64 BaseAddress;
+ // The Base address length
+ UINT64 BaseAddressLength;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ Status = FdtGetParentAddressInfo (
+ Fdt,
+ DeviceNode,
+ &AddressCells,
+ &SizeCells
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Don't support more than 64 bits and less than 32 bits addresses.
+ if ((AddressCells < 1) ||
+ (AddressCells > 2) ||
+ (SizeCells < 1) ||
+ (SizeCells > 2))
+ {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ Data = fdt_getprop (Fdt, DeviceNode, "reg", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize < (INT32)(sizeof (UINT32) *
+ GET_DT_REG_ADDRESS_OFFSET (1, AddressCells, SizeCells)) - 1))
+ {
+ // If error or not enough space.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ if (AddressCells == 2) {
+ BaseAddress = FdtReadUnaligned64 ((UINT64 *)Data);
+ } else {
+ BaseAddress = FdtReadUnaligned32 ((UINT32 *)Data);
+ }
+
+ SizeValue = Data + (sizeof (UINT32) *
+ GET_DT_REG_SIZE_OFFSET (0, AddressCells, SizeCells));
+ if (SizeCells == 2) {
+ BaseAddressLength = FdtReadUnaligned64 ((UINT64 *)SizeValue);
+ } else {
+ BaseAddressLength = FdtReadUnaligned32 ((UINT32 *)SizeValue);
+ }
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ DevDesc,
+ BaseAddress,
+ BaseAddressLength
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
+
+/** Device dispatcher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+ @param [in] CompatibleInfo List of "compatible" property values for
+ the device.
+ @param [in] DevDesc A NULL terminated string containing a short
+ description of the device.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+DeviceDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch,
+ IN CONST COMPATIBILITY_INFO *CompatibleInfo,
+ IN CONST CHAR8 *DevDesc
+ )
+{
+ EFI_STATUS Status;
+ INT32 DevNode;
+ UINT32 Index;
+ UINT32 DevNodeCount;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ // Count the number of device nodes.
+ Status = FdtCountCompatNodeInBranch (
+ Fdt,
+ FdtBranch,
+ CompatibleInfo,
+ &DevNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ if (DevNodeCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ DevNode = FdtBranch;
+ for (Index = 0; Index < DevNodeCount; Index++) {
+ // Search the next device node in the branch.
+ Status = FdtGetNextCompatNodeInBranch (
+ Fdt,
+ FdtBranch,
+ CompatibleInfo,
+ &DevNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ if (Status == EFI_NOT_FOUND) {
+ // Should have found the node.
+ Status = EFI_ABORTED;
+ }
+
+ goto exit_handler;
+ }
+
+ Status = DeviceNodeParser (
+ FdtParserHandle,
+ DevNode,
+ Index,
+ DevDesc
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ goto exit_handler;
+ }
+ } // for
+
+exit_handler:
+ return Status;
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Common/DeviceParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Common/DeviceParser.h
new file mode 100755
index 000000000000..5bf4dabed88f
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Common/DeviceParser.h
@@ -0,0 +1,37 @@
+/** @file
+ Device Node Address Range Parser.
+
+ Copyright (c) 2021 - 2024, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef DEVICE_PARSER_H_
+#define DEVICE_PARSER_H_
+
+/** Device dispatcher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+ @param [in] CompatibleInfo List of "compatible" property values for
+ the device.
+ @param [in] DevDesc A NULL terminated string containing a short
+ description of the device.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+DeviceDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch,
+ IN CONST COMPATIBILITY_INFO *CompatibleInfo,
+ IN CONST CHAR8 *DevDesc
+ );
+
+#endif // DEVICE_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtInfoParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtInfoParser.c
new file mode 100755
index 000000000000..eb758b3317b0
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtInfoParser.c
@@ -0,0 +1,191 @@
+/** @file
+ A Flattened Device Tree parser that scans the platform devices to
+ retrieve the base address and the range.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include
+#include
+#include
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "Arm/Gic/ArmGicDispatcher.h"
+#include "PciConfigSpaceParser.h"
+#include "SerialPortParser.h"
+#include "RtcParser.h"
+
+/** Function pointer to a parser function.
+
+ @param [in] ParserHandle Handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *FDT_HW_INFO_PARSER_FUNC)(
+ IN CONST FDT_HW_INFO_PARSER_HANDLE ParserHandle,
+ IN INT32 FdtBranch
+ );
+
+/** Ordered table of parsers/dispatchers.
+
+ A parser parses a Device Tree to retrieve relevant
+ hardware information.
+
+ This can also be a dispatcher, i.e. a function that
+ is not parsing a Device Tree but calling other parsers.
+*/
+STATIC CONST FDT_HW_INFO_PARSER_FUNC HwInfoParserTable[] = {
+ ArmGicDispatcher,
+ PciConfigInfoParser,
+ SerialPortDispatcher,
+ RtcDispatcher
+};
+
+/** Main dispatcher: sequentially call the parsers/dispatchers
+ of the HwInfoParserTable.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Parser did not find any device information
+ in the FDT.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+MainDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+
+ if (fdt_check_header (FdtParserHandle->Fdt) < 0) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < ARRAY_SIZE (HwInfoParserTable); Index++) {
+ Status = HwInfoParserTable[Index](
+ FdtParserHandle,
+ FdtBranch
+ );
+ if (EFI_ERROR (Status) &&
+ (Status != EFI_NOT_FOUND))
+ {
+ // If EFI_NOT_FOUND, the parser didn't find information in the DT.
+ // Don't trigger an error.
+ ASSERT (FALSE);
+ return Status;
+ }
+ } // for
+
+ return EFI_SUCCESS;
+}
+
+/** A callback function that is called with the information about
+ the platform device found in the FDT and is used to populate
+ the PLATFORM_DEVICE_INFO structure.
+
+ @param [in] Context Pointer to the platform device information structure.
+ @param [in] Desc A NULL terminated string describing the device.
+ @param [in] Base Base address of the device.
+ @param [in] Range Base address range of the device.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Maximum platform device count exceeded.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+HwAddressInfo (
+ IN VOID *Context,
+ IN CONST CHAR8 *Desc,
+ IN UINT64 Base,
+ IN UINT64 Range
+ )
+{
+ PLATFROM_DEVICE_INFO *PlatInfo;
+ UINTN Index;
+
+ if (Context == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((DEBUG_INFO, "%a: 0x%llx - 0x%llx\n", Desc, Base, Range));
+
+ // The address range cannot be zero.
+ if (Range == 0) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "Error: Invalid device range: %a: 0x%llx - 0x%llx\n",
+ Desc,
+ Base,
+ Range
+ ));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PlatInfo = (PLATFROM_DEVICE_INFO *)Context;
+ if (PlatInfo->MaxDevices >= MAX_PLAT_DEVICE_COUNT) {
+ DEBUG ((DEBUG_ERROR, "Error: Maximum platform device count exceeded!\n"));
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Index = PlatInfo->MaxDevices++;
+ PlatInfo->Dev[Index].BaseAddress = Base;
+ PlatInfo->Dev[Index].Length = Range;
+ AsciiStrnCpyS (PlatInfo->Dev[Index].Desc, 16, Desc, AsciiStrLen (Desc));
+
+ return EFI_SUCCESS;
+}
+
+/** Parse the platform FDT and populate the platform device info.
+
+ @param [in] FdtBase Pointer to the platform FDT.
+ @param [in] PlatInfo Pointer to the platform device info to populate.
+
+ @retval EFI_SUCCESS Success.
+ @retval EFI_INVALID_PARAMETER A parameter is invalid.
+ @retval EFI_NOT_FOUND Parser did not find any device information
+ in the FDT.
+**/
+EFI_STATUS
+EFIAPI
+ArmParsePlatformDeviceFdt (
+ IN VOID *FdtBase,
+ IN PLATFROM_DEVICE_INFO *PlatInfo
+ )
+{
+ FDT_HW_INFO_PARSER Parser;
+
+ if ((FdtBase == NULL) ||
+ (PlatInfo == NULL) ||
+ (fdt_check_header (FdtBase) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Parser.Fdt = FdtBase;
+ Parser.HwAddressInfo = HwAddressInfo;
+ Parser.Context = PlatInfo;
+
+ return MainDispatcher (&Parser, 0);
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtInfoParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtInfoParser.h
new file mode 100755
index 000000000000..8362a851d388
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtInfoParser.h
@@ -0,0 +1,52 @@
+/** @file
+ A Flattened Device Tree parser that scans the platform devices to
+ retrieve the base address and the range.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef FDT_INFO_PARSER_H_
+#define FDT_INFO_PARSER_H_
+
+/** Function pointer called by the parser to add information.
+
+ @param [in] Context A pointer to the caller's context.
+ @param [in] Desc An optional NULL terminated string
+ describing the hardware resource.
+ @param [in] Base Base address.
+ @param [in] Range Address range.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Maximum platform device count exceeded.
+**/
+typedef
+EFI_STATUS
+(EFIAPI *HW_ADDRESS_INFO)(
+ IN VOID *Context,
+ IN CONST CHAR8 *Desc,
+ IN UINT64 Base,
+ IN UINT64 Range
+ );
+
+/** A structure describing the instance of the FdtHwInfoParser.
+*/
+typedef struct FdtHwInfoParser {
+ /// Pointer to the HwDataSource i.e. the
+ /// Flattened Device Tree (Fdt).
+ VOID *Fdt;
+
+ /// Pointer to the caller's context.
+ VOID *Context;
+
+ /// Callback function to notify information
+ /// about platform devices found in the FDT.
+ HW_ADDRESS_INFO HwAddressInfo;
+} FDT_HW_INFO_PARSER;
+
+/** A pointer type for FDT_HW_INFO_PARSER.
+*/
+typedef FDT_HW_INFO_PARSER *FDT_HW_INFO_PARSER_HANDLE;
+
+#endif // FDT_INFO_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtUtility.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtUtility.c
new file mode 100755
index 000000000000..3bf059a0d1bc
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtUtility.c
@@ -0,0 +1,879 @@
+/** @file
+ Flattened device tree utility.
+
+ Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Device tree Specification - Release v0.3
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml
+ - linux//Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml
+**/
+
+#include "FdtUtility.h"
+
+/** Check whether a node has the input name.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to check the name.
+ @param [in] SearchName Node name to search.
+ This is a NULL terminated string.
+
+ @retval True The node has the input name.
+ @retval FALSE Otherwise, or error.
+**/
+STATIC
+BOOLEAN
+EFIAPI
+FdtNodeHasName (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ IN CONST VOID *SearchName
+ )
+{
+ CONST CHAR8 *NodeName;
+ UINT32 Length;
+
+ if ((Fdt == NULL) ||
+ (SearchName == NULL))
+ {
+ ASSERT (FALSE);
+ return FALSE;
+ }
+
+ // Always compare the whole string. Don't stop at the "@" char.
+ Length = (UINT32)AsciiStrLen (SearchName);
+
+ // Get the address of the node name.
+ NodeName = fdt_offset_ptr (Fdt, Node + FDT_TAGSIZE, Length + 1);
+ if (NodeName == NULL) {
+ return FALSE;
+ }
+
+ // SearchName must be longer than the node name.
+ if (Length > AsciiStrLen (NodeName)) {
+ return FALSE;
+ }
+
+ if (AsciiStrnCmp (NodeName, SearchName, Length) != 0) {
+ return FALSE;
+ }
+
+ // The name matches perfectly, or
+ // the node name is XXX@addr and the XXX matches.
+ if ((NodeName[Length] == '\0') ||
+ (NodeName[Length] == '@'))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/** Iterate through the list of strings in the Context,
+ and check whether at least one string is matching the
+ "compatible" property of the node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to operate the check on.
+ @param [in] CompatInfo COMPATIBILITY_INFO containing the list of compatible
+ strings to compare with the "compatible" property
+ of the node.
+
+ @retval TRUE At least one string matched, the node is compatible.
+ @retval FALSE Otherwise, or error.
+**/
+BOOLEAN
+EFIAPI
+FdtNodeIsCompatible (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ IN CONST VOID *CompatInfo
+ )
+{
+ UINT32 Index;
+ CONST COMPATIBILITY_STR *CompatibleTable;
+ UINT32 Count;
+ CONST VOID *Prop;
+ INT32 PropLen;
+
+ if ((Fdt == NULL) ||
+ (CompatInfo == NULL))
+ {
+ ASSERT (FALSE);
+ return FALSE;
+ }
+
+ Count = ((COMPATIBILITY_INFO *)CompatInfo)->Count;
+ CompatibleTable = ((COMPATIBILITY_INFO *)CompatInfo)->CompatTable;
+
+ // Get the "compatible" property.
+ Prop = fdt_getprop (Fdt, Node, "compatible", &PropLen);
+ if ((Prop == NULL) || (PropLen < 0)) {
+ return FALSE;
+ }
+
+ for (Index = 0; Index < Count; Index++) {
+ if (fdt_stringlist_contains (
+ Prop,
+ PropLen,
+ CompatibleTable[Index].CompatStr
+ ))
+ {
+ return TRUE;
+ }
+ } // for
+
+ return FALSE;
+}
+
+/** Check whether a node has a property.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to operate the check on.
+ @param [in] PropertyName Name of the property to search.
+ This is a NULL terminated string.
+
+ @retval True The node has the property.
+ @retval FALSE Otherwise, or error.
+**/
+BOOLEAN
+EFIAPI
+FdtNodeHasProperty (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ IN CONST VOID *PropertyName
+ )
+{
+ INT32 Size;
+ CONST VOID *Prop;
+
+ if ((Fdt == NULL) ||
+ (PropertyName == NULL))
+ {
+ ASSERT (FALSE);
+ return FALSE;
+ }
+
+ Prop = fdt_getprop (Fdt, Node, PropertyName, &Size);
+ if ((Prop == NULL) || (Size < 0)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** Get the next node in the whole DT fulfilling a condition.
+
+ The condition to fulfill is checked by the NodeChecker function.
+ Context is passed to NodeChecker.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node fulfilling the
+ condition.
+ @param [in, out] Depth Depth is incremented/decremented of the depth
+ difference between the input Node and the
+ output Node.
+ E.g.: If the output Node is a child node
+ of the input Node, contains (+1).
+ @param [in] NodeChecker Function called to check if the condition
+ is fulfilled.
+ @param [in] Context Context for the NodeChecker.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FdtGetNextCondNode (
+ IN CONST VOID *Fdt,
+ IN OUT INT32 *Node,
+ IN OUT INT32 *Depth,
+ IN NODE_CHECKER_FUNC NodeChecker,
+ IN CONST VOID *Context
+ )
+{
+ INT32 CurrNode;
+
+ if ((Fdt == NULL) ||
+ (Node == NULL) ||
+ (Depth == NULL) ||
+ (NodeChecker == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CurrNode = *Node;
+ do {
+ CurrNode = fdt_next_node (Fdt, CurrNode, Depth);
+ if ((CurrNode == -FDT_ERR_NOTFOUND) ||
+ (*Depth < 0))
+ {
+ // End of the tree, no matching node found.
+ return EFI_NOT_FOUND;
+ } else if (CurrNode < 0) {
+ // An error occurred.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ } while (!NodeChecker (Fdt, CurrNode, Context));
+
+ // Matching node found.
+ *Node = CurrNode;
+ return EFI_SUCCESS;
+}
+
+/** Get the next node in a branch fulfilling a condition.
+
+ The condition to fulfill is checked by the NodeChecker function.
+ Context is passed to NodeChecker.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this
+ branch.
+ Write (-1) to search the whole tree.
+ @param [in] NodeChecker Function called to check if the condition
+ is fulfilled.
+ @param [in] Context Context for the NodeChecker.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset
+ of the next node in the branch
+ fulfilling the condition.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FdtGetNextCondNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN NODE_CHECKER_FUNC NodeChecker,
+ IN CONST VOID *Context,
+ IN OUT INT32 *Node
+ )
+{
+ EFI_STATUS Status;
+ INT32 CurrNode;
+ INT32 Depth;
+
+ if ((Fdt == NULL) ||
+ (Node == NULL) ||
+ (NodeChecker == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CurrNode = FdtBranch;
+ Depth = 0;
+
+ // First, check the Node is in the sub-nodes of the branch.
+ // This allows to find the relative depth of Node in the branch.
+ if (CurrNode != *Node) {
+ for (CurrNode = fdt_next_node (Fdt, CurrNode, &Depth);
+ (CurrNode >= 0) && (Depth > 0);
+ CurrNode = fdt_next_node (Fdt, CurrNode, &Depth))
+ {
+ if (CurrNode == *Node) {
+ // Node found.
+ break;
+ }
+ } // for
+
+ if ((CurrNode < 0) || (Depth <= 0)) {
+ // Node is not a node in the branch, or an error occurred.
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ // Get the next node in the tree fulfilling the condition,
+ // in any branch.
+ Status = FdtGetNextCondNode (
+ Fdt,
+ Node,
+ &Depth,
+ NodeChecker,
+ Context
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (Status == EFI_NOT_FOUND);
+ return Status;
+ }
+
+ if (Depth <= 0) {
+ // The node found is not in the right branch.
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Get the next node in a branch having a matching name.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] NodeName The node name to search.
+ This is a NULL terminated string.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node in the branch
+ having a matching name.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetNextNamedNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *NodeName,
+ IN OUT INT32 *Node
+ )
+{
+ return FdtGetNextCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ FdtNodeHasName,
+ NodeName,
+ Node
+ );
+}
+
+/** Get the next node in a branch with at least one compatible property.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] CompatNamesInfo Table of compatible strings to compare with
+ the compatible property of the node.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node in the branch
+ being compatible.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetNextCompatNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST COMPATIBILITY_INFO *CompatNamesInfo,
+ IN OUT INT32 *Node
+ )
+{
+ return FdtGetNextCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ FdtNodeIsCompatible,
+ (CONST VOID *)CompatNamesInfo,
+ Node
+ );
+}
+
+/** Get the next node in a branch having the PropName property.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] PropName Name of the property to search.
+ This is a NULL terminated string.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node in the branch
+ being compatible.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetNextPropNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *PropName,
+ IN OUT INT32 *Node
+ )
+{
+ return FdtGetNextCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ FdtNodeHasProperty,
+ (CONST VOID *)PropName,
+ Node
+ );
+}
+
+/** Count the number of Device Tree nodes fulfilling a condition
+ in a Device Tree branch.
+
+ The condition to fulfill is checked by the NodeChecker function.
+ Context is passed to NodeChecker.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] NodeChecker Function called to check the condition is
+ fulfilled.
+ @param [in] Context Context for the NodeChecker.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+FdtCountCondNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN NODE_CHECKER_FUNC NodeChecker,
+ IN CONST VOID *Context,
+ OUT UINT32 *NodeCount
+ )
+{
+ EFI_STATUS Status;
+ INT32 CurrNode;
+
+ if ((Fdt == NULL) ||
+ (NodeChecker == NULL) ||
+ (NodeCount == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *NodeCount = 0;
+ CurrNode = FdtBranch;
+ while (TRUE) {
+ Status = FdtGetNextCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ NodeChecker,
+ Context,
+ &CurrNode
+ );
+ if (EFI_ERROR (Status) &&
+ (Status != EFI_NOT_FOUND))
+ {
+ ASSERT (FALSE);
+ return Status;
+ } else if (Status == EFI_NOT_FOUND) {
+ break;
+ }
+
+ (*NodeCount)++;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Count the number of nodes in a branch with the input name.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] NodeName Node name to search.
+ This is a NULL terminated string.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtCountNamedNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *NodeName,
+ OUT UINT32 *NodeCount
+ )
+{
+ return FdtCountCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ FdtNodeHasName,
+ NodeName,
+ NodeCount
+ );
+}
+
+/** Count the number of nodes in a branch with at least
+ one compatible property.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] CompatNamesInfo Table of compatible strings to
+ compare with the compatible property
+ of the node.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtCountCompatNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST COMPATIBILITY_INFO *CompatNamesInfo,
+ OUT UINT32 *NodeCount
+ )
+{
+ return FdtCountCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ FdtNodeIsCompatible,
+ CompatNamesInfo,
+ NodeCount
+ );
+}
+
+/** Count the number of nodes in a branch having the PropName property.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] PropName Name of the property to search.
+ This is a NULL terminated string.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtCountPropNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *PropName,
+ OUT UINT32 *NodeCount
+ )
+{
+ return FdtCountCondNodeInBranch (
+ Fdt,
+ FdtBranch,
+ FdtNodeHasProperty,
+ PropName,
+ NodeCount
+ );
+}
+
+/** Get the interrupt-controller node handling the interrupts of
+ the input node.
+
+ To do this, recursively search a node with either the "interrupt-controller"
+ or the "interrupt-parent" property in the parents of Node.
+
+ Devicetree Specification, Release v0.3,
+ 2.4.1 "Properties for Interrupt Generating Devices":
+ Because the hierarchy of the nodes in the interrupt tree
+ might not match the devicetree, the interrupt-parent
+ property is available to make the definition of an
+ interrupt parent explicit. The value is the phandle to the
+ interrupt parent. If this property is missing from a
+ device, its interrupt parent is assumed to be its devicetree
+ parent.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to start the search.
+ @param [out] IntcNode If success, contains the offset of the
+ interrupt-controller node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_NOT_FOUND No interrupt-controller node found.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetIntcParentNode (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *IntcNode
+ )
+{
+ CONST UINT32 *PHandle;
+ INT32 Size;
+ CONST VOID *Prop;
+
+ if ((Fdt == NULL) ||
+ (IntcNode == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ while (TRUE) {
+ // Check whether the node has the "interrupt-controller" property.
+ Prop = fdt_getprop (Fdt, Node, "interrupt-controller", &Size);
+ if ((Prop != NULL) && (Size >= 0)) {
+ // The interrupt-controller has been found.
+ *IntcNode = Node;
+ return EFI_SUCCESS;
+ } else {
+ // Check whether the node has the "interrupt-parent" property.
+ PHandle = fdt_getprop (Fdt, Node, "interrupt-parent", &Size);
+ if ((PHandle != NULL) && (Size == sizeof (UINT32))) {
+ // The phandle of the interrupt-controller has been found.
+ // Search the node having this phandle and return it.
+ Node = fdt_node_offset_by_phandle (Fdt, FdtReadUnaligned32 (PHandle));
+ if (Node < 0) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ *IntcNode = Node;
+ return EFI_SUCCESS;
+ } else if (Size != -FDT_ERR_NOTFOUND) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ }
+
+ if (Node == 0) {
+ // We are at the root of the tree. Not parent available.
+ return EFI_NOT_FOUND;
+ }
+
+ // Get the parent of the node.
+ Node = fdt_parent_offset (Fdt, Node);
+ if (Node < 0) {
+ // An error occurred.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ } // while
+}
+
+/** Get the "interrupt-cells" property value of the node.
+
+ The "interrupts" property requires to know the number of cells used
+ to encode an interrupt. This information is stored in the
+ interrupt-controller of the input Node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
+ @param [in] IntcNode Offset of an interrupt-controller node.
+ @param [out] IntCells If success, contains the "interrupt-cells"
+ property of the IntcNode.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetInterruptCellsInfo (
+ IN CONST VOID *Fdt,
+ IN INT32 IntcNode,
+ OUT INT32 *IntCells
+ )
+{
+ CONST UINT32 *Data;
+ INT32 Size;
+
+ if ((Fdt == NULL) ||
+ (IntCells == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Data = fdt_getprop (Fdt, IntcNode, "#interrupt-cells", &Size);
+ if ((Data == NULL) || (Size != sizeof (UINT32))) {
+ // If error or not on one UINT32 cell.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ *IntCells = FdtReadUnaligned32 (Data);
+
+ return EFI_SUCCESS;
+}
+
+/** Get the "#address-cells" and/or "#size-cells" property of the node.
+
+ According to the Device Tree specification, s2.3.5 "#address-cells and
+ #size-cells":
+ "If missing, a client program should assume a default value of 2 for
+ #address-cells, and a value of 1 for #size-cells."
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node having to get the
+ "#address-cells" and "#size-cells"
+ properties from.
+ @param [out] AddressCells If success, number of address-cells.
+ If the property is not available,
+ default value is 2.
+ @param [out] SizeCells If success, number of size-cells.
+ If the property is not available,
+ default value is 1.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetAddressInfo (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *AddressCells, OPTIONAL
+ OUT INT32 *SizeCells OPTIONAL
+ )
+{
+ if (Fdt == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (AddressCells != NULL) {
+ *AddressCells = fdt_address_cells (Fdt, Node);
+ if (*AddressCells < 0) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ }
+
+ if (SizeCells != NULL) {
+ *SizeCells = fdt_size_cells (Fdt, Node);
+ if (*SizeCells < 0) {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Get the "#address-cells" and/or "#size-cells" property of the parent node.
+
+ According to the Device Tree specification, s2.3.5 "#address-cells and
+ #size-cells":
+ "If missing, a client program should assume a default value of 2 for
+ #address-cells, and a value of 1 for #size-cells."
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node having to get the
+ "#address-cells" and "#size-cells"
+ properties from its parent.
+ @param [out] AddressCells If success, number of address-cells.
+ If the property is not available,
+ default value is 2.
+ @param [out] SizeCells If success, number of size-cells.
+ If the property is not available,
+ default value is 1.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetParentAddressInfo (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *AddressCells, OPTIONAL
+ OUT INT32 *SizeCells OPTIONAL
+ )
+{
+ if (Fdt == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Node = fdt_parent_offset (Fdt, Node);
+ if (Node < 0) {
+ // End of the tree, or an error occurred.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ return FdtGetAddressInfo (Fdt, Node, AddressCells, SizeCells);
+}
+
+/** A helper function to support unaligned read access for UINT64 types.
+
+ @param [in] Data Pointer to UINT64 data that may be unaligned.
+
+ @retval The UINT64 value read from the input data pointer.
+**/
+UINT64
+FdtReadUnaligned64 (
+ CONST UINT64 *Data
+ )
+{
+ return SwapBytes64 (ReadUnaligned64 (Data));
+}
+
+/** A helper function to support unaligned read access for UINT32 types.
+
+ @param [in] Data Pointer to a UINT32 data that may be unaligned.
+
+ @retval The UINT32 value read from the input data pointer.
+**/
+UINT32
+FdtReadUnaligned32 (
+ CONST UINT32 *Data
+ )
+{
+ return SwapBytes32 (ReadUnaligned32 (Data));
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtUtility.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtUtility.h
new file mode 100755
index 000000000000..79e601622258
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/FdtUtility.h
@@ -0,0 +1,517 @@
+/** @file
+ Flattened device tree utility.
+
+ Copyright (c) 2021, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Device tree Specification - Release v0.3
+ - linux/Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml
+ - linux//Documentation/devicetree/bindings/interrupt-controller/arm%2Cgic.yaml
+**/
+
+#ifndef FDT_UTILITY_H_
+#define FDT_UTILITY_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/** Get the offset of an address in a "reg" Device Tree property.
+
+ In a Device Tree, the "reg" property stores address/size couples.
+ They are stored on N 32-bits cells.
+ Based on the value of the #address-cells, the #size-cells and the
+ index in the "reg" property, compute the number of 32-bits cells
+ to skip.
+
+ @param [in] Index Index in the reg property.
+ @param [in] AddrCells Number of cells used to store an address.
+ @param [in] SizeCells Number of cells used to store the size of
+ an address.
+
+ @retval Number of 32-bits cells to skip to access the address.
+*/
+#define GET_DT_REG_ADDRESS_OFFSET(Index, AddrCells, SizeCells) ( \
+ (Index) * ((AddrCells) + (SizeCells)) \
+ )
+
+/** Get the offset of an address size in a "reg" Device Tree property.
+
+ In a Device Tree, the "reg" property stores address/size couples.
+ They are stored on N 32-bits cells.
+ Based on the value of the #address-cells, the #size-cells and the
+ index in the "reg" property, compute the number of 32-bits cells
+ to skip.
+
+ @param [in] Index Index in the reg property.
+ @param [in] AddrCells Number of cells used to store an address.
+ @param [in] SizeCells Number of cells used to store the size of
+ an address.
+
+ @retval Number of 32-bits cells to skip to access the address size.
+*/
+#define GET_DT_REG_SIZE_OFFSET(Index, AddrCells, SizeCells) ( \
+ GET_DT_REG_ADDRESS_OFFSET ((Index), (AddrCells), (SizeCells)) + \
+ (SizeCells) \
+ )
+
+/// Maximum string length for compatible names.
+#define COMPATIBLE_STR_LEN (32U)
+
+/// Interrupt macros
+#define PPI_OFFSET (16U)
+#define SPI_OFFSET (32U)
+#define DT_PPI_IRQ (1U)
+#define DT_SPI_IRQ (0U)
+#define DT_IRQ_IS_EDGE_TRIGGERED(x) ((((x) & (BIT0 | BIT1)) != 0))
+#define DT_IRQ_IS_ACTIVE_LOW(x) ((((x) & (BIT1 | BIT3)) != 0))
+#define IRQ_TYPE_OFFSET (0U)
+#define IRQ_NUMBER_OFFSET (1U)
+#define IRQ_FLAGS_OFFSET (2U)
+
+/** Get the interrupt Id of an interrupt described in a fdt.
+
+ Data must describe a GIC interrupt. A GIC interrupt is on at least
+ 3 UINT32 cells.
+ This function DOES NOT SUPPORT extended SPI range and extended PPI range.
+
+ @param [in] Data Pointer to the first cell of an "interrupts" property.
+
+ @retval The interrupt id.
+**/
+UINT32
+EFIAPI
+FdtGetInterruptId (
+ UINT32 CONST *Data
+ );
+
+/** Get the ACPI interrupt flags of an interrupt described in a fdt.
+
+ Data must describe a GIC interrupt. A GIC interrupt is on at least
+ 3 UINT32 cells.
+
+ @param [in] Data Pointer to the first cell of an "interrupts" property.
+
+ @retval The interrupt flags (for ACPI).
+**/
+UINT32
+EFIAPI
+FdtGetInterruptFlags (
+ UINT32 CONST *Data
+ );
+
+/** A structure describing a compatibility string.
+*/
+typedef struct CompatStr {
+ CONST CHAR8 CompatStr[COMPATIBLE_STR_LEN];
+} COMPATIBILITY_STR;
+
+/** Structure containing a list of compatible names and their count.
+*/
+typedef struct CompatibilityInfo {
+ /// Count of entries in the NAME_TABLE.
+ UINT32 Count;
+
+ /// Pointer to a table storing the names.
+ CONST COMPATIBILITY_STR *CompatTable;
+} COMPATIBILITY_INFO;
+
+/** Operate a check on a Device Tree node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] NodeOffset Offset of the node to compare input string.
+ @param [in] Context Context to operate the check on the node.
+
+ @retval True The check is correct.
+ @retval FALSE Otherwise, or error.
+**/
+typedef
+BOOLEAN
+(EFIAPI *NODE_CHECKER_FUNC)(
+ IN CONST VOID *Fdt,
+ IN INT32 NodeOffset,
+ IN CONST VOID *Context
+ );
+
+/** Iterate through the list of strings in the Context,
+ and check whether at least one string is matching the
+ "compatible" property of the node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to operate the check on.
+ @param [in] CompatInfo COMPATIBILITY_INFO containing the list of compatible
+ strings to compare with the "compatible" property
+ of the node.
+
+ @retval TRUE At least one string matched, the node is compatible.
+ @retval FALSE Otherwise, or error.
+**/
+BOOLEAN
+EFIAPI
+FdtNodeIsCompatible (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ IN CONST VOID *CompatInfo
+ );
+
+/** Check whether a node has a property.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to operate the check on.
+ @param [in] PropertyName Name of the property to search.
+ This is a NULL terminated string.
+
+ @retval True The node has the property.
+ @retval FALSE Otherwise, or error.
+**/
+BOOLEAN
+EFIAPI
+FdtNodeHasProperty (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ IN CONST VOID *PropertyName
+ );
+
+/** Get the next node in a branch having a matching name.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] NodeName The node name to search.
+ This is a NULL terminated string.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node in the branch
+ having a matching name.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetNextNamedNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *NodeName,
+ IN OUT INT32 *Node
+ );
+
+/** Get the next node in a branch with at least one compatible property.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] CompatNamesInfo Table of compatible strings to compare with
+ the compatible property of the node.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node in the branch
+ being compatible.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetNextCompatNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST COMPATIBILITY_INFO *CompatNamesInfo,
+ IN OUT INT32 *Node
+ );
+
+/** Get the next node in a branch having the PropName property.
+
+ The Device tree is traversed in a depth-first search, starting from Node.
+ The input Node is skipped.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] PropName Name of the property to search.
+ This is a NULL terminated string.
+ @param [in, out] Node At entry: Node offset to start the search.
+ This first node is skipped.
+ Write (-1) to search the whole tree.
+ At exit: If success, contains the offset of
+ the next node in the branch
+ being compatible.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND No matching node found.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetNextPropNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *PropName,
+ IN OUT INT32 *Node
+ );
+
+/** Count the number of nodes in a branch with the input name.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] NodeName Node name to search.
+ This is a NULL terminated string.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtCountNamedNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *NodeName,
+ OUT UINT32 *NodeCount
+ );
+
+/** Count the number of nodes in a branch with at least
+ one compatible property.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] CompatibleTable Table of compatible strings to
+ compare with the compatible property
+ of the node.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtCountCompatNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST COMPATIBILITY_INFO *CompatNamesInfo,
+ OUT UINT32 *NodeCount
+ );
+
+/** Count the number of nodes in a branch having the PropName property.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] FdtBranch Only search in the sub-nodes of this branch.
+ Write (-1) to search the whole tree.
+ @param [in] PropName Name of the property to search.
+ This is a NULL terminated string.
+ @param [out] NodeCount If success, contains the count of nodes
+ fulfilling the condition.
+ Can be 0.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtCountPropNodeInBranch (
+ IN CONST VOID *Fdt,
+ IN INT32 FdtBranch,
+ IN CONST CHAR8 *PropName,
+ OUT UINT32 *NodeCount
+ );
+
+/** Get the interrupt-controller node handling the interrupts of
+ the input node.
+
+ To do this, recursively search a node with either the "interrupt-controller"
+ or the "interrupt-parent" property in the parents of Node.
+
+ Devicetree Specification, Release v0.3,
+ 2.4.1 "Properties for Interrupt Generating Devices":
+ Because the hierarchy of the nodes in the interrupt tree
+ might not match the devicetree, the interrupt-parent
+ property is available to make the definition of an
+ interrupt parent explicit. The value is the phandle to the
+ interrupt parent. If this property is missing from a
+ device, its interrupt parent is assumed to be its devicetree
+ parent.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node to start the search.
+ @param [out] IntcNode If success, contains the offset of the
+ interrupt-controller node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_NOT_FOUND No interrupt-controller node found.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetIntcParentNode (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *IntcNode
+ );
+
+/** Get the "interrupt-cells" property value of the node.
+
+ The "interrupts" property requires to know the number of cells used
+ to encode an interrupt. This information is stored in the
+ interrupt-controller of the input Node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
+ @param [in] IntcNode Offset of an interrupt-controller node.
+ @param [out] IntCells If success, contains the "interrupt-cells"
+ property of the IntcNode.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetInterruptCellsInfo (
+ IN CONST VOID *Fdt,
+ IN INT32 IntcNode,
+ OUT INT32 *InterruptCells
+ );
+
+/** Get the "#address-cells" and/or "#size-cells" property of the node.
+
+ According to the Device Tree specification, s2.3.5 "#address-cells and
+ #size-cells":
+ "If missing, a client program should assume a default value of 2 for
+ #address-cells, and a value of 1 for #size-cells."
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node having to get the
+ "#address-cells" and "#size-cells"
+ properties from.
+ @param [out] AddressCells If success, number of address-cells.
+ If the property is not available,
+ default value is 2.
+ @param [out] SizeCells If success, number of size-cells.
+ If the property is not available,
+ default value is 1.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetAddressInfo (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *AddressCells, OPTIONAL
+ OUT INT32 *SizeCells OPTIONAL
+ );
+
+/** Get the "#address-cells" and/or "#size-cells" property of the parent node.
+
+ According to the Device Tree specification, s2.3.5 "#address-cells and
+ #size-cells":
+ "If missing, a client program should assume a default value of 2 for
+ #address-cells, and a value of 1 for #size-cells."
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node having to get the
+ "#address-cells" and "#size-cells"
+ properties from its parent.
+ @param [out] AddressCells If success, number of address-cells.
+ If the property is not available,
+ default value is 2.
+ @param [out] SizeCells If success, number of size-cells.
+ If the property is not available,
+ default value is 1.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetParentAddressInfo (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *AddressCells, OPTIONAL
+ OUT INT32 *SizeCells OPTIONAL
+ );
+
+/** For relevant architectures, get the "#address-cells" and/or "#size-cells"
+ property of the node.
+
+ According to the Device Tree specification, s2.3.5 "#address-cells and
+ #size-cells":
+ "If missing, a client program should assume a default value of 2 for
+ #address-cells, and a value of 1 for #size-cells."
+
+ @param [in] Fdt Pointer to a Flattened Device Tree.
+ @param [in] Node Offset of the node having to get the
+ "#address-cells" and "#size-cells"
+ properties from.
+ @param [out] AddressCells If success, number of address-cells.
+ If the property is not available,
+ default value is 2.
+ @param [out] SizeCells If success, number of size-cells.
+ If the property is not available,
+ default value is 1.
+
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+FdtGetIntcAddressCells (
+ IN CONST VOID *Fdt,
+ IN INT32 Node,
+ OUT INT32 *AddressCells, OPTIONAL
+ OUT INT32 *SizeCells OPTIONAL
+ );
+
+/** A helper function to support unaligned read access for UINT64 types.
+
+ @param [in] Data Pointer to UINT64 data that may be unaligned.
+
+ @retval The UINT64 value read from the input data pointer.
+**/
+UINT64
+FdtReadUnaligned64 (
+ CONST UINT64 *Data
+ );
+
+/** A helper function to support unaligned read access for UINT32 types.
+
+ @param [in] Data Pointer to a UINT32 data that may be unaligned.
+
+ @retval The UINT32 value read from the input data pointer.
+**/
+UINT32
+FdtReadUnaligned32 (
+ CONST UINT32 *Data
+ );
+
+#endif // FDT_UTILITY_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Pci/PciConfigSpaceParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Pci/PciConfigSpaceParser.c
new file mode 100755
index 000000000000..6bf70c662449
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Pci/PciConfigSpaceParser.c
@@ -0,0 +1,467 @@
+/** @file
+ PCI Configuration Space Parser.
+
+ Copyright (c) 2021 - 2024, ARM Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml
+ - PCI Firmware Specification - Revision 3.0
+ - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9
+ - Devicetree Specification Release v0.3
+ - linux kernel code
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "PciConfigSpaceParser.h"
+
+/** List of "compatible" property values for host PCIe bridges nodes.
+
+ Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR PciCompatibleStr[] = {
+ { "pci-host-ecam-generic" }
+};
+
+/** COMPATIBILITY_INFO structure for the PCIe.
+*/
+STATIC CONST COMPATIBILITY_INFO PciCompatibleInfo = {
+ ARRAY_SIZE (PciCompatibleStr),
+ PciCompatibleStr
+};
+
+/** PCI configuration space attributes.
+*/
+STATIC CONST CHAR8 *PciConfigSpaceAttributesInfo[] = {
+ "PCI Cfg Space",
+ "PCI I/O Space",
+ "PCI 32bit Mem",
+ "PCI 64bit Mem"
+};
+
+/** Get the Segment group (also called: Domain Id) of a host-pci node.
+
+ kernel/Documentation/devicetree/bindings/pci/pci.txt:
+ "It is required to either not set this property at all or set it for all
+ host bridges in the system"
+
+ The function checks the "linux,pci-domain" property of the host-pci node.
+ Either all host-pci nodes must have this property, or none of them. If the
+ property is available, read it. Otherwise dynamically assign the Ids.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
+ @param [in] HostPciNode Offset of a host-pci node.
+ @param [out] SegGroup Segment group assigned to the host-pci controller.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetPciSegGroup (
+ IN CONST VOID *Fdt,
+ IN INT32 HostPciNode,
+ OUT INT32 *SegGroup
+ )
+{
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ STATIC INT32 LocalSegGroup = 0;
+
+ if ((Fdt == NULL) ||
+ (SegGroup == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Data = fdt_getprop (Fdt, HostPciNode, "linux,pci-domain", &DataSize);
+ if ((Data == NULL) || (DataSize < 0)) {
+ // Did not find property, assign the DomainIds ourselves.
+ if (LocalSegGroup < 0) {
+ // "linux,pci-domain" property was defined for another node.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ *SegGroup = LocalSegGroup++;
+ return EFI_SUCCESS;
+ }
+
+ if ((DataSize > sizeof (UINT32)) ||
+ (LocalSegGroup > 0))
+ {
+ // Property on more than 1 cell or
+ // "linux,pci-domain" property was not defined for a node.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ // If one node has the "linux,pci-domain" property, then all the host-pci
+ // nodes must have it.
+ LocalSegGroup = -1;
+
+ *SegGroup = FdtReadUnaligned32 ((UINT32 *)Data);
+ return EFI_SUCCESS;
+}
+
+/** Parse the bus-range controlled by this host-pci node.
+
+ @param [in] Fdt Pointer to a Flattened Device Tree (Fdt).
+ @param [in] HostPciNode Offset of a host-pci node.
+ @param [out] StartBusNumber Start bus number
+ @param [out] EndBusNumber End bus number
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PopulateBusRange (
+ IN CONST VOID *Fdt,
+ IN INT32 HostPciNode,
+ OUT UINT8 *StartBusNumber,
+ OUT UINT8 *EndBusNumber
+ )
+{
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ UINT32 StartBus;
+ UINT32 EndBus;
+
+ if ((Fdt == NULL) ||
+ (StartBusNumber == NULL) ||
+ (EndBusNumber == NULL))
+ {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Data = fdt_getprop (Fdt, HostPciNode, "bus-range", &DataSize);
+ if ((Data == NULL) || (DataSize < 0)) {
+ // No evidence this property is mandatory. Use default values.
+ StartBus = 0;
+ EndBus = 255;
+ } else if (DataSize == (2 * sizeof (UINT32))) {
+ // If available, the property is on two integers.
+ StartBus = FdtReadUnaligned32 ((UINT32 *)Data);
+ Data += sizeof (UINT32);
+ EndBus = FdtReadUnaligned32 ((UINT32 *)Data);
+ } else {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ *StartBusNumber = StartBus;
+ *EndBusNumber = EndBus;
+
+ return EFI_SUCCESS;
+}
+
+/** Parse the PCI address map.
+
+ The PCI address map is available in the "ranges" device-tree property.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] HostPciNode Offset of a host-pci node.
+ @param [in] AddressCells # of cells used to encode an address on
+ the parent bus.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+ParseAddressMap (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 HostPciNode,
+ IN INT32 AddressCells
+ )
+{
+ EFI_STATUS Status;
+ CONST UINT8 *Data;
+ INT32 DataSize;
+ UINT32 Index;
+ UINT32 Offset;
+ UINT32 AddressMapSize;
+ UINT32 Count;
+ UINT32 PciAddressAttr;
+ UINT8 SpaceCode;
+ UINT64 PciAddress;
+ UINT64 CpuAddress;
+ UINT64 AddressSize;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ // The mapping is done on AddressMapSize bytes.
+ AddressMapSize = (PCI_ADDRESS_CELLS + AddressCells + PCI_SIZE_CELLS) *
+ sizeof (UINT32);
+
+ Data = fdt_getprop (Fdt, HostPciNode, "ranges", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize < 0) ||
+ ((DataSize % AddressMapSize) != 0))
+ {
+ // If error or not on AddressMapSize bytes.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ Count = DataSize / AddressMapSize;
+ Status = EFI_SUCCESS;
+ for (Index = 0; Index < Count; Index++) {
+ Offset = Index * AddressMapSize;
+
+ // Pci address attributes
+ PciAddressAttr = FdtReadUnaligned32 ((UINT32 *)&Data[Offset]);
+ SpaceCode = READ_PCI_SS (PciAddressAttr);
+ Offset += sizeof (UINT32);
+
+ // Pci address
+ PciAddress = FdtReadUnaligned64 ((UINT64 *)&Data[Offset]);
+ Offset += (PCI_ADDRESS_CELLS - 1) * sizeof (UINT32);
+
+ // Cpu address
+ if (AddressCells == 2) {
+ CpuAddress = FdtReadUnaligned64 ((UINT64 *)&Data[Offset]);
+ } else {
+ CpuAddress = FdtReadUnaligned32 ((UINT32 *)&Data[Offset]);
+ }
+
+ Offset += AddressCells * sizeof (UINT32);
+
+ // Address size
+ AddressSize = FdtReadUnaligned64 ((UINT64 *)&Data[Offset]);
+ Offset += PCI_SIZE_CELLS * sizeof (UINT32);
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ PciConfigSpaceAttributesInfo[SpaceCode],
+ CpuAddress,
+ AddressSize
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ break;
+ }
+ }
+ } // for
+
+ return Status;
+}
+
+/** Parse a Host-pci node.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] HostPciNode Offset of a host-pci node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+PciNodeParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 HostPciNode
+ )
+{
+ EFI_STATUS Status;
+ INT32 AddressCells;
+ INT32 SizeCells;
+ CONST UINT8 *Data;
+ CONST UINT8 *Range;
+ INT32 DataSize;
+ INT32 SegGroup;
+ UINT8 StartBusNumber;
+ UINT8 EndBusNumber;
+ UINT64 ConfigSpaceBaseAddress;
+ UINT64 ConfigSpaceBaseAddressRange;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ // Segment Group / DomainId
+ Status = GetPciSegGroup (Fdt, HostPciNode, &SegGroup);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Bus range
+ Status = PopulateBusRange (
+ Fdt,
+ HostPciNode,
+ &StartBusNumber,
+ &EndBusNumber
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ Status = FdtGetParentAddressInfo (
+ Fdt,
+ HostPciNode,
+ &AddressCells,
+ &SizeCells
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ // Only support 32/64 bits addresses.
+ if ((AddressCells < 1) ||
+ (AddressCells > 2) ||
+ (SizeCells < 1) ||
+ (SizeCells > 2))
+ {
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ Data = fdt_getprop (Fdt, HostPciNode, "reg", &DataSize);
+ if ((Data == NULL) ||
+ (DataSize != ((AddressCells + SizeCells) * sizeof (UINT32))))
+ {
+ // If error or wrong size.
+ ASSERT (FALSE);
+ return EFI_ABORTED;
+ }
+
+ Range = Data + (sizeof (UINT32) * AddressCells);
+
+ // Base address
+ if (AddressCells == 2) {
+ ConfigSpaceBaseAddress = FdtReadUnaligned64 ((UINT64 *)Data);
+ ConfigSpaceBaseAddressRange = FdtReadUnaligned64 ((UINT64 *)Range);
+ } else {
+ ConfigSpaceBaseAddress = FdtReadUnaligned32 ((UINT32 *)Data);
+ ConfigSpaceBaseAddressRange = FdtReadUnaligned32 ((UINT32 *)Range);
+ }
+
+ if (FdtParserHandle->HwAddressInfo != NULL) {
+ Status = FdtParserHandle->HwAddressInfo (
+ FdtParserHandle->Context,
+ "PCI Config",
+ ConfigSpaceBaseAddress,
+ ConfigSpaceBaseAddressRange
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+ }
+
+ // Address map
+ Status = ParseAddressMap (
+ FdtParserHandle,
+ HostPciNode,
+ AddressCells
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/** PCI information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+PciConfigInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ INT32 PciNode;
+ UINT32 PciNodeCount;
+ VOID *Fdt;
+
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Fdt = FdtParserHandle->Fdt;
+
+ // Only search host-pci devices.
+ // PCI Firmware Specification Revision 3.0, s4.1.2. "MCFG Table Description":
+ // "This table directly refers to PCI Segment Groups defined in the system
+ // via the _SEG object in the ACPI name space for the applicable host bridge
+ // device."
+ Status = FdtCountCompatNodeInBranch (
+ Fdt,
+ FdtBranch,
+ &PciCompatibleInfo,
+ &PciNodeCount
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return Status;
+ }
+
+ if (PciNodeCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ // Parse each host-pci node in the branch.
+ PciNode = FdtBranch;
+ for (Index = 0; Index < PciNodeCount; Index++) {
+ Status = FdtGetNextCompatNodeInBranch (
+ Fdt,
+ FdtBranch,
+ &PciCompatibleInfo,
+ &PciNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ if (Status == EFI_NOT_FOUND) {
+ // Should have found the node.
+ Status = EFI_ABORTED;
+ }
+
+ return Status;
+ }
+
+ Status = PciNodeParser (FdtParserHandle, PciNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ break;
+ }
+ } // for
+
+ return Status;
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Pci/PciConfigSpaceParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Pci/PciConfigSpaceParser.h
new file mode 100755
index 000000000000..656afc2c207c
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Pci/PciConfigSpaceParser.h
@@ -0,0 +1,78 @@
+/** @file
+ PCI Configuration Space Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/pci/host-generic-pci.yaml
+ - PCI Firmware Specification - Revision 3.0
+ - Open Firmware Recommended Practice: Interrupt Mapping, Version 0.9
+ - Devicetree Specification Release v0.3
+ - linux kernel code
+**/
+
+#ifndef PCI_CONFIG_SPACE_PARSER_H_
+#define PCI_CONFIG_SPACE_PARSER_H_
+
+/** Read LEN bits at OFF offsets bits of the ADDR.
+
+ @param [in] ADDR Address to read the bits from.
+ @param [in] OFF Offset of the bits to read.
+ @param [in] LEN Number of bits to read.
+
+ @return The bits read.
+**/
+#define READ_BITS(ADDR, OFF, LEN) (((ADDR) >> (OFF)) & ((1<<(LEN))-1))
+
+/* Pci address attributes.
+*/
+/// 0 if relocatable.
+#define READ_PCI_N(ADDR) READ_BITS((ADDR), 31, 1)
+/// 1 if prefetchable.
+#define READ_PCI_P(ADDR) READ_BITS((ADDR), 30, 1)
+/// 1 if aliased.
+#define READ_PCI_T(ADDR) READ_BITS((ADDR), 29, 1)
+
+/** Space code.
+
+ 00: Configuration Space
+ 01: I/O Space
+ 10: 32-bit-address Memory Space
+ 11: 64-bit-address Memory Space
+*/
+#define READ_PCI_SS(ADDR) READ_BITS((ADDR), 24, 2)
+/// Bus number.
+#define READ_PCI_BBBBBBBB(ADDR) READ_BITS((ADDR), 16, 8)
+/// Device number.
+#define READ_PCI_DDDDD(ADDR) READ_BITS((ADDR), 11, 5)
+
+/** Number of device-tree cells used for PCI nodes properties.
+
+ Values are well defined, except the "#interrupt-cells" which
+ is assumed to be 1.
+*/
+#define PCI_ADDRESS_CELLS 3U
+#define PCI_SIZE_CELLS 2U
+#define PCI_INTERRUPTS_CELLS 1U
+
+/** PCI information parser function.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+PciConfigInfoParser (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ );
+
+#endif // PCI_CONFIG_SPACE_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Rtc/RtcParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Rtc/RtcParser.c
new file mode 100644
index 000000000000..4df322165fae
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Rtc/RtcParser.c
@@ -0,0 +1,62 @@
+/** @file
+ RTC Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Documentation/devicetree/bindings/rtc/arm,pl031.yaml
+ - Documentation/devicetree/bindings/rtc/rtc-cmos.txt
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "Common/DeviceParser.h"
+
+/** List of "compatible" property values for RTC nodes.
+
+ Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR RtcCompatibleStr[] = {
+ { "motorola,mc146818" },
+ { "arm,pl031" }
+};
+
+/** COMPATIBILITY_INFO structure for the RtcCompatible.
+*/
+CONST COMPATIBILITY_INFO RtcCompatibleInfo = {
+ ARRAY_SIZE (RtcCompatibleStr),
+ RtcCompatibleStr
+};
+
+/** RTC dispatcher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+RtcDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ )
+{
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return DeviceDispatcher (
+ FdtParserHandle,
+ FdtBranch,
+ &RtcCompatibleInfo,
+ "RTC"
+ );
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Rtc/RtcParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Rtc/RtcParser.h
new file mode 100644
index 000000000000..8b65c8663296
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Rtc/RtcParser.h
@@ -0,0 +1,34 @@
+/** @file
+ RTC Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - Documentation/devicetree/bindings/rtc/arm,pl031.yaml
+ - Documentation/devicetree/bindings/rtc/rtc-cmos.txt
+**/
+
+#ifndef RTC_PARSER_H_
+#define RTC_PARSER_H_
+
+/** Rtc dispatcher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+RtcDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ );
+
+#endif // RTC_PARSER_H_
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Serial/SerialPortParser.c b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Serial/SerialPortParser.c
new file mode 100755
index 000000000000..550c6d6aa2f0
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Serial/SerialPortParser.c
@@ -0,0 +1,65 @@
+/** @file
+ Serial Port Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/serial/serial.yaml
+ - linux/Documentation/devicetree/bindings/serial/8250.txt
+ - linux/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
+ - linux/Documentation/devicetree/bindings/serial/pl011.yaml
+**/
+
+#include "FdtUtility.h"
+#include "FdtInfoParser.h"
+#include "Common/DeviceParser.h"
+
+/** List of "compatible" property values for serial port nodes.
+
+ Any other "compatible" value is not supported by this module.
+*/
+STATIC CONST COMPATIBILITY_STR SerialCompatibleStr[] = {
+ { "ns16550a" },
+ { "arm,sbsa-uart" },
+ { "arm,pl011" }
+};
+
+/** COMPATIBILITY_INFO structure for the SerialCompatible.
+*/
+CONST COMPATIBILITY_INFO SerialCompatibleInfo = {
+ ARRAY_SIZE (SerialCompatibleStr),
+ SerialCompatibleStr
+};
+
+/** SerialPort dispatcher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+SerialPortDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ )
+{
+ if (FdtParserHandle == NULL) {
+ ASSERT (FALSE);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return DeviceDispatcher (
+ FdtParserHandle,
+ FdtBranch,
+ &SerialCompatibleInfo,
+ "Serial Port"
+ );
+}
diff --git a/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Serial/SerialPortParser.h b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Serial/SerialPortParser.h
new file mode 100755
index 000000000000..bed8d499e619
--- /dev/null
+++ b/ArmVirtPkg/Library/ArmPlatformDeviceInfoLib/HwInfoParser/Serial/SerialPortParser.h
@@ -0,0 +1,34 @@
+/** @file
+ Serial Port Parser.
+
+ Copyright (c) 2021 - 2024, Arm Limited. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Reference(s):
+ - linux/Documentation/devicetree/bindings/serial/serial.yaml
+ - linux/Documentation/devicetree/bindings/serial/8250.txt
+**/
+
+#ifndef SERIAL_PORT_PARSER_H_
+#define SERIAL_PORT_PARSER_H_
+
+/** SerialPort dispatcher.
+
+ @param [in] FdtParserHandle A handle to the parser instance.
+ @param [in] FdtBranch When searching for DT node name, restrict
+ the search to this Device Tree branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_ABORTED An error occurred.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_NOT_FOUND Not found.
+ @retval EFI_UNSUPPORTED Unsupported.
+**/
+EFI_STATUS
+EFIAPI
+SerialPortDispatcher (
+ IN CONST FDT_HW_INFO_PARSER_HANDLE FdtParserHandle,
+ IN INT32 FdtBranch
+ );
+
+#endif // SERIAL_PORT_PARSER_H_
diff --git a/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoLib.c b/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoLib.c
index 98cc13870599..0f444b693fa3 100644
--- a/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoLib.c
@@ -1,6 +1,6 @@
/** @file
- Copyright (c) 2022, Arm Limited. All rights reserved.
+ Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -241,3 +241,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.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureMmio (
+ IN UINT64 IpaWidth
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
diff --git a/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.c b/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.c
index 79412897f225..e3e849d0aaa3 100644
--- a/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.c
@@ -1,21 +1,30 @@
/** @file
Kvmtool virtual memory map library.
- Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.
+ Copyright (c) 2018 - 2024, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
+#include
+#include
#include
+#include
+#include
#include
#include
#include
#include
// Number of Virtual Memory Map Descriptors
-#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS 5
+#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS (MAX_PLAT_DEVICE_COUNT + 4)
+
+// A platform device information structure used to
+// collate the MMIO base address and range for the
+// Platform devices.
+STATIC PLATFROM_DEVICE_INFO mPlatInfo;
/**
Return the Virtual Memory Map of your platform
@@ -38,11 +47,28 @@ ArmVirtGetMemoryMap (
ARM_MEMORY_REGION_DESCRIPTOR *VirtualMemoryTable;
UINTN Idx;
EFI_PHYSICAL_ADDRESS TopOfAddressSpace;
+ VOID *DtbBase;
+ UINTN Devices;
+ EFI_STATUS Status;
ASSERT (VirtualMemoryMap != NULL);
TopOfAddressSpace = LShiftU64 (1ULL, ArmGetPhysicalAddressBits ());
+ // Parse the FDT and populate the PLATFROM_DEVICE_INFO structure with
+ // the MMIO base address and range for the devices present in the FDT.
+ DtbBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
+
+ ZeroMem (&mPlatInfo, sizeof (PLATFROM_DEVICE_INFO));
+ Status = ArmParsePlatformDeviceFdt (DtbBase, &mPlatInfo);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ if (mPlatInfo.MaxDevices > MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS) {
+ return;
+ }
+
VirtualMemoryTable = (ARM_MEMORY_REGION_DESCRIPTOR *)
AllocatePages (
EFI_SIZE_TO_PAGES (
@@ -66,26 +92,27 @@ ArmVirtGetMemoryMap (
VirtualMemoryTable[Idx].Length = PcdGet64 (PcdSystemMemorySize);
VirtualMemoryTable[Idx].Attributes = ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK;
- // Peripheral space before DRAM
- VirtualMemoryTable[++Idx].PhysicalBase = 0x0;
- VirtualMemoryTable[Idx].VirtualBase = 0x0;
- VirtualMemoryTable[Idx].Length = PcdGet64 (PcdSystemMemoryBase);
- VirtualMemoryTable[Idx].Attributes = ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;
-
- // Peripheral space after DRAM
- VirtualMemoryTable[++Idx].PhysicalBase = PcdGet64 (PcdSystemMemoryBase) +
- PcdGet64 (PcdSystemMemorySize);
- VirtualMemoryTable[Idx].VirtualBase = VirtualMemoryTable[Idx].PhysicalBase;
- VirtualMemoryTable[Idx].Length = TopOfAddressSpace -
- VirtualMemoryTable[Idx].PhysicalBase;
- VirtualMemoryTable[Idx].Attributes = ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;
-
// Map the FV region as normal executable memory
VirtualMemoryTable[++Idx].PhysicalBase = PcdGet64 (PcdFvBaseAddress);
VirtualMemoryTable[Idx].VirtualBase = VirtualMemoryTable[Idx].PhysicalBase;
VirtualMemoryTable[Idx].Length = FixedPcdGet32 (PcdFvSize);
VirtualMemoryTable[Idx].Attributes = ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK;
+ // Map peripheral devices
+ for (Devices = 0; Devices < mPlatInfo.MaxDevices; Devices++) {
+ VirtualMemoryTable[++Idx].PhysicalBase = mPlatInfo.Dev[Devices].BaseAddress;
+ VirtualMemoryTable[Idx].VirtualBase = mPlatInfo.Dev[Devices].BaseAddress;
+ // Some devices may only use few registers and would report the length
+ // accordingly. Although this is correct, the device is expected to reserve
+ // at least a page for MMIO page mapping. Therefore, align the address
+ // range to the nearest page size.
+ VirtualMemoryTable[Idx].Length = ALIGN_VALUE (
+ mPlatInfo.Dev[Devices].Length,
+ EFI_PAGE_SIZE
+ );
+ VirtualMemoryTable[Idx].Attributes = ARM_MEMORY_REGION_ATTRIBUTE_DEVICE;
+ }
+
// End of Table
VirtualMemoryTable[++Idx].PhysicalBase = 0;
VirtualMemoryTable[Idx].VirtualBase = 0;
@@ -96,3 +123,63 @@ ArmVirtGetMemoryMap (
*VirtualMemoryMap = VirtualMemoryTable;
}
+
+/** Configure the MMIO regions as shared with the VMM.
+
+ Set the protection attribute for the MMIO regions that do not belong to
+ the Realm Device 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.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureMmio (
+ IN UINT64 IpaWidth
+ )
+{
+ RETURN_STATUS Status;
+ UINTN Devices;
+ UINT64 BaseAddress;
+ UINT64 Length;
+ BOOLEAN IsProtectedMmio;
+
+ if (!IsRealm ()) {
+ return RETURN_UNSUPPORTED;
+ }
+
+ for (Devices = 0; Devices < mPlatInfo.MaxDevices; Devices++) {
+ BaseAddress = mPlatInfo.Dev[Devices].BaseAddress;
+ Length = ALIGN_VALUE (mPlatInfo.Dev[Devices].Length, EFI_PAGE_SIZE);
+
+ Status = ArmCcaMemRangeIsProtectedMmio (
+ BaseAddress,
+ Length,
+ &IsProtectedMmio
+ );
+ if (RETURN_ERROR (Status)) {
+ break;
+ }
+
+ if (IsProtectedMmio) {
+ // The Realm Device memory is not shared with the host. So skip.
+ continue;
+ }
+
+ // Set the protection attribute for the Peripheral memory as host shared.
+ Status = ArmCcaSetMemoryProtectAttribute (
+ BaseAddress,
+ Length,
+ IpaWidth,
+ TRUE
+ );
+ if (RETURN_ERROR (Status)) {
+ break;
+ }
+ } // for
+
+ return Status;
+}
diff --git a/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.inf b/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.inf
index a354e734ab1b..0e256a01c64e 100644
--- a/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.inf
+++ b/ArmVirtPkg/Library/KvmtoolVirtMemInfoLib/KvmtoolVirtMemInfoLib.inf
@@ -1,7 +1,7 @@
## @file
# Kvmtool virtual memory map library.
#
-# Copyright (c) 2018, ARM Limited. All rights reserved.
+# Copyright (c) 2018 - 2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -24,9 +24,12 @@
EmbeddedPkg/EmbeddedPkg.dec
MdeModulePkg/MdeModulePkg.dec
MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
[LibraryClasses]
+ ArmCcaLib
ArmLib
+ ArmPlatformDeviceInfoLib
BaseLib
BaseMemoryLib
DebugLib
@@ -37,6 +40,8 @@
gArmTokenSpaceGuid.PcdFvBaseAddress
gArmTokenSpaceGuid.PcdSystemMemoryBase
gArmTokenSpaceGuid.PcdSystemMemorySize
+ gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
[FixedPcd]
gArmTokenSpaceGuid.PcdFvSize
+ gArmVirtTokenSpaceGuid.PcdMaxPlatDeviceCount
diff --git a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
index d435d725983d..13f034232fb2 100644
--- a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
@@ -1,6 +1,7 @@
/** @file
Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
+ Copyright (c) 2023, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -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.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureMmio (
+ IN UINT64 IpaWidth
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
diff --git a/ArmVirtPkg/Library/XenVirtMemInfoLib/XenVirtMemInfoLib.c b/ArmVirtPkg/Library/XenVirtMemInfoLib/XenVirtMemInfoLib.c
index ac0c75aecfe5..954382c05504 100644
--- a/ArmVirtPkg/Library/XenVirtMemInfoLib/XenVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/XenVirtMemInfoLib/XenVirtMemInfoLib.c
@@ -1,6 +1,7 @@
/** @file
Copyright (c) 2014-2017, Linaro Limited. All rights reserved.
+ Copyright (c) 2023, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -55,3 +56,23 @@ ArmVirtGetMemoryMap (
*VirtualMemoryMap = mVirtualMemoryTable;
}
+
+/**
+ 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.
+**/
+RETURN_STATUS
+EFIAPI
+ArmCcaConfigureMmio (
+ IN UINT64 IpaWidth
+ )
+{
+ return RETURN_UNSUPPORTED;
+}
diff --git a/ArmVirtPkg/PrePi/AArch64/ModuleEntryPoint.S b/ArmVirtPkg/PrePi/AArch64/ModuleEntryPoint.S
index 06a0020f250c..32a622d2f721 100644
--- a/ArmVirtPkg/PrePi/AArch64/ModuleEntryPoint.S
+++ b/ArmVirtPkg/PrePi/AArch64/ModuleEntryPoint.S
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2011-2013, ARM Limited. All rights reserved.
+// Copyright (c) 2011-2023, Arm Limited. All rights reserved.
// Copyright (c) 2015-2016, Linaro Limited. All rights reserved.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -28,6 +28,10 @@ ASM_FUNC(_ModuleEntryPoint)
bl ASM_PFX(DiscoverDramFromDt)
+ // Check if we are in a Realm and configure
+ // the System Memory as Protected RAM.
+ bl ASM_PFX(ArmCcaConfigureSystemMemory)
+
// Get ID of this CPU in Multicore system
bl ASM_PFX(ArmReadMpidr)
// Keep a copy of the MpId register value
diff --git a/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf b/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
index ed8b89e2b68c..d79cc2be5797 100755
--- a/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
+++ b/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
@@ -1,6 +1,6 @@
#/** @file
#
-# Copyright (c) 2011-2015, ARM Ltd. All rights reserved.
+# Copyright (c) 2011-2023, Arm Limited. All rights reserved.
# Copyright (c) 2015, Linaro Ltd. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -38,6 +38,7 @@
OvmfPkg/OvmfPkg.dec
[LibraryClasses]
+ ArmCcaInitPeiLib
BaseLib
DebugLib
FdtLib
diff --git a/ArmVirtPkg/PrePi/PrePi.c b/ArmVirtPkg/PrePi/PrePi.c
index 9dbb5af942e7..002f42a2b962 100755
--- a/ArmVirtPkg/PrePi/PrePi.c
+++ b/ArmVirtPkg/PrePi/PrePi.c
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
#include
#include
@@ -34,6 +35,7 @@ PrePiMain (
CHAR8 Buffer[100];
UINTN CharCount;
UINTN StacksSize;
+ RETURN_STATUS RetStatus;
// Initialize the architecture specific bits
ArchInitialize ();
@@ -61,6 +63,12 @@ PrePiMain (
Status = MemoryPeim (UefiMemoryBase, FixedPcdGet32 (PcdSystemMemoryUefiRegionSize));
ASSERT_EFI_ERROR (Status);
+ // Perform the Arm CCA specific initialisations.
+ RetStatus = ArmCcaInitialize ();
+ if (RETURN_ERROR (RetStatus)) {
+ CpuDeadLoop ();
+ }
+
// Initialize the Serial Port
SerialPortInitialize ();
CharCount = AsciiSPrint (
diff --git a/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.c b/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.c
new file mode 100644
index 000000000000..9212f0f6d252
--- /dev/null
+++ b/ArmVirtPkg/RealmApertureManagementProtocolDxe/RealmApertureManagementProtocolDxe.c
@@ -0,0 +1,660 @@
+/** @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
+#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;
+
+ /// The RIPAS for the aperture.
+ RIPAS Ripas;
+} 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] MemStart 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;
+ ApertInfo->Ripas = RipasEmpty;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: ApertRef = 0x%p, Memory = 0x%lx, Pages = 0x%x, "
+ "MemoryAttributes = 0x%x, Ripas = 0x%x\n",
+ __func__,
+ ApertInfo,
+ ApertInfo->BaseAddress,
+ ApertInfo->Pages,
+ ApertInfo->MemoryAttributes,
+ ApertInfo->Ripas
+ ));
+
+ // Set the Realm IPA state to Empty to open the Aperture
+ Status = RsiSetIpaState (
+ (UINT64 *)Memory,
+ (Pages * EFI_PAGE_SIZE),
+ RipasEmpty,
+ RIPAS_CHANGE_FLAGS_RSI_NO_CHANGE_DESTROYED
+ );
+ if (RETURN_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: RSI Set IPA State failed, Address = %p, Pages = 0x%lx, "
+ "Status = %r\n",
+ Memory,
+ Pages,
+ Status
+ ));
+ goto error_handler;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "SUCCESS: RSI Set IPA State complete, Address = %p, Pages = 0x%lx, "
+ "Status = %r\n",
+ Memory,
+ Pages,
+ Status
+ ));
+
+ InsertHeadList (&mApertureInfos, &ApertInfo->Link);
+ *ApertureReference = (EFI_HANDLE *)&ApertInfo->Link;
+
+ return Status;
+
+error_handler:
+
+ FreePool (ApertInfo);
+
+error_handler1:
+ Status1 = ArmCcaSetMemoryProtectAttribute (
+ Memory,
+ EFI_PAGES_TO_SIZE (Pages),
+ mIpaWidth,
+ FALSE
+ );
+ 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 the first error code
+ 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;
+
+ if (ApertureReference == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ApertInfo = NULL;
+ 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, Ripas = 0x%x\n",
+ __func__,
+ ApertInfo,
+ ApertInfo->BaseAddress,
+ ApertInfo->Pages,
+ ApertInfo->MemoryAttributes,
+ ApertInfo->Ripas
+ ));
+
+ // Set the Realm IPA state to RAM to close the Aperture
+ Status = RsiSetIpaState (
+ (UINT64 *)ApertInfo->BaseAddress,
+ (ApertInfo->Pages * EFI_PAGE_SIZE),
+ RipasRam,
+ RIPAS_CHANGE_FLAGS_RSI_NO_CHANGE_DESTROYED
+ );
+ if (RETURN_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: RSI Set IPA State failed, Address = %p, Pages = 0x%lx, "
+ "Status = %r\n",
+ ApertInfo->BaseAddress,
+ ApertInfo->Pages,
+ Status
+ ));
+ return Status;
+ }
+
+ 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;
+ 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;
+ }
+
+ // Retrieve the IPA Width of the Realm for subsequent use to configure
+ // the protection attribute of memory regions.
+ Status = GetIpaWidth (&mIpaWidth);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_ERROR,
+ "ERROR: Failed to get Ipa Width, Status = %r\n",
+ Status
+ ));
+ ASSERT (0);
+ return Status;
+ }
+
+ /*
+ 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;
+ }
+
+ Handle = NULL;
+ 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 000000000000..2e3021b82bd7
--- /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/MdePkg/Include/AArch64/AArch64.h b/MdePkg/Include/AArch64/AArch64.h
index 9650608e7bab..86ba0cb10038 100644
--- a/MdePkg/Include/AArch64/AArch64.h
+++ b/MdePkg/Include/AArch64/AArch64.h
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
- Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+ Copyright (c) 2011 - 2023, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -32,6 +32,7 @@
// ID_AA64PFR0 - AArch64 Processor Feature Register 0 definitions
#define AARCH64_PFR0_FP (0xF << 16)
#define AARCH64_PFR0_GIC (0xF << 24)
+#define AARCH64_PFR0_RME (0xFULL << 52)
// ID_AA64DFR0 - AArch64 Debug Feature Register 0 definitions
#define AARCH64_DFR0_TRACEVER (0xFULL << 4)
diff --git a/MdePkg/Include/Library/ArmLib.h b/MdePkg/Include/Library/ArmLib.h
index 087cddfb76c3..c7541d963771 100644
--- a/MdePkg/Include/Library/ArmLib.h
+++ b/MdePkg/Include/Library/ArmLib.h
@@ -1,7 +1,7 @@
/** @file
Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
- Copyright (c) 2011 - 2016, ARM Ltd. All rights reserved.
+ Copyright (c) 2011 - 2023, Arm Limited. All rights reserved.
Copyright (c) 2020 - 2021, NUVIA Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -770,6 +770,19 @@ ArmHasSecurityExtensions (
VOID
);
+#else
+
+/** Checks if RME is implemented.
+
+ @retval TRUE RME is implemented.
+ @retval FALSE RME is not implemented.
+**/
+BOOLEAN
+EFIAPI
+ArmHasRme (
+ VOID
+ );
+
#endif // MDE_CPU_ARM
#endif // ARM_LIB_H_
diff --git a/MdePkg/Library/BaseRngLib/AArch64/Rndr.c b/MdePkg/Library/BaseRngLib/AArch64/Rndr.c
index 2c5344384059..1d097fd5c549 100644
--- a/MdePkg/Library/BaseRngLib/AArch64/Rndr.c
+++ b/MdePkg/Library/BaseRngLib/AArch64/Rndr.c
@@ -45,7 +45,14 @@ BaseRngLibConstructor (
// MSR. A non-zero value indicates that the processor supports the RNDR instruction.
//
Isar0 = ArmReadIdAA64Isar0Reg ();
- mRndrSupported = !!((Isar0 >> ARM_ID_AA64ISAR0_EL1_RNDR_SHIFT) & ARM_ID_AA64ISAR0_EL1_RNDR_MASK);
+ mRndrSupported = (((Isar0 >> ARM_ID_AA64ISAR0_EL1_RNDR_SHIFT) &
+ ARM_ID_AA64ISAR0_EL1_RNDR_MASK) != 0);
+ if (!mRndrSupported) {
+ DEBUG ((
+ DEBUG_WARN,
+ "WARNING: BaseRngLib: RNDR instruction not supported by the processor.\n"
+ ));
+ }
return EFI_SUCCESS;
}