X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmPkg%2FDrivers%2FCpuDxe%2FArm%2FMmu.c;h=d3c307f4831788a0bb4a4ba944957d97f743ae2a;hp=63da8ba8cb88567b6ed8943ffcb22903ede1e56f;hb=08b504d9d81795fad64a5b4c482b30a93a5a68aa;hpb=d9ac8a457671d6425f345ede4110c1af5ee02956 diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c index 63da8ba8cb..d3c307f483 100644 --- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c +++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c @@ -3,6 +3,7 @@ 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.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License @@ -18,6 +19,13 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include "CpuDxe.h" +#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \ + EFI_MEMORY_WC | \ + EFI_MEMORY_WT | \ + EFI_MEMORY_WB | \ + EFI_MEMORY_UCE | \ + EFI_MEMORY_WP) + // First Level Descriptors typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR; @@ -62,7 +70,7 @@ SectionToGcdAttributes ( // determine protection attributes switch(SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) { case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write - //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP; + //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP; break; case TT_DESCRIPTOR_SECTION_AP_RW_NO: @@ -73,7 +81,7 @@ SectionToGcdAttributes ( // read only cases map to write-protect case TT_DESCRIPTOR_SECTION_AP_RO_NO: case TT_DESCRIPTOR_SECTION_AP_RO_RO: - *GcdAttributes |= EFI_MEMORY_WP; + *GcdAttributes |= EFI_MEMORY_RO; break; default: @@ -126,7 +134,7 @@ PageToGcdAttributes ( // determine protection attributes switch(PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) { case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write - //*GcdAttributes |= EFI_MEMORY_WP | EFI_MEMORY_RP; + //*GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP; break; case TT_DESCRIPTOR_PAGE_AP_RW_NO: @@ -137,7 +145,7 @@ PageToGcdAttributes ( // read only cases map to write-protect case TT_DESCRIPTOR_PAGE_AP_RO_NO: case TT_DESCRIPTOR_PAGE_AP_RO_RO: - *GcdAttributes |= EFI_MEMORY_WP; + *GcdAttributes |= EFI_MEMORY_RO; break; default: @@ -297,6 +305,11 @@ SyncCacheConfig ( } 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, @@ -341,10 +354,11 @@ SyncCacheConfig ( EFI_STATUS UpdatePageEntries ( - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length, - IN UINT64 Attributes, - IN EFI_PHYSICAL_ADDRESS VirtualMask + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + IN EFI_PHYSICAL_ADDRESS VirtualMask, + OUT BOOLEAN *FlushTlbs OPTIONAL ) { EFI_STATUS Status; @@ -367,50 +381,48 @@ UpdatePageEntries ( // 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; - 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 - switch (Attributes) { - case EFI_MEMORY_UC: - // 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 - break; - - case EFI_MEMORY_WC: - // 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 - break; - - case EFI_MEMORY_WT: - // 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 - break; - - case EFI_MEMORY_WB: - // 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 - break; + 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; + } - case EFI_MEMORY_WP: - case EFI_MEMORY_XP: - case EFI_MEMORY_UCE: - // cannot be implemented UEFI definition unclear for ARM - // Cause a page fault if these ranges are accessed. - EntryValue = TT_DESCRIPTOR_PAGE_TYPE_FAULT; - DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting page %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes)); - break; + // Although the PI spec is unclear on this, the GCD guarantees that only + // one Attribute bit is set at a time, so the order of the conditionals below + // is irrelevant. If no memory attribute is specified, we preserve whatever + // memory type is set in the page tables, and update the permission attributes + // only. + if (Attributes & EFI_MEMORY_UC) { + // 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) { + // 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) { + // 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) { + // 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 + } else if (Attributes & CACHE_ATTRIBUTE_MASK) { + // catch unsupported memory type attributes + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } - default: - return EFI_UNSUPPORTED; + 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 @@ -440,6 +452,9 @@ UpdatePageEntries ( // Re-read descriptor Descriptor = FirstLevelTable[FirstLevelIdx]; + if (FlushTlbs != NULL) { + *FlushTlbs = TRUE; + } } // Obtain page table base address @@ -465,15 +480,16 @@ UpdatePageEntries ( 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); + + // Clean/invalidate the cache for this page, but only + // if we are modifying the memory type attributes + if (((CurrentPageTableEntry ^ PageTableEntry) & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) != 0) { + WriteBackInvalidateDataCacheRange (Mva, TT_DESCRIPTOR_PAGE_SIZE); + } } Status = EFI_SUCCESS; @@ -509,53 +525,49 @@ UpdateSectionEntries ( // EntryValue: values at bit positions specified by EntryMask // Make sure we handle a section range that is unmapped - EntryMask = TT_DESCRIPTOR_SECTION_TYPE_MASK; + 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 - switch(Attributes) { - case EFI_MEMORY_UC: - // 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 - break; - - case EFI_MEMORY_WC: - // 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 - break; - - case EFI_MEMORY_WT: - // 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 - break; - - case EFI_MEMORY_WB: - // 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 - break; - - case EFI_MEMORY_WP: - case EFI_MEMORY_XP: - case EFI_MEMORY_RP: - case EFI_MEMORY_UCE: - // cannot be implemented UEFI definition unclear for ARM - // Cause a page fault if these ranges are accessed. - EntryValue = TT_DESCRIPTOR_SECTION_TYPE_FAULT; - DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): setting section %lx with unsupported attribute %x will page fault on access\n", BaseAddress, Attributes)); - break; + // Although the PI spec is unclear on this, the GCD guarantees that only + // one Attribute bit is set at a time, so the order of the conditionals below + // is irrelevant. If no memory attribute is specified, we preserve whatever + // memory type is set in the page tables, and update the permission attributes + // only. + if (Attributes & EFI_MEMORY_UC) { + // 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) { + // 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) { + // 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) { + // 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 + } else if (Attributes & CACHE_ATTRIBUTE_MASK) { + // catch unsupported memory type attributes + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + if (Attributes & EFI_MEMORY_RO) { + EntryValue |= TT_DESCRIPTOR_SECTION_AP_RO_RO; + } else { + EntryValue |= TT_DESCRIPTOR_SECTION_AP_RW_RW; + } - default: - return EFI_UNSUPPORTED; + if (Attributes & EFI_MEMORY_XP) { + EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK; } // obtain page table base @@ -575,7 +587,12 @@ UpdateSectionEntries ( // 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); + Status = UpdatePageEntries ( + (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT, + TT_DESCRIPTOR_SECTION_SIZE, + Attributes, + VirtualMask, + NULL); } else { // still a section entry @@ -590,15 +607,16 @@ UpdateSectionEntries ( 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); + + // Clean/invalidate the cache for this section, but only + // if we are modifying the memory type attributes + if (((CurrentDescriptor ^ Descriptor) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) != 0) { + WriteBackInvalidateDataCacheRange (Mva, SIZE_1MB); + } } Status = EFI_SUCCESS; @@ -673,27 +691,59 @@ SetMemoryAttributes ( ) { EFI_STATUS Status; + UINT64 ChunkLength; + BOOLEAN FlushTlbs; - if(((BaseAddress & 0xFFFFF) == 0) && ((Length & 0xFFFFF) == 0)) { - // Is the base and length a multiple of 1 MB? - DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU section 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes)); - Status = UpdateSectionEntries (BaseAddress, Length, Attributes, VirtualMask); - } else { - // Base and/or length is not a multiple of 1 MB - DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): MMU page 0x%x length 0x%x to %lx\n", (UINTN)BaseAddress, (UINTN)Length, Attributes)); - Status = UpdatePageEntries (BaseAddress, Length, Attributes, VirtualMask); + if (Length == 0) { + return EFI_SUCCESS; } - // Flush d-cache so descriptors make it back to uncached memory for subsequent table walks - // flush and invalidate pages - //TODO: Do we really need to invalidate the caches everytime we change the memory attributes ? - ArmCleanInvalidateDataCache (); + 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, + "SetMemoryAttributes(): MMU section 0x%lx length 0x%lx to %lx\n", + BaseAddress, ChunkLength, Attributes)); - ArmInvalidateInstructionCache (); + 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; + } - // Invalidate all TLB entries so changes are synced - ArmInvalidateTlb (); + DEBUG ((DEBUG_PAGE, + "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n", + BaseAddress, ChunkLength, Attributes)); + Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes, + VirtualMask, &FlushTlbs); + } + + if (EFI_ERROR (Status)) { + break; + } + + BaseAddress += ChunkLength; + Length -= ChunkLength; + } + + if (FlushTlbs) { + ArmInvalidateTlb (); + } return Status; } @@ -725,20 +775,14 @@ EfiAttributeToArmAttribute ( ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1 break; - case EFI_MEMORY_WP: - case EFI_MEMORY_XP: - case EFI_MEMORY_RP: case EFI_MEMORY_UCE: default: - // Cannot be implemented UEFI definition unclear for ARM - // Cause a page fault if these ranges are accessed. ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT; - DEBUG ((EFI_D_PAGE, "SetMemoryAttributes(): Unsupported attribute %x will page fault on access\n", EfiAttributes)); break; } // Determine protection attributes - if (EfiAttributes & EFI_MEMORY_WP) { + if (EfiAttributes & EFI_MEMORY_RO) { ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO; } else { ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW;