aarch64: give direct-booted guests ACPI via a synthesized EFI handoff - #3
Closed
tonicmuroq wants to merge 1 commit into
Closed
tonicmuroq wants to merge 1 commit into
tonicmuroq wants to merge 1 commit into
Conversation
tonicmuroq
force-pushed
the
feat/aarch64-acpi-direct-boot-pr
branch
from
September 19, 2026 14:47
8e705ef to
29e17dd
Compare
A direct-kernel-booted aarch64 guest only ever saw the FDT, so every ACPI
table create_acpi_tables() had already written went unused. PCI hotplug is
ACPI-only (GED -> PHPR.PSCN -> PCNT -> DVNT/B0EJ), which left vm.add-net and
vm.remove-device returning success while the guest noticed neither: a hot-added
NIC never appeared, and an ejected one stayed on the bus forever along with its
tap.
The arm64 kernel does not need real firmware for this. It takes the RSDP from
the EFI configuration table, which it finds through `linux,uefi-system-table`
and the `linux,uefi-mmap-*` pointers in the device tree's /chosen node, and it
does not care who produced those structures. So synthesize them:
- arch/src/aarch64/efi.rs builds an EFI System Table, a configuration table
and an EFI memory map.
- fdt::create_stub_fdt() emits a device tree holding only /chosen.
dt_scan_depth1_nodes() treats any other depth-1 node as proof of a real
DTB and disables ACPI, so describing no hardware is what lets the guest
take its hardware from ACPI — and is why acpi=force is not needed.
Selected by `--platform acpi_boot=on|off`, defaulting on for aarch64.
The stub tree also carries `linux,uefi-secure-boot = 0`: Ubuntu's kernel makes
it a required property in efi_get_fdt_params(), and without it the EFI handoff
is abandoned, the memory map is never installed, and — with no /memory node —
memblock comes up empty and paging_init panics with "Failed to allocate page
table page". Mainline ignores the property.
Four configuration tables are published. ACPI 2.0 carries the RSDP. EFI RT
Properties declares no runtime services, so the guest never calls into
firmware that does not exist. SMBIOS3 is how aarch64 Linux locates DMI at all,
which makes the tables setup_smbios() writes reachable for the first time on
this path. LINUX_EFI_MEMRESERVE lets the GICv3 ITS persist its LPI property
and pending tables through efi_mem_reserve_persistent(); without it the guest
warns twice per boot out of irq-gic-v3-its.c.
Verified on a c4a-highmem-96-metal host, Ubuntu 24.04 arm64 guest:
- /sys/firmware/{acpi,efi,dmi} present, ACPI0013 GED bound to a GIC SPI,
GICv3/ITS, PSCI, PMU, the SPCR console and DMI all taken from ACPI
- zero kernel warnings, and no unavailable ranges in the memory map
- vm.add-net: GED interrupt fires, the NIC appears with no guest-side rescan
- vm.remove-device: the guest ejects it and it leaves CH's device tree
- clone: the new MAC appears by itself and DHCPs a distinct lease, the
snapshot's NIC is gone, and no restore tap is left behind
- acpi_boot=off still boots the same guest through the device tree
tonicmuroq
force-pushed
the
feat/aarch64-acpi-direct-boot-pr
branch
from
September 19, 2026 14:58
29e17dd to
8108d55
Compare
Collaborator
|
Closing in favour of the patch-branch flow we use for everything that is headed upstream: a single commit on a branch rebased onto upstream/main, rather than a PR against The work is carried over unchanged onto
Nothing about the approach changed — the analysis and the verification in this PR still stand. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A direct-kernel-booted aarch64 guest only ever saw the FDT. Every ACPI table
create_acpi_tables()wrote went unused, and since CH's PCI hotplug is ACPI-only (GEDESCN→PHPR.PSCN→PCNT→DVNT/B0EJ),vm.add-netandvm.remove-deviceboth returned success while the guest noticed neither:For anything that clones from a snapshot this means the clone keeps the snapshot's MAC, so a MAC-keyed DHCP server hands every clone the same address, and the throwaway restore tap leaks for the life of the VM. x86 is unaffected — direct kernel boot still carries ACPI there.
What
The arm64 kernel doesn't need real firmware for ACPI. It takes the RSDP from the EFI configuration table, which it finds via
linux,uefi-system-tableand thelinux,uefi-mmap-*pointers in/chosen, and it doesn't care whether firmware or the VMM produced those structures. So we synthesize them. (Same approach OpenVMM uses for aarch64 direct boot.)arch/src/aarch64/efi.rs(new) — EFI System Table, configuration table, and EFI memory map. Dependency-free, including a small CRC32.fdt::create_stub_fdt()— a device tree with only/chosen.dt_scan_depth1_nodes()treats any other depth-1 node as proof of a real DTB and disables ACPI, so describing no hardware is exactly what lets the guest take its hardware from ACPI — and is whyacpi=forceis not needed.--platform acpi_boot=on|off, defaulting on for aarch64.Four configuration tables are published:
setup_smbios()writes were unreachable on the device-tree path and are now visibleefi_mem_reserve_persistent(); without it the guest warns twice per boot out ofirq-gic-v3-its.clinux,uefi-secure-boot = 0is load-bearing. Ubuntu's kernel makes it a required property inefi_get_fdt_params(). Without it the EFI handoff is abandoned, the memory map is never installed, and — with no/memorynode — memblock comes up empty andpaging_initpanics withFailed to allocate page table page. The symptom looks nothing like secure boot. Mainline ignores the property.Verification
On a
c4a-highmem-96-metalhost (aarch64 bare metal), Ubuntu 24.04 arm64 guest, direct kernel boot:/sys/firmware/acpi devicetree dmi efi fdt— devicetree holds only the stubACPI0013:00bound,12: GICv3 42 Edge ACPI:Gedarmv8_pmuv3_0, 21 counters (MADT GICCperformance_interrupt = 0x17)sys_vendor = Cloud Hypervisorvm.add-netvm.remove-deviceacpi_boot=offUnit tests
Nine new tests, all on aarch64. The byte-level structures and the two invariants the whole mode rests on are the parts that would fail silently, so those are what they cover:
system_table_header_is_well_formedheader_size, the CRC32 recomputed over the zeroed header, and the UTF-16LE vendor stringconfiguration_table_points_at_every_structurememory_map_covers_ram_without_overlapping/memoryto fall back on), plus firmware-owned spans never being typed conventional and the EFI region being runtime- not boot-services datastub_fdt_has_only_a_chosen_nodedt_scan_depth1_nodes()back to "real DTB" and turns ACPI offstub_fdt_publishes_the_efi_handofflinux,uefi-*property, includinglinux,uefi-secure-bootstub_fdt_carries_the_initramfs_when_there_is_onemetadata_stays_below_the_acpi_region,crc32_matches_known_vector,reserved_span_is_punched_out_of_ramMutation-checked — each of these fails on the corresponding break and nothing else does:
cargo fmt --check,cargo clippy --release --all-targetsandcargo test -p archare clean on aarch64.Open questions
configure_system_acpi()ignoresvcpu_topology,pci_space_infoand the vgic handle — all of that is ACPI's job now. It's the right split, but the two paths do take visibly different inputs.--platform num_pci_segments/NUMA configs hasn't been exercised.Note on the branch
Based on
origin/dev. My localdevhad three unpushed commits, so this was cherry-picked onto the remote head to keep the diff to this change alone.