From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: edk2-devel@lists.01.org, leif.lindholm@linaro.org, lersek@redhat.com
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Subject: [PATCH 2/5] ArmPkg: move ARM version of SetMemoryAttributes to ArmMmuLib
Date: Wed, 1 Mar 2017 16:31:40 +0000 [thread overview]
Message-ID: <1488385903-30267-3-git-send-email-ard.biesheuvel@linaro.org> (raw)
In-Reply-To: <1488385903-30267-1-git-send-email-ard.biesheuvel@linaro.org>
... where it belongs, since AARCH64 already keeps it there, and
non DXE users of ArmMmuLib (such as DxeIpl, for the non-executable
stack) may need its functionality as well.
While at it, rename SetMemoryAttributes to ArmSetMemoryAttributes,
and make any functions that are not exported STATIC. Also, replace
an explicit gBS->AllocatePages() call [which is DXE specific] with
MemoryAllocationLib::AllocatePages().
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
ArmPkg/Drivers/CpuDxe/Arm/Mmu.c | 368 --------------------
ArmPkg/Drivers/CpuDxe/CpuDxe.h | 14 +-
ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c | 2 +-
ArmPkg/Include/Library/ArmMmuLib.h | 8 +
ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c | 2 +-
ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c | 368 ++++++++++++++++++++
6 files changed, 379 insertions(+), 383 deletions(-)
diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
index 6322d301060e..b985dd743f02 100644
--- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
@@ -343,374 +343,6 @@ SyncCacheConfig (
return EFI_SUCCESS;
}
-
-
-EFI_STATUS
-UpdatePageEntries (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINT64 Length,
- IN UINT64 Attributes,
- IN EFI_PHYSICAL_ADDRESS VirtualMask
- )
-{
- EFI_STATUS Status;
- UINT32 EntryValue;
- UINT32 EntryMask;
- UINT32 FirstLevelIdx;
- UINT32 Offset;
- UINT32 NumPageEntries;
- UINT32 Descriptor;
- UINT32 p;
- UINT32 PageTableIndex;
- UINT32 PageTableEntry;
- UINT32 CurrentPageTableEntry;
- VOID *Mva;
-
- volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
- volatile ARM_PAGE_TABLE_ENTRY *PageTable;
-
- Status = EFI_SUCCESS;
-
- // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
- // EntryValue: values at bit positions specified by EntryMask
- EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK;
- if ((Attributes & EFI_MEMORY_XP) != 0) {
- EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN;
- } else {
- EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
- }
-
- // Although the PI spec is unclear on this the GCD guarantees that only
- // one Attribute bit is set at a time, so we can safely use a switch statement
- if ((Attributes & EFI_MEMORY_UC) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
- // map to strongly ordered
- EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
- } else if ((Attributes & EFI_MEMORY_WC) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
- // map to normal non-cachable
- EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
- } else if ((Attributes & EFI_MEMORY_WT) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
- // write through with no-allocate
- EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
- } else if ((Attributes & EFI_MEMORY_WB) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
- // write back (with allocate)
- EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
- }
-
- if ((Attributes & EFI_MEMORY_RO) != 0) {
- EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
- } else {
- EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
- }
-
- // Obtain page table base
- FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
-
- // Calculate number of 4KB page table entries to change
- NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
-
- // Iterate for the number of 4KB pages to change
- Offset = 0;
- for(p = 0; p < NumPageEntries; p++) {
- // Calculate index into first level translation table for page table value
-
- FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
- ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
-
- // Read the descriptor from the first level page table
- Descriptor = FirstLevelTable[FirstLevelIdx];
-
- // Does this descriptor need to be converted from section entry to 4K pages?
- if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
- Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
- if (EFI_ERROR(Status)) {
- // Exit for loop
- break;
- }
-
- // Re-read descriptor
- Descriptor = FirstLevelTable[FirstLevelIdx];
- }
-
- // Obtain page table base address
- PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
-
- // Calculate index into the page table
- PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
- ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
-
- // Get the entry
- CurrentPageTableEntry = PageTable[PageTableIndex];
-
- // Mask off appropriate fields
- PageTableEntry = CurrentPageTableEntry & ~EntryMask;
-
- // Mask in new attributes and/or permissions
- PageTableEntry |= EntryValue;
-
- if (VirtualMask != 0) {
- // Make this virtual address point at a physical page
- PageTableEntry &= ~VirtualMask;
- }
-
- if (CurrentPageTableEntry != PageTableEntry) {
- Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
- if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {
- // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
- // Note assumes switch(Attributes), not ARMv7 possibilities
- WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
- }
-
- // Only need to update if we are changing the entry
- PageTable[PageTableIndex] = PageTableEntry;
- ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
- }
-
- Status = EFI_SUCCESS;
- Offset += TT_DESCRIPTOR_PAGE_SIZE;
-
- } // End first level translation table loop
-
- return Status;
-}
-
-
-
-EFI_STATUS
-UpdateSectionEntries (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINT64 Length,
- IN UINT64 Attributes,
- IN EFI_PHYSICAL_ADDRESS VirtualMask
- )
-{
- EFI_STATUS Status = EFI_SUCCESS;
- UINT32 EntryMask;
- UINT32 EntryValue;
- UINT32 FirstLevelIdx;
- UINT32 NumSections;
- UINT32 i;
- UINT32 CurrentDescriptor;
- UINT32 Descriptor;
- VOID *Mva;
- volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
-
- // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
- // EntryValue: values at bit positions specified by EntryMask
-
- // Make sure we handle a section range that is unmapped
- EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK | TT_DESCRIPTOR_SECTION_XN_MASK |
- TT_DESCRIPTOR_SECTION_AP_MASK;
- EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
-
- // Although the PI spec is unclear on this the GCD guarantees that only
- // one Attribute bit is set at a time, so we can safely use a switch statement
- if ((Attributes & EFI_MEMORY_UC) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
- // map to strongly ordered
- EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
- } else if ((Attributes & EFI_MEMORY_WC) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
- // map to normal non-cachable
- EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
- } else if ((Attributes & EFI_MEMORY_WT) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
- // write through with no-allocate
- EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
- } else if ((Attributes & EFI_MEMORY_WB) != 0) {
- // modify cacheability attributes
- EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
- // write back (with allocate)
- EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
- }
-
- if ((Attributes & EFI_MEMORY_RO) != 0) {
- EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
- } else {
- EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
- }
-
- if ((Attributes & EFI_MEMORY_XP) != 0) {
- EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
- }
-
- // obtain page table base
- FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
-
- // calculate index into first level translation table for start of modification
- FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
- ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
-
- // calculate number of 1MB first level entries this applies to
- NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
-
- // iterate through each descriptor
- for(i=0; i<NumSections; i++) {
- CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
-
- // has this descriptor already been coverted to pages?
- if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
- // forward this 1MB range to page table function instead
- Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);
- } else {
- // still a section entry
-
- // mask off appropriate fields
- Descriptor = CurrentDescriptor & ~EntryMask;
-
- // mask in new attributes and/or permissions
- Descriptor |= EntryValue;
- if (VirtualMask != 0) {
- Descriptor &= ~VirtualMask;
- }
-
- if (CurrentDescriptor != Descriptor) {
- Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
- if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {
- // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
- // Note assumes switch(Attributes), not ARMv7 possabilities
- WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
- }
-
- // Only need to update if we are changing the descriptor
- FirstLevelTable[FirstLevelIdx + i] = Descriptor;
- ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
- }
-
- Status = EFI_SUCCESS;
- }
- }
-
- return Status;
-}
-
-EFI_STATUS
-ConvertSectionToPages (
- IN EFI_PHYSICAL_ADDRESS BaseAddress
- )
-{
- EFI_STATUS Status;
- EFI_PHYSICAL_ADDRESS PageTableAddr;
- UINT32 FirstLevelIdx;
- UINT32 SectionDescriptor;
- UINT32 PageTableDescriptor;
- UINT32 PageDescriptor;
- UINT32 Index;
-
- volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
- volatile ARM_PAGE_TABLE_ENTRY *PageTable;
-
- DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
-
- // Obtain page table base
- FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
-
- // Calculate index into first level translation table for start of modification
- FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
- ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
-
- // Get section attributes and convert to page attributes
- SectionDescriptor = FirstLevelTable[FirstLevelIdx];
- PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
-
- // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
- Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 1, &PageTableAddr);
- if (EFI_ERROR(Status)) {
- return Status;
- }
-
- PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)(UINTN)PageTableAddr;
-
- // Write the page table entries out
- for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
- PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
- }
-
- // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
- WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)PageTableAddr, TT_DESCRIPTOR_PAGE_SIZE);
-
- // Formulate page table entry, Domain=0, NS=0
- PageTableDescriptor = (((UINTN)PageTableAddr) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
-
- // Write the page table entry out, replacing section entry
- FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
-
- return EFI_SUCCESS;
-}
-
-
-
-EFI_STATUS
-SetMemoryAttributes (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINT64 Length,
- IN UINT64 Attributes,
- IN EFI_PHYSICAL_ADDRESS VirtualMask
- )
-{
- EFI_STATUS Status;
- UINT64 ChunkLength;
- BOOLEAN FlushTlbs;
-
- FlushTlbs = FALSE;
- while (Length > 0) {
- if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
- Length >= TT_DESCRIPTOR_SECTION_SIZE) {
-
- ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
-
- DEBUG ((DEBUG_PAGE | DEBUG_INFO,
- "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
- BaseAddress, ChunkLength, Attributes));
-
- Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes,
- VirtualMask);
-
- FlushTlbs = TRUE;
- } else {
-
- //
- // Process page by page until the next section boundary, but only if
- // we have more than a section's worth of area to deal with after that.
- //
- ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
- (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
- if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
- ChunkLength = Length;
- }
-
- DEBUG ((DEBUG_PAGE | DEBUG_INFO,
- "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
- BaseAddress, ChunkLength, Attributes));
-
- Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,
- VirtualMask);
- }
-
- if (EFI_ERROR (Status)) {
- break;
- }
-
- BaseAddress += ChunkLength;
- Length -= ChunkLength;
- }
-
- if (FlushTlbs) {
- ArmInvalidateTlb ();
- }
- return Status;
-}
-
UINT64
EfiAttributeToArmAttribute (
IN UINT64 EfiAttributes
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
index a46db8d25754..a0f71e69ec09 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
@@ -19,6 +19,7 @@
#include <Uefi.h>
#include <Library/ArmLib.h>
+#include <Library/ArmMmuLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
@@ -112,11 +113,6 @@ SyncCacheConfig (
IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol
);
-EFI_STATUS
-ConvertSectionToPages (
- IN EFI_PHYSICAL_ADDRESS BaseAddress
- );
-
/**
* Publish ARM Processor Data table in UEFI SYSTEM Table.
* @param HobStart Pointer to the beginning of the HOB List from PEI.
@@ -132,14 +128,6 @@ PublishArmProcessorTable(
VOID
);
-EFI_STATUS
-SetMemoryAttributes (
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINT64 Length,
- IN UINT64 Attributes,
- IN EFI_PHYSICAL_ADDRESS VirtualMask
- );
-
// The ARM Attributes might be defined on 64-bit (case of the long format description table)
UINT64
EfiAttributeToArmAttribute (
diff --git a/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c b/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c
index 0f36a058407a..d0a3fedd3aa7 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c
+++ b/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c
@@ -210,7 +210,7 @@ CpuSetMemoryAttributes (
if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) ||
((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
{
- return SetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);
+ return ArmSetMemoryAttributes (BaseAddress, Length, EfiAttributes, 0);
} else {
return EFI_SUCCESS;
}
diff --git a/ArmPkg/Include/Library/ArmMmuLib.h b/ArmPkg/Include/Library/ArmMmuLib.h
index c1d43872d548..d3a302fa8125 100644
--- a/ArmPkg/Include/Library/ArmMmuLib.h
+++ b/ArmPkg/Include/Library/ArmMmuLib.h
@@ -62,4 +62,12 @@ ArmReplaceLiveTranslationEntry (
IN UINT64 Value
);
+EFI_STATUS
+ArmSetMemoryAttributes (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN EFI_PHYSICAL_ADDRESS VirtualMask
+ );
+
#endif
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index df170d20a2c2..77f108971f3e 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -447,7 +447,7 @@ GcdAttributeToPageAttribute (
}
EFI_STATUS
-SetMemoryAttributes (
+ArmSetMemoryAttributes (
IN EFI_PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN UINT64 Attributes,
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
index 4b6f4ce392b7..93980d6d12db 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
@@ -16,6 +16,7 @@
#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>
@@ -36,6 +37,12 @@
#define ID_MMFR0_SHR_IMP_HW_COHERENT 1
#define ID_MMFR0_SHR_IGNORED 0xf
+// First Level Descriptors
+typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR;
+
+// Second Level Descriptors
+typedef UINT32 ARM_PAGE_TABLE_ENTRY;
+
UINTN
EFIAPI
ArmReadIdMmfr0 (
@@ -406,6 +413,367 @@ ArmConfigureMmu (
return RETURN_SUCCESS;
}
+STATIC
+EFI_STATUS
+ConvertSectionToPages (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress
+ )
+{
+ UINT32 FirstLevelIdx;
+ UINT32 SectionDescriptor;
+ UINT32 PageTableDescriptor;
+ UINT32 PageDescriptor;
+ UINT32 Index;
+
+ volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
+ volatile ARM_PAGE_TABLE_ENTRY *PageTable;
+
+ DEBUG ((EFI_D_PAGE, "Converting section at 0x%x to pages\n", (UINTN)BaseAddress));
+
+ // Obtain page table base
+ FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
+
+ // Calculate index into first level translation table for start of modification
+ FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
+ ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
+
+ // Get section attributes and convert to page attributes
+ SectionDescriptor = FirstLevelTable[FirstLevelIdx];
+ PageDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
+
+ // Allocate a page table for the 4KB entries (we use up a full page even though we only need 1KB)
+ PageTable = (volatile ARM_PAGE_TABLE_ENTRY *)AllocatePages (1);
+ if (PageTable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ // Write the page table entries out
+ for (Index = 0; Index < TRANSLATION_TABLE_PAGE_COUNT; Index++) {
+ PageTable[Index] = TT_DESCRIPTOR_PAGE_BASE_ADDRESS(BaseAddress + (Index << 12)) | PageDescriptor;
+ }
+
+ // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks
+ WriteBackInvalidateDataCacheRange ((VOID *)PageTable, TT_DESCRIPTOR_PAGE_SIZE);
+
+ // Formulate page table entry, Domain=0, NS=0
+ PageTableDescriptor = (((UINTN)PageTable) & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK) | TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE;
+
+ // Write the page table entry out, replacing section entry
+ FirstLevelTable[FirstLevelIdx] = PageTableDescriptor;
+
+ return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+UpdatePageEntries (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN EFI_PHYSICAL_ADDRESS VirtualMask
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EntryValue;
+ UINT32 EntryMask;
+ UINT32 FirstLevelIdx;
+ UINT32 Offset;
+ UINT32 NumPageEntries;
+ UINT32 Descriptor;
+ UINT32 p;
+ UINT32 PageTableIndex;
+ UINT32 PageTableEntry;
+ UINT32 CurrentPageTableEntry;
+ VOID *Mva;
+
+ volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
+ volatile ARM_PAGE_TABLE_ENTRY *PageTable;
+
+ Status = EFI_SUCCESS;
+
+ // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
+ // EntryValue: values at bit positions specified by EntryMask
+ EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK;
+ if ((Attributes & EFI_MEMORY_XP) != 0) {
+ EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN;
+ } else {
+ EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
+ }
+
+ // Although the PI spec is unclear on this the GCD guarantees that only
+ // one Attribute bit is set at a time, so we can safely use a switch statement
+ if ((Attributes & EFI_MEMORY_UC) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
+ // map to strongly ordered
+ EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
+ } else if ((Attributes & EFI_MEMORY_WC) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
+ // map to normal non-cachable
+ EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
+ } else if ((Attributes & EFI_MEMORY_WT) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
+ // write through with no-allocate
+ EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
+ } else if ((Attributes & EFI_MEMORY_WB) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK;
+ // write back (with allocate)
+ EntryValue |= TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
+ }
+
+ if ((Attributes & EFI_MEMORY_RO) != 0) {
+ EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
+ } else {
+ EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
+ }
+
+ // Obtain page table base
+ FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
+
+ // Calculate number of 4KB page table entries to change
+ NumPageEntries = Length / TT_DESCRIPTOR_PAGE_SIZE;
+
+ // Iterate for the number of 4KB pages to change
+ Offset = 0;
+ for(p = 0; p < NumPageEntries; p++) {
+ // Calculate index into first level translation table for page table value
+
+ FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress + Offset) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
+ ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
+
+ // Read the descriptor from the first level page table
+ Descriptor = FirstLevelTable[FirstLevelIdx];
+
+ // Does this descriptor need to be converted from section entry to 4K pages?
+ if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Descriptor)) {
+ Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
+ if (EFI_ERROR(Status)) {
+ // Exit for loop
+ break;
+ }
+
+ // Re-read descriptor
+ Descriptor = FirstLevelTable[FirstLevelIdx];
+ }
+
+ // Obtain page table base address
+ PageTable = (ARM_PAGE_TABLE_ENTRY *)TT_DESCRIPTOR_PAGE_BASE_ADDRESS(Descriptor);
+
+ // Calculate index into the page table
+ PageTableIndex = ((BaseAddress + Offset) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
+ ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
+
+ // Get the entry
+ CurrentPageTableEntry = PageTable[PageTableIndex];
+
+ // Mask off appropriate fields
+ PageTableEntry = CurrentPageTableEntry & ~EntryMask;
+
+ // Mask in new attributes and/or permissions
+ PageTableEntry |= EntryValue;
+
+ if (VirtualMask != 0) {
+ // Make this virtual address point at a physical page
+ PageTableEntry &= ~VirtualMask;
+ }
+
+ if (CurrentPageTableEntry != PageTableEntry) {
+ Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT));
+ if ((CurrentPageTableEntry & TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) == TT_DESCRIPTOR_PAGE_CACHEABLE_MASK) {
+ // The current section mapping is cacheable so Clean/Invalidate the MVA of the page
+ // Note assumes switch(Attributes), not ARMv7 possibilities
+ WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE);
+ }
+
+ // Only need to update if we are changing the entry
+ PageTable[PageTableIndex] = PageTableEntry;
+ ArmUpdateTranslationTableEntry ((VOID *)&PageTable[PageTableIndex], Mva);
+ }
+
+ Status = EFI_SUCCESS;
+ Offset += TT_DESCRIPTOR_PAGE_SIZE;
+
+ } // End first level translation table loop
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+UpdateSectionEntries (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN EFI_PHYSICAL_ADDRESS VirtualMask
+ )
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ UINT32 EntryMask;
+ UINT32 EntryValue;
+ UINT32 FirstLevelIdx;
+ UINT32 NumSections;
+ UINT32 i;
+ UINT32 CurrentDescriptor;
+ UINT32 Descriptor;
+ VOID *Mva;
+ volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable;
+
+ // EntryMask: bitmask of values to change (1 = change this value, 0 = leave alone)
+ // EntryValue: values at bit positions specified by EntryMask
+
+ // Make sure we handle a section range that is unmapped
+ EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK | TT_DESCRIPTOR_SECTION_XN_MASK |
+ TT_DESCRIPTOR_SECTION_AP_MASK;
+ EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
+
+ // Although the PI spec is unclear on this the GCD guarantees that only
+ // one Attribute bit is set at a time, so we can safely use a switch statement
+ if ((Attributes & EFI_MEMORY_UC) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
+ // map to strongly ordered
+ EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0
+ } else if ((Attributes & EFI_MEMORY_WC) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
+ // map to normal non-cachable
+ EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0
+ } else if ((Attributes & EFI_MEMORY_WT) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
+ // write through with no-allocate
+ EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0
+ } else if ((Attributes & EFI_MEMORY_WB) != 0) {
+ // modify cacheability attributes
+ EntryMask |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK;
+ // write back (with allocate)
+ EntryValue |= TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1
+ }
+
+ if ((Attributes & EFI_MEMORY_RO) != 0) {
+ EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO;
+ } else {
+ EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW;
+ }
+
+ if ((Attributes & EFI_MEMORY_XP) != 0) {
+ EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
+ }
+
+ // obtain page table base
+ FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
+
+ // calculate index into first level translation table for start of modification
+ FirstLevelIdx = TT_DESCRIPTOR_SECTION_BASE_ADDRESS(BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT;
+ ASSERT (FirstLevelIdx < TRANSLATION_TABLE_SECTION_COUNT);
+
+ // calculate number of 1MB first level entries this applies to
+ NumSections = Length / TT_DESCRIPTOR_SECTION_SIZE;
+
+ // iterate through each descriptor
+ for(i=0; i<NumSections; i++) {
+ CurrentDescriptor = FirstLevelTable[FirstLevelIdx + i];
+
+ // has this descriptor already been coverted to pages?
+ if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(CurrentDescriptor)) {
+ // forward this 1MB range to page table function instead
+ Status = UpdatePageEntries ((FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, TT_DESCRIPTOR_SECTION_SIZE, Attributes, VirtualMask);
+ } else {
+ // still a section entry
+
+ // mask off appropriate fields
+ Descriptor = CurrentDescriptor & ~EntryMask;
+
+ // mask in new attributes and/or permissions
+ Descriptor |= EntryValue;
+ if (VirtualMask != 0) {
+ Descriptor &= ~VirtualMask;
+ }
+
+ if (CurrentDescriptor != Descriptor) {
+ Mva = (VOID *)(UINTN)(((UINTN)FirstLevelTable) << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
+ if ((CurrentDescriptor & TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) == TT_DESCRIPTOR_SECTION_CACHEABLE_MASK) {
+ // The current section mapping is cacheable so Clean/Invalidate the MVA of the section
+ // Note assumes switch(Attributes), not ARMv7 possabilities
+ WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB);
+ }
+
+ // Only need to update if we are changing the descriptor
+ FirstLevelTable[FirstLevelIdx + i] = Descriptor;
+ ArmUpdateTranslationTableEntry ((VOID *)&FirstLevelTable[FirstLevelIdx + i], Mva);
+ }
+
+ Status = EFI_SUCCESS;
+ }
+ }
+
+ return Status;
+}
+
+EFI_STATUS
+ArmSetMemoryAttributes (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN EFI_PHYSICAL_ADDRESS VirtualMask
+ )
+{
+ EFI_STATUS Status;
+ UINT64 ChunkLength;
+ BOOLEAN FlushTlbs;
+
+ FlushTlbs = FALSE;
+ while (Length > 0) {
+ if ((BaseAddress % TT_DESCRIPTOR_SECTION_SIZE == 0) &&
+ Length >= TT_DESCRIPTOR_SECTION_SIZE) {
+
+ ChunkLength = Length - Length % TT_DESCRIPTOR_SECTION_SIZE;
+
+ DEBUG ((DEBUG_PAGE | DEBUG_INFO,
+ "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n",
+ BaseAddress, ChunkLength, Attributes));
+
+ Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes,
+ VirtualMask);
+
+ FlushTlbs = TRUE;
+ } else {
+
+ //
+ // Process page by page until the next section boundary, but only if
+ // we have more than a section's worth of area to deal with after that.
+ //
+ ChunkLength = TT_DESCRIPTOR_SECTION_SIZE -
+ (BaseAddress % TT_DESCRIPTOR_SECTION_SIZE);
+ if (ChunkLength + TT_DESCRIPTOR_SECTION_SIZE > Length) {
+ ChunkLength = Length;
+ }
+
+ DEBUG ((DEBUG_PAGE | DEBUG_INFO,
+ "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n",
+ BaseAddress, ChunkLength, Attributes));
+
+ Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes,
+ VirtualMask);
+ }
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ BaseAddress += ChunkLength;
+ Length -= ChunkLength;
+ }
+
+ if (FlushTlbs) {
+ ArmInvalidateTlb ();
+ }
+ return Status;
+}
+
RETURN_STATUS
ArmSetMemoryRegionNoExec (
IN EFI_PHYSICAL_ADDRESS BaseAddress,
--
2.7.4
next prev parent reply other threads:[~2017-03-01 16:31 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-03-01 16:31 [PATCH 0/5] ArmPkg, ArmVirtPkg ARM: enable non-executable stack Ard Biesheuvel
2017-03-01 16:31 ` [PATCH 1/5] ArmPkg/ArmMmuLib AARCH64: use correct return type for exported functions Ard Biesheuvel
2017-03-06 14:57 ` Leif Lindholm
2017-03-01 16:31 ` Ard Biesheuvel [this message]
2017-03-06 16:03 ` [PATCH 2/5] ArmPkg: move ARM version of SetMemoryAttributes to ArmMmuLib Leif Lindholm
2017-03-06 16:05 ` Ard Biesheuvel
2017-03-06 16:21 ` Leif Lindholm
2017-03-01 16:31 ` [PATCH 3/5] ArmPkg/ArmMmuLib: remove VirtualMask arg from ArmSetMemoryAttributes Ard Biesheuvel
2017-03-06 16:06 ` Leif Lindholm
2017-03-01 16:31 ` [PATCH 4/5] ArmPkg/ArmMmuLib ARM: implement memory permission control routines Ard Biesheuvel
2017-03-06 16:11 ` Leif Lindholm
2017-03-01 16:31 ` [PATCH 5/5] ArmVirtPkg: enable non-executable DXE stack for all platforms Ard Biesheuvel
2017-03-01 19:10 ` Laszlo Ersek
2017-03-01 19:10 ` Ard Biesheuvel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1488385903-30267-3-git-send-email-ard.biesheuvel@linaro.org \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox