From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
To: Leif Lindholm <leif.lindholm@linaro.org>
Cc: "edk2-devel@lists.01.org" <edk2-devel@lists.01.org>,
Laszlo Ersek <lersek@redhat.com>
Subject: Re: [PATCH 2/5] ArmPkg: move ARM version of SetMemoryAttributes to ArmMmuLib
Date: Mon, 6 Mar 2017 17:05:58 +0100 [thread overview]
Message-ID: <CAKv+Gu9Kcm6iT7Dvf16bZmsATaTdV8pSfuPptO-ZAGK=DgmCFg@mail.gmail.com> (raw)
In-Reply-To: <20170306160325.GX16034@bivouac.eciton.net>
On 6 March 2017 at 17:03, Leif Lindholm <leif.lindholm@linaro.org> wrote:
> n Wed, Mar 01, 2017 at 04:31:40PM +0000, Ard Biesheuvel wrote:
>> ... 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;
>> +
>
> Copied from ArmPkg/Drivers/CpuDxe/Arm/Mmu.c, but not deleted there.
> Can it be, or can it be moved out into a header somewhere?
>
> No other comments.
>
It is used in both places, so I'd need to put in in a header file
ArmPkg/Include/Chipset/ArmV7Mmu.h comes to mind ...
>> 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-06 16:06 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 ` [PATCH 2/5] ArmPkg: move ARM version of SetMemoryAttributes to ArmMmuLib Ard Biesheuvel
2017-03-06 16:03 ` Leif Lindholm
2017-03-06 16:05 ` Ard Biesheuvel [this message]
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='CAKv+Gu9Kcm6iT7Dvf16bZmsATaTdV8pSfuPptO-ZAGK=DgmCFg@mail.gmail.com' \
--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