/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
*
- * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug
- * QEMU<->SeaBIOS interface is not based on the "CPU index", but on the APIC
- * ID of hotplugged CPUs[1]. This means that FW_CFG_MAX_CPUS is not the
- * "maximum number of CPUs", but the "limit to the APIC ID values SeaBIOS
- * may see".
+ * For machine types prior to 1.8, SeaBIOS needs FW_CFG_MAX_CPUS for
+ * building MPTable, ACPI MADT, ACPI CPU hotplug and ACPI SRAT table,
+ * that tables are based on xAPIC ID and QEMU<->SeaBIOS interface
+ * for CPU hotplug also uses APIC ID and not "CPU index".
+ * This means that FW_CFG_MAX_CPUS is not the "maximum number of CPUs",
+ * but the "limit to the APIC ID values SeaBIOS may see".
*
- * So, this means we must not use max_cpus, here, but the maximum possible
- * APIC ID value, plus one.
- *
- * [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is
- * the APIC ID, not the "CPU index"
+ * So for compatibility reasons with old BIOSes we are stuck with
+ * "etc/max-cpus" actually being apic_id_limit
*/
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)pcms->apic_id_limit);
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
}
}
-static int pc_present_cpus_count(PCMachineState *pcms)
-{
- int i, boot_cpus = 0;
- for (i = 0; i < pcms->possible_cpus->len; i++) {
- if (pcms->possible_cpus->cpus[i].cpu) {
- boot_cpus++;
- }
- }
- return boot_cpus;
-}
-
static X86CPU *pc_new_cpu(const char *typename, int64_t apic_id,
Error **errp)
{
* This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
*/
pcms->apic_id_limit = x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
- if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
- error_report("max_cpus is too large. APIC ID of last CPU is %u",
- pcms->apic_id_limit - 1);
- exit(1);
- }
-
pcms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
sizeof(CPUArchId) * max_cpus);
for (i = 0; i < max_cpus; i++) {
fw_cfg_add_file(pcms->fw_cfg, "etc/msr_feature_control", val, sizeof(*val));
}
+static void rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count)
+{
+ if (cpus_count > 0xff) {
+ /* If the number of CPUs can't be represented in 8 bits, the
+ * BIOS must use "etc/boot-cpus". Set RTC field to 0 just
+ * to make old BIOSes fail more predictably.
+ */
+ rtc_set_memory(rtc, 0x5f, 0);
+ } else {
+ rtc_set_memory(rtc, 0x5f, cpus_count - 1);
+ }
+}
+
static
void pc_machine_done(Notifier *notifier, void *data)
{
PCIBus *bus = pcms->bus;
/* set the number of CPUs */
- rtc_set_memory(pcms->rtc, 0x5f, pc_present_cpus_count(pcms) - 1);
+ rtc_set_cpus_count(pcms->rtc, le16_to_cpu(pcms->boot_cpus_le));
if (bus) {
int extra_hosts = 0;
acpi_setup();
if (pcms->fw_cfg) {
+ MachineClass *mc = MACHINE_GET_CLASS(pcms);
+
pc_build_smbios(pcms->fw_cfg);
pc_build_feature_control_file(pcms);
+
+ if (mc->max_cpus > 255) {
+ fw_cfg_add_file(pcms->fw_cfg, "etc/boot-cpus", &pcms->boot_cpus_le,
+ sizeof(pcms->boot_cpus_le));
+ }
}
}
pcspk_init(isa_bus, pit);
}
- serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
+ serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS);
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
}
}
+ /* increment the number of CPUs */
+ pcms->boot_cpus_le = cpu_to_le16(le16_to_cpu(pcms->boot_cpus_le) + 1);
if (dev->hotplugged) {
- /* increment the number of CPUs */
- rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
+ /* Update the number of CPUs in CMOS */
+ rtc_set_cpus_count(pcms->rtc, le16_to_cpu(pcms->boot_cpus_le));
}
found_cpu = pc_find_cpu_slot(pcms, CPU(dev), NULL);
found_cpu->cpu = NULL;
object_unparent(OBJECT(dev));
- rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) - 1);
+ /* decrement the number of CPUs */
+ pcms->boot_cpus_le = cpu_to_le16(le16_to_cpu(pcms->boot_cpus_le) - 1);
+ /* Update the number of CPUs in CMOS */
+ rtc_set_cpus_count(pcms->rtc, le16_to_cpu(pcms->boot_cpus_le));
out:
error_propagate(errp, local_err);
}
{
PCMachineState *pcms = PC_MACHINE(obj);
- object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int",
- pc_machine_get_hotplug_memory_region_size,
- NULL, NULL, NULL, &error_abort);
-
pcms->max_ram_below_4g = 0; /* use default */
- object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
- pc_machine_get_max_ram_below_4g,
- pc_machine_set_max_ram_below_4g,
- NULL, NULL, &error_abort);
- object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G,
- "Maximum ram below the 4G boundary (32bit boundary)",
- &error_abort);
-
pcms->smm = ON_OFF_AUTO_AUTO;
- object_property_add(obj, PC_MACHINE_SMM, "OnOffAuto",
- pc_machine_get_smm,
- pc_machine_set_smm,
- NULL, NULL, &error_abort);
- object_property_set_description(obj, PC_MACHINE_SMM,
- "Enable SMM (pc & q35)",
- &error_abort);
-
pcms->vmport = ON_OFF_AUTO_AUTO;
- object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto",
- pc_machine_get_vmport,
- pc_machine_set_vmport,
- NULL, NULL, &error_abort);
- object_property_set_description(obj, PC_MACHINE_VMPORT,
- "Enable vmport (pc & q35)",
- &error_abort);
-
/* nvdimm is disabled on default. */
pcms->acpi_nvdimm_state.is_enabled = false;
- object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
- pc_machine_set_nvdimm, &error_abort);
}
static void pc_machine_reset(void)
hc->unplug_request = pc_machine_device_unplug_request_cb;
hc->unplug = pc_machine_device_unplug_cb;
nc->nmi_monitor_handler = x86_nmi;
+
+ object_class_property_add(oc, PC_MACHINE_MEMHP_REGION_SIZE, "int",
+ pc_machine_get_hotplug_memory_region_size, NULL,
+ NULL, NULL, &error_abort);
+
+ object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
+ pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g,
+ NULL, NULL, &error_abort);
+
+ object_class_property_set_description(oc, PC_MACHINE_MAX_RAM_BELOW_4G,
+ "Maximum ram below the 4G boundary (32bit boundary)", &error_abort);
+
+ object_class_property_add(oc, PC_MACHINE_SMM, "OnOffAuto",
+ pc_machine_get_smm, pc_machine_set_smm,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, PC_MACHINE_SMM,
+ "Enable SMM (pc & q35)", &error_abort);
+
+ object_class_property_add(oc, PC_MACHINE_VMPORT, "OnOffAuto",
+ pc_machine_get_vmport, pc_machine_set_vmport,
+ NULL, NULL, &error_abort);
+ object_class_property_set_description(oc, PC_MACHINE_VMPORT,
+ "Enable vmport (pc & q35)", &error_abort);
+
+ object_class_property_add_bool(oc, PC_MACHINE_NVDIMM,
+ pc_machine_get_nvdimm, pc_machine_set_nvdimm, &error_abort);
}
static const TypeInfo pc_machine_info = {