aarch64: fix SMBIOS parsing crash when loaded via shim

map startup-only regions as Normal memory
This commit is contained in:
Sam Demeulemeester
2026-08-10 16:51:47 +02:00
parent 5b28992a7a
commit 386ba63cec
+14 -8
View File
@@ -8,7 +8,8 @@
// regions described by the BIOS/EFI memory map, so all mapping functions // regions described by the BIOS/EFI memory map, so all mapping functions
// simply return the physical address unchanged. RAM is mapped as Normal // simply return the physical address unchanged. RAM is mapped as Normal
// write-back cacheable memory, the frame buffer as Normal non-cacheable // write-back cacheable memory, the frame buffer as Normal non-cacheable
// memory, and anything else mapped on demand (MMIO) as Device-nGnRnE. // memory, and anything else mapped on demand either as Normal write-back
// (RAM-resident firmware tables) or as Device-nGnRnE (MMIO).
// //
// The tables use the 4KB granule with a 48-bit input address range and // The tables use the 4KB granule with a 48-bit input address range and
// are built from 1GB and 2MB block descriptors only. paging_init() is // are built from 1GB and 2MB block descriptors only. paging_init() is
@@ -66,6 +67,7 @@
typedef struct { typedef struct {
uint64_t start; uint64_t start;
uint64_t end; uint64_t end;
uint64_t attrs;
} device_region_t; } device_region_t;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -287,13 +289,13 @@ void paging_init(void)
paging_incomplete |= !map_range(cmd_line_start, cmd_line_end, PTE_ATTR_NORMAL); paging_incomplete |= !map_range(cmd_line_start, cmd_line_end, PTE_ATTR_NORMAL);
} }
// Replay the device mappings made via map_region. // Replay the mappings made via map_region.
for (int i = 0; i < num_device_regions; i++) { for (int i = 0; i < num_device_regions; i++) {
paging_incomplete |= !map_range(device_regions[i].start, device_regions[i].end, PTE_ATTR_DEVICE); paging_incomplete |= !map_range(device_regions[i].start, device_regions[i].end, device_regions[i].attrs);
} }
} }
uintptr_t map_region(uintptr_t base_addr, size_t size, bool only_for_startup __attribute__((unused))) uintptr_t map_region(uintptr_t base_addr, size_t size, bool only_for_startup)
{ {
if (size == 0) { if (size == 0) {
size = 1; size = 1;
@@ -304,6 +306,12 @@ uintptr_t map_region(uintptr_t base_addr, size_t size, bool only_for_startup __a
return 0; return 0;
} }
// Startup-only regions are RAM-resident firmware tables (ACPI, SMBIOS,
// ...) and must be mapped as Normal memory: their parsers do unaligned
// accesses, which fault on Device memory. Everything else is assumed
// to be MMIO. Regions mapped earlier keep their original attributes.
uint64_t attrs = only_for_startup ? (PTE_ATTR_NORMAL | PTE_PXN) : PTE_ATTR_DEVICE;
// Record the region (at block granularity) so the mapping can be // Record the region (at block granularity) so the mapping can be
// replayed when the page tables are rebuilt after relocation. // replayed when the page tables are rebuilt after relocation.
uint64_t start = base_addr & ~(BLOCK_2M - 1); uint64_t start = base_addr & ~(BLOCK_2M - 1);
@@ -320,13 +328,11 @@ uintptr_t map_region(uintptr_t base_addr, size_t size, bool only_for_startup __a
} }
device_regions[num_device_regions].start = start; device_regions[num_device_regions].start = start;
device_regions[num_device_regions].end = end; device_regions[num_device_regions].end = end;
device_regions[num_device_regions].attrs = attrs;
num_device_regions++; num_device_regions++;
} }
// Anything not already mapped is assumed to be MMIO and gets a Device if (!map_range(base_addr, base_addr + size, attrs)) {
// mapping. Regions mapped earlier (e.g. RAM, the frame buffer) keep
// their original attributes.
if (!map_range(base_addr, base_addr + size, PTE_ATTR_DEVICE)) {
return 0; return 0;
} }