Initial count of active cpus is communicated to bios from qemu via CMOS_BIOS_SMP_COUNT io port. However if cpus are hotplugged after boot and then guest is rebooted without taking down qemu then bios might be stuck at smp_probe while (cmos_smp_count + 1 != readl(&CountCPUs)) yield(); where cmos_smp_count + 1 will be less that CountCPUs due to additional hotplugged cpus.
One way to fix bug is to take in account hotplugged cpus and count online cpus in cpu status bitmap that qemu provides at 0xaf00 and bios uses for ACPI cpu hotplug in acpi-dsdt.dsl.
Alternative ways to fix issue is disscussed on following thread: http://www.seabios.org/pipermail/seabios/2011-August/002147.html without any conclusion
Rationale why counting cpus in cpu_sts bitmap at 0xaf00 might be better that updating CMOS_BIOS_SMP_COUNT in qemu. feeble one: we already relying on cpu_sts at 0xaf00 for ACPI cpu hotplug machinery and it seems that there is no standard way to pass this info (so we are now using "board extension"). 2nd: another possible use for cpu_sts bitmap is when cpus are able to be (hot)plugged in nonconsecutive order, MADT + CPON package should be build taking in account info about which cpus are (un)plugged.
However there is compatibility question: What to do with qemu versions that doen't have cpu_hotplug?
--- src/smp.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/src/smp.c b/src/smp.c index 8c077a1..f24d6fa 100644 --- a/src/smp.c +++ b/src/smp.c @@ -17,6 +17,9 @@
#define APIC_ENABLED 0x0100
+#define ACPI_CPU_STATUS_MAP 0xaf00 +#define ACPI_CPU_STATUS_MAP_SZ 32 + struct { u32 ecx, eax, edx; } smp_mtrr[32] VAR16VISIBLE; u32 smp_mtrr_count VAR16VISIBLE;
@@ -115,6 +118,21 @@ smp_probe(void) msleep(10); } else { u8 cmos_smp_count = inb_cmos(CMOS_BIOS_SMP_COUNT); + u8 i = 0, acpi_cpu_online_count = 0; + + /* count plugged in cpus in acpi PRST bitmap */ + while (i < ACPI_CPU_STATUS_MAP_SZ) { + u8 j = 0, status = inb(ACPI_CPU_STATUS_MAP + (i++)); + while (j < 8) + if ((status >> j++) & 1) + ++acpi_cpu_online_count; + } + /* if one or several cpus were hotplugged then on reboot we should + * take them into account and wait for them too*/ + if (cmos_smp_count < --acpi_cpu_online_count) + cmos_smp_count = acpi_cpu_online_count; + + dprintf(1, "acpi_cpu_online_count: %d\n", acpi_cpu_online_count); while (cmos_smp_count + 1 != readl(&CountCPUs)) yield(); }