Subject: [PATCH] acpi: keep the CPU objects macOS needs without the FIRMWARE tell Targets VMAware check: FIRMWARE (CPU hotplug I/O signature) macOS references \_SB.CPUS.Cxxx from its own CpuPlug SSDT, so the guest needs QEMU's build_cpus_aml output (the \_SB.PCI0.PRES controller and the \_SB.CPUS Processor objects) or it hangs in ACPI namespace init. 01-firmware dropped that block because build_cpus_aml emits the CPU-hotplug register region at I/O port 0x0CD8, which VMAware v2.8.1 FIRMWARE byte-matches as the WordData sequence 0x0B 0xD8 0x0C and flips to a QEMU verdict. HEAD has no such check. This re-adds the block for every guest (no opt-in) and removes that one tell at the source rather than removing the whole block: - the controller _UID is renamed from "CPU Hotplug resources" to "Reserved resources C", clearing the FIRMWARE string match the same way 01-firmware renamed the other reserved-resource devices. - the PRST OperationRegion base is emitted as Add(0x0C00, 0xD8) for a SystemIO base. It evaluates to the identical 0x0CD8 at namespace load, so the port QEMU decodes and the port OVMF's SMM code reads directly are both unchanged. Relocating the base instead was tried and breaks every SMM/OVMF guest (macOS hangs at boot, Windows too), because OVMF hardcodes 0x0CD8. Splitting the operand keeps 0x0CD8 live but never places the three matched bytes next to each other in the AML. The _CRS IO descriptor keeps 0x0CD8 untouched: a small-resource IO descriptor encodes its base as 0xD8 0x0C preceded by the 0x47/0x01 descriptor bytes, never by 0x0B, so it is not the sequence the scanner matches. Verified against QEMU 11.1.2: macOS boots to login and powers off via S5; VMAware v2.8.1 and HEAD both read bare metal with FIRMWARE clear on the stealth guest; Windows and Linux boot unchanged. Applies to QEMU v11.1.2, on top of 01-firmware. From the source root: patch -p1 < 11-macos-cpu-hotplug.patch --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1002,6 +1002,16 @@ } aml_append(dsdt, scope); + { + CPUHotplugFeatures opts = { + .acpi_1_compatible = true, + .smi_path = pm->smi_on_cpuhp ? "\\_SB.PCI0.SMI0.SMIC" : NULL, + .fw_unplugs_cpu = pm->smi_on_cpu_unplug, + }; + build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry, + pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02", + AML_SYSTEM_IO); + } crs_range_set_init(&crs_range_set); bus = PC_MACHINE(machine)->pcibus; --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -363,7 +363,7 @@ aml_append(cpu_ctrl_dev, aml_name_decl("_HID", aml_eisaid("PNP0A06"))); aml_append(cpu_ctrl_dev, - aml_name_decl("_UID", aml_string("CPU Hotplug resources"))); + aml_name_decl("_UID", aml_string("Reserved resources C"))); aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY)); @@ -379,9 +379,21 @@ aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); - /* declare CPU hotplug MMIO region with related access fields */ + /* + * Declare the CPU hotplug register block. The region is at the same + * fixed base QEMU decodes and firmware (OVMF SMM) reads directly, so + * that is unchanged. Only the base operand's AML encoding is split + * across an Add() for a SystemIO base: emitting the 16-bit port as a + * single WordData leaks the exact three-byte sequence a VM-detection + * scanner byte-matches on. The Add() evaluates to the identical base + * at namespace load but never places those bytes contiguously. + */ + Aml *region_base = (rs == AML_SYSTEM_IO) + ? aml_add(aml_int(base_addr & 0xff00), + aml_int(base_addr & 0x00ff), NULL) + : aml_int(base_addr); aml_append(cpu_ctrl_dev, - aml_operation_region("PRST", rs, aml_int(base_addr), + aml_operation_region("PRST", rs, region_base, ACPI_CPU_HOTPLUG_REG_LEN)); field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK,