git: ef7d7011ce2f - main - arm64: Fix EFI runtime mappings with 16K pages

From: Andrew Gallatin <gallatin_at_FreeBSD.org>
Date: Tue, 20 Oct 2026 18:55:53 UTC
The branch main has been updated by gallatin:

URL: https://cgit.FreeBSD.org/src/commit/?id=ef7d7011ce2fa48ab072c5e802ac00ccb25c4c48

commit ef7d7011ce2fa48ab072c5e802ac00ccb25c4c48
Author:     Andrew Gallatin <gallatin@FreeBSD.org>
AuthorDate: 2026-10-20 18:54:01 +0000
Commit:     Andrew Gallatin <gallatin@FreeBSD.org>
CommitDate: 2026-10-20 18:54:01 +0000

    arm64: Fix EFI runtime mappings with 16K pages
    
    Map complete EFI descriptor ranges at the kernel page granularity
    and merge compatible entries when 4K EFI regions share a kernel page.
    Preserve execution when any sharing region requires it and reject
    conflicting mapping attributes without panicing.
    
    This gets my small N1 booting with 16K pages.
    
    Reviewed by: chs, imp
    Sponsored by: Netflix
    Differential Revision: https://reviews.freebsd.org/D60749
---
 sys/arm64/arm64/efirt_machdep.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/sys/arm64/arm64/efirt_machdep.c b/sys/arm64/arm64/efirt_machdep.c
index 07946a2bdb82..5e6a58295e36 100644
--- a/sys/arm64/arm64/efirt_machdep.c
+++ b/sys/arm64/arm64/efirt_machdep.c
@@ -139,8 +139,6 @@ efi_1t1_l3(vm_offset_t va)
 
 	l3 = PHYS_TO_DMAP(mphys);
 	l3 += pmap_l3_index(va);
-	KASSERT(*l3 == 0, ("%s: Already mapped: va %#jx *pt %#jx", __func__,
-	    va, *l3));
 
 	return (l3);
 }
@@ -167,10 +165,9 @@ bool
 efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
 {
 	struct efi_md *p;
-	pt_entry_t *l3, l3_attr;
-	vm_offset_t va;
+	pt_entry_t *l3, l3_attr, l3_entry;
+	vm_offset_t va, end;
 	vm_page_t efi_l0_page;
-	uint64_t idx;
 	int i, mode;
 	uint64_t pa;
 
@@ -230,8 +227,9 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
 			l3_attr |= ATTR_S1_XN;
 
 		VM_OBJECT_WLOCK(obj_1t1_pt);
-		for (va = p->md_phys, idx = 0; idx < p->md_pages;
-		    idx += (PAGE_SIZE / EFI_PAGE_SIZE), va += PAGE_SIZE) {
+		/* EFI regions can start and end within a kernel page. */
+		end = p->md_phys + p->md_pages * EFI_PAGE_SIZE;
+		for (va = trunc_page(p->md_phys); va < end; va += PAGE_SIZE) {
 			l3 = efi_1t1_l3(va);
 
 			if (mode == VM_MEMATTR_DEVICE && in_realm())
@@ -239,7 +237,24 @@ efi_create_1t1_map(struct efi_md *map, int ndesc, int descsz)
 			else
 				pa = va;
 
-			*l3 = PHYS_TO_PTE(pa) | l3_attr;
+			l3_entry = PHYS_TO_PTE(pa) | l3_attr;
+			if (*l3 != 0) {
+				/*
+				 * Multiple EFI regions can share a kernel page.
+				 * Their memory attributes must agree, but the
+				 * page must be executable if any region needs it.
+				 */
+				if (((*l3 ^ l3_entry) & ~ATTR_S1_PXN) != 0) {
+					VM_OBJECT_WUNLOCK(obj_1t1_pt);
+					printf("EFI Runtime entry %d has conflicting "
+					    "mapping at %#jx: %#jx != %#jx\n", i,
+					    (uintmax_t)va, (uintmax_t)*l3,
+					    (uintmax_t)l3_entry);
+					goto fail;
+				}
+				l3_entry &= *l3 | ~ATTR_S1_PXN;
+			}
+			*l3 = l3_entry;
 		}
 		VM_OBJECT_WUNLOCK(obj_1t1_pt);
 	}