From 3bee1d1d14c583929af068d8fd6eef54d21046e4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:49 +0200 Subject: [PATCH 01/21] microvm: name qboot binary qboot.rom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qboot isn't a bios and shouldnt be named that way. Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Message-id: 20200915120909.20838-2-kraxel@redhat.com --- hw/i386/microvm.c | 4 ++-- pc-bios/meson.build | 2 +- pc-bios/{bios-microvm.bin => qboot.rom} | Bin roms/Makefile | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) rename pc-bios/{bios-microvm.bin => qboot.rom} (100%) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 81d0888930..b1dc7e49c1 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -47,7 +47,7 @@ #include "kvm_i386.h" #include "hw/xen/start_info.h" -#define MICROVM_BIOS_FILENAME "bios-microvm.bin" +#define MICROVM_QBOOT_FILENAME "qboot.rom" static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) { @@ -158,7 +158,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) } if (bios_name == NULL) { - bios_name = MICROVM_BIOS_FILENAME; + bios_name = MICROVM_QBOOT_FILENAME; } x86_bios_rom_init(get_system_memory(), true); } diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 182d5ebb35..9759dba097 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -27,7 +27,7 @@ endif blobs = files( 'bios.bin', 'bios-256k.bin', - 'bios-microvm.bin', + 'qboot.rom', 'sgabios.bin', 'vgabios.bin', 'vgabios-cirrus.bin', diff --git a/pc-bios/bios-microvm.bin b/pc-bios/qboot.rom similarity index 100% rename from pc-bios/bios-microvm.bin rename to pc-bios/qboot.rom diff --git a/roms/Makefile b/roms/Makefile index 3726f06fe7..1395a46695 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -68,7 +68,7 @@ default help: @echo " efi -- update UEFI (edk2) platform firmware" @echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine" @echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine" - @echo " bios-microvm -- update bios-microvm.bin (qboot)" + @echo " qboot -- update qboot" @echo " clean -- delete the files generated by the previous" \ "build targets" @@ -183,9 +183,9 @@ opensbi64-generic: cp opensbi/build/platform/generic/firmware/fw_dynamic.bin ../pc-bios/opensbi-riscv64-generic-fw_dynamic.bin cp opensbi/build/platform/generic/firmware/fw_dynamic.elf ../pc-bios/opensbi-riscv64-generic-fw_dynamic.elf -bios-microvm: +qboot: $(MAKE) -C qboot - cp qboot/bios.bin ../pc-bios/bios-microvm.bin + cp qboot/bios.bin ../pc-bios/qboot.rom npcm7xx_bootrom: $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix) From be404fa560c72f1810c506cb83673a71ee72cd49 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:50 +0200 Subject: [PATCH 02/21] seabios: add microvm config, update build rules Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Message-id: 20200915120909.20838-3-kraxel@redhat.com --- roms/Makefile | 5 ++++- roms/config.seabios-microvm | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 roms/config.seabios-microvm diff --git a/roms/Makefile b/roms/Makefile index 1395a46695..1489d47350 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -72,9 +72,12 @@ default help: @echo " clean -- delete the files generated by the previous" \ "build targets" -bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k +bios: build-seabios-config-seabios-128k \ + build-seabios-config-seabios-256k \ + build-seabios-config-seabios-microvm cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin + cp seabios/builds/seabios-microvm/bios.bin ../pc-bios/bios-microvm.bin vgabios seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) diff --git a/roms/config.seabios-microvm b/roms/config.seabios-microvm new file mode 100644 index 0000000000..a253e2edc6 --- /dev/null +++ b/roms/config.seabios-microvm @@ -0,0 +1,26 @@ +CONFIG_QEMU=y +CONFIG_QEMU_HARDWARE=y +CONFIG_PERMIT_UNALIGNED_PCIROM=y +CONFIG_ROM_SIZE=128 +CONFIG_XEN=n +CONFIG_BOOTSPLASH=n +CONFIG_ATA=n +CONFIG_AHCI=n +CONFIG_SDCARD=n +CONFIG_PVSCSI=n +CONFIG_ESP_SCSI=n +CONFIG_LSI_SCSI=n +CONFIG_MEGASAS=n +CONFIG_MPT_SCSI=n +CONFIG_FLOPPY=n +CONFIG_FLASH_FLOPPY=n +CONFIG_NVME=n +CONFIG_PS2PORT=n +CONFIG_USB=n +CONFIG_LPT=n +CONFIG_RTC_TIMER=n +CONFIG_USE_SMM=n +CONFIG_PMTIMER=n +CONFIG_TCGBIOS=n +CONFIG_HARDWARE_IRQ=n +CONFIG_ACPI_PARSE=y From 38edb514c9c2cba3c3d536504b2cddbfdc915056 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:51 +0200 Subject: [PATCH 03/21] seabios: add bios-microvm.bin binary Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Message-id: 20200915120909.20838-4-kraxel@redhat.com --- pc-bios/bios-microvm.bin | Bin 0 -> 131072 bytes pc-bios/meson.build | 1 + 2 files changed, 1 insertion(+) create mode 100644 pc-bios/bios-microvm.bin diff --git a/pc-bios/bios-microvm.bin b/pc-bios/bios-microvm.bin new file mode 100644 index 0000000000000000000000000000000000000000..eefd32197e8d637be0266e3410551f711728c6d0 GIT binary patch literal 131072 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%0A{S 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 Date: Tue, 15 Sep 2020 14:08:52 +0200 Subject: [PATCH 04/21] acpi: ged: add control regs Add control regs (sleep, reset) for hw-reduced acpi. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-5-kraxel@redhat.com --- hw/acpi/generic_event_device.c | 44 ++++++++++++++++++++++++++ include/hw/acpi/generic_event_device.h | 12 +++++++ 2 files changed, 56 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index b8abdefa1c..491df80a5c 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -20,6 +20,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" +#include "sysemu/runstate.h" static const uint32_t ged_supported_events[] = { ACPI_GED_MEM_HOTPLUG_EVT, @@ -176,6 +177,45 @@ static const MemoryRegionOps ged_evt_ops = { }, }; +static uint64_t ged_regs_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + +static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + bool slp_en; + int slp_typ; + + switch (addr) { + case ACPI_GED_REG_SLEEP_CTL: + slp_typ = (data >> 2) & 0x07; + slp_en = (data >> 5) & 0x01; + if (slp_en && slp_typ == 5) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } + return; + case ACPI_GED_REG_SLEEP_STS: + return; + case ACPI_GED_REG_RESET: + if (data == ACPI_GED_RESET_VALUE) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } + return; + } +} + +static const MemoryRegionOps ged_regs_ops = { + .read = ged_regs_read, + .write = ged_regs_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -332,6 +372,10 @@ static void acpi_ged_initfn(Object *obj) sysbus_init_mmio(sbd, &s->container_memhp); acpi_memory_hotplug_init(&s->container_memhp, OBJECT(dev), &s->memhp_state, 0); + + memory_region_init_io(&ged_st->regs, obj, &ged_regs_ops, ged_st, + TYPE_ACPI_GED "-regs", ACPI_GED_REG_COUNT); + sysbus_init_mmio(sbd, &ged_st->regs); } static void acpi_ged_class_init(ObjectClass *class, void *data) diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index 1be05a3c0f..38aec526f9 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -74,6 +74,17 @@ DECLARE_INSTANCE_CHECKER(AcpiGedState, ACPI_GED, #define ACPI_GED_EVT_SEL_OFFSET 0x0 #define ACPI_GED_EVT_SEL_LEN 0x4 +#define ACPI_GED_REG_SLEEP_CTL 0x00 +#define ACPI_GED_REG_SLEEP_STS 0x01 +#define ACPI_GED_REG_RESET 0x02 +#define ACPI_GED_REG_COUNT 0x03 + +/* ACPI_GED_REG_RESET value for reset*/ +#define ACPI_GED_RESET_VALUE 0x42 + +/* ACPI_GED_REG_SLEEP_CTL.SLP_TYP value for S5 (aka poweroff) */ +#define ACPI_GED_SLP_TYP_S5 0x05 + #define GED_DEVICE "GED" #define AML_GED_EVT_REG "EREG" #define AML_GED_EVT_SEL "ESEL" @@ -89,6 +100,7 @@ DECLARE_INSTANCE_CHECKER(AcpiGedState, ACPI_GED, typedef struct GEDState { MemoryRegion evt; + MemoryRegion regs; uint32_t sel; } GEDState; From 2025e97dc55107e4f0d2b7553d7eb325bc1f437d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:53 +0200 Subject: [PATCH 05/21] acpi: ged: add x86 device variant. Set AcpiDeviceIfClass->madt_cpu, otherwise identical to TYPE_ACPI_GED. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-6-kraxel@redhat.com --- hw/i386/generic_event_device_x86.c | 36 ++++++++++++++++++++++++++ hw/i386/meson.build | 1 + include/hw/acpi/generic_event_device.h | 4 +++ 3 files changed, 41 insertions(+) create mode 100644 hw/i386/generic_event_device_x86.c diff --git a/hw/i386/generic_event_device_x86.c b/hw/i386/generic_event_device_x86.c new file mode 100644 index 0000000000..e26fb02a2e --- /dev/null +++ b/hw/i386/generic_event_device_x86.c @@ -0,0 +1,36 @@ +/* + * x86 variant of the generic event device for hw reduced acpi + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + */ + +#include "qemu/osdep.h" +#include "hw/acpi/generic_event_device.h" +#include "hw/i386/pc.h" + +static void acpi_ged_x86_class_init(ObjectClass *class, void *data) +{ + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class); + + adevc->madt_cpu = pc_madt_cpu_entry; +} + +static const TypeInfo acpi_ged_x86_info = { + .name = TYPE_ACPI_GED_X86, + .parent = TYPE_ACPI_GED, + .class_init = acpi_ged_x86_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { TYPE_ACPI_DEVICE_IF }, + { } + } +}; + +static void acpi_ged_x86_register_types(void) +{ + type_register_static(&acpi_ged_x86_info); +} + +type_init(acpi_ged_x86_register_types) diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 63918fbe22..1a7d1a685d 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -18,6 +18,7 @@ i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) i386_ss.add(when: 'CONFIG_VTD', if_true: files('intel_iommu.c')) i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c')) +i386_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device_x86.c')) i386_ss.add(when: 'CONFIG_PC', if_true: files( 'pc.c', 'pc_sysfw.c', diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index 38aec526f9..241151662b 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -71,6 +71,10 @@ typedef struct AcpiGedState AcpiGedState; DECLARE_INSTANCE_CHECKER(AcpiGedState, ACPI_GED, TYPE_ACPI_GED) +#define TYPE_ACPI_GED_X86 "acpi-ged-x86" +#define ACPI_GED_X86(obj) \ + OBJECT_CHECK(AcpiGedX86State, (obj), TYPE_ACPI_GED_X86) + #define ACPI_GED_EVT_SEL_OFFSET 0x0 #define ACPI_GED_EVT_SEL_LEN 0x4 From 7bf2567c12320731b115af83f4991bbc9203df70 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:54 +0200 Subject: [PATCH 06/21] acpi: move acpi_dsdt_add_power_button() to ged MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow reuse for microvm. Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-7-kraxel@redhat.com --- hw/acpi/generic_event_device.c | 8 ++++++++ hw/arm/virt-acpi-build.c | 8 -------- include/hw/acpi/generic_event_device.h | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 491df80a5c..6df400e1ee 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -142,6 +142,14 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_append(table, dev); } +void acpi_dsdt_add_power_button(Aml *scope) +{ + Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(scope, dev); +} + /* Memory read by the GED _EVT AML dynamic method */ static uint64_t ged_evt_read(void *opaque, hwaddr addr, unsigned size) { diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9efd7a3881..6bff5e3738 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -357,14 +357,6 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap, aml_append(scope, dev); } -static void acpi_dsdt_add_power_button(Aml *scope) -{ - Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); - aml_append(scope, dev); -} - static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) { PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index 241151662b..ac921e9f0c 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -120,5 +120,6 @@ struct AcpiGedState { void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev, uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base); +void acpi_dsdt_add_power_button(Aml *scope); #endif From d4e9d577be587c6f2e41557f7e4ac0b185f0bb1b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:55 +0200 Subject: [PATCH 07/21] microvm: make virtio irq base runtime configurable Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-8-kraxel@redhat.com --- hw/i386/microvm.c | 11 +++++++---- include/hw/i386/microvm.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index b1dc7e49c1..e4501f2cdf 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -121,10 +121,11 @@ static void microvm_devices_init(MicrovmMachineState *mms) kvmclock_create(); + mms->virtio_irq_base = 5; for (i = 0; i < VIRTIO_NUM_TRANSPORTS; i++) { sysbus_create_simple("virtio-mmio", VIRTIO_MMIO_BASE + i * 512, - x86ms->gsi[VIRTIO_IRQ_BASE + i]); + x86ms->gsi[mms->virtio_irq_base + i]); } /* Optional and legacy devices */ @@ -227,7 +228,7 @@ static void microvm_memory_init(MicrovmMachineState *mms) x86ms->ioapic_as = &address_space_memory; } -static gchar *microvm_get_mmio_cmdline(gchar *name) +static gchar *microvm_get_mmio_cmdline(gchar *name, uint32_t virtio_irq_base) { gchar *cmdline; gchar *separator; @@ -247,7 +248,7 @@ static gchar *microvm_get_mmio_cmdline(gchar *name) ret = g_snprintf(cmdline, VIRTIO_CMDLINE_MAXLEN, " virtio_mmio.device=512@0x%lx:%ld", VIRTIO_MMIO_BASE + index * 512, - VIRTIO_IRQ_BASE + index); + virtio_irq_base + index); if (ret < 0 || ret >= VIRTIO_CMDLINE_MAXLEN) { g_free(cmdline); return NULL; @@ -259,6 +260,7 @@ static gchar *microvm_get_mmio_cmdline(gchar *name) static void microvm_fix_kernel_cmdline(MachineState *machine) { X86MachineState *x86ms = X86_MACHINE(machine); + MicrovmMachineState *mms = MICROVM_MACHINE(machine); BusState *bus; BusChild *kid; char *cmdline; @@ -282,7 +284,8 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) BusState *mmio_bus = &mmio_virtio_bus->parent_obj; if (!QTAILQ_EMPTY(&mmio_bus->children)) { - gchar *mmio_cmdline = microvm_get_mmio_cmdline(mmio_bus->name); + gchar *mmio_cmdline = microvm_get_mmio_cmdline + (mmio_bus->name, mms->virtio_irq_base); if (mmio_cmdline) { char *newcmd = g_strjoin(NULL, cmdline, mmio_cmdline, NULL); g_free(mmio_cmdline); diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index 36dbcdd123..87593694eb 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -28,7 +28,6 @@ /* Platform virtio definitions */ #define VIRTIO_MMIO_BASE 0xfeb00000 -#define VIRTIO_IRQ_BASE 5 #define VIRTIO_NUM_TRANSPORTS 8 #define VIRTIO_CMDLINE_MAXLEN 64 @@ -59,6 +58,7 @@ struct MicrovmMachineState { bool auto_kernel_cmdline; /* Machine state */ + uint32_t virtio_irq_base; bool kernel_cmdline_fixed; }; typedef struct MicrovmMachineState MicrovmMachineState; From 8045df14bccaeec39291c3d83fea2f638656b5d0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:56 +0200 Subject: [PATCH 08/21] microvm/acpi: add minimal acpi support $subject says all. Can be controlled using -M microvm,acpi=on/off. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-9-kraxel@redhat.com --- hw/i386/Kconfig | 1 + hw/i386/acpi-microvm.c | 187 ++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-microvm.h | 8 ++ hw/i386/meson.build | 2 +- hw/i386/microvm.c | 40 ++++++++ include/hw/i386/microvm.h | 9 ++ 6 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 hw/i386/acpi-microvm.c create mode 100644 hw/i386/acpi-microvm.h diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 03e347b207..d0bd8b537d 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -103,6 +103,7 @@ config MICROVM select I8259 select MC146818RTC select VIRTIO_MMIO + select ACPI_HW_REDUCED config X86_IOMMU bool diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c new file mode 100644 index 0000000000..06ef33949f --- /dev/null +++ b/hw/i386/acpi-microvm.c @@ -0,0 +1,187 @@ +/* Support for generating ACPI tables and passing them to Guests + * + * Copyright (C) 2008-2010 Kevin O'Connor + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2013 Red Hat Inc + * + * Author: Michael S. Tsirkin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +#include "exec/memory.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/bios-linker-loader.h" +#include "hw/acpi/generic_event_device.h" +#include "hw/acpi/utils.h" +#include "hw/boards.h" +#include "hw/i386/fw_cfg.h" +#include "hw/i386/microvm.h" + +#include "acpi-common.h" +#include "acpi-microvm.h" + +static void +build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, + MicrovmMachineState *mms) +{ + X86MachineState *x86ms = X86_MACHINE(mms); + Aml *dsdt, *sb_scope, *scope, *pkg; + bool ambiguous; + Object *isabus; + + isabus = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); + assert(isabus); + assert(!ambiguous); + + dsdt = init_aml_allocator(); + + /* Reserve space for header */ + acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader)); + + sb_scope = aml_scope("_SB"); + fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg); + isa_build_aml(ISA_BUS(isabus), sb_scope); + build_ged_aml(sb_scope, GED_DEVICE, HOTPLUG_HANDLER(mms->acpi_dev), + GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); + acpi_dsdt_add_power_button(sb_scope); + aml_append(dsdt, sb_scope); + + /* ACPI 5.0: Table 7-209 System State Package */ + scope = aml_scope("\\"); + pkg = aml_package(4); + aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5)); + aml_append(pkg, aml_int(0)); /* ignored */ + aml_append(pkg, aml_int(0)); /* reserved */ + aml_append(pkg, aml_int(0)); /* reserved */ + aml_append(scope, aml_name_decl("_S5", pkg)); + aml_append(dsdt, scope); + + /* copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); + build_header(linker, table_data, + (void *)(table_data->data + table_data->len - dsdt->buf->len), + "DSDT", dsdt->buf->len, 2, NULL, NULL); + free_aml_allocator(); +} + +static void acpi_build_microvm(AcpiBuildTables *tables, + MicrovmMachineState *mms) +{ + MachineState *machine = MACHINE(mms); + GArray *table_offsets; + GArray *tables_blob = tables->table_data; + unsigned dsdt, xsdt; + AcpiFadtData pmfadt = { + /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */ + .rev = 5, + .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) | + (1 << ACPI_FADT_F_RESET_REG_SUP)), + + /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */ + .sleep_ctl = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_CTL, + }, + .sleep_sts = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_STS, + }, + + /* ACPI 5.0: 4.8.3.6 Reset Register */ + .reset_reg = { + .space_id = AML_AS_SYSTEM_MEMORY, + .bit_width = 8, + .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_RESET, + }, + .reset_val = ACPI_GED_RESET_VALUE, + }; + + table_offsets = g_array_new(false, true /* clear */, + sizeof(uint32_t)); + bios_linker_loader_alloc(tables->linker, + ACPI_BUILD_TABLE_FILE, tables_blob, + 64 /* Ensure FACS is aligned */, + false /* high memory */); + + dsdt = tables_blob->len; + build_dsdt_microvm(tables_blob, tables->linker, mms); + + pmfadt.dsdt_tbl_offset = &dsdt; + pmfadt.xdsdt_tbl_offset = &dsdt; + acpi_add_table(table_offsets, tables_blob); + build_fadt(tables_blob, tables->linker, &pmfadt, NULL, NULL); + + acpi_add_table(table_offsets, tables_blob); + acpi_build_madt(tables_blob, tables->linker, X86_MACHINE(machine), + mms->acpi_dev, false); + + xsdt = tables_blob->len; + build_xsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); + + /* RSDP is in FSEG memory, so allocate it separately */ + { + AcpiRsdpData rsdp_data = { + /* ACPI 2.0: 5.2.4.3 RSDP Structure */ + .revision = 2, /* xsdt needs v2 */ + .oem_id = ACPI_BUILD_APPNAME6, + .xsdt_tbl_offset = &xsdt, + .rsdt_tbl_offset = NULL, + }; + build_rsdp(tables->rsdp, tables->linker, &rsdp_data); + } + + /* Cleanup memory that's no longer used. */ + g_array_free(table_offsets, true); +} + +static void acpi_build_no_update(void *build_opaque) +{ + /* nothing, microvm tables don't change at runtime */ +} + +void acpi_setup_microvm(MicrovmMachineState *mms) +{ + X86MachineState *x86ms = X86_MACHINE(mms); + AcpiBuildTables tables; + + assert(x86ms->fw_cfg); + + if (!x86_machine_is_acpi_enabled(x86ms)) { + return; + } + + acpi_build_tables_init(&tables); + acpi_build_microvm(&tables, mms); + + /* Now expose it all to Guest */ + acpi_add_rom_blob(acpi_build_no_update, NULL, + tables.table_data, + ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_MAX_SIZE); + acpi_add_rom_blob(acpi_build_no_update, NULL, + tables.linker->cmd_blob, + "etc/table-loader", 0); + acpi_add_rom_blob(acpi_build_no_update, NULL, + tables.rsdp, + ACPI_BUILD_RSDP_FILE, 0); + + acpi_build_tables_cleanup(&tables, false); +} diff --git a/hw/i386/acpi-microvm.h b/hw/i386/acpi-microvm.h new file mode 100644 index 0000000000..dfe853690e --- /dev/null +++ b/hw/i386/acpi-microvm.h @@ -0,0 +1,8 @@ +#ifndef HW_I386_ACPI_MICROVM_H +#define HW_I386_ACPI_MICROVM_H + +#include "hw/i386/microvm.h" + +void acpi_setup_microvm(MicrovmMachineState *mms); + +#endif diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 1a7d1a685d..e5d109f5c6 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -11,7 +11,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), if_false: files('x86-iommu-stub.c')) i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) -i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c')) +i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('microvm.c', 'acpi-microvm.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index e4501f2cdf..e1b86da8a9 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -26,6 +26,8 @@ #include "sysemu/cpus.h" #include "sysemu/numa.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" +#include "acpi-microvm.h" #include "hw/loader.h" #include "hw/irq.h" @@ -41,6 +43,8 @@ #include "hw/i386/e820_memory_layout.h" #include "hw/i386/fw_cfg.h" #include "hw/virtio/virtio-mmio.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/generic_event_device.h" #include "cpu.h" #include "elf.h" @@ -129,6 +133,17 @@ static void microvm_devices_init(MicrovmMachineState *mms) } /* Optional and legacy devices */ + if (x86_machine_is_acpi_enabled(x86ms)) { + DeviceState *dev = qdev_new(TYPE_ACPI_GED_X86); + qdev_prop_set_uint32(dev, "ged-event", ACPI_GED_PWR_DOWN_EVT); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, GED_MMIO_BASE); + /* sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, GED_MMIO_BASE_MEMHP); */ + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, GED_MMIO_BASE_REGS); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + x86ms->gsi[GED_MMIO_IRQ]); + sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + mms->acpi_dev = ACPI_DEVICE_IF(dev); + } if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { qemu_irq *i8259; @@ -438,6 +453,26 @@ static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value, mms->auto_kernel_cmdline = value; } +static void microvm_machine_done(Notifier *notifier, void *data) +{ + MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, + machine_done); + + acpi_setup_microvm(mms); +} + +static void microvm_powerdown_req(Notifier *notifier, void *data) +{ + MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, + powerdown_req); + + if (mms->acpi_dev) { + Object *obj = OBJECT(mms->acpi_dev); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); + adevc->send_event(mms->acpi_dev, ACPI_POWER_DOWN_STATUS); + } +} + static void microvm_machine_initfn(Object *obj) { MicrovmMachineState *mms = MICROVM_MACHINE(obj); @@ -452,6 +487,11 @@ static void microvm_machine_initfn(Object *obj) /* State */ mms->kernel_cmdline_fixed = false; + + mms->machine_done.notify = microvm_machine_done; + qemu_add_machine_init_done_notifier(&mms->machine_done); + mms->powerdown_req.notify = microvm_powerdown_req; + qemu_register_powerdown_notifier(&mms->powerdown_req); } static void microvm_class_init(ObjectClass *oc, void *data) diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index 87593694eb..d75dcf8203 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -24,6 +24,7 @@ #include "hw/boards.h" #include "hw/i386/x86.h" +#include "hw/acpi/acpi_dev_interface.h" #include "qom/object.h" /* Platform virtio definitions */ @@ -31,6 +32,11 @@ #define VIRTIO_NUM_TRANSPORTS 8 #define VIRTIO_CMDLINE_MAXLEN 64 +#define GED_MMIO_BASE 0xfea00000 +#define GED_MMIO_BASE_MEMHP (GED_MMIO_BASE + 0x100) +#define GED_MMIO_BASE_REGS (GED_MMIO_BASE + 0x200) +#define GED_MMIO_IRQ 9 + /* Machine type options */ #define MICROVM_MACHINE_PIT "pit" #define MICROVM_MACHINE_PIC "pic" @@ -60,6 +66,9 @@ struct MicrovmMachineState { /* Machine state */ uint32_t virtio_irq_base; bool kernel_cmdline_fixed; + Notifier machine_done; + Notifier powerdown_req; + AcpiDeviceIf *acpi_dev; }; typedef struct MicrovmMachineState MicrovmMachineState; From 3b98c65f75c016d317f70b8b47c86044d0260d53 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:57 +0200 Subject: [PATCH 09/21] microvm/acpi: add acpi_dsdt_add_virtio() for x86 Makes x86 linux kernel find virtio-mmio devices automatically. Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-10-kraxel@redhat.com --- hw/i386/acpi-microvm.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index 06ef33949f..b9ce3768b2 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "exec/memory.h" @@ -32,10 +33,60 @@ #include "hw/boards.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/microvm.h" +#include "hw/virtio/virtio-mmio.h" #include "acpi-common.h" #include "acpi-microvm.h" +static void acpi_dsdt_add_virtio(Aml *scope, + MicrovmMachineState *mms) +{ + gchar *separator; + long int index; + BusState *bus; + BusChild *kid; + + bus = sysbus_get_default(); + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO); + + if (obj) { + VirtIOMMIOProxy *mmio = VIRTIO_MMIO(obj); + VirtioBusState *mmio_virtio_bus = &mmio->bus; + BusState *mmio_bus = &mmio_virtio_bus->parent_obj; + + if (QTAILQ_EMPTY(&mmio_bus->children)) { + continue; + } + separator = g_strrstr(mmio_bus->name, "."); + if (!separator) { + continue; + } + if (qemu_strtol(separator + 1, NULL, 10, &index) != 0) { + continue; + } + + uint32_t irq = mms->virtio_irq_base + index; + hwaddr base = VIRTIO_MMIO_BASE + index * 512; + hwaddr size = 512; + + Aml *dev = aml_device("VR%02u", (unsigned)index); + aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005"))); + aml_append(dev, aml_name_decl("_UID", aml_int(index))); + aml_append(dev, aml_name_decl("_CCA", aml_int(1))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE)); + aml_append(crs, + aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_EXCLUSIVE, &irq, 1)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + } + } +} + static void build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, MicrovmMachineState *mms) @@ -60,6 +111,7 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, build_ged_aml(sb_scope, GED_DEVICE, HOTPLUG_HANDLER(mms->acpi_dev), GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); acpi_dsdt_add_power_button(sb_scope); + acpi_dsdt_add_virtio(sb_scope, mms); aml_append(dsdt, sb_scope); /* ACPI 5.0: Table 7-209 System State Package */ From 55c4b069973196b1d9089162e31f5bd7d9f17630 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:58 +0200 Subject: [PATCH 10/21] microvm/acpi: use GSI 16-23 for virtio With ACPI enabled and IO-APIC being properly declared in the ACPI tables we can use interrupt lines 16-23 for virtio and avoid shared interrupts. With acpi disabled we continue to use lines 5-12. Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-11-kraxel@redhat.com --- hw/i386/microvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index e1b86da8a9..ca0c9983f1 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -125,7 +125,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) kvmclock_create(); - mms->virtio_irq_base = 5; + mms->virtio_irq_base = x86_machine_is_acpi_enabled(x86ms) ? 16 : 5; for (i = 0; i < VIRTIO_NUM_TRANSPORTS; i++) { sysbus_create_simple("virtio-mmio", VIRTIO_MMIO_BASE + i * 512, From 67eb6a4007fd8f9073020e506453ff5b7c25cb34 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:08:59 +0200 Subject: [PATCH 11/21] microvm/acpi: use seabios with acpi=on With acpi=off continue to use qboot. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Reviewed-by: Sergio Lopez Message-id: 20200915120909.20838-12-kraxel@redhat.com --- hw/i386/microvm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index ca0c9983f1..54510a03f7 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -52,6 +52,7 @@ #include "hw/xen/start_info.h" #define MICROVM_QBOOT_FILENAME "qboot.rom" +#define MICROVM_BIOS_FILENAME "bios-microvm.bin" static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) { @@ -174,7 +175,9 @@ static void microvm_devices_init(MicrovmMachineState *mms) } if (bios_name == NULL) { - bios_name = MICROVM_QBOOT_FILENAME; + bios_name = x86_machine_is_acpi_enabled(x86ms) + ? MICROVM_BIOS_FILENAME + : MICROVM_QBOOT_FILENAME; } x86_bios_rom_init(get_system_memory(), true); } From f6f7e2d88d0b29d8b6e1a12a5f3f9f31faff9846 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:00 +0200 Subject: [PATCH 12/21] microvm/acpi: disable virtio-mmio cmdline hack ... in case we are using ACPI. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Reviewed-by: Sergio Lopez Message-id: 20200915120909.20838-13-kraxel@redhat.com --- hw/i386/microvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 54510a03f7..04209eb38f 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -343,7 +343,8 @@ static void microvm_machine_reset(MachineState *machine) CPUState *cs; X86CPU *cpu; - if (machine->kernel_filename != NULL && + if (!x86_machine_is_acpi_enabled(X86_MACHINE(machine)) && + machine->kernel_filename != NULL && mms->auto_kernel_cmdline && !mms->kernel_cmdline_fixed) { microvm_fix_kernel_cmdline(machine); mms->kernel_cmdline_fixed = true; From 9927a6329a4ce832b4b529903b0a1e1f580cbf0f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:01 +0200 Subject: [PATCH 13/21] x86: constify x86_machine_is_*_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Message-id: 20200915120909.20838-14-kraxel@redhat.com --- hw/i386/x86.c | 4 ++-- include/hw/i386/x86.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index c1954db152..e2a5005f38 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -821,7 +821,7 @@ void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) bios); } -bool x86_machine_is_smm_enabled(X86MachineState *x86ms) +bool x86_machine_is_smm_enabled(const X86MachineState *x86ms) { bool smm_available = false; @@ -863,7 +863,7 @@ static void x86_machine_set_smm(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &x86ms->smm, errp); } -bool x86_machine_is_acpi_enabled(X86MachineState *x86ms) +bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms) { if (x86ms->acpi == ON_OFF_AUTO_OFF) { return false; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 1a188a7dea..25c904638c 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -100,8 +100,8 @@ void x86_load_linux(X86MachineState *x86ms, bool pvh_enabled, bool linuxboot_dma_enabled); -bool x86_machine_is_smm_enabled(X86MachineState *x86ms); -bool x86_machine_is_acpi_enabled(X86MachineState *x86ms); +bool x86_machine_is_smm_enabled(const X86MachineState *x86ms); +bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); /* Global System Interrupts */ From 50aef1318166c2a5551d43fb99c5a517f54fcf3c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:02 +0200 Subject: [PATCH 14/21] x86: move acpi_dev from pc/microvm Both pc and microvm machine types have a acpi_dev field. Move it to the common base type. Signed-off-by: Gerd Hoffmann Reviewed-by: Sergio Lopez Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-15-kraxel@redhat.com --- hw/i386/acpi-build.c | 2 +- hw/i386/acpi-microvm.c | 5 +++-- hw/i386/microvm.c | 10 ++++++---- hw/i386/pc.c | 34 +++++++++++++++++++--------------- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- include/hw/i386/microvm.h | 1 - include/hw/i386/pc.h | 1 - include/hw/i386/x86.h | 1 + 9 files changed, 32 insertions(+), 26 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 7a5a8b3521..0e0535d2e3 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2431,7 +2431,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); acpi_build_madt(tables_blob, tables->linker, x86ms, - ACPI_DEVICE_IF(pcms->acpi_dev), true); + ACPI_DEVICE_IF(x86ms->acpi_dev), true); vmgenid_dev = find_vmgenid_dev(); if (vmgenid_dev) { diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index b9ce3768b2..df39c5d3bd 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -108,7 +108,7 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, sb_scope = aml_scope("_SB"); fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg); isa_build_aml(ISA_BUS(isabus), sb_scope); - build_ged_aml(sb_scope, GED_DEVICE, HOTPLUG_HANDLER(mms->acpi_dev), + build_ged_aml(sb_scope, GED_DEVICE, x86ms->acpi_dev, GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); acpi_dsdt_add_power_button(sb_scope); acpi_dsdt_add_virtio(sb_scope, mms); @@ -136,6 +136,7 @@ static void acpi_build_microvm(AcpiBuildTables *tables, MicrovmMachineState *mms) { MachineState *machine = MACHINE(mms); + X86MachineState *x86ms = X86_MACHINE(mms); GArray *table_offsets; GArray *tables_blob = tables->table_data; unsigned dsdt, xsdt; @@ -183,7 +184,7 @@ static void acpi_build_microvm(AcpiBuildTables *tables, acpi_add_table(table_offsets, tables_blob); acpi_build_madt(tables_blob, tables->linker, X86_MACHINE(machine), - mms->acpi_dev, false); + ACPI_DEVICE_IF(x86ms->acpi_dev), false); xsdt = tables_blob->len; build_xsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 04209eb38f..9df15354ce 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -143,7 +143,7 @@ static void microvm_devices_init(MicrovmMachineState *mms) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, x86ms->gsi[GED_MMIO_IRQ]); sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); - mms->acpi_dev = ACPI_DEVICE_IF(dev); + x86ms->acpi_dev = HOTPLUG_HANDLER(dev); } if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { @@ -469,11 +469,13 @@ static void microvm_powerdown_req(Notifier *notifier, void *data) { MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, powerdown_req); + X86MachineState *x86ms = X86_MACHINE(mms); - if (mms->acpi_dev) { - Object *obj = OBJECT(mms->acpi_dev); + if (x86ms->acpi_dev) { + Object *obj = OBJECT(x86ms->acpi_dev); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); - adevc->send_event(mms->acpi_dev, ACPI_POWER_DOWN_STATUS); + adevc->send_event(ACPI_DEVICE_IF(x86ms->acpi_dev), + ACPI_POWER_DOWN_STATUS); } } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index d11daacc23..a18140421e 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1274,6 +1274,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { const PCMachineState *pcms = PC_MACHINE(hotplug_dev); + const X86MachineState *x86ms = X86_MACHINE(hotplug_dev); const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); @@ -1285,7 +1286,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, * but pcms->acpi_dev is still created. Check !acpi_enabled in * addition to cover this case. */ - if (!pcms->acpi_dev || !x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { + if (!x86ms->acpi_dev || !x86_machine_is_acpi_enabled(x86ms)) { error_setg(errp, "memory hotplug is not enabled: missing acpi device or acpi disabled"); return; @@ -1296,7 +1297,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - hotplug_handler_pre_plug(pcms->acpi_dev, dev, &local_err); + hotplug_handler_pre_plug(x86ms->acpi_dev, dev, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1311,6 +1312,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev, { Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); MachineState *ms = MACHINE(hotplug_dev); bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); @@ -1323,7 +1325,7 @@ static void pc_memory_plug(HotplugHandler *hotplug_dev, nvdimm_plug(ms->nvdimms_state); } - hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort); + hotplug_handler_plug(x86ms->acpi_dev, dev, &error_abort); out: error_propagate(errp, local_err); } @@ -1331,14 +1333,14 @@ out: static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); /* * When -no-acpi is used with Q35 machine type, no ACPI is built, * but pcms->acpi_dev is still created. Check !acpi_enabled in * addition to cover this case. */ - if (!pcms->acpi_dev || !x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) { + if (!x86ms->acpi_dev || !x86_machine_is_acpi_enabled(x86ms)) { error_setg(errp, "memory hotplug is not enabled: missing acpi device or acpi disabled"); return; @@ -1349,7 +1351,7 @@ static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, return; } - hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, + hotplug_handler_unplug_request(x86ms->acpi_dev, dev, errp); } @@ -1357,9 +1359,10 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); Error *local_err = NULL; - hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); if (local_err) { goto out; } @@ -1403,10 +1406,10 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); - X86MachineState *x86ms = X86_MACHINE(pcms); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - if (pcms->acpi_dev) { - hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + if (x86ms->acpi_dev) { + hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); if (local_err) { goto out; } @@ -1432,8 +1435,9 @@ static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, int idx = -1; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - if (!pcms->acpi_dev) { + if (!x86ms->acpi_dev) { error_setg(errp, "CPU hot unplug not supported without ACPI"); return; } @@ -1445,7 +1449,7 @@ static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, return; } - hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, + hotplug_handler_unplug_request(x86ms->acpi_dev, dev, errp); } @@ -1456,9 +1460,9 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); - X86MachineState *x86ms = X86_MACHINE(pcms); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); if (local_err) { goto out; } @@ -1487,7 +1491,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); - X86MachineState *x86ms = X86_MACHINE(pcms); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); unsigned int smp_cores = ms->smp.cores; unsigned int smp_threads = ms->smp.threads; X86CPUTopoInfo topo_info; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 33fa035fb7..6f3e78bb60 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -293,7 +293,7 @@ static void pc_init1(MachineState *machine, object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER, - (Object **)&pcms->acpi_dev, + (Object **)&x86ms->acpi_dev, object_property_allow_set_link, OBJ_PROP_LINK_STRONG); object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 0cb9c18cd4..622d039717 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -240,7 +240,7 @@ static void pc_q35_init(MachineState *machine) object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, TYPE_HOTPLUG_HANDLER, - (Object **)&pcms->acpi_dev, + (Object **)&x86ms->acpi_dev, object_property_allow_set_link, OBJ_PROP_LINK_STRONG); object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index d75dcf8203..be2d95af4d 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -68,7 +68,6 @@ struct MicrovmMachineState { bool kernel_cmdline_fixed; Notifier machine_done; Notifier powerdown_req; - AcpiDeviceIf *acpi_dev; }; typedef struct MicrovmMachineState MicrovmMachineState; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 421a77acc2..c14e14dfe0 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -30,7 +30,6 @@ struct PCMachineState { Notifier machine_done; /* Pointers to devices and objects: */ - HotplugHandler *acpi_dev; PCIBus *bus; I2CBus *smbus; PFlashCFI01 *flash[2]; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 25c904638c..f540e801a8 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -52,6 +52,7 @@ struct X86MachineState { FWCfgState *fw_cfg; qemu_irq *gsi; GMappedFile *initrd_mapped_file; + HotplugHandler *acpi_dev; /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; From 0cca1a918b858b37484c571a9a989e2d06eeccae Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:03 +0200 Subject: [PATCH 15/21] x86: move cpu hotplug from pc to x86 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 Reviewed-by: Sergio Lopez Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-16-kraxel@redhat.com --- hw/i386/pc.c | 281 +----------------------------------------- hw/i386/x86.c | 271 ++++++++++++++++++++++++++++++++++++++++ include/hw/i386/x86.h | 10 ++ 3 files changed, 286 insertions(+), 276 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a18140421e..b55369357e 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 e2a5005f38..c2ea989579 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) { diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index f540e801a8..0eef9fb0c0 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); From e3ab9873d28684858b055ff4e964aa2334bdbd73 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:04 +0200 Subject: [PATCH 16/21] microvm: wire up hotplug The cpu hotplug code handles the initialization of coldplugged cpus too, so it is needed even in case cpu hotplug is not supported. Wire cpu hotplug up for microvm. Without this we get a broken MADT table. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Reviewed-by: Sergio Lopez Message-id: 20200915120909.20838-17-kraxel@redhat.com --- hw/i386/microvm.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 9df15354ce..8de8ca8649 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -320,6 +320,39 @@ static void microvm_fix_kernel_cmdline(MachineState *machine) g_free(cmdline); } +static void microvm_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + x86_cpu_pre_plug(hotplug_dev, dev, errp); +} + +static void microvm_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + x86_cpu_plug(hotplug_dev, dev, errp); +} + +static void microvm_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + error_setg(errp, "unplug not supported by microvm"); +} + +static void microvm_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + error_setg(errp, "unplug not supported by microvm"); +} + +static HotplugHandler *microvm_get_hotplug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + return HOTPLUG_HANDLER(machine); + } + return NULL; +} + static void microvm_machine_state_init(MachineState *machine) { MicrovmMachineState *mms = MICROVM_MACHINE(machine); @@ -503,6 +536,7 @@ static void microvm_machine_initfn(Object *obj) static void microvm_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = microvm_machine_state_init; @@ -524,6 +558,13 @@ static void microvm_class_init(ObjectClass *oc, void *data) /* Machine class handlers */ mc->reset = microvm_machine_reset; + /* hotplug (for cpu coldplug) */ + mc->get_hotplug_handler = microvm_get_hotplug_handler; + hc->pre_plug = microvm_device_pre_plug_cb; + hc->plug = microvm_device_plug_cb; + hc->unplug_request = microvm_device_unplug_request_cb; + hc->unplug = microvm_device_unplug_cb; + object_class_property_add(oc, MICROVM_MACHINE_PIC, "OnOffAuto", microvm_machine_get_pic, microvm_machine_set_pic, @@ -573,6 +614,7 @@ static const TypeInfo microvm_machine_info = { .class_size = sizeof(MicrovmMachineClass), .class_init = microvm_class_init, .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, { } }, }; From 34b36c3beeaa843d2ae73d6367484961e8bd3352 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:05 +0200 Subject: [PATCH 17/21] tests/acpi: allow microvm test data updates. Also add empty test data files. Signed-off-by: Gerd Hoffmann Message-id: 20200915120909.20838-18-kraxel@redhat.com --- tests/data/acpi/microvm/APIC | 0 tests/data/acpi/microvm/DSDT | 0 tests/data/acpi/microvm/FACP | 0 tests/qtest/bios-tables-test-allowed-diff.h | 3 +++ 4 files changed, 3 insertions(+) create mode 100644 tests/data/acpi/microvm/APIC create mode 100644 tests/data/acpi/microvm/DSDT create mode 100644 tests/data/acpi/microvm/FACP diff --git a/tests/data/acpi/microvm/APIC b/tests/data/acpi/microvm/APIC new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/microvm/DSDT b/tests/data/acpi/microvm/DSDT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/microvm/FACP b/tests/data/acpi/microvm/FACP new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..97c3fa621b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,4 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/microvm/APIC", +"tests/data/acpi/microvm/DSDT", +"tests/data/acpi/microvm/FACP", From 3cac3784f7fac7642ccb9676613ba2efe2290f0d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:06 +0200 Subject: [PATCH 18/21] tests/acpi: allow override blkdev microvm needs virtio-blk instead of ide. Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Reviewed-by: Sergio Lopez Message-id: 20200915120909.20838-19-kraxel@redhat.com --- tests/qtest/bios-tables-test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 504b810af5..1779af1cc8 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -72,6 +72,7 @@ typedef struct { const char *variant; const char *uefi_fl1; const char *uefi_fl2; + const char *blkdev; const char *cd; const uint64_t ram_start; const uint64_t scan_len; @@ -666,9 +667,10 @@ static void test_acpi_one(const char *params, test_data *data) args = g_strdup_printf("-machine %s,kernel-irqchip=off %s -accel tcg " "-net none -display none %s " "-drive id=hd0,if=none,file=%s,format=raw " - "-device ide-hd,drive=hd0 ", + "-device %s,drive=hd0 ", data->machine, data->tcg_only ? "" : "-accel kvm", - params ? params : "", disk); + params ? params : "", disk, + data->blkdev ?: "ide-hd"); } data->qts = qtest_init(args); From c06cbf3d79c0160629f79a79b94dcf6eca5601dc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:07 +0200 Subject: [PATCH 19/21] tests/acpi: add microvm test Signed-off-by: Gerd Hoffmann Reviewed-by: Igor Mammedov Message-id: 20200915120909.20838-20-kraxel@redhat.com --- tests/qtest/bios-tables-test.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 1779af1cc8..a9c8d478ae 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1044,6 +1044,20 @@ static void test_acpi_virt_tcg_memhp(void) } +static void test_acpi_microvm_tcg(void) +{ + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = "microvm"; + data.required_struct_types = NULL; /* no smbios */ + data.required_struct_types_len = 0; + data.blkdev = "virtio-blk-device"; + test_acpi_one(" -machine microvm,acpi=on,rtc=off", + &data); + free_test_data(&data); +} + static void test_acpi_virt_tcg_numamem(void) { test_data data = { @@ -1161,6 +1175,7 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat); + qtest_add_func("acpi/microvm", test_acpi_microvm_tcg); } else if (strcmp(arch, "aarch64") == 0) { qtest_add_func("acpi/virt", test_acpi_virt_tcg); qtest_add_func("acpi/virt/numamem", test_acpi_virt_tcg_numamem); From 312354f8c9b64278b8f0aef1567f213a98cf0e80 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 15 Sep 2020 14:09:08 +0200 Subject: [PATCH 20/21] tests/acpi: update expected data files for microvm Also clear tests/qtest/bios-tables-test-allowed-diff.h Signed-off-by: Gerd Hoffmann Message-id: 20200915120909.20838-21-kraxel@redhat.com --- 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 tests/qtest/bios-tables-test-allowed-diff.h | 3 --- 4 files changed, 3 deletions(-) diff --git a/tests/data/acpi/microvm/APIC b/tests/data/acpi/microvm/APIC index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7472c7e830b6c7139720e93dd544d4441556661d 100644 GIT binary patch literal 70 zcmZ<^@N{-#U|?Xp?&R<65v<@85#S6GV`N}p0I@+d1H*%VV48!00Yo$MFfcGM9QX$! LS=jzFFfalDb_vO4WME)Qa`Jcf2v%^42yh08F)}bPfY=~fz+E^#*eO1`NzX0Z*^MLK!_$Q~ z(A788z`($Wfg?UR#F2}eBi=bESdf>C%bihxRe+U|r;;JM$;jE?*AT3p$MFIyBS(Cw zrwb!Q13yHA02i-2;}7N^%nTe#0t_HMK5Xu;F7aSZypGNRo(2Yn#tdMcyzZ_p3=nO6 zT-=>(49tw23=AMm4X#11?hHH(3=95ou|zj=gSgJF!LB|EBC_$WVIiCaAWp&pfyvbg zTucmHOl<+-K~7u^(M^&d2E?)ezW@Vg180a`4AD(aEMY+g<`6j_zaW1D0|QftBo{kJ yytA_-Bg8FIT;gq<42%qX8~!mcF)(zpF)%WUgWV+{5CaMx(|93v7A{r>1_l6fuuTB~ literal 0 HcmV?d00001 diff --git a/tests/data/acpi/microvm/FACP b/tests/data/acpi/microvm/FACP index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0ba5795d622349e1521138e4123544637b4ab9af 100644 GIT binary patch literal 268 zcmZ>BbPnKQWME*ebMklg2v%^42yh08F)}bPfY>0Kftr9tfPsO7fq{u>!9S3(P9TEb Qn1g|V0ct Date: Tue, 15 Sep 2020 14:09:09 +0200 Subject: [PATCH 21/21] microvm: enable ramfb Signed-off-by: Gerd Hoffmann Message-id: 20200915120909.20838-22-kraxel@redhat.com --- hw/i386/microvm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 8de8ca8649..60d3272230 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -39,6 +39,7 @@ #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" #include "hw/char/serial.h" +#include "hw/display/ramfb.h" #include "hw/i386/topology.h" #include "hw/i386/e820_memory_layout.h" #include "hw/i386/fw_cfg.h" @@ -604,6 +605,8 @@ static void microvm_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, MICROVM_MACHINE_AUTO_KERNEL_CMDLINE, "Set off to disable adding virtio-mmio devices to the kernel cmdline"); + + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); } static const TypeInfo microvm_machine_info = {