From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- roms/edk2/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c | 402 +++++++++++++++++++ roms/edk2/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c | 511 +++++++++++++++++++++++++ roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.c | 283 ++++++++++++++ roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.h | 146 +++++++ roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.inf | 71 ++++ roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c | 211 ++++++++++ roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMpCore.c | 97 +++++ roms/edk2/ArmPkg/Drivers/CpuDxe/Exception.c | 98 +++++ 8 files changed, 1819 insertions(+) create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.c create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.h create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.inf create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMpCore.c create mode 100644 roms/edk2/ArmPkg/Drivers/CpuDxe/Exception.c (limited to 'roms/edk2/ArmPkg/Drivers/CpuDxe') diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/roms/edk2/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c new file mode 100644 index 000000000..fca2d4f76 --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c @@ -0,0 +1,402 @@ +/*++ + +Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
+Portions copyright (c) 2010, Apple Inc. All rights reserved.
+Portions copyright (c) 2011-2013, ARM Ltd. All rights reserved.
+Copyright (c) 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + + +--*/ + +#include +#include "CpuDxe.h" + +#define INVALID_ENTRY ((UINT32)~0) + +#define MIN_T0SZ 16 +#define BITS_PER_LEVEL 9 + +STATIC +VOID +GetRootTranslationTableInfo ( + IN UINTN T0SZ, + OUT UINTN *RootTableLevel, + OUT UINTN *RootTableEntryCount + ) +{ + *RootTableLevel = (T0SZ - MIN_T0SZ) / BITS_PER_LEVEL; + *RootTableEntryCount = TT_ENTRY_COUNT >> (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL; +} + +STATIC +UINT64 +PageAttributeToGcdAttribute ( + IN UINT64 PageAttributes + ) +{ + UINT64 GcdAttributes; + + switch (PageAttributes & TT_ATTR_INDX_MASK) { + case TT_ATTR_INDX_DEVICE_MEMORY: + GcdAttributes = EFI_MEMORY_UC; + break; + case TT_ATTR_INDX_MEMORY_NON_CACHEABLE: + GcdAttributes = EFI_MEMORY_WC; + break; + case TT_ATTR_INDX_MEMORY_WRITE_THROUGH: + GcdAttributes = EFI_MEMORY_WT; + break; + case TT_ATTR_INDX_MEMORY_WRITE_BACK: + GcdAttributes = EFI_MEMORY_WB; + break; + default: + DEBUG ((DEBUG_ERROR, + "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n", + PageAttributes)); + ASSERT (0); + // The Global Coherency Domain (GCD) value is defined as a bit set. + // Returning 0 means no attribute has been set. + GcdAttributes = 0; + } + + // Determine protection attributes + if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) || + ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO)) { + // Read only cases map to write-protect + GcdAttributes |= EFI_MEMORY_RO; + } + + // Process eXecute Never attribute + if ((PageAttributes & (TT_PXN_MASK | TT_UXN_MASK)) != 0) { + GcdAttributes |= EFI_MEMORY_XP; + } + + return GcdAttributes; +} + +STATIC +UINT64 +GetFirstPageAttribute ( + IN UINT64 *FirstLevelTableAddress, + IN UINTN TableLevel + ) +{ + UINT64 FirstEntry; + + // Get the first entry of the table + FirstEntry = *FirstLevelTableAddress; + + if ((TableLevel != 3) && (FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY) { + // Only valid for Levels 0, 1 and 2 + + // Get the attribute of the subsequent table + return GetFirstPageAttribute ((UINT64*)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1); + } else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) || + ((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3))) + { + return FirstEntry & TT_ATTR_INDX_MASK; + } else { + return INVALID_ENTRY; + } +} + +STATIC +UINT64 +GetNextEntryAttribute ( + IN UINT64 *TableAddress, + IN UINTN EntryCount, + IN UINTN TableLevel, + IN UINT64 BaseAddress, + IN OUT UINT32 *PrevEntryAttribute, + IN OUT UINT64 *StartGcdRegion + ) +{ + UINTN Index; + UINT64 Entry; + UINT32 EntryAttribute; + UINT32 EntryType; + EFI_STATUS Status; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + + // Get the memory space map from GCD + MemorySpaceMap = NULL; + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + // We cannot get more than 3-level page table + ASSERT (TableLevel <= 3); + + // While the top level table might not contain TT_ENTRY_COUNT entries; + // the subsequent ones should be filled up + for (Index = 0; Index < EntryCount; Index++) { + Entry = TableAddress[Index]; + EntryType = Entry & TT_TYPE_MASK; + EntryAttribute = Entry & TT_ATTR_INDX_MASK; + + // If Entry is a Table Descriptor type entry then go through the sub-level table + if ((EntryType == TT_TYPE_BLOCK_ENTRY) || + ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3))) { + if ((*PrevEntryAttribute == INVALID_ENTRY) || (EntryAttribute != *PrevEntryAttribute)) { + if (*PrevEntryAttribute != INVALID_ENTRY) { + // Update GCD with the last region + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, + *StartGcdRegion, + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion, + PageAttributeToGcdAttribute (*PrevEntryAttribute)); + } + + // Start of the new region + *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)); + *PrevEntryAttribute = EntryAttribute; + } else { + continue; + } + } else if (EntryType == TT_TYPE_TABLE_ENTRY) { + // Table Entry type is only valid for Level 0, 1, 2 + ASSERT (TableLevel < 3); + + // Increase the level number and scan the sub-level table + GetNextEntryAttribute ((UINT64*)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), + TT_ENTRY_COUNT, TableLevel + 1, + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))), + PrevEntryAttribute, StartGcdRegion); + } else { + if (*PrevEntryAttribute != INVALID_ENTRY) { + // Update GCD with the last region + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, + *StartGcdRegion, + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel))) - *StartGcdRegion, + PageAttributeToGcdAttribute (*PrevEntryAttribute)); + + // Start of the new region + *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL(TableLevel)); + *PrevEntryAttribute = INVALID_ENTRY; + } + } + } + + FreePool (MemorySpaceMap); + + return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL(TableLevel)); +} + +EFI_STATUS +SyncCacheConfig ( + IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol + ) +{ + EFI_STATUS Status; + UINT32 PageAttribute = 0; + UINT64 *FirstLevelTableAddress; + UINTN TableLevel; + UINTN TableCount; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + UINTN Tcr; + UINTN T0SZ; + UINT64 BaseAddressGcdRegion; + UINT64 EndAddressGcdRegion; + + // This code assumes MMU is enabled and filed with section translations + ASSERT (ArmMmuEnabled ()); + + // + // Get the memory space map from GCD + // + MemorySpaceMap = NULL; + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs + // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a + // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were + // a client) to update its copy of the attributes. This is bad architecture and should be replaced + // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead. + + // Obtain page table base + FirstLevelTableAddress = (UINT64*)(ArmGetTTBR0BaseAddress ()); + + // Get Translation Control Register value + Tcr = ArmGetTCR (); + // Get Address Region Size + T0SZ = Tcr & TCR_T0SZ_MASK; + + // Get the level of the first table for the indicated Address Region Size + GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount); + + // First Attribute of the Page Tables + PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel); + + // We scan from the start of the memory map (ie: at the address 0x0) + BaseAddressGcdRegion = 0x0; + EndAddressGcdRegion = GetNextEntryAttribute (FirstLevelTableAddress, + TableCount, TableLevel, + BaseAddressGcdRegion, + &PageAttribute, &BaseAddressGcdRegion); + + // Update GCD with the last region if valid + if (PageAttribute != INVALID_ENTRY) { + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, + BaseAddressGcdRegion, + EndAddressGcdRegion - BaseAddressGcdRegion, + PageAttributeToGcdAttribute (PageAttribute)); + } + + FreePool (MemorySpaceMap); + + return EFI_SUCCESS; +} + +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ) +{ + UINT64 ArmAttributes; + + switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) { + case EFI_MEMORY_UC: + if (ArmReadCurrentEL () == AARCH64_EL2) { + ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK; + } else { + ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK; + } + break; + case EFI_MEMORY_WC: + ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE; + break; + case EFI_MEMORY_WT: + ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE; + break; + case EFI_MEMORY_WB: + ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE; + break; + default: + ArmAttributes = TT_ATTR_INDX_MASK; + } + + // Set the access flag to match the block attributes + ArmAttributes |= TT_AF; + + // Determine protection attributes + if (EfiAttributes & EFI_MEMORY_RO) { + ArmAttributes |= TT_AP_RO_RO; + } + + // Process eXecute Never attribute + if (EfiAttributes & EFI_MEMORY_XP) { + ArmAttributes |= TT_PXN_MASK; + } + + return ArmAttributes; +} + +// This function will recursively go down the page table to find the first block address linked to 'BaseAddress'. +// And then the function will identify the size of the region that has the same page table attribute. +EFI_STATUS +GetMemoryRegionRec ( + IN UINT64 *TranslationTable, + IN UINTN TableLevel, + IN UINT64 *LastBlockEntry, + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT64 *NextTranslationTable; + UINT64 *BlockEntry; + UINT64 BlockEntryType; + UINT64 EntryType; + + if (TableLevel != 3) { + BlockEntryType = TT_TYPE_BLOCK_ENTRY; + } else { + BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3; + } + + // Find the block entry linked to the Base Address + BlockEntry = (UINT64*)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress); + EntryType = *BlockEntry & TT_TYPE_MASK; + + if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) { + NextTranslationTable = (UINT64*)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE); + + // The entry is a page table, so we go to the next level + Status = GetMemoryRegionRec ( + NextTranslationTable, // Address of the next level page table + TableLevel + 1, // Next Page Table level + (UINTN*)TT_LAST_BLOCK_ADDRESS(NextTranslationTable, TT_ENTRY_COUNT), + BaseAddress, RegionLength, RegionAttributes); + + // In case of 'Success', it means the end of the block region has been found into the upper + // level translation table + if (!EFI_ERROR(Status)) { + return EFI_SUCCESS; + } + + // Now we processed the table move to the next entry + BlockEntry++; + } else if (EntryType == BlockEntryType) { + // We have found the BlockEntry attached to the address. We save its start address (the start + // address might be before the 'BaseAddress') and attributes + *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL(TableLevel) - 1); + *RegionLength = 0; + *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK; + } else { + // We have an 'Invalid' entry + return EFI_UNSUPPORTED; + } + + while (BlockEntry <= LastBlockEntry) { + if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) { + *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL(TableLevel); + } else { + // In case we have found the end of the region we return success + return EFI_SUCCESS; + } + BlockEntry++; + } + + // If we have reached the end of the TranslationTable and we have not found the end of the region then + // we return EFI_NOT_FOUND. + // The caller will continue to look for the memory region at its level + return EFI_NOT_FOUND; +} + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT64 *TranslationTable; + UINTN TableLevel; + UINTN EntryCount; + UINTN T0SZ; + + ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL)); + + TranslationTable = ArmGetTTBR0BaseAddress (); + + T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; + // Get the Table info from T0SZ + GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount); + + Status = GetMemoryRegionRec (TranslationTable, TableLevel, + (UINTN*)TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount), + BaseAddress, RegionLength, RegionAttributes); + + // If the region continues up to the end of the root table then GetMemoryRegionRec() + // will return EFI_NOT_FOUND + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else { + return Status; + } +} diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/roms/edk2/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c new file mode 100644 index 000000000..6fb5112a1 --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c @@ -0,0 +1,511 @@ +/*++ + +Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
+Portions copyright (c) 2010, Apple Inc. All rights reserved.
+Portions copyright (c) 2013, ARM Ltd. All rights reserved.
+Copyright (c) 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + + +--*/ + +#include +#include "CpuDxe.h" + +EFI_STATUS +SectionToGcdAttributes ( + IN UINT32 SectionAttributes, + OUT UINT64 *GcdAttributes + ) +{ + *GcdAttributes = 0; + + // determine cacheability attributes + switch(SectionAttributes & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) { + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WT; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE: + *GcdAttributes |= EFI_MEMORY_WC; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + default: + return EFI_UNSUPPORTED; + } + + // determine protection attributes + switch(SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) { + case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write + //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP; + break; + + case TT_DESCRIPTOR_SECTION_AP_RW_NO: + case TT_DESCRIPTOR_SECTION_AP_RW_RW: + // normal read/write access, do not add additional attributes + break; + + // read only cases map to write-protect + case TT_DESCRIPTOR_SECTION_AP_RO_NO: + case TT_DESCRIPTOR_SECTION_AP_RO_RO: + *GcdAttributes |= EFI_MEMORY_RO; + break; + + default: + return EFI_UNSUPPORTED; + } + + // now process eXectue Never attribute + if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) { + *GcdAttributes |= EFI_MEMORY_XP; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +PageToGcdAttributes ( + IN UINT32 PageAttributes, + OUT UINT64 *GcdAttributes + ) +{ + *GcdAttributes = 0; + + // determine cacheability attributes + switch(PageAttributes & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) { + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WT; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE: + *GcdAttributes |= EFI_MEMORY_WC; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + default: + return EFI_UNSUPPORTED; + } + + // determine protection attributes + switch(PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) { + case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write + //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP; + break; + + case TT_DESCRIPTOR_PAGE_AP_RW_NO: + case TT_DESCRIPTOR_PAGE_AP_RW_RW: + // normal read/write access, do not add additional attributes + break; + + // read only cases map to write-protect + case TT_DESCRIPTOR_PAGE_AP_RO_NO: + case TT_DESCRIPTOR_PAGE_AP_RO_RO: + *GcdAttributes |= EFI_MEMORY_RO; + break; + + default: + return EFI_UNSUPPORTED; + } + + // now process eXectue Never attribute + if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) { + *GcdAttributes |= EFI_MEMORY_XP; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +SyncCacheConfigPage ( + IN UINT32 SectionIndex, + IN UINT32 FirstLevelDescriptor, + IN UINTN NumberOfDescriptors, + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN OUT EFI_PHYSICAL_ADDRESS *NextRegionBase, + IN OUT UINT64 *NextRegionLength, + IN OUT UINT32 *NextSectionAttributes + ) +{ + EFI_STATUS Status; + UINT32 i; + volatile ARM_PAGE_TABLE_ENTRY *SecondLevelTable; + UINT32 NextPageAttributes = 0; + UINT32 PageAttributes = 0; + UINT32 BaseAddress; + UINT64 GcdAttributes; + + // Get the Base Address from FirstLevelDescriptor; + BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + + // Convert SectionAttributes into PageAttributes + NextPageAttributes = + TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(*NextSectionAttributes,0) | + TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(*NextSectionAttributes); + + // obtain page table base + SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK); + + for (i=0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) { + if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) { + // extract attributes (cacheability and permissions) + PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK); + + if (NextPageAttributes == 0) { + // start on a new region + *NextRegionLength = 0; + *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT); + NextPageAttributes = PageAttributes; + } else if (PageAttributes != NextPageAttributes) { + // Convert Section Attributes into GCD Attributes + Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes); + + // start on a new region + *NextRegionLength = 0; + *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT); + NextPageAttributes = PageAttributes; + } + } else if (NextPageAttributes != 0) { + // Convert Page Attributes into GCD Attributes + Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes); + + *NextRegionLength = 0; + *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT); + NextPageAttributes = 0; + } + *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE; + } + + // Convert back PageAttributes into SectionAttributes + *NextSectionAttributes = + TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(NextPageAttributes,0) | + TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(NextPageAttributes); + + return EFI_SUCCESS; +} + +EFI_STATUS +SyncCacheConfig ( + IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol + ) +{ + EFI_STATUS Status; + UINT32 i; + EFI_PHYSICAL_ADDRESS NextRegionBase; + UINT64 NextRegionLength; + UINT32 NextSectionAttributes = 0; + UINT32 SectionAttributes = 0; + UINT64 GcdAttributes; + volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + + + DEBUG ((DEBUG_PAGE, "SyncCacheConfig()\n")); + + // This code assumes MMU is enabled and filed with section translations + ASSERT (ArmMmuEnabled ()); + + // + // Get the memory space map from GCD + // + MemorySpaceMap = NULL; + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + + // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs + // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a + // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were + // a client) to update its copy of the attributes. This is bad architecture and should be replaced + // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead. + + // obtain page table base + FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ()); + + // Get the first region + NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK); + + // iterate through each 1MB descriptor + NextRegionBase = NextRegionLength = 0; + for (i=0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) { + if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) { + // extract attributes (cacheability and permissions) + SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK); + + if (NextSectionAttributes == 0) { + // start on a new region + NextRegionLength = 0; + NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + NextSectionAttributes = SectionAttributes; + } else if (SectionAttributes != NextSectionAttributes) { + // Convert Section Attributes into GCD Attributes + Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes); + + // start on a new region + NextRegionLength = 0; + NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + NextSectionAttributes = SectionAttributes; + } + NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE; + } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(FirstLevelTable[i])) { + // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from + // bits that are actually part of the pagetable address. We clear it out to zero so that + // the SyncCacheConfigPage will use the page attributes instead of trying to convert the + // section attributes into page attributes + NextSectionAttributes = 0; + Status = SyncCacheConfigPage ( + i,FirstLevelTable[i], + NumberOfDescriptors, MemorySpaceMap, + &NextRegionBase,&NextRegionLength,&NextSectionAttributes); + ASSERT_EFI_ERROR (Status); + } else { + // We do not support yet 16MB sections + ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION); + + // start on a new region + if (NextSectionAttributes != 0) { + // Convert Section Attributes into GCD Attributes + Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes); + + NextRegionLength = 0; + NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(i << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + NextSectionAttributes = 0; + } + NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE; + } + } // section entry loop + + if (NextSectionAttributes != 0) { + // Convert Section Attributes into GCD Attributes + Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes); + } + + FreePool (MemorySpaceMap); + + return EFI_SUCCESS; +} + +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ) +{ + UINT64 ArmAttributes; + + switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) { + case EFI_MEMORY_UC: + // Map to strongly ordered + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0 + break; + + case EFI_MEMORY_WC: + // Map to normal non-cachable + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0 + break; + + case EFI_MEMORY_WT: + // Write through with no-allocate + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0 + break; + + case EFI_MEMORY_WB: + // Write back (with allocate) + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1 + break; + + case EFI_MEMORY_UCE: + default: + ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT; + break; + } + + // Determine protection attributes + if (EfiAttributes & EFI_MEMORY_RO) { + ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO; + } else { + ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW; + } + + // Determine eXecute Never attribute + if (EfiAttributes & EFI_MEMORY_XP) { + ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK; + } + + return ArmAttributes; +} + +EFI_STATUS +GetMemoryRegionPage ( + IN UINT32 *PageTable, + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + UINT32 PageAttributes; + UINT32 TableIndex; + UINT32 PageDescriptor; + + // Convert the section attributes into page attributes + PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0); + + // Calculate index into first level translation table for start of modification + TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT; + ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT); + + // Go through the page table to find the end of the section + for (; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) { + // Get the section at the given index + PageDescriptor = PageTable[TableIndex]; + + if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) { + // Case: End of the boundary of the region + return EFI_SUCCESS; + } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) { + if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) { + *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE; + } else { + // Case: End of the boundary of the region + return EFI_SUCCESS; + } + } else { + // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region. + ASSERT(0); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT32 TableIndex; + UINT32 PageAttributes; + UINT32 PageTableIndex; + UINT32 SectionDescriptor; + ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable; + UINT32 *PageTable; + + // Initialize the arguments + *RegionLength = 0; + + // Obtain page table base + FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress (); + + // Calculate index into first level translation table for start of modification + TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT; + ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT); + + // Get the section at the given index + SectionDescriptor = FirstLevelTable[TableIndex]; + if (!SectionDescriptor) { + return EFI_NOT_FOUND; + } + + // If 'BaseAddress' belongs to the section then round it to the section boundary + if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) || + ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) + { + *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK; + *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK; + } else { + // Otherwise, we round it to the page boundary + *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK; + + // Get the attribute at the page table level (Level 2) + PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK); + + // Calculate index into first level translation table for start of modification + PageTableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT; + ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT); + + PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK; + *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) | + TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes); + } + + for (;TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) { + // Get the section at the given index + SectionDescriptor = FirstLevelTable[TableIndex]; + + // If the entry is a level-2 page table then we scan it to find the end of the region + if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor)) { + // Extract the page table location from the descriptor + PageTable = (UINT32*)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK); + + // Scan the page table to find the end of the region. + Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes); + + // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop + if (Status == EFI_SUCCESS) { + break; + } + } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) || + ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) { + if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) { + // If the attributes of the section differ from the one targeted then we exit the loop + break; + } else { + *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE; + } + } else { + // If we are on an invalid section then it means it is the end of our section. + break; + } + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.c new file mode 100644 index 000000000..082ef30fb --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.c @@ -0,0 +1,283 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" + +#include + +BOOLEAN mIsFlushingGCD; + +/** + This function flushes the range of addresses from Start to Start+Length + from the processor's data cache. If Start is not aligned to a cache line + boundary, then the bytes before Start to the preceding cache line boundary + are also flushed. If Start+Length is not aligned to a cache line boundary, + then the bytes past Start+Length to the end of the next cache line boundary + are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be + supported. If the data cache is fully coherent with all DMA operations, then + this function can just return EFI_SUCCESS. If the processor does not support + flushing a range of the data cache, then the entire data cache can be flushed. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param Start The beginning physical address to flush from the processor's data + cache. + @param Length The number of bytes to flush from the processor's data cache. This + function may flush more bytes than Length specifies depending upon + the granularity of the flush operation that the processor supports. + @param FlushType Specifies the type of flush operation to perform. + + @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from + the processor's data cache. + @retval EFI_UNSUPPORTED The processor does not support the cache flush type specified + by FlushType. + @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed + from the processor's data cache. + +**/ +EFI_STATUS +EFIAPI +CpuFlushCpuDataCache ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_CPU_FLUSH_TYPE FlushType + ) +{ + + switch (FlushType) { + case EfiCpuFlushTypeWriteBack: + WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length); + break; + case EfiCpuFlushTypeInvalidate: + InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length); + break; + case EfiCpuFlushTypeWriteBackInvalidate: + WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length); + break; + default: + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + This function enables interrupt processing by the processor. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS Interrupts are enabled on the processor. + @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor. + +**/ +EFI_STATUS +EFIAPI +CpuEnableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ) +{ + ArmEnableInterrupts (); + + return EFI_SUCCESS; +} + + +/** + This function disables interrupt processing by the processor. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS Interrupts are disabled on the processor. + @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor. + +**/ +EFI_STATUS +EFIAPI +CpuDisableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ) +{ + ArmDisableInterrupts (); + + return EFI_SUCCESS; +} + + +/** + This function retrieves the processor's current interrupt state a returns it in + State. If interrupts are currently enabled, then TRUE is returned. If interrupts + are currently disabled, then FALSE is returned. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param State A pointer to the processor's current interrupt state. Set to TRUE if + interrupts are enabled and FALSE if interrupts are disabled. + + @retval EFI_SUCCESS The processor's current interrupt state was returned in State. + @retval EFI_INVALID_PARAMETER State is NULL. + +**/ +EFI_STATUS +EFIAPI +CpuGetInterruptState ( + IN EFI_CPU_ARCH_PROTOCOL *This, + OUT BOOLEAN *State + ) +{ + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + *State = ArmGetInterruptState(); + return EFI_SUCCESS; +} + + +/** + This function generates an INIT on the processor. If this function succeeds, then the + processor will be reset, and control will not be returned to the caller. If InitType is + not supported by this processor, or the processor cannot programmatically generate an + INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error + occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param InitType The type of processor INIT to perform. + + @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen. + @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported + by this processor. + @retval EFI_DEVICE_ERROR The processor INIT failed. + +**/ +EFI_STATUS +EFIAPI +CpuInit ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_CPU_INIT_TYPE InitType + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +CpuRegisterInterruptHandler ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return RegisterInterruptHandler (InterruptType, InterruptHandler); +} + +EFI_STATUS +EFIAPI +CpuGetTimerValue ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN UINT32 TimerIndex, + OUT UINT64 *TimerValue, + OUT UINT64 *TimerPeriod OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Callback function for idle events. + + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +IdleLoopEventCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + CpuSleep (); +} + +// +// Globals used to initialize the protocol +// +EFI_HANDLE mCpuHandle = NULL; +EFI_CPU_ARCH_PROTOCOL mCpu = { + CpuFlushCpuDataCache, + CpuEnableInterrupt, + CpuDisableInterrupt, + CpuGetInterruptState, + CpuInit, + CpuRegisterInterruptHandler, + CpuGetTimerValue, + CpuSetMemoryAttributes, + 0, // NumberOfTimers + 2048, // DmaBufferAlignment +}; + +STATIC +VOID +InitializeDma ( + IN OUT EFI_CPU_ARCH_PROTOCOL *CpuArchProtocol + ) +{ + CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule (); +} + +EFI_STATUS +CpuDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT IdleLoopEvent; + + InitializeExceptions (&mCpu); + + InitializeDma (&mCpu); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mCpuHandle, + &gEfiCpuArchProtocolGuid, &mCpu, + NULL + ); + + // + // Make sure GCD and MMU settings match. This API calls gDS->SetMemorySpaceAttributes () + // and that calls EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes, so this code needs to go + // after the protocol is installed + // + mIsFlushingGCD = TRUE; + SyncCacheConfig (&mCpu); + mIsFlushingGCD = FALSE; + + // If the platform is a MPCore system then install the Configuration Table describing the + // secondary core states + if (ArmIsMpCore()) { + PublishArmProcessorTable(); + } + + // + // Setup a callback for idle events + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IdleLoopEventCallback, + NULL, + &gIdleLoopEventGuid, + &IdleLoopEvent + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.h new file mode 100644 index 000000000..3fe5c24d5 --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.h @@ -0,0 +1,146 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __CPU_DXE_ARM_EXCEPTION_H__ +#define __CPU_DXE_ARM_EXCEPTION_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern BOOLEAN mIsFlushingGCD; + +/** + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + + @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +RegisterInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + + +/** + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + + @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +RegisterDebuggerInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + + +EFI_STATUS +EFIAPI +CpuSetMemoryAttributes ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +EFI_STATUS +InitializeExceptions ( + IN EFI_CPU_ARCH_PROTOCOL *Cpu + ); + +EFI_STATUS +SyncCacheConfig ( + IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol + ); + +/** + * Publish ARM Processor Data table in UEFI SYSTEM Table. + * @param HobStart Pointer to the beginning of the HOB List from PEI. + * + * Description : This function iterates through HOB list and finds ARM processor Table Entry HOB. + * If the ARM processor Table Entry HOB is found, the HOB data is copied to run-time memory + * and a pointer is assigned to it in ARM processor table. Then the ARM processor table is + * installed in EFI configuration table. +**/ +VOID +EFIAPI +PublishArmProcessorTable( + VOID + ); + +// The ARM Attributes might be defined on 64-bit (case of the long format description table) +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ); + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ); + +EFI_STATUS +SetGcdMemorySpaceAttributes ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +#endif // __CPU_DXE_ARM_EXCEPTION_H__ diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.inf new file mode 100644 index 000000000..e5549fc71 --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuDxe.inf @@ -0,0 +1,71 @@ +#/** @file +# +# DXE CPU driver +# +# Copyright (c) 2009, Apple Inc. All rights reserved.
+# Copyright (c) 2011-2013, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmCpuDxe + FILE_GUID = B8D9777E-D72A-451F-9BDB-BAFB52A68415 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = CpuDxeInitialize + +[Sources.Common] + CpuDxe.c + CpuDxe.h + CpuMpCore.c + CpuMmuCommon.c + Exception.c + +[Sources.ARM] + Arm/Mmu.c + +[Sources.AARCH64] + AArch64/Mmu.c + +[Packages] + ArmPkg/ArmPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + ArmLib + ArmMmuLib + BaseMemoryLib + CacheMaintenanceLib + CpuLib + CpuExceptionHandlerLib + DebugLib + DefaultExceptionHandlerLib + DxeServicesTableLib + HobLib + PeCoffGetEntryPointLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiCpuArchProtocolGuid + +[Guids] + gEfiDebugImageInfoTableGuid + gArmMpCoreInfoGuid + gIdleLoopEventGuid + gEfiVectorHandoffTableGuid + +[Pcd.common] + gArmTokenSpaceGuid.PcdVFPEnabled + +[FeaturePcd.common] + gArmTokenSpaceGuid.PcdDebuggerExceptionSupport + +[Depex] + gHardwareInterruptProtocolGuid OR gHardwareInterrupt2ProtocolGuid diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c new file mode 100644 index 000000000..cdb1d6786 --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c @@ -0,0 +1,211 @@ +/** @file +* +* Copyright (c) 2013, ARM Limited. All rights reserved. +* Copyright (c) 2017, Intel Corporation. All rights reserved.
+* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include "CpuDxe.h" + +/** + Searches memory descriptors covered by given memory range. + + This function searches into the Gcd Memory Space for descriptors + (from StartIndex to EndIndex) that contains the memory range + specified by BaseAddress and Length. + + @param MemorySpaceMap Gcd Memory Space Map as array. + @param NumberOfDescriptors Number of descriptors in map. + @param BaseAddress BaseAddress for the requested range. + @param Length Length for the requested range. + @param StartIndex Start index into the Gcd Memory Space Map. + @param EndIndex End index into the Gcd Memory Space Map. + + @retval EFI_SUCCESS Search successfully. + @retval EFI_NOT_FOUND The requested descriptors does not exist. + +**/ +EFI_STATUS +SearchGcdMemorySpaces ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + OUT UINTN *StartIndex, + OUT UINTN *EndIndex + ) +{ + UINTN Index; + + *StartIndex = 0; + *EndIndex = 0; + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) && + (BaseAddress < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) { + *StartIndex = Index; + } + if (((BaseAddress + Length - 1) >= MemorySpaceMap[Index].BaseAddress) && + ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) { + *EndIndex = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + + +/** + Sets the attributes for a specified range in Gcd Memory Space Map. + + This function sets the attributes for a specified range in + Gcd Memory Space Map. + + @param MemorySpaceMap Gcd Memory Space Map as array + @param NumberOfDescriptors Number of descriptors in map + @param BaseAddress BaseAddress for the range + @param Length Length for the range + @param Attributes Attributes to set + + @retval EFI_SUCCESS Memory attributes set successfully + @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space + +**/ +EFI_STATUS +SetGcdMemorySpaceAttributes ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN StartIndex; + UINTN EndIndex; + EFI_PHYSICAL_ADDRESS RegionStart; + UINT64 RegionLength; + + DEBUG ((DEBUG_GCD, "SetGcdMemorySpaceAttributes[0x%lX; 0x%lX] = 0x%lX\n", + BaseAddress, BaseAddress + Length, Attributes)); + + // We do not support a smaller granularity than 4KB on ARM Architecture + if ((Length & EFI_PAGE_MASK) != 0) { + DEBUG ((DEBUG_WARN, + "Warning: We do not support smaller granularity than 4KB on ARM Architecture (passed length: 0x%lX).\n", + Length)); + } + + // + // Get all memory descriptors covered by the memory range + // + Status = SearchGcdMemorySpaces ( + MemorySpaceMap, + NumberOfDescriptors, + BaseAddress, + Length, + &StartIndex, + &EndIndex + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Go through all related descriptors and set attributes accordingly + // + for (Index = StartIndex; Index <= EndIndex; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) { + continue; + } + // + // Calculate the start and end address of the overlapping range + // + if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) { + RegionStart = BaseAddress; + } else { + RegionStart = MemorySpaceMap[Index].BaseAddress; + } + if ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length)) { + RegionLength = BaseAddress + Length - RegionStart; + } else { + RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart; + } + // + // Set memory attributes according to MTRR attribute and the original attribute of descriptor + // + gDS->SetMemorySpaceAttributes ( + RegionStart, + RegionLength, + (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes) + ); + } + + return EFI_SUCCESS; +} + +/** + This function modifies the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +CpuSetMemoryAttributes ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 EfiAttributes + ) +{ + EFI_STATUS Status; + UINTN ArmAttributes; + UINTN RegionBaseAddress; + UINTN RegionLength; + UINTN RegionArmAttributes; + + if (mIsFlushingGCD) { + return EFI_SUCCESS; + } + + if ((BaseAddress & (SIZE_4KB - 1)) != 0) { + // Minimum granularity is SIZE_4KB (4KB on ARM) + DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes)); + return EFI_UNSUPPORTED; + } + + // Convert the 'Attribute' into ARM Attribute + ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes); + + // Get the region starting from 'BaseAddress' and its 'Attribute' + RegionBaseAddress = BaseAddress; + Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes); + + // Data & Instruction Caches are flushed when we set new memory attributes. + // So, we only set the attributes if the new region is different. + if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) || + ((BaseAddress + Length) > (RegionBaseAddress + RegionLength))) + { + return ArmSetMemoryAttributes (BaseAddress, Length, EfiAttributes); + } else { + return EFI_SUCCESS; + } +} diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMpCore.c b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMpCore.c new file mode 100644 index 000000000..d16ae3979 --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/CpuMpCore.c @@ -0,0 +1,97 @@ +/** @file +* +* Copyright (c) 2011-2014, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include +#include +#include +#include + +#include + +ARM_PROCESSOR_TABLE mArmProcessorTableTemplate = { + { + EFI_ARM_PROCESSOR_TABLE_SIGNATURE, + 0, + EFI_ARM_PROCESSOR_TABLE_REVISION, + EFI_ARM_PROCESSOR_TABLE_OEM_ID, + EFI_ARM_PROCESSOR_TABLE_OEM_TABLE_ID, + EFI_ARM_PROCESSOR_TABLE_OEM_REVISION, + EFI_ARM_PROCESSOR_TABLE_CREATOR_ID, + EFI_ARM_PROCESSOR_TABLE_CREATOR_REVISION, + { 0 }, + 0 + }, //ARM Processor table header + 0, // Number of entries in ARM processor Table + NULL // ARM Processor Table +}; + +/** Publish ARM Processor Data table in UEFI SYSTEM Table. + * @param: HobStart Pointer to the beginning of the HOB List from PEI. + * + * Description : This function iterates through HOB list and finds ARM processor Table Entry HOB. + * If the ARM processor Table Entry HOB is found, the HOB data is copied to run-time memory + * and a pointer is assigned to it in ARM processor table. Then the ARM processor table is + * installed in EFI configuration table. +**/ +VOID +EFIAPI +PublishArmProcessorTable ( + VOID + ) +{ + EFI_PEI_HOB_POINTERS Hob; + + Hob.Raw = GetHobList (); + + // Iterate through the HOBs and find if there is ARM PROCESSOR ENTRY HOB + for (; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) { + // Check for Correct HOB type + if ((GET_HOB_TYPE (Hob)) == EFI_HOB_TYPE_GUID_EXTENSION) { + // Check for correct GUID type + if (CompareGuid(&(Hob.Guid->Name), &gArmMpCoreInfoGuid)) { + ARM_PROCESSOR_TABLE *ArmProcessorTable; + EFI_STATUS Status; + + // Allocate Runtime memory for ARM processor table + ArmProcessorTable = (ARM_PROCESSOR_TABLE*)AllocateRuntimePool(sizeof(ARM_PROCESSOR_TABLE)); + + // Check if the memory allocation is successful or not + ASSERT(NULL != ArmProcessorTable); + + // Set ARM processor table to default values + CopyMem(ArmProcessorTable,&mArmProcessorTableTemplate,sizeof(ARM_PROCESSOR_TABLE)); + + // Fill in Length fields of ARM processor table + ArmProcessorTable->Header.Length = sizeof(ARM_PROCESSOR_TABLE); + ArmProcessorTable->Header.DataLen = GET_GUID_HOB_DATA_SIZE(Hob); + + // Fill in Identifier(ARM processor table GUID) + ArmProcessorTable->Header.Identifier = gArmMpCoreInfoGuid; + + // Set Number of ARM core entries in the Table + ArmProcessorTable->NumberOfEntries = GET_GUID_HOB_DATA_SIZE(Hob)/sizeof(ARM_CORE_INFO); + + // Allocate runtime memory for ARM processor Table entries + ArmProcessorTable->ArmCpus = (ARM_CORE_INFO*)AllocateRuntimePool ( + ArmProcessorTable->NumberOfEntries * sizeof(ARM_CORE_INFO)); + + // Check if the memory allocation is successful or not + ASSERT(NULL != ArmProcessorTable->ArmCpus); + + // Copy ARM Processor Table data from HOB list to newly allocated memory + CopyMem(ArmProcessorTable->ArmCpus,GET_GUID_HOB_DATA(Hob), ArmProcessorTable->Header.DataLen); + + // Install the ARM Processor table into EFI system configuration table + Status = gBS->InstallConfigurationTable (&gArmMpCoreInfoGuid, ArmProcessorTable); + + ASSERT_EFI_ERROR (Status); + } + } + } +} diff --git a/roms/edk2/ArmPkg/Drivers/CpuDxe/Exception.c b/roms/edk2/ArmPkg/Drivers/CpuDxe/Exception.c new file mode 100644 index 000000000..50ed50ebb --- /dev/null +++ b/roms/edk2/ArmPkg/Drivers/CpuDxe/Exception.c @@ -0,0 +1,98 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" +#include +#include + +EFI_STATUS +InitializeExceptions ( + IN EFI_CPU_ARCH_PROTOCOL *Cpu + ) { + EFI_STATUS Status; + EFI_VECTOR_HANDOFF_INFO *VectorInfoList; + EFI_VECTOR_HANDOFF_INFO *VectorInfo; + BOOLEAN IrqEnabled; + BOOLEAN FiqEnabled; + + VectorInfo = (EFI_VECTOR_HANDOFF_INFO *)NULL; + Status = EfiGetSystemConfigurationTable(&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList); + if (Status == EFI_SUCCESS && VectorInfoList != NULL) { + VectorInfo = VectorInfoList; + } + + // initialize the CpuExceptionHandlerLib so we take over the exception vector table from the DXE Core + InitializeCpuExceptionHandlers(VectorInfo); + + Status = EFI_SUCCESS; + + // + // Disable interrupts + // + Cpu->GetInterruptState (Cpu, &IrqEnabled); + Cpu->DisableInterrupt (Cpu); + + // + // EFI does not use the FIQ, but a debugger might so we must disable + // as we take over the exception vectors. + // + FiqEnabled = ArmGetFiqState (); + ArmDisableFiq (); + + if (FiqEnabled) { + ArmEnableFiq (); + } + + if (IrqEnabled) { + // + // Restore interrupt state + // + Status = Cpu->EnableInterrupt (Cpu); + } + + // + // On a DEBUG build, unmask SErrors so they are delivered right away rather + // than when the OS unmasks them. This gives us a better chance of figuring + // out the cause. + // + DEBUG_CODE ( + ArmEnableAsynchronousAbort (); + ); + + return Status; +} + +/** +This function registers and enables the handler specified by InterruptHandler for a processor +interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the +handler for the processor interrupt or exception type specified by InterruptType is uninstalled. +The installed handler is called once for each processor interrupt or exception. + +@param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts +are enabled and FALSE if interrupts are disabled. +@param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called +when a processor interrupt occurs. If this parameter is NULL, then the handler +will be uninstalled. + +@retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. +@retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was +previously installed. +@retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not +previously installed. +@retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +RegisterInterruptHandler( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) { + // pass down to CpuExceptionHandlerLib + return (EFI_STATUS)RegisterCpuInterruptHandler(InterruptType, InterruptHandler); +} -- cgit 1.2.3-korg