aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c')
-rw-r--r--roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c b/roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
new file mode 100644
index 000000000..15e836e75
--- /dev/null
+++ b/roms/edk2/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
@@ -0,0 +1,413 @@
+/** @file
+* File managing the MMU for ARMv7 architecture
+*
+* Copyright (c) 2011-2016, ARM Limited. All rights reserved.
+*
+* SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include <Uefi.h>
+#include <Chipset/ArmV7.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/ArmLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+
+#define ID_MMFR0_SHARELVL_SHIFT 12
+#define ID_MMFR0_SHARELVL_MASK 0xf
+#define ID_MMFR0_SHARELVL_ONE 0
+#define ID_MMFR0_SHARELVL_TWO 1
+
+#define ID_MMFR0_INNERSHR_SHIFT 28
+#define ID_MMFR0_INNERSHR_MASK 0xf
+#define ID_MMFR0_OUTERSHR_SHIFT 8
+#define ID_MMFR0_OUTERSHR_MASK 0xf
+
+#define ID_MMFR0_SHR_IMP_UNCACHED 0
+#define ID_MMFR0_SHR_IMP_HW_COHERENT 1
+#define ID_MMFR0_SHR_IGNORED 0xf
+
+UINTN
+EFIAPI
+ArmReadIdMmfr0 (
+ VOID
+ );
+
+BOOLEAN
+EFIAPI
+ArmHasMpExtensions (
+ VOID
+ );
+
+STATIC
+BOOLEAN
+PreferNonshareableMemory (
+ VOID
+ )
+{
+ UINTN Mmfr;
+ UINTN Val;
+
+ if (FeaturePcdGet (PcdNormalMemoryNonshareableOverride)) {
+ return TRUE;
+ }
+
+ //
+ // Check whether the innermost level of shareability (the level we will use
+ // by default to map normal memory) is implemented with hardware coherency
+ // support. Otherwise, revert to mapping as non-shareable.
+ //
+ Mmfr = ArmReadIdMmfr0 ();
+ switch ((Mmfr >> ID_MMFR0_SHARELVL_SHIFT) & ID_MMFR0_SHARELVL_MASK) {
+ case ID_MMFR0_SHARELVL_ONE:
+ // one level of shareability
+ Val = (Mmfr >> ID_MMFR0_OUTERSHR_SHIFT) & ID_MMFR0_OUTERSHR_MASK;
+ break;
+ case ID_MMFR0_SHARELVL_TWO:
+ // two levels of shareability
+ Val = (Mmfr >> ID_MMFR0_INNERSHR_SHIFT) & ID_MMFR0_INNERSHR_MASK;
+ break;
+ default:
+ // unexpected value -> shareable is the safe option
+ ASSERT (FALSE);
+ return FALSE;
+ }
+ return Val != ID_MMFR0_SHR_IMP_HW_COHERENT;
+}
+
+STATIC
+VOID
+PopulateLevel2PageTable (
+ IN UINT32 *SectionEntry,
+ IN UINT32 PhysicalBase,
+ IN UINT32 RemainLength,
+ IN ARM_MEMORY_REGION_ATTRIBUTES Attributes
+ )
+{
+ UINT32* PageEntry;
+ UINT32 Pages;
+ UINT32 Index;
+ UINT32 PageAttributes;
+ UINT32 SectionDescriptor;
+ UINT32 TranslationTable;
+ UINT32 BaseSectionAddress;
+ UINT32 FirstPageOffset;
+
+ switch (Attributes) {
+ case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
+ PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:
+ PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
+ PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
+ PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
+ PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
+ PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
+ break;
+ default:
+ PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
+ break;
+ }
+
+ if (PreferNonshareableMemory ()) {
+ PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
+ }
+
+ // Check if the Section Entry has already been populated. Otherwise attach a
+ // Level 2 Translation Table to it
+ if (*SectionEntry != 0) {
+ // The entry must be a page table. Otherwise it exists an overlapping in the memory map
+ if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(*SectionEntry)) {
+ TranslationTable = *SectionEntry & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK;
+ } else if ((*SectionEntry & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
+ // Case where a virtual memory map descriptor overlapped a section entry
+
+ // Allocate a Level2 Page Table for this Section
+ TranslationTable = (UINTN)AllocateAlignedPages (
+ EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_PAGE_SIZE),
+ TRANSLATION_TABLE_PAGE_ALIGNMENT);
+
+ // Translate the Section Descriptor into Page Descriptor
+ SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
+
+ BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(*SectionEntry);
+
+ //
+ // Make sure we are not inadvertently hitting in the caches
+ // when populating the page tables
+ //
+ InvalidateDataCacheRange ((VOID *)TranslationTable,
+ TRANSLATION_TABLE_PAGE_SIZE);
+
+ // Populate the new Level2 Page Table for the section
+ PageEntry = (UINT32*)TranslationTable;
+ for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
+ PageEntry[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseSectionAddress + (Index << 12)) | SectionDescriptor;
+ }
+
+ // Overwrite the section entry to point to the new Level2 Translation Table
+ *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
+ (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
+ TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
+ } else {
+ // We do not support the other section type (16MB Section)
+ ASSERT(0);
+ return;
+ }
+ } else {
+ TranslationTable = (UINTN)AllocateAlignedPages (
+ EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_PAGE_SIZE),
+ TRANSLATION_TABLE_PAGE_ALIGNMENT);
+ //
+ // Make sure we are not inadvertently hitting in the caches
+ // when populating the page tables
+ //
+ InvalidateDataCacheRange ((VOID *)TranslationTable,
+ TRANSLATION_TABLE_PAGE_SIZE);
+ ZeroMem ((VOID *)TranslationTable, TRANSLATION_TABLE_PAGE_SIZE);
+
+ *SectionEntry = (TranslationTable & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) |
+ (IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(Attributes) ? (1 << 3) : 0) |
+ TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
+ }
+
+ FirstPageOffset = (PhysicalBase & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
+ PageEntry = (UINT32 *)TranslationTable + FirstPageOffset;
+ Pages = RemainLength / TT_DESCRIPTOR_PAGE_SIZE;
+
+ ASSERT (FirstPageOffset + Pages <= TRANSLATION_TABLE_PAGE_COUNT);
+
+ for (Index = 0; Index < Pages; Index++) {
+ *PageEntry++ = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(PhysicalBase) | PageAttributes;
+ PhysicalBase += TT_DESCRIPTOR_PAGE_SIZE;
+ }
+
+ //
+ // Invalidate again to ensure that any line fetches that may have occurred
+ // [speculatively] since the previous invalidate are evicted again.
+ //
+ ArmDataMemoryBarrier ();
+ InvalidateDataCacheRange ((UINT32 *)TranslationTable + FirstPageOffset,
+ RemainLength / TT_DESCRIPTOR_PAGE_SIZE * sizeof (*PageEntry));
+}
+
+STATIC
+VOID
+FillTranslationTable (
+ IN UINT32 *TranslationTable,
+ IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryRegion
+ )
+{
+ UINT32 *SectionEntry;
+ UINT32 Attributes;
+ UINT32 PhysicalBase;
+ UINT64 RemainLength;
+ UINT32 PageMapLength;
+
+ ASSERT(MemoryRegion->Length > 0);
+
+ if (MemoryRegion->PhysicalBase >= SIZE_4GB) {
+ return;
+ }
+
+ PhysicalBase = MemoryRegion->PhysicalBase;
+ RemainLength = MIN(MemoryRegion->Length, SIZE_4GB - PhysicalBase);
+
+ switch (MemoryRegion->Attributes) {
+ case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
+ Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
+ Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(0);
+ Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
+ Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(0);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
+ Attributes = TT_DESCRIPTOR_SECTION_DEVICE(0);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
+ Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
+ Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:
+ Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK(1);
+ Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
+ Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH(1);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
+ Attributes = TT_DESCRIPTOR_SECTION_DEVICE(1);
+ break;
+ case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
+ Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(1);
+ break;
+ default:
+ Attributes = TT_DESCRIPTOR_SECTION_UNCACHED(0);
+ break;
+ }
+
+ if (PreferNonshareableMemory ()) {
+ Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
+ }
+
+ // Get the first section entry for this mapping
+ SectionEntry = TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(TranslationTable, MemoryRegion->VirtualBase);
+
+ while (RemainLength != 0) {
+ if (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE == 0 &&
+ RemainLength >= TT_DESCRIPTOR_SECTION_SIZE) {
+ // Case: Physical address aligned on the Section Size (1MB) && the length
+ // is greater than the Section Size
+ *SectionEntry = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(PhysicalBase) | Attributes;
+
+ //
+ // Issue a DMB to ensure that the page table entry update made it to
+ // memory before we issue the invalidate, otherwise, a subsequent
+ // speculative fetch could observe the old value.
+ //
+ ArmDataMemoryBarrier ();
+ ArmInvalidateDataCacheEntryByMVA ((UINTN)SectionEntry++);
+
+ PhysicalBase += TT_DESCRIPTOR_SECTION_SIZE;
+ RemainLength -= TT_DESCRIPTOR_SECTION_SIZE;
+ } else {
+ PageMapLength = MIN (RemainLength, TT_DESCRIPTOR_SECTION_SIZE -
+ (PhysicalBase % TT_DESCRIPTOR_SECTION_SIZE));
+
+ // Case: Physical address aligned on the Section Size (1MB) && the length
+ // does not fill a section
+ // Case: Physical address NOT aligned on the Section Size (1MB)
+ PopulateLevel2PageTable (SectionEntry, PhysicalBase, PageMapLength,
+ MemoryRegion->Attributes);
+
+ //
+ // Issue a DMB to ensure that the page table entry update made it to
+ // memory before we issue the invalidate, otherwise, a subsequent
+ // speculative fetch could observe the old value.
+ //
+ ArmDataMemoryBarrier ();
+ ArmInvalidateDataCacheEntryByMVA ((UINTN)SectionEntry++);
+
+ // If it is the last entry
+ if (RemainLength < TT_DESCRIPTOR_SECTION_SIZE) {
+ break;
+ }
+
+ PhysicalBase += PageMapLength;
+ RemainLength -= PageMapLength;
+ }
+ }
+}
+
+RETURN_STATUS
+EFIAPI
+ArmConfigureMmu (
+ IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable,
+ OUT VOID **TranslationTableBase OPTIONAL,
+ OUT UINTN *TranslationTableSize OPTIONAL
+ )
+{
+ VOID *TranslationTable;
+ UINT32 TTBRAttributes;
+
+ TranslationTable = AllocateAlignedPages (
+ EFI_SIZE_TO_PAGES (TRANSLATION_TABLE_SECTION_SIZE),
+ TRANSLATION_TABLE_SECTION_ALIGNMENT);
+ if (TranslationTable == NULL) {
+ return RETURN_OUT_OF_RESOURCES;
+ }
+
+ if (TranslationTableBase != NULL) {
+ *TranslationTableBase = TranslationTable;
+ }
+
+ if (TranslationTableSize != NULL) {
+ *TranslationTableSize = TRANSLATION_TABLE_SECTION_SIZE;
+ }
+
+ //
+ // Make sure we are not inadvertently hitting in the caches
+ // when populating the page tables
+ //
+ InvalidateDataCacheRange (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
+ ZeroMem (TranslationTable, TRANSLATION_TABLE_SECTION_SIZE);
+
+ while (MemoryTable->Length != 0) {
+ FillTranslationTable (TranslationTable, MemoryTable);
+ MemoryTable++;
+ }
+
+ TTBRAttributes = ArmHasMpExtensions () ? TTBR_MP_WRITE_BACK_ALLOC
+ : TTBR_WRITE_BACK_ALLOC;
+ if (TTBRAttributes & TTBR_SHAREABLE) {
+ if (PreferNonshareableMemory ()) {
+ TTBRAttributes ^= TTBR_SHAREABLE;
+ } else {
+ //
+ // Unlike the S bit in the short descriptors, which implies inner shareable
+ // on an implementation that supports two levels, the meaning of the S bit
+ // in the TTBR depends on the NOS bit, which defaults to Outer Shareable.
+ // However, we should only set this bit after we have confirmed that the
+ // implementation supports multiple levels, or else the NOS bit is UNK/SBZP
+ //
+ if (((ArmReadIdMmfr0 () >> 12) & 0xf) != 0) {
+ TTBRAttributes |= TTBR_NOT_OUTER_SHAREABLE;
+ }
+ }
+ }
+
+ ArmSetTTBR0 ((VOID *)((UINTN)TranslationTable | TTBRAttributes));
+
+ //
+ // The TTBCR register value is undefined at reset in the Non-Secure world.
+ // Writing 0 has the effect of:
+ // Clearing EAE: Use short descriptors, as mandated by specification.
+ // Clearing PD0 and PD1: Translation Table Walk Disable is off.
+ // Clearing N: Perform all translation table walks through TTBR0.
+ // (0 is the default reset value in systems not implementing
+ // the Security Extensions.)
+ //
+ ArmSetTTBCR (0);
+
+ ArmSetDomainAccessControl (DOMAIN_ACCESS_CONTROL_NONE(15) |
+ DOMAIN_ACCESS_CONTROL_NONE(14) |
+ DOMAIN_ACCESS_CONTROL_NONE(13) |
+ DOMAIN_ACCESS_CONTROL_NONE(12) |
+ DOMAIN_ACCESS_CONTROL_NONE(11) |
+ DOMAIN_ACCESS_CONTROL_NONE(10) |
+ DOMAIN_ACCESS_CONTROL_NONE( 9) |
+ DOMAIN_ACCESS_CONTROL_NONE( 8) |
+ DOMAIN_ACCESS_CONTROL_NONE( 7) |
+ DOMAIN_ACCESS_CONTROL_NONE( 6) |
+ DOMAIN_ACCESS_CONTROL_NONE( 5) |
+ DOMAIN_ACCESS_CONTROL_NONE( 4) |
+ DOMAIN_ACCESS_CONTROL_NONE( 3) |
+ DOMAIN_ACCESS_CONTROL_NONE( 2) |
+ DOMAIN_ACCESS_CONTROL_NONE( 1) |
+ DOMAIN_ACCESS_CONTROL_CLIENT(0));
+
+ ArmEnableInstructionCache();
+ ArmEnableDataCache();
+ ArmEnableMmu();
+ return RETURN_SUCCESS;
+}