public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v5 00/38] Implement strict memory permissions throughout
@ 2023-03-13 17:16 Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 01/38] ArmPkg/ArmMmuLib ARM: Remove half baked large page support Ard Biesheuvel
                   ` (39 more replies)
  0 siblings, 40 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Link: https://bugzilla.tianocore.org/show_bug.cgi?id=4369

This v5 now covers a lot more ground, and has ballooned quite
substantially as a result. The series is essentially a proof of concept
of a way to implement rigorous W^X memory protections from SEC all the
way to booting the OS.

In particular:
- the AArch64 WXN control is enabled so that NX is implied for all
  writable memory regions, which is rather helpful when testing changes
  such as these;
- avoid PEIM shadowing where possible, as that would involve managing
  the executable permissions of the shadowed code
- remap the DXE core code section read-only explicitly from IPL
- equip the DXE core with a way to manage memory permissions before the
  CPU arch protocol driver is dispatched;
- permit the NX memory protection policy to apply to code memory type
  regions as well
- check the NX compat DLL flag and section alignment to decide whether
  an image can be loaded when the NX policy is applied to such a code
  region 
- implement the EFI memory attributes protocol (for ARM and AArch64
  only) so that such NX compat compliant images have a way to create
  executable mappings 

v4:
- major cleanup of the 32-bit ARM code
- add support for EFI_MEMORY_RP using the access flag
- enable stack guard in ArmVirtPkg (which uses EFI_MEMORY_RP)
- incorporate optimization from other series [0] to avoid splitting
  block entries unnecessarily

v3:
- fix ARM32 bug in attribute conversion
- add Liming's ack to patch #1
- include draft patch (NOT FOR MERGE) used to test the changes

v2:
- drop patch to bump exposed UEFI revision to v2.10
- add missing permitted return values to protocol definition

[0] https://edk2.groups.io/g/devel/message/99801

Cc: Michael Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Michael Kubacki <michael.kubacki@microsoft.com>
Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Rebecca Cran <quic_rcran@quicinc.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Taylor Beebe <t@taylorbeebe.com>

Ard Biesheuvel (38):
  ArmPkg/ArmMmuLib ARM: Remove half baked large page support
  ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field
  ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion
  ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask
  ArmPkg/ArmMmuLib ARM: Clear individual permission bits
  ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag
  ArmVirtPkg: Enable stack guard
  ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
  ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion
  MdePkg: Add Memory Attribute Protocol definition
  ArmPkg/CpuDxe: Implement EFI memory attributes protocol
  ArmPkg/CpuDxe: Perform preliminary NX remap of free memory
  MdeModulePkg/DxeCore: Unconditionally set memory protections
  ArmPkg/Mmu: Remove handling of NONSECURE memory regions
  ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory
  MdePkg/BasePeCoffLib: Add API to keep track of relocation range
  MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default
  MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch
  MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time
  MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback
  ArmPkg: Implement ArmSetMemoryOverrideLib
  MdeModulePkg/PcdPeim: Permit unshadowed execution
  EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
  ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default
  ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs
  ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code
    flash
  BaseTools/GccBase AARCH64: Avoid page sharing between code and data
  ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory
    permissions
  MdePkg/PeCoffLib: Capture DLL characteristics field in image context
  MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics
  MdeModulePkg/DxeCore: Remove redundant DEBUG statements
  MdeModulePkg/DxeCore: Update memory protections before freeing a
    region
  MdeModulePkg/DxeCore: Disregard runtime alignment for image protection
  MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage()
  MdeModulePkg/DxeCore: Clear NX permissions on non-protected images
  MdeModulePkg/DxeCore: Permit NX protection for code regions
  MdeModulePkg/DxeCore: Check NX compat when using restricted code
    regions
  MdeModulePkg DEC: Remove inaccurate comment

 ArmPkg/ArmPkg.dec                                                  |   5 +
 ArmPkg/ArmPkg.dsc                                                  |   1 +
 ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c                                |  25 +-
 ArmPkg/Drivers/CpuDxe/Arm/Mmu.c                                    |  96 +++++--
 ArmPkg/Drivers/CpuDxe/CpuDxe.c                                     |  87 ++++++
 ArmPkg/Drivers/CpuDxe/CpuDxe.h                                     |  17 ++
 ArmPkg/Drivers/CpuDxe/CpuDxe.inf                                   |   5 +
 ArmPkg/Drivers/CpuDxe/MemoryAttribute.c                            | 271 ++++++++++++++++++
 ArmPkg/Include/Chipset/ArmV7Mmu.h                                  | 131 ++++-----
 ArmPkg/Include/Library/ArmLib.h                                    |  17 +-
 ArmPkg/Include/Library/ArmMmuLib.h                                 |  34 +++
 ArmPkg/Library/ArmLib/Arm/ArmV7Support.S                           |   2 +
 ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c                   | 103 ++++++-
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c                    |   8 +-
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c                       |  51 ++--
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c                     | 173 ++++++++++--
 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c   |  78 ++++++
 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf |  28 ++
 ArmVirtPkg/ArmVirt.dsc.inc                                         |   3 +
 ArmVirtPkg/ArmVirtQemu.dsc                                         |  11 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc                                   |   1 +
 ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S  |   2 +-
 ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c         |   4 +-
 BaseTools/Scripts/GccBase.lds                                      |  13 +-
 EmbeddedPkg/Include/Library/PrePiLib.h                             |  16 --
 EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c                    |  51 ++++
 EmbeddedPkg/Library/PrePiLib/PrePi.h                               |  13 +
 EmbeddedPkg/Library/PrePiLib/PrePiLib.c                            |   4 +
 EmbeddedPkg/Library/PrePiLib/PrePiLib.inf                          |  12 +
 EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c                    |  23 ++
 MdeModulePkg/Core/Dxe/DxeMain.h                                    |   6 +-
 MdeModulePkg/Core/Dxe/Image/Image.c                                |   8 +-
 MdeModulePkg/Core/Dxe/Mem/Page.c                                   |  15 +-
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c                      | 288 +++++++++++---------
 MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c                     |  73 +++++
 MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf                            |   6 +-
 MdeModulePkg/Core/DxeIplPeim/DxeLoad.c                             |  24 +-
 MdeModulePkg/MdeModulePkg.dec                                      |   7 +-
 MdeModulePkg/Universal/PCD/Pei/Pcd.c                               | 112 ++++----
 MdeModulePkg/Universal/PCD/Pei/Pcd.inf                             |   1 +
 MdePkg/Include/IndustryStandard/PeImage.h                          |  15 +
 MdePkg/Include/Library/PeCoffLib.h                                 |  27 ++
 MdePkg/Include/Protocol/MemoryAttribute.h                          | 142 ++++++++++
 MdePkg/Library/BasePeCoffLib/BasePeCoff.c                          | 105 ++++++-
 MdePkg/MdePkg.dec                                                  |   3 +
 45 files changed, 1682 insertions(+), 435 deletions(-)
 create mode 100644 ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
 create mode 100644 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
 create mode 100644 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
 create mode 100644 EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
 create mode 100644 EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
 create mode 100644 MdePkg/Include/Protocol/MemoryAttribute.h

-- 
2.39.2


^ permalink raw reply	[flat|nested] 63+ messages in thread

* [PATCH v5 01/38] ArmPkg/ArmMmuLib ARM: Remove half baked large page support
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 02/38] ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field Ard Biesheuvel
                   ` (38 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Large page support on 32-bit ARM is essentially a glorified contiguous
bit where 16 consecutive entries describing a contiguous range with the
same attributes are presented in a way that permits the TLB to cache its
translation with a single entry.

This was never wired up completely, and does not add a lot of value in
EFI, where the page granularity is 4k and we expect to be able to set RO
and XP permissions on individual pages.

Given that large page support complicates the handling of the XN bit at
the page level (which is in a different place depending on whether the
page is small or large), let's just rip it out.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Drivers/CpuDxe/Arm/Mmu.c                 |  8 ++---
 ArmPkg/Include/Chipset/ArmV7Mmu.h               | 38 ++++++--------------
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c |  7 ++--
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c    |  2 +-
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c  |  2 +-
 5 files changed, 19 insertions(+), 38 deletions(-)

diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
index 2daf47ba6fe5..ea856f5cdd26 100644
--- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
@@ -165,7 +165,7 @@ SyncCacheConfigPage (
 
   // Convert SectionAttributes into PageAttributes
   NextPageAttributes =
-    TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (*NextSectionAttributes, 0) |
+    TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (*NextSectionAttributes) |
     TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (*NextSectionAttributes);
 
   // obtain page table base
@@ -212,7 +212,7 @@ SyncCacheConfigPage (
 
   // Convert back PageAttributes into SectionAttributes
   *NextSectionAttributes =
-    TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (NextPageAttributes, 0) |
+    TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (NextPageAttributes) |
     TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (NextPageAttributes);
 
   return EFI_SUCCESS;
@@ -399,7 +399,7 @@ GetMemoryRegionPage (
   UINT32  PageDescriptor;
 
   // Convert the section attributes into page attributes
-  PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0);
+  PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes);
 
   // Calculate index into first level translation table for start of modification
   TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK)  >> TT_DESCRIPTOR_PAGE_BASE_SHIFT;
@@ -479,7 +479,7 @@ GetMemoryRegion (
     ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT);
 
     PageAttributes    = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;
-    *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) |
+    *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes) |
                         TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
   }
 
diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
index db99527d6efa..7501ebfdf97f 100644
--- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
+++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
@@ -98,9 +98,8 @@
 #define TT_DESCRIPTOR_PAGE_AP_RO_NO  ((1UL << 9) | (1UL << 4))
 #define TT_DESCRIPTOR_PAGE_AP_RO_RO  ((1UL << 9) | (3UL << 4))
 
-#define TT_DESCRIPTOR_SECTION_XN_MASK    (0x1UL << 4)
-#define TT_DESCRIPTOR_PAGE_XN_MASK       (0x1UL << 0)
-#define TT_DESCRIPTOR_LARGEPAGE_XN_MASK  (0x1UL << 15)
+#define TT_DESCRIPTOR_SECTION_XN_MASK  (0x1UL << 4)
+#define TT_DESCRIPTOR_PAGE_XN_MASK     (0x1UL << 0)
 
 #define TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK                    ((3UL << 12) | (1UL << 3) | (1UL << 2))
 #define TT_DESCRIPTOR_SECTION_CACHEABLE_MASK                       (1UL << 3)
@@ -124,30 +123,14 @@
 #define TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC        ((1UL << 6) | (1UL << 3) | (1UL << 2))
 #define TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE    ((2UL << 6) | (0UL << 3) | (0UL << 2))
 
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_MASK                    ((3UL << 12) | (1UL << 3) | (1UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_STRONGLY_ORDERED        ((0UL << 12) | (0UL << 3) | (0UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_SHAREABLE_DEVICE        ((0UL << 12) | (0UL << 3) | (1UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC  ((0UL << 12) | (1UL << 3) | (0UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC     ((0UL << 12) | (1UL << 3) | (1UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_NON_CACHEABLE           ((1UL << 12) | (0UL << 3) | (0UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_WRITE_BACK_ALLOC        ((1UL << 12) | (1UL << 3) | (1UL << 2))
-#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE    ((2UL << 12) | (0UL << 3) | (0UL << 2))
+#define TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_AP_MASK) >> 6) & TT_DESCRIPTOR_PAGE_AP_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_NG_MASK) >> 6) & TT_DESCRIPTOR_PAGE_NG_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_PAGE_S(Desc)             ((((Desc) & TT_DESCRIPTOR_SECTION_S_MASK) >> 6) & TT_DESCRIPTOR_PAGE_S_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) >> 4) & TT_DESCRIPTOR_PAGE_XN_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 12)) >> 6) | (Desc & (0x3 << 2)))
 
-#define TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(Desc)                         ((((Desc) & TT_DESCRIPTOR_SECTION_AP_MASK) >> 6) & TT_DESCRIPTOR_PAGE_AP_MASK)
-#define TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(Desc)                         ((((Desc) & TT_DESCRIPTOR_SECTION_NG_MASK) >> 6) & TT_DESCRIPTOR_PAGE_NG_MASK)
-#define TT_DESCRIPTOR_CONVERT_TO_PAGE_S(Desc)                          ((((Desc) & TT_DESCRIPTOR_SECTION_S_MASK) >> 6) & TT_DESCRIPTOR_PAGE_S_MASK)
-#define TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(Desc, IsLargePage)            ((IsLargePage)?\
-                                                                    ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) << 11) & TT_DESCRIPTOR_LARGEPAGE_XN_MASK):    \
-                                                                    ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) >> 4) & TT_DESCRIPTOR_PAGE_XN_MASK))
-#define TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(Desc, IsLargePage)  (IsLargePage?    \
-                                                                    (((Desc) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) & TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_MASK): \
-                                                                    (((((Desc) & (0x3 << 12)) >> 6) | (Desc & (0x3 << 2)))))
-
-#define TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(Desc)  ((((Desc) & TT_DESCRIPTOR_PAGE_AP_MASK) << 6) & TT_DESCRIPTOR_SECTION_AP_MASK)
-
-#define TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(Desc, IsLargePage)  (IsLargePage?    \
-                                                                    (((Desc) & TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_MASK) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK): \
-                                                                    (((((Desc) & (0x3 << 6)) << 6) | (Desc & (0x3 << 2)))))
+#define TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_AP_MASK) << 6) & TT_DESCRIPTOR_SECTION_AP_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 6)) << 6) | (Desc & (0x3 << 2)))
 
 #define TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK  (TT_DESCRIPTOR_SECTION_NS_MASK | TT_DESCRIPTOR_SECTION_NG_MASK |               \
                                                              TT_DESCRIPTOR_SECTION_S_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | \
@@ -230,8 +213,7 @@ typedef UINT32 ARM_PAGE_TABLE_ENTRY;
 
 UINT32
 ConvertSectionAttributesToPageAttributes (
-  IN UINT32   SectionAttributes,
-  IN BOOLEAN  IsLargePage
+  IN UINT32  SectionAttributes
   );
 
 #endif // ARMV7_MMU_H_
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
index bee8ad7028d3..6e2f08a7ce15 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
@@ -15,16 +15,15 @@
 
 UINT32
 ConvertSectionAttributesToPageAttributes (
-  IN UINT32   SectionAttributes,
-  IN BOOLEAN  IsLargePage
+  IN UINT32  SectionAttributes
   )
 {
   UINT32  PageAttributes;
 
   PageAttributes  = 0;
-  PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes, IsLargePage);
+  PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes);
-  PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes, IsLargePage);
+  PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes);
 
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
index 9e304ea05e63..28cc9b2fe058 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
@@ -145,7 +145,7 @@ PopulateLevel2PageTable (
                                   );
 
       // Translate the Section Descriptor into Page Descriptor
-      SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry, FALSE);
+      SectionDescriptor = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (*SectionEntry);
 
       BaseSectionAddress = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*SectionEntry);
 
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
index b402197ade99..9ca00c976d5f 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
@@ -53,7 +53,7 @@ ConvertSectionToPages (
 
   // Get section attributes and convert to page attributes
   SectionDescriptor = FirstLevelTable[FirstLevelIdx];
-  PageDescriptor    = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor, FALSE);
+  PageDescriptor    = TT_DESCRIPTOR_PAGE_TYPE_PAGE | ConvertSectionAttributesToPageAttributes (SectionDescriptor);
 
   // 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);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 02/38] ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 01/38] ArmPkg/ArmMmuLib ARM: Remove half baked large page support Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-14 17:24   ` Leif Lindholm
  2023-03-13 17:16 ` [PATCH v5 03/38] ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion Ard Biesheuvel
                   ` (37 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

With large page support out of the picture, we can treat bits 1 and 0 of
the page descriptor as individual valid and XN bits, instead of treating
XN as a page type. Doing so aligns the handling of the attribute with
the section descriptor layout, as well as the XN handling on AArch64,
and this is beneficial for maintainability.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Include/Chipset/ArmV7Mmu.h              |  8 +++-----
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c | 12 ++++++------
 2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
index 7501ebfdf97f..6a2584ceb303 100644
--- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
+++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
@@ -54,11 +54,9 @@
 #define TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Desc)  (((Desc) & 3UL) == TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE)
 
 // Translation table descriptor types
-#define TT_DESCRIPTOR_PAGE_TYPE_MASK       (3UL << 0)
-#define TT_DESCRIPTOR_PAGE_TYPE_FAULT      (0UL << 0)
-#define TT_DESCRIPTOR_PAGE_TYPE_PAGE       (2UL << 0)
-#define TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN    (3UL << 0)
-#define TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE  (1UL << 0)
+#define TT_DESCRIPTOR_PAGE_TYPE_MASK   (1UL << 1)
+#define TT_DESCRIPTOR_PAGE_TYPE_FAULT  (0UL << 1)
+#define TT_DESCRIPTOR_PAGE_TYPE_PAGE   (1UL << 1)
 
 // Section descriptor definitions
 #define TT_DESCRIPTOR_SECTION_SIZE  (0x00100000)
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
index 9ca00c976d5f..12d0f4c30f8e 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
@@ -104,12 +104,8 @@ 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 | 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;
-  }
+  EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_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 the order of the conditionals below
@@ -148,6 +144,10 @@ UpdatePageEntries (
     EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
   }
 
+  if ((Attributes & EFI_MEMORY_XP) != 0) {
+    EntryValue |= TT_DESCRIPTOR_PAGE_XN_MASK;
+  }
+
   // Obtain page table base
   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 03/38] ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 01/38] ArmPkg/ArmMmuLib ARM: Remove half baked large page support Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 02/38] ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 04/38] ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask Ard Biesheuvel
                   ` (36 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

The section-to-page attribute conversion takes the shareability and
execute-never attributes into account, whereas the page-to-section
counterpart does not. The result is that GetMemoryRegionPage () -which
takes a section attribute argument (via *RegionAttributes) that is
ostensibly based on the first page in the range, but differs from the
actual page attributes when converted back- may return with a
RegionLength of zero. This is incorrect, and confuses code that scans a
region by calling GetMemoryRegion () in sequence.

So fix the conversion, and ASSERT () on a non-zero region length.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Drivers/CpuDxe/Arm/Mmu.c   | 3 +++
 ArmPkg/Include/Chipset/ArmV7Mmu.h | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
index ea856f5cdd26..8eb1f71395f5 100644
--- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
@@ -480,6 +480,8 @@ GetMemoryRegion (
 
     PageAttributes    = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK;
     *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes) |
+                        TT_DESCRIPTOR_CONVERT_TO_SECTION_S (PageAttributes) |
+                        TT_DESCRIPTOR_CONVERT_TO_SECTION_XN (PageAttributes) |
                         TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
   }
 
@@ -494,6 +496,7 @@ GetMemoryRegion (
 
       // Scan the page table to find the end of the region.
       Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes);
+      ASSERT (*RegionLength > 0);
 
       // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop
       if (Status == EFI_SUCCESS) {
diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
index 6a2584ceb303..e0219747df86 100644
--- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
+++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
@@ -128,6 +128,8 @@
 #define TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 12)) >> 6) | (Desc & (0x3 << 2)))
 
 #define TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_AP_MASK) << 6) & TT_DESCRIPTOR_SECTION_AP_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_SECTION_S(Desc)             ((((Desc) & TT_DESCRIPTOR_PAGE_S_MASK) << 6) & TT_DESCRIPTOR_SECTION_S_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_SECTION_XN(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_XN_MASK) << 4) & TT_DESCRIPTOR_SECTION_XN_MASK)
 #define TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 6)) << 6) | (Desc & (0x3 << 2)))
 
 #define TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK  (TT_DESCRIPTOR_SECTION_NS_MASK | TT_DESCRIPTOR_SECTION_NG_MASK |               \
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 04/38] ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (2 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 03/38] ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-14 17:55   ` [edk2-devel] " Leif Lindholm
  2023-03-13 17:16 ` [PATCH v5 05/38] ArmPkg/ArmMmuLib ARM: Clear individual permission bits Ard Biesheuvel
                   ` (35 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Split the ARM permission fields in the short descriptors into an access
flag and AP[2:1] as per the recommendation in the ARM ARM. This makes
the access flag available separately, which allows us to implement
EFI_MEMORY_RP memory analogous to how it will be implemented for
AArch64.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Drivers/CpuDxe/Arm/Mmu.c                 | 47 ++++++++++----------
 ArmPkg/Include/Chipset/ArmV7Mmu.h               | 40 +++++++++++------
 ArmPkg/Library/ArmLib/Arm/ArmV7Support.S        |  2 +
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c |  1 +
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c  | 12 ++++-
 5 files changed, 63 insertions(+), 39 deletions(-)

diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
index 8eb1f71395f5..07faab8216ec 100644
--- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
@@ -50,30 +50,27 @@ 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_RO | EFI_MEMORY_RP;
-      break;
-
-    case TT_DESCRIPTOR_SECTION_AP_RW_NO:
+    case TT_DESCRIPTOR_SECTION_AP_NO_RW:
     case TT_DESCRIPTOR_SECTION_AP_RW_RW:
       // normal read/write access, do not add additional attributes
       break;
 
     // read only cases map to write-protect
-    case TT_DESCRIPTOR_SECTION_AP_RO_NO:
+    case TT_DESCRIPTOR_SECTION_AP_NO_RO:
     case TT_DESCRIPTOR_SECTION_AP_RO_RO:
       *GcdAttributes |= EFI_MEMORY_RO;
       break;
-
-    default:
-      return EFI_UNSUPPORTED;
   }
 
   // now process eXectue Never attribute
-  if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) {
+  if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0) {
     *GcdAttributes |= EFI_MEMORY_XP;
   }
 
+  if ((SectionAttributes & TT_DESCRIPTOR_SECTION_AF) == 0) {
+    *GcdAttributes |= EFI_MEMORY_RP;
+  }
+
   return EFI_SUCCESS;
 }
 
@@ -114,30 +111,27 @@ 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_RO | EFI_MEMORY_RP;
-      break;
-
-    case TT_DESCRIPTOR_PAGE_AP_RW_NO:
+    case TT_DESCRIPTOR_PAGE_AP_NO_RW:
     case TT_DESCRIPTOR_PAGE_AP_RW_RW:
       // normal read/write access, do not add additional attributes
       break;
 
     // read only cases map to write-protect
-    case TT_DESCRIPTOR_PAGE_AP_RO_NO:
+    case TT_DESCRIPTOR_PAGE_AP_NO_RO:
     case TT_DESCRIPTOR_PAGE_AP_RO_RO:
       *GcdAttributes |= EFI_MEMORY_RO;
       break;
-
-    default:
-      return EFI_UNSUPPORTED;
   }
 
   // now process eXectue Never attribute
-  if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) {
+  if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0) {
     *GcdAttributes |= EFI_MEMORY_XP;
   }
 
+  if ((PageAttributes & TT_DESCRIPTOR_PAGE_AF) == 0) {
+    *GcdAttributes |= EFI_MEMORY_RP;
+  }
+
   return EFI_SUCCESS;
 }
 
@@ -166,6 +160,7 @@ SyncCacheConfigPage (
   // Convert SectionAttributes into PageAttributes
   NextPageAttributes =
     TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (*NextSectionAttributes) |
+    TT_DESCRIPTOR_CONVERT_TO_PAGE_AF (*NextSectionAttributes) |
     TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (*NextSectionAttributes);
 
   // obtain page table base
@@ -174,7 +169,7 @@ SyncCacheConfigPage (
   for (i = 0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {
     if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
       // extract attributes (cacheability and permissions)
-      PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);
+      PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_AF);
 
       if (NextPageAttributes == 0) {
         // start on a new region
@@ -213,6 +208,7 @@ SyncCacheConfigPage (
   // Convert back PageAttributes into SectionAttributes
   *NextSectionAttributes =
     TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (NextPageAttributes) |
+    TT_DESCRIPTOR_CONVERT_TO_SECTION_AF (NextPageAttributes) |
     TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (NextPageAttributes);
 
   return EFI_SUCCESS;
@@ -256,14 +252,14 @@ SyncCacheConfig (
   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());
 
   // Get the first region
-  NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
+  NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF);
 
   // iterate through each 1MB descriptor
   NextRegionBase = NextRegionLength = 0;
   for (i = 0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {
     if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
       // extract attributes (cacheability and permissions)
-      SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
+      SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF);
 
       if (NextSectionAttributes == 0) {
         // start on a new region
@@ -383,6 +379,10 @@ EfiAttributeToArmAttribute (
     ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
   }
 
+  if ((EfiAttributes & EFI_MEMORY_RP) == 0) {
+    ArmAttributes |= TT_DESCRIPTOR_SECTION_AF;
+  }
+
   return ArmAttributes;
 }
 
@@ -482,6 +482,7 @@ GetMemoryRegion (
     *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes) |
                         TT_DESCRIPTOR_CONVERT_TO_SECTION_S (PageAttributes) |
                         TT_DESCRIPTOR_CONVERT_TO_SECTION_XN (PageAttributes) |
+                        TT_DESCRIPTOR_CONVERT_TO_SECTION_AF (PageAttributes) |
                         TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
   }
 
diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
index e0219747df86..da4f3160f8ff 100644
--- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
+++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
@@ -80,21 +80,21 @@
 #define TT_DESCRIPTOR_PAGE_S_NOT_SHARED  (0UL << 10)
 #define TT_DESCRIPTOR_PAGE_S_SHARED      (1UL << 10)
 
-#define TT_DESCRIPTOR_SECTION_AP_MASK   ((1UL << 15) | (3UL << 10))
-#define TT_DESCRIPTOR_SECTION_AP_NO_NO  ((0UL << 15) | (0UL << 10))
-#define TT_DESCRIPTOR_SECTION_AP_RW_NO  ((0UL << 15) | (1UL << 10))
-#define TT_DESCRIPTOR_SECTION_AP_RW_RO  ((0UL << 15) | (2UL << 10))
-#define TT_DESCRIPTOR_SECTION_AP_RW_RW  ((0UL << 15) | (3UL << 10))
-#define TT_DESCRIPTOR_SECTION_AP_RO_NO  ((1UL << 15) | (1UL << 10))
-#define TT_DESCRIPTOR_SECTION_AP_RO_RO  ((1UL << 15) | (3UL << 10))
+#define TT_DESCRIPTOR_SECTION_AP_MASK   ((1UL << 15) | (1UL << 11))
+#define TT_DESCRIPTOR_SECTION_AP_NO_RW  ((0UL << 15) | (0UL << 11))
+#define TT_DESCRIPTOR_SECTION_AP_RW_RW  ((0UL << 15) | (1UL << 11))
+#define TT_DESCRIPTOR_SECTION_AP_NO_RO  ((1UL << 15) | (0UL << 11))
+#define TT_DESCRIPTOR_SECTION_AP_RO_RO  ((1UL << 15) | (1UL << 11))
 
-#define TT_DESCRIPTOR_PAGE_AP_MASK   ((1UL << 9) | (3UL << 4))
-#define TT_DESCRIPTOR_PAGE_AP_NO_NO  ((0UL << 9) | (0UL << 4))
-#define TT_DESCRIPTOR_PAGE_AP_RW_NO  ((0UL << 9) | (1UL << 4))
-#define TT_DESCRIPTOR_PAGE_AP_RW_RO  ((0UL << 9) | (2UL << 4))
-#define TT_DESCRIPTOR_PAGE_AP_RW_RW  ((0UL << 9) | (3UL << 4))
-#define TT_DESCRIPTOR_PAGE_AP_RO_NO  ((1UL << 9) | (1UL << 4))
-#define TT_DESCRIPTOR_PAGE_AP_RO_RO  ((1UL << 9) | (3UL << 4))
+#define TT_DESCRIPTOR_SECTION_AF  (1UL << 10)
+
+#define TT_DESCRIPTOR_PAGE_AP_MASK   ((1UL << 9) | (1UL << 5))
+#define TT_DESCRIPTOR_PAGE_AP_NO_RW  ((0UL << 9) | (0UL << 5))
+#define TT_DESCRIPTOR_PAGE_AP_RW_RW  ((0UL << 9) | (1UL << 5))
+#define TT_DESCRIPTOR_PAGE_AP_NO_RO  ((1UL << 9) | (0UL << 5))
+#define TT_DESCRIPTOR_PAGE_AP_RO_RO  ((1UL << 9) | (1UL << 5))
+
+#define TT_DESCRIPTOR_PAGE_AF  (1UL << 4)
 
 #define TT_DESCRIPTOR_SECTION_XN_MASK  (0x1UL << 4)
 #define TT_DESCRIPTOR_PAGE_XN_MASK     (0x1UL << 0)
@@ -124,20 +124,24 @@
 #define TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_AP_MASK) >> 6) & TT_DESCRIPTOR_PAGE_AP_MASK)
 #define TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_NG_MASK) >> 6) & TT_DESCRIPTOR_PAGE_NG_MASK)
 #define TT_DESCRIPTOR_CONVERT_TO_PAGE_S(Desc)             ((((Desc) & TT_DESCRIPTOR_SECTION_S_MASK) >> 6) & TT_DESCRIPTOR_PAGE_S_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_PAGE_AF(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_AF) >> 6) & TT_DESCRIPTOR_PAGE_AF)
 #define TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) >> 4) & TT_DESCRIPTOR_PAGE_XN_MASK)
 #define TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 12)) >> 6) | (Desc & (0x3 << 2)))
 
 #define TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_AP_MASK) << 6) & TT_DESCRIPTOR_SECTION_AP_MASK)
 #define TT_DESCRIPTOR_CONVERT_TO_SECTION_S(Desc)             ((((Desc) & TT_DESCRIPTOR_PAGE_S_MASK) << 6) & TT_DESCRIPTOR_SECTION_S_MASK)
+#define TT_DESCRIPTOR_CONVERT_TO_SECTION_AF(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_AF) << 6) & TT_DESCRIPTOR_SECTION_AF)
 #define TT_DESCRIPTOR_CONVERT_TO_SECTION_XN(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_XN_MASK) << 4) & TT_DESCRIPTOR_SECTION_XN_MASK)
 #define TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 6)) << 6) | (Desc & (0x3 << 2)))
 
 #define TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK  (TT_DESCRIPTOR_SECTION_NS_MASK | TT_DESCRIPTOR_SECTION_NG_MASK |               \
                                                              TT_DESCRIPTOR_SECTION_S_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | \
+                                                             TT_DESCRIPTOR_SECTION_AF | \
                                                              TT_DESCRIPTOR_SECTION_XN_MASK | TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK)
 
 #define TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK  (TT_DESCRIPTOR_PAGE_NG_MASK | TT_DESCRIPTOR_PAGE_S_MASK |                  \
                                                              TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK | \
+                                                             TT_DESCRIPTOR_PAGE_AF | \
                                                              TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK)
 
 #define TT_DESCRIPTOR_SECTION_DOMAIN_MASK  (0x0FUL << 5)
@@ -159,6 +163,7 @@
                                                             TT_DESCRIPTOR_SECTION_S_SHARED                          | \
                                                             TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
                                                             TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
+                                                            TT_DESCRIPTOR_SECTION_AF                                | \
                                                             TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC)
 #define TT_DESCRIPTOR_SECTION_WRITE_THROUGH(NonSecure)  (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
                                                             ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
@@ -166,6 +171,7 @@
                                                             TT_DESCRIPTOR_SECTION_S_SHARED                          | \
                                                             TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
                                                             TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
+                                                            TT_DESCRIPTOR_SECTION_AF                                | \
                                                             TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
 #define TT_DESCRIPTOR_SECTION_DEVICE(NonSecure)         (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
                                                             ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
@@ -174,6 +180,7 @@
                                                             TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
                                                             TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
                                                             TT_DESCRIPTOR_SECTION_XN_MASK                           | \
+                                                            TT_DESCRIPTOR_SECTION_AF                                | \
                                                             TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE)
 #define TT_DESCRIPTOR_SECTION_UNCACHED(NonSecure)       (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |    \
                                                            ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
@@ -181,28 +188,33 @@
                                                            TT_DESCRIPTOR_SECTION_S_NOT_SHARED                      | \
                                                            TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
                                                            TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
+                                                            TT_DESCRIPTOR_SECTION_AF                                | \
                                                            TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE)
 
 #define TT_DESCRIPTOR_PAGE_WRITE_BACK     (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
                                                         TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
                                                         TT_DESCRIPTOR_PAGE_S_SHARED                                                       | \
                                                         TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
+                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
                                                         TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC)
 #define TT_DESCRIPTOR_PAGE_WRITE_THROUGH  (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
                                                         TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
                                                         TT_DESCRIPTOR_PAGE_S_SHARED                                                       | \
                                                         TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
+                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
                                                         TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
 #define TT_DESCRIPTOR_PAGE_DEVICE         (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
                                                         TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
                                                         TT_DESCRIPTOR_PAGE_S_NOT_SHARED                                                   | \
                                                         TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
+                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
                                                         TT_DESCRIPTOR_PAGE_XN_MASK                                                        | \
                                                         TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE)
 #define TT_DESCRIPTOR_PAGE_UNCACHED       (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
                                                         TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
                                                         TT_DESCRIPTOR_PAGE_S_NOT_SHARED                                                   | \
                                                         TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
+                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
                                                         TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE)
 
 // First Level Descriptors
diff --git a/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S b/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S
index 4925f6628e1e..1f396adffc11 100644
--- a/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S
+++ b/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S
@@ -16,6 +16,7 @@
 .set CTRL_C_BIT,  (1 << 2)
 .set CTRL_B_BIT,  (1 << 7)
 .set CTRL_I_BIT,  (1 << 12)
+.set CTRL_AFE_BIT,(1 << 29)
 
 
 ASM_FUNC(ArmInvalidateDataCacheEntryByMVA)
@@ -64,6 +65,7 @@ ASM_FUNC(ArmInvalidateInstructionCache)
 ASM_FUNC(ArmEnableMmu)
   mrc     p15,0,R0,c1,c0,0
   orr     R0,R0,#1
+  orr     R0,R0,#CTRL_AFE_BIT
   mcr     p15,0,R0,c1,c0,0
   dsb
   isb
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
index 6e2f08a7ce15..52dbfd714029 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
@@ -23,6 +23,7 @@ ConvertSectionAttributesToPageAttributes (
   PageAttributes  = 0;
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes);
+  PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AF (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes);
   PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes);
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
index 12d0f4c30f8e..484c67476619 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
@@ -104,7 +104,7 @@ 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 | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK;
+  EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK | TT_DESCRIPTOR_PAGE_AF;
   EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
 
   // Although the PI spec is unclear on this, the GCD guarantees that only
@@ -138,6 +138,10 @@ UpdatePageEntries (
     return EFI_UNSUPPORTED;
   }
 
+  if ((Attributes & EFI_MEMORY_RP) == 0) {
+    EntryValue |= TT_DESCRIPTOR_PAGE_AF;
+  }
+
   if ((Attributes & EFI_MEMORY_RO) != 0) {
     EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
   } else {
@@ -237,7 +241,7 @@ UpdateSectionEntries (
 
   // 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;
+              TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF;
   EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
 
   // Although the PI spec is unclear on this, the GCD guarantees that only
@@ -281,6 +285,10 @@ UpdateSectionEntries (
     EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
   }
 
+  if ((Attributes & EFI_MEMORY_RP) == 0) {
+    EntryValue |= TT_DESCRIPTOR_SECTION_AF;
+  }
+
   // obtain page table base
   FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 05/38] ArmPkg/ArmMmuLib ARM: Clear individual permission bits
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (3 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 04/38] ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 06/38] ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag Ard Biesheuvel
                   ` (34 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Currently, the MMU code that is supposed to clear the RO or XP
attributes from a region just clears both unconditionally. This
approximates the desired behavior to some extent, but it does mean that
setting the RO bit first on a code region, and then clearing the XP bit
results both RO and XP being cleared, and we end up with writable code,
and avoiding that is the point of all these protections.

Once we introduce RP support, this will only get worse, so let's fix
this up, by reshuffling the attribute update code to take the entry mask
from the caller, and use the mask to preserve other attributes when
clearing RO or XP.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c | 94 +++++++++++++++++---
 1 file changed, 81 insertions(+), 13 deletions(-)

diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
index 484c67476619..23f613f5dbb0 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
@@ -81,12 +81,12 @@ UpdatePageEntries (
   IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
   IN  UINT64                Length,
   IN  UINT64                Attributes,
+  IN  UINT32                EntryMask,
   OUT BOOLEAN               *FlushTlbs OPTIONAL
   )
 {
   EFI_STATUS  Status;
   UINT32      EntryValue;
-  UINT32      EntryMask;
   UINT32      FirstLevelIdx;
   UINT32      Offset;
   UINT32      NumPageEntries;
@@ -104,7 +104,6 @@ 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 | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK | TT_DESCRIPTOR_PAGE_AF;
   EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
 
   // Although the PI spec is unclear on this, the GCD guarantees that only
@@ -220,11 +219,11 @@ EFI_STATUS
 UpdateSectionEntries (
   IN EFI_PHYSICAL_ADDRESS  BaseAddress,
   IN UINT64                Length,
-  IN UINT64                Attributes
+  IN UINT64                Attributes,
+  IN UINT32                EntryMask
   )
 {
   EFI_STATUS                           Status;
-  UINT32                               EntryMask;
   UINT32                               EntryValue;
   UINT32                               FirstLevelIdx;
   UINT32                               NumSections;
@@ -240,8 +239,6 @@ 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 | TT_DESCRIPTOR_SECTION_XN_MASK |
-              TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF;
   EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
 
   // Although the PI spec is unclear on this, the GCD guarantees that only
@@ -310,6 +307,7 @@ UpdateSectionEntries (
                  (FirstLevelIdx + i) << TT_DESCRIPTOR_SECTION_BASE_SHIFT,
                  TT_DESCRIPTOR_SECTION_SIZE,
                  Attributes,
+                 ConvertSectionAttributesToPageAttributes (EntryMask),
                  NULL
                  );
     } else {
@@ -340,11 +338,26 @@ UpdateSectionEntries (
   return Status;
 }
 
+/**
+  Update the permission or memory type attributes on a range of memory.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+  @param  Attributes            A mask of EFI_MEMORY_xx constants.
+  @param  SectionMask           A mask of short descriptor section attributes
+                                describing which descriptor bits to update.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+STATIC
 EFI_STATUS
-ArmSetMemoryAttributes (
+SetMemoryAttributes (
   IN EFI_PHYSICAL_ADDRESS  BaseAddress,
   IN UINT64                Length,
-  IN UINT64                Attributes
+  IN UINT64                Attributes,
+  IN UINT32                SectionMask
   )
 {
   EFI_STATUS  Status;
@@ -375,7 +388,12 @@ ArmSetMemoryAttributes (
         Attributes
         ));
 
-      Status = UpdateSectionEntries (BaseAddress, ChunkLength, Attributes);
+      Status = UpdateSectionEntries (
+                 BaseAddress,
+                 ChunkLength,
+                 Attributes,
+                 SectionMask
+                 );
 
       FlushTlbs = TRUE;
     } else {
@@ -401,6 +419,7 @@ ArmSetMemoryAttributes (
                  BaseAddress,
                  ChunkLength,
                  Attributes,
+                 ConvertSectionAttributesToPageAttributes (SectionMask),
                  &FlushTlbs
                  );
     }
@@ -420,13 +439,47 @@ ArmSetMemoryAttributes (
   return Status;
 }
 
+/**
+  Update the permission or memory type attributes on a range of memory.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+  @param  Attributes            A mask of EFI_MEMORY_xx constants.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+ArmSetMemoryAttributes (
+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN UINT64                Length,
+  IN UINT64                Attributes
+  )
+{
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           Attributes,
+           TT_DESCRIPTOR_SECTION_TYPE_MASK |
+           TT_DESCRIPTOR_SECTION_XN_MASK |
+           TT_DESCRIPTOR_SECTION_AP_MASK |
+           TT_DESCRIPTOR_SECTION_AF
+           );
+}
+
 EFI_STATUS
 ArmSetMemoryRegionNoExec (
   IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
   IN  UINT64                Length
   )
 {
-  return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           EFI_MEMORY_XP,
+           TT_DESCRIPTOR_SECTION_XN_MASK
+           );
 }
 
 EFI_STATUS
@@ -435,7 +488,12 @@ ArmClearMemoryRegionNoExec (
   IN  UINT64                Length
   )
 {
-  return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           0,
+           TT_DESCRIPTOR_SECTION_XN_MASK
+           );
 }
 
 EFI_STATUS
@@ -444,7 +502,12 @@ ArmSetMemoryRegionReadOnly (
   IN  UINT64                Length
   )
 {
-  return ArmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           EFI_MEMORY_RO,
+           TT_DESCRIPTOR_SECTION_AP_MASK
+           );
 }
 
 EFI_STATUS
@@ -453,5 +516,10 @@ ArmClearMemoryRegionReadOnly (
   IN  UINT64                Length
   )
 {
-  return ArmSetMemoryAttributes (BaseAddress, Length, __EFI_MEMORY_RWX);
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           0,
+           TT_DESCRIPTOR_SECTION_AP_MASK
+           );
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 06/38] ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (4 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 05/38] ArmPkg/ArmMmuLib ARM: Clear individual permission bits Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 07/38] ArmVirtPkg: Enable stack guard Ard Biesheuvel
                   ` (33 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Implement support for read-protected memory by wiring it up to the
access flag in the page table descriptor. The resulting mapping is
implicitly non-writable and non-executable as well, but this is good
enough for implementing this attribute, as we never rely on write or
execute permissions without read permissions.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c              |  8 ++-
 ArmPkg/Include/Library/ArmMmuLib.h               | 34 ++++++++++++
 ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c | 58 +++++++++++++++++++-
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c   | 48 ++++++++++++++++
 4 files changed, 144 insertions(+), 4 deletions(-)

diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
index 8bb33046e707..8bda11f08a30 100644
--- a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
@@ -64,6 +64,10 @@ PageAttributeToGcdAttribute (
   }
 
   // Determine protection attributes
+  if ((PageAttributes & TT_AF) == 0) {
+    GcdAttributes |= EFI_MEMORY_RP;
+  }
+
   if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) ||
       ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO))
   {
@@ -301,7 +305,9 @@ EfiAttributeToArmAttribute (
   }
 
   // Set the access flag to match the block attributes
-  ArmAttributes |= TT_AF;
+  if ((EfiAttributes & EFI_MEMORY_RP) == 0) {
+    ArmAttributes |= TT_AF;
+  }
 
   // Determine protection attributes
   if ((EfiAttributes & EFI_MEMORY_RO) != 0) {
diff --git a/ArmPkg/Include/Library/ArmMmuLib.h b/ArmPkg/Include/Library/ArmMmuLib.h
index b745e2230e7e..4cf59a1e376b 100644
--- a/ArmPkg/Include/Library/ArmMmuLib.h
+++ b/ArmPkg/Include/Library/ArmMmuLib.h
@@ -21,6 +21,40 @@ ArmConfigureMmu (
   OUT UINTN                         *TranslationTableSize  OPTIONAL
   );
 
+/**
+  Convert a region of memory to read-protected, by clearing the access flag.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+EFIAPI
+ArmSetMemoryRegionNoAccess (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINT64                Length
+  );
+
+/**
+  Convert a region of memory to read-enabled, by setting the access flag.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+EFIAPI
+ArmClearMemoryRegionNoAccess (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINT64                Length
+  );
+
 EFI_STATUS
 EFIAPI
 ArmSetMemoryRegionNoExec (
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index 764c7d362e2e..6d21a2e41dd1 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -438,7 +438,11 @@ GcdAttributeToPageAttribute (
     PageAttributes |= TT_AP_NO_RO;
   }
 
-  return PageAttributes | TT_AF;
+  if ((GcdAttributes & EFI_MEMORY_RP) == 0) {
+    PageAttributes |= TT_AF;
+  }
+
+  return PageAttributes;
 }
 
 EFI_STATUS
@@ -459,9 +463,9 @@ ArmSetMemoryAttributes (
     // No memory type was set in Attributes, so we are going to update the
     // permissions only.
     //
-    PageAttributes   &= TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK;
+    PageAttributes   &= TT_AP_MASK | TT_UXN_MASK | TT_PXN_MASK | TT_AF;
     PageAttributeMask = ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AP_MASK |
-                          TT_PXN_MASK | TT_XN_MASK);
+                          TT_PXN_MASK | TT_XN_MASK | TT_AF);
   }
 
   return UpdateRegionMapping (
@@ -534,6 +538,54 @@ ArmClearMemoryRegionNoExec (
            );
 }
 
+/**
+  Convert a region of memory to read-protected, by clearing the access flag.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+ArmSetMemoryRegionNoAccess (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINT64                Length
+  )
+{
+  return SetMemoryRegionAttribute (
+           BaseAddress,
+           Length,
+           0,
+           ~(TT_ADDRESS_MASK_BLOCK_ENTRY | TT_AF)
+           );
+}
+
+/**
+  Convert a region of memory to read-enabled, by setting the access flag.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+ArmClearMemoryRegionNoAccess (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINT64                Length
+  )
+{
+  return SetMemoryRegionAttribute (
+           BaseAddress,
+           Length,
+           TT_AF,
+           ~TT_ADDRESS_MASK_BLOCK_ENTRY
+           );
+}
+
 EFI_STATUS
 ArmSetMemoryRegionReadOnly (
   IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
index 23f613f5dbb0..247cf87bf3d3 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
@@ -523,3 +523,51 @@ ArmClearMemoryRegionReadOnly (
            TT_DESCRIPTOR_SECTION_AP_MASK
            );
 }
+
+/**
+  Convert a region of memory to read-protected, by clearing the access flag.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+ArmSetMemoryRegionNoAccess (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINT64                Length
+  )
+{
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           EFI_MEMORY_RP,
+           TT_DESCRIPTOR_SECTION_AF
+           );
+}
+
+/**
+  Convert a region of memory to read-enabled, by setting the access flag.
+
+  @param  BaseAddress           The start of the region.
+  @param  Length                The size of the region.
+
+  @retval EFI_SUCCESS           The attributes were set successfully.
+  @retval EFI_OUT_OF_RESOURCES  The operation failed due to insufficient memory.
+
+**/
+EFI_STATUS
+ArmClearMemoryRegionNoAccess (
+  IN  EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN  UINT64                Length
+  )
+{
+  return SetMemoryAttributes (
+           BaseAddress,
+           Length,
+           0,
+           TT_DESCRIPTOR_SECTION_AF
+           );
+}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 07/38] ArmVirtPkg: Enable stack guard
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (5 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 06/38] ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible Ard Biesheuvel
                   ` (32 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Enable the stack guard in ArmVirtPkg builds, so that stack overflows are
caught as they occur, rather than when they happen to hit a read-only
memory region.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmVirtPkg/ArmVirt.dsc.inc | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index 74d98e6314c4..5b18184be263 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -363,6 +363,8 @@ [PcdsFixedAtBuild.common]
   #
   gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy|0xC000000000007FD5
 
+  gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard|TRUE
+
 [Components.common]
   #
   # Ramdisk support
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (6 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 07/38] ArmVirtPkg: Enable stack guard Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-14 18:13   ` [edk2-devel] " Leif Lindholm
  2023-03-13 17:16 ` [PATCH v5 09/38] ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion Ard Biesheuvel
                   ` (31 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Currently, the ARM MMU page table logic will break down any block entry
that overlaps with the region being mapped, even if the block entry in
question is using the same attributes as the new region.

This means that creating a non-executable mapping inside a region that
is already mapped non-executable at a coarser granularity may trigger a
call to AllocatePages (), which may recurse back into the page table
code to update the attributes on the newly allocated page tables.

Let's avoid this, by preserving the block entry if it already covers the
region being mapped with the correct attributes.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c | 10 ++++++++++
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c   | 11 +++++++++++
 2 files changed, 21 insertions(+)

diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index 6d21a2e41dd1..1ce200c43c72 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -251,6 +251,16 @@ UpdateRegionMappingRecursive (
       ASSERT (Level < 3);
 
       if (!IsTableEntry (*Entry, Level)) {
+        //
+        // If the region we are trying to map is already covered by a block
+        // entry with the right attributes, don't bother splitting it up.
+        //
+        if (IsBlockEntry (*Entry, Level) &&
+            ((*Entry & TT_ATTRIBUTES_MASK & ~AttributeClearMask) == AttributeSetMask))
+        {
+          continue;
+        }
+
         //
         // No table entry exists yet, so we need to allocate a page table
         // for the next level.
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
index 247cf87bf3d3..299d38ad07e8 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
@@ -170,6 +170,17 @@ UpdatePageEntries (
 
     // Does this descriptor need to be converted from section entry to 4K pages?
     if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (Descriptor)) {
+      //
+      // If the section mapping covers the requested region with the expected
+      // attributes, splitting it is unnecessary, and should be avoided as it
+      // may result in unbounded recursion when using a strict NX policy.
+      //
+      if ((EntryValue & ~TT_DESCRIPTOR_PAGE_TYPE_MASK & EntryMask) ==
+          (ConvertSectionAttributesToPageAttributes (Descriptor) & EntryMask))
+      {
+        continue;
+      }
+
       Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
       if (EFI_ERROR (Status)) {
         // Exit for loop
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 09/38] ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (7 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-15 18:08   ` [edk2-devel] " Leif Lindholm
  2023-03-13 17:16 ` [PATCH v5 10/38] MdePkg: Add Memory Attribute Protocol definition Ard Biesheuvel
                   ` (30 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

In preparation for introducing an implementation of the EFI memory
attributes protocol that is shared between ARM and AArch64, unify the
existing code that converts a page table descriptor into a
EFI_MEMORY_xxx bitfield, so it can be called from the generic code.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c | 17 +++++++++
 ArmPkg/Drivers/CpuDxe/Arm/Mmu.c     | 38 ++++++++++++++++++++
 ArmPkg/Drivers/CpuDxe/CpuDxe.h      | 14 ++++++++
 3 files changed, 69 insertions(+)

diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
index 8bda11f08a30..4a416743fb8a 100644
--- a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
@@ -83,6 +83,23 @@ PageAttributeToGcdAttribute (
   return GcdAttributes;
 }
 
+/**
+  Convert a arch specific set of page attributes into a mask
+  of EFI_MEMORY_xx constants.
+
+  @param  PageAttributes  The set of page attributes.
+
+  @retval The mask of EFI_MEMORY_xx constants.
+
+**/
+UINT64
+RegionAttributeToGcdAttribute (
+  IN UINTN  PageAttributes
+  )
+{
+  return PageAttributeToGcdAttribute (PageAttributes);
+}
+
 STATIC
 UINT64
 GetFirstPageAttribute (
diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
index 07faab8216ec..8e0dd5d2aaca 100644
--- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
+++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
@@ -13,6 +13,15 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include <Library/MemoryAllocationLib.h>
 #include "CpuDxe.h"
 
+/**
+  Convert a set of ARM short descriptor section attributes into a mask
+  of EFI_MEMORY_xx constants.
+
+  @param  SectionAttributes   The set of page attributes.
+  @param  GcdAttributes       Pointer to the return value.
+
+**/
+STATIC
 EFI_STATUS
 SectionToGcdAttributes (
   IN  UINT32  SectionAttributes,
@@ -74,6 +83,35 @@ SectionToGcdAttributes (
   return EFI_SUCCESS;
 }
 
+/**
+  Convert a arch specific set of page attributes into a mask
+  of EFI_MEMORY_xx constants.
+
+  @param  PageAttributes  The set of page attributes.
+
+  @retval The mask of EFI_MEMORY_xx constants.
+
+**/
+UINT64
+RegionAttributeToGcdAttribute (
+  IN UINTN  PageAttributes
+  )
+{
+  UINT64  Result;
+
+  SectionToGcdAttributes (PageAttributes, &Result);
+  return Result;
+}
+
+/**
+  Convert a set of ARM short descriptor page attributes into a mask
+  of EFI_MEMORY_xx constants.
+
+  @param  PageAttributes      The set of page attributes.
+  @param  GcdAttributes       Pointer to the return value.
+
+**/
+STATIC
 EFI_STATUS
 PageToGcdAttributes (
   IN  UINT32  PageAttributes,
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
index ff672390ce51..8cb105dcc841 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
@@ -126,4 +126,18 @@ SetGcdMemorySpaceAttributes (
   IN UINT64                           Attributes
   );
 
+/**
+  Convert a arch specific set of page attributes into a mask
+  of EFI_MEMORY_xx constants.
+
+  @param  PageAttributes  The set of page attributes.
+
+  @retval The mask of EFI_MEMORY_xx constants.
+
+**/
+UINT64
+RegionAttributeToGcdAttribute (
+  IN UINTN  PageAttributes
+  );
+
 #endif // CPU_DXE_H_
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 10/38] MdePkg: Add Memory Attribute Protocol definition
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (8 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 09/38] ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol Ard Biesheuvel
                   ` (29 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Add the Memory Attribute Protocol definition, which was adopted and
included in version 2.10 of the UEFI specification.

Link: https://bugzilla.tianocore.org/show_bug.cgi?id=3519
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Protocol/MemoryAttribute.h | 142 ++++++++++++++++++++
 MdePkg/MdePkg.dec                         |   3 +
 2 files changed, 145 insertions(+)

diff --git a/MdePkg/Include/Protocol/MemoryAttribute.h b/MdePkg/Include/Protocol/MemoryAttribute.h
new file mode 100644
index 000000000000..5c6b7badb589
--- /dev/null
+++ b/MdePkg/Include/Protocol/MemoryAttribute.h
@@ -0,0 +1,142 @@
+/** @file
+
+  EFI Memory Attribute Protocol provides retrieval and update service
+  for memory attributes in EFI environment.
+
+  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef EFI_MEMORY_ATTRIBUTE_H_
+#define EFI_MEMORY_ATTRIBUTE_H_
+
+#define EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID \
+  { \
+    0xf4560cf6, 0x40ec, 0x4b4a, { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \
+  }
+
+typedef struct _EFI_MEMORY_ATTRIBUTE_PROTOCOL EFI_MEMORY_ATTRIBUTE_PROTOCOL;
+
+/**
+  This function set given attributes of the memory region specified by
+  BaseAddress and Length.
+
+  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
+
+  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+  @param  Attributes        The bit mask of attributes to set for the memory
+                            region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+                                Attributes specified an illegal combination of
+                                attributes that cannot be set together.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more
+                                bytes of the memory resource range specified
+                                by BaseAddress and Length.
+                                The bit mask of attributes is not supported for
+                                the memory resource range specified by
+                                BaseAddress and Length.
+  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
+                                lack of system resources.
+  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
+                                controlled by system firmware and cannot be
+                                updated via the protocol.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_SET_MEMORY_ATTRIBUTES)(
+  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL       *This,
+  IN  EFI_PHYSICAL_ADDRESS                BaseAddress,
+  IN  UINT64                              Length,
+  IN  UINT64                              Attributes
+  );
+
+/**
+  This function clears given attributes of the memory region specified by
+  BaseAddress and Length.
+
+  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
+
+  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+  @param  Attributes        The bit mask of attributes to clear for the memory
+                            region.
+
+  @retval EFI_SUCCESS           The attributes were cleared for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+                                Attributes specified an illegal combination of
+                                attributes that cannot be cleared together.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more
+                                bytes of the memory resource range specified
+                                by BaseAddress and Length.
+                                The bit mask of attributes is not supported for
+                                the memory resource range specified by
+                                BaseAddress and Length.
+  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
+                                lack of system resources.
+  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
+                                controlled by system firmware and cannot be
+                                updated via the protocol.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_CLEAR_MEMORY_ATTRIBUTES)(
+  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL       *This,
+  IN  EFI_PHYSICAL_ADDRESS                BaseAddress,
+  IN  UINT64                              Length,
+  IN  UINT64                              Attributes
+  );
+
+/**
+  This function retrieves the attributes of the memory region specified by
+  BaseAddress and Length. If different attributes are got from different part
+  of the memory region, EFI_NO_MAPPING will be returned.
+
+  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+  @param  Attributes        Pointer to attributes returned.
+
+  @retval EFI_SUCCESS           The attributes got for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+                                Attributes is NULL.
+  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
+                                region.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more
+                                bytes of the memory resource range specified
+                                by BaseAddress and Length.
+
+**/
+typedef
+EFI_STATUS
+(EFIAPI *EFI_GET_MEMORY_ATTRIBUTES)(
+  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL       *This,
+  IN  EFI_PHYSICAL_ADDRESS                BaseAddress,
+  IN  UINT64                              Length,
+  OUT UINT64                              *Attributes
+  );
+
+///
+/// EFI Memory Attribute Protocol provides services to retrieve or update
+/// attribute of memory in the EFI environment.
+///
+struct _EFI_MEMORY_ATTRIBUTE_PROTOCOL {
+  EFI_GET_MEMORY_ATTRIBUTES      GetMemoryAttributes;
+  EFI_SET_MEMORY_ATTRIBUTES      SetMemoryAttributes;
+  EFI_CLEAR_MEMORY_ATTRIBUTES    ClearMemoryAttributes;
+};
+
+extern EFI_GUID  gEfiMemoryAttributeProtocolGuid;
+
+#endif
diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index 80b655905314..2d643bede1d7 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -1922,6 +1922,9 @@ [Protocols]
   ## Include/Protocol/RedfishDiscover.h
   gEfiRedfishDiscoverProtocolGuid      = { 0x5db12509, 0x4550, 0x4347, { 0x96, 0xb3, 0x73, 0xc0, 0xff, 0x6e, 0x86, 0x9f }}
 
+  ## Include/Protocol/MemoryAttribute.h
+  gEfiMemoryAttributeProtocolGuid = { 0xf4560cf6, 0x40ec, 0x4b4a, { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 }}
+
   #
   # Protocols defined in Shell2.0
   #
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (9 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 10/38] MdePkg: Add Memory Attribute Protocol definition Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-15 18:31   ` Leif Lindholm
  2023-03-13 17:16 ` [PATCH v5 12/38] ArmPkg/CpuDxe: Perform preliminary NX remap of free memory Ard Biesheuvel
                   ` (28 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Expose the protocol introduced in v2.10 that permits the caller to
manage mapping permissions in the page tables.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Drivers/CpuDxe/CpuDxe.c          |   2 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.h          |   3 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.inf        |   2 +
 ArmPkg/Drivers/CpuDxe/MemoryAttribute.c | 271 ++++++++++++++++++++
 4 files changed, 278 insertions(+)

diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
index e6742f0a25fc..d04958e79e52 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
@@ -244,6 +244,8 @@ CpuDxeInitialize (
                   &mCpuHandle,
                   &gEfiCpuArchProtocolGuid,
                   &mCpu,
+                  &gEfiMemoryAttributeProtocolGuid,
+                  &mMemoryAttribute,
                   NULL
                   );
 
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
index 8cb105dcc841..ce2981361aca 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
@@ -30,9 +30,12 @@
 #include <Protocol/Cpu.h>
 #include <Protocol/DebugSupport.h>
 #include <Protocol/LoadedImage.h>
+#include <Protocol/MemoryAttribute.h>
 
 extern BOOLEAN  mIsFlushingGCD;
 
+extern EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute;
+
 /**
   This function registers and enables the handler specified by InterruptHandler for a processor
   interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
index 10792b393fc8..e732e21cb94a 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
@@ -23,6 +23,7 @@ [Sources.Common]
   CpuDxe.h
   CpuMmuCommon.c
   Exception.c
+  MemoryAttribute.c
 
 [Sources.ARM]
   Arm/Mmu.c
@@ -53,6 +54,7 @@ [LibraryClasses]
 
 [Protocols]
   gEfiCpuArchProtocolGuid
+  gEfiMemoryAttributeProtocolGuid
 
 [Guids]
   gEfiDebugImageInfoTableGuid
diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
new file mode 100644
index 000000000000..b47464c0269e
--- /dev/null
+++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
@@ -0,0 +1,271 @@
+/** @file
+
+  Copyright (c) 2023, Google LLC. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuDxe.h"
+
+/**
+  This function retrieves the attributes of the memory region specified by
+  BaseAddress and Length. If different attributes are got from different part
+  of the memory region, EFI_NO_MAPPING will be returned.
+
+  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+  @param  Attributes        Pointer to attributes returned.
+
+  @retval EFI_SUCCESS           The attributes got for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+                                Attributes is NULL.
+  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
+                                region.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more
+                                bytes of the memory resource range specified
+                                by BaseAddress and Length.
+
+**/
+STATIC
+EFI_STATUS
+GetMemoryAttributes (
+  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
+  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
+  IN  UINT64                         Length,
+  OUT UINT64                         *Attributes
+  )
+{
+  UINTN       RegionAddress;
+  UINTN       RegionLength;
+  UINTN       RegionAttributes;
+  UINTN       Union;
+  UINTN       Intersection;
+  EFI_STATUS  Status;
+
+  if ((Length == 0) || (Attributes == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  DEBUG ((
+    DEBUG_VERBOSE,
+    "%a: BaseAddress == 0x%lx, Length == 0x%lx\n",
+    __FUNCTION__,
+    BaseAddress,
+    Length
+    ));
+
+  Union        = 0;
+  Intersection = MAX_UINTN;
+
+  for (RegionAddress = (UINTN)BaseAddress;
+       RegionAddress < (UINTN)(BaseAddress + Length);
+       RegionAddress += RegionLength)
+  {
+    Status = GetMemoryRegion (
+               &RegionAddress,
+               &RegionLength,
+               &RegionAttributes
+               );
+
+    DEBUG ((
+      DEBUG_VERBOSE,
+      "%a: RegionAddress == 0x%lx, RegionLength == 0x%lx, RegionAttributes == 0x%lx\n",
+      __FUNCTION__,
+      (UINT64)RegionAddress,
+      (UINT64)RegionLength,
+      (UINT64)RegionAttributes
+      ));
+
+    if (EFI_ERROR (Status)) {
+      return EFI_NO_MAPPING;
+    }
+
+    Union        |= RegionAttributes;
+    Intersection &= RegionAttributes;
+  }
+
+  DEBUG ((
+    DEBUG_VERBOSE,
+    "%a: Union == %lx, Intersection == %lx\n",
+    __FUNCTION__,
+    (UINT64)Union,
+    (UINT64)Intersection
+    ));
+
+  if (Union != Intersection) {
+    return EFI_NO_MAPPING;
+  }
+
+  *Attributes  = RegionAttributeToGcdAttribute (Union);
+  *Attributes &= EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP;
+  return EFI_SUCCESS;
+}
+
+/**
+  This function set given attributes of the memory region specified by
+  BaseAddress and Length.
+
+  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
+
+  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+  @param  Attributes        The bit mask of attributes to set for the memory
+                            region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+                                Attributes specified an illegal combination of
+                                attributes that cannot be set together.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more
+                                bytes of the memory resource range specified
+                                by BaseAddress and Length.
+                                The bit mask of attributes is not supported for
+                                the memory resource range specified by
+                                BaseAddress and Length.
+  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
+                                lack of system resources.
+  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
+                                controlled by system firmware and cannot be
+                                updated via the protocol.
+
+**/
+STATIC
+EFI_STATUS
+SetMemoryAttributes (
+  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
+  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
+  IN  UINT64                         Length,
+  IN  UINT64                         Attributes
+  )
+{
+  EFI_STATUS  Status;
+
+  DEBUG ((
+    DEBUG_INFO,
+    "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
+    __FUNCTION__,
+    (UINTN)BaseAddress,
+    (UINTN)Length,
+    (UINTN)Attributes
+    ));
+
+  if ((Length == 0) ||
+      ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Attributes & EFI_MEMORY_RP) != 0) {
+    Status = ArmSetMemoryRegionNoAccess (BaseAddress, Length);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  if ((Attributes & EFI_MEMORY_RO) != 0) {
+    Status = ArmSetMemoryRegionReadOnly (BaseAddress, Length);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  if ((Attributes & EFI_MEMORY_XP) != 0) {
+    Status = ArmSetMemoryRegionNoExec (BaseAddress, Length);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function clears given attributes of the memory region specified by
+  BaseAddress and Length.
+
+  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
+
+  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+  @param  Attributes        The bit mask of attributes to clear for the memory
+                            region.
+
+  @retval EFI_SUCCESS           The attributes were cleared for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+                                Attributes specified an illegal combination of
+                                attributes that cannot be cleared together.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more
+                                bytes of the memory resource range specified
+                                by BaseAddress and Length.
+                                The bit mask of attributes is not supported for
+                                the memory resource range specified by
+                                BaseAddress and Length.
+  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
+                                lack of system resources.
+  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
+                                controlled by system firmware and cannot be
+                                updated via the protocol.
+
+**/
+STATIC
+EFI_STATUS
+ClearMemoryAttributes (
+  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
+  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
+  IN  UINT64                         Length,
+  IN  UINT64                         Attributes
+  )
+{
+  EFI_STATUS  Status;
+
+  DEBUG ((
+    DEBUG_INFO,
+    "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
+    __FUNCTION__,
+    (UINTN)BaseAddress,
+    (UINTN)Length,
+    (UINTN)Attributes
+    ));
+
+  if ((Length == 0) ||
+      ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
+  {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Attributes & EFI_MEMORY_RP) != 0) {
+    Status = ArmClearMemoryRegionNoAccess (BaseAddress, Length);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  if ((Attributes & EFI_MEMORY_RO) != 0) {
+    Status = ArmClearMemoryRegionReadOnly (BaseAddress, Length);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  if ((Attributes & EFI_MEMORY_XP) != 0) {
+    Status = ArmClearMemoryRegionNoExec (BaseAddress, Length);
+    if (EFI_ERROR (Status)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute = {
+  GetMemoryAttributes,
+  SetMemoryAttributes,
+  ClearMemoryAttributes
+};
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 12/38] ArmPkg/CpuDxe: Perform preliminary NX remap of free memory
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (10 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 13/38] MdeModulePkg/DxeCore: Unconditionally set memory protections Ard Biesheuvel
                   ` (27 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

The DXE core implementation of PcdDxeNxMemoryProtectionPolicy already
contains an assertion that EfiConventionalMemory and EfiBootServicesData
are subjected to the same policy when it comes to the use of NX
permissions. The reason for this is that we may otherwise end up with
unbounded recursion in the page table code, given that allocating a page
table would then involve a permission attribute change, and this could
result in the need for a block entry to be split, which would trigger
the allocation of a page table recursively.

For the same reason, a shortcut exists in ApplyMemoryProtectionPolicy()
where, instead of setting the memory attributes unconditionally, we
compare the NX policies and avoid touching the page tables if they are
the same for the old and the new memory types. Without this shortcut, we
may end up in a situation where, as the CPU arch protocol DXE driver is
ramping up, the same unbounded recursion is triggered, due to the fact
that the NX policy for EfiConventionalMemory has not been applied yet.

To break this cycle, let's remap all EfiConventionalMemory regions
according to the NX policy for EfiBootServicesData before exposing the
CPU arch protocol to the DXE core and other drivers. This ensures that
creating EfiBootServicesData allocations does not result in memory
attribute changes, and therefore no recursion.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/ArmPkg.dec                |  5 ++
 ArmPkg/Drivers/CpuDxe/CpuDxe.c   | 85 ++++++++++++++++++++
 ArmPkg/Drivers/CpuDxe/CpuDxe.inf |  3 +
 3 files changed, 93 insertions(+)

diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
index f17ba913e6de..3f9f6bc3fd93 100644
--- a/ArmPkg/ArmPkg.dec
+++ b/ArmPkg/ArmPkg.dec
@@ -144,6 +144,11 @@ [PcdsFeatureFlag.common]
   # If PcdMonitorConduitHvc = TRUE, conduit = HVC
   gArmTokenSpaceGuid.PcdMonitorConduitHvc|FALSE|BOOLEAN|0x00000047
 
+  # Whether to remap all unused memory NX before installing the CPU arch
+  # protocol driver. This is needed on platforms that map all DRAM with RWX
+  # attributes initially, and can be disabled otherwise.
+  gArmTokenSpaceGuid.PcdRemapUnusedMemoryNx|TRUE|BOOLEAN|0x00000048
+
 [PcdsFeatureFlag.ARM]
   # Whether to map normal memory as non-shareable. FALSE is the safe choice, but
   # TRUE may be appropriate to fix performance problems if you don't care about
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
index d04958e79e52..f820f3f62189 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
@@ -11,6 +11,8 @@
 
 #include <Guid/IdleLoopEvent.h>
 
+#include <Library/MemoryAllocationLib.h>
+
 BOOLEAN  mIsFlushingGCD;
 
 /**
@@ -227,6 +229,75 @@ InitializeDma (
   CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule ();
 }
 
+/**
+  Map all EfiConventionalMemory regions in the memory map with NX
+  attributes so that allocating or freeing EfiBootServicesData regions
+  does not result in changes to memory permission attributes.
+
+**/
+STATIC
+VOID
+RemapUnusedMemoryNx (
+  VOID
+  )
+{
+  UINT64                 TestBit;
+  UINTN                  MemoryMapSize;
+  UINTN                  MapKey;
+  UINTN                  DescriptorSize;
+  UINT32                 DescriptorVersion;
+  EFI_MEMORY_DESCRIPTOR  *MemoryMap;
+  EFI_MEMORY_DESCRIPTOR  *MemoryMapEntry;
+  EFI_MEMORY_DESCRIPTOR  *MemoryMapEnd;
+  EFI_STATUS             Status;
+
+  TestBit = LShiftU64 (1, EfiBootServicesData);
+  if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) == 0) {
+    return;
+  }
+
+  MemoryMapSize = 0;
+  MemoryMap     = NULL;
+
+  Status = gBS->GetMemoryMap (
+                  &MemoryMapSize,
+                  MemoryMap,
+                  &MapKey,
+                  &DescriptorSize,
+                  &DescriptorVersion
+                  );
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
+  do {
+    MemoryMap = (EFI_MEMORY_DESCRIPTOR *)AllocatePool (MemoryMapSize);
+    ASSERT (MemoryMap != NULL);
+    Status = gBS->GetMemoryMap (
+                    &MemoryMapSize,
+                    MemoryMap,
+                    &MapKey,
+                    &DescriptorSize,
+                    &DescriptorVersion
+                    );
+    if (EFI_ERROR (Status)) {
+      FreePool (MemoryMap);
+    }
+  } while (Status == EFI_BUFFER_TOO_SMALL);
+
+  ASSERT_EFI_ERROR (Status);
+
+  MemoryMapEntry = MemoryMap;
+  MemoryMapEnd   = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
+  while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
+    if (MemoryMapEntry->Type == EfiConventionalMemory) {
+      ArmSetMemoryRegionNoExec (
+        MemoryMapEntry->PhysicalStart,
+        EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages)
+        );
+    }
+
+    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
+  }
+}
+
 EFI_STATUS
 CpuDxeInitialize (
   IN EFI_HANDLE        ImageHandle,
@@ -240,6 +311,20 @@ CpuDxeInitialize (
 
   InitializeDma (&mCpu);
 
+  //
+  // Once we install the CPU arch protocol, the DXE core's memory
+  // protection routines will invoke them to manage the permissions of page
+  // allocations as they are created. Given that this includes pages
+  // allocated for page tables by this driver, we must ensure that unused
+  // memory is mapped with the same permissions as boot services data
+  // regions. Otherwise, we may end up with unbounded recursion, due to the
+  // fact that updating permissions on a newly allocated page table may trigger
+  // a block entry split, which triggers a page table allocation, etc etc
+  //
+  if (FeaturePcdGet (PcdRemapUnusedMemoryNx)) {
+    RemapUnusedMemoryNx ();
+  }
+
   Status = gBS->InstallMultipleProtocolInterfaces (
                   &mCpuHandle,
                   &gEfiCpuArchProtocolGuid,
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
index e732e21cb94a..7d8132200e64 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
@@ -48,6 +48,7 @@ [LibraryClasses]
   DefaultExceptionHandlerLib
   DxeServicesTableLib
   HobLib
+  MemoryAllocationLib
   PeCoffGetEntryPointLib
   UefiDriverEntryPoint
   UefiLib
@@ -64,9 +65,11 @@ [Guids]
 
 [Pcd.common]
   gArmTokenSpaceGuid.PcdVFPEnabled
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeNxMemoryProtectionPolicy
 
 [FeaturePcd.common]
   gArmTokenSpaceGuid.PcdDebuggerExceptionSupport
+  gArmTokenSpaceGuid.PcdRemapUnusedMemoryNx
 
 [Depex]
   gHardwareInterruptProtocolGuid OR gHardwareInterrupt2ProtocolGuid
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 13/38] MdeModulePkg/DxeCore: Unconditionally set memory protections
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (11 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 12/38] ArmPkg/CpuDxe: Perform preliminary NX remap of free memory Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 14/38] ArmPkg/Mmu: Remove handling of NONSECURE memory regions Ard Biesheuvel
                   ` (26 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Instead of relying on a questionable heuristic that avoids calling into
the SetMemoryAttributes () DXE service when the old memory type and the
new one are subjected to the same NX memory protection policy, make this
call unconditionally. This avoids corner cases where memory region
attributes are out of sync with the policy, either due to the fact that
we are in the middle of ramping up the protections, or due to explicit
invocations of SetMemoryAttributes() by drivers.

This requires the architecture page table code to be able to deal with
this, in particular, it needs to be robust against potential recursion
due to NX policies being applied to newly allocated page tables.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index b89ab046fa73..5a82eee80781 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -1246,7 +1246,6 @@ ApplyMemoryProtectionPolicy (
   IN  UINT64                Length
   )
 {
-  UINT64  OldAttributes;
   UINT64  NewAttributes;
 
   //
@@ -1302,16 +1301,5 @@ ApplyMemoryProtectionPolicy (
   //
   NewAttributes = GetPermissionAttributeForMemoryType (NewType);
 
-  if (OldType != EfiMaxMemoryType) {
-    OldAttributes = GetPermissionAttributeForMemoryType (OldType);
-    if (OldAttributes == NewAttributes) {
-      // policy is the same between OldType and NewType
-      return EFI_SUCCESS;
-    }
-  } else if (NewAttributes == 0) {
-    // newly added region of a type that does not require protection
-    return EFI_SUCCESS;
-  }
-
   return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 14/38] ArmPkg/Mmu: Remove handling of NONSECURE memory regions
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (12 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 13/38] MdeModulePkg/DxeCore: Unconditionally set memory protections Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 15/38] ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory Ard Biesheuvel
                   ` (25 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Non-secure memory is a distinction that only matters when executing code
in the secure world that reasons about the secure vs non-secure address
spaces. EDK2 was not designed for that, and the AArch64 version of the
MMU handling library already treats them as identical, so let's just
drop the ARM memory region types that mark memory as 'non-secure'
explicitly.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Include/Chipset/ArmV7Mmu.h                | 51 +++++++-------------
 ArmPkg/Include/Library/ArmLib.h                  | 11 -----
 ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c |  5 --
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c     | 33 +++----------
 4 files changed, 24 insertions(+), 76 deletions(-)

diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
index da4f3160f8ff..89b81e33d004 100644
--- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
+++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
@@ -157,39 +157,24 @@
 #define TT_DESCRIPTOR_PAGE_BASE_ADDRESS(a)  ((a) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK)
 #define TT_DESCRIPTOR_PAGE_BASE_SHIFT  12
 
-#define TT_DESCRIPTOR_SECTION_WRITE_BACK(NonSecure)     (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
-                                                            ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
-                                                            TT_DESCRIPTOR_SECTION_NG_GLOBAL                         | \
-                                                            TT_DESCRIPTOR_SECTION_S_SHARED                          | \
-                                                            TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
-                                                            TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
-                                                            TT_DESCRIPTOR_SECTION_AF                                | \
-                                                            TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC)
-#define TT_DESCRIPTOR_SECTION_WRITE_THROUGH(NonSecure)  (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
-                                                            ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
-                                                            TT_DESCRIPTOR_SECTION_NG_GLOBAL                         | \
-                                                            TT_DESCRIPTOR_SECTION_S_SHARED                          | \
-                                                            TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
-                                                            TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
-                                                            TT_DESCRIPTOR_SECTION_AF                                | \
-                                                            TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
-#define TT_DESCRIPTOR_SECTION_DEVICE(NonSecure)         (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
-                                                            ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
-                                                            TT_DESCRIPTOR_SECTION_NG_GLOBAL                         | \
-                                                            TT_DESCRIPTOR_SECTION_S_NOT_SHARED                      | \
-                                                            TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
-                                                            TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
-                                                            TT_DESCRIPTOR_SECTION_XN_MASK                           | \
-                                                            TT_DESCRIPTOR_SECTION_AF                                | \
-                                                            TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE)
-#define TT_DESCRIPTOR_SECTION_UNCACHED(NonSecure)       (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |    \
-                                                           ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
-                                                           TT_DESCRIPTOR_SECTION_NG_GLOBAL                         | \
-                                                           TT_DESCRIPTOR_SECTION_S_NOT_SHARED                      | \
-                                                           TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
-                                                           TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
-                                                            TT_DESCRIPTOR_SECTION_AF                                | \
-                                                           TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE)
+#define TT_DESCRIPTOR_SECTION_DEFAULT  (TT_DESCRIPTOR_SECTION_TYPE_SECTION | \
+                                        TT_DESCRIPTOR_SECTION_NG_GLOBAL    | \
+                                        TT_DESCRIPTOR_SECTION_S_SHARED     | \
+                                        TT_DESCRIPTOR_SECTION_DOMAIN(0)    | \
+                                        TT_DESCRIPTOR_SECTION_AP_RW_RW     | \
+                                        TT_DESCRIPTOR_SECTION_AF)
+
+#define TT_DESCRIPTOR_SECTION_WRITE_BACK  (TT_DESCRIPTOR_SECTION_DEFAULT | \
+                                           TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC)
+
+#define TT_DESCRIPTOR_SECTION_WRITE_THROUGH  (TT_DESCRIPTOR_SECTION_DEFAULT | \
+                                              TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
+
+#define TT_DESCRIPTOR_SECTION_DEVICE  (TT_DESCRIPTOR_SECTION_DEFAULT | \
+                                       TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE)
+
+#define TT_DESCRIPTOR_SECTION_UNCACHED  (TT_DESCRIPTOR_SECTION_DEFAULT | \
+                                         TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE)
 
 #define TT_DESCRIPTOR_PAGE_WRITE_BACK     (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
                                                         TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
diff --git a/ArmPkg/Include/Library/ArmLib.h b/ArmPkg/Include/Library/ArmLib.h
index fa605f128bfd..a53f60d98852 100644
--- a/ArmPkg/Include/Library/ArmLib.h
+++ b/ArmPkg/Include/Library/ArmLib.h
@@ -25,29 +25,18 @@
                                      EFI_MEMORY_WT | EFI_MEMORY_WB | \
                                      EFI_MEMORY_UCE)
 
-/**
- * The UEFI firmware must not use the ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_* attributes.
- *
- * The Non Secure memory attribute (ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_*) should only
- * be used in Secure World to distinguished Secure to Non-Secure memory.
- */
 typedef enum {
   ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED = 0,
-  ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED,
   ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK,
-  ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK,
 
   // On some platforms, memory mapped flash region is designed as not supporting
   // shareable attribute, so WRITE_BACK_NONSHAREABLE is added for such special
   // need.
   // Do NOT use below two attributes if you are not sure.
   ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE,
-  ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE,
 
   ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH,
-  ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH,
   ARM_MEMORY_REGION_ATTRIBUTE_DEVICE,
-  ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE
 } ARM_MEMORY_REGION_ATTRIBUTES;
 
 #define IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(attr)  ((UINT32)(attr) & 1)
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index 1ce200c43c72..ee4c5c995ce8 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -39,26 +39,21 @@ ArmMemoryAttributeToPageAttribute (
 {
   switch (Attributes) {
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:
       return TT_ATTR_INDX_MEMORY_WRITE_BACK;
 
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
       return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;
 
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
       return TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;
 
     // Uncached and device mappings are treated as outer shareable by default,
     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
       return TT_ATTR_INDX_MEMORY_NON_CACHEABLE;
 
     default:
       ASSERT (0);
     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
       if (ArmReadCurrentEL () == AARCH64_EL2) {
         return TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;
       } else {
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
index 28cc9b2fe058..154298357460 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
@@ -100,24 +100,19 @@ PopulateLevel2PageTable (
 
   switch (Attributes) {
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_BACK;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:
       PageAttributes  = TT_DESCRIPTOR_PAGE_WRITE_BACK;
       PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
       PageAttributes = TT_DESCRIPTOR_PAGE_DEVICE;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
       PageAttributes = TT_DESCRIPTOR_PAGE_UNCACHED;
       break;
     default:
@@ -239,39 +234,23 @@ FillTranslationTable (
 
   switch (MemoryRegion->Attributes) {
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
-      Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK (0);
+      Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
-      Attributes  = TT_DESCRIPTOR_SECTION_WRITE_BACK (0);
+      Attributes  = TT_DESCRIPTOR_SECTION_WRITE_BACK;
       Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
-      Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH (0);
+      Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
-      Attributes = TT_DESCRIPTOR_SECTION_DEVICE (0);
+      Attributes = TT_DESCRIPTOR_SECTION_DEVICE;
       break;
     case ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED:
-      Attributes = TT_DESCRIPTOR_SECTION_UNCACHED (0);
-      break;
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK:
-      Attributes = TT_DESCRIPTOR_SECTION_WRITE_BACK (1);
-      break;
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE:
-      Attributes  = TT_DESCRIPTOR_SECTION_WRITE_BACK (1);
-      Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
-      break;
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH:
-      Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH (1);
-      break;
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE:
-      Attributes = TT_DESCRIPTOR_SECTION_DEVICE (1);
-      break;
-    case ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED:
-      Attributes = TT_DESCRIPTOR_SECTION_UNCACHED (1);
+      Attributes = TT_DESCRIPTOR_SECTION_UNCACHED;
       break;
     default:
-      Attributes = TT_DESCRIPTOR_SECTION_UNCACHED (0);
+      Attributes = TT_DESCRIPTOR_SECTION_UNCACHED;
       break;
   }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 15/38] ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (13 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 14/38] ArmPkg/Mmu: Remove handling of NONSECURE memory regions Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 16/38] MdePkg/BasePeCoffLib: Add API to keep track of relocation range Ard Biesheuvel
                   ` (24 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

To prepare for the enablement of booting EFI with the SCTLR.WXN control
enabled, which makes all writeable memory regions non-executable by
default, introduce a memory type that we will use to describe the flash
region that carries the SEC and PEIM modules that execute in place. Even
if these are implicitly read-only due to the ROM nature, they need to be
mapped with read-only attributes in the page tables to be able to
execute from them.

Also add the XP counterpart which will be used for all normal DRAM right
at the outset.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/Include/Library/ArmLib.h                  |  6 ++++
 ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c | 30 ++++++++++++++++----
 ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c     | 16 +++++++++++
 3 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/ArmPkg/Include/Library/ArmLib.h b/ArmPkg/Include/Library/ArmLib.h
index a53f60d98852..fb1ae57b3522 100644
--- a/ArmPkg/Include/Library/ArmLib.h
+++ b/ArmPkg/Include/Library/ArmLib.h
@@ -35,6 +35,12 @@ typedef enum {
   // Do NOT use below two attributes if you are not sure.
   ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE,
 
+  // Special region types for memory that must be mapped with read-only or
+  // non-execute permissions from the very start, e.g., to support the use
+  // of the WXN virtual memory control.
+  ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO,
+  ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP,
+
   ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH,
   ARM_MEMORY_REGION_ATTRIBUTE_DEVICE,
 } ARM_MEMORY_REGION_ATTRIBUTES;
diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
index ee4c5c995ce8..419b3b028201 100644
--- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
@@ -37,12 +37,34 @@ ArmMemoryAttributeToPageAttribute (
   IN ARM_MEMORY_REGION_ATTRIBUTES  Attributes
   )
 {
+  UINT64  Permissions;
+
+  switch (Attributes) {
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO:
+      Permissions = TT_AP_NO_RO;
+      break;
+
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP:
+    case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
+      if (ArmReadCurrentEL () == AARCH64_EL2) {
+        Permissions = TT_XN_MASK;
+      } else {
+        Permissions = TT_UXN_MASK | TT_PXN_MASK;
+      }
+      break;
+    default:
+      Permissions = 0;
+      break;
+  }
+
   switch (Attributes) {
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE:
       return TT_ATTR_INDX_MEMORY_WRITE_BACK;
 
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK:
-      return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE;
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO:
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP:
+      return TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE | Permissions;
 
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
       return TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE;
@@ -54,11 +76,7 @@ ArmMemoryAttributeToPageAttribute (
     default:
       ASSERT (0);
     case ARM_MEMORY_REGION_ATTRIBUTE_DEVICE:
-      if (ArmReadCurrentEL () == AARCH64_EL2) {
-        return TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK;
-      } else {
-        return TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK;
-      }
+      return TT_ATTR_INDX_DEVICE_MEMORY | Permissions;
   }
 }
 
diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
index 154298357460..00c5f42cd91a 100644
--- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
+++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c
@@ -106,6 +106,14 @@ PopulateLevel2PageTable (
       PageAttributes  = TT_DESCRIPTOR_PAGE_WRITE_BACK;
       PageAttributes &= ~TT_DESCRIPTOR_PAGE_S_SHARED;
       break;
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO:
+      PageAttributes  = TT_DESCRIPTOR_PAGE_WRITE_BACK;
+      PageAttributes |= TT_DESCRIPTOR_PAGE_AP_NO_RO;
+      break;
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP:
+      PageAttributes  = TT_DESCRIPTOR_PAGE_WRITE_BACK;
+      PageAttributes |= TT_DESCRIPTOR_PAGE_XN_MASK;
+      break;
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
       PageAttributes = TT_DESCRIPTOR_PAGE_WRITE_THROUGH;
       break;
@@ -240,6 +248,14 @@ FillTranslationTable (
       Attributes  = TT_DESCRIPTOR_SECTION_WRITE_BACK;
       Attributes &= ~TT_DESCRIPTOR_SECTION_S_SHARED;
       break;
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO:
+      Attributes  = TT_DESCRIPTOR_SECTION_WRITE_BACK;
+      Attributes |= TT_DESCRIPTOR_SECTION_AP_NO_RO;
+      break;
+    case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP:
+      Attributes  = TT_DESCRIPTOR_SECTION_WRITE_BACK;
+      Attributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
+      break;
     case ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH:
       Attributes = TT_DESCRIPTOR_SECTION_WRITE_THROUGH;
       break;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 16/38] MdePkg/BasePeCoffLib: Add API to keep track of relocation range
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (14 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 15/38] ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 17/38] MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default Ard Biesheuvel
                   ` (23 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Add a library call to obtain the start and end of the region covered by
relocation fixups. This will be used in a future patch to limit the
range of memory that needs to be remapped with read-write-execute
permissions at ExitBootServices() time.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdePkg/Include/Library/PeCoffLib.h        | 23 ++++++
 MdePkg/Library/BasePeCoffLib/BasePeCoff.c | 82 +++++++++++++++++++-
 2 files changed, 104 insertions(+), 1 deletion(-)

diff --git a/MdePkg/Include/Library/PeCoffLib.h b/MdePkg/Include/Library/PeCoffLib.h
index b45879453785..df2f7f5e5961 100644
--- a/MdePkg/Include/Library/PeCoffLib.h
+++ b/MdePkg/Include/Library/PeCoffLib.h
@@ -382,4 +382,27 @@ PeCoffLoaderUnloadImage (
   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
   );
 
+/**
+  Retrieve the range subject to relocation fixups from the recorded fixup data
+  of a runtime image
+
+  @param       ImageBase           The base address of a PE/COFF image that has been loaded
+                                   and relocated into system memory.
+  @param       ImageSize           The size, in bytes, of the PE/COFF image.
+  @param       RelocationData      A pointer to the relocation data that was collected when the
+                                   PE/COFF image was relocated using PeCoffLoaderRelocateImage().
+  @param[out]  RelocationRangeMin  The start of the relocated range.
+  @param[out]  RelocationRangeMax  The end of the relocated range.
+
+**/
+VOID
+EFIAPI
+PeCoffLoaderGetRelocationRange (
+  IN  PHYSICAL_ADDRESS  ImageBase,
+  IN  UINTN             ImageSize,
+  IN  VOID              *RelocationData,
+  OUT PHYSICAL_ADDRESS  *RelocationRangeMin,
+  OUT PHYSICAL_ADDRESS  *RelocationRangeMax
+  );
+
 #endif
diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
index 97a8aaf8c73d..31e1f2035963 100644
--- a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
+++ b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
@@ -936,6 +936,8 @@ PeCoffLoaderRelocateImage (
   PHYSICAL_ADDRESS                     BaseAddress;
   UINT32                               NumberOfRvaAndSizes;
   UINT32                               TeStrippedOffset;
+  PHYSICAL_ADDRESS                     *RelocRangeStart;
+  PHYSICAL_ADDRESS                     *RelocRangeEnd;
 
   ASSERT (ImageContext != NULL);
 
@@ -1043,6 +1045,21 @@ PeCoffLoaderRelocateImage (
     // Run the relocation information and apply the fixups
     //
     FixupData = ImageContext->FixupData;
+    if (FixupData != NULL) {
+      FixupData = ALIGN_POINTER (FixupData, sizeof (PHYSICAL_ADDRESS));
+
+      //
+      // Use the first two UINT64s in the fixup data to keep track of the start
+      // and end of the region that is subject to relocation fixups.
+      //
+      RelocRangeStart = (PHYSICAL_ADDRESS *)FixupData;
+      RelocRangeEnd   = RelocRangeStart + 1;
+      FixupData      += 2 * sizeof (PHYSICAL_ADDRESS);
+
+      *RelocRangeStart = MAX_UINT64;
+      *RelocRangeEnd   = 0;
+    }
+
     while ((UINTN)RelocBase < (UINTN)RelocBaseEnd) {
       Reloc = (UINT16 *)((CHAR8 *)RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
       //
@@ -1070,6 +1087,14 @@ PeCoffLoaderRelocateImage (
         return RETURN_LOAD_ERROR;
       }
 
+      //
+      // Capture this page in the recorded relocation range
+      //
+      if (FixupData != NULL) {
+        *RelocRangeStart = MIN (*RelocRangeStart, (UINTN)FixupBase);
+        *RelocRangeEnd   = MAX (*RelocRangeEnd, (UINTN)FixupBase + SIZE_4KB);
+      }
+
       //
       // Run this relocation record
       //
@@ -1470,6 +1495,9 @@ PeCoffLoaderLoadImage (
   //
   ImageContext->FixupData = NULL;
 
+  // Add two UINT64s at the start to carry the min/max of the relocated range
+  ImageContext->FixupDataSize += 2 * sizeof (PHYSICAL_ADDRESS);
+
   //
   // Load the Codeview information if present
   //
@@ -1824,7 +1852,8 @@ PeCoffLoaderRelocateImageForRuntime (
     // by code will not be fixed up, since that would set them back to
     // defaults.
     //
-    FixupData     = RelocationData;
+    FixupData     = ALIGN_POINTER (RelocationData, sizeof (PHYSICAL_ADDRESS));
+    FixupData    += 2 * sizeof (PHYSICAL_ADDRESS);
     RelocBaseOrig = RelocBase;
     while ((UINTN)RelocBase < (UINTN)RelocBaseEnd) {
       //
@@ -1994,3 +2023,54 @@ PeCoffLoaderUnloadImage (
   PeCoffLoaderUnloadImageExtraAction (ImageContext);
   return RETURN_SUCCESS;
 }
+
+/**
+  Retrieve the range subject to relocation fixups from the recorded fixup data
+  of a runtime image
+
+  @param       ImageBase           The base address of a PE/COFF image that has been loaded
+                                   and relocated into system memory.
+  @param       ImageSize           The size, in bytes, of the PE/COFF image.
+  @param       RelocationData      A pointer to the relocation data that was collected when the
+                                   PE/COFF image was relocated using PeCoffLoaderRelocateImage().
+  @param[out]  RelocationRangeMin  The start of the relocated range.
+  @param[out]  RelocationRangeMax  The end of the relocated range.
+
+**/
+VOID
+EFIAPI
+PeCoffLoaderGetRelocationRange (
+  IN  PHYSICAL_ADDRESS  ImageBase,
+  IN  UINTN             ImageSize,
+  IN  VOID              *RelocationData,
+  OUT PHYSICAL_ADDRESS  *RelocationRangeMin,
+  OUT PHYSICAL_ADDRESS  *RelocationRangeMax
+  )
+{
+  PHYSICAL_ADDRESS  *FixupData;
+
+  if ((RelocationData == NULL) || (ImageBase == 0x0)) {
+    return;
+  }
+
+  FixupData = ALIGN_POINTER (RelocationData, sizeof (PHYSICAL_ADDRESS));
+
+  if ((FixupData[0] == MAX_UINT64) && (FixupData[1] == 0)) {
+    // No fixups recorded
+    *RelocationRangeMin = ImageBase;
+    *RelocationRangeMax = ImageBase;
+    return;
+  }
+
+  if ((FixupData[0] < ImageBase) ||
+      (FixupData[1] > (ImageBase + ImageSize)))
+  {
+    ASSERT (FALSE);
+    *RelocationRangeMin = ImageBase;
+    *RelocationRangeMax = ImageBase + ImageSize;
+    return;
+  }
+
+  *RelocationRangeMin = FixupData[0];
+  *RelocationRangeMax = FixupData[1];
+}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 17/38] MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (15 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 16/38] MdePkg/BasePeCoffLib: Add API to keep track of relocation range Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 18/38] MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch Ard Biesheuvel
                   ` (22 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Currently, the DXE IPL relies on permanent memory being available, but
does not DEPEX on the associated PPI. Instead, it registers for PEIM
shadowing, and only proceeds when running shadowed, and this implies
that permanent memory has been installed.

While PEIM shadowing is typically good for performance, there are
reasons why we might prefer to avoid it, e.g., when running under
virtualization in a mode where the write protection of the ROM is an
advantage from a safety PoV, and where the performance is identical.

This is especially true when code executing from ordinary RAM needs some
additional work to be executable, like when enabling WXN on ARM, which
only permits execution from memory that is mapped read-only.

So permit DXE IPL to run unshadowed, based on the existing PCD that
decides whether or not shadowing is preferred. While making this
behavior depend on this PCD is strictly redundant (as the IPL PEIM will
be shadowed anyway, even if RegisterForShadow() is not called), let's
test it anyway to avoid modifying the behavior on existing platforms.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf |  5 +++-
 MdeModulePkg/Core/DxeIplPeim/DxeLoad.c  | 24 +++++++++++---------
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
index 052ea0ec1a6f..62821477d012 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
@@ -112,6 +112,9 @@ [FeaturePcd.X64]
 [FeaturePcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSupportUefiDecompress ## CONSUMES
 
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdShadowPeimOnBoot            ## CONSUMES
+
 [Pcd.IA32,Pcd.X64]
   gEfiMdeModulePkgTokenSpaceGuid.PcdUse1GPageTable                      ## SOMETIMES_CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask    ## CONSUMES
@@ -128,7 +131,7 @@ [Pcd.IA32,Pcd.X64,Pcd.ARM,Pcd.AARCH64]
   gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy       ## SOMETIMES_CONSUMES
 
 [Depex]
-  gEfiPeiLoadFilePpiGuid AND gEfiPeiMasterBootModePpiGuid
+  gEfiPeiLoadFilePpiGuid AND gEfiPeiMasterBootModePpiGuid AND gEfiPeiMemoryDiscoveredPpiGuid
 
 #
 # [BootMode]
diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c b/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c
index 2c19f1a507ba..228d39a618d3 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeLoad.c
@@ -77,18 +77,20 @@ PeimInitializeDxeIpl (
   BootMode = GetBootModeHob ();
 
   if (BootMode != BOOT_ON_S3_RESUME) {
-    Status = PeiServicesRegisterForShadow (FileHandle);
-    if (Status == EFI_SUCCESS) {
-      //
-      // EFI_SUCESS means it is the first time to call register for shadow.
-      //
-      return Status;
-    }
+    if (PcdGetBool (PcdShadowPeimOnBoot)) {
+      Status = PeiServicesRegisterForShadow (FileHandle);
+      if (Status == EFI_SUCCESS) {
+        //
+        // EFI_SUCESS means it is the first time to call register for shadow.
+        //
+        return Status;
+      }
 
-    //
-    // Ensure that DXE IPL is shadowed to permanent memory.
-    //
-    ASSERT (Status == EFI_ALREADY_STARTED);
+      //
+      // Ensure that DXE IPL is shadowed to permanent memory.
+      //
+      ASSERT (Status == EFI_ALREADY_STARTED);
+    }
 
     //
     // DXE core load requires permanent memory.
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 18/38] MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (16 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 17/38] MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-16 22:20   ` [edk2-devel] " osd
  2023-03-13 17:16 ` [PATCH v5 19/38] MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time Ard Biesheuvel
                   ` (21 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

To permit the platform to adopt a stricter policy when it comes to
memory protections, and map all memory XP by default, add the necessary
handling to the DXE IPL PEIM to ensure that the DXE core code section is
mapped executable before invoking the DXE core.

It is up to the DXE core itself to manage the executable permissions on
other DXE and UEFI drivers and applications that it dispatches.

Note that this requires that the DXE IPL executes non-shadowed from a FV
that is mapped executable.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c | 73 ++++++++++++++++++++
 MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf        |  1 +
 2 files changed, 74 insertions(+)

diff --git a/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c b/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c
index f62b6dcb38a7..c57ffa87e30f 100644
--- a/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c
+++ b/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c
@@ -11,6 +11,73 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
 #include "DxeIpl.h"
 
 #include <Library/ArmMmuLib.h>
+#include <Library/PeCoffLib.h>
+
+/**
+  Discover the code sections of the DXE core, and remap them read-only
+  and executable.
+
+  @param DxeCoreEntryPoint  The entrypoint of the DXE core executable.
+  @param HobList            The list of HOBs passed to the DXE core from PEI.
+**/
+STATIC
+VOID
+RemapDxeCoreCodeReadOnly (
+  IN EFI_PHYSICAL_ADDRESS  DxeCoreEntryPoint,
+  IN EFI_PEI_HOB_POINTERS  HobList
+  )
+{
+  EFI_PEI_HOB_POINTERS                 Hob;
+  PE_COFF_LOADER_IMAGE_CONTEXT         ImageContext;
+  RETURN_STATUS                        Status;
+  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
+  EFI_IMAGE_SECTION_HEADER             *Section;
+  UINTN                                Index;
+
+  ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
+  ImageContext.Handle    = NULL;
+
+  //
+  // Find the module HOB for the DXE core
+  //
+  for (Hob.Raw = HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
+    if ((GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) &&
+        (CompareGuid (&Hob.MemoryAllocation->AllocDescriptor.Name, &gEfiHobMemoryAllocModuleGuid)) &&
+        (Hob.MemoryAllocationModule->EntryPoint == DxeCoreEntryPoint))
+    {
+      ImageContext.Handle = (VOID *)(UINTN)Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress;
+      break;
+    }
+  }
+
+  ASSERT (ImageContext.Handle != NULL);
+
+  Status = PeCoffLoaderGetImageInfo (&ImageContext);
+  ASSERT_RETURN_ERROR (Status);
+
+  Hdr.Union = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)ImageContext.Handle +
+                                                  ImageContext.PeCoffHeaderOffset);
+  ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
+
+  Section = (EFI_IMAGE_SECTION_HEADER *)((UINT8 *)Hdr.Union + sizeof (UINT32) +
+                                         sizeof (EFI_IMAGE_FILE_HEADER) +
+                                         Hdr.Pe32->FileHeader.SizeOfOptionalHeader
+                                         );
+
+  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
+    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
+      ArmSetMemoryRegionReadOnly (
+        (UINTN)((UINT8 *)ImageContext.Handle + Section[Index].VirtualAddress),
+        Section[Index].Misc.VirtualSize
+        );
+
+      ArmClearMemoryRegionNoExec (
+        (UINTN)((UINT8 *)ImageContext.Handle + Section[Index].VirtualAddress),
+        Section[Index].Misc.VirtualSize
+        );
+    }
+  }
+}
 
 /**
    Transfers control to DxeCore.
@@ -33,6 +100,12 @@ HandOffToDxeCore (
   VOID        *TopOfStack;
   EFI_STATUS  Status;
 
+  //
+  // DRAM may be mapped with non-executable permissions by default, so
+  // we'll need to map the DXE core code region executable explicitly.
+  //
+  RemapDxeCoreCodeReadOnly (DxeCoreEntryPoint, HobList);
+
   //
   // Allocate 128KB for the Stack
   //
diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
index 62821477d012..d85ca79dc0c3 100644
--- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
@@ -82,6 +82,7 @@ [LibraryClasses]
 
 [LibraryClasses.ARM, LibraryClasses.AARCH64]
   ArmMmuLib
+  PeCoffLib
 
 [Ppis]
   gEfiDxeIplPpiGuid                      ## PRODUCES
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 19/38] MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (17 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 18/38] MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 20/38] MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback Ard Biesheuvel
                   ` (20 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Instead of remapping all DXE runtime drivers with read-write-execute
permissions entirely when ExitBootServices() is called, remap only the
parts of those images that require writable access for applying
relocation fixups at SetVirtualAddressMap() time.

As illustrated below, this greatly reduces the footprint of those
regions, which is important for safe execution. And given that the most
important ISAs and toolchains split executable code from relocatable
quantities, the remapped pages in question are generally not the ones
that contain code as well.

On a ArmVirtQemu build, the footprint of those RWX pages is shown below.

As future work, we might investigate whether we can find a way to
guarantee in general that images are built in a way where executable
code and relocatable data never share a 4 KiB page, in which case we
could apply EFI_MEMORY_XP permissions here instead of allowing RWX.

Before:

  SetUefiImageMemoryAttributes - 0x0000000047600000 - 0x0000000000050000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000044290000 - 0x0000000000050000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000044230000 - 0x0000000000050000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x00000000441D0000 - 0x0000000000050000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x00000000440D0000 - 0x0000000000050000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000043F90000 - 0x0000000000040000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000043F40000 - 0x0000000000040000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000043EF0000 - 0x0000000000040000 (0x0000000000000008)

After:

  SetUefiImageMemoryAttributes - 0x0000000047630000 - 0x0000000000001000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x00000000442C0000 - 0x0000000000001000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000044260000 - 0x0000000000001000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000044200000 - 0x0000000000001000 (0x0000000000000008)
  SetUefiImageMemoryAttributes - 0x0000000044100000 - 0x0000000000001000 (0x0000000000000008)

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 5a82eee80781..3e6f2b4e74cc 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -1060,6 +1060,8 @@ MemoryProtectionExitBootServicesCallback (
 {
   EFI_RUNTIME_IMAGE_ENTRY  *RuntimeImage;
   LIST_ENTRY               *Link;
+  PHYSICAL_ADDRESS         RelocationRangeStart;
+  PHYSICAL_ADDRESS         RelocationRangeEnd;
 
   //
   // We need remove the RT protection, because RT relocation need write code segment
@@ -1073,7 +1075,22 @@ MemoryProtectionExitBootServicesCallback (
   if (mImageProtectionPolicy != 0) {
     for (Link = gRuntime->ImageHead.ForwardLink; Link != &gRuntime->ImageHead; Link = Link->ForwardLink) {
       RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);
-      SetUefiImageMemoryAttributes ((UINT64)(UINTN)RuntimeImage->ImageBase, ALIGN_VALUE (RuntimeImage->ImageSize, EFI_PAGE_SIZE), 0);
+
+      PeCoffLoaderGetRelocationRange (
+        (PHYSICAL_ADDRESS)(UINTN)RuntimeImage->ImageBase,
+        (UINTN)ALIGN_VALUE (RuntimeImage->ImageSize, EFI_PAGE_SIZE),
+        RuntimeImage->RelocationData,
+        &RelocationRangeStart,
+        &RelocationRangeEnd
+        );
+
+      if (RelocationRangeEnd > RelocationRangeStart) {
+        SetUefiImageMemoryAttributes (
+          RelocationRangeStart,
+          (UINTN)(RelocationRangeEnd - RelocationRangeStart),
+          0
+          );
+      }
     }
   }
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 20/38] MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (18 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 19/38] MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib Ard Biesheuvel
                   ` (19 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Store the address of the SetMemoryAttributes() member of the CPU arch
protocol in a global variable, and invoke it via this variable. This
by itself should have not result in functional changes, but it permits
platforms to provide an preliminary implementation of this member at
link time, allowing the DXE core to enforce strict memory permissions
even before dispatching the CPU arch protocol driver itself.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 3e6f2b4e74cc..8df3e881c5c4 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -66,6 +66,8 @@ extern LIST_ENTRY  mGcdMemorySpaceMap;
 
 STATIC LIST_ENTRY  mProtectedImageRecordList;
 
+EFI_CPU_SET_MEMORY_ATTRIBUTES  gCpuSetMemoryAttributes;
+
 /**
   Sort code section in image record, based upon CodeSegmentBase from low to high.
 
@@ -224,8 +226,8 @@ SetUefiImageMemoryAttributes (
 
   DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes));
 
-  ASSERT (gCpu != NULL);
-  gCpu->SetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);
+  ASSERT (gCpuSetMemoryAttributes != NULL);
+  gCpuSetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);
 }
 
 /**
@@ -408,7 +410,7 @@ ProtectUefiImage (
   DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));
   DEBUG ((DEBUG_INFO, "  - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));
 
-  if (gCpu == NULL) {
+  if (gCpuSetMemoryAttributes == NULL) {
     return;
   }
 
@@ -995,6 +997,8 @@ MemoryProtectionCpuArchProtocolNotify (
     goto Done;
   }
 
+  gCpuSetMemoryAttributes = gCpu->SetMemoryAttributes;
+
   //
   // Apply the memory protection policy on non-BScode/RTcode regions.
   //
@@ -1278,7 +1282,7 @@ ApplyMemoryProtectionPolicy (
   // permission attributes, and it is the job of the driver that installs this
   // protocol to set the permissions on existing allocations.
   //
-  if (gCpu == NULL) {
+  if (gCpuSetMemoryAttributes == NULL) {
     return EFI_SUCCESS;
   }
 
@@ -1318,5 +1322,5 @@ ApplyMemoryProtectionPolicy (
   //
   NewAttributes = GetPermissionAttributeForMemoryType (NewType);
 
-  return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
+  return gCpuSetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
 }
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (19 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 20/38] MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-16 13:27   ` [edk2-devel] " Leif Lindholm
  2023-03-13 17:16 ` [PATCH v5 22/38] MdeModulePkg/PcdPeim: Permit unshadowed execution Ard Biesheuvel
                   ` (18 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Implement the ARM version of a NULL class library that can be overlaid
on top of the DXE core to equip it right from its launch with an
implementation of the CPU arch protocol member that sets type and
permission attributes on memory regions.

This bridges the gap between dispatch of DXE core and dispatch of the
DXE driver that implements the CPU arch protocol, removing the need to
rely on memory mappings that are writable and executable at the same
time.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmPkg/ArmPkg.dsc                                                  |  1 +
 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c   | 78 ++++++++++++++++++++
 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf | 28 +++++++
 3 files changed, 107 insertions(+)

diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
index 3fb95d1951a9..43eb0f4f463e 100644
--- a/ArmPkg/ArmPkg.dsc
+++ b/ArmPkg/ArmPkg.dsc
@@ -119,6 +119,7 @@ [Components.common]
   ArmPkg/Library/ArmPsciResetSystemLib/ArmPsciResetSystemLib.inf
   ArmPkg/Library/ArmExceptionLib/ArmExceptionLib.inf
   ArmPkg/Library/ArmExceptionLib/ArmRelocateExceptionLib.inf
+  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
 
   ArmPkg/Drivers/CpuDxe/CpuDxe.inf
   ArmPkg/Drivers/CpuPei/CpuPei.inf
diff --git a/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
new file mode 100644
index 000000000000..866dbbdaa7d5
--- /dev/null
+++ b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
@@ -0,0 +1,78 @@
+/** @file
+  Overlay implementation of DXE core gCpuSetMemoryAttributes for ARM.
+
+  Copyright (c) 2023, Google LLC. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+
+#include <Library/ArmMmuLib.h>
+#include <Library/DebugLib.h>
+#include <Protocol/Cpu.h>
+
+extern EFI_CPU_SET_MEMORY_ATTRIBUTES  gCpuSetMemoryAttributes;
+
+STATIC UINTN  mRecursionLevel;
+
+/**
+  Clone of CPU_ARCH_PROTOCOL::SetMemoryAttributes() which is made available to
+  the DXE core by NULL library class resolution, so that it can manage page
+  permissions right from the start.
+
+  @param  This                  CPU arch protocol pointer, should be NULL.
+  @param  BaseAddress           Start address of the region.
+  @param  Length                Size of the region, in bytes.
+  @param  Attributes            Attributes to set on the region.
+
+  @retval EFI_SUCCESS           Operation completed successfully.
+  @retval EFI_OUT_OF_RESOURCES  Operation failed due to lack of memory.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EarlyArmSetMemoryAttributes (
+  IN  EFI_CPU_ARCH_PROTOCOL  *This,
+  IN  EFI_PHYSICAL_ADDRESS   BaseAddress,
+  IN  UINT64                 Length,
+  IN  UINT64                 Attributes
+  )
+{
+  EFI_STATUS  Status;
+
+  // There are cases where the use of strict memory permissions may trigger
+  // unbounded recursion in the page table code. This happens when setting
+  // memory permissions results in a page table split and therefore a page
+  // allocation, which could trigger a recursive invocation of this function.
+  ASSERT (mRecursionLevel < 2);
+
+  mRecursionLevel++;
+
+  Status = ArmSetMemoryAttributes (
+             BaseAddress,
+             Length,
+             Attributes
+             );
+
+  mRecursionLevel--;
+  return Status;
+}
+
+/**
+  Library constructor.
+
+  @retval RETURN_SUCCESS   Operation successful.
+
+**/
+RETURN_STATUS
+EFIAPI
+ArmSetMemoryOverrideLibConstructor (
+  VOID
+  )
+{
+  gCpuSetMemoryAttributes = EarlyArmSetMemoryAttributes;
+
+  return RETURN_SUCCESS;
+}
diff --git a/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
new file mode 100644
index 000000000000..2dc9d42d09bd
--- /dev/null
+++ b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
@@ -0,0 +1,28 @@
+## @file
+#  Overlay implementation of DXE core gCpuSetMemoryAttributes for ARM.
+#
+#  Copyright (c) 2023, Google LLC. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = ArmSetMemoryOverrideLib
+  FILE_GUID                      = 849a43c0-6ad9-428e-8a5a-e090f7853bd3
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = NULL|DXE_CORE
+  CONSTRUCTOR                    = ArmSetMemoryOverrideLibConstructor
+
+[Sources.common]
+  ArmSetMemoryOverrideLib.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  ArmMmuLib
+  DebugLib
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 22/38] MdeModulePkg/PcdPeim: Permit unshadowed execution
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (20 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-13 17:16 ` [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution Ard Biesheuvel
                   ` (17 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

PEIM shadowing is optional, but the PCD PEIM does so explicitly,
seemingly without a functional need. So make this behavior dependent on
the existing PCD, which is generally (and by default) set to TRUE,
whereas some systems (such as ARM virtual machines) may prefer to
disable shadowing.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Universal/PCD/Pei/Pcd.c   | 112 ++++++++++----------
 MdeModulePkg/Universal/PCD/Pei/Pcd.inf |   1 +
 2 files changed, 58 insertions(+), 55 deletions(-)

diff --git a/MdeModulePkg/Universal/PCD/Pei/Pcd.c b/MdeModulePkg/Universal/PCD/Pei/Pcd.c
index c51ac96a32e3..632254f1c5fa 100644
--- a/MdeModulePkg/Universal/PCD/Pei/Pcd.c
+++ b/MdeModulePkg/Universal/PCD/Pei/Pcd.c
@@ -346,73 +346,75 @@ PcdPeimInit (
 {
   EFI_STATUS  Status;
 
-  Status = PeiServicesRegisterForShadow (FileHandle);
-  if (Status == EFI_ALREADY_STARTED) {
-    //
-    // This is now starting in memory, the second time starting.
-    //
-    EFI_PEI_PPI_DESCRIPTOR  *OldPpiList;
-    EFI_PEI_PPI_DESCRIPTOR  *OldPpiList2;
-    VOID                    *Ppi;
-    VOID                    *Ppi2;
+  if (PcdGetBool (PcdShadowPeimOnBoot)) {
+    Status = PeiServicesRegisterForShadow (FileHandle);
+    if (Status == EFI_ALREADY_STARTED) {
+      //
+      // This is now starting in memory, the second time starting.
+      //
+      EFI_PEI_PPI_DESCRIPTOR  *OldPpiList;
+      EFI_PEI_PPI_DESCRIPTOR  *OldPpiList2;
+      VOID                    *Ppi;
+      VOID                    *Ppi2;
 
-    OldPpiList = NULL;
-    Status     = PeiServicesLocatePpi (
-                   &gPcdPpiGuid,
-                   0,
-                   &OldPpiList,
-                   &Ppi
-                   );
-    ASSERT_EFI_ERROR (Status);
-
-    if (OldPpiList != NULL) {
-      Status = PeiServicesReInstallPpi (OldPpiList, &mPpiList[0]);
+      OldPpiList = NULL;
+      Status     = PeiServicesLocatePpi (
+                     &gPcdPpiGuid,
+                     0,
+                     &OldPpiList,
+                     &Ppi
+                     );
       ASSERT_EFI_ERROR (Status);
-    }
 
-    OldPpiList2 = NULL;
-    Status      = PeiServicesLocatePpi (
-                    &gGetPcdInfoPpiGuid,
-                    0,
-                    &OldPpiList2,
-                    &Ppi2
-                    );
-    ASSERT_EFI_ERROR (Status);
+      if (OldPpiList != NULL) {
+        Status = PeiServicesReInstallPpi (OldPpiList, &mPpiList[0]);
+        ASSERT_EFI_ERROR (Status);
+      }
 
-    if (OldPpiList2 != NULL) {
-      Status = PeiServicesReInstallPpi (OldPpiList2, &mPpiList2[0]);
+      OldPpiList2 = NULL;
+      Status      = PeiServicesLocatePpi (
+                      &gGetPcdInfoPpiGuid,
+                      0,
+                      &OldPpiList2,
+                      &Ppi2
+                      );
       ASSERT_EFI_ERROR (Status);
-    }
 
-    OldPpiList = NULL;
-    Status     = PeiServicesLocatePpi (
-                   &gEfiPeiPcdPpiGuid,
-                   0,
-                   &OldPpiList,
-                   &Ppi
-                   );
-    ASSERT_EFI_ERROR (Status);
+      if (OldPpiList2 != NULL) {
+        Status = PeiServicesReInstallPpi (OldPpiList2, &mPpiList2[0]);
+        ASSERT_EFI_ERROR (Status);
+      }
 
-    if (OldPpiList != NULL) {
-      Status = PeiServicesReInstallPpi (OldPpiList, &mPpiList[1]);
+      OldPpiList = NULL;
+      Status     = PeiServicesLocatePpi (
+                     &gEfiPeiPcdPpiGuid,
+                     0,
+                     &OldPpiList,
+                     &Ppi
+                     );
       ASSERT_EFI_ERROR (Status);
-    }
 
-    OldPpiList2 = NULL;
-    Status      = PeiServicesLocatePpi (
-                    &gEfiGetPcdInfoPpiGuid,
-                    0,
-                    &OldPpiList2,
-                    &Ppi2
-                    );
-    ASSERT_EFI_ERROR (Status);
+      if (OldPpiList != NULL) {
+        Status = PeiServicesReInstallPpi (OldPpiList, &mPpiList[1]);
+        ASSERT_EFI_ERROR (Status);
+      }
 
-    if (OldPpiList2 != NULL) {
-      Status = PeiServicesReInstallPpi (OldPpiList2, &mPpiList2[1]);
+      OldPpiList2 = NULL;
+      Status      = PeiServicesLocatePpi (
+                      &gEfiGetPcdInfoPpiGuid,
+                      0,
+                      &OldPpiList2,
+                      &Ppi2
+                      );
       ASSERT_EFI_ERROR (Status);
-    }
 
-    return Status;
+      if (OldPpiList2 != NULL) {
+        Status = PeiServicesReInstallPpi (OldPpiList2, &mPpiList2[1]);
+        ASSERT_EFI_ERROR (Status);
+      }
+
+      return Status;
+    }
   }
 
   BuildPcdDatabase (FileHandle);
diff --git a/MdeModulePkg/Universal/PCD/Pei/Pcd.inf b/MdeModulePkg/Universal/PCD/Pei/Pcd.inf
index 7152a7d53b7d..a820ace70ec2 100644
--- a/MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+++ b/MdeModulePkg/Universal/PCD/Pei/Pcd.inf
@@ -343,6 +343,7 @@ [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdMaxPeiPcdCallBackNumberPerPcdEntry ## SOMETIMES_CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdNvStoreDefaultValueBuffer ## SOMETIMES_CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdSetNvStoreDefaultId       ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdShadowPeimOnBoot          ## CONSUMES
 
 [Depex]
   TRUE
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (21 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 22/38] MdeModulePkg/PcdPeim: Permit unshadowed execution Ard Biesheuvel
@ 2023-03-13 17:16 ` Ard Biesheuvel
  2023-03-16 13:33   ` Leif Lindholm
  2023-03-13 17:17 ` [PATCH v5 24/38] ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default Ard Biesheuvel
                   ` (16 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:16 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Deal with DRAM memory potentially being mapped with non-executable
permissions, by mapping the DXE core code sections explicitly before
launch.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 EmbeddedPkg/Include/Library/PrePiLib.h          | 16 ------
 EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c | 51 ++++++++++++++++++++
 EmbeddedPkg/Library/PrePiLib/PrePi.h            | 13 +++++
 EmbeddedPkg/Library/PrePiLib/PrePiLib.c         |  4 ++
 EmbeddedPkg/Library/PrePiLib/PrePiLib.inf       | 12 +++++
 EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c | 23 +++++++++
 6 files changed, 103 insertions(+), 16 deletions(-)

diff --git a/EmbeddedPkg/Include/Library/PrePiLib.h b/EmbeddedPkg/Include/Library/PrePiLib.h
index 93a9115eac2d..14f2bbc38dae 100644
--- a/EmbeddedPkg/Include/Library/PrePiLib.h
+++ b/EmbeddedPkg/Include/Library/PrePiLib.h
@@ -758,22 +758,6 @@ AllocateAlignedPages (
   IN UINTN  Alignment
   );
 
-EFI_STATUS
-EFIAPI
-LoadPeCoffImage (
-  IN  VOID                  *PeCoffImage,
-  OUT EFI_PHYSICAL_ADDRESS  *ImageAddress,
-  OUT UINT64                *ImageSize,
-  OUT EFI_PHYSICAL_ADDRESS  *EntryPoint
-  );
-
-EFI_STATUS
-EFIAPI
-LoadDxeCoreFromFfsFile (
-  IN EFI_PEI_FILE_HANDLE  FileHandle,
-  IN UINTN                StackSize
-  );
-
 EFI_STATUS
 EFIAPI
 LoadDxeCoreFromFv (
diff --git a/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
new file mode 100644
index 000000000000..40d4ed9d77bd
--- /dev/null
+++ b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
@@ -0,0 +1,51 @@
+/** @file
+  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PrePi.h"
+
+#include <Library/ArmMmuLib.h>
+
+/**
+  Remap the code section of the DXE core with the read-only and executable
+  permissions.
+
+  @param  ImageContext    The image context describing the loaded PE/COFF image
+
+**/
+VOID
+EFIAPI
+RemapDxeCore (
+  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
+  )
+{
+  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
+  EFI_IMAGE_SECTION_HEADER             *Section;
+  UINTN                                Index;
+
+  Hdr.Union = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)ImageContext->Handle +
+                                                  ImageContext->PeCoffHeaderOffset);
+  ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
+
+  Section = (EFI_IMAGE_SECTION_HEADER *)((UINT8 *)Hdr.Union + sizeof (UINT32) +
+                                         sizeof (EFI_IMAGE_FILE_HEADER) +
+                                         Hdr.Pe32->FileHeader.SizeOfOptionalHeader
+                                         );
+
+  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
+    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
+      ArmSetMemoryRegionReadOnly (
+        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
+        Section[Index].Misc.VirtualSize
+        );
+
+      ArmClearMemoryRegionNoExec (
+        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
+        Section[Index].Misc.VirtualSize
+        );
+    }
+  }
+}
diff --git a/EmbeddedPkg/Library/PrePiLib/PrePi.h b/EmbeddedPkg/Library/PrePiLib/PrePi.h
index a00c946512f4..a0f8837d1d37 100644
--- a/EmbeddedPkg/Library/PrePiLib/PrePi.h
+++ b/EmbeddedPkg/Library/PrePiLib/PrePi.h
@@ -37,4 +37,17 @@
 #define GET_GUID_HOB_DATA(GuidHob)       ((VOID *) (((UINT8 *) &((GuidHob)->Name)) + sizeof (EFI_GUID)))
 #define GET_GUID_HOB_DATA_SIZE(GuidHob)  (((GuidHob)->Header).HobLength - sizeof (EFI_HOB_GUID_TYPE))
 
+/**
+  Remap the code section of the DXE core with the read-only and executable
+  permissions.
+
+  @param  ImageContext    The image context describing the loaded PE/COFF image
+
+**/
+VOID
+EFIAPI
+RemapDxeCore (
+  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
+  );
+
 #endif
diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
index 3cf866dab248..188ad5c518a8 100644
--- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
+++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
@@ -54,6 +54,7 @@ AllocateCodePages (
   return NULL;
 }
 
+STATIC
 EFI_STATUS
 EFIAPI
 LoadPeCoffImage (
@@ -105,6 +106,8 @@ LoadPeCoffImage (
   //
   InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize);
 
+  RemapDxeCore (&ImageContext);
+
   return Status;
 }
 
@@ -114,6 +117,7 @@ VOID
   IN  VOID *HobStart
   );
 
+STATIC
 EFI_STATUS
 EFIAPI
 LoadDxeCoreFromFfsFile (
diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
index 090bfe888f52..2df5928c51d5 100644
--- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
+++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
@@ -31,11 +31,20 @@ [Sources.common]
   FwVol.c
   PrePiLib.c
 
+[Sources.X64, Sources.IA32]
+  X86/RemapDxeCore.c
+
+[Sources.AARCH64, Sources.ARM]
+  Arm/RemapDxeCore.c
+
 [Packages]
   MdePkg/MdePkg.dec
   EmbeddedPkg/EmbeddedPkg.dec
   MdeModulePkg/MdeModulePkg.dec
 
+[Packages.ARM, Packages.AARCH64]
+  ArmPkg/ArmPkg.dec
+
 [LibraryClasses]
   BaseLib
   DebugLib
@@ -50,6 +59,9 @@ [LibraryClasses]
   PerformanceLib
   HobLib
 
+[LibraryClasses.ARM, LibraryClasses.AARCH64]
+  ArmMmuLib
+
 [Guids]
   gEfiMemoryTypeInformationGuid
 
diff --git a/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
new file mode 100644
index 000000000000..1899c050fdec
--- /dev/null
+++ b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
@@ -0,0 +1,23 @@
+/** @file
+  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "PrePi.h"
+
+/**
+  Remap the code section of the DXE core with the read-only and executable
+  permissions.
+
+  @param  ImageContext    The image context describing the loaded PE/COFF image
+
+**/
+VOID
+EFIAPI
+RemapDxeCore (
+  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
+  )
+{
+}
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 24/38] ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (22 preceding siblings ...)
  2023-03-13 17:16 ` [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 25/38] ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs Ard Biesheuvel
                   ` (15 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Now that all the plumbing is in place, we can switch to a default policy
of XP for all memory mappings straight out of reset. This reduces the
risk of running with memory ranges mapped as both writable and
executable at the same time.

Note this this requires the overlay library to be added to the DXE core,
as otherwise, it will not be able to dispatch the CPU arch protocol DXE
driver (or any other DXE driver for that matter), as it would lack the
ability to grant executable permissions to those executables.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmVirtPkg/ArmVirtQemu.dsc                                 | 1 +
 ArmVirtPkg/ArmVirtQemuKernel.dsc                           | 1 +
 ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c | 2 +-
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index 72a0cacab4a8..b9c244f16e04 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -372,6 +372,7 @@ [Components.common]
   #
   MdeModulePkg/Core/Dxe/DxeMain.inf {
     <LibraryClasses>
+      NULL|ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
       NULL|MdeModulePkg/Library/DxeCrc32GuidedSectionExtractLib/DxeCrc32GuidedSectionExtractLib.inf
       DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
   }
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index 3cb9120e4e10..c09755e6e1b9 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -278,6 +278,7 @@ [Components.common]
   #
   MdeModulePkg/Core/Dxe/DxeMain.inf {
     <LibraryClasses>
+      NULL|ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
       NULL|MdeModulePkg/Library/DxeCrc32GuidedSectionExtractLib/DxeCrc32GuidedSectionExtractLib.inf
       DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
   }
diff --git a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
index 9cf43f06c073..aa083cec2082 100644
--- a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
@@ -91,7 +91,7 @@ ArmVirtGetMemoryMap (
   VirtualMemoryTable[0].PhysicalBase = PcdGet64 (PcdSystemMemoryBase);
   VirtualMemoryTable[0].VirtualBase  = VirtualMemoryTable[0].PhysicalBase;
   VirtualMemoryTable[0].Length       = *(UINT64 *)GET_GUID_HOB_DATA (MemorySizeHob);
-  VirtualMemoryTable[0].Attributes   = ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK;
+  VirtualMemoryTable[0].Attributes   = ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_XP;
 
   DEBUG ((
     DEBUG_INFO,
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 25/38] ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (23 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 24/38] ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 26/38] ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code flash Ard Biesheuvel
                   ` (14 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

The PEI flavor of the ArmMmuLib will install a HOB that exposes its
implementation of the special helper routine that is used to update live
entries, so that other instantiations of ArmMmuLib can invoke it. This
is needed to ensure that splitting page tables using break-before-make
(BBM) does not unmap the code that is performing the split.

However, the BASE variety of ArmMmuLib discovers the HOB and sets a
global pointer to refer to it, which is not possible in PEIMs, and so
all PEIMs must use the PEI variety of this library if one does.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmVirtPkg/ArmVirtQemu.dsc | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index b9c244f16e04..7f79a2b5fa6b 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -107,6 +107,9 @@ [LibraryClasses.common.PEIM]
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibDTpm/Tpm2DeviceLibDTpm.inf
 !endif
 
+[LibraryClasses.AARCH64.PEIM]
+  ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf
+
 [LibraryClasses.common.DXE_DRIVER]
   ReportStatusCodeLib|MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
 
@@ -333,12 +336,7 @@ [Components.common]
   ArmPlatformPkg/PrePeiCore/PrePeiCoreUniCore.inf
   MdeModulePkg/Core/Pei/PeiMain.inf
   ArmPlatformPkg/PlatformPei/PlatformPeim.inf
-  ArmVirtPkg/MemoryInitPei/MemoryInitPeim.inf {
-    <LibraryClasses>
-!if $(ARCH) == AARCH64
-      ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf
-!endif
-  }
+  ArmVirtPkg/MemoryInitPei/MemoryInitPeim.inf
   ArmPkg/Drivers/CpuPei/CpuPei.inf
 
 !if $(TPM2_ENABLE) == TRUE
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 26/38] ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code flash
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (24 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 25/38] ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data Ard Biesheuvel
                   ` (13 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Map the code flash with read-only attributes so we can execute from it
even under a memory protection regime that enables WXN, making all
writable memory regions non-executable by default.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
index aa083cec2082..a5324b1e4eed 100644
--- a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
+++ b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c
@@ -115,7 +115,7 @@ ArmVirtGetMemoryMap (
   VirtualMemoryTable[2].PhysicalBase = PcdGet64 (PcdFvBaseAddress);
   VirtualMemoryTable[2].VirtualBase  = VirtualMemoryTable[2].PhysicalBase;
   VirtualMemoryTable[2].Length       = FixedPcdGet32 (PcdFvSize);
-  VirtualMemoryTable[2].Attributes   = ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK;
+  VirtualMemoryTable[2].Attributes   = ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_RO;
 
   // End of Table
   ZeroMem (&VirtualMemoryTable[3], sizeof (ARM_MEMORY_REGION_DESCRIPTOR));
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (25 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 26/38] ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code flash Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-16 13:46   ` [edk2-devel] " Leif Lindholm
  2023-03-13 17:17 ` [PATCH v5 28/38] ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory permissions Ard Biesheuvel
                   ` (12 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

The AArch64 ARM architecture supports a hardware enforcement mode for
mutual exclusion between code and data: any page that is mapped writable
is implicitly non-executable as well.

This means that remapping part of a runtime image for reapplying
relocation fixups may result in any code sharing the same page to lose
its executable permissions.

Let's avoid this, by moving all quantities that are subject to
relocation fixups to a separate page if the build is using 64k section
alignment, which is only the case when building a runtime driver for
AArch64.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 BaseTools/Scripts/GccBase.lds | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/BaseTools/Scripts/GccBase.lds b/BaseTools/Scripts/GccBase.lds
index 83cebd29d599..63e097e0727c 100644
--- a/BaseTools/Scripts/GccBase.lds
+++ b/BaseTools/Scripts/GccBase.lds
@@ -21,9 +21,8 @@ SECTIONS {
   . = PECOFF_HEADER_SIZE;
 
   .text : ALIGN(CONSTANT(COMMONPAGESIZE)) {
-    *(.text .text.* .stub .gnu.linkonce.t.*)
+    *(.text .text.* .stub .gnu.linkonce.t.* .plt)
     *(.rodata .rodata.* .gnu.linkonce.r.*)
-    *(.got .got.*)
 
     /*
      * The contents of AutoGen.c files are mostly constant from the POV of the
@@ -34,6 +33,16 @@ SECTIONS {
      * emitted GUIDs here.
      */
     *:AutoGen.obj(.data.g*Guid)
+
+    /*
+     * AArch64 runtime drivers use 64k alignment, and may run in a mode where
+     * mutual exclusion of RO and XP mappings are hardware enforced. In such
+     * cases, the input sections below, which carry any quantities that are
+     * subject to relocation fixups at runtime, must not share a 4 KiB page
+     * with any code content.
+     */
+    . = ALIGN(CONSTANT(COMMONPAGESIZE) > 0x1000 ? 0x1000 : 0x20);
+    *(.got .got.* .data.rel.ro)
   }
 
   /*
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 28/38] ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory permissions
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (26 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 29/38] MdePkg/PeCoffLib: Capture DLL characteristics field in image context Ard Biesheuvel
                   ` (11 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Enable the WXN system control bit straight out of reset when running in
EL1 with the initial ID map from flash. This setting will be inherited
by the page table code after it sets up the permanent boot time page
tables, resulting in all memory mappings that are not explicitly mapped
as read-only to be non-executable.

Note that this requires runtime drivers to be built with position
independent codegen, to ensure that all absolute symbol references are
moved into a separate section in the binary. Otherwise, unmapping the
pages that are subject to relocation fixups at runtime (during the
invocation of SetVirtualAddressMap()) could result in code mappings
losing their executable permissions.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 ArmVirtPkg/ArmVirt.dsc.inc                                        | 1 +
 ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index 5b18184be263..928dd6330edb 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -31,6 +31,7 @@ [BuildOptions.common.EDKII.DXE_CORE,BuildOptions.common.EDKII.DXE_DRIVER,BuildOp
 
 [BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
   GCC:*_*_ARM_DLINK_FLAGS = -z common-page-size=0x1000
+  GCC:*_*_AARCH64_CC_FLAGS = -fpie
   GCC:*_*_AARCH64_DLINK_FLAGS = -z common-page-size=0x10000
 
 [LibraryClasses.common]
diff --git a/ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S b/ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S
index 5ac7c732f6ec..51c089a45ffc 100644
--- a/ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S
+++ b/ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S
@@ -38,7 +38,7 @@
  .set    SCTLR_EL1_ITD,       0x1 << 7
  .set    SCTLR_EL1_RES1,      (0x1 << 11) | (0x1 << 20) | (0x1 << 22) | (0x1 << 28) | (0x1 << 29)
  .set    sctlrval, SCTLR_ELx_M | SCTLR_ELx_C | SCTLR_ELx_SA | SCTLR_EL1_ITD | SCTLR_EL1_SED
- .set    sctlrval, sctlrval | SCTLR_ELx_I | SCTLR_EL1_SPAN | SCTLR_EL1_RES1
+ .set    sctlrval, sctlrval | SCTLR_ELx_I | SCTLR_EL1_SPAN | SCTLR_EL1_RES1 | SCTLR_EL1_WXN
 
 
 ASM_FUNC(ArmPlatformPeiBootAction)
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 29/38] MdePkg/PeCoffLib: Capture DLL characteristics field in image context
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (27 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 28/38] ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory permissions Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 30/38] MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics Ard Biesheuvel
                   ` (10 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

When loading a PE/COFF image, capture the DLL characteristics field of
the header into our image context structure so we can refer to it when
mapping the image.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdePkg/Include/Library/PeCoffLib.h        |  4 ++++
 MdePkg/Library/BasePeCoffLib/BasePeCoff.c | 23 +++++++++++---------
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/MdePkg/Include/Library/PeCoffLib.h b/MdePkg/Include/Library/PeCoffLib.h
index df2f7f5e5961..cb48b4a8f85c 100644
--- a/MdePkg/Include/Library/PeCoffLib.h
+++ b/MdePkg/Include/Library/PeCoffLib.h
@@ -171,6 +171,10 @@ typedef struct {
   ///
   UINT16                      ImageType;
   ///
+  /// Set by PeCoffLoaderGetImageInfo() to the DLL flags stored in the PE/COFF header.
+  ///
+  UINT16                      DllCharacteristics;
+  ///
   /// Set by PeCoffLoaderGetImageInfo() to TRUE if the PE/COFF image does not contain
   /// relocation information.
   ///
diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
index 31e1f2035963..fb6847e62a8d 100644
--- a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
+++ b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
@@ -308,10 +308,11 @@ PeCoffLoaderGetPeHeader (
       //
       // Use PE32 offset
       //
-      ImageContext->ImageType        = Hdr.Pe32->OptionalHeader.Subsystem;
-      ImageContext->ImageSize        = (UINT64)Hdr.Pe32->OptionalHeader.SizeOfImage;
-      ImageContext->SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
-      ImageContext->SizeOfHeaders    = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
+      ImageContext->ImageType          = Hdr.Pe32->OptionalHeader.Subsystem;
+      ImageContext->ImageSize          = (UINT64)Hdr.Pe32->OptionalHeader.SizeOfImage;
+      ImageContext->SectionAlignment   = Hdr.Pe32->OptionalHeader.SectionAlignment;
+      ImageContext->SizeOfHeaders      = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
+      ImageContext->DllCharacteristics = Hdr.Pe32->OptionalHeader.DllCharacteristics;
     } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
       //
       // 1. Check FileHeader.NumberOfRvaAndSizes filed.
@@ -429,10 +430,11 @@ PeCoffLoaderGetPeHeader (
       //
       // Use PE32+ offset
       //
-      ImageContext->ImageType        = Hdr.Pe32Plus->OptionalHeader.Subsystem;
-      ImageContext->ImageSize        = (UINT64)Hdr.Pe32Plus->OptionalHeader.SizeOfImage;
-      ImageContext->SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
-      ImageContext->SizeOfHeaders    = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;
+      ImageContext->ImageType          = Hdr.Pe32Plus->OptionalHeader.Subsystem;
+      ImageContext->ImageSize          = (UINT64)Hdr.Pe32Plus->OptionalHeader.SizeOfImage;
+      ImageContext->SectionAlignment   = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
+      ImageContext->SizeOfHeaders      = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders;
+      ImageContext->DllCharacteristics = Hdr.Pe32Plus->OptionalHeader.DllCharacteristics;
     } else {
       ImageContext->ImageError = IMAGE_ERROR_INVALID_MACHINE_TYPE;
       return RETURN_UNSUPPORTED;
@@ -545,8 +547,9 @@ PeCoffLoaderGetPeHeader (
   Retrieves information about a PE/COFF image.
 
   Computes the PeCoffHeaderOffset, IsTeImage, ImageType, ImageAddress, ImageSize,
-  DestinationAddress, RelocationsStripped, SectionAlignment, SizeOfHeaders, and
-  DebugDirectoryEntryRva fields of the ImageContext structure.
+  DestinationAddress, RelocationsStripped, SectionAlignment, SizeOfHeaders,
+  DllCharacteristics, and DebugDirectoryEntryRva fields of the ImageContext
+  structure.
   If ImageContext is NULL, then return RETURN_INVALID_PARAMETER.
   If the PE/COFF image accessed through the ImageRead service in the ImageContext
   structure is not a supported PE/COFF image type, then return RETURN_UNSUPPORTED.
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 30/38] MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (28 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 29/38] MdePkg/PeCoffLib: Capture DLL characteristics field in image context Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 31/38] MdeModulePkg/DxeCore: Remove redundant DEBUG statements Ard Biesheuvel
                   ` (9 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Add the various symbolic constants that the PE/COFF spec v8.3 defines
for the DllCharacteristics field of the PE optional header as
preprocessor macros so we can test for them in C code.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdePkg/Include/IndustryStandard/PeImage.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/MdePkg/Include/IndustryStandard/PeImage.h b/MdePkg/Include/IndustryStandard/PeImage.h
index dd4cc25483bc..f8d726b88353 100644
--- a/MdePkg/Include/IndustryStandard/PeImage.h
+++ b/MdePkg/Include/IndustryStandard/PeImage.h
@@ -108,6 +108,21 @@ typedef struct {
 #define EFI_IMAGE_FILE_DLL                  BIT13    ///< 0x2000  File is a DLL.
 #define EFI_IMAGE_FILE_BYTES_REVERSED_HI    BIT15    ///< 0x8000  Bytes of machine word are reversed.
 
+///
+/// DLL Characteristics
+///
+#define EFI_IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA        BIT5   ///< 0x0020 Image can handle a high entropy 64-bit VA space.
+#define EFI_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE           BIT6   ///< 0x0040 DLL can be relocated at load time.
+#define EFI_IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY        BIT7   ///< 0x0080 Code Integrity checks are enforced.
+#define EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT              BIT8   ///< 0x0100 Image is NX compatible.
+#define EFI_IMAGE_DLLCHARACTERISTICS_NO_ISOLATION           BIT9   ///< 0x0200 Isolation aware, but do not isolate the image.
+#define EFI_IMAGE_DLLCHARACTERISTICS_NO_SEH                 BIT10  ///< 0x0400 Does not use structured exception (SE) handling.
+#define EFI_IMAGE_DLLCHARACTERISTICS_NO_BIND                BIT11  ///< 0x0800 Do not bind the image.
+#define EFI_IMAGE_DLLCHARACTERISTICS_APPCONTAINER           BIT12  ///< 0x1000 Image must execute in an AppContainer.
+#define EFI_IMAGE_DLLCHARACTERISTICS_WDM_DRIVER             BIT13  ///< 0x2000 A WDM driver.
+#define EFI_IMAGE_DLLCHARACTERISTICS_GUARD_CF               BIT14  ///< 0x4000 Image supports Control Flow Guard.
+#define EFI_IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE  BIT15  ///< 0x8000 Terminal Server aware.
+
 ///
 /// Header Data Directories.
 ///
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 31/38] MdeModulePkg/DxeCore: Remove redundant DEBUG statements
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (29 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 30/38] MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region Ard Biesheuvel
                   ` (8 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

The image name is printed at DEBUG_VERBOSE level already when entering
the routine that enables the memory protections, so printing it again
after issuing a warning is unnecessary - let's remove it.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 8df3e881c5c4..85c5a6a7c758 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -477,11 +477,6 @@ ProtectUefiImage (
       "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
       SectionAlignment
       ));
-    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageAddress);
-    if (PdbPointer != NULL) {
-      DEBUG ((DEBUG_VERBOSE, "!!!!!!!!  Image - %a  !!!!!!!!\n", PdbPointer));
-    }
-
     goto Finish;
   }
 
@@ -558,11 +553,6 @@ ProtectUefiImage (
     // of course).
     //
     DEBUG ((DEBUG_WARN, "!!!!!!!!  ProtectUefiImageCommon - CodeSegmentCount is 0  !!!!!!!!\n"));
-    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageAddress);
-    if (PdbPointer != NULL) {
-      DEBUG ((DEBUG_WARN, "!!!!!!!!  Image - %a  !!!!!!!!\n", PdbPointer));
-    }
-
     goto Finish;
   }
 
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (30 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 31/38] MdeModulePkg/DxeCore: Remove redundant DEBUG statements Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-16 13:51   ` Leif Lindholm
  2023-03-13 17:17 ` [PATCH v5 33/38] MdeModulePkg/DxeCore: Disregard runtime alignment for image protection Ard Biesheuvel
                   ` (7 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Currently, we invoke ApplyMemoryProtectionPolicy() after
CoreInternalFreePages() has returned successfully, in order to update
the memory permission attributes of the region to match the policy for
EfiConventionalMemory.

There are two problems with that:
- CoreInternalFreePages() will round up the size of the allocation to
  the appropriate alignment of the memory type, but we only remap the
  number of pages that was passed by the caller, leaving the remaining
  pages freed but mapped with the old permissions;
- in DEBUG builds, we may attempt to clear the entire region while it is
  still mapped with read-only or read-protect attributes.

Let's address both issues, by updating the permissions before performing
the actual conversion.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Mem/Page.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c
index 5903ce7ab525..f5b940bbc25b 100644
--- a/MdeModulePkg/Core/Dxe/Mem/Page.c
+++ b/MdeModulePkg/Core/Dxe/Mem/Page.c
@@ -1519,8 +1519,8 @@ CoreAllocatePages (
   @return EFI_SUCCESS         -Pages successfully freed.
 
 **/
+STATIC
 EFI_STATUS
-EFIAPI
 CoreInternalFreePages (
   IN EFI_PHYSICAL_ADDRESS  Memory,
   IN UINTN                 NumberOfPages,
@@ -1574,6 +1574,13 @@ CoreInternalFreePages (
   NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;
   NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);
 
+  ApplyMemoryProtectionPolicy (
+    Entry->Type,
+    EfiConventionalMemory,
+    Memory,
+    EFI_PAGES_TO_SIZE (NumberOfPages)
+    );
+
   if (MemoryType != NULL) {
     *MemoryType = Entry->Type;
   }
@@ -1628,12 +1635,6 @@ CoreFreePages (
       NULL
       );
     InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);
-    ApplyMemoryProtectionPolicy (
-      MemoryType,
-      EfiConventionalMemory,
-      Memory,
-      EFI_PAGES_TO_SIZE (NumberOfPages)
-      );
   }
 
   return Status;
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 33/38] MdeModulePkg/DxeCore: Disregard runtime alignment for image protection
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (31 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 34/38] MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage() Ard Biesheuvel
                   ` (6 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Image protection in DXE pertains to the memory permission attributes
used at boot time, when the page size is guaranteed to be 4k. Whether or
not the minimum section alignment is even higher when running under the
OS is not relevant here, so just use the EFI page size as the minimum
section alignment directly.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 49 +-------------------
 1 file changed, 1 insertion(+), 48 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 85c5a6a7c758..045e2f391bc0 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -304,51 +304,6 @@ SetUefiImageProtectionAttributes (
   return;
 }
 
-/**
-  Return if the PE image section is aligned.
-
-  @param[in]  SectionAlignment    PE/COFF section alignment
-  @param[in]  MemoryType          PE/COFF image memory type
-
-  @retval TRUE  The PE image section is aligned.
-  @retval FALSE The PE image section is not aligned.
-**/
-BOOLEAN
-IsMemoryProtectionSectionAligned (
-  IN UINT32           SectionAlignment,
-  IN EFI_MEMORY_TYPE  MemoryType
-  )
-{
-  UINT32  PageAlignment;
-
-  switch (MemoryType) {
-    case EfiRuntimeServicesCode:
-    case EfiACPIMemoryNVS:
-      PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
-      break;
-    case EfiRuntimeServicesData:
-    case EfiACPIReclaimMemory:
-      ASSERT (FALSE);
-      PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
-      break;
-    case EfiBootServicesCode:
-    case EfiLoaderCode:
-    case EfiReservedMemoryType:
-      PageAlignment = EFI_PAGE_SIZE;
-      break;
-    default:
-      ASSERT (FALSE);
-      PageAlignment = EFI_PAGE_SIZE;
-      break;
-  }
-
-  if ((SectionAlignment & (PageAlignment - 1)) != 0) {
-    return FALSE;
-  } else {
-    return TRUE;
-  }
-}
-
 /**
   Free Image record.
 
@@ -404,7 +359,6 @@ ProtectUefiImage (
   IMAGE_PROPERTIES_RECORD               *ImageRecord;
   CHAR8                                 *PdbPointer;
   IMAGE_PROPERTIES_RECORD_CODE_SECTION  *ImageRecordCodeSection;
-  BOOLEAN                               IsAligned;
   UINT32                                ProtectionPolicy;
 
   DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));
@@ -470,8 +424,7 @@ ProtectUefiImage (
     SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
   }
 
-  IsAligned = IsMemoryProtectionSectionAligned (SectionAlignment, LoadedImage->ImageCodeType);
-  if (!IsAligned) {
+  if (SectionAlignment >= EFI_PAGE_SIZE) {
     DEBUG ((
       DEBUG_VERBOSE,
       "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 34/38] MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage()
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (32 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 33/38] MdeModulePkg/DxeCore: Disregard runtime alignment for image protection Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 35/38] MdeModulePkg/DxeCore: Clear NX permissions on non-protected images Ard Biesheuvel
                   ` (5 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

In preparation for adding support for a more restrictive NX memory
policy, update the prototype of UefiProtectImage() so it returns a
EFI_STATUS, and deal with its failure in CoreLoadImage.

This should never fail for the DxeCore itself or for drivers that are
loaded before the CPU arch protocol is dispatched, so in those cases, an
ASSERT() is sufficient.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/DxeMain.h               |  6 +++++-
 MdeModulePkg/Core/Dxe/Image/Image.c           |  8 ++++++--
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 21 ++++++++++++--------
 3 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/DxeMain.h b/MdeModulePkg/Core/Dxe/DxeMain.h
index 815a6b4bd844..b618feded39e 100644
--- a/MdeModulePkg/Core/Dxe/DxeMain.h
+++ b/MdeModulePkg/Core/Dxe/DxeMain.h
@@ -2733,8 +2733,12 @@ RemoveImageRecord (
 
   @param[in]  LoadedImage              The loaded image protocol
   @param[in]  LoadedImageDevicePath    The loaded image device path protocol
+
+  @return EFI_SUCCESS       Image protection was configured according to the
+                            applicable policy.
+  @return other             Image protection could not be applied.
 **/
-VOID
+EFI_STATUS
 ProtectUefiImage (
   IN EFI_LOADED_IMAGE_PROTOCOL  *LoadedImage,
   IN EFI_DEVICE_PATH_PROTOCOL   *LoadedImageDevicePath
diff --git a/MdeModulePkg/Core/Dxe/Image/Image.c b/MdeModulePkg/Core/Dxe/Image/Image.c
index 8704ebea9a7c..df2afbc299e3 100644
--- a/MdeModulePkg/Core/Dxe/Image/Image.c
+++ b/MdeModulePkg/Core/Dxe/Image/Image.c
@@ -270,7 +270,8 @@ CoreInitializeImageServices (
 
   InitializeListHead (&mAvailableEmulators);
 
-  ProtectUefiImage (&Image->Info, Image->LoadedImageDevicePath);
+  Status = ProtectUefiImage (&Image->Info, Image->LoadedImageDevicePath);
+  ASSERT_EFI_ERROR (Status);
 
   return Status;
 }
@@ -1448,7 +1449,10 @@ CoreLoadImageCommon (
     }
   }
 
-  ProtectUefiImage (&Image->Info, Image->LoadedImageDevicePath);
+  Status = ProtectUefiImage (&Image->Info, Image->LoadedImageDevicePath);
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
 
   //
   // Success.  Return the image handle
diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 045e2f391bc0..301ddd6eb053 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -341,8 +341,12 @@ FreeImageRecord (
 
   @param[in]  LoadedImage              The loaded image protocol
   @param[in]  LoadedImageDevicePath    The loaded image device path protocol
+
+  @return EFI_SUCCESS       Image protection was configured according to the
+                            applicable policy.
+  @return other             Image protection could not be applied.
 **/
-VOID
+EFI_STATUS
 ProtectUefiImage (
   IN EFI_LOADED_IMAGE_PROTOCOL  *LoadedImage,
   IN EFI_DEVICE_PATH_PROTOCOL   *LoadedImageDevicePath
@@ -365,23 +369,23 @@ ProtectUefiImage (
   DEBUG ((DEBUG_INFO, "  - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));
 
   if (gCpuSetMemoryAttributes == NULL) {
-    return;
+    return EFI_SUCCESS;
   }
 
   ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);
   switch (ProtectionPolicy) {
     case DO_NOT_PROTECT:
-      return;
+      return EFI_SUCCESS;
     case PROTECT_IF_ALIGNED_ELSE_ALLOW:
       break;
     default:
       ASSERT (FALSE);
-      return;
+      return EFI_SUCCESS;
   }
 
   ImageRecord = AllocateZeroPool (sizeof (*ImageRecord));
   if (ImageRecord == NULL) {
-    return;
+    return EFI_SUCCESS;
   }
 
   ImageRecord->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE;
@@ -481,7 +485,7 @@ ProtectUefiImage (
       //
       ImageRecordCodeSection = AllocatePool (sizeof (*ImageRecordCodeSection));
       if (ImageRecordCodeSection == NULL) {
-        return;
+        return EFI_SUCCESS;
       }
 
       ImageRecordCodeSection->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE;
@@ -538,7 +542,7 @@ ProtectUefiImage (
   InsertTailList (&mProtectedImageRecordList, &ImageRecord->Link);
 
 Finish:
-  return;
+  return EFI_SUCCESS;
 }
 
 /**
@@ -988,7 +992,8 @@ MemoryProtectionCpuArchProtocolNotify (
       LoadedImageDevicePath = NULL;
     }
 
-    ProtectUefiImage (LoadedImage, LoadedImageDevicePath);
+    Status = ProtectUefiImage (LoadedImage, LoadedImageDevicePath);
+    ASSERT_EFI_ERROR (Status);
   }
 
   FreePool (HandleBuffer);
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 35/38] MdeModulePkg/DxeCore: Clear NX permissions on non-protected images
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (33 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 34/38] MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage() Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-17 20:04   ` [edk2-devel] " Oliver Smith-Denny
  2023-03-13 17:17 ` [PATCH v5 36/38] MdeModulePkg/DxeCore: Permit NX protection for code regions Ard Biesheuvel
                   ` (4 subsequent siblings)
  39 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

Currently, we rely on the memory type for loading images being
executable by default, and only restrict the permissions if the policy
says so, and the image sections are suitably aligned. This requires that
the various 'code' memory types are executable by default, which is
unfortunate.

In order to be able to tighten this, let's update the image protection
policy handling so that images that should not be mapped with strict
separation of RW- and R-X are remapped RWX explicitly if the memory type
used for loading the images is marked as NX by default.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 98 +++++++++++---------
 1 file changed, 54 insertions(+), 44 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 301ddd6eb053..7c7a946c1b48 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -373,11 +373,62 @@ ProtectUefiImage (
   }
 
   ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);
+
+  ImageAddress = LoadedImage->ImageBase;
+
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageAddress);
+  if (PdbPointer != NULL) {
+    DEBUG ((DEBUG_VERBOSE, "  Image - %a\n", PdbPointer));
+  }
+
   switch (ProtectionPolicy) {
-    case DO_NOT_PROTECT:
-      return EFI_SUCCESS;
     case PROTECT_IF_ALIGNED_ELSE_ALLOW:
-      break;
+      //
+      // Check PE/COFF image
+      //
+      DosHdr             = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;
+      PeCoffHeaderOffset = 0;
+      if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
+        PeCoffHeaderOffset = DosHdr->e_lfanew;
+      }
+
+      Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);
+      if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
+        DEBUG ((DEBUG_INFO, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
+        // It might be image in SMM.
+      } else {
+        //
+        // Get SectionAlignment
+        //
+        if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+          SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
+        } else {
+          SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
+        }
+
+        if (SectionAlignment >= EFI_PAGE_SIZE) {
+          break;
+        }
+
+        DEBUG ((
+          DEBUG_VERBOSE,
+          "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
+          SectionAlignment
+          ));
+      }
+      // fall through to unprotect image if needed
+    case DO_NOT_PROTECT:
+      if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) &
+           LShiftU64 (1, LoadedImage->ImageCodeType)) != 0)
+      {
+        SetUefiImageMemoryAttributes (
+          (UINTN)LoadedImage->ImageBase,
+          (LoadedImage->ImageSize + EFI_PAGE_MASK) & ~(UINT64)EFI_PAGE_MASK,
+          0
+          );
+      }
+
+      return EFI_SUCCESS;
     default:
       ASSERT (FALSE);
       return EFI_SUCCESS;
@@ -396,47 +447,6 @@ ProtectUefiImage (
   ImageRecord->ImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase;
   ImageRecord->ImageSize = LoadedImage->ImageSize;
 
-  ImageAddress = LoadedImage->ImageBase;
-
-  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageAddress);
-  if (PdbPointer != NULL) {
-    DEBUG ((DEBUG_VERBOSE, "  Image - %a\n", PdbPointer));
-  }
-
-  //
-  // Check PE/COFF image
-  //
-  DosHdr             = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;
-  PeCoffHeaderOffset = 0;
-  if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
-    PeCoffHeaderOffset = DosHdr->e_lfanew;
-  }
-
-  Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);
-  if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
-    DEBUG ((DEBUG_VERBOSE, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
-    // It might be image in SMM.
-    goto Finish;
-  }
-
-  //
-  // Get SectionAlignment
-  //
-  if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
-    SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
-  } else {
-    SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
-  }
-
-  if (SectionAlignment >= EFI_PAGE_SIZE) {
-    DEBUG ((
-      DEBUG_VERBOSE,
-      "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
-      SectionAlignment
-      ));
-    goto Finish;
-  }
-
   Section = (EFI_IMAGE_SECTION_HEADER *)(
                                          (UINT8 *)(UINTN)ImageAddress +
                                          PeCoffHeaderOffset +
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 36/38] MdeModulePkg/DxeCore: Permit NX protection for code regions
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (34 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 35/38] MdeModulePkg/DxeCore: Clear NX permissions on non-protected images Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 37/38] MdeModulePkg/DxeCore: Check NX compat when using restricted " Ard Biesheuvel
                   ` (3 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

We currently do not permit NX protection for code regions, as these
regions are normally populated by the image loader, which will set
different permissions for the code and data sections of the PE/COFF
image, all of which will be covered by a single code region in the EFI
memory map.

However, this means that allocating pages of such a code type will
always return memory that has both writable and executable permissions,
and this is something we want to avoid.

So let's rework the NX memory protection init code so it can deal with
the NX policy including such code regions as well.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 35 +++++++++++++++-----
 MdeModulePkg/MdeModulePkg.dec                 |  3 +-
 2 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index 7c7a946c1b48..bce211a09c3e 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -624,6 +624,29 @@ GetPermissionAttributeForMemoryType (
   }
 }
 
+/**
+  Return the EFI memory permission attribute to be used for regions of type
+  'MemoryType' when performing the initial remap of all active regions. This
+  takes into account that code regions should be disregarded in this case.
+
+  @param MemoryType       Memory type.
+**/
+STATIC
+UINT64
+GetInitialPermissionAttributeForMemoryType (
+  IN EFI_MEMORY_TYPE  MemoryType
+  )
+{
+  switch (MemoryType) {
+  case EfiBootServicesCode:
+  case EfiRuntimeServicesCode:
+  case EfiLoaderCode:
+    return 0;
+  default:
+    return GetPermissionAttributeForMemoryType (MemoryType);
+  }
+}
+
 /**
   Sort memory map entries based upon PhysicalStart, from low to high.
 
@@ -701,10 +724,10 @@ MergeMemoryMapForProtectionPolicy (
 
     do {
       MemoryBlockLength = (UINT64)(EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages));
-      Attributes        = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
+      Attributes        = GetInitialPermissionAttributeForMemoryType (MemoryMapEntry->Type);
 
-      if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
-          (Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type)) &&
+      if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) && (Attributes != 0) &&
+          (Attributes == GetInitialPermissionAttributeForMemoryType (NextMemoryMapEntry->Type)) &&
           ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart))
       {
         MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
@@ -831,7 +854,7 @@ InitializeDxeNxMemoryProtectionPolicy (
   MemoryMapEntry = MemoryMap;
   MemoryMapEnd   = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
   while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
-    Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
+    Attributes = GetInitialPermissionAttributeForMemoryType (MemoryMapEntry->Type);
     if (Attributes != 0) {
       SetUefiImageMemoryAttributes (
         MemoryMapEntry->PhysicalStart,
@@ -1129,13 +1152,9 @@ CoreInitializeMemoryProtection (
 
   //
   // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
-  // - code regions should have no EFI_MEMORY_XP attribute
   // - EfiConventionalMemory and EfiBootServicesData should use the
   //   same attribute
   //
-  ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);
-  ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);
-  ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);
   ASSERT (
     GetPermissionAttributeForMemoryType (EfiBootServicesData) ==
     GetPermissionAttributeForMemoryType (EfiConventionalMemory)
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index e8058c8bfaec..720dec58dfc4 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -1388,8 +1388,7 @@ [PcdsFixedAtBuild, PcdsPatchableInModule]
   #  OEM Reserved       0x4000000000000000<BR>
   #  OS Reserved        0x8000000000000000<BR>
   #
-  # NOTE: User must NOT set NX protection for EfiLoaderCode / EfiBootServicesCode / EfiRuntimeServicesCode. <BR>
-  #       User MUST set the same NX protection for EfiBootServicesData and EfiConventionalMemory. <BR>
+  # NOTE: User MUST set the same NX protection for EfiBootServicesData and EfiConventionalMemory. <BR>
   #
   # e.g. 0x7FD5 can be used for all memory except Code. <BR>
   # e.g. 0x7BD4 can be used for all memory except Code and ACPINVS/Reserved. <BR>
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 37/38] MdeModulePkg/DxeCore: Check NX compat when using restricted code regions
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (35 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 36/38] MdeModulePkg/DxeCore: Permit NX protection for code regions Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-13 17:17 ` [PATCH v5 38/38] MdeModulePkg DEC: Remove inaccurate comment Ard Biesheuvel
                   ` (2 subsequent siblings)
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

We currently do not permit the various 'code' type regions to be covered
by the NX memory policy, and so allocations of such types are created as
both writable and executable before being populated with executable
code.

Before adding the ability to protect those regions as well, let's make
sure that the images in question are compatible with such a policy, and
have the NX_COMPAT DLL flag set in the PE/COFF header.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 38 ++++++++++++++++++--
 1 file changed, 36 insertions(+), 2 deletions(-)

diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
index bce211a09c3e..91a04ac2ac0b 100644
--- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
+++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
@@ -364,6 +364,7 @@ ProtectUefiImage (
   CHAR8                                 *PdbPointer;
   IMAGE_PROPERTIES_RECORD_CODE_SECTION  *ImageRecordCodeSection;
   UINT32                                ProtectionPolicy;
+  UINT16                                DllCharacteristics;
 
   DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));
   DEBUG ((DEBUG_INFO, "  - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));
@@ -401,9 +402,34 @@ ProtectUefiImage (
         // Get SectionAlignment
         //
         if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
-          SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
+          SectionAlignment   = Hdr.Pe32->OptionalHeader.SectionAlignment;
+          DllCharacteristics = Hdr.Pe32->OptionalHeader.DllCharacteristics;
         } else {
-          SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
+          SectionAlignment   = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
+          DllCharacteristics = Hdr.Pe32Plus->OptionalHeader.DllCharacteristics;
+        }
+
+        //
+        // If the NX memory policy applies to the code memory region type used
+        // for this image, ensure that the image has the NX compat flag set,
+        // which means that the program's logic does not assume that memory
+        // allocations are mapped both writable and executable at the same time.
+        // Also ensure that the section alignment is sufficient, as otherwise,
+        // the image's code and data sections might share a page that would
+        // require a mapping that is both writable and executable.
+        //
+        if ((LoadedImage != gDxeCoreLoadedImage) &&
+            (GetImageType (LoadedImageDevicePath) != IMAGE_FROM_FV) &&
+            (((DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT) == 0) ||
+             (SectionAlignment < EFI_PAGE_SIZE)) &&
+            (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) &
+             LShiftU64 (1, LoadedImage->ImageCodeType)) != 0) {
+
+          DEBUG ((
+            DEBUG_VERBOSE,
+            "!!!!!!!!  ProtectUefiImageCommon - Image does not comply with NX policy for code memory region type !!!!!!!!\n"
+            ));
+          return EFI_UNSUPPORTED;
         }
 
         if (SectionAlignment >= EFI_PAGE_SIZE) {
@@ -1154,12 +1180,20 @@ CoreInitializeMemoryProtection (
   // Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
   // - EfiConventionalMemory and EfiBootServicesData should use the
   //   same attribute
+  // - the image protection policy must cover 3rd party images if
+  //   any code memory types are being mapped NX by default
   //
   ASSERT (
     GetPermissionAttributeForMemoryType (EfiBootServicesData) ==
     GetPermissionAttributeForMemoryType (EfiConventionalMemory)
     );
 
+  if (((GetPermissionAttributeForMemoryType (EfiLoaderCode) |
+        GetPermissionAttributeForMemoryType (EfiBootServicesCode) |
+        GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode)) & EFI_MEMORY_XP) != 0) {
+    ASSERT ((mImageProtectionPolicy & BIT0) == BIT0);
+  }
+
   Status = CoreCreateEvent (
              EVT_NOTIFY_SIGNAL,
              TPL_CALLBACK,
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [PATCH v5 38/38] MdeModulePkg DEC: Remove inaccurate comment
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (36 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 37/38] MdeModulePkg/DxeCore: Check NX compat when using restricted " Ard Biesheuvel
@ 2023-03-13 17:17 ` Ard Biesheuvel
  2023-03-16 14:04 ` [edk2-devel] [PATCH v5 00/38] Implement strict memory permissions throughout Leif Lindholm
  2023-03-17 21:41 ` Oliver Smith-Denny
  39 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-13 17:17 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Michael Kinney, Liming Gao, Jiewen Yao,
	Michael Kubacki, Sean Brogan, Rebecca Cran, Leif Lindholm,
	Sami Mujawar, Taylor Beebe

The comment regarding the configured image protection policy states that
data regions of a loaded image may be mapped NX based on the configured
NX memory policy for boot/runtime services or loader data regions.

This is inaccurate: all image sections will be covered by the same code
region in the memory map, so the NX protection policy for data regions
has no bearing on this whatsoever.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 MdeModulePkg/MdeModulePkg.dec | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 720dec58dfc4..b42af1faee25 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -1356,10 +1356,6 @@ [PcdsFixedAtBuild, PcdsPatchableInModule]
   #    BIT0       - Image from unknown device. <BR>
   #    BIT1       - Image from firmware volume.<BR>
   #  <BR>
-  #  Note: If a bit is cleared, the data section could be still non-executable if
-  #  PcdDxeNxMemoryProtectionPolicy is enabled for EfiLoaderData, EfiBootServicesData
-  #  and/or EfiRuntimeServicesData.<BR>
-  #  <BR>
   # @Prompt Set image protection policy.
   # @ValidRange 0x80000002 | 0x00000000 - 0x0000001F
   gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy|0x00000002|UINT32|0x00001047
-- 
2.39.2


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [PATCH v5 02/38] ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field
  2023-03-13 17:16 ` [PATCH v5 02/38] ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field Ard Biesheuvel
@ 2023-03-14 17:24   ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-14 17:24 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:38 +0100, Ard Biesheuvel wrote:
> With large page support out of the picture, we can treat bits 1 and 0 of
> the page descriptor as individual valid and XN bits, instead of treating
> XN as a page type. Doing so aligns the handling of the attribute with
> the section descriptor layout, as well as the XN handling on AArch64,
> and this is beneficial for maintainability.

On average, this is a big enough improvement that I'm happy for it to
go in, but it *does* take a step away from the actual architectural
definition.

I'll choose to see it as viewing aarch32 through aarch64 goggles,
which feels reasonable these days.

Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>

> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  ArmPkg/Include/Chipset/ArmV7Mmu.h              |  8 +++-----
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c | 12 ++++++------
>  2 files changed, 9 insertions(+), 11 deletions(-)
> 
> diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
> index 7501ebfdf97f..6a2584ceb303 100644
> --- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
> +++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
> @@ -54,11 +54,9 @@
>  #define TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Desc)  (((Desc) & 3UL) == TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE)
>  
>  // Translation table descriptor types
> -#define TT_DESCRIPTOR_PAGE_TYPE_MASK       (3UL << 0)
> -#define TT_DESCRIPTOR_PAGE_TYPE_FAULT      (0UL << 0)
> -#define TT_DESCRIPTOR_PAGE_TYPE_PAGE       (2UL << 0)
> -#define TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN    (3UL << 0)
> -#define TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE  (1UL << 0)
> +#define TT_DESCRIPTOR_PAGE_TYPE_MASK   (1UL << 1)
> +#define TT_DESCRIPTOR_PAGE_TYPE_FAULT  (0UL << 1)
> +#define TT_DESCRIPTOR_PAGE_TYPE_PAGE   (1UL << 1)
>  
>  // Section descriptor definitions
>  #define TT_DESCRIPTOR_SECTION_SIZE  (0x00100000)
> diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> index 9ca00c976d5f..12d0f4c30f8e 100644
> --- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> @@ -104,12 +104,8 @@ 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 | 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;
> -  }
> +  EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_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 the order of the conditionals below
> @@ -148,6 +144,10 @@ UpdatePageEntries (
>      EntryValue |= TT_DESCRIPTOR_PAGE_AP_RW_RW;
>    }
>  
> +  if ((Attributes & EFI_MEMORY_XP) != 0) {
> +    EntryValue |= TT_DESCRIPTOR_PAGE_XN_MASK;
> +  }
> +
>    // Obtain page table base
>    FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
>  
> -- 
> 2.39.2

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 04/38] ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask
  2023-03-13 17:16 ` [PATCH v5 04/38] ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask Ard Biesheuvel
@ 2023-03-14 17:55   ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-14 17:55 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:40 +0100, Ard Biesheuvel wrote:
> Split the ARM permission fields in the short descriptors into an access
> flag and AP[2:1] as per the recommendation in the ARM ARM. This makes
> the access flag available separately, which allows us to implement
> EFI_MEMORY_RP memory analogous to how it will be implemented for
> AArch64.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>

Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>

Comment: it seems your setup has lost the diff.orderFile setting, so
we end up with the new definitions after their first use.

> ---
>  ArmPkg/Drivers/CpuDxe/Arm/Mmu.c                 | 47 ++++++++++----------
>  ArmPkg/Include/Chipset/ArmV7Mmu.h               | 40 +++++++++++------
>  ArmPkg/Library/ArmLib/Arm/ArmV7Support.S        |  2 +
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c |  1 +
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c  | 12 ++++-
>  5 files changed, 63 insertions(+), 39 deletions(-)
> 
> diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
> index 8eb1f71395f5..07faab8216ec 100644
> --- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
> +++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
> @@ -50,30 +50,27 @@ 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_RO | EFI_MEMORY_RP;
> -      break;
> -
> -    case TT_DESCRIPTOR_SECTION_AP_RW_NO:
> +    case TT_DESCRIPTOR_SECTION_AP_NO_RW:
>      case TT_DESCRIPTOR_SECTION_AP_RW_RW:
>        // normal read/write access, do not add additional attributes
>        break;
>  
>      // read only cases map to write-protect
> -    case TT_DESCRIPTOR_SECTION_AP_RO_NO:
> +    case TT_DESCRIPTOR_SECTION_AP_NO_RO:
>      case TT_DESCRIPTOR_SECTION_AP_RO_RO:
>        *GcdAttributes |= EFI_MEMORY_RO;
>        break;
> -
> -    default:
> -      return EFI_UNSUPPORTED;
>    }
>  
>    // now process eXectue Never attribute
> -  if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) {
> +  if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0) {
>      *GcdAttributes |= EFI_MEMORY_XP;
>    }
>  
> +  if ((SectionAttributes & TT_DESCRIPTOR_SECTION_AF) == 0) {
> +    *GcdAttributes |= EFI_MEMORY_RP;
> +  }
> +
>    return EFI_SUCCESS;
>  }
>  
> @@ -114,30 +111,27 @@ 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_RO | EFI_MEMORY_RP;
> -      break;
> -
> -    case TT_DESCRIPTOR_PAGE_AP_RW_NO:
> +    case TT_DESCRIPTOR_PAGE_AP_NO_RW:
>      case TT_DESCRIPTOR_PAGE_AP_RW_RW:
>        // normal read/write access, do not add additional attributes
>        break;
>  
>      // read only cases map to write-protect
> -    case TT_DESCRIPTOR_PAGE_AP_RO_NO:
> +    case TT_DESCRIPTOR_PAGE_AP_NO_RO:
>      case TT_DESCRIPTOR_PAGE_AP_RO_RO:
>        *GcdAttributes |= EFI_MEMORY_RO;
>        break;
> -
> -    default:
> -      return EFI_UNSUPPORTED;
>    }
>  
>    // now process eXectue Never attribute
> -  if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) {
> +  if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0) {
>      *GcdAttributes |= EFI_MEMORY_XP;
>    }
>  
> +  if ((PageAttributes & TT_DESCRIPTOR_PAGE_AF) == 0) {
> +    *GcdAttributes |= EFI_MEMORY_RP;
> +  }
> +
>    return EFI_SUCCESS;
>  }
>  
> @@ -166,6 +160,7 @@ SyncCacheConfigPage (
>    // Convert SectionAttributes into PageAttributes
>    NextPageAttributes =
>      TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (*NextSectionAttributes) |
> +    TT_DESCRIPTOR_CONVERT_TO_PAGE_AF (*NextSectionAttributes) |
>      TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (*NextSectionAttributes);
>  
>    // obtain page table base
> @@ -174,7 +169,7 @@ SyncCacheConfigPage (
>    for (i = 0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) {
>      if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) {
>        // extract attributes (cacheability and permissions)
> -      PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK);
> +      PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_AF);
>  
>        if (NextPageAttributes == 0) {
>          // start on a new region
> @@ -213,6 +208,7 @@ SyncCacheConfigPage (
>    // Convert back PageAttributes into SectionAttributes
>    *NextSectionAttributes =
>      TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (NextPageAttributes) |
> +    TT_DESCRIPTOR_CONVERT_TO_SECTION_AF (NextPageAttributes) |
>      TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (NextPageAttributes);
>  
>    return EFI_SUCCESS;
> @@ -256,14 +252,14 @@ SyncCacheConfig (
>    FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ());
>  
>    // Get the first region
> -  NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
> +  NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF);
>  
>    // iterate through each 1MB descriptor
>    NextRegionBase = NextRegionLength = 0;
>    for (i = 0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) {
>      if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) {
>        // extract attributes (cacheability and permissions)
> -      SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK);
> +      SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF);
>  
>        if (NextSectionAttributes == 0) {
>          // start on a new region
> @@ -383,6 +379,10 @@ EfiAttributeToArmAttribute (
>      ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK;
>    }
>  
> +  if ((EfiAttributes & EFI_MEMORY_RP) == 0) {
> +    ArmAttributes |= TT_DESCRIPTOR_SECTION_AF;
> +  }
> +
>    return ArmAttributes;
>  }
>  
> @@ -482,6 +482,7 @@ GetMemoryRegion (
>      *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes) |
>                          TT_DESCRIPTOR_CONVERT_TO_SECTION_S (PageAttributes) |
>                          TT_DESCRIPTOR_CONVERT_TO_SECTION_XN (PageAttributes) |
> +                        TT_DESCRIPTOR_CONVERT_TO_SECTION_AF (PageAttributes) |
>                          TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes);
>    }
>  
> diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h
> index e0219747df86..da4f3160f8ff 100644
> --- a/ArmPkg/Include/Chipset/ArmV7Mmu.h
> +++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h
> @@ -80,21 +80,21 @@
>  #define TT_DESCRIPTOR_PAGE_S_NOT_SHARED  (0UL << 10)
>  #define TT_DESCRIPTOR_PAGE_S_SHARED      (1UL << 10)
>  
> -#define TT_DESCRIPTOR_SECTION_AP_MASK   ((1UL << 15) | (3UL << 10))
> -#define TT_DESCRIPTOR_SECTION_AP_NO_NO  ((0UL << 15) | (0UL << 10))
> -#define TT_DESCRIPTOR_SECTION_AP_RW_NO  ((0UL << 15) | (1UL << 10))
> -#define TT_DESCRIPTOR_SECTION_AP_RW_RO  ((0UL << 15) | (2UL << 10))
> -#define TT_DESCRIPTOR_SECTION_AP_RW_RW  ((0UL << 15) | (3UL << 10))
> -#define TT_DESCRIPTOR_SECTION_AP_RO_NO  ((1UL << 15) | (1UL << 10))
> -#define TT_DESCRIPTOR_SECTION_AP_RO_RO  ((1UL << 15) | (3UL << 10))
> +#define TT_DESCRIPTOR_SECTION_AP_MASK   ((1UL << 15) | (1UL << 11))
> +#define TT_DESCRIPTOR_SECTION_AP_NO_RW  ((0UL << 15) | (0UL << 11))
> +#define TT_DESCRIPTOR_SECTION_AP_RW_RW  ((0UL << 15) | (1UL << 11))
> +#define TT_DESCRIPTOR_SECTION_AP_NO_RO  ((1UL << 15) | (0UL << 11))
> +#define TT_DESCRIPTOR_SECTION_AP_RO_RO  ((1UL << 15) | (1UL << 11))
>  
> -#define TT_DESCRIPTOR_PAGE_AP_MASK   ((1UL << 9) | (3UL << 4))
> -#define TT_DESCRIPTOR_PAGE_AP_NO_NO  ((0UL << 9) | (0UL << 4))
> -#define TT_DESCRIPTOR_PAGE_AP_RW_NO  ((0UL << 9) | (1UL << 4))
> -#define TT_DESCRIPTOR_PAGE_AP_RW_RO  ((0UL << 9) | (2UL << 4))
> -#define TT_DESCRIPTOR_PAGE_AP_RW_RW  ((0UL << 9) | (3UL << 4))
> -#define TT_DESCRIPTOR_PAGE_AP_RO_NO  ((1UL << 9) | (1UL << 4))
> -#define TT_DESCRIPTOR_PAGE_AP_RO_RO  ((1UL << 9) | (3UL << 4))
> +#define TT_DESCRIPTOR_SECTION_AF  (1UL << 10)
> +
> +#define TT_DESCRIPTOR_PAGE_AP_MASK   ((1UL << 9) | (1UL << 5))
> +#define TT_DESCRIPTOR_PAGE_AP_NO_RW  ((0UL << 9) | (0UL << 5))
> +#define TT_DESCRIPTOR_PAGE_AP_RW_RW  ((0UL << 9) | (1UL << 5))
> +#define TT_DESCRIPTOR_PAGE_AP_NO_RO  ((1UL << 9) | (0UL << 5))
> +#define TT_DESCRIPTOR_PAGE_AP_RO_RO  ((1UL << 9) | (1UL << 5))
> +
> +#define TT_DESCRIPTOR_PAGE_AF  (1UL << 4)
>  
>  #define TT_DESCRIPTOR_SECTION_XN_MASK  (0x1UL << 4)
>  #define TT_DESCRIPTOR_PAGE_XN_MASK     (0x1UL << 0)
> @@ -124,20 +124,24 @@
>  #define TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_AP_MASK) >> 6) & TT_DESCRIPTOR_PAGE_AP_MASK)
>  #define TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_NG_MASK) >> 6) & TT_DESCRIPTOR_PAGE_NG_MASK)
>  #define TT_DESCRIPTOR_CONVERT_TO_PAGE_S(Desc)             ((((Desc) & TT_DESCRIPTOR_SECTION_S_MASK) >> 6) & TT_DESCRIPTOR_PAGE_S_MASK)
> +#define TT_DESCRIPTOR_CONVERT_TO_PAGE_AF(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_AF) >> 6) & TT_DESCRIPTOR_PAGE_AF)
>  #define TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(Desc)            ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) >> 4) & TT_DESCRIPTOR_PAGE_XN_MASK)
>  #define TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 12)) >> 6) | (Desc & (0x3 << 2)))
>  
>  #define TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_AP_MASK) << 6) & TT_DESCRIPTOR_SECTION_AP_MASK)
>  #define TT_DESCRIPTOR_CONVERT_TO_SECTION_S(Desc)             ((((Desc) & TT_DESCRIPTOR_PAGE_S_MASK) << 6) & TT_DESCRIPTOR_SECTION_S_MASK)
> +#define TT_DESCRIPTOR_CONVERT_TO_SECTION_AF(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_AF) << 6) & TT_DESCRIPTOR_SECTION_AF)
>  #define TT_DESCRIPTOR_CONVERT_TO_SECTION_XN(Desc)            ((((Desc) & TT_DESCRIPTOR_PAGE_XN_MASK) << 4) & TT_DESCRIPTOR_SECTION_XN_MASK)
>  #define TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(Desc)  ((((Desc) & (0x3 << 6)) << 6) | (Desc & (0x3 << 2)))
>  
>  #define TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK  (TT_DESCRIPTOR_SECTION_NS_MASK | TT_DESCRIPTOR_SECTION_NG_MASK |               \
>                                                               TT_DESCRIPTOR_SECTION_S_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | \
> +                                                             TT_DESCRIPTOR_SECTION_AF | \
>                                                               TT_DESCRIPTOR_SECTION_XN_MASK | TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK)
>  
>  #define TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK  (TT_DESCRIPTOR_PAGE_NG_MASK | TT_DESCRIPTOR_PAGE_S_MASK |                  \
>                                                               TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK | \
> +                                                             TT_DESCRIPTOR_PAGE_AF | \
>                                                               TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK)
>  
>  #define TT_DESCRIPTOR_SECTION_DOMAIN_MASK  (0x0FUL << 5)
> @@ -159,6 +163,7 @@
>                                                              TT_DESCRIPTOR_SECTION_S_SHARED                          | \
>                                                              TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
>                                                              TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
> +                                                            TT_DESCRIPTOR_SECTION_AF                                | \
>                                                              TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC)
>  #define TT_DESCRIPTOR_SECTION_WRITE_THROUGH(NonSecure)  (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
>                                                              ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
> @@ -166,6 +171,7 @@
>                                                              TT_DESCRIPTOR_SECTION_S_SHARED                          | \
>                                                              TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
>                                                              TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
> +                                                            TT_DESCRIPTOR_SECTION_AF                                | \
>                                                              TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
>  #define TT_DESCRIPTOR_SECTION_DEVICE(NonSecure)         (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |     \
>                                                              ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
> @@ -174,6 +180,7 @@
>                                                              TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
>                                                              TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
>                                                              TT_DESCRIPTOR_SECTION_XN_MASK                           | \
> +                                                            TT_DESCRIPTOR_SECTION_AF                                | \
>                                                              TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE)
>  #define TT_DESCRIPTOR_SECTION_UNCACHED(NonSecure)       (TT_DESCRIPTOR_SECTION_TYPE_SECTION                                                           |    \
>                                                             ((NonSecure) ?  TT_DESCRIPTOR_SECTION_NS : 0)    | \
> @@ -181,28 +188,33 @@
>                                                             TT_DESCRIPTOR_SECTION_S_NOT_SHARED                      | \
>                                                             TT_DESCRIPTOR_SECTION_DOMAIN(0)                         | \
>                                                             TT_DESCRIPTOR_SECTION_AP_RW_RW                          | \
> +                                                            TT_DESCRIPTOR_SECTION_AF                                | \
>                                                             TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE)
>  
>  #define TT_DESCRIPTOR_PAGE_WRITE_BACK     (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
>                                                          TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
>                                                          TT_DESCRIPTOR_PAGE_S_SHARED                                                       | \
>                                                          TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
> +                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
>                                                          TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC)
>  #define TT_DESCRIPTOR_PAGE_WRITE_THROUGH  (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
>                                                          TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
>                                                          TT_DESCRIPTOR_PAGE_S_SHARED                                                       | \
>                                                          TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
> +                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
>                                                          TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC)
>  #define TT_DESCRIPTOR_PAGE_DEVICE         (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
>                                                          TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
>                                                          TT_DESCRIPTOR_PAGE_S_NOT_SHARED                                                   | \
>                                                          TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
> +                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
>                                                          TT_DESCRIPTOR_PAGE_XN_MASK                                                        | \
>                                                          TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE)
>  #define TT_DESCRIPTOR_PAGE_UNCACHED       (TT_DESCRIPTOR_PAGE_TYPE_PAGE                                                           |          \
>                                                          TT_DESCRIPTOR_PAGE_NG_GLOBAL                                                      | \
>                                                          TT_DESCRIPTOR_PAGE_S_NOT_SHARED                                                   | \
>                                                          TT_DESCRIPTOR_PAGE_AP_RW_RW                                                       | \
> +                                                        TT_DESCRIPTOR_PAGE_AF                                                             | \
>                                                          TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE)
>  
>  // First Level Descriptors
> diff --git a/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S b/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S
> index 4925f6628e1e..1f396adffc11 100644
> --- a/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S
> +++ b/ArmPkg/Library/ArmLib/Arm/ArmV7Support.S
> @@ -16,6 +16,7 @@
>  .set CTRL_C_BIT,  (1 << 2)
>  .set CTRL_B_BIT,  (1 << 7)
>  .set CTRL_I_BIT,  (1 << 12)
> +.set CTRL_AFE_BIT,(1 << 29)
>  
>  
>  ASM_FUNC(ArmInvalidateDataCacheEntryByMVA)
> @@ -64,6 +65,7 @@ ASM_FUNC(ArmInvalidateInstructionCache)
>  ASM_FUNC(ArmEnableMmu)
>    mrc     p15,0,R0,c1,c0,0
>    orr     R0,R0,#1
> +  orr     R0,R0,#CTRL_AFE_BIT
>    mcr     p15,0,R0,c1,c0,0
>    dsb
>    isb
> diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
> index 6e2f08a7ce15..52dbfd714029 100644
> --- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
> +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c
> @@ -23,6 +23,7 @@ ConvertSectionAttributesToPageAttributes (
>    PageAttributes  = 0;
>    PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (SectionAttributes);
>    PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (SectionAttributes);
> +  PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_AF (SectionAttributes);
>    PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_XN (SectionAttributes);
>    PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_NG (SectionAttributes);
>    PageAttributes |= TT_DESCRIPTOR_CONVERT_TO_PAGE_S (SectionAttributes);
> diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> index 12d0f4c30f8e..484c67476619 100644
> --- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> @@ -104,7 +104,7 @@ 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 | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK;
> +  EntryMask = TT_DESCRIPTOR_PAGE_TYPE_MASK | TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK | TT_DESCRIPTOR_PAGE_AF;
>    EntryValue = TT_DESCRIPTOR_PAGE_TYPE_PAGE;
>  
>    // Although the PI spec is unclear on this, the GCD guarantees that only
> @@ -138,6 +138,10 @@ UpdatePageEntries (
>      return EFI_UNSUPPORTED;
>    }
>  
> +  if ((Attributes & EFI_MEMORY_RP) == 0) {
> +    EntryValue |= TT_DESCRIPTOR_PAGE_AF;
> +  }
> +
>    if ((Attributes & EFI_MEMORY_RO) != 0) {
>      EntryValue |= TT_DESCRIPTOR_PAGE_AP_RO_RO;
>    } else {
> @@ -237,7 +241,7 @@ UpdateSectionEntries (
>  
>    // 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;
> +              TT_DESCRIPTOR_SECTION_AP_MASK | TT_DESCRIPTOR_SECTION_AF;
>    EntryValue = TT_DESCRIPTOR_SECTION_TYPE_SECTION;
>  
>    // Although the PI spec is unclear on this, the GCD guarantees that only
> @@ -281,6 +285,10 @@ UpdateSectionEntries (
>      EntryValue |= TT_DESCRIPTOR_SECTION_XN_MASK;
>    }
>  
> +  if ((Attributes & EFI_MEMORY_RP) == 0) {
> +    EntryValue |= TT_DESCRIPTOR_SECTION_AF;
> +  }
> +
>    // obtain page table base
>    FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress ();
>  
> -- 
> 2.39.2
> 
> 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
  2023-03-13 17:16 ` [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible Ard Biesheuvel
@ 2023-03-14 18:13   ` Leif Lindholm
  2023-03-14 18:29     ` Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Leif Lindholm @ 2023-03-14 18:13 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:44 +0100, Ard Biesheuvel wrote:
> Currently, the ARM MMU page table logic will break down any block entry
> that overlaps with the region being mapped, even if the block entry in
> question is using the same attributes as the new region.
> 
> This means that creating a non-executable mapping inside a region that
> is already mapped non-executable at a coarser granularity may trigger a
> call to AllocatePages (), which may recurse back into the page table
> code to update the attributes on the newly allocated page tables.
> 
> Let's avoid this, by preserving the block entry if it already covers the
> region being mapped with the correct attributes.

So if a later mapping is made inside the same block with conflicting
attributes? That triggers the break down at that point and because the
existing mapping did not conflict, it'll all flush out?

/
    Leif

> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c | 10 ++++++++++
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c   | 11 +++++++++++
>  2 files changed, 21 insertions(+)
> 
> diff --git a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
> index 6d21a2e41dd1..1ce200c43c72 100644
> --- a/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
> +++ b/ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c
> @@ -251,6 +251,16 @@ UpdateRegionMappingRecursive (
>        ASSERT (Level < 3);
>  
>        if (!IsTableEntry (*Entry, Level)) {
> +        //
> +        // If the region we are trying to map is already covered by a block
> +        // entry with the right attributes, don't bother splitting it up.
> +        //
> +        if (IsBlockEntry (*Entry, Level) &&
> +            ((*Entry & TT_ATTRIBUTES_MASK & ~AttributeClearMask) == AttributeSetMask))
> +        {
> +          continue;
> +        }
> +
>          //
>          // No table entry exists yet, so we need to allocate a page table
>          // for the next level.
> diff --git a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> index 247cf87bf3d3..299d38ad07e8 100644
> --- a/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> +++ b/ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c
> @@ -170,6 +170,17 @@ UpdatePageEntries (
>  
>      // Does this descriptor need to be converted from section entry to 4K pages?
>      if (!TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (Descriptor)) {
> +      //
> +      // If the section mapping covers the requested region with the expected
> +      // attributes, splitting it is unnecessary, and should be avoided as it
> +      // may result in unbounded recursion when using a strict NX policy.
> +      //
> +      if ((EntryValue & ~TT_DESCRIPTOR_PAGE_TYPE_MASK & EntryMask) ==
> +          (ConvertSectionAttributesToPageAttributes (Descriptor) & EntryMask))
> +      {
> +        continue;
> +      }
> +
>        Status = ConvertSectionToPages (FirstLevelIdx << TT_DESCRIPTOR_SECTION_BASE_SHIFT);
>        if (EFI_ERROR (Status)) {
>          // Exit for loop
> -- 
> 2.39.2
> 
> 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
  2023-03-14 18:13   ` [edk2-devel] " Leif Lindholm
@ 2023-03-14 18:29     ` Ard Biesheuvel
  2023-03-15 18:02       ` Leif Lindholm
  0 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-14 18:29 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Tue, 14 Mar 2023 at 19:13, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>
> On Mon, Mar 13, 2023 at 18:16:44 +0100, Ard Biesheuvel wrote:
> > Currently, the ARM MMU page table logic will break down any block entry
> > that overlaps with the region being mapped, even if the block entry in
> > question is using the same attributes as the new region.
> >
> > This means that creating a non-executable mapping inside a region that
> > is already mapped non-executable at a coarser granularity may trigger a
> > call to AllocatePages (), which may recurse back into the page table
> > code to update the attributes on the newly allocated page tables.
> >
> > Let's avoid this, by preserving the block entry if it already covers the
> > region being mapped with the correct attributes.
>
> So if a later mapping is made inside the same block with conflicting
> attributes? That triggers the break down at that point and because the
> existing mapping did not conflict, it'll all flush out?
>

Indeed.

The case here is simply, e.g., mapping a single page XP that is
already covered by a 2 MB XP block: without this patch, we break down
that 2 MB block into page mappings that all have the same attributes.
If the 4k page being remapped is being allocated for a page table, we
may end up with unbounded recursion.

If the attributes are actually different, the split still happens. But
otherwise, the block mapping is retained.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
  2023-03-14 18:29     ` Ard Biesheuvel
@ 2023-03-15 18:02       ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-15 18:02 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Tue, Mar 14, 2023 at 19:29:39 +0100, Ard Biesheuvel wrote:
> On Tue, 14 Mar 2023 at 19:13, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
> >
> > On Mon, Mar 13, 2023 at 18:16:44 +0100, Ard Biesheuvel wrote:
> > > Currently, the ARM MMU page table logic will break down any block entry
> > > that overlaps with the region being mapped, even if the block entry in
> > > question is using the same attributes as the new region.
> > >
> > > This means that creating a non-executable mapping inside a region that
> > > is already mapped non-executable at a coarser granularity may trigger a
> > > call to AllocatePages (), which may recurse back into the page table
> > > code to update the attributes on the newly allocated page tables.
> > >
> > > Let's avoid this, by preserving the block entry if it already covers the
> > > region being mapped with the correct attributes.
> >
> > So if a later mapping is made inside the same block with conflicting
> > attributes? That triggers the break down at that point and because the
> > existing mapping did not conflict, it'll all flush out?
> >
> 
> Indeed.
> 
> The case here is simply, e.g., mapping a single page XP that is
> already covered by a 2 MB XP block: without this patch, we break down
> that 2 MB block into page mappings that all have the same attributes.
> If the 4k page being remapped is being allocated for a page table, we
> may end up with unbounded recursion.
> 
> If the attributes are actually different, the split still happens. But
> otherwise, the block mapping is retained.

Makes sense.

Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 09/38] ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion
  2023-03-13 17:16 ` [PATCH v5 09/38] ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion Ard Biesheuvel
@ 2023-03-15 18:08   ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-15 18:08 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:45 +0100, Ard Biesheuvel wrote:
> In preparation for introducing an implementation of the EFI memory
> attributes protocol that is shared between ARM and AArch64, unify the
> existing code that converts a page table descriptor into a
> EFI_MEMORY_xxx bitfield, so it can be called from the generic code.

Two bits of nitpicking:
1) You use _xxx from here and _xx in the comments below.

> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c | 17 +++++++++
>  ArmPkg/Drivers/CpuDxe/Arm/Mmu.c     | 38 ++++++++++++++++++++
>  ArmPkg/Drivers/CpuDxe/CpuDxe.h      | 14 ++++++++
>  3 files changed, 69 insertions(+)
> 
> diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
> index 8bda11f08a30..4a416743fb8a 100644
> --- a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
> +++ b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c
> @@ -83,6 +83,23 @@ PageAttributeToGcdAttribute (
>    return GcdAttributes;
>  }
>  
> +/**
> +  Convert a arch specific set of page attributes into a mask

"an arch"
(and again x2 below)

Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>

> +  of EFI_MEMORY_xx constants.
> +
> +  @param  PageAttributes  The set of page attributes.
> +
> +  @retval The mask of EFI_MEMORY_xx constants.
> +
> +**/
> +UINT64
> +RegionAttributeToGcdAttribute (
> +  IN UINTN  PageAttributes
> +  )
> +{
> +  return PageAttributeToGcdAttribute (PageAttributes);
> +}
> +
>  STATIC
>  UINT64
>  GetFirstPageAttribute (
> diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
> index 07faab8216ec..8e0dd5d2aaca 100644
> --- a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
> +++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c
> @@ -13,6 +13,15 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>  #include <Library/MemoryAllocationLib.h>
>  #include "CpuDxe.h"
>  
> +/**
> +  Convert a set of ARM short descriptor section attributes into a mask
> +  of EFI_MEMORY_xx constants.
> +
> +  @param  SectionAttributes   The set of page attributes.
> +  @param  GcdAttributes       Pointer to the return value.
> +
> +**/
> +STATIC
>  EFI_STATUS
>  SectionToGcdAttributes (
>    IN  UINT32  SectionAttributes,
> @@ -74,6 +83,35 @@ SectionToGcdAttributes (
>    return EFI_SUCCESS;
>  }
>  
> +/**
> +  Convert a arch specific set of page attributes into a mask
> +  of EFI_MEMORY_xx constants.
> +
> +  @param  PageAttributes  The set of page attributes.
> +
> +  @retval The mask of EFI_MEMORY_xx constants.
> +
> +**/
> +UINT64
> +RegionAttributeToGcdAttribute (
> +  IN UINTN  PageAttributes
> +  )
> +{
> +  UINT64  Result;
> +
> +  SectionToGcdAttributes (PageAttributes, &Result);
> +  return Result;
> +}
> +
> +/**
> +  Convert a set of ARM short descriptor page attributes into a mask
> +  of EFI_MEMORY_xx constants.
> +
> +  @param  PageAttributes      The set of page attributes.
> +  @param  GcdAttributes       Pointer to the return value.
> +
> +**/
> +STATIC
>  EFI_STATUS
>  PageToGcdAttributes (
>    IN  UINT32  PageAttributes,
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> index ff672390ce51..8cb105dcc841 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> @@ -126,4 +126,18 @@ SetGcdMemorySpaceAttributes (
>    IN UINT64                           Attributes
>    );
>  
> +/**
> +  Convert a arch specific set of page attributes into a mask
> +  of EFI_MEMORY_xx constants.
> +
> +  @param  PageAttributes  The set of page attributes.
> +
> +  @retval The mask of EFI_MEMORY_xx constants.
> +
> +**/
> +UINT64
> +RegionAttributeToGcdAttribute (
> +  IN UINTN  PageAttributes
> +  );
> +
>  #endif // CPU_DXE_H_
> -- 
> 2.39.2
> 
> 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol
  2023-03-13 17:16 ` [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol Ard Biesheuvel
@ 2023-03-15 18:31   ` Leif Lindholm
  2023-03-16  7:19     ` [edk2-devel] " Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Leif Lindholm @ 2023-03-15 18:31 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:47 +0100, Ard Biesheuvel wrote:
> Expose the protocol introduced in v2.10 that permits the caller to
> manage mapping permissions in the page tables.

Nitpicks and a question:

> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  ArmPkg/Drivers/CpuDxe/CpuDxe.c          |   2 +
>  ArmPkg/Drivers/CpuDxe/CpuDxe.h          |   3 +
>  ArmPkg/Drivers/CpuDxe/CpuDxe.inf        |   2 +
>  ArmPkg/Drivers/CpuDxe/MemoryAttribute.c | 271 ++++++++++++++++++++
>  4 files changed, 278 insertions(+)
> 
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> index e6742f0a25fc..d04958e79e52 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> @@ -244,6 +244,8 @@ CpuDxeInitialize (
>                    &mCpuHandle,
>                    &gEfiCpuArchProtocolGuid,
>                    &mCpu,
> +                  &gEfiMemoryAttributeProtocolGuid,
> +                  &mMemoryAttribute,
>                    NULL
>                    );
>  
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> index 8cb105dcc841..ce2981361aca 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> @@ -30,9 +30,12 @@
>  #include <Protocol/Cpu.h>
>  #include <Protocol/DebugSupport.h>
>  #include <Protocol/LoadedImage.h>
> +#include <Protocol/MemoryAttribute.h>
>  
>  extern BOOLEAN  mIsFlushingGCD;
>  
> +extern EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute;
> +
>  /**
>    This function registers and enables the handler specified by InterruptHandler for a processor
>    interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> index 10792b393fc8..e732e21cb94a 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> @@ -23,6 +23,7 @@ [Sources.Common]
>    CpuDxe.h
>    CpuMmuCommon.c
>    Exception.c
> +  MemoryAttribute.c
>  
>  [Sources.ARM]
>    Arm/Mmu.c
> @@ -53,6 +54,7 @@ [LibraryClasses]
>  
>  [Protocols]
>    gEfiCpuArchProtocolGuid
> +  gEfiMemoryAttributeProtocolGuid
>  
>  [Guids]
>    gEfiDebugImageInfoTableGuid
> diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> new file mode 100644
> index 000000000000..b47464c0269e
> --- /dev/null
> +++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> @@ -0,0 +1,271 @@
> +/** @file
> +
> +  Copyright (c) 2023, Google LLC. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "CpuDxe.h"
> +
> +/**
> +  This function retrieves the attributes of the memory region specified by
> +  BaseAddress and Length. If different attributes are got from different part

"from different part" -> "for different parts"?

> +  of the memory region, EFI_NO_MAPPING will be returned.
> +
> +  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
> +  @param  BaseAddress       The physical address that is the start address of
> +                            a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +  @param  Attributes        Pointer to attributes returned.
> +
> +  @retval EFI_SUCCESS           The attributes got for the memory region.
> +  @retval EFI_INVALID_PARAMETER Length is zero.
> +                                Attributes is NULL.
> +  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
> +                                region.
> +  @retval EFI_UNSUPPORTED       The processor does not support one or more
> +                                bytes of the memory resource range specified
> +                                by BaseAddress and Length.

Question: this implementation never returns EFI_UNSUPPORTED.
Is this seen as a "some architectures may have some restricted ranges
not configurable by MMU" which simply does not apply for ARM* -
i.e. the operation is considered "supported" even if on a region not
backed by anything?

> +
> +**/
> +STATIC
> +EFI_STATUS
> +GetMemoryAttributes (
> +  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
> +  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
> +  IN  UINT64                         Length,
> +  OUT UINT64                         *Attributes
> +  )
> +{
> +  UINTN       RegionAddress;
> +  UINTN       RegionLength;
> +  UINTN       RegionAttributes;
> +  UINTN       Union;
> +  UINTN       Intersection;
> +  EFI_STATUS  Status;
> +
> +  if ((Length == 0) || (Attributes == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  DEBUG ((
> +    DEBUG_VERBOSE,
> +    "%a: BaseAddress == 0x%lx, Length == 0x%lx\n",
> +    __FUNCTION__,
> +    BaseAddress,
> +    Length
> +    ));
> +
> +  Union        = 0;
> +  Intersection = MAX_UINTN;
> +
> +  for (RegionAddress = (UINTN)BaseAddress;
> +       RegionAddress < (UINTN)(BaseAddress + Length);
> +       RegionAddress += RegionLength)
> +  {
> +    Status = GetMemoryRegion (
> +               &RegionAddress,
> +               &RegionLength,
> +               &RegionAttributes
> +               );
> +
> +    DEBUG ((
> +      DEBUG_VERBOSE,
> +      "%a: RegionAddress == 0x%lx, RegionLength == 0x%lx, RegionAttributes == 0x%lx\n",
> +      __FUNCTION__,
> +      (UINT64)RegionAddress,
> +      (UINT64)RegionLength,
> +      (UINT64)RegionAttributes
> +      ));
> +
> +    if (EFI_ERROR (Status)) {
> +      return EFI_NO_MAPPING;
> +    }
> +
> +    Union        |= RegionAttributes;
> +    Intersection &= RegionAttributes;
> +  }
> +
> +  DEBUG ((
> +    DEBUG_VERBOSE,
> +    "%a: Union == %lx, Intersection == %lx\n",
> +    __FUNCTION__,
> +    (UINT64)Union,
> +    (UINT64)Intersection
> +    ));
> +
> +  if (Union != Intersection) {
> +    return EFI_NO_MAPPING;
> +  }
> +
> +  *Attributes  = RegionAttributeToGcdAttribute (Union);
> +  *Attributes &= EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP;
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function set given attributes of the memory region specified by

sets

/
    Leif

> +  BaseAddress and Length.
> +
> +  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
> +
> +  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
> +  @param  BaseAddress       The physical address that is the start address of
> +                            a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +  @param  Attributes        The bit mask of attributes to set for the memory
> +                            region.
> +
> +  @retval EFI_SUCCESS           The attributes were set for the memory region.
> +  @retval EFI_INVALID_PARAMETER Length is zero.
> +                                Attributes specified an illegal combination of
> +                                attributes that cannot be set together.
> +  @retval EFI_UNSUPPORTED       The processor does not support one or more
> +                                bytes of the memory resource range specified
> +                                by BaseAddress and Length.
> +                                The bit mask of attributes is not supported for
> +                                the memory resource range specified by
> +                                BaseAddress and Length.
> +  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
> +                                lack of system resources.
> +  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
> +                                controlled by system firmware and cannot be
> +                                updated via the protocol.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +SetMemoryAttributes (
> +  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
> +  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
> +  IN  UINT64                         Length,
> +  IN  UINT64                         Attributes
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
> +    __FUNCTION__,
> +    (UINTN)BaseAddress,
> +    (UINTN)Length,
> +    (UINTN)Attributes
> +    ));
> +
> +  if ((Length == 0) ||
> +      ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((Attributes & EFI_MEMORY_RP) != 0) {
> +    Status = ArmSetMemoryRegionNoAccess (BaseAddress, Length);
> +    if (EFI_ERROR (Status)) {
> +      return EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  if ((Attributes & EFI_MEMORY_RO) != 0) {
> +    Status = ArmSetMemoryRegionReadOnly (BaseAddress, Length);
> +    if (EFI_ERROR (Status)) {
> +      return EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  if ((Attributes & EFI_MEMORY_XP) != 0) {
> +    Status = ArmSetMemoryRegionNoExec (BaseAddress, Length);
> +    if (EFI_ERROR (Status)) {
> +      return EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function clears given attributes of the memory region specified by
> +  BaseAddress and Length.
> +
> +  The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
> +
> +  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
> +  @param  BaseAddress       The physical address that is the start address of
> +                            a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +  @param  Attributes        The bit mask of attributes to clear for the memory
> +                            region.
> +
> +  @retval EFI_SUCCESS           The attributes were cleared for the memory region.
> +  @retval EFI_INVALID_PARAMETER Length is zero.
> +                                Attributes specified an illegal combination of
> +                                attributes that cannot be cleared together.
> +  @retval EFI_UNSUPPORTED       The processor does not support one or more
> +                                bytes of the memory resource range specified
> +                                by BaseAddress and Length.
> +                                The bit mask of attributes is not supported for
> +                                the memory resource range specified by
> +                                BaseAddress and Length.
> +  @retval EFI_OUT_OF_RESOURCES  Requested attributes cannot be applied due to
> +                                lack of system resources.
> +  @retval EFI_ACCESS_DENIED     Attributes for the requested memory region are
> +                                controlled by system firmware and cannot be
> +                                updated via the protocol.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +ClearMemoryAttributes (
> +  IN  EFI_MEMORY_ATTRIBUTE_PROTOCOL  *This,
> +  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
> +  IN  UINT64                         Length,
> +  IN  UINT64                         Attributes
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
> +    __FUNCTION__,
> +    (UINTN)BaseAddress,
> +    (UINTN)Length,
> +    (UINTN)Attributes
> +    ));
> +
> +  if ((Length == 0) ||
> +      ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
> +  {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((Attributes & EFI_MEMORY_RP) != 0) {
> +    Status = ArmClearMemoryRegionNoAccess (BaseAddress, Length);
> +    if (EFI_ERROR (Status)) {
> +      return EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  if ((Attributes & EFI_MEMORY_RO) != 0) {
> +    Status = ArmClearMemoryRegionReadOnly (BaseAddress, Length);
> +    if (EFI_ERROR (Status)) {
> +      return EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  if ((Attributes & EFI_MEMORY_XP) != 0) {
> +    Status = ArmClearMemoryRegionNoExec (BaseAddress, Length);
> +    if (EFI_ERROR (Status)) {
> +      return EFI_UNSUPPORTED;
> +    }
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute = {
> +  GetMemoryAttributes,
> +  SetMemoryAttributes,
> +  ClearMemoryAttributes
> +};
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol
  2023-03-15 18:31   ` Leif Lindholm
@ 2023-03-16  7:19     ` Ard Biesheuvel
  2023-03-16  9:27       ` Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-16  7:19 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Wed, 15 Mar 2023 at 19:31, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>
> On Mon, Mar 13, 2023 at 18:16:47 +0100, Ard Biesheuvel wrote:
> > Expose the protocol introduced in v2.10 that permits the caller to
> > manage mapping permissions in the page tables.
>
> Nitpicks and a question:
>
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> >  ArmPkg/Drivers/CpuDxe/CpuDxe.c          |   2 +
> >  ArmPkg/Drivers/CpuDxe/CpuDxe.h          |   3 +
> >  ArmPkg/Drivers/CpuDxe/CpuDxe.inf        |   2 +
> >  ArmPkg/Drivers/CpuDxe/MemoryAttribute.c | 271 ++++++++++++++++++++
> >  4 files changed, 278 insertions(+)
> >
> > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > index e6742f0a25fc..d04958e79e52 100644
> > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > @@ -244,6 +244,8 @@ CpuDxeInitialize (
> >                    &mCpuHandle,
> >                    &gEfiCpuArchProtocolGuid,
> >                    &mCpu,
> > +                  &gEfiMemoryAttributeProtocolGuid,
> > +                  &mMemoryAttribute,
> >                    NULL
> >                    );
> >
> > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > index 8cb105dcc841..ce2981361aca 100644
> > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > @@ -30,9 +30,12 @@
> >  #include <Protocol/Cpu.h>
> >  #include <Protocol/DebugSupport.h>
> >  #include <Protocol/LoadedImage.h>
> > +#include <Protocol/MemoryAttribute.h>
> >
> >  extern BOOLEAN  mIsFlushingGCD;
> >
> > +extern EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute;
> > +
> >  /**
> >    This function registers and enables the handler specified by InterruptHandler for a processor
> >    interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > index 10792b393fc8..e732e21cb94a 100644
> > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > @@ -23,6 +23,7 @@ [Sources.Common]
> >    CpuDxe.h
> >    CpuMmuCommon.c
> >    Exception.c
> > +  MemoryAttribute.c
> >
> >  [Sources.ARM]
> >    Arm/Mmu.c
> > @@ -53,6 +54,7 @@ [LibraryClasses]
> >
> >  [Protocols]
> >    gEfiCpuArchProtocolGuid
> > +  gEfiMemoryAttributeProtocolGuid
> >
> >  [Guids]
> >    gEfiDebugImageInfoTableGuid
> > diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> > new file mode 100644
> > index 000000000000..b47464c0269e
> > --- /dev/null
> > +++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> > @@ -0,0 +1,271 @@
> > +/** @file
> > +
> > +  Copyright (c) 2023, Google LLC. All rights reserved.
> > +
> > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#include "CpuDxe.h"
> > +
> > +/**
> > +  This function retrieves the attributes of the memory region specified by
> > +  BaseAddress and Length. If different attributes are got from different part
>
> "from different part" -> "for different parts"?
>

Yeah. this is copy/paste from the protocol definition, which prose I
didn't write. I'll fix up both instances.

> > +  of the memory region, EFI_NO_MAPPING will be returned.
> > +
> > +  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
> > +  @param  BaseAddress       The physical address that is the start address of
> > +                            a memory region.
> > +  @param  Length            The size in bytes of the memory region.
> > +  @param  Attributes        Pointer to attributes returned.
> > +
> > +  @retval EFI_SUCCESS           The attributes got for the memory region.
> > +  @retval EFI_INVALID_PARAMETER Length is zero.
> > +                                Attributes is NULL.
> > +  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
> > +                                region.
> > +  @retval EFI_UNSUPPORTED       The processor does not support one or more
> > +                                bytes of the memory resource range specified
> > +                                by BaseAddress and Length.
>
> Question: this implementation never returns EFI_UNSUPPORTED.
> Is this seen as a "some architectures may have some restricted ranges
> not configurable by MMU" which simply does not apply for ARM* -
> i.e. the operation is considered "supported" even if on a region not
> backed by anything?
>

Yeah, good point.

The UEFI spec does not really reason about this at all in the
specification of the protocol, but I guess it would make a lot of
sense to only allow this to be used on regions that are marked as
system memory in the GCD memory map.

I'll have a stab at implementing it like that.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol
  2023-03-16  7:19     ` [edk2-devel] " Ard Biesheuvel
@ 2023-03-16  9:27       ` Ard Biesheuvel
  2023-03-16 11:41         ` Leif Lindholm
  0 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-16  9:27 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Thu, 16 Mar 2023 at 08:19, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Wed, 15 Mar 2023 at 19:31, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
> >
> > On Mon, Mar 13, 2023 at 18:16:47 +0100, Ard Biesheuvel wrote:
> > > Expose the protocol introduced in v2.10 that permits the caller to
> > > manage mapping permissions in the page tables.
> >
> > Nitpicks and a question:
> >
> > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > ---
> > >  ArmPkg/Drivers/CpuDxe/CpuDxe.c          |   2 +
> > >  ArmPkg/Drivers/CpuDxe/CpuDxe.h          |   3 +
> > >  ArmPkg/Drivers/CpuDxe/CpuDxe.inf        |   2 +
> > >  ArmPkg/Drivers/CpuDxe/MemoryAttribute.c | 271 ++++++++++++++++++++
> > >  4 files changed, 278 insertions(+)
> > >
> > > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > > index e6742f0a25fc..d04958e79e52 100644
> > > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > > @@ -244,6 +244,8 @@ CpuDxeInitialize (
> > >                    &mCpuHandle,
> > >                    &gEfiCpuArchProtocolGuid,
> > >                    &mCpu,
> > > +                  &gEfiMemoryAttributeProtocolGuid,
> > > +                  &mMemoryAttribute,
> > >                    NULL
> > >                    );
> > >
> > > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > > index 8cb105dcc841..ce2981361aca 100644
> > > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > > @@ -30,9 +30,12 @@
> > >  #include <Protocol/Cpu.h>
> > >  #include <Protocol/DebugSupport.h>
> > >  #include <Protocol/LoadedImage.h>
> > > +#include <Protocol/MemoryAttribute.h>
> > >
> > >  extern BOOLEAN  mIsFlushingGCD;
> > >
> > > +extern EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute;
> > > +
> > >  /**
> > >    This function registers and enables the handler specified by InterruptHandler for a processor
> > >    interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> > > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > > index 10792b393fc8..e732e21cb94a 100644
> > > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > > @@ -23,6 +23,7 @@ [Sources.Common]
> > >    CpuDxe.h
> > >    CpuMmuCommon.c
> > >    Exception.c
> > > +  MemoryAttribute.c
> > >
> > >  [Sources.ARM]
> > >    Arm/Mmu.c
> > > @@ -53,6 +54,7 @@ [LibraryClasses]
> > >
> > >  [Protocols]
> > >    gEfiCpuArchProtocolGuid
> > > +  gEfiMemoryAttributeProtocolGuid
> > >
> > >  [Guids]
> > >    gEfiDebugImageInfoTableGuid
> > > diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> > > new file mode 100644
> > > index 000000000000..b47464c0269e
> > > --- /dev/null
> > > +++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> > > @@ -0,0 +1,271 @@
> > > +/** @file
> > > +
> > > +  Copyright (c) 2023, Google LLC. All rights reserved.
> > > +
> > > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > > +
> > > +**/
> > > +
> > > +#include "CpuDxe.h"
> > > +
> > > +/**
> > > +  This function retrieves the attributes of the memory region specified by
> > > +  BaseAddress and Length. If different attributes are got from different part
> >
> > "from different part" -> "for different parts"?
> >
>
> Yeah. this is copy/paste from the protocol definition, which prose I
> didn't write. I'll fix up both instances.
>
> > > +  of the memory region, EFI_NO_MAPPING will be returned.
> > > +
> > > +  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
> > > +  @param  BaseAddress       The physical address that is the start address of
> > > +                            a memory region.
> > > +  @param  Length            The size in bytes of the memory region.
> > > +  @param  Attributes        Pointer to attributes returned.
> > > +
> > > +  @retval EFI_SUCCESS           The attributes got for the memory region.
> > > +  @retval EFI_INVALID_PARAMETER Length is zero.
> > > +                                Attributes is NULL.
> > > +  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
> > > +                                region.
> > > +  @retval EFI_UNSUPPORTED       The processor does not support one or more
> > > +                                bytes of the memory resource range specified
> > > +                                by BaseAddress and Length.
> >
> > Question: this implementation never returns EFI_UNSUPPORTED.
> > Is this seen as a "some architectures may have some restricted ranges
> > not configurable by MMU" which simply does not apply for ARM* -
> > i.e. the operation is considered "supported" even if on a region not
> > backed by anything?
> >
>
> Yeah, good point.
>
> The UEFI spec does not really reason about this at all in the
> specification of the protocol, but I guess it would make a lot of
> sense to only allow this to be used on regions that are marked as
> system memory in the GCD memory map.
>
> I'll have a stab at implementing it like that.

Something like this applied on top should work:

diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
index b47464c0269e..46f0760b8e3b 100644
--- a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
+++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
@@ -8,6 +8,41 @@

 #include "CpuDxe.h"

+/**
+  Check whether the provided memory range is covered by a single entry of type
+  EfiGcdSystemMemory in the GCD memory map.
+
+  @param  BaseAddress       The physical address that is the start address of
+                            a memory region.
+  @param  Length            The size in bytes of the memory region.
+
+  @return Whether the region is system memory or not.
+**/
+STATIC
+BOOLEAN
+RegionIsSystemMemory (
+  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
+  IN  UINT64                         Length
+  )
+{
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  GcdDescriptor;
+  EFI_PHYSICAL_ADDRESS             GcdEndAddress;
+  EFI_STATUS                       Status;
+
+  Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor);
+  if (EFI_ERROR (Status) ||
+      (GcdDescriptor.GcdMemoryType != EfiGcdMemoryTypeSystemMemory)) {
+    return FALSE;
+  }
+
+  GcdEndAddress = GcdDescriptor.BaseAddress + GcdDescriptor.Length;
+
+  //
+  // Return TRUE if the GCD descriptor covers the range entirely
+  //
+  return GcdEndAddress >= (BaseAddress + Length);
+}
+
 /**
   This function retrieves the attributes of the memory region specified by
   BaseAddress and Length. If different attributes are got from different part
@@ -49,6 +84,10 @@ GetMemoryAttributes (
     return EFI_INVALID_PARAMETER;
   }

+  if (!RegionIsSystemMemory (BaseAddress, Length)) {
+    return EFI_UNSUPPORTED;
+  }
+
   DEBUG ((
     DEBUG_VERBOSE,
     "%a: BaseAddress == 0x%lx, Length == 0x%lx\n",
@@ -160,6 +199,10 @@ SetMemoryAttributes (
     return EFI_INVALID_PARAMETER;
   }

+  if (!RegionIsSystemMemory (BaseAddress, Length)) {
+    return EFI_UNSUPPORTED;
+  }
+
   if ((Attributes & EFI_MEMORY_RP) != 0) {
     Status = ArmSetMemoryRegionNoAccess (BaseAddress, Length);
     if (EFI_ERROR (Status)) {
@@ -240,6 +283,10 @@ ClearMemoryAttributes (
     return EFI_INVALID_PARAMETER;
   }

+  if (!RegionIsSystemMemory (BaseAddress, Length)) {
+    return EFI_UNSUPPORTED;
+  }
+
   if ((Attributes & EFI_MEMORY_RP) != 0) {
     Status = ArmClearMemoryRegionNoAccess (BaseAddress, Length);
     if (EFI_ERROR (Status)) {

^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol
  2023-03-16  9:27       ` Ard Biesheuvel
@ 2023-03-16 11:41         ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 11:41 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Thu, Mar 16, 2023 at 10:27:48 +0100, Ard Biesheuvel wrote:
> On Thu, 16 Mar 2023 at 08:19, Ard Biesheuvel <ardb@kernel.org> wrote:
> >
> > On Wed, 15 Mar 2023 at 19:31, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
> > >
> > > On Mon, Mar 13, 2023 at 18:16:47 +0100, Ard Biesheuvel wrote:
> > > > Expose the protocol introduced in v2.10 that permits the caller to
> > > > manage mapping permissions in the page tables.
> > >
> > > Nitpicks and a question:
> > >
> > > > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > > > ---
> > > >  ArmPkg/Drivers/CpuDxe/CpuDxe.c          |   2 +
> > > >  ArmPkg/Drivers/CpuDxe/CpuDxe.h          |   3 +
> > > >  ArmPkg/Drivers/CpuDxe/CpuDxe.inf        |   2 +
> > > >  ArmPkg/Drivers/CpuDxe/MemoryAttribute.c | 271 ++++++++++++++++++++
> > > >  4 files changed, 278 insertions(+)
> > > >
> > > > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > > > index e6742f0a25fc..d04958e79e52 100644
> > > > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > > > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> > > > @@ -244,6 +244,8 @@ CpuDxeInitialize (
> > > >                    &mCpuHandle,
> > > >                    &gEfiCpuArchProtocolGuid,
> > > >                    &mCpu,
> > > > +                  &gEfiMemoryAttributeProtocolGuid,
> > > > +                  &mMemoryAttribute,
> > > >                    NULL
> > > >                    );
> > > >
> > > > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > > > index 8cb105dcc841..ce2981361aca 100644
> > > > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > > > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> > > > @@ -30,9 +30,12 @@
> > > >  #include <Protocol/Cpu.h>
> > > >  #include <Protocol/DebugSupport.h>
> > > >  #include <Protocol/LoadedImage.h>
> > > > +#include <Protocol/MemoryAttribute.h>
> > > >
> > > >  extern BOOLEAN  mIsFlushingGCD;
> > > >
> > > > +extern EFI_MEMORY_ATTRIBUTE_PROTOCOL  mMemoryAttribute;
> > > > +
> > > >  /**
> > > >    This function registers and enables the handler specified by InterruptHandler for a processor
> > > >    interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> > > > diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > > > index 10792b393fc8..e732e21cb94a 100644
> > > > --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > > > +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> > > > @@ -23,6 +23,7 @@ [Sources.Common]
> > > >    CpuDxe.h
> > > >    CpuMmuCommon.c
> > > >    Exception.c
> > > > +  MemoryAttribute.c
> > > >
> > > >  [Sources.ARM]
> > > >    Arm/Mmu.c
> > > > @@ -53,6 +54,7 @@ [LibraryClasses]
> > > >
> > > >  [Protocols]
> > > >    gEfiCpuArchProtocolGuid
> > > > +  gEfiMemoryAttributeProtocolGuid
> > > >
> > > >  [Guids]
> > > >    gEfiDebugImageInfoTableGuid
> > > > diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> > > > new file mode 100644
> > > > index 000000000000..b47464c0269e
> > > > --- /dev/null
> > > > +++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> > > > @@ -0,0 +1,271 @@
> > > > +/** @file
> > > > +
> > > > +  Copyright (c) 2023, Google LLC. All rights reserved.
> > > > +
> > > > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > > > +
> > > > +**/
> > > > +
> > > > +#include "CpuDxe.h"
> > > > +
> > > > +/**
> > > > +  This function retrieves the attributes of the memory region specified by
> > > > +  BaseAddress and Length. If different attributes are got from different part
> > >
> > > "from different part" -> "for different parts"?
> > >
> >
> > Yeah. this is copy/paste from the protocol definition, which prose I
> > didn't write. I'll fix up both instances.
> >
> > > > +  of the memory region, EFI_NO_MAPPING will be returned.
> > > > +
> > > > +  @param  This              The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
> > > > +  @param  BaseAddress       The physical address that is the start address of
> > > > +                            a memory region.
> > > > +  @param  Length            The size in bytes of the memory region.
> > > > +  @param  Attributes        Pointer to attributes returned.
> > > > +
> > > > +  @retval EFI_SUCCESS           The attributes got for the memory region.
> > > > +  @retval EFI_INVALID_PARAMETER Length is zero.
> > > > +                                Attributes is NULL.
> > > > +  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory
> > > > +                                region.
> > > > +  @retval EFI_UNSUPPORTED       The processor does not support one or more
> > > > +                                bytes of the memory resource range specified
> > > > +                                by BaseAddress and Length.
> > >
> > > Question: this implementation never returns EFI_UNSUPPORTED.
> > > Is this seen as a "some architectures may have some restricted ranges
> > > not configurable by MMU" which simply does not apply for ARM* -
> > > i.e. the operation is considered "supported" even if on a region not
> > > backed by anything?
> > >
> >
> > Yeah, good point.
> >
> > The UEFI spec does not really reason about this at all in the
> > specification of the protocol, but I guess it would make a lot of
> > sense to only allow this to be used on regions that are marked as
> > system memory in the GCD memory map.
> >
> > I'll have a stab at implementing it like that.
> 
> Something like this applied on top should work:

Perfect, thanks.
With this folded in:
Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>

> diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> index b47464c0269e..46f0760b8e3b 100644
> --- a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> +++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> @@ -8,6 +8,41 @@
> 
>  #include "CpuDxe.h"
> 
> +/**
> +  Check whether the provided memory range is covered by a single entry of type
> +  EfiGcdSystemMemory in the GCD memory map.
> +
> +  @param  BaseAddress       The physical address that is the start address of
> +                            a memory region.
> +  @param  Length            The size in bytes of the memory region.
> +
> +  @return Whether the region is system memory or not.
> +**/
> +STATIC
> +BOOLEAN
> +RegionIsSystemMemory (
> +  IN  EFI_PHYSICAL_ADDRESS           BaseAddress,
> +  IN  UINT64                         Length
> +  )
> +{
> +  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  GcdDescriptor;
> +  EFI_PHYSICAL_ADDRESS             GcdEndAddress;
> +  EFI_STATUS                       Status;
> +
> +  Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor);
> +  if (EFI_ERROR (Status) ||
> +      (GcdDescriptor.GcdMemoryType != EfiGcdMemoryTypeSystemMemory)) {
> +    return FALSE;
> +  }
> +
> +  GcdEndAddress = GcdDescriptor.BaseAddress + GcdDescriptor.Length;
> +
> +  //
> +  // Return TRUE if the GCD descriptor covers the range entirely
> +  //
> +  return GcdEndAddress >= (BaseAddress + Length);
> +}
> +
>  /**
>    This function retrieves the attributes of the memory region specified by
>    BaseAddress and Length. If different attributes are got from different part
> @@ -49,6 +84,10 @@ GetMemoryAttributes (
>      return EFI_INVALID_PARAMETER;
>    }
> 
> +  if (!RegionIsSystemMemory (BaseAddress, Length)) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
>    DEBUG ((
>      DEBUG_VERBOSE,
>      "%a: BaseAddress == 0x%lx, Length == 0x%lx\n",
> @@ -160,6 +199,10 @@ SetMemoryAttributes (
>      return EFI_INVALID_PARAMETER;
>    }
> 
> +  if (!RegionIsSystemMemory (BaseAddress, Length)) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
>    if ((Attributes & EFI_MEMORY_RP) != 0) {
>      Status = ArmSetMemoryRegionNoAccess (BaseAddress, Length);
>      if (EFI_ERROR (Status)) {
> @@ -240,6 +283,10 @@ ClearMemoryAttributes (
>      return EFI_INVALID_PARAMETER;
>    }
> 
> +  if (!RegionIsSystemMemory (BaseAddress, Length)) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
>    if ((Attributes & EFI_MEMORY_RP) != 0) {
>      Status = ArmClearMemoryRegionNoAccess (BaseAddress, Length);
>      if (EFI_ERROR (Status)) {

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib
  2023-03-13 17:16 ` [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib Ard Biesheuvel
@ 2023-03-16 13:27   ` Leif Lindholm
  2023-03-16 14:20     ` Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 13:27 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:57 +0100, Ard Biesheuvel wrote:
> Implement the ARM version of a NULL class library that can be overlaid
> on top of the DXE core to equip it right from its launch with an
> implementation of the CPU arch protocol member that sets type and
> permission attributes on memory regions.
> 
> This bridges the gap between dispatch of DXE core and dispatch of the
> DXE driver that implements the CPU arch protocol, removing the need to
> rely on memory mappings that are writable and executable at the same
> time.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  ArmPkg/ArmPkg.dsc                                                  |  1 +
>  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c   | 78 ++++++++++++++++++++
>  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf | 28 +++++++
>  3 files changed, 107 insertions(+)
> 
> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> index 3fb95d1951a9..43eb0f4f463e 100644
> --- a/ArmPkg/ArmPkg.dsc
> +++ b/ArmPkg/ArmPkg.dsc
> @@ -119,6 +119,7 @@ [Components.common]
>    ArmPkg/Library/ArmPsciResetSystemLib/ArmPsciResetSystemLib.inf
>    ArmPkg/Library/ArmExceptionLib/ArmExceptionLib.inf
>    ArmPkg/Library/ArmExceptionLib/ArmRelocateExceptionLib.inf
> +  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
>  
>    ArmPkg/Drivers/CpuDxe/CpuDxe.inf
>    ArmPkg/Drivers/CpuPei/CpuPei.inf
> diff --git a/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
> new file mode 100644
> index 000000000000..866dbbdaa7d5
> --- /dev/null
> +++ b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
> @@ -0,0 +1,78 @@
> +/** @file
> +  Overlay implementation of DXE core gCpuSetMemoryAttributes for ARM.
> +
> +  Copyright (c) 2023, Google LLC. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <PiDxe.h>
> +
> +#include <Library/ArmMmuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Protocol/Cpu.h>
> +
> +extern EFI_CPU_SET_MEMORY_ATTRIBUTES  gCpuSetMemoryAttributes;

Could this declaration live in a global include?

/
    Leif

> +
> +STATIC UINTN  mRecursionLevel;
> +
> +/**
> +  Clone of CPU_ARCH_PROTOCOL::SetMemoryAttributes() which is made available to
> +  the DXE core by NULL library class resolution, so that it can manage page
> +  permissions right from the start.
> +
> +  @param  This                  CPU arch protocol pointer, should be NULL.
> +  @param  BaseAddress           Start address of the region.
> +  @param  Length                Size of the region, in bytes.
> +  @param  Attributes            Attributes to set on the region.
> +
> +  @retval EFI_SUCCESS           Operation completed successfully.
> +  @retval EFI_OUT_OF_RESOURCES  Operation failed due to lack of memory.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +EarlyArmSetMemoryAttributes (
> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN  EFI_PHYSICAL_ADDRESS   BaseAddress,
> +  IN  UINT64                 Length,
> +  IN  UINT64                 Attributes
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  // There are cases where the use of strict memory permissions may trigger
> +  // unbounded recursion in the page table code. This happens when setting
> +  // memory permissions results in a page table split and therefore a page
> +  // allocation, which could trigger a recursive invocation of this function.
> +  ASSERT (mRecursionLevel < 2);
> +
> +  mRecursionLevel++;
> +
> +  Status = ArmSetMemoryAttributes (
> +             BaseAddress,
> +             Length,
> +             Attributes
> +             );
> +
> +  mRecursionLevel--;
> +  return Status;
> +}
> +
> +/**
> +  Library constructor.
> +
> +  @retval RETURN_SUCCESS   Operation successful.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +ArmSetMemoryOverrideLibConstructor (
> +  VOID
> +  )
> +{
> +  gCpuSetMemoryAttributes = EarlyArmSetMemoryAttributes;
> +
> +  return RETURN_SUCCESS;
> +}
> diff --git a/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
> new file mode 100644
> index 000000000000..2dc9d42d09bd
> --- /dev/null
> +++ b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
> @@ -0,0 +1,28 @@
> +## @file
> +#  Overlay implementation of DXE core gCpuSetMemoryAttributes for ARM.
> +#
> +#  Copyright (c) 2023, Google LLC. All rights reserved.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 1.29
> +  BASE_NAME                      = ArmSetMemoryOverrideLib
> +  FILE_GUID                      = 849a43c0-6ad9-428e-8a5a-e090f7853bd3
> +  MODULE_TYPE                    = BASE
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = NULL|DXE_CORE
> +  CONSTRUCTOR                    = ArmSetMemoryOverrideLibConstructor
> +
> +[Sources.common]
> +  ArmSetMemoryOverrideLib.c
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> +  ArmMmuLib
> +  DebugLib
> -- 
> 2.39.2
> 
> 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
  2023-03-13 17:16 ` [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution Ard Biesheuvel
@ 2023-03-16 13:33   ` Leif Lindholm
  2023-03-16 13:50     ` [edk2-devel] " Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 13:33 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:16:59 +0100, Ard Biesheuvel wrote:
> Deal with DRAM memory potentially being mapped with non-executable
> permissions, by mapping the DXE core code sections explicitly before
> launch.

Could you add a note about why LoadPeCoffImage/LoadDxeCoreFromFfsFile
are made private?

/
    Leif

> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  EmbeddedPkg/Include/Library/PrePiLib.h          | 16 ------
>  EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c | 51 ++++++++++++++++++++
>  EmbeddedPkg/Library/PrePiLib/PrePi.h            | 13 +++++
>  EmbeddedPkg/Library/PrePiLib/PrePiLib.c         |  4 ++
>  EmbeddedPkg/Library/PrePiLib/PrePiLib.inf       | 12 +++++
>  EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c | 23 +++++++++
>  6 files changed, 103 insertions(+), 16 deletions(-)
> 
> diff --git a/EmbeddedPkg/Include/Library/PrePiLib.h b/EmbeddedPkg/Include/Library/PrePiLib.h
> index 93a9115eac2d..14f2bbc38dae 100644
> --- a/EmbeddedPkg/Include/Library/PrePiLib.h
> +++ b/EmbeddedPkg/Include/Library/PrePiLib.h
> @@ -758,22 +758,6 @@ AllocateAlignedPages (
>    IN UINTN  Alignment
>    );
>  
> -EFI_STATUS
> -EFIAPI
> -LoadPeCoffImage (
> -  IN  VOID                  *PeCoffImage,
> -  OUT EFI_PHYSICAL_ADDRESS  *ImageAddress,
> -  OUT UINT64                *ImageSize,
> -  OUT EFI_PHYSICAL_ADDRESS  *EntryPoint
> -  );
> -
> -EFI_STATUS
> -EFIAPI
> -LoadDxeCoreFromFfsFile (
> -  IN EFI_PEI_FILE_HANDLE  FileHandle,
> -  IN UINTN                StackSize
> -  );
> -
>  EFI_STATUS
>  EFIAPI
>  LoadDxeCoreFromFv (
> diff --git a/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
> new file mode 100644
> index 000000000000..40d4ed9d77bd
> --- /dev/null
> +++ b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
> @@ -0,0 +1,51 @@
> +/** @file
> +  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "PrePi.h"
> +
> +#include <Library/ArmMmuLib.h>
> +
> +/**
> +  Remap the code section of the DXE core with the read-only and executable
> +  permissions.
> +
> +  @param  ImageContext    The image context describing the loaded PE/COFF image
> +
> +**/
> +VOID
> +EFIAPI
> +RemapDxeCore (
> +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
> +  )
> +{
> +  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
> +  EFI_IMAGE_SECTION_HEADER             *Section;
> +  UINTN                                Index;
> +
> +  Hdr.Union = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)ImageContext->Handle +
> +                                                  ImageContext->PeCoffHeaderOffset);
> +  ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
> +
> +  Section = (EFI_IMAGE_SECTION_HEADER *)((UINT8 *)Hdr.Union + sizeof (UINT32) +
> +                                         sizeof (EFI_IMAGE_FILE_HEADER) +
> +                                         Hdr.Pe32->FileHeader.SizeOfOptionalHeader
> +                                         );
> +
> +  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
> +    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
> +      ArmSetMemoryRegionReadOnly (
> +        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
> +        Section[Index].Misc.VirtualSize
> +        );
> +
> +      ArmClearMemoryRegionNoExec (
> +        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
> +        Section[Index].Misc.VirtualSize
> +        );
> +    }
> +  }
> +}
> diff --git a/EmbeddedPkg/Library/PrePiLib/PrePi.h b/EmbeddedPkg/Library/PrePiLib/PrePi.h
> index a00c946512f4..a0f8837d1d37 100644
> --- a/EmbeddedPkg/Library/PrePiLib/PrePi.h
> +++ b/EmbeddedPkg/Library/PrePiLib/PrePi.h
> @@ -37,4 +37,17 @@
>  #define GET_GUID_HOB_DATA(GuidHob)       ((VOID *) (((UINT8 *) &((GuidHob)->Name)) + sizeof (EFI_GUID)))
>  #define GET_GUID_HOB_DATA_SIZE(GuidHob)  (((GuidHob)->Header).HobLength - sizeof (EFI_HOB_GUID_TYPE))
>  
> +/**
> +  Remap the code section of the DXE core with the read-only and executable
> +  permissions.
> +
> +  @param  ImageContext    The image context describing the loaded PE/COFF image
> +
> +**/
> +VOID
> +EFIAPI
> +RemapDxeCore (
> +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
> +  );
> +
>  #endif
> diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
> index 3cf866dab248..188ad5c518a8 100644
> --- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
> +++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
> @@ -54,6 +54,7 @@ AllocateCodePages (
>    return NULL;
>  }
>  
> +STATIC
>  EFI_STATUS
>  EFIAPI
>  LoadPeCoffImage (
> @@ -105,6 +106,8 @@ LoadPeCoffImage (
>    //
>    InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize);
>  
> +  RemapDxeCore (&ImageContext);
> +
>    return Status;
>  }
>  
> @@ -114,6 +117,7 @@ VOID
>    IN  VOID *HobStart
>    );
>  
> +STATIC
>  EFI_STATUS
>  EFIAPI
>  LoadDxeCoreFromFfsFile (
> diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
> index 090bfe888f52..2df5928c51d5 100644
> --- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
> +++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
> @@ -31,11 +31,20 @@ [Sources.common]
>    FwVol.c
>    PrePiLib.c
>  
> +[Sources.X64, Sources.IA32]
> +  X86/RemapDxeCore.c
> +
> +[Sources.AARCH64, Sources.ARM]
> +  Arm/RemapDxeCore.c
> +
>  [Packages]
>    MdePkg/MdePkg.dec
>    EmbeddedPkg/EmbeddedPkg.dec
>    MdeModulePkg/MdeModulePkg.dec
>  
> +[Packages.ARM, Packages.AARCH64]
> +  ArmPkg/ArmPkg.dec
> +
>  [LibraryClasses]
>    BaseLib
>    DebugLib
> @@ -50,6 +59,9 @@ [LibraryClasses]
>    PerformanceLib
>    HobLib
>  
> +[LibraryClasses.ARM, LibraryClasses.AARCH64]
> +  ArmMmuLib
> +
>  [Guids]
>    gEfiMemoryTypeInformationGuid
>  
> diff --git a/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
> new file mode 100644
> index 000000000000..1899c050fdec
> --- /dev/null
> +++ b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
> @@ -0,0 +1,23 @@
> +/** @file
> +  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "PrePi.h"
> +
> +/**
> +  Remap the code section of the DXE core with the read-only and executable
> +  permissions.
> +
> +  @param  ImageContext    The image context describing the loaded PE/COFF image
> +
> +**/
> +VOID
> +EFIAPI
> +RemapDxeCore (
> +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
> +  )
> +{
> +}
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data
  2023-03-13 17:17 ` [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data Ard Biesheuvel
@ 2023-03-16 13:46   ` Leif Lindholm
  2023-03-16 14:30     ` Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 13:46 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:17:03 +0100, Ard Biesheuvel wrote:
> The AArch64 ARM architecture supports a hardware enforcement mode for
> mutual exclusion between code and data: any page that is mapped writable
> is implicitly non-executable as well.
> 
> This means that remapping part of a runtime image for reapplying
> relocation fixups may result in any code sharing the same page to lose
> its executable permissions.
> 
> Let's avoid this, by moving all quantities that are subject to
> relocation fixups to a separate page if the build is using 64k section
> alignment, which is only the case when building a runtime driver for
> AArch64.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  BaseTools/Scripts/GccBase.lds | 13 +++++++++++--
>  1 file changed, 11 insertions(+), 2 deletions(-)
> 
> diff --git a/BaseTools/Scripts/GccBase.lds b/BaseTools/Scripts/GccBase.lds
> index 83cebd29d599..63e097e0727c 100644
> --- a/BaseTools/Scripts/GccBase.lds
> +++ b/BaseTools/Scripts/GccBase.lds
> @@ -21,9 +21,8 @@ SECTIONS {
>    . = PECOFF_HEADER_SIZE;
>  
>    .text : ALIGN(CONSTANT(COMMONPAGESIZE)) {
> -    *(.text .text.* .stub .gnu.linkonce.t.*)
> +    *(.text .text.* .stub .gnu.linkonce.t.* .plt)
>      *(.rodata .rodata.* .gnu.linkonce.r.*)
> -    *(.got .got.*)
>  
>      /*
>       * The contents of AutoGen.c files are mostly constant from the POV of the
> @@ -34,6 +33,16 @@ SECTIONS {
>       * emitted GUIDs here.
>       */
>      *:AutoGen.obj(.data.g*Guid)
> +
> +    /*
> +     * AArch64 runtime drivers use 64k alignment, and may run in a mode where

Hmm ... is this strictly speaking true?
I.e., yes, all 4k pages within a 64k page need to share the same
permissions, but that could arguably be provided by pooling 4k
allocations together for multiple runtime drivers?

Will this alignment constraint conflict with that, or just help
enforce the mapping compatibility?

/
    Leif

> +     * mutual exclusion of RO and XP mappings are hardware enforced. In such
> +     * cases, the input sections below, which carry any quantities that are
> +     * subject to relocation fixups at runtime, must not share a 4 KiB page
> +     * with any code content.
> +     */
> +    . = ALIGN(CONSTANT(COMMONPAGESIZE) > 0x1000 ? 0x1000 : 0x20);
> +    *(.got .got.* .data.rel.ro)
>    }
>  
>    /*
> -- 
> 2.39.2
> 
> 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
  2023-03-16 13:33   ` Leif Lindholm
@ 2023-03-16 13:50     ` Ard Biesheuvel
  2023-03-16 14:09       ` Leif Lindholm
  0 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-16 13:50 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Thu, 16 Mar 2023 at 14:33, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>
> On Mon, Mar 13, 2023 at 18:16:59 +0100, Ard Biesheuvel wrote:
> > Deal with DRAM memory potentially being mapped with non-executable
> > permissions, by mapping the DXE core code sections explicitly before
> > launch.
>
> Could you add a note about why LoadPeCoffImage/LoadDxeCoreFromFfsFile
> are made private?
>

Actually, they shouldn't - they are used elsewhere too.

I confused myself into thinking that LoadPeCoffImage() should be made
private as it now has 'special' behavior that only applies to DxeCore,
but in fact, the whole purpose in life of PrePi is to load DxeCore and
run it, so that was kind of implied anyway.

So I intend to simply drop those changes.


>
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> >  EmbeddedPkg/Include/Library/PrePiLib.h          | 16 ------
> >  EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c | 51 ++++++++++++++++++++
> >  EmbeddedPkg/Library/PrePiLib/PrePi.h            | 13 +++++
> >  EmbeddedPkg/Library/PrePiLib/PrePiLib.c         |  4 ++
> >  EmbeddedPkg/Library/PrePiLib/PrePiLib.inf       | 12 +++++
> >  EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c | 23 +++++++++
> >  6 files changed, 103 insertions(+), 16 deletions(-)
> >
> > diff --git a/EmbeddedPkg/Include/Library/PrePiLib.h b/EmbeddedPkg/Include/Library/PrePiLib.h
> > index 93a9115eac2d..14f2bbc38dae 100644
> > --- a/EmbeddedPkg/Include/Library/PrePiLib.h
> > +++ b/EmbeddedPkg/Include/Library/PrePiLib.h
> > @@ -758,22 +758,6 @@ AllocateAlignedPages (
> >    IN UINTN  Alignment
> >    );
> >
> > -EFI_STATUS
> > -EFIAPI
> > -LoadPeCoffImage (
> > -  IN  VOID                  *PeCoffImage,
> > -  OUT EFI_PHYSICAL_ADDRESS  *ImageAddress,
> > -  OUT UINT64                *ImageSize,
> > -  OUT EFI_PHYSICAL_ADDRESS  *EntryPoint
> > -  );
> > -
> > -EFI_STATUS
> > -EFIAPI
> > -LoadDxeCoreFromFfsFile (
> > -  IN EFI_PEI_FILE_HANDLE  FileHandle,
> > -  IN UINTN                StackSize
> > -  );
> > -
> >  EFI_STATUS
> >  EFIAPI
> >  LoadDxeCoreFromFv (
> > diff --git a/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
> > new file mode 100644
> > index 000000000000..40d4ed9d77bd
> > --- /dev/null
> > +++ b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
> > @@ -0,0 +1,51 @@
> > +/** @file
> > +  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
> > +
> > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#include "PrePi.h"
> > +
> > +#include <Library/ArmMmuLib.h>
> > +
> > +/**
> > +  Remap the code section of the DXE core with the read-only and executable
> > +  permissions.
> > +
> > +  @param  ImageContext    The image context describing the loaded PE/COFF image
> > +
> > +**/
> > +VOID
> > +EFIAPI
> > +RemapDxeCore (
> > +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
> > +  )
> > +{
> > +  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
> > +  EFI_IMAGE_SECTION_HEADER             *Section;
> > +  UINTN                                Index;
> > +
> > +  Hdr.Union = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)ImageContext->Handle +
> > +                                                  ImageContext->PeCoffHeaderOffset);
> > +  ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
> > +
> > +  Section = (EFI_IMAGE_SECTION_HEADER *)((UINT8 *)Hdr.Union + sizeof (UINT32) +
> > +                                         sizeof (EFI_IMAGE_FILE_HEADER) +
> > +                                         Hdr.Pe32->FileHeader.SizeOfOptionalHeader
> > +                                         );
> > +
> > +  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
> > +    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
> > +      ArmSetMemoryRegionReadOnly (
> > +        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
> > +        Section[Index].Misc.VirtualSize
> > +        );
> > +
> > +      ArmClearMemoryRegionNoExec (
> > +        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
> > +        Section[Index].Misc.VirtualSize
> > +        );
> > +    }
> > +  }
> > +}
> > diff --git a/EmbeddedPkg/Library/PrePiLib/PrePi.h b/EmbeddedPkg/Library/PrePiLib/PrePi.h
> > index a00c946512f4..a0f8837d1d37 100644
> > --- a/EmbeddedPkg/Library/PrePiLib/PrePi.h
> > +++ b/EmbeddedPkg/Library/PrePiLib/PrePi.h
> > @@ -37,4 +37,17 @@
> >  #define GET_GUID_HOB_DATA(GuidHob)       ((VOID *) (((UINT8 *) &((GuidHob)->Name)) + sizeof (EFI_GUID)))
> >  #define GET_GUID_HOB_DATA_SIZE(GuidHob)  (((GuidHob)->Header).HobLength - sizeof (EFI_HOB_GUID_TYPE))
> >
> > +/**
> > +  Remap the code section of the DXE core with the read-only and executable
> > +  permissions.
> > +
> > +  @param  ImageContext    The image context describing the loaded PE/COFF image
> > +
> > +**/
> > +VOID
> > +EFIAPI
> > +RemapDxeCore (
> > +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
> > +  );
> > +
> >  #endif
> > diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
> > index 3cf866dab248..188ad5c518a8 100644
> > --- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
> > +++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
> > @@ -54,6 +54,7 @@ AllocateCodePages (
> >    return NULL;
> >  }
> >
> > +STATIC
> >  EFI_STATUS
> >  EFIAPI
> >  LoadPeCoffImage (
> > @@ -105,6 +106,8 @@ LoadPeCoffImage (
> >    //
> >    InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize);
> >
> > +  RemapDxeCore (&ImageContext);
> > +
> >    return Status;
> >  }
> >
> > @@ -114,6 +117,7 @@ VOID
> >    IN  VOID *HobStart
> >    );
> >
> > +STATIC
> >  EFI_STATUS
> >  EFIAPI
> >  LoadDxeCoreFromFfsFile (
> > diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
> > index 090bfe888f52..2df5928c51d5 100644
> > --- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
> > +++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
> > @@ -31,11 +31,20 @@ [Sources.common]
> >    FwVol.c
> >    PrePiLib.c
> >
> > +[Sources.X64, Sources.IA32]
> > +  X86/RemapDxeCore.c
> > +
> > +[Sources.AARCH64, Sources.ARM]
> > +  Arm/RemapDxeCore.c
> > +
> >  [Packages]
> >    MdePkg/MdePkg.dec
> >    EmbeddedPkg/EmbeddedPkg.dec
> >    MdeModulePkg/MdeModulePkg.dec
> >
> > +[Packages.ARM, Packages.AARCH64]
> > +  ArmPkg/ArmPkg.dec
> > +
> >  [LibraryClasses]
> >    BaseLib
> >    DebugLib
> > @@ -50,6 +59,9 @@ [LibraryClasses]
> >    PerformanceLib
> >    HobLib
> >
> > +[LibraryClasses.ARM, LibraryClasses.AARCH64]
> > +  ArmMmuLib
> > +
> >  [Guids]
> >    gEfiMemoryTypeInformationGuid
> >
> > diff --git a/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
> > new file mode 100644
> > index 000000000000..1899c050fdec
> > --- /dev/null
> > +++ b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
> > @@ -0,0 +1,23 @@
> > +/** @file
> > +  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
> > +
> > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#include "PrePi.h"
> > +
> > +/**
> > +  Remap the code section of the DXE core with the read-only and executable
> > +  permissions.
> > +
> > +  @param  ImageContext    The image context describing the loaded PE/COFF image
> > +
> > +**/
> > +VOID
> > +EFIAPI
> > +RemapDxeCore (
> > +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
> > +  )
> > +{
> > +}
> > --
> > 2.39.2
> >
>
>
> 
>
>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region
  2023-03-13 17:17 ` [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region Ard Biesheuvel
@ 2023-03-16 13:51   ` Leif Lindholm
  2023-03-16 14:00     ` [edk2-devel] " Ard Biesheuvel
  0 siblings, 1 reply; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 13:51 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: devel, Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Mon, Mar 13, 2023 at 18:17:08 +0100, Ard Biesheuvel wrote:
> Currently, we invoke ApplyMemoryProtectionPolicy() after
> CoreInternalFreePages() has returned successfully, in order to update
> the memory permission attributes of the region to match the policy for
> EfiConventionalMemory.
> 
> There are two problems with that:
> - CoreInternalFreePages() will round up the size of the allocation to
>   the appropriate alignment of the memory type, but we only remap the
>   number of pages that was passed by the caller, leaving the remaining
>   pages freed but mapped with the old permissions;
> - in DEBUG builds, we may attempt to clear the entire region while it is
>   still mapped with read-only or read-protect attributes.
> 
> Let's address both issues, by updating the permissions before performing
> the actual conversion.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>  MdeModulePkg/Core/Dxe/Mem/Page.c | 15 ++++++++-------
>  1 file changed, 8 insertions(+), 7 deletions(-)
> 
> diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c
> index 5903ce7ab525..f5b940bbc25b 100644
> --- a/MdeModulePkg/Core/Dxe/Mem/Page.c
> +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c
> @@ -1519,8 +1519,8 @@ CoreAllocatePages (
>    @return EFI_SUCCESS         -Pages successfully freed.
>  
>  **/
> +STATIC
>  EFI_STATUS
> -EFIAPI

This is addressing a historic oversight (possibly caused by the STATIC
function ban), but it's not *really* related to the change in question.

/
    Leif

>  CoreInternalFreePages (
>    IN EFI_PHYSICAL_ADDRESS  Memory,
>    IN UINTN                 NumberOfPages,
> @@ -1574,6 +1574,13 @@ CoreInternalFreePages (
>    NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;
>    NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);
>  
> +  ApplyMemoryProtectionPolicy (
> +    Entry->Type,
> +    EfiConventionalMemory,
> +    Memory,
> +    EFI_PAGES_TO_SIZE (NumberOfPages)
> +    );
> +
>    if (MemoryType != NULL) {
>      *MemoryType = Entry->Type;
>    }
> @@ -1628,12 +1635,6 @@ CoreFreePages (
>        NULL
>        );
>      InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);
> -    ApplyMemoryProtectionPolicy (
> -      MemoryType,
> -      EfiConventionalMemory,
> -      Memory,
> -      EFI_PAGES_TO_SIZE (NumberOfPages)
> -      );
>    }
>  
>    return Status;
> -- 
> 2.39.2
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region
  2023-03-16 13:51   ` Leif Lindholm
@ 2023-03-16 14:00     ` Ard Biesheuvel
  2023-03-16 14:52       ` Leif Lindholm
  0 siblings, 1 reply; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-16 14:00 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Thu, 16 Mar 2023 at 14:51, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>
> On Mon, Mar 13, 2023 at 18:17:08 +0100, Ard Biesheuvel wrote:
> > Currently, we invoke ApplyMemoryProtectionPolicy() after
> > CoreInternalFreePages() has returned successfully, in order to update
> > the memory permission attributes of the region to match the policy for
> > EfiConventionalMemory.
> >
> > There are two problems with that:
> > - CoreInternalFreePages() will round up the size of the allocation to
> >   the appropriate alignment of the memory type, but we only remap the
> >   number of pages that was passed by the caller, leaving the remaining
> >   pages freed but mapped with the old permissions;
> > - in DEBUG builds, we may attempt to clear the entire region while it is
> >   still mapped with read-only or read-protect attributes.
> >
> > Let's address both issues, by updating the permissions before performing
> > the actual conversion.
> >
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> >  MdeModulePkg/Core/Dxe/Mem/Page.c | 15 ++++++++-------
> >  1 file changed, 8 insertions(+), 7 deletions(-)
> >
> > diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c
> > index 5903ce7ab525..f5b940bbc25b 100644
> > --- a/MdeModulePkg/Core/Dxe/Mem/Page.c
> > +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c
> > @@ -1519,8 +1519,8 @@ CoreAllocatePages (
> >    @return EFI_SUCCESS         -Pages successfully freed.
> >
> >  **/
> > +STATIC
> >  EFI_STATUS
> > -EFIAPI
> >  CoreInternalFreePages (
>
> This is addressing a historic oversight (possibly caused by the STATIC
> function ban), but it's not *really* related to the change in question.
>

Not entirely. I am moving some logic from a caller into the callee.
This is only safe if there are no other callers, and making it STATIC
makes it more likely that other callers that may exist in one form or
another (i.e., out of tree forks) will fail at build time rather than
misbehave.

Or that was the rationale, anyway. I'm happy to drop it as well.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 00/38] Implement strict memory permissions throughout
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (37 preceding siblings ...)
  2023-03-13 17:17 ` [PATCH v5 38/38] MdeModulePkg DEC: Remove inaccurate comment Ard Biesheuvel
@ 2023-03-16 14:04 ` Leif Lindholm
  2023-03-17 21:41 ` Oliver Smith-Denny
  39 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 14:04 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

Hi Ard,

For all the patches I haven't explicitly asked any questions about:
Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>


On Mon, Mar 13, 2023 at 18:16:36 +0100, Ard Biesheuvel wrote:
> Link: https://bugzilla.tianocore.org/show_bug.cgi?id=4369
> 
> This v5 now covers a lot more ground, and has ballooned quite
> substantially as a result. The series is essentially a proof of concept
> of a way to implement rigorous W^X memory protections from SEC all the
> way to booting the OS.
> 
> In particular:
> - the AArch64 WXN control is enabled so that NX is implied for all
>   writable memory regions, which is rather helpful when testing changes
>   such as these;
> - avoid PEIM shadowing where possible, as that would involve managing
>   the executable permissions of the shadowed code
> - remap the DXE core code section read-only explicitly from IPL
> - equip the DXE core with a way to manage memory permissions before the
>   CPU arch protocol driver is dispatched;
> - permit the NX memory protection policy to apply to code memory type
>   regions as well
> - check the NX compat DLL flag and section alignment to decide whether
>   an image can be loaded when the NX policy is applied to such a code
>   region 
> - implement the EFI memory attributes protocol (for ARM and AArch64
>   only) so that such NX compat compliant images have a way to create
>   executable mappings 
> 
> v4:
> - major cleanup of the 32-bit ARM code
> - add support for EFI_MEMORY_RP using the access flag
> - enable stack guard in ArmVirtPkg (which uses EFI_MEMORY_RP)
> - incorporate optimization from other series [0] to avoid splitting
>   block entries unnecessarily
> 
> v3:
> - fix ARM32 bug in attribute conversion
> - add Liming's ack to patch #1
> - include draft patch (NOT FOR MERGE) used to test the changes
> 
> v2:
> - drop patch to bump exposed UEFI revision to v2.10
> - add missing permitted return values to protocol definition
> 
> [0] https://edk2.groups.io/g/devel/message/99801
> 
> Cc: Michael Kinney <michael.d.kinney@intel.com>
> Cc: Liming Gao <gaoliming@byosoft.com.cn>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Michael Kubacki <michael.kubacki@microsoft.com>
> Cc: Sean Brogan <sean.brogan@microsoft.com>
> Cc: Rebecca Cran <quic_rcran@quicinc.com>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Taylor Beebe <t@taylorbeebe.com>
> 
> Ard Biesheuvel (38):
>   ArmPkg/ArmMmuLib ARM: Remove half baked large page support
>   ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field
>   ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion
>   ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask
>   ArmPkg/ArmMmuLib ARM: Clear individual permission bits
>   ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag
>   ArmVirtPkg: Enable stack guard
>   ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
>   ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion
>   MdePkg: Add Memory Attribute Protocol definition
>   ArmPkg/CpuDxe: Implement EFI memory attributes protocol
>   ArmPkg/CpuDxe: Perform preliminary NX remap of free memory
>   MdeModulePkg/DxeCore: Unconditionally set memory protections
>   ArmPkg/Mmu: Remove handling of NONSECURE memory regions
>   ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory
>   MdePkg/BasePeCoffLib: Add API to keep track of relocation range
>   MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default
>   MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch
>   MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time
>   MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback
>   ArmPkg: Implement ArmSetMemoryOverrideLib
>   MdeModulePkg/PcdPeim: Permit unshadowed execution
>   EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
>   ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default
>   ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs
>   ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code
>     flash
>   BaseTools/GccBase AARCH64: Avoid page sharing between code and data
>   ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory
>     permissions
>   MdePkg/PeCoffLib: Capture DLL characteristics field in image context
>   MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics
>   MdeModulePkg/DxeCore: Remove redundant DEBUG statements
>   MdeModulePkg/DxeCore: Update memory protections before freeing a
>     region
>   MdeModulePkg/DxeCore: Disregard runtime alignment for image protection
>   MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage()
>   MdeModulePkg/DxeCore: Clear NX permissions on non-protected images
>   MdeModulePkg/DxeCore: Permit NX protection for code regions
>   MdeModulePkg/DxeCore: Check NX compat when using restricted code
>     regions
>   MdeModulePkg DEC: Remove inaccurate comment
> 
>  ArmPkg/ArmPkg.dec                                                  |   5 +
>  ArmPkg/ArmPkg.dsc                                                  |   1 +
>  ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c                                |  25 +-
>  ArmPkg/Drivers/CpuDxe/Arm/Mmu.c                                    |  96 +++++--
>  ArmPkg/Drivers/CpuDxe/CpuDxe.c                                     |  87 ++++++
>  ArmPkg/Drivers/CpuDxe/CpuDxe.h                                     |  17 ++
>  ArmPkg/Drivers/CpuDxe/CpuDxe.inf                                   |   5 +
>  ArmPkg/Drivers/CpuDxe/MemoryAttribute.c                            | 271 ++++++++++++++++++
>  ArmPkg/Include/Chipset/ArmV7Mmu.h                                  | 131 ++++-----
>  ArmPkg/Include/Library/ArmLib.h                                    |  17 +-
>  ArmPkg/Include/Library/ArmMmuLib.h                                 |  34 +++
>  ArmPkg/Library/ArmLib/Arm/ArmV7Support.S                           |   2 +
>  ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c                   | 103 ++++++-
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c                    |   8 +-
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c                       |  51 ++--
>  ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c                     | 173 ++++++++++--
>  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c   |  78 ++++++
>  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf |  28 ++
>  ArmVirtPkg/ArmVirt.dsc.inc                                         |   3 +
>  ArmVirtPkg/ArmVirtQemu.dsc                                         |  11 +-
>  ArmVirtPkg/ArmVirtQemuKernel.dsc                                   |   1 +
>  ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S  |   2 +-
>  ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c         |   4 +-
>  BaseTools/Scripts/GccBase.lds                                      |  13 +-
>  EmbeddedPkg/Include/Library/PrePiLib.h                             |  16 --
>  EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c                    |  51 ++++
>  EmbeddedPkg/Library/PrePiLib/PrePi.h                               |  13 +
>  EmbeddedPkg/Library/PrePiLib/PrePiLib.c                            |   4 +
>  EmbeddedPkg/Library/PrePiLib/PrePiLib.inf                          |  12 +
>  EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c                    |  23 ++
>  MdeModulePkg/Core/Dxe/DxeMain.h                                    |   6 +-
>  MdeModulePkg/Core/Dxe/Image/Image.c                                |   8 +-
>  MdeModulePkg/Core/Dxe/Mem/Page.c                                   |  15 +-
>  MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c                      | 288 +++++++++++---------
>  MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c                     |  73 +++++
>  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf                            |   6 +-
>  MdeModulePkg/Core/DxeIplPeim/DxeLoad.c                             |  24 +-
>  MdeModulePkg/MdeModulePkg.dec                                      |   7 +-
>  MdeModulePkg/Universal/PCD/Pei/Pcd.c                               | 112 ++++----
>  MdeModulePkg/Universal/PCD/Pei/Pcd.inf                             |   1 +
>  MdePkg/Include/IndustryStandard/PeImage.h                          |  15 +
>  MdePkg/Include/Library/PeCoffLib.h                                 |  27 ++
>  MdePkg/Include/Protocol/MemoryAttribute.h                          | 142 ++++++++++
>  MdePkg/Library/BasePeCoffLib/BasePeCoff.c                          | 105 ++++++-
>  MdePkg/MdePkg.dec                                                  |   3 +
>  45 files changed, 1682 insertions(+), 435 deletions(-)
>  create mode 100644 ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
>  create mode 100644 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
>  create mode 100644 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
>  create mode 100644 EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
>  create mode 100644 EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
>  create mode 100644 MdePkg/Include/Protocol/MemoryAttribute.h
> 
> -- 
> 2.39.2
> 
> 
> 
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
  2023-03-16 13:50     ` [edk2-devel] " Ard Biesheuvel
@ 2023-03-16 14:09       ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 14:09 UTC (permalink / raw)
  To: Ard Biesheuvel, devel
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On 2023-03-16 13:50, Ard Biesheuvel wrote:
> On Thu, 16 Mar 2023 at 14:33, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>>
>> On Mon, Mar 13, 2023 at 18:16:59 +0100, Ard Biesheuvel wrote:
>>> Deal with DRAM memory potentially being mapped with non-executable
>>> permissions, by mapping the DXE core code sections explicitly before
>>> launch.
>>
>> Could you add a note about why LoadPeCoffImage/LoadDxeCoreFromFfsFile
>> are made private?
>>
> 
> Actually, they shouldn't - they are used elsewhere too.
> 
> I confused myself into thinking that LoadPeCoffImage() should be made
> private as it now has 'special' behavior that only applies to DxeCore,
> but in fact, the whole purpose in life of PrePi is to load DxeCore and
> run it, so that was kind of implied anyway.
> 
> So I intend to simply drop those changes.

Works for me :)
With that:
Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>

> 
>>
>>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>>> ---
>>>   EmbeddedPkg/Include/Library/PrePiLib.h          | 16 ------
>>>   EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c | 51 ++++++++++++++++++++
>>>   EmbeddedPkg/Library/PrePiLib/PrePi.h            | 13 +++++
>>>   EmbeddedPkg/Library/PrePiLib/PrePiLib.c         |  4 ++
>>>   EmbeddedPkg/Library/PrePiLib/PrePiLib.inf       | 12 +++++
>>>   EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c | 23 +++++++++
>>>   6 files changed, 103 insertions(+), 16 deletions(-)
>>>
>>> diff --git a/EmbeddedPkg/Include/Library/PrePiLib.h b/EmbeddedPkg/Include/Library/PrePiLib.h
>>> index 93a9115eac2d..14f2bbc38dae 100644
>>> --- a/EmbeddedPkg/Include/Library/PrePiLib.h
>>> +++ b/EmbeddedPkg/Include/Library/PrePiLib.h
>>> @@ -758,22 +758,6 @@ AllocateAlignedPages (
>>>     IN UINTN  Alignment
>>>     );
>>>
>>> -EFI_STATUS
>>> -EFIAPI
>>> -LoadPeCoffImage (
>>> -  IN  VOID                  *PeCoffImage,
>>> -  OUT EFI_PHYSICAL_ADDRESS  *ImageAddress,
>>> -  OUT UINT64                *ImageSize,
>>> -  OUT EFI_PHYSICAL_ADDRESS  *EntryPoint
>>> -  );
>>> -
>>> -EFI_STATUS
>>> -EFIAPI
>>> -LoadDxeCoreFromFfsFile (
>>> -  IN EFI_PEI_FILE_HANDLE  FileHandle,
>>> -  IN UINTN                StackSize
>>> -  );
>>> -
>>>   EFI_STATUS
>>>   EFIAPI
>>>   LoadDxeCoreFromFv (
>>> diff --git a/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
>>> new file mode 100644
>>> index 000000000000..40d4ed9d77bd
>>> --- /dev/null
>>> +++ b/EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
>>> @@ -0,0 +1,51 @@
>>> +/** @file
>>> +  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +#include "PrePi.h"
>>> +
>>> +#include <Library/ArmMmuLib.h>
>>> +
>>> +/**
>>> +  Remap the code section of the DXE core with the read-only and executable
>>> +  permissions.
>>> +
>>> +  @param  ImageContext    The image context describing the loaded PE/COFF image
>>> +
>>> +**/
>>> +VOID
>>> +EFIAPI
>>> +RemapDxeCore (
>>> +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
>>> +  )
>>> +{
>>> +  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
>>> +  EFI_IMAGE_SECTION_HEADER             *Section;
>>> +  UINTN                                Index;
>>> +
>>> +  Hdr.Union = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)ImageContext->Handle +
>>> +                                                  ImageContext->PeCoffHeaderOffset);
>>> +  ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
>>> +
>>> +  Section = (EFI_IMAGE_SECTION_HEADER *)((UINT8 *)Hdr.Union + sizeof (UINT32) +
>>> +                                         sizeof (EFI_IMAGE_FILE_HEADER) +
>>> +                                         Hdr.Pe32->FileHeader.SizeOfOptionalHeader
>>> +                                         );
>>> +
>>> +  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
>>> +    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
>>> +      ArmSetMemoryRegionReadOnly (
>>> +        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
>>> +        Section[Index].Misc.VirtualSize
>>> +        );
>>> +
>>> +      ArmClearMemoryRegionNoExec (
>>> +        (UINTN)(ImageContext->ImageAddress + Section[Index].VirtualAddress),
>>> +        Section[Index].Misc.VirtualSize
>>> +        );
>>> +    }
>>> +  }
>>> +}
>>> diff --git a/EmbeddedPkg/Library/PrePiLib/PrePi.h b/EmbeddedPkg/Library/PrePiLib/PrePi.h
>>> index a00c946512f4..a0f8837d1d37 100644
>>> --- a/EmbeddedPkg/Library/PrePiLib/PrePi.h
>>> +++ b/EmbeddedPkg/Library/PrePiLib/PrePi.h
>>> @@ -37,4 +37,17 @@
>>>   #define GET_GUID_HOB_DATA(GuidHob)       ((VOID *) (((UINT8 *) &((GuidHob)->Name)) + sizeof (EFI_GUID)))
>>>   #define GET_GUID_HOB_DATA_SIZE(GuidHob)  (((GuidHob)->Header).HobLength - sizeof (EFI_HOB_GUID_TYPE))
>>>
>>> +/**
>>> +  Remap the code section of the DXE core with the read-only and executable
>>> +  permissions.
>>> +
>>> +  @param  ImageContext    The image context describing the loaded PE/COFF image
>>> +
>>> +**/
>>> +VOID
>>> +EFIAPI
>>> +RemapDxeCore (
>>> +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
>>> +  );
>>> +
>>>   #endif
>>> diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
>>> index 3cf866dab248..188ad5c518a8 100644
>>> --- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
>>> +++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.c
>>> @@ -54,6 +54,7 @@ AllocateCodePages (
>>>     return NULL;
>>>   }
>>>
>>> +STATIC
>>>   EFI_STATUS
>>>   EFIAPI
>>>   LoadPeCoffImage (
>>> @@ -105,6 +106,8 @@ LoadPeCoffImage (
>>>     //
>>>     InvalidateInstructionCacheRange ((VOID *)(UINTN)*ImageAddress, (UINTN)*ImageSize);
>>>
>>> +  RemapDxeCore (&ImageContext);
>>> +
>>>     return Status;
>>>   }
>>>
>>> @@ -114,6 +117,7 @@ VOID
>>>     IN  VOID *HobStart
>>>     );
>>>
>>> +STATIC
>>>   EFI_STATUS
>>>   EFIAPI
>>>   LoadDxeCoreFromFfsFile (
>>> diff --git a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
>>> index 090bfe888f52..2df5928c51d5 100644
>>> --- a/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
>>> +++ b/EmbeddedPkg/Library/PrePiLib/PrePiLib.inf
>>> @@ -31,11 +31,20 @@ [Sources.common]
>>>     FwVol.c
>>>     PrePiLib.c
>>>
>>> +[Sources.X64, Sources.IA32]
>>> +  X86/RemapDxeCore.c
>>> +
>>> +[Sources.AARCH64, Sources.ARM]
>>> +  Arm/RemapDxeCore.c
>>> +
>>>   [Packages]
>>>     MdePkg/MdePkg.dec
>>>     EmbeddedPkg/EmbeddedPkg.dec
>>>     MdeModulePkg/MdeModulePkg.dec
>>>
>>> +[Packages.ARM, Packages.AARCH64]
>>> +  ArmPkg/ArmPkg.dec
>>> +
>>>   [LibraryClasses]
>>>     BaseLib
>>>     DebugLib
>>> @@ -50,6 +59,9 @@ [LibraryClasses]
>>>     PerformanceLib
>>>     HobLib
>>>
>>> +[LibraryClasses.ARM, LibraryClasses.AARCH64]
>>> +  ArmMmuLib
>>> +
>>>   [Guids]
>>>     gEfiMemoryTypeInformationGuid
>>>
>>> diff --git a/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
>>> new file mode 100644
>>> index 000000000000..1899c050fdec
>>> --- /dev/null
>>> +++ b/EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
>>> @@ -0,0 +1,23 @@
>>> +/** @file
>>> +  Copyright (c) 2023, Google LLC. All rights reserved.<BR>
>>> +
>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>> +
>>> +**/
>>> +
>>> +#include "PrePi.h"
>>> +
>>> +/**
>>> +  Remap the code section of the DXE core with the read-only and executable
>>> +  permissions.
>>> +
>>> +  @param  ImageContext    The image context describing the loaded PE/COFF image
>>> +
>>> +**/
>>> +VOID
>>> +EFIAPI
>>> +RemapDxeCore (
>>> +  IN  CONST PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
>>> +  )
>>> +{
>>> +}
>>> --
>>> 2.39.2
>>>
>>
>>
>> 
>>
>>


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib
  2023-03-16 13:27   ` [edk2-devel] " Leif Lindholm
@ 2023-03-16 14:20     ` Ard Biesheuvel
  0 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-16 14:20 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Thu, 16 Mar 2023 at 14:27, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>
> On Mon, Mar 13, 2023 at 18:16:57 +0100, Ard Biesheuvel wrote:
> > Implement the ARM version of a NULL class library that can be overlaid
> > on top of the DXE core to equip it right from its launch with an
> > implementation of the CPU arch protocol member that sets type and
> > permission attributes on memory regions.
> >
> > This bridges the gap between dispatch of DXE core and dispatch of the
> > DXE driver that implements the CPU arch protocol, removing the need to
> > rely on memory mappings that are writable and executable at the same
> > time.
> >
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> >  ArmPkg/ArmPkg.dsc                                                  |  1 +
> >  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c   | 78 ++++++++++++++++++++
> >  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf | 28 +++++++
> >  3 files changed, 107 insertions(+)
> >
> > diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> > index 3fb95d1951a9..43eb0f4f463e 100644
> > --- a/ArmPkg/ArmPkg.dsc
> > +++ b/ArmPkg/ArmPkg.dsc
> > @@ -119,6 +119,7 @@ [Components.common]
> >    ArmPkg/Library/ArmPsciResetSystemLib/ArmPsciResetSystemLib.inf
> >    ArmPkg/Library/ArmExceptionLib/ArmExceptionLib.inf
> >    ArmPkg/Library/ArmExceptionLib/ArmRelocateExceptionLib.inf
> > +  ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
> >
> >    ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> >    ArmPkg/Drivers/CpuPei/CpuPei.inf
> > diff --git a/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
> > new file mode 100644
> > index 000000000000..866dbbdaa7d5
> > --- /dev/null
> > +++ b/ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
> > @@ -0,0 +1,78 @@
> > +/** @file
> > +  Overlay implementation of DXE core gCpuSetMemoryAttributes for ARM.
> > +
> > +  Copyright (c) 2023, Google LLC. All rights reserved.
> > +
> > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +**/
> > +
> > +#include <PiDxe.h>
> > +
> > +#include <Library/ArmMmuLib.h>
> > +#include <Library/DebugLib.h>
> > +#include <Protocol/Cpu.h>
> > +
> > +extern EFI_CPU_SET_MEMORY_ATTRIBUTES  gCpuSetMemoryAttributes;
>
> Could this declaration live in a global include?
>

Yes, In fact, instead of modeling this as a NULL library class, we
could actually define a library class for this, whose include file
only has this extern declaration and nothing else (and a big fat
comment, obviously, describing the point of that)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data
  2023-03-16 13:46   ` [edk2-devel] " Leif Lindholm
@ 2023-03-16 14:30     ` Ard Biesheuvel
  0 siblings, 0 replies; 63+ messages in thread
From: Ard Biesheuvel @ 2023-03-16 14:30 UTC (permalink / raw)
  To: devel, quic_llindhol
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On Thu, 16 Mar 2023 at 14:46, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>
> On Mon, Mar 13, 2023 at 18:17:03 +0100, Ard Biesheuvel wrote:
> > The AArch64 ARM architecture supports a hardware enforcement mode for
> > mutual exclusion between code and data: any page that is mapped writable
> > is implicitly non-executable as well.
> >
> > This means that remapping part of a runtime image for reapplying
> > relocation fixups may result in any code sharing the same page to lose
> > its executable permissions.
> >
> > Let's avoid this, by moving all quantities that are subject to
> > relocation fixups to a separate page if the build is using 64k section
> > alignment, which is only the case when building a runtime driver for
> > AArch64.
> >
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> >  BaseTools/Scripts/GccBase.lds | 13 +++++++++++--
> >  1 file changed, 11 insertions(+), 2 deletions(-)
> >
> > diff --git a/BaseTools/Scripts/GccBase.lds b/BaseTools/Scripts/GccBase.lds
> > index 83cebd29d599..63e097e0727c 100644
> > --- a/BaseTools/Scripts/GccBase.lds
> > +++ b/BaseTools/Scripts/GccBase.lds
> > @@ -21,9 +21,8 @@ SECTIONS {
> >    . = PECOFF_HEADER_SIZE;
> >
> >    .text : ALIGN(CONSTANT(COMMONPAGESIZE)) {
> > -    *(.text .text.* .stub .gnu.linkonce.t.*)
> > +    *(.text .text.* .stub .gnu.linkonce.t.* .plt)
> >      *(.rodata .rodata.* .gnu.linkonce.r.*)
> > -    *(.got .got.*)
> >
> >      /*
> >       * The contents of AutoGen.c files are mostly constant from the POV of the
> > @@ -34,6 +33,16 @@ SECTIONS {
> >       * emitted GUIDs here.
> >       */
> >      *:AutoGen.obj(.data.g*Guid)
> > +
> > +    /*
> > +     * AArch64 runtime drivers use 64k alignment, and may run in a mode where
>
> Hmm ... is this strictly speaking true?
> I.e., yes, all 4k pages within a 64k page need to share the same
> permissions, but that could arguably be provided by pooling 4k
> allocations together for multiple runtime drivers?
>

That only works for dynamic allocations. The static allocations are
all part of the PE/COFF image, whose size needs to be a multiple of
the section alignment.

If we decide we don't care about the image base and size after loading
the image (which is reasonable, and TE is based on this notion as
well), we could free the leading and trailing memory that we don't
actually use after dispatching the image.

*However*, the code section, which sits in between those, will always
need to be a multiple of 64k, as it will be mapped with R-X
permissions under the OS. This change only ensures that *within* that
region, the relocatable quantities that may need to be rewritten when
SetVirtualAddressMap() is called don't share a 4k page with executable
instructions, as those would then lose their exec permissions under
WXN.




> Will this alignment constraint conflict with that, or just help
> enforce the mapping compatibility?
>
> /
>     Leif
>
> > +     * mutual exclusion of RO and XP mappings are hardware enforced. In such
> > +     * cases, the input sections below, which carry any quantities that are
> > +     * subject to relocation fixups at runtime, must not share a 4 KiB page
> > +     * with any code content.
> > +     */
> > +    . = ALIGN(CONSTANT(COMMONPAGESIZE) > 0x1000 ? 0x1000 : 0x20);
> > +    *(.got .got.* .data.rel.ro)
> >    }
> >
> >    /*
> > --
> > 2.39.2
> >
> >
> >
> >
> >
> >
>
>
> 
>
>

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region
  2023-03-16 14:00     ` [edk2-devel] " Ard Biesheuvel
@ 2023-03-16 14:52       ` Leif Lindholm
  0 siblings, 0 replies; 63+ messages in thread
From: Leif Lindholm @ 2023-03-16 14:52 UTC (permalink / raw)
  To: Ard Biesheuvel, devel
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Sami Mujawar, Taylor Beebe

On 2023-03-16 14:00, Ard Biesheuvel wrote:
> On Thu, 16 Mar 2023 at 14:51, Leif Lindholm <quic_llindhol@quicinc.com> wrote:
>>
>> On Mon, Mar 13, 2023 at 18:17:08 +0100, Ard Biesheuvel wrote:
>>> Currently, we invoke ApplyMemoryProtectionPolicy() after
>>> CoreInternalFreePages() has returned successfully, in order to update
>>> the memory permission attributes of the region to match the policy for
>>> EfiConventionalMemory.
>>>
>>> There are two problems with that:
>>> - CoreInternalFreePages() will round up the size of the allocation to
>>>    the appropriate alignment of the memory type, but we only remap the
>>>    number of pages that was passed by the caller, leaving the remaining
>>>    pages freed but mapped with the old permissions;
>>> - in DEBUG builds, we may attempt to clear the entire region while it is
>>>    still mapped with read-only or read-protect attributes.
>>>
>>> Let's address both issues, by updating the permissions before performing
>>> the actual conversion.
>>>
>>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>>> ---
>>>   MdeModulePkg/Core/Dxe/Mem/Page.c | 15 ++++++++-------
>>>   1 file changed, 8 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c
>>> index 5903ce7ab525..f5b940bbc25b 100644
>>> --- a/MdeModulePkg/Core/Dxe/Mem/Page.c
>>> +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c
>>> @@ -1519,8 +1519,8 @@ CoreAllocatePages (
>>>     @return EFI_SUCCESS         -Pages successfully freed.
>>>
>>>   **/
>>> +STATIC
>>>   EFI_STATUS
>>> -EFIAPI
>>>   CoreInternalFreePages (
>>
>> This is addressing a historic oversight (possibly caused by the STATIC
>> function ban), but it's not *really* related to the change in question.
>>
> 
> Not entirely. I am moving some logic from a caller into the callee.
> This is only safe if there are no other callers, and making it STATIC
> makes it more likely that other callers that may exist in one form or
> another (i.e., out of tree forks) will fail at build time rather than
> misbehave.
> 
> Or that was the rationale, anyway. I'm happy to drop it as well.

I'm OK with that rationale, but I think in that case it needs calling 
out in the commit message.

/
     Leif


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 18/38] MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch
  2023-03-13 17:16 ` [PATCH v5 18/38] MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch Ard Biesheuvel
@ 2023-03-16 22:20   ` osd
  0 siblings, 0 replies; 63+ messages in thread
From: osd @ 2023-03-16 22:20 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Leif Lindholm, Sami Mujawar,
	Taylor Beebe

On 3/13/2023 10:16 AM, Ard Biesheuvel wrote:
One nitpick below.

> To permit the platform to adopt a stricter policy when it comes to
> memory protections, and map all memory XP by default, add the necessary
> handling to the DXE IPL PEIM to ensure that the DXE core code section is
> mapped executable before invoking the DXE core.
> 
> It is up to the DXE core itself to manage the executable permissions on
> other DXE and UEFI drivers and applications that it dispatches.
> 
> Note that this requires that the DXE IPL executes non-shadowed from a FV
> that is mapped executable.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>   MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c | 73 ++++++++++++++++++++
>   MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf        |  1 +
>   2 files changed, 74 insertions(+)
> 
> diff --git a/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c b/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c
> index f62b6dcb38a7..c57ffa87e30f 100644
> --- a/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c
> +++ b/MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c
> @@ -11,6 +11,73 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
>   #include "DxeIpl.h"
> 
>   
> 
>   #include <Library/ArmMmuLib.h>
> 
> +#include <Library/PeCoffLib.h>
> 
> +
> 
> +/**
> 
> +  Discover the code sections of the DXE core, and remap them read-only
> 
> +  and executable.
> 
> +
> 
> +  @param DxeCoreEntryPoint  The entrypoint of the DXE core executable.
> 
> +  @param HobList            The list of HOBs passed to the DXE core from PEI.
> 
> +**/
> 
> +STATIC
> 
> +VOID
> 
> +RemapDxeCoreCodeReadOnly (

nit: should this be called RemapDxeCoreCodeReadExecute or something? The 
naming seems confusing here to say map read only when we are mapping it 
read and execute.

> 
> +  IN EFI_PHYSICAL_ADDRESS  DxeCoreEntryPoint,
> 
> +  IN EFI_PEI_HOB_POINTERS  HobList
> 
> +  )
> 
> +{
> 
> +  EFI_PEI_HOB_POINTERS                 Hob;
> 
> +  PE_COFF_LOADER_IMAGE_CONTEXT         ImageContext;
> 
> +  RETURN_STATUS                        Status;
> 
> +  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION  Hdr;
> 
> +  EFI_IMAGE_SECTION_HEADER             *Section;
> 
> +  UINTN                                Index;
> 
> +
> 
> +  ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
> 
> +  ImageContext.Handle    = NULL;
> 
> +
> 
> +  //
> 
> +  // Find the module HOB for the DXE core
> 
> +  //
> 
> +  for (Hob.Raw = HobList.Raw; !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
> 
> +    if ((GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) &&
> 
> +        (CompareGuid (&Hob.MemoryAllocation->AllocDescriptor.Name, &gEfiHobMemoryAllocModuleGuid)) &&
> 
> +        (Hob.MemoryAllocationModule->EntryPoint == DxeCoreEntryPoint))
> 
> +    {
> 
> +      ImageContext.Handle = (VOID *)(UINTN)Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress;
> 
> +      break;
> 
> +    }
> 
> +  }
> 
> +
> 
> +  ASSERT (ImageContext.Handle != NULL);
> 
> +
> 
> +  Status = PeCoffLoaderGetImageInfo (&ImageContext);
> 
> +  ASSERT_RETURN_ERROR (Status);
> 
> +
> 
> +  Hdr.Union = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)ImageContext.Handle +
> 
> +                                                  ImageContext.PeCoffHeaderOffset);
> 
> +  ASSERT (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE);
> 
> +
> 
> +  Section = (EFI_IMAGE_SECTION_HEADER *)((UINT8 *)Hdr.Union + sizeof (UINT32) +
> 
> +                                         sizeof (EFI_IMAGE_FILE_HEADER) +
> 
> +                                         Hdr.Pe32->FileHeader.SizeOfOptionalHeader
> 
> +                                         );
> 
> +
> 
> +  for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
> 
> +    if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
> 
> +      ArmSetMemoryRegionReadOnly (
> 
> +        (UINTN)((UINT8 *)ImageContext.Handle + Section[Index].VirtualAddress),
> 
> +        Section[Index].Misc.VirtualSize
> 
> +        );
> 
> +
> 
> +      ArmClearMemoryRegionNoExec (
> 
> +        (UINTN)((UINT8 *)ImageContext.Handle + Section[Index].VirtualAddress),
> 
> +        Section[Index].Misc.VirtualSize
> 
> +        );
> 
> +    }
> 
> +  }
> 
> +}
> 
>   
> 
>   /**
> 
>      Transfers control to DxeCore.
> 
> @@ -33,6 +100,12 @@ HandOffToDxeCore (
>     VOID        *TopOfStack;
> 
>     EFI_STATUS  Status;
> 
>   
> 
> +  //
> 
> +  // DRAM may be mapped with non-executable permissions by default, so
> 
> +  // we'll need to map the DXE core code region executable explicitly.
> 
> +  //
> 
> +  RemapDxeCoreCodeReadOnly (DxeCoreEntryPoint, HobList);
> 
> +
> 
>     //
> 
>     // Allocate 128KB for the Stack
> 
>     //
> 
> diff --git a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
> index 62821477d012..d85ca79dc0c3 100644
> --- a/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
> +++ b/MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
> @@ -82,6 +82,7 @@ [LibraryClasses]
>   
> 
>   [LibraryClasses.ARM, LibraryClasses.AARCH64]
> 
>     ArmMmuLib
> 
> +  PeCoffLib
> 
>   
> 
>   [Ppis]
> 
>     gEfiDxeIplPpiGuid                      ## PRODUCES
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 35/38] MdeModulePkg/DxeCore: Clear NX permissions on non-protected images
  2023-03-13 17:17 ` [PATCH v5 35/38] MdeModulePkg/DxeCore: Clear NX permissions on non-protected images Ard Biesheuvel
@ 2023-03-17 20:04   ` Oliver Smith-Denny
  0 siblings, 0 replies; 63+ messages in thread
From: Oliver Smith-Denny @ 2023-03-17 20:04 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Leif Lindholm, Sami Mujawar,
	Taylor Beebe

On 3/13/2023 10:17 AM, Ard Biesheuvel wrote:
> Currently, we rely on the memory type for loading images being
> executable by default, and only restrict the permissions if the policy
> says so, and the image sections are suitably aligned. This requires that
> the various 'code' memory types are executable by default, which is
> unfortunate.
> 
> In order to be able to tighten this, let's update the image protection
> policy handling so that images that should not be mapped with strict
> separation of RW- and R-X are remapped RWX explicitly if the memory type
> used for loading the images is marked as NX by default.
> 
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
>   MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 98 +++++++++++---------
>   1 file changed, 54 insertions(+), 44 deletions(-)
> 
> diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
> index 301ddd6eb053..7c7a946c1b48 100644
> --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
> +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c
> @@ -373,11 +373,62 @@ ProtectUefiImage (
>     }
> 
>   
> 
>     ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);
> 
> +
> 
> +  ImageAddress = LoadedImage->ImageBase;
> 
> +
> 
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageAddress);
> 
> +  if (PdbPointer != NULL) {
> 
> +    DEBUG ((DEBUG_VERBOSE, "  Image - %a\n", PdbPointer));
> 
> +  }
> 
> +
> 
>     switch (ProtectionPolicy) {
> 
> -    case DO_NOT_PROTECT:
> 
> -      return EFI_SUCCESS;
> 
>       case PROTECT_IF_ALIGNED_ELSE_ALLOW:
> 
> -      break;
> 
> +      //
> 
> +      // Check PE/COFF image
> 
> +      //
> 
> +      DosHdr             = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;
> 
> +      PeCoffHeaderOffset = 0;
> 
> +      if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
> 
> +        PeCoffHeaderOffset = DosHdr->e_lfanew;
> 
> +      }
> 
> +
> 
> +      Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);
> 
> +      if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
> 
> +        DEBUG ((DEBUG_INFO, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
> 
> +        // It might be image in SMM.

In this case we now fall through to explicitly unprotect the image that 
does not have the EFI_IMAGE_NT_SIGNATURE. Is it expected that we will 
always run these images (or that if we shouldn't run it that that will 
be caught elsewhere) and that giving it full permissions is permissible?

> 
> +      } else {
> 
> +        //
> 
> +        // Get SectionAlignment
> 
> +        //
> 
> +        if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
> 
> +          SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
> 
> +        } else {
> 
> +          SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
> 
> +        }
> 
> +
> 
> +        if (SectionAlignment >= EFI_PAGE_SIZE) {
> 
> +          break;
> 
> +        }
> 
> +
> 
> +        DEBUG ((
> 
> +          DEBUG_VERBOSE,
> 
> +          "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
> 
> +          SectionAlignment
> 
> +          ));
> 
> +      }
> 
> +      // fall through to unprotect image if needed
> 
> +    case DO_NOT_PROTECT:
> 
> +      if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) &
> 
> +           LShiftU64 (1, LoadedImage->ImageCodeType)) != 0)
> 
> +      {
> 
> +        SetUefiImageMemoryAttributes (
> 
> +          (UINTN)LoadedImage->ImageBase,
> 
> +          (LoadedImage->ImageSize + EFI_PAGE_MASK) & ~(UINT64)EFI_PAGE_MASK,
> 
> +          0
> 
> +          );
> 
> +      }
> 
> +
> 
> +      return EFI_SUCCESS;
> 
>       default:
> 
>         ASSERT (FALSE);
> 
>         return EFI_SUCCESS;
> 
> @@ -396,47 +447,6 @@ ProtectUefiImage (
>     ImageRecord->ImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase;
> 
>     ImageRecord->ImageSize = LoadedImage->ImageSize;
> 
>   
> 
> -  ImageAddress = LoadedImage->ImageBase;
> 
> -
> 
> -  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageAddress);
> 
> -  if (PdbPointer != NULL) {
> 
> -    DEBUG ((DEBUG_VERBOSE, "  Image - %a\n", PdbPointer));
> 
> -  }
> 
> -
> 
> -  //
> 
> -  // Check PE/COFF image
> 
> -  //
> 
> -  DosHdr             = (EFI_IMAGE_DOS_HEADER *)(UINTN)ImageAddress;
> 
> -  PeCoffHeaderOffset = 0;
> 
> -  if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
> 
> -    PeCoffHeaderOffset = DosHdr->e_lfanew;
> 
> -  }
> 
> -
> 
> -  Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *)(UINTN)ImageAddress + PeCoffHeaderOffset);
> 
> -  if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
> 
> -    DEBUG ((DEBUG_VERBOSE, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
> 
> -    // It might be image in SMM.
> 
> -    goto Finish;
> 
> -  }
> 
> -
> 
> -  //
> 
> -  // Get SectionAlignment
> 
> -  //
> 
> -  if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
> 
> -    SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
> 
> -  } else {
> 
> -    SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
> 
> -  }
> 
> -
> 
> -  if (SectionAlignment >= EFI_PAGE_SIZE) {
> 
> -    DEBUG ((
> 
> -      DEBUG_VERBOSE,
> 
> -      "!!!!!!!!  ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect  !!!!!!!!\n",
> 
> -      SectionAlignment
> 
> -      ));
> 
> -    goto Finish;
> 
> -  }
> 
> -
> 
>     Section = (EFI_IMAGE_SECTION_HEADER *)(
> 
>                                            (UINT8 *)(UINTN)ImageAddress +
> 
>                                            PeCoffHeaderOffset +
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [edk2-devel] [PATCH v5 00/38] Implement strict memory permissions throughout
  2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
                   ` (38 preceding siblings ...)
  2023-03-16 14:04 ` [edk2-devel] [PATCH v5 00/38] Implement strict memory permissions throughout Leif Lindholm
@ 2023-03-17 21:41 ` Oliver Smith-Denny
  39 siblings, 0 replies; 63+ messages in thread
From: Oliver Smith-Denny @ 2023-03-17 21:41 UTC (permalink / raw)
  To: devel, ardb
  Cc: Michael Kinney, Liming Gao, Jiewen Yao, Michael Kubacki,
	Sean Brogan, Rebecca Cran, Leif Lindholm, Sami Mujawar,
	Taylor Beebe

Hi Ard,

For the patchset:

Reviewed- and Tested-by: Oliver Smith-Denny <osd@smith-denny.com>

Thanks for sending this out! I tested with some integrations to Project 
Mu on both a virtual and physical platform.

Oliver

On 3/13/2023 10:16 AM, Ard Biesheuvel wrote:
> Link: https://bugzilla.tianocore.org/show_bug.cgi?id=4369
> 
> 
> 
> This v5 now covers a lot more ground, and has ballooned quite
> 
> substantially as a result. The series is essentially a proof of concept
> 
> of a way to implement rigorous W^X memory protections from SEC all the
> 
> way to booting the OS.
> 
> 
> 
> In particular:
> 
> - the AArch64 WXN control is enabled so that NX is implied for all
> 
>    writable memory regions, which is rather helpful when testing changes
> 
>    such as these;
> 
> - avoid PEIM shadowing where possible, as that would involve managing
> 
>    the executable permissions of the shadowed code
> 
> - remap the DXE core code section read-only explicitly from IPL
> 
> - equip the DXE core with a way to manage memory permissions before the
> 
>    CPU arch protocol driver is dispatched;
> 
> - permit the NX memory protection policy to apply to code memory type
> 
>    regions as well
> 
> - check the NX compat DLL flag and section alignment to decide whether
> 
>    an image can be loaded when the NX policy is applied to such a code
> 
>    region
> 
> - implement the EFI memory attributes protocol (for ARM and AArch64
> 
>    only) so that such NX compat compliant images have a way to create
> 
>    executable mappings
> 
> 
> 
> v4:
> 
> - major cleanup of the 32-bit ARM code
> 
> - add support for EFI_MEMORY_RP using the access flag
> 
> - enable stack guard in ArmVirtPkg (which uses EFI_MEMORY_RP)
> 
> - incorporate optimization from other series [0] to avoid splitting
> 
>    block entries unnecessarily
> 
> 
> 
> v3:
> 
> - fix ARM32 bug in attribute conversion
> 
> - add Liming's ack to patch #1
> 
> - include draft patch (NOT FOR MERGE) used to test the changes
> 
> 
> 
> v2:
> 
> - drop patch to bump exposed UEFI revision to v2.10
> 
> - add missing permitted return values to protocol definition
> 
> 
> 
> [0] https://edk2.groups.io/g/devel/message/99801
> 
> 
> 
> Cc: Michael Kinney <michael.d.kinney@intel.com>
> 
> Cc: Liming Gao <gaoliming@byosoft.com.cn>
> 
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> 
> Cc: Michael Kubacki <michael.kubacki@microsoft.com>
> 
> Cc: Sean Brogan <sean.brogan@microsoft.com>
> 
> Cc: Rebecca Cran <quic_rcran@quicinc.com>
> 
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> 
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> 
> Cc: Taylor Beebe <t@taylorbeebe.com>
> 
> 
> 
> Ard Biesheuvel (38):
> 
>    ArmPkg/ArmMmuLib ARM: Remove half baked large page support
> 
>    ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field
> 
>    ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion
> 
>    ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask
> 
>    ArmPkg/ArmMmuLib ARM: Clear individual permission bits
> 
>    ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag
> 
>    ArmVirtPkg: Enable stack guard
> 
>    ArmPkg/ArmMmuLib: Avoid splitting block entries if possible
> 
>    ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion
> 
>    MdePkg: Add Memory Attribute Protocol definition
> 
>    ArmPkg/CpuDxe: Implement EFI memory attributes protocol
> 
>    ArmPkg/CpuDxe: Perform preliminary NX remap of free memory
> 
>    MdeModulePkg/DxeCore: Unconditionally set memory protections
> 
>    ArmPkg/Mmu: Remove handling of NONSECURE memory regions
> 
>    ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory
> 
>    MdePkg/BasePeCoffLib: Add API to keep track of relocation range
> 
>    MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default
> 
>    MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch
> 
>    MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time
> 
>    MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback
> 
>    ArmPkg: Implement ArmSetMemoryOverrideLib
> 
>    MdeModulePkg/PcdPeim: Permit unshadowed execution
> 
>    EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution
> 
>    ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default
> 
>    ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs
> 
>    ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code
> 
>      flash
> 
>    BaseTools/GccBase AARCH64: Avoid page sharing between code and data
> 
>    ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory
> 
>      permissions
> 
>    MdePkg/PeCoffLib: Capture DLL characteristics field in image context
> 
>    MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics
> 
>    MdeModulePkg/DxeCore: Remove redundant DEBUG statements
> 
>    MdeModulePkg/DxeCore: Update memory protections before freeing a
> 
>      region
> 
>    MdeModulePkg/DxeCore: Disregard runtime alignment for image protection
> 
>    MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage()
> 
>    MdeModulePkg/DxeCore: Clear NX permissions on non-protected images
> 
>    MdeModulePkg/DxeCore: Permit NX protection for code regions
> 
>    MdeModulePkg/DxeCore: Check NX compat when using restricted code
> 
>      regions
> 
>    MdeModulePkg DEC: Remove inaccurate comment
> 
> 
> 
>   ArmPkg/ArmPkg.dec                                                  |   5 +
> 
>   ArmPkg/ArmPkg.dsc                                                  |   1 +
> 
>   ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c                                |  25 +-
> 
>   ArmPkg/Drivers/CpuDxe/Arm/Mmu.c                                    |  96 +++++--
> 
>   ArmPkg/Drivers/CpuDxe/CpuDxe.c                                     |  87 ++++++
> 
>   ArmPkg/Drivers/CpuDxe/CpuDxe.h                                     |  17 ++
> 
>   ArmPkg/Drivers/CpuDxe/CpuDxe.inf                                   |   5 +
> 
>   ArmPkg/Drivers/CpuDxe/MemoryAttribute.c                            | 271 ++++++++++++++++++
> 
>   ArmPkg/Include/Chipset/ArmV7Mmu.h                                  | 131 ++++-----
> 
>   ArmPkg/Include/Library/ArmLib.h                                    |  17 +-
> 
>   ArmPkg/Include/Library/ArmMmuLib.h                                 |  34 +++
> 
>   ArmPkg/Library/ArmLib/Arm/ArmV7Support.S                           |   2 +
> 
>   ArmPkg/Library/ArmMmuLib/AArch64/ArmMmuLibCore.c                   | 103 ++++++-
> 
>   ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibConvert.c                    |   8 +-
> 
>   ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibCore.c                       |  51 ++--
> 
>   ArmPkg/Library/ArmMmuLib/Arm/ArmMmuLibUpdate.c                     | 173 ++++++++++--
> 
>   ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c   |  78 ++++++
> 
>   ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf |  28 ++
> 
>   ArmVirtPkg/ArmVirt.dsc.inc                                         |   3 +
> 
>   ArmVirtPkg/ArmVirtQemu.dsc                                         |  11 +-
> 
>   ArmVirtPkg/ArmVirtQemuKernel.dsc                                   |   1 +
> 
>   ArmVirtPkg/Library/ArmPlatformLibQemu/AArch64/ArmPlatformHelper.S  |   2 +-
> 
>   ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.c         |   4 +-
> 
>   BaseTools/Scripts/GccBase.lds                                      |  13 +-
> 
>   EmbeddedPkg/Include/Library/PrePiLib.h                             |  16 --
> 
>   EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c                    |  51 ++++
> 
>   EmbeddedPkg/Library/PrePiLib/PrePi.h                               |  13 +
> 
>   EmbeddedPkg/Library/PrePiLib/PrePiLib.c                            |   4 +
> 
>   EmbeddedPkg/Library/PrePiLib/PrePiLib.inf                          |  12 +
> 
>   EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c                    |  23 ++
> 
>   MdeModulePkg/Core/Dxe/DxeMain.h                                    |   6 +-
> 
>   MdeModulePkg/Core/Dxe/Image/Image.c                                |   8 +-
> 
>   MdeModulePkg/Core/Dxe/Mem/Page.c                                   |  15 +-
> 
>   MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c                      | 288 +++++++++++---------
> 
>   MdeModulePkg/Core/DxeIplPeim/Arm/DxeLoadFunc.c                     |  73 +++++
> 
>   MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf                            |   6 +-
> 
>   MdeModulePkg/Core/DxeIplPeim/DxeLoad.c                             |  24 +-
> 
>   MdeModulePkg/MdeModulePkg.dec                                      |   7 +-
> 
>   MdeModulePkg/Universal/PCD/Pei/Pcd.c                               | 112 ++++----
> 
>   MdeModulePkg/Universal/PCD/Pei/Pcd.inf                             |   1 +
> 
>   MdePkg/Include/IndustryStandard/PeImage.h                          |  15 +
> 
>   MdePkg/Include/Library/PeCoffLib.h                                 |  27 ++
> 
>   MdePkg/Include/Protocol/MemoryAttribute.h                          | 142 ++++++++++
> 
>   MdePkg/Library/BasePeCoffLib/BasePeCoff.c                          | 105 ++++++-
> 
>   MdePkg/MdePkg.dec                                                  |   3 +
> 
>   45 files changed, 1682 insertions(+), 435 deletions(-)
> 
>   create mode 100644 ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
> 
>   create mode 100644 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.c
> 
>   create mode 100644 ArmPkg/Library/ArmSetMemoryOverrideLib/ArmSetMemoryOverrideLib.inf
> 
>   create mode 100644 EmbeddedPkg/Library/PrePiLib/Arm/RemapDxeCore.c
> 
>   create mode 100644 EmbeddedPkg/Library/PrePiLib/X86/RemapDxeCore.c
> 
>   create mode 100644 MdePkg/Include/Protocol/MemoryAttribute.h
> 
> 
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2023-03-17 21:41 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-13 17:16 [PATCH v5 00/38] Implement strict memory permissions throughout Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 01/38] ArmPkg/ArmMmuLib ARM: Remove half baked large page support Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 02/38] ArmPkg/ArmMmuLib ARM: Split off XN page descriptor bit from type field Ard Biesheuvel
2023-03-14 17:24   ` Leif Lindholm
2023-03-13 17:16 ` [PATCH v5 03/38] ArmPkg/CpuDxe ARM: Fix page-to-section attribute conversion Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 04/38] ArmPkg/ArmMmuLib ARM: Isolate the access flag from AP mask Ard Biesheuvel
2023-03-14 17:55   ` [edk2-devel] " Leif Lindholm
2023-03-13 17:16 ` [PATCH v5 05/38] ArmPkg/ArmMmuLib ARM: Clear individual permission bits Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 06/38] ArmPkg/ArmMmuLib: Implement EFI_MEMORY_RP using access flag Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 07/38] ArmVirtPkg: Enable stack guard Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 08/38] ArmPkg/ArmMmuLib: Avoid splitting block entries if possible Ard Biesheuvel
2023-03-14 18:13   ` [edk2-devel] " Leif Lindholm
2023-03-14 18:29     ` Ard Biesheuvel
2023-03-15 18:02       ` Leif Lindholm
2023-03-13 17:16 ` [PATCH v5 09/38] ArmPkg/CpuDxe: Expose unified region-to-EFI attribute conversion Ard Biesheuvel
2023-03-15 18:08   ` [edk2-devel] " Leif Lindholm
2023-03-13 17:16 ` [PATCH v5 10/38] MdePkg: Add Memory Attribute Protocol definition Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 11/38] ArmPkg/CpuDxe: Implement EFI memory attributes protocol Ard Biesheuvel
2023-03-15 18:31   ` Leif Lindholm
2023-03-16  7:19     ` [edk2-devel] " Ard Biesheuvel
2023-03-16  9:27       ` Ard Biesheuvel
2023-03-16 11:41         ` Leif Lindholm
2023-03-13 17:16 ` [PATCH v5 12/38] ArmPkg/CpuDxe: Perform preliminary NX remap of free memory Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 13/38] MdeModulePkg/DxeCore: Unconditionally set memory protections Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 14/38] ArmPkg/Mmu: Remove handling of NONSECURE memory regions Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 15/38] ArmPkg/ArmMmuLib: Introduce region types for RO/XP WB cached memory Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 16/38] MdePkg/BasePeCoffLib: Add API to keep track of relocation range Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 17/38] MdeModulePkg/DxeIpl: Avoid shadowing IPL PEIM by default Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 18/38] MdeModulePkg/DxeIpl AARCH64: Remap DXE core code section before launch Ard Biesheuvel
2023-03-16 22:20   ` [edk2-devel] " osd
2023-03-13 17:16 ` [PATCH v5 19/38] MdeModulePkg/DxeCore: Reduce range of W+X remaps at EBS time Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 20/38] MdeModulePkg/DxeCore: Permit preliminary CPU arch fallback Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 21/38] ArmPkg: Implement ArmSetMemoryOverrideLib Ard Biesheuvel
2023-03-16 13:27   ` [edk2-devel] " Leif Lindholm
2023-03-16 14:20     ` Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 22/38] MdeModulePkg/PcdPeim: Permit unshadowed execution Ard Biesheuvel
2023-03-13 17:16 ` [PATCH v5 23/38] EmbeddedPkg/PrePiLib AARCH64: Remap DXE core before execution Ard Biesheuvel
2023-03-16 13:33   ` Leif Lindholm
2023-03-16 13:50     ` [edk2-devel] " Ard Biesheuvel
2023-03-16 14:09       ` Leif Lindholm
2023-03-13 17:17 ` [PATCH v5 24/38] ArmVirtPkg/ArmVirtQemu: Use XP memory mappings by default Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 25/38] ArmVirtPkg/ArmVirtQemu: Use PEI flavor of ArmMmuLib for all PEIMs Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 26/38] ArmVirtPkg/ArmVirtQemu: Use read-only memory region type for code flash Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 27/38] BaseTools/GccBase AARCH64: Avoid page sharing between code and data Ard Biesheuvel
2023-03-16 13:46   ` [edk2-devel] " Leif Lindholm
2023-03-16 14:30     ` Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 28/38] ArmVirtPkg/ArmVirtQemu: Enable hardware enforced W^X memory permissions Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 29/38] MdePkg/PeCoffLib: Capture DLL characteristics field in image context Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 30/38] MdePkg/IndustryStandard: PeImage.h: Import DLL characteristics Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 31/38] MdeModulePkg/DxeCore: Remove redundant DEBUG statements Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 32/38] MdeModulePkg/DxeCore: Update memory protections before freeing a region Ard Biesheuvel
2023-03-16 13:51   ` Leif Lindholm
2023-03-16 14:00     ` [edk2-devel] " Ard Biesheuvel
2023-03-16 14:52       ` Leif Lindholm
2023-03-13 17:17 ` [PATCH v5 33/38] MdeModulePkg/DxeCore: Disregard runtime alignment for image protection Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 34/38] MdeModulePkg/DxeCore: Deal with failure in UefiProtectImage() Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 35/38] MdeModulePkg/DxeCore: Clear NX permissions on non-protected images Ard Biesheuvel
2023-03-17 20:04   ` [edk2-devel] " Oliver Smith-Denny
2023-03-13 17:17 ` [PATCH v5 36/38] MdeModulePkg/DxeCore: Permit NX protection for code regions Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 37/38] MdeModulePkg/DxeCore: Check NX compat when using restricted " Ard Biesheuvel
2023-03-13 17:17 ` [PATCH v5 38/38] MdeModulePkg DEC: Remove inaccurate comment Ard Biesheuvel
2023-03-16 14:04 ` [edk2-devel] [PATCH v5 00/38] Implement strict memory permissions throughout Leif Lindholm
2023-03-17 21:41 ` Oliver Smith-Denny

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox