From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-ot1-f41.google.com (mail-ot1-f41.google.com [209.85.210.41]) by mx.groups.io with SMTP id smtpd.web09.15594.1583345581458169441 for ; Wed, 04 Mar 2020 10:13:01 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@linaro.org header.s=google header.b=wOlhY+ew; spf=pass (domain: linaro.org, ip: 209.85.210.41, mailfrom: ard.biesheuvel@linaro.org) Received: by mail-ot1-f41.google.com with SMTP id x97so2963136ota.6 for ; Wed, 04 Mar 2020 10:13:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=hSZzgXWwEylIjV618MhB87lJkou9AFy9cmv8mczXeYc=; b=wOlhY+ewbF072B/dt5bHJnCuqyKBOFpRo/81fHZEzkpXYMb187wgu9cvYzd058CAMu LbckKHNygk4ohXtIpO7cg9fCdrdizaFvluUHhrr8NDAOtv548Bz7ycHU0AvnzJv7aB+f ISmx/KtWlD46cmGOeZl+lvVpEHwp1yGGEU902/XdrgOGO4DsdoqmWOqp02arv1HYDa4O i3pNsJFjQsWPgPG6qFF77blQ9rvoQq+8QTA31KmzY3D4duSWvsaaX3DNkMjyxbK4wsp5 55XDcLvPjMm1bRg0u1T49nflIMbBKT3lFQN6hZzx40/HzRfsZjy+w0a2XZB1YPh1I1gK 4gIw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=hSZzgXWwEylIjV618MhB87lJkou9AFy9cmv8mczXeYc=; b=ay/J36hiBKDgwoFXI5gvbkeHYLfEqMfBmUxiCgTbu9rIDOecO8NTNjQwV13OWngl16 uD2yX6TUXTFivhgkrCKreEEqTENc7te5uxiaviCiqNJdCNAJiPU80WN8R8S7PRO/Fo7C hzFqHht1bAdazczD6tNh2TDWZLDrEknohzmHjzV92bnLvx98GKfPLqG+E07wGPe+TVIe DJmd6HTKsD25FBhz/TQhTexUAJw9KKfjzdldkBA7nEsJoKVN+CV2TFLDQHw5sGGV3sK6 eqilIgPln9lCWZEMXa1YJObr+ReimmrvPXThBeog6K/eph9IbmW13oxDjjNzsrNnb+Dl +l7w== X-Gm-Message-State: ANhLgQ2EdJWsPUkmeM6L8+4Ckiu+Nrl9hmakYwOWrLlP+/jZRrz6Adyl 5PHffS1Kwt3iYXqfUOzIKLpb5Ip58bfLVA== X-Google-Smtp-Source: ADFU+vtP/TWe1tDRkhOOyLA4WSGkCEEnM5A0IPwstG/lGRcYNEJoqbULNxD1Feoo4v4HSByFOaRDdA== X-Received: by 2002:a9d:6c9:: with SMTP id 67mr3503801otx.363.1583345579547; Wed, 04 Mar 2020 10:12:59 -0800 (PST) Return-Path: Received: from cam-smtp0.cambridge.arm.com ([2a01:cb1d:112:6f00:816e:ff0d:fb69:f613]) by smtp.gmail.com with ESMTPSA id p65sm9083971oif.47.2020.03.04.10.12.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 04 Mar 2020 10:12:58 -0800 (PST) From: "Ard Biesheuvel" To: devel@edk2.groups.io Cc: leif@nuviainc.com, Ard Biesheuvel Subject: [PATCH v2 3/9] ArmPkg/ArmMmuLib ARM: split ArmMmuLibCore.c into core and update code Date: Wed, 4 Mar 2020 19:12:40 +0100 Message-Id: <20200304181246.23513-4-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200304181246.23513-1-ard.biesheuvel@linaro.org> References: <20200304181246.23513-1-ard.biesheuvel@linaro.org> Unlike the AArch64 implementation of ArmMmuLib, which combines the initial page table population code with the code that runs at later stages to manage permission attributes in the page tables, ARM uses two completely separate sets of routines for this. Since ArmMmuLib is a static library, we can prevent duplication of this code between different users, which usually only need one or the other. (Note that LTO should also achieve the same.) This also makes it easier to reason about modifying the cache maintenance handling, and replace the set/way ops with by-VA ops, since the code that performs the set/way ops only executes when the MMU is still off. Signed-off-by: Ard Biesheuvel --- ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c | 32 ++ ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c | 434 ------------------- ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c | 435 ++++++++++++++++++++ ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf | 2 + 4 files changed, 469 insertions(+), 434 deletions(-) diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c new file mode 100644 index 000000000000..e3b02a9fba57 --- /dev/null +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c @@ -0,0 +1,32 @@ +/** @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 + +#include + +#include + +UINT32 +ConvertSectionAttributesToPageAttributes ( + IN UINT32 SectionAttributes, + IN BOOLEAN IsLargePage + ) +{ + UINT32 PageAttributes; + + PageAttributes = 0; + PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes, IsLargePage); + PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes); + PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes, IsLargePage); + PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes); + PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes); + + return PageAttributes; +} diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c index a6601258bee0..aca7a37facac 100644 --- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c @@ -31,15 +31,6 @@ #define ID_MMFR0_SHR_IMP_HW_COHERENT 1 #define ID_MMFR0_SHR_IGNORED 0xf -#define __EFI_MEMORY_RWX 0 // no restrictions - -#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \ - EFI_MEMORY_WC | \ - EFI_MEMORY_WT | \ - EFI_MEMORY_WB | \ - EFI_MEMORY_UCE | \ - EFI_MEMORY_WP) - UINTN EFIAPI ArmReadIdMmfr0 ( @@ -52,24 +43,6 @@ ArmHasMpExtensions ( VOID ); -UINT32 -ConvertSectionAttributesToPageAttributes ( - IN UINT32 SectionAttributes, - IN BOOLEAN IsLargePage - ) -{ - UINT32 PageAttributes; - - PageAttributes = 0; - PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes, IsLargePage); - PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes); - PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes, IsLargePage); - PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes); - PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes); - - return PageAttributes; -} - STATIC BOOLEAN PreferNonshareableMemory ( @@ -423,410 +396,3 @@ ArmConfigureMmu ( ArmEnableMmu(); 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; - } - - // 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, - OUT BOOLEAN *FlushTlbs OPTIONAL - ) -{ - 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) { - 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 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; - } - - if (Attributes & EFI_MEMORY_RO) { - 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]; - if (FlushTlbs != NULL) { - *FlushTlbs = TRUE; - } - } - - // 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 (CurrentPageTableEntry != PageTableEntry) { - Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT)); - - // 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 - ) -{ - 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 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; - } - - if (Attributes & EFI_MEMORY_XP) { - 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 (UINT64)MAX_ADDRESS) { - return EFI_UNSUPPORTED; - } - - Length = MIN (Length, (UINT64)MAX_ADDRESS - BaseAddress + 1); - if (Length == 0) { - return EFI_SUCCESS; - } - - 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)); - - Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes); - - 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, - "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n", - BaseAddress, ChunkLength, Attributes)); - - Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes, - &FlushTlbs); - } - - if (EFI_ERROR (Status)) { - break; - } - - BaseAddress += ChunkLength; - Length -= ChunkLength; - } - - if (FlushTlbs) { - ArmInvalidateTlb (); - } - return Status; -} - -EFI_STATUS -ArmSetMemoryRegionNoExec ( - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length - ) -{ - return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP); -} - -EFI_STATUS -ArmClearMemoryRegionNoExec ( - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length - ) -{ - return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX); -} - -EFI_STATUS -ArmSetMemoryRegionReadOnly ( - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length - ) -{ - return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO); -} - -EFI_STATUS -ArmClearMemoryRegionReadOnly ( - IN EFI_PHYSICAL_ADDRESS BaseAddress, - IN UINT64 Length - ) -{ - return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX); -} diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c new file mode 100644 index 000000000000..3dafe1d964cd --- /dev/null +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c @@ -0,0 +1,435 @@ +/** @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 + +#include +#include +#include +#include +#include +#include + +#include + +#define __EFI_MEMORY_RWX 0 // no restrictions + +#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | \ + EFI_MEMORY_WC | \ + EFI_MEMORY_WT | \ + EFI_MEMORY_WB | \ + EFI_MEMORY_UCE | \ + EFI_MEMORY_WP) + +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; + } + + // 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, + OUT BOOLEAN *FlushTlbs OPTIONAL + ) +{ + 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) { + 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 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; + } + + if (Attributes & EFI_MEMORY_RO) { + 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]; + if (FlushTlbs != NULL) { + *FlushTlbs = TRUE; + } + } + + // 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 (CurrentPageTableEntry != PageTableEntry) { + Mva = (VOID *)(UINTN)((((UINTN)FirstLevelIdx) << TT_DESCRIPTOR_SECTION_BASE_SHIFT) + (PageTableIndex << TT_DESCRIPTOR_PAGE_BASE_SHIFT)); + + // 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 + ) +{ + 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 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; + } + + if (Attributes & EFI_MEMORY_XP) { + 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 (UINT64)MAX_ADDRESS) { + return EFI_UNSUPPORTED; + } + + Length = MIN (Length, (UINT64)MAX_ADDRESS - BaseAddress + 1); + if (Length == 0) { + return EFI_SUCCESS; + } + + 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)); + + Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes); + + 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, + "SetMemoryAttributes(): MMU page 0x%lx length 0x%lx to %lx\n", + BaseAddress, ChunkLength, Attributes)); + + Status = UpdatePageEntries (BaseAddress, ChunkLength, Attributes, + &FlushTlbs); + } + + if (EFI_ERROR (Status)) { + break; + } + + BaseAddress += ChunkLength; + Length -= ChunkLength; + } + + if (FlushTlbs) { + ArmInvalidateTlb (); + } + return Status; +} + +EFI_STATUS +ArmSetMemoryRegionNoExec ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP); +} + +EFI_STATUS +ArmClearMemoryRegionNoExec ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX); +} + +EFI_STATUS +ArmSetMemoryRegionReadOnly ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO); +} + +EFI_STATUS +ArmClearMemoryRegionReadOnly ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ) +{ + return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX); +} diff --git a/ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf b/ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf index 3dfe68ba48a6..2a7e7147958c 100644 --- a/ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf +++ b/ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf @@ -23,7 +23,9 @@ [Sources.AARCH64] AArch64/ArmMmuLibReplaceEntry.S [Sources.ARM] + Arm/ArmMmuLibConvert.c Arm/ArmMmuLibCore.c + Arm/ArmMmuLibUpdate.c Arm/ArmMmuLibV7Support.S |GCC Arm/ArmMmuLibV7Support.asm |RVCT -- 2.17.1