From c00c1216ce7f6e02b00da95747a7f4d4c239c1b1 Mon Sep 17 00:00:00 2001
From: Richard Lyu <richard.lyu@suse.com>
Date: Tue, 14 Apr 2026 16:09:38 +0800
Subject: [PATCH 2/2] ArmPkg/CpuDxe: Refuse to clear XN from device memory
 mappings

The ARM architecture requires the XN attribute on device memory
mappings. Failure to preserve XN permits speculative instruction
fetches to MMIO regions, leading to unpredictable behavior such as
stuck transactions in the memory controller or the I-cache prefetcher
inadvertently acknowledging device interrupts.

Add a check in ClearMemoryAttributes() that returns EFI_UNSUPPORTED
when the caller requests clearing EFI_MEMORY_XP on a region that
contains device memory. The check traverses the translation table
to determine whether any portion of the given address range is
mapped with the device memory attribute.

Signed-off-by: Richard Lyu <richard.lyu@suse.com>
---
 ArmPkg/Drivers/CpuDxe/MemoryAttribute.c | 26 +++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
index 65d232c71f81..4830546daf83 100644
--- a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
+++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
@@ -214,6 +214,11 @@ ClearMemoryAttributes (
   IN  UINT64                         Attributes
   )
 {
+  UINTN       RegionAddress;
+  UINTN       RegionLength;
+  UINTN       RegionAttributes;
+  EFI_STATUS  Status;
+
   DEBUG ((
     DEBUG_INFO,
     "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
@@ -236,6 +241,27 @@ ClearMemoryAttributes (
     return EFI_INVALID_PARAMETER;
   }
 
+  // ARM requires XN on device memory to prevent speculative instruction fetches
+  if ((Attributes & EFI_MEMORY_XP) != 0) {
+    for (RegionAddress = (UINTN)BaseAddress;
+         RegionAddress < (UINTN)(BaseAddress + Length);
+         RegionAddress += RegionLength)
+    {
+      Status = GetMemoryRegion (
+                 &RegionAddress,
+                 &RegionLength,
+                 &RegionAttributes
+                 );
+      if (EFI_ERROR (Status)) {
+        return EFI_UNSUPPORTED;
+      }
+
+      if ((RegionAttributes & TT_ATTR_INDX_MASK) == TT_ATTR_INDX_DEVICE_MEMORY) {
+        return EFI_UNSUPPORTED;
+      }
+    }
+  }
+
   return ArmSetMemoryAttributes (BaseAddress, Length, 0, Attributes);
 }
 
-- 
2.51.0

