mbox series

[v8,00/21] microvm: add acpi support

Message ID 20200915120909.20838-1-kraxel@redhat.com
Headers show
Series microvm: add acpi support | expand

Message

Gerd Hoffmann Sept. 15, 2020, 12:08 p.m. UTC
I know that not supporting ACPI in microvm is intentional.  If you still
don't want ACPI this is perfectly fine, you can use the usual -no-acpi
switch to toggle ACPI support.

These are the advantages you are going to loose then:

  (1) virtio-mmio device discovery without command line hacks (tweaking
      the command line is a problem when not using direct kernel boot).
  (2) Better IO-APIC support, we can use IRQ lines 16-23.
  (3) ACPI power button (aka powerdown request) works.
  (4) machine poweroff (aka S5 state) works.

Together with seabios patches for virtio-mmio support this allows to
boot standard fedora images (cloud, coreos, workstation live) with the
microvm machine type.

git branch for testing (including updated seabios):
	https://git.kraxel.org/cgit/qemu/log/?h=sirius/microvm

changes in v2:
  * some acpi cleanups are an separate patch series now.
  * switched to hw reduced acpi & generic event device.
  * misc fixes here and there.

changes in v3:
  * depeds on "[PATCH v6 00/16] acpi: i386 tweaks" series.
  * renamed qboot to qboot.bin
  * updated seabios to master branch snapshot.
    - this version boots fine with rtc=off
  * generic event device tweaks (Igor's comments).
  * make SMP work.
  * add RfC patches to turn off acpi by default for microvm.
  * misc fixes here and there.

changes in v4:
  * rebase to latest master
    - this also depends on the pending seabios update
  * drop some patches which got cherry-picked.
  * wire up cpu hotplug (also does coldplug cpu init).
  * add microvm acpi test case.
  * dropped RfC patches to turn off acpi by default for microvm.
  * misc fixes here and there.

changes in v5:
  * rebase to latest master (seabios dependency is merged meanwhile)
  * fix DSDT version (mst).
  * add comments referencing the ACPI spec for easier review (mst).

changes in v6:
  * rebase to latest master, adapt to meson build system.
  * pick up some review tags.
  * misc fixes here and there.

changes in v7:
  * rebase to latest master, adapt to x86 cpu hotplug changes.
  * meson tweaks for firmware binaries.
  * pick up some review tags.

changes in v8:
  * rebase to latest master, adapt to typedef changes.
  * pick up some review tags.

take care,
  Gerd

Gerd Hoffmann (21):
  microvm: name qboot binary qboot.rom
  seabios: add microvm config, update build rules
  seabios: add bios-microvm.bin binary
  acpi: ged: add control regs
  acpi: ged: add x86 device variant.
  acpi: move acpi_dsdt_add_power_button() to ged
  microvm: make virtio irq base runtime configurable
  microvm/acpi: add minimal acpi support
  microvm/acpi: add acpi_dsdt_add_virtio() for x86
  microvm/acpi: use GSI 16-23 for virtio
  microvm/acpi: use seabios with acpi=on
  microvm/acpi: disable virtio-mmio cmdline hack
  x86: constify x86_machine_is_*_enabled
  x86: move acpi_dev from pc/microvm
  x86: move cpu hotplug from pc to x86
  microvm: wire up hotplug
  tests/acpi: allow microvm test data updates.
  tests/acpi: allow override blkdev
  tests/acpi: add microvm test
  tests/acpi: update expected data files for microvm
  microvm: enable ramfb

 hw/i386/acpi-microvm.h                 |   8 +
 include/hw/acpi/generic_event_device.h |  17 ++
 include/hw/i386/microvm.h              |  10 +-
 include/hw/i386/pc.h                   |   1 -
 include/hw/i386/x86.h                  |  15 +-
 hw/acpi/generic_event_device.c         |  52 +++++
 hw/arm/virt-acpi-build.c               |   8 -
 hw/i386/acpi-build.c                   |   2 +-
 hw/i386/acpi-microvm.c                 | 240 ++++++++++++++++++++
 hw/i386/generic_event_device_x86.c     |  36 +++
 hw/i386/microvm.c                      | 108 ++++++++-
 hw/i386/pc.c                           | 297 ++-----------------------
 hw/i386/pc_piix.c                      |   2 +-
 hw/i386/pc_q35.c                       |   2 +-
 hw/i386/x86.c                          | 275 ++++++++++++++++++++++-
 tests/qtest/bios-tables-test.c         |  21 +-
 hw/i386/Kconfig                        |   1 +
 hw/i386/meson.build                    |   3 +-
 pc-bios/bios-microvm.bin               | Bin 65536 -> 131072 bytes
 pc-bios/meson.build                    |   1 +
 pc-bios/qboot.rom                      | Bin 0 -> 65536 bytes
 roms/Makefile                          |  11 +-
 roms/config.seabios-microvm            |  26 +++
 tests/data/acpi/microvm/APIC           | Bin 0 -> 70 bytes
 tests/data/acpi/microvm/DSDT           | Bin 0 -> 365 bytes
 tests/data/acpi/microvm/FACP           | Bin 0 -> 268 bytes
 26 files changed, 823 insertions(+), 313 deletions(-)
 create mode 100644 hw/i386/acpi-microvm.h
 create mode 100644 hw/i386/acpi-microvm.c
 create mode 100644 hw/i386/generic_event_device_x86.c
 create mode 100644 pc-bios/qboot.rom
 create mode 100644 roms/config.seabios-microvm
 create mode 100644 tests/data/acpi/microvm/APIC
 create mode 100644 tests/data/acpi/microvm/DSDT
 create mode 100644 tests/data/acpi/microvm/FACP

-- 
2.27.0

Comments

Igor Mammedov Sept. 15, 2020, 12:55 p.m. UTC | #1
On Tue, 15 Sep 2020 14:09:03 +0200
Gerd Hoffmann <kraxel@redhat.com> wrote:

> The cpu hotplug code handles the initialization of coldplugged cpus

> too, so it is needed even in case cpu hotplug is not supported.

> 

> Move the code from pc to x86, so microvm can use it.

> 

> Move both plug and unplug to keep everything in one place, even

> though microvm needs plug only.

> 

> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

> Reviewed-by: Sergio Lopez <slp@redhat.com>


Reviewed-by: Igor Mammedov <imammedo@redhat.com>


> ---

>  include/hw/i386/x86.h |  10 ++

>  hw/i386/pc.c          | 281 +-----------------------------------------

>  hw/i386/x86.c         | 271 ++++++++++++++++++++++++++++++++++++++++

>  3 files changed, 286 insertions(+), 276 deletions(-)

> 

> diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h

> index f540e801a837..0eef9fb0c0d7 100644

> --- a/include/hw/i386/x86.h

> +++ b/include/hw/i386/x86.h

> @@ -92,6 +92,16 @@ CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms,

>                                               unsigned cpu_index);

>  int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx);

>  const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms);

> +CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx);

> +void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count);

> +void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,

> +                      DeviceState *dev, Error **errp);

> +void x86_cpu_plug(HotplugHandler *hotplug_dev,

> +                  DeviceState *dev, Error **errp);

> +void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,

> +                               DeviceState *dev, Error **errp);

> +void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,

> +                       DeviceState *dev, Error **errp);

>  

>  void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw);

>  

> diff --git a/hw/i386/pc.c b/hw/i386/pc.c

> index a18140421e92..b55369357e5d 100644

> --- a/hw/i386/pc.c

> +++ b/hw/i386/pc.c

> @@ -803,19 +803,6 @@ void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp)

>      }

>  }

>  

> -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 "FW_CFG_NB_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)

>  {

> @@ -825,7 +812,7 @@ void pc_machine_done(Notifier *notifier, void *data)

>      PCIBus *bus = pcms->bus;

>  

>      /* set the number of CPUs */

> -    rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);

> +    x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);

>  

>      if (bus) {

>          int extra_hosts = 0;

> @@ -1373,264 +1360,6 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev,

>      error_propagate(errp, local_err);

>  }

>  

> -static int pc_apic_cmp(const void *a, const void *b)

> -{

> -   CPUArchId *apic_a = (CPUArchId *)a;

> -   CPUArchId *apic_b = (CPUArchId *)b;

> -

> -   return apic_a->arch_id - apic_b->arch_id;

> -}

> -

> -/* returns pointer to CPUArchId descriptor that matches CPU's apic_id

> - * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no

> - * entry corresponding to CPU's apic_id returns NULL.

> - */

> -static CPUArchId *pc_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)

> -{

> -    CPUArchId apic_id, *found_cpu;

> -

> -    apic_id.arch_id = id;

> -    found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,

> -        ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),

> -        pc_apic_cmp);

> -    if (found_cpu && idx) {

> -        *idx = found_cpu - ms->possible_cpus->cpus;

> -    }

> -    return found_cpu;

> -}

> -

> -static void pc_cpu_plug(HotplugHandler *hotplug_dev,

> -                        DeviceState *dev, Error **errp)

> -{

> -    CPUArchId *found_cpu;

> -    Error *local_err = NULL;

> -    X86CPU *cpu = X86_CPU(dev);

> -    PCMachineState *pcms = PC_MACHINE(hotplug_dev);

> -    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> -

> -    if (x86ms->acpi_dev) {

> -        hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err);

> -        if (local_err) {

> -            goto out;

> -        }

> -    }

> -

> -    /* increment the number of CPUs */

> -    x86ms->boot_cpus++;

> -    if (x86ms->rtc) {

> -        rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);

> -    }

> -    if (x86ms->fw_cfg) {

> -        fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);

> -    }

> -

> -    found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);

> -    found_cpu->cpu = OBJECT(dev);

> -out:

> -    error_propagate(errp, local_err);

> -}

> -static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,

> -                                     DeviceState *dev, Error **errp)

> -{

> -    int idx = -1;

> -    X86CPU *cpu = X86_CPU(dev);

> -    PCMachineState *pcms = PC_MACHINE(hotplug_dev);

> -    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> -

> -    if (!x86ms->acpi_dev) {

> -        error_setg(errp, "CPU hot unplug not supported without ACPI");

> -        return;

> -    }

> -

> -    pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx);

> -    assert(idx != -1);

> -    if (idx == 0) {

> -        error_setg(errp, "Boot CPU is unpluggable");

> -        return;

> -    }

> -

> -    hotplug_handler_unplug_request(x86ms->acpi_dev, dev,

> -                                   errp);

> -}

> -

> -static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev,

> -                             DeviceState *dev, Error **errp)

> -{

> -    CPUArchId *found_cpu;

> -    Error *local_err = NULL;

> -    X86CPU *cpu = X86_CPU(dev);

> -    PCMachineState *pcms = PC_MACHINE(hotplug_dev);

> -    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> -

> -    hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err);

> -    if (local_err) {

> -        goto out;

> -    }

> -

> -    found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL);

> -    found_cpu->cpu = NULL;

> -    qdev_unrealize(dev);

> -

> -    /* decrement the number of CPUs */

> -    x86ms->boot_cpus--;

> -    /* Update the number of CPUs in CMOS */

> -    rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);

> -    fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);

> - out:

> -    error_propagate(errp, local_err);

> -}

> -

> -static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev,

> -                            DeviceState *dev, Error **errp)

> -{

> -    int idx;

> -    CPUState *cs;

> -    CPUArchId *cpu_slot;

> -    X86CPUTopoIDs topo_ids;

> -    X86CPU *cpu = X86_CPU(dev);

> -    CPUX86State *env = &cpu->env;

> -    MachineState *ms = MACHINE(hotplug_dev);

> -    PCMachineState *pcms = PC_MACHINE(hotplug_dev);

> -    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> -    unsigned int smp_cores = ms->smp.cores;

> -    unsigned int smp_threads = ms->smp.threads;

> -    X86CPUTopoInfo topo_info;

> -

> -    if(!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {

> -        error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",

> -                   ms->cpu_type);

> -        return;

> -    }

> -

> -    init_topo_info(&topo_info, x86ms);

> -

> -    env->nr_dies = x86ms->smp_dies;

> -

> -    /*

> -     * If APIC ID is not set,

> -     * set it based on socket/die/core/thread properties.

> -     */

> -    if (cpu->apic_id == UNASSIGNED_APIC_ID) {

> -        int max_socket = (ms->smp.max_cpus - 1) /

> -                                smp_threads / smp_cores / x86ms->smp_dies;

> -

> -        /*

> -         * die-id was optional in QEMU 4.0 and older, so keep it optional

> -         * if there's only one die per socket.

> -         */

> -        if (cpu->die_id < 0 && x86ms->smp_dies == 1) {

> -            cpu->die_id = 0;

> -        }

> -

> -        if (cpu->socket_id < 0) {

> -            error_setg(errp, "CPU socket-id is not set");

> -            return;

> -        } else if (cpu->socket_id > max_socket) {

> -            error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",

> -                       cpu->socket_id, max_socket);

> -            return;

> -        }

> -        if (cpu->die_id < 0) {

> -            error_setg(errp, "CPU die-id is not set");

> -            return;

> -        } else if (cpu->die_id > x86ms->smp_dies - 1) {

> -            error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",

> -                       cpu->die_id, x86ms->smp_dies - 1);

> -            return;

> -        }

> -        if (cpu->core_id < 0) {

> -            error_setg(errp, "CPU core-id is not set");

> -            return;

> -        } else if (cpu->core_id > (smp_cores - 1)) {

> -            error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",

> -                       cpu->core_id, smp_cores - 1);

> -            return;

> -        }

> -        if (cpu->thread_id < 0) {

> -            error_setg(errp, "CPU thread-id is not set");

> -            return;

> -        } else if (cpu->thread_id > (smp_threads - 1)) {

> -            error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",

> -                       cpu->thread_id, smp_threads - 1);

> -            return;

> -        }

> -

> -        topo_ids.pkg_id = cpu->socket_id;

> -        topo_ids.die_id = cpu->die_id;

> -        topo_ids.core_id = cpu->core_id;

> -        topo_ids.smt_id = cpu->thread_id;

> -        cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);

> -    }

> -

> -    cpu_slot = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, &idx);

> -    if (!cpu_slot) {

> -        MachineState *ms = MACHINE(pcms);

> -

> -        x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);

> -        error_setg(errp,

> -            "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"

> -            " APIC ID %" PRIu32 ", valid index range 0:%d",

> -            topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,

> -            cpu->apic_id, ms->possible_cpus->len - 1);

> -        return;

> -    }

> -

> -    if (cpu_slot->cpu) {

> -        error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",

> -                   idx, cpu->apic_id);

> -        return;

> -    }

> -

> -    /* if 'address' properties socket-id/core-id/thread-id are not set, set them

> -     * so that machine_query_hotpluggable_cpus would show correct values

> -     */

> -    /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()

> -     * once -smp refactoring is complete and there will be CPU private

> -     * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */

> -    x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);

> -    if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) {

> -        error_setg(errp, "property socket-id: %u doesn't match set apic-id:"

> -            " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id,

> -            topo_ids.pkg_id);

> -        return;

> -    }

> -    cpu->socket_id = topo_ids.pkg_id;

> -

> -    if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) {

> -        error_setg(errp, "property die-id: %u doesn't match set apic-id:"

> -            " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id);

> -        return;

> -    }

> -    cpu->die_id = topo_ids.die_id;

> -

> -    if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) {

> -        error_setg(errp, "property core-id: %u doesn't match set apic-id:"

> -            " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id,

> -            topo_ids.core_id);

> -        return;

> -    }

> -    cpu->core_id = topo_ids.core_id;

> -

> -    if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) {

> -        error_setg(errp, "property thread-id: %u doesn't match set apic-id:"

> -            " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id,

> -            topo_ids.smt_id);

> -        return;

> -    }

> -    cpu->thread_id = topo_ids.smt_id;

> -

> -    if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&

> -        !kvm_hv_vpindex_settable()) {

> -        error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");

> -        return;

> -    }

> -

> -    cs = CPU(cpu);

> -    cs->cpu_index = idx;

> -

> -    numa_cpu_pre_plug(cpu_slot, dev, errp);

> -}

> -

>  static void pc_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev,

>                                        DeviceState *dev, Error **errp)

>  {

> @@ -1699,7 +1428,7 @@ static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,

>      if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {

>          pc_memory_pre_plug(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {

> -        pc_cpu_pre_plug(hotplug_dev, dev, errp);

> +        x86_cpu_pre_plug(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||

>                 object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {

>          pc_virtio_md_pci_pre_plug(hotplug_dev, dev, errp);

> @@ -1712,7 +1441,7 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,

>      if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {

>          pc_memory_plug(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {

> -        pc_cpu_plug(hotplug_dev, dev, errp);

> +        x86_cpu_plug(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||

>                 object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {

>          pc_virtio_md_pci_plug(hotplug_dev, dev, errp);

> @@ -1725,7 +1454,7 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,

>      if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {

>          pc_memory_unplug_request(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {

> -        pc_cpu_unplug_request_cb(hotplug_dev, dev, errp);

> +        x86_cpu_unplug_request_cb(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||

>                 object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {

>          pc_virtio_md_pci_unplug_request(hotplug_dev, dev, errp);

> @@ -1741,7 +1470,7 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,

>      if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {

>          pc_memory_unplug(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {

> -        pc_cpu_unplug_cb(hotplug_dev, dev, errp);

> +        x86_cpu_unplug_cb(hotplug_dev, dev, errp);

>      } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_PMEM_PCI) ||

>                 object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {

>          pc_virtio_md_pci_unplug(hotplug_dev, dev, errp);

> diff --git a/hw/i386/x86.c b/hw/i386/x86.c

> index e2a5005f389c..c2ea989579c9 100644

> --- a/hw/i386/x86.c

> +++ b/hw/i386/x86.c

> @@ -41,6 +41,7 @@

>  #include "hw/i386/topology.h"

>  #include "hw/i386/fw_cfg.h"

>  #include "hw/intc/i8259.h"

> +#include "hw/rtc/mc146818rtc.h"

>  

>  #include "hw/acpi/cpu_hotplug.h"

>  #include "hw/irq.h"

> @@ -137,6 +138,276 @@ void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version)

>      }

>  }

>  

> +void x86_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 "FW_CFG_NB_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 int x86_apic_cmp(const void *a, const void *b)

> +{

> +   CPUArchId *apic_a = (CPUArchId *)a;

> +   CPUArchId *apic_b = (CPUArchId *)b;

> +

> +   return apic_a->arch_id - apic_b->arch_id;

> +}

> +

> +/*

> + * returns pointer to CPUArchId descriptor that matches CPU's apic_id

> + * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no

> + * entry corresponding to CPU's apic_id returns NULL.

> + */

> +CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx)

> +{

> +    CPUArchId apic_id, *found_cpu;

> +

> +    apic_id.arch_id = id;

> +    found_cpu = bsearch(&apic_id, ms->possible_cpus->cpus,

> +        ms->possible_cpus->len, sizeof(*ms->possible_cpus->cpus),

> +        x86_apic_cmp);

> +    if (found_cpu && idx) {

> +        *idx = found_cpu - ms->possible_cpus->cpus;

> +    }

> +    return found_cpu;

> +}

> +

> +void x86_cpu_plug(HotplugHandler *hotplug_dev,

> +                  DeviceState *dev, Error **errp)

> +{

> +    CPUArchId *found_cpu;

> +    Error *local_err = NULL;

> +    X86CPU *cpu = X86_CPU(dev);

> +    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> +

> +    if (x86ms->acpi_dev) {

> +        hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err);

> +        if (local_err) {

> +            goto out;

> +        }

> +    }

> +

> +    /* increment the number of CPUs */

> +    x86ms->boot_cpus++;

> +    if (x86ms->rtc) {

> +        x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);

> +    }

> +    if (x86ms->fw_cfg) {

> +        fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);

> +    }

> +

> +    found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);

> +    found_cpu->cpu = OBJECT(dev);

> +out:

> +    error_propagate(errp, local_err);

> +}

> +

> +void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,

> +                               DeviceState *dev, Error **errp)

> +{

> +    int idx = -1;

> +    X86CPU *cpu = X86_CPU(dev);

> +    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> +

> +    if (!x86ms->acpi_dev) {

> +        error_setg(errp, "CPU hot unplug not supported without ACPI");

> +        return;

> +    }

> +

> +    x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);

> +    assert(idx != -1);

> +    if (idx == 0) {

> +        error_setg(errp, "Boot CPU is unpluggable");

> +        return;

> +    }

> +

> +    hotplug_handler_unplug_request(x86ms->acpi_dev, dev,

> +                                   errp);

> +}

> +

> +void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev,

> +                       DeviceState *dev, Error **errp)

> +{

> +    CPUArchId *found_cpu;

> +    Error *local_err = NULL;

> +    X86CPU *cpu = X86_CPU(dev);

> +    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> +

> +    hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err);

> +    if (local_err) {

> +        goto out;

> +    }

> +

> +    found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL);

> +    found_cpu->cpu = NULL;

> +    qdev_unrealize(dev);

> +

> +    /* decrement the number of CPUs */

> +    x86ms->boot_cpus--;

> +    /* Update the number of CPUs in CMOS */

> +    x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);

> +    fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);

> + out:

> +    error_propagate(errp, local_err);

> +}

> +

> +void x86_cpu_pre_plug(HotplugHandler *hotplug_dev,

> +                      DeviceState *dev, Error **errp)

> +{

> +    int idx;

> +    CPUState *cs;

> +    CPUArchId *cpu_slot;

> +    X86CPUTopoIDs topo_ids;

> +    X86CPU *cpu = X86_CPU(dev);

> +    CPUX86State *env = &cpu->env;

> +    MachineState *ms = MACHINE(hotplug_dev);

> +    X86MachineState *x86ms = X86_MACHINE(hotplug_dev);

> +    unsigned int smp_cores = ms->smp.cores;

> +    unsigned int smp_threads = ms->smp.threads;

> +    X86CPUTopoInfo topo_info;

> +

> +    if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {

> +        error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",

> +                   ms->cpu_type);

> +        return;

> +    }

> +

> +    init_topo_info(&topo_info, x86ms);

> +

> +    env->nr_dies = x86ms->smp_dies;

> +

> +    /*

> +     * If APIC ID is not set,

> +     * set it based on socket/die/core/thread properties.

> +     */

> +    if (cpu->apic_id == UNASSIGNED_APIC_ID) {

> +        int max_socket = (ms->smp.max_cpus - 1) /

> +                                smp_threads / smp_cores / x86ms->smp_dies;

> +

> +        /*

> +         * die-id was optional in QEMU 4.0 and older, so keep it optional

> +         * if there's only one die per socket.

> +         */

> +        if (cpu->die_id < 0 && x86ms->smp_dies == 1) {

> +            cpu->die_id = 0;

> +        }

> +

> +        if (cpu->socket_id < 0) {

> +            error_setg(errp, "CPU socket-id is not set");

> +            return;

> +        } else if (cpu->socket_id > max_socket) {

> +            error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u",

> +                       cpu->socket_id, max_socket);

> +            return;

> +        }

> +        if (cpu->die_id < 0) {

> +            error_setg(errp, "CPU die-id is not set");

> +            return;

> +        } else if (cpu->die_id > x86ms->smp_dies - 1) {

> +            error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u",

> +                       cpu->die_id, x86ms->smp_dies - 1);

> +            return;

> +        }

> +        if (cpu->core_id < 0) {

> +            error_setg(errp, "CPU core-id is not set");

> +            return;

> +        } else if (cpu->core_id > (smp_cores - 1)) {

> +            error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",

> +                       cpu->core_id, smp_cores - 1);

> +            return;

> +        }

> +        if (cpu->thread_id < 0) {

> +            error_setg(errp, "CPU thread-id is not set");

> +            return;

> +        } else if (cpu->thread_id > (smp_threads - 1)) {

> +            error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u",

> +                       cpu->thread_id, smp_threads - 1);

> +            return;

> +        }

> +

> +        topo_ids.pkg_id = cpu->socket_id;

> +        topo_ids.die_id = cpu->die_id;

> +        topo_ids.core_id = cpu->core_id;

> +        topo_ids.smt_id = cpu->thread_id;

> +        cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids);

> +    }

> +

> +    cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx);

> +    if (!cpu_slot) {

> +        MachineState *ms = MACHINE(x86ms);

> +

> +        x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);

> +        error_setg(errp,

> +            "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with"

> +            " APIC ID %" PRIu32 ", valid index range 0:%d",

> +            topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id,

> +            cpu->apic_id, ms->possible_cpus->len - 1);

> +        return;

> +    }

> +

> +    if (cpu_slot->cpu) {

> +        error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists",

> +                   idx, cpu->apic_id);

> +        return;

> +    }

> +

> +    /* if 'address' properties socket-id/core-id/thread-id are not set, set them

> +     * so that machine_query_hotpluggable_cpus would show correct values

> +     */

> +    /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn()

> +     * once -smp refactoring is complete and there will be CPU private

> +     * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */

> +    x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids);

> +    if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) {

> +        error_setg(errp, "property socket-id: %u doesn't match set apic-id:"

> +            " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id,

> +            topo_ids.pkg_id);

> +        return;

> +    }

> +    cpu->socket_id = topo_ids.pkg_id;

> +

> +    if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) {

> +        error_setg(errp, "property die-id: %u doesn't match set apic-id:"

> +            " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id);

> +        return;

> +    }

> +    cpu->die_id = topo_ids.die_id;

> +

> +    if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) {

> +        error_setg(errp, "property core-id: %u doesn't match set apic-id:"

> +            " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id,

> +            topo_ids.core_id);

> +        return;

> +    }

> +    cpu->core_id = topo_ids.core_id;

> +

> +    if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) {

> +        error_setg(errp, "property thread-id: %u doesn't match set apic-id:"

> +            " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id,

> +            topo_ids.smt_id);

> +        return;

> +    }

> +    cpu->thread_id = topo_ids.smt_id;

> +

> +    if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) &&

> +        !kvm_hv_vpindex_settable()) {

> +        error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX");

> +        return;

> +    }

> +

> +    cs = CPU(cpu);

> +    cs->cpu_index = idx;

> +

> +    numa_cpu_pre_plug(cpu_slot, dev, errp);

> +}

> +

>  CpuInstanceProperties

>  x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index)

>  {