diff options
40 files changed, 694 insertions, 488 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a7f0acf866..6a197bd358 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2373,11 +2373,18 @@ S: Maintained F: include/sysemu/cryptodev*.h F: backends/cryptodev*.c +Python library +M: John Snow <jsnow@redhat.com> +M: Cleber Rosa <crosa@redhat.com> +R: Eduardo Habkost <ehabkost@redhat.com> +S: Maintained +F: python/ +T: git https://gitlab.com/jsnow/qemu.git python + Python scripts M: Eduardo Habkost <ehabkost@redhat.com> M: Cleber Rosa <crosa@redhat.com> S: Odd fixes -F: python/qemu/*py F: scripts/*.py F: tests/*.py diff --git a/accel/stubs/xen-stub.c b/accel/stubs/xen-stub.c index 7ba0b697f4..7054965c48 100644 --- a/accel/stubs/xen-stub.c +++ b/accel/stubs/xen-stub.c @@ -7,7 +7,7 @@ #include "qemu/osdep.h" #include "sysemu/xen.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-migration.h" bool xen_allowed; diff --git a/hw/acpi/aml-build-stub.c b/hw/acpi/aml-build-stub.c index 58b2e16227..8d8ad1a314 100644 --- a/hw/acpi/aml-build-stub.c +++ b/hw/acpi/aml-build-stub.c @@ -57,6 +57,20 @@ Aml *aml_irq_no_flags(uint8_t irq) return NULL; } +Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro, + AmlLevelAndEdge level_and_edge, + AmlActiveHighAndLow high_and_low, AmlShared shared, + uint32_t *irq_list, uint8_t irq_count) +{ + return NULL; +} + +Aml *aml_memory32_fixed(uint32_t addr, uint32_t size, + AmlReadAndWrite read_and_write) +{ + return NULL; +} + Aml *aml_int(const uint64_t val) { return NULL; diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 32aa15533b..eea059ffef 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -105,6 +105,7 @@ config MICROVM select VIRTIO_MMIO select ACPI_HW_REDUCED select PCI_EXPRESS_GENERIC_BRIDGE + select USB_XHCI_SYSBUS config X86_IOMMU bool diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 45ad2f9533..e3a4bc206c 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2477,7 +2477,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(x86ms->acpi_dev), true); + ACPI_DEVICE_IF(x86ms->acpi_dev)); vmgenid_dev = find_vmgenid_dev(); if (vmgenid_dev) { diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index ab9b00581a..8a76965406 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -72,8 +72,7 @@ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, } void acpi_build_madt(GArray *table_data, BIOSLinker *linker, - X86MachineState *x86ms, AcpiDeviceIf *adev, - bool has_pci) + X86MachineState *x86ms, AcpiDeviceIf *adev) { MachineClass *mc = MACHINE_GET_CLASS(x86ms); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(x86ms)); @@ -113,20 +112,17 @@ void acpi_build_madt(GArray *table_data, BIOSLinker *linker, intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ } - if (has_pci) { - for (i = 1; i < 16; i++) { -#define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) - if (!(ACPI_BUILD_PCI_IRQS & (1 << i))) { - /* No need for a INT source override structure. */ - continue; - } - intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); - intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; - intsrcovr->length = sizeof(*intsrcovr); - intsrcovr->source = i; - intsrcovr->gsi = cpu_to_le32(i); - intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ + for (i = 1; i < 16; i++) { + if (!(x86ms->pci_irq_mask & (1 << i))) { + /* No need for a INT source override structure. */ + continue; } + intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); + intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = i; + intsrcovr->gsi = cpu_to_le32(i); + intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ } if (x2apic_mode) { diff --git a/hw/i386/acpi-common.h b/hw/i386/acpi-common.h index 9cac18dddf..c30e461f18 100644 --- a/hw/i386/acpi-common.h +++ b/hw/i386/acpi-common.h @@ -9,7 +9,6 @@ #define ACPI_BUILD_IOAPIC_ID 0x0 void acpi_build_madt(GArray *table_data, BIOSLinker *linker, - X86MachineState *x86ms, AcpiDeviceIf *adev, - bool has_pci); + X86MachineState *x86ms, AcpiDeviceIf *adev); #endif diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index f16f231195..d34a301b84 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -35,6 +35,7 @@ #include "hw/i386/microvm.h" #include "hw/pci/pci.h" #include "hw/pci/pcie_host.h" +#include "hw/usb/xhci.h" #include "hw/virtio/virtio-mmio.h" #include "acpi-common.h" @@ -89,6 +90,13 @@ static void acpi_dsdt_add_virtio(Aml *scope, } } +static void acpi_dsdt_add_xhci(Aml *scope, MicrovmMachineState *mms) +{ + if (machine_usb(MACHINE(mms))) { + xhci_sysbus_build_aml(scope, MICROVM_XHCI_BASE, MICROVM_XHCI_IRQ); + } +} + static void acpi_dsdt_add_pci(Aml *scope, MicrovmMachineState *mms) { if (mms->pcie != ON_OFF_AUTO_ON) { @@ -123,6 +131,7 @@ build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); acpi_dsdt_add_power_button(sb_scope); acpi_dsdt_add_virtio(sb_scope, mms); + acpi_dsdt_add_xhci(sb_scope, mms); acpi_dsdt_add_pci(sb_scope, mms); aml_append(dsdt, sb_scope); @@ -196,7 +205,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), - ACPI_DEVICE_IF(x86ms->acpi_dev), false); + ACPI_DEVICE_IF(x86ms->acpi_dev)); 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 68a7f424ac..5428448b70 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -47,6 +47,7 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/generic_event_device.h" #include "hw/pci-host/gpex.h" +#include "hw/usb/xhci.h" #include "cpu.h" #include "elf.h" @@ -197,6 +198,18 @@ static void microvm_devices_init(MicrovmMachineState *mms) x86ms->acpi_dev = HOTPLUG_HANDLER(dev); } + if (x86_machine_is_acpi_enabled(x86ms) && machine_usb(MACHINE(mms))) { + DeviceState *dev = qdev_new(TYPE_XHCI_SYSBUS); + qdev_prop_set_uint32(dev, "intrs", 1); + qdev_prop_set_uint32(dev, "slots", XHCI_MAXSLOTS); + qdev_prop_set_uint32(dev, "p2", 8); + qdev_prop_set_uint32(dev, "p3", 8); + sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, MICROVM_XHCI_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, + x86ms->gsi[MICROVM_XHCI_IRQ]); + } + if (x86_machine_is_acpi_enabled(x86ms) && mms->pcie == ON_OFF_AUTO_ON) { /* use topmost 25% of the address space available */ hwaddr phys_size = (hwaddr)1 << X86_CPU(first_cpu)->phys_bits; @@ -210,6 +223,12 @@ static void microvm_devices_init(MicrovmMachineState *mms) mms->gpex.ecam.size = PCIE_ECAM_SIZE; mms->gpex.irq = PCIE_IRQ_BASE; create_gpex(mms); + x86ms->pci_irq_mask = ((1 << (PCIE_IRQ_BASE + 0)) | + (1 << (PCIE_IRQ_BASE + 1)) | + (1 << (PCIE_IRQ_BASE + 2)) | + (1 << (PCIE_IRQ_BASE + 3))); + } else { + x86ms->pci_irq_mask = 0; } if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 3137a20085..5944fc44ed 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -1178,6 +1178,7 @@ static void x86_machine_initfn(Object *obj) x86ms->smm = ON_OFF_AUTO_AUTO; x86ms->acpi = ON_OFF_AUTO_AUTO; x86ms->smp_dies = 1; + x86ms->pci_irq_mask = ACPI_BUILD_PCI_IRQS; } static void x86_machine_class_init(ObjectClass *oc, void *data) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index f3ababf33b..9519c33c09 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -24,7 +24,7 @@ #include "hw/xen/xen-bus.h" #include "hw/xen/xen-x86.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-migration.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/range.h" diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 4dd2ba9630..a674ce4c54 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -32,8 +32,6 @@ config USB_EHCI_SYSBUS config USB_XHCI bool - default y if PCI_DEVICES - depends on PCI select USB config USB_XHCI_PCI @@ -50,8 +48,8 @@ config USB_XHCI_NEC config USB_XHCI_SYSBUS bool - default y if USB_XHCI - select USB + default y + select USB_XHCI config USB_MUSB bool diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 2efa6fa0f8..5707b2cabd 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -34,8 +34,8 @@ static Property nec_xhci_properties[] = { xhci.flags, XHCI_FLAG_SS_FIRST, true), DEFINE_PROP_BIT("force-pcie-endcap", XHCIPciState, xhci.flags, XHCI_FLAG_FORCE_PCIE_ENDCAP, false), - DEFINE_PROP_UINT32("intrs", XHCIPciState, xhci.numintrs, MAXINTRS), - DEFINE_PROP_UINT32("slots", XHCIPciState, xhci.numslots, MAXSLOTS), + DEFINE_PROP_UINT32("intrs", XHCIPciState, xhci.numintrs, XHCI_MAXINTRS), + DEFINE_PROP_UINT32("slots", XHCIPciState, xhci.numslots, XHCI_MAXSLOTS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index a6d746e1da..b78fcd2bb2 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -240,8 +240,8 @@ static void qemu_xhci_instance_init(Object *obj) s->msi = ON_OFF_AUTO_OFF; s->msix = ON_OFF_AUTO_AUTO; - xhci->numintrs = MAXINTRS; - xhci->numslots = MAXSLOTS; + xhci->numintrs = XHCI_MAXINTRS; + xhci->numslots = XHCI_MAXSLOTS; xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST); } diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index 852ca5103b..29185d2261 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -13,6 +13,7 @@ #include "trace.h" #include "qapi/error.h" #include "hcd-xhci-sysbus.h" +#include "hw/acpi/aml-build.h" #include "hw/irq.h" static void xhci_sysbus_intr_raise(XHCIState *xhci, int n, bool level) @@ -68,9 +69,23 @@ static void xhci_sysbus_instance_init(Object *obj) s->xhci.intr_raise = xhci_sysbus_intr_raise; } +void xhci_sysbus_build_aml(Aml *scope, uint32_t mmio, unsigned int irq) +{ + Aml *dev = aml_device("XHCI"); + Aml *crs = aml_resource_template(); + + aml_append(crs, aml_memory32_fixed(mmio, XHCI_LEN_REGS, 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("_HID", aml_eisaid("PNP0D10"))); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + static Property xhci_sysbus_props[] = { - DEFINE_PROP_UINT32("intrs", XHCISysbusState, xhci.numintrs, MAXINTRS), - DEFINE_PROP_UINT32("slots", XHCISysbusState, xhci.numslots, MAXSLOTS), + DEFINE_PROP_UINT32("intrs", XHCISysbusState, xhci.numintrs, XHCI_MAXINTRS), + DEFINE_PROP_UINT32("slots", XHCISysbusState, xhci.numslots, XHCI_MAXSLOTS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/usb/hcd-xhci-sysbus.h b/hw/usb/hcd-xhci-sysbus.h index a308753ceb..fdfcbbee3b 100644 --- a/hw/usb/hcd-xhci-sysbus.h +++ b/hw/usb/hcd-xhci-sysbus.h @@ -15,7 +15,6 @@ #include "hcd-xhci.h" #include "hw/sysbus.h" -#define TYPE_XHCI_SYSBUS "sysbus-xhci" #define XHCI_SYSBUS(obj) \ OBJECT_CHECK(XHCISysbusState, (obj), TYPE_XHCI_SYSBUS) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 5e8bed9ef9..79ce5c4be6 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -46,15 +46,13 @@ #define TRANSFER_LIMIT 256 #define LEN_CAP 0x40 -#define LEN_OPER (0x400 + 0x10 * MAXPORTS) -#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20) -#define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20) +#define LEN_OPER (0x400 + 0x10 * XHCI_MAXPORTS) +#define LEN_RUNTIME ((XHCI_MAXINTRS + 1) * 0x20) +#define LEN_DOORBELL ((XHCI_MAXSLOTS + 1) * 0x20) #define OFF_OPER LEN_CAP #define OFF_RUNTIME 0x1000 #define OFF_DOORBELL 0x2000 -/* must be power of 2 */ -#define LEN_REGS 0x4000 #if (OFF_OPER + LEN_OPER) > OFF_RUNTIME #error Increase OFF_RUNTIME @@ -62,8 +60,8 @@ #if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL #error Increase OFF_DOORBELL #endif -#if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS -# error Increase LEN_REGS +#if (OFF_DOORBELL + LEN_DOORBELL) > XHCI_LEN_REGS +# error Increase XHCI_LEN_REGS #endif /* bit definitions */ @@ -3276,11 +3274,11 @@ static void usb_xhci_init(XHCIState *xhci) xhci->usbsts = USBSTS_HCH; - if (xhci->numports_2 > MAXPORTS_2) { - xhci->numports_2 = MAXPORTS_2; + if (xhci->numports_2 > XHCI_MAXPORTS_2) { + xhci->numports_2 = XHCI_MAXPORTS_2; } - if (xhci->numports_3 > MAXPORTS_3) { - xhci->numports_3 = MAXPORTS_3; + if (xhci->numports_3 > XHCI_MAXPORTS_3) { + xhci->numports_3 = XHCI_MAXPORTS_3; } usbports = MAX(xhci->numports_2, xhci->numports_3); xhci->numports = xhci->numports_2 + xhci->numports_3; @@ -3302,7 +3300,7 @@ static void usb_xhci_init(XHCIState *xhci) USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; - assert(i < MAXPORTS); + assert(i < XHCI_MAXPORTS); snprintf(port->name, sizeof(port->name), "usb2 port #%d", i+1); speedmask |= port->speedmask; } @@ -3316,7 +3314,7 @@ static void usb_xhci_init(XHCIState *xhci) } port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_SUPER; - assert(i < MAXPORTS); + assert(i < XHCI_MAXPORTS); snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1); speedmask |= port->speedmask; } @@ -3331,8 +3329,8 @@ static void usb_xhci_realize(DeviceState *dev, Error **errp) XHCIState *xhci = XHCI(dev); - if (xhci->numintrs > MAXINTRS) { - xhci->numintrs = MAXINTRS; + if (xhci->numintrs > XHCI_MAXINTRS) { + xhci->numintrs = XHCI_MAXINTRS; } while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */ xhci->numintrs++; @@ -3340,8 +3338,8 @@ static void usb_xhci_realize(DeviceState *dev, Error **errp) if (xhci->numintrs < 1) { xhci->numintrs = 1; } - if (xhci->numslots > MAXSLOTS) { - xhci->numslots = MAXSLOTS; + if (xhci->numslots > XHCI_MAXSLOTS) { + xhci->numslots = XHCI_MAXSLOTS; } if (xhci->numslots < 1) { xhci->numslots = 1; @@ -3355,7 +3353,7 @@ static void usb_xhci_realize(DeviceState *dev, Error **errp) usb_xhci_init(xhci); xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci); - memory_region_init(&xhci->mem, OBJECT(dev), "xhci", LEN_REGS); + memory_region_init(&xhci->mem, OBJECT(dev), "xhci", XHCI_LEN_REGS); memory_region_init_io(&xhci->mem_cap, OBJECT(dev), &xhci_cap_ops, xhci, "capabilities", LEN_CAP); memory_region_init_io(&xhci->mem_oper, OBJECT(dev), &xhci_oper_ops, xhci, diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index f859a17e73..ccf50ae28b 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -24,23 +24,13 @@ #include "qom/object.h" #include "hw/usb.h" +#include "hw/usb/xhci.h" #include "sysemu/dma.h" -#define TYPE_XHCI "base-xhci" -#define TYPE_NEC_XHCI "nec-usb-xhci" -#define TYPE_QEMU_XHCI "qemu-xhci" - OBJECT_DECLARE_SIMPLE_TYPE(XHCIState, XHCI) -#define MAXPORTS_2 15 -#define MAXPORTS_3 15 - -#define MAXPORTS (MAXPORTS_2 + MAXPORTS_3) -#define MAXSLOTS 64 -#define MAXINTRS 16 - /* Very pessimistic, let's hope it's enough for all cases */ -#define EV_QUEUE (((3 * 24) + 16) * MAXSLOTS) +#define EV_QUEUE (((3 * 24) + 16) * XHCI_MAXSLOTS) typedef struct XHCIStreamContext XHCIStreamContext; typedef struct XHCIEPContext XHCIEPContext; @@ -217,15 +207,15 @@ typedef struct XHCIState { uint32_t dcbaap_high; uint32_t config; - USBPort uports[MAX_CONST(MAXPORTS_2, MAXPORTS_3)]; - XHCIPort ports[MAXPORTS]; - XHCISlot slots[MAXSLOTS]; + USBPort uports[MAX_CONST(XHCI_MAXPORTS_2, XHCI_MAXPORTS_3)]; + XHCIPort ports[XHCI_MAXPORTS]; + XHCISlot slots[XHCI_MAXSLOTS]; uint32_t numports; /* Runtime Registers */ int64_t mfindex_start; QEMUTimer *mfwrap_timer; - XHCIInterrupter intr[MAXINTRS]; + XHCIInterrupter intr[XHCI_MAXINTRS]; XHCIRing cmd_ring; diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index 91b064575d..0fc2160077 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -41,7 +41,7 @@ * 7 | parallel | * 8 | rtc | rtc (rtc=on) * 9 | acpi | acpi (ged) - * 10 | pci lnk | + * 10 | pci lnk | xhci (usb=on) * 11 | pci lnk | * 12 | ps2 | pcie * 13 | fpu | pcie @@ -60,6 +60,9 @@ #define GED_MMIO_BASE_REGS (GED_MMIO_BASE + 0x200) #define GED_MMIO_IRQ 9 +#define MICROVM_XHCI_BASE 0xfe900000 +#define MICROVM_XHCI_IRQ 10 + #define PCIE_MMIO_BASE 0xc0000000 #define PCIE_MMIO_SIZE 0x20000000 #define PCIE_ECAM_BASE 0xe0000000 diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index d5dcf7a07f..bfa9cb2a25 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -58,6 +58,7 @@ struct X86MachineState { /* CPU and apic information: */ bool apic_xrupt_override; + unsigned pci_irq_mask; unsigned apic_id_limit; uint16_t boot_cpus; unsigned smp_dies; @@ -114,6 +115,7 @@ bool x86_machine_is_acpi_enabled(const X86MachineState *x86ms); /* Global System Interrupts */ #define GSI_NUM_PINS IOAPIC_NUM_PINS +#define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) typedef struct GSIState { qemu_irq i8259_irq[ISA_NUM_IRQS]; diff --git a/include/hw/usb/xhci.h b/include/hw/usb/xhci.h new file mode 100644 index 0000000000..5c90e1373e --- /dev/null +++ b/include/hw/usb/xhci.h @@ -0,0 +1,21 @@ +#ifndef HW_USB_XHCI_H +#define HW_USB_XHCI_H + +#define TYPE_XHCI "base-xhci" +#define TYPE_NEC_XHCI "nec-usb-xhci" +#define TYPE_QEMU_XHCI "qemu-xhci" +#define TYPE_XHCI_SYSBUS "sysbus-xhci" + +#define XHCI_MAXPORTS_2 15 +#define XHCI_MAXPORTS_3 15 + +#define XHCI_MAXPORTS (XHCI_MAXPORTS_2 + XHCI_MAXPORTS_3) +#define XHCI_MAXSLOTS 64 +#define XHCI_MAXINTRS 16 + +/* must be power of 2 */ +#define XHCI_LEN_REGS 0x4000 + +void xhci_sysbus_build_aml(Aml *scope, uint32_t mmio, unsigned int irq); + +#endif diff --git a/migration/savevm.c b/migration/savevm.c index d2e141f7b1..ff33e210eb 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -42,7 +42,6 @@ #include "postcopy-ram.h" #include "qapi/error.h" #include "qapi/qapi-commands-migration.h" -#include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "sysemu/cpus.h" diff --git a/python/mypy.ini b/python/mypy.ini new file mode 100644 index 0000000000..1a581c5f1e --- /dev/null +++ b/python/mypy.ini @@ -0,0 +1,4 @@ +[mypy] +strict = True +python_version = 3.6 +warn_unused_configs = True diff --git a/python/qemu/.isort.cfg b/python/qemu/.isort.cfg new file mode 100644 index 0000000000..6d0fd6cc0d --- /dev/null +++ b/python/qemu/.isort.cfg @@ -0,0 +1,7 @@ +[settings] +force_grid_wrap=4 +force_sort_within_sections=True +include_trailing_comma=True +line_length=72 +lines_after_imports=2 +multi_line_output=3 \ No newline at end of file diff --git a/python/qemu/accel.py b/python/qemu/accel.py index 7fabe62920..297933df2a 100644 --- a/python/qemu/accel.py +++ b/python/qemu/accel.py @@ -17,6 +17,8 @@ accelerators. import logging import os import subprocess +from typing import List, Optional + LOG = logging.getLogger(__name__) @@ -29,7 +31,7 @@ ADDITIONAL_ARCHES = { } -def list_accel(qemu_bin): +def list_accel(qemu_bin: str) -> List[str]: """ List accelerators enabled in the QEMU binary. @@ -49,7 +51,8 @@ def list_accel(qemu_bin): return [acc.strip() for acc in out.splitlines()[1:]] -def kvm_available(target_arch=None, qemu_bin=None): +def kvm_available(target_arch: Optional[str] = None, + qemu_bin: Optional[str] = None) -> bool: """ Check if KVM is available using the following heuristic: - Kernel module is present in the host; @@ -72,7 +75,7 @@ def kvm_available(target_arch=None, qemu_bin=None): return True -def tcg_available(qemu_bin): +def tcg_available(qemu_bin: str) -> bool: """ Check if TCG is available. diff --git a/python/qemu/console_socket.py b/python/qemu/console_socket.py index 70869fbbdc..f060d79e06 100644 --- a/python/qemu/console_socket.py +++ b/python/qemu/console_socket.py @@ -13,10 +13,11 @@ which can drain a socket and optionally dump the bytes to file. # the COPYING file in the top-level directory. # +from collections import deque import socket import threading -from collections import deque import time +from typing import Deque, Optional class ConsoleSocket(socket.socket): @@ -29,22 +30,22 @@ class ConsoleSocket(socket.socket): Optionally a file path can be passed in and we will also dump the characters to this file for debugging purposes. """ - def __init__(self, address, file=None, drain=False): - self._recv_timeout_sec = 300 + def __init__(self, address: str, file: Optional[str] = None, + drain: bool = False): + self._recv_timeout_sec = 300.0 self._sleep_time = 0.5 - self._buffer = deque() + self._buffer: Deque[int] = deque() socket.socket.__init__(self, socket.AF_UNIX, socket.SOCK_STREAM) self.connect(address) self._logfile = None if file: - self._logfile = open(file, "w") + self._logfile = open(file, "bw") self._open = True + self._drain_thread = None if drain: self._drain_thread = self._thread_start() - else: - self._drain_thread = None - def _drain_fn(self): + def _drain_fn(self) -> None: """Drains the socket and runs while the socket is open.""" while self._open: try: @@ -55,7 +56,7 @@ class ConsoleSocket(socket.socket): # self._open is set to False. time.sleep(self._sleep_time) - def _thread_start(self): + def _thread_start(self) -> threading.Thread: """Kick off a thread to drain the socket.""" # Configure socket to not block and timeout. # This allows our drain thread to not block @@ -67,7 +68,7 @@ class ConsoleSocket(socket.socket): drain_thread.start() return drain_thread - def close(self): + def close(self) -> None: """Close the base object and wait for the thread to terminate""" if self._open: self._open = False @@ -79,51 +80,42 @@ class ConsoleSocket(socket.socket): self._logfile.close() self._logfile = None - def _drain_socket(self): + def _drain_socket(self) -> None: """process arriving characters into in memory _buffer""" data = socket.socket.recv(self, 1) - # latin1 is needed since there are some chars - # we are receiving that cannot be encoded to utf-8 - # such as 0xe2, 0x80, 0xA6. - string = data.decode("latin1") if self._logfile: - self._logfile.write("{}".format(string)) + self._logfile.write(data) self._logfile.flush() - for c in string: - self._buffer.extend(c) + self._buffer.extend(data) - def recv(self, bufsize=1): + def recv(self, bufsize: int = 1, flags: int = 0) -> bytes: """Return chars from in memory buffer. Maintains the same API as socket.socket.recv. """ if self._drain_thread is None: # Not buffering the socket, pass thru to socket. - return socket.socket.recv(self, bufsize) + return socket.socket.recv(self, bufsize, flags) + assert not flags, "Cannot pass flags to recv() in drained mode" start_time = time.time() while len(self._buffer) < bufsize: time.sleep(self._sleep_time) elapsed_sec = time.time() - start_time if elapsed_sec > self._recv_timeout_sec: raise socket.timeout - chars = ''.join([self._buffer.popleft() for i in range(bufsize)]) - # We choose to use latin1 to remain consistent with - # handle_read() and give back the same data as the user would - # receive if they were reading directly from the - # socket w/o our intervention. - return chars.encode("latin1") + return bytes((self._buffer.popleft() for i in range(bufsize))) - def setblocking(self, value): + def setblocking(self, value: bool) -> None: """When not draining we pass thru to the socket, since when draining we control socket blocking. """ if self._drain_thread is None: socket.socket.setblocking(self, value) - def settimeout(self, seconds): + def settimeout(self, value: Optional[float]) -> None: """When not draining we pass thru to the socket, since when draining we control the timeout. """ - if seconds is not None: - self._recv_timeout_sec = seconds + if value is not None: + self._recv_timeout_sec = value if self._drain_thread is None: - socket.socket.settimeout(self, seconds) + socket.socket.settimeout(self, value) diff --git a/python/qemu/machine.py b/python/qemu/machine.py index 82f3731fc3..6420f01bed 100644 --- a/python/qemu/machine.py +++ b/python/qemu/machine.py @@ -18,17 +18,29 @@ which provides facilities for managing the lifetime of a QEMU VM. # import errno +from itertools import chain import logging import os -import subprocess import shutil import signal +import socket +import subprocess import tempfile -from typing import Optional, Type from types import TracebackType -from . import console_socket +from typing import ( + Any, + BinaryIO, + Dict, + List, + Optional, + Sequence, + Tuple, + Type, +) + +from . import console_socket, qmp +from .qmp import QMPMessage, QMPReturnValue, SocketAddrT -from . import qmp LOG = logging.getLogger(__name__) @@ -57,7 +69,7 @@ class AbnormalShutdown(QEMUMachineError): class QEMUMachine: """ - A QEMU VM + A QEMU VM. Use this object as a context manager to ensure the QEMU process terminates:: @@ -67,10 +79,17 @@ class QEMUMachine: # vm is guaranteed to be shut down here """ - def __init__(self, binary, args=None, wrapper=None, name=None, - test_dir="/var/tmp", monitor_address=None, - socket_scm_helper=None, sock_dir=None, - drain_console=False, console_log=None): + def __init__(self, + binary: str, + args: Sequence[str] = (), + wrapper: Sequence[str] = (), + name: Optional[str] = None, + test_dir: str = "/var/tmp", + monitor_address: Optional[SocketAddrT] = None, + socket_scm_helper: Optional[str] = None, + sock_dir: Optional[str] = None, + drain_console: bool = False, + console_log: Optional[str] = None): ''' Initialize a QEMUMachine @@ -82,45 +101,30 @@ class QEMUMachine: @param monitor_address: address for QMP monitor @param socket_scm_helper: helper program, required for send_fd_scm() @param sock_dir: where to create socket (overrides test_dir for sock) - @param console_log: (optional) path to console log file @param drain_console: (optional) True to drain console socket to buffer + @param console_log: (optional) path to console log file @note: Qemu process is not started until launch() is used. ''' - if args is None: - args = [] - if wrapper is None: - wrapper = [] - if name is None: - name = "qemu-%d" % os.getpid() - if sock_dir is None: - sock_dir = test_dir - self._name = name - self._monitor_address = monitor_address - self._vm_monitor = None - self._qemu_log_path = None - self._qemu_log_file = None - self._popen = None + # Direct user configuration + self._binary = binary - self._args = list(args) # Force copy args in case we modify them + self._args = list(args) self._wrapper = wrapper - self._events = [] - self._iolog = None - self._socket_scm_helper = socket_scm_helper - self._qmp_set = True # Enable QMP monitor by default. - self._qmp = None - self._qemu_full_args = None + + self._name = name or "qemu-%d" % os.getpid() self._test_dir = test_dir - self._temp_dir = None - self._sock_dir = sock_dir - self._launched = False - self._machine = None - self._console_index = 0 - self._console_set = False - self._console_device_type = None - self._console_address = None - self._console_socket = None - self._remove_files = [] - self._user_killed = False + self._sock_dir = sock_dir or self._test_dir + self._socket_scm_helper = socket_scm_helper + + if monitor_address is not None: + self._monitor_address = monitor_address + self._remove_monitor_sockfile = False + else: + self._monitor_address = os.path.join( + self._sock_dir, f"{self._name}-monitor.sock" + ) + self._remove_monitor_sockfile = True + self._console_log_path = console_log if self._console_log_path: # In order to log the console, buffering needs to be enabled. @@ -128,7 +132,29 @@ class QEMUMachine: else: self._drain_console = drain_console - def __enter__(self): + # Runstate + self._qemu_log_path: Optional[str] = None + self._qemu_log_file: Optional[BinaryIO] = None + self._popen: Optional['subprocess.Popen[bytes]'] = None + self._events: List[QMPMessage] = [] + self._iolog: Optional[str] = None + self._qmp_set = True # Enable QMP monitor by default. + self._qmp_connection: Optional[qmp.QEMUMonitorProtocol] = None + self._qemu_full_args: Tuple[str, ...] = () + self._temp_dir: Optional[str] = None + self._launched = False + self._machine: Optional[str] = None + self._console_index = 0 + self._console_set = False + self._console_device_type: Optional[str] = None + self._console_address = os.path.join( + self._sock_dir, f"{self._name}-console.sock" + ) + self._console_socket: Optional[socket.socket] = None + self._remove_files: List[str] = [] + self._user_killed = False + + def __enter__(self) -> 'QEMUMachine': return self def __exit__(self, @@ -137,14 +163,15 @@ class QEMUMachine: exc_tb: Optional[TracebackType]) -> None: self.shutdown() - def add_monitor_null(self): + def add_monitor_null(self) -> None: """ This can be used to add an unused monitor instance. """ self._args.append('-monitor') self._args.append('null') - def add_fd(self, fd, fdset, opaque, opts=''): + def add_fd(self, fd: int, fdset: int, + opaque: str, opts: str = '') -> 'QEMUMachine': """ Pass a file descriptor to the VM """ @@ -163,7 +190,8 @@ class QEMUMachine: self._args.append(','.join(options)) return self - def send_fd_scm(self, fd=None, file_path=None): + def send_fd_scm(self, fd: Optional[int] = None, + file_path: Optional[str] = None) -> int: """ Send an fd or file_path to socket_scm_helper. @@ -207,7 +235,7 @@ class QEMUMachine: return proc.returncode @staticmethod - def _remove_if_exists(path): + def _remove_if_exists(path: str) -> None: """ Remove file object at path if it exists """ @@ -218,46 +246,52 @@ class QEMUMachine: return raise - def is_running(self): + def is_running(self) -> bool: """Returns true if the VM is running.""" return self._popen is not None and self._popen.poll() is None - def exitcode(self): + @property + def _subp(self) -> 'subprocess.Popen[bytes]': + if self._popen is None: + raise QEMUMachineError('Subprocess pipe not present') + return self._popen + + def exitcode(self) -> Optional[int]: """Returns the exit code if possible, or None.""" if self._popen is None: return None return self._popen.poll() - def get_pid(self): + def get_pid(self) -> Optional[int]: """Returns the PID of the running process, or None.""" if not self.is_running(): return None - return self._popen.pid + return self._subp.pid - def _load_io_log(self): + def _load_io_log(self) -> None: if self._qemu_log_path is not None: with open(self._qemu_log_path, "r") as iolog: self._iolog = iolog.read() - def _base_args(self): + @property + def _base_args(self) -> List[str]: args = ['-display', 'none', '-vga', 'none'] + if self._qmp_set: if isinstance(self._monitor_address, tuple): - moncdev = "socket,id=mon,host=%s,port=%s" % ( - self._monitor_address[0], - self._monitor_address[1]) + moncdev = "socket,id=mon,host={},port={}".format( + *self._monitor_address + ) else: - moncdev = 'socket,id=mon,path=%s' % self._vm_monitor + moncdev = f"socket,id=mon,path={self._monitor_address}" args.extend(['-chardev', moncdev, '-mon', 'chardev=mon,mode=control']) + if self._machine is not None: args.extend(['-machine', self._machine]) for _ in range(self._console_index): args.extend(['-serial', 'null']) if self._console_set: - self._console_address = os.path.join(self._sock_dir, - self._name + "-console.sock") - self._remove_files.append(self._console_address) chardev = ('socket,id=console,path=%s,server,nowait' % self._console_address) args.extend(['-chardev', chardev]) @@ -268,26 +302,29 @@ class QEMUMachine: args.extend(['-device', device]) return args - def _pre_launch(self): + def _pre_launch(self) -> None: self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") self._qemu_log_file = open(self._qemu_log_path, 'wb') + if self._console_set: + self._remove_files.append(self._console_address) + if self._qmp_set: - if self._monitor_address is not None: - self._vm_monitor = self._monitor_address - else: - self._vm_monitor = os.path.join(self._sock_dir, - self._name + "-monitor.sock") - self._remove_files.append(self._vm_monitor) - self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, server=True, - nickname=self._name) - - def _post_launch(self): - if self._qmp: + if self._remove_monitor_sockfile: + assert isinstance(self._monitor_address, str) + self._remove_files.append(self._monitor_address) + self._qmp_connection = qmp.QEMUMonitorProtocol( + self._monitor_address, + server=True, + nickname=self._name + ) + + def _post_launch(self) -> None: + if self._qmp_connection: self._qmp.accept() - def _post_shutdown(self): + def _post_shutdown(self) -> None: """ Called to cleanup the VM instance after the process has exited. May also be called after a failed launch. @@ -295,9 +332,9 @@ class QEMUMachine: # Comprehensive reset for the failed launch case: self._early_cleanup() - if self._qmp: + if self._qmp_connection: self._qmp.close() - self._qmp = None + self._qmp_connection = None self._load_io_log() @@ -327,7 +364,7 @@ class QEMUMachine: self._user_killed = False self._launched = False - def launch(self): + def launch(self) -> None: """ Launch the VM and make sure we cleanup and expose the command line/output in case of exception @@ -337,7 +374,7 @@ class QEMUMachine: raise QEMUMachineError('VM already launched') self._iolog = None - self._qemu_full_args = None + self._qemu_full_args = () try: self._launch() self._launched = True @@ -351,14 +388,18 @@ class QEMUMachine: LOG.debug('Output: %r', self._iolog) raise - def _launch(self): + def _launch(self) -> None: """ Launch the VM and establish a QMP connection """ devnull = open(os.path.devnull, 'rb') self._pre_launch() - self._qemu_full_args = (self._wrapper + [self._binary] + - self._base_args() + self._args) + self._qemu_full_args = tuple( + chain(self._wrapper, + [self._binary], + self._base_args, + self._args) + ) LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) self._popen = subprocess.Popen(self._qemu_full_args, stdin=devnull, @@ -390,8 +431,8 @@ class QEMUMachine: waiting for the QEMU process to terminate. """ self._early_cleanup() - self._popen.kill() - self._popen.wait(timeout=60) + self._subp.kill() + self._subp.wait(timeout=60) def _soft_shutdown(self, timeout: Optional[int], has_quit: bool = False) -> None: @@ -409,13 +450,13 @@ class QEMUMachine: """ self._early_cleanup() - if self._qmp is not None: + if self._qmp_connection: if not has_quit: # Might raise ConnectionReset self._qmp.cmd('quit') # May raise subprocess.TimeoutExpired - self._popen.wait(timeout=timeout) + self._subp.wait(timeout=timeout) def _do_shutdown(self, timeout: Optional[int], has_quit: bool = False) -> None: @@ -466,7 +507,7 @@ class QEMUMachine: finally: self._post_shutdown() - def kill(self): + def kill(self) -> None: """ Terminate the VM forcefully, wait for it to exit, and perform cleanup. """ @@ -481,7 +522,7 @@ class QEMUMachine: """ self.shutdown(has_quit=True, timeout=timeout) - def set_qmp_monitor(self, enabled=True): + def set_qmp_monitor(self, enabled: bool = True) -> None: """ Set the QMP monitor. @@ -490,39 +531,45 @@ class QEMUMachine: line. Default is True. @note: call this function before launch(). """ - if enabled: - self._qmp_set = True - else: - self._qmp_set = False - self._qmp = None + self._qmp_set = enabled - def qmp(self, cmd, conv_keys=True, **args): - """ - Invoke a QMP command and return the response dict - """ + @property + def _qmp(self) -> qmp.QEMUMonitorProtocol: + if self._qmp_connection is None: + raise QEMUMachineError("Attempt to access QMP with no connection") + return self._qmp_connection + + @classmethod + def _qmp_args(cls, _conv_keys: bool = True, **args: Any) -> Dict[str, Any]: qmp_args = dict() for key, value in args.items(): - if conv_keys: + if _conv_keys: qmp_args[key.replace('_', '-')] = value else: qmp_args[key] = value + return qmp_args + def qmp(self, cmd: str, + conv_keys: bool = True, + **args: Any) -> QMPMessage: + """ + Invoke a QMP command and return the response dict + """ + qmp_args = self._qmp_args(conv_keys, **args) return self._qmp.cmd(cmd, args=qmp_args) - def command(self, cmd, conv_keys=True, **args): + def command(self, cmd: str, + conv_keys: bool = True, + **args: Any) -> QMPReturnValue: """ Invoke a QMP command. On success return the response dict. On failure raise an exception. """ - reply = self.qmp(cmd, conv_keys, **args) - if reply is None: - raise qmp.QMPError("Monitor is closed") - if "error" in reply: - raise qmp.QMPResponseError(reply) - return reply["return"] + qmp_args = self._qmp_args(conv_keys, **args) + return self._qmp.command(cmd, **qmp_args) - def get_qmp_event(self, wait=False): + def get_qmp_event(self, wait: bool = False) -> Optional[QMPMessage]: """ Poll for one queued QMP events and return it """ @@ -530,7 +577,7 @@ class QEMUMachine: return self._events.pop(0) return self._qmp.pull_event(wait=wait) - def get_qmp_events(self, wait=False): + def get_qmp_events(self, wait: bool = False) -> List[QMPMessage]: """ Poll for queued QMP events and return a list of dicts """ @@ -541,7 +588,7 @@ class QEMUMachine: return events @staticmethod - def event_match(event, match=None): + def event_match(event: Any, match: Optional[Any]) -> bool: """ Check if an event matches optional match criteria. @@ -571,9 +618,11 @@ class QEMUMachine: return True except TypeError: # either match or event wasn't iterable (not a dict) - return match == event + return bool(match == event) - def event_wait(self, name, timeout=60.0, match=None): + def event_wait(self, name: str, + timeout: float = 60.0, + match: Optional[QMPMessage] = None) -> Optional[QMPMessage]: """ event_wait waits for and returns a named event from QMP with a timeout. @@ -583,22 +632,33 @@ class QEMUMachine: """ return self.events_wait([(name, match)], timeout) - def events_wait(self, events, timeout=60.0): + def events_wait(self, + events: Sequence[Tuple[str, Any]], + timeout: float = 60.0) -> Optional[QMPMessage]: """ - events_wait waits for and returns a named event - from QMP with a timeout. + events_wait waits for and returns a single named event from QMP. + In the case of multiple qualifying events, this function returns the + first one. - events: a sequence of (name, match_criteria) tuples. - The match criteria are optional and may be None. - See event_match for details. - timeout: QEMUMonitorProtocol.pull_event timeout parameter. + :param events: A sequence of (name, match_criteria) tuples. + The match criteria are optional and may be None. + See event_match for details. + :param timeout: Optional timeout, in seconds. + See QEMUMonitorProtocol.pull_event. + + :raise QMPTimeoutError: If timeout was non-zero and no matching events + were found. + :return: A QMP event matching the filter criteria. + If timeout was 0 and no event matched, None. """ - def _match(event): + def _match(event: QMPMessage) -> bool: for name, match in events: if event['event'] == name and self.event_match(event, match): return True return False + event: Optional[QMPMessage] + # Search cached events for event in self._events: if _match(event): @@ -608,26 +668,30 @@ class QEMUMachine: # Poll for new events while True: event = self._qmp.pull_event(wait=timeout) + if event is None: + # NB: None is only returned when timeout is false-ish. + # Timeouts raise QMPTimeoutError instead! + break if _match(event): return event self._events.append(event) return None - def get_log(self): + def get_log(self) -> Optional[str]: """ After self.shutdown or failed qemu execution, this returns the output of the qemu process. """ return self._iolog - def add_args(self, *args): + def add_args(self, *args: str) -> None: """ Adds to the list of extra arguments to be given to the QEMU binary """ self._args.extend(args) - def set_machine(self, machine_type): + def set_machine(self, machine_type: str) -> None: """ Sets the machine type @@ -636,7 +700,9 @@ class QEMUMachine: """ self._machine = machine_type - def set_console(self, device_type=None, console_index=0): + def set_console(self, + device_type: Optional[str] = None, + console_index: int = 0) -> None: """ Sets the device type for a console device @@ -667,7 +733,7 @@ class QEMUMachine: self._console_index = console_index @property - def console_socket(self): + def console_socket(self) -> socket.socket: """ Returns a socket connected to the console """ diff --git a/python/qemu/qmp.py b/python/qemu/qmp.py index 7935dababb..2cd4d43036 100644 --- a/python/qemu/qmp.py +++ b/python/qemu/qmp.py @@ -7,21 +7,22 @@ # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. -import json import errno -import socket +import json import logging +import socket +from types import TracebackType from typing import ( Any, - cast, Dict, + List, Optional, TextIO, - Type, Tuple, + Type, Union, + cast, ) -from types import TracebackType # QMPMessage is a QMP Message of any kind. @@ -90,7 +91,9 @@ class QEMUMonitorProtocol: #: Logger object for debugging messages logger = logging.getLogger('QMP') - def __init__(self, address, server=False, nickname=None): + def __init__(self, address: SocketAddrT, + server: bool = False, + nickname: Optional[str] = None): """ Create a QEMUMonitorProtocol class. @@ -102,7 +105,7 @@ class QEMUMonitorProtocol: @note No connection is established, this is done by the connect() or accept() methods """ - self.__events = [] + self.__events: List[QMPMessage] = [] self.__address = address self.__sock = self.__get_sock() self.__sockfile: Optional[TextIO] = None @@ -114,14 +117,14 @@ class QEMUMonitorProtocol: self.__sock.bind(self.__address) self.__sock.listen(1) - def __get_sock(self): + def __get_sock(self) -> socket.socket: if isinstance(self.__address, tuple): family = socket.AF_INET else: family = socket.AF_UNIX return socket.socket(family, socket.SOCK_STREAM) - def __negotiate_capabilities(self): + def __negotiate_capabilities(self) -> QMPMessage: greeting = self.__json_read() if greeting is None or "QMP" not in greeting: raise QMPConnectError @@ -131,7 +134,7 @@ class QEMUMonitorProtocol: return greeting raise QMPCapabilitiesError - def __json_read(self, only_event=False): + def __json_read(self, only_event: bool = False) -> Optional[QMPMessage]: assert self.__sockfile is not None while True: data = self.__sockfile.readline() @@ -148,7 +151,7 @@ class QEMUMonitorProtocol: continue return resp - def __get_events(self, wait=False): + def __get_events(self, wait: Union[bool, float] = False) -> None: """ Check for new events in the stream and cache them in __events. @@ -161,15 +164,19 @@ class QEMUMonitorProtocol: retrieved or if some other error occurred. """ + # Current timeout and blocking status + current_timeout = self.__sock.gettimeout() + # Check for new events regardless and pull them into the cache: - self.__sock.setblocking(False) + self.__sock.settimeout(0) # i.e. setblocking(False) try: self.__json_read() except OSError as err: - if err.errno == errno.EAGAIN: - # No data available - pass - self.__sock.setblocking(True) + # EAGAIN: No data available; not critical + if err.errno != errno.EAGAIN: + raise + finally: + self.__sock.settimeout(current_timeout) # Wait for new events, if needed. # if wait is 0.0, this means "no wait" and is also implicitly false. @@ -178,15 +185,18 @@ class QEMUMonitorProtocol: self.__sock.settimeout(wait) try: ret = self.__json_read(only_event=True) - except socket.timeout: - raise QMPTimeoutError("Timeout waiting for event") - except: - raise QMPConnectError("Error while reading from socket") + except socket.timeout as err: + raise QMPTimeoutError("Timeout waiting for event") from err + except Exception as err: + msg = "Error while reading from socket" + raise QMPConnectError(msg) from err + finally: + self.__sock.settimeout(current_timeout) + if ret is None: raise QMPConnectError("Error while reading from socket") - self.__sock.settimeout(None) - def __enter__(self): + def __enter__(self) -> 'QEMUMonitorProtocol': # Implement context manager enter function. return self @@ -199,7 +209,7 @@ class QEMUMonitorProtocol: # Implement context manager exit function. self.close() - def connect(self, negotiate=True): + def connect(self, negotiate: bool = True) -> Optional[QMPMessage]: """ Connect to the QMP Monitor and perform capabilities negotiation. @@ -214,7 +224,7 @@ class QEMUMonitorProtocol: return self.__negotiate_capabilities() return None - def accept(self, timeout=15.0): + def accept(self, timeout: Optional[float] = 15.0) -> QMPMessage: """ Await connection from QMP Monitor and perform capabilities negotiation. @@ -250,7 +260,9 @@ class QEMUMonitorProtocol: self.logger.debug("<<< %s", resp) return resp - def cmd(self, name, args=None, cmd_id=None): + def cmd(self, name: str, + args: Optional[Dict[str, Any]] = None, + cmd_id: Optional[Any] = None) -> QMPMessage: """ Build a QMP command and send it to the QMP Monitor. @@ -258,14 +270,14 @@ class QEMUMonitorProtocol: @param args: command arguments (dict) @param cmd_id: command id (dict, list, string or int) """ - qmp_cmd = {'execute': name} + qmp_cmd: QMPMessage = {'execute': name} if args: qmp_cmd['arguments'] = args if cmd_id: qmp_cmd['id'] = cmd_id return self.cmd_obj(qmp_cmd) - def command(self, cmd, **kwds): + def command(self, cmd: str, **kwds: Any) -> QMPReturnValue: """ Build and send a QMP command to the monitor, report errors if any """ @@ -278,7 +290,8 @@ class QEMUMonitorProtocol: ) return cast(QMPReturnValue, ret['return']) - def pull_event(self, wait=False): + def pull_event(self, + wait: Union[bool, float] = False) -> Optional[QMPMessage]: """ Pulls a single event. @@ -298,7 +311,7 @@ class QEMUMonitorProtocol: return self.__events.pop(0) return None - def get_events(self, wait=False): + def get_events(self, wait: bool = False) -> List[QMPMessage]: """ Get a list of available QMP events. @@ -315,13 +328,13 @@ class QEMUMonitorProtocol: self.__get_events(wait) return self.__events - def clear_events(self): + def clear_events(self) -> None: """ Clear current list of pending events. """ self.__events = [] - def close(self): + def close(self) -> None: """ Close the socket and socket file. """ @@ -330,16 +343,22 @@ class QEMUMonitorProtocol: if self.__sockfile: self.__sockfile.close() - def settimeout(self, timeout): + def settimeout(self, timeout: Optional[float]) -> None: """ Set the socket timeout. - @param timeout (float): timeout in seconds, or None. + @param timeout (float): timeout in seconds (non-zero), or None. @note This is a wrap around socket.settimeout + + @raise ValueError: if timeout was set to 0. """ + if timeout == 0: + msg = "timeout cannot be 0; this engages non-blocking mode." + msg += " Use 'None' instead to disable timeouts." + raise ValueError(msg) self.__sock.settimeout(timeout) - def get_sock_fd(self): + def get_sock_fd(self) -> int: """ Get the socket file descriptor. @@ -347,7 +366,7 @@ class QEMUMonitorProtocol: """ return self.__sock.fileno() - def is_scm_available(self): + def is_scm_available(self) -> bool: """ Check if the socket allows for SCM_RIGHTS. diff --git a/python/qemu/qtest.py b/python/qemu/qtest.py index 888c8bd2f6..39a0cf62fe 100644 --- a/python/qemu/qtest.py +++ b/python/qemu/qtest.py @@ -17,11 +17,17 @@ subclass of QEMUMachine, respectively. # Based on qmp.py. # -import socket import os -from typing import Optional, TextIO +import socket +from typing import ( + List, + Optional, + Sequence, + TextIO, +) from .machine import QEMUMachine +from .qmp import SocketAddrT class QEMUQtestProtocol: @@ -38,7 +44,8 @@ class QEMUQtestProtocol: No conection is estabalished by __init__(), this is done by the connect() or accept() methods. """ - def __init__(self, address, server=False): + def __init__(self, address: SocketAddrT, + server: bool = False): self._address = address self._sock = self._get_sock() self._sockfile: Optional[TextIO] = None @@ -46,14 +53,14 @@ class QEMUQtestProtocol: self._sock.bind(self._address) self._sock.listen(1) - def _get_sock(self): + def _get_sock(self) -> socket.socket: if isinstance(self._address, tuple): family = socket.AF_INET else: family = socket.AF_UNIX return socket.socket(family, socket.SOCK_STREAM) - def connect(self): + def connect(self) -> None: """ Connect to the qtest socket. @@ -62,7 +69,7 @@ class QEMUQtestProtocol: self._sock.connect(self._address) self._sockfile = self._sock.makefile(mode='r') - def accept(self): + def accept(self) -> None: """ Await connection from QEMU. @@ -71,7 +78,7 @@ class QEMUQtestProtocol: self._sock, _ = self._sock.accept() self._sockfile = self._sock.makefile(mode='r') - def cmd(self, qtest_cmd): + def cmd(self, qtest_cmd: str) -> str: """ Send a qtest command on the wire. @@ -82,14 +89,16 @@ class QEMUQtestProtocol: resp = self._sockfile.readline() return resp - def close(self): - """Close this socket.""" + def close(self) -> None: + """ + Close this socket. + """ self._sock.close() if self._sockfile: self._sockfile.close() self._sockfile = None - def settimeout(self, timeout): + def settimeout(self, timeout: Optional[float]) -> None: """Set a timeout, in seconds.""" self._sock.settimeout(timeout) @@ -99,8 +108,13 @@ class QEMUQtestMachine(QEMUMachine): A QEMU VM, with a qtest socket available. """ - def __init__(self, binary, args=None, name=None, test_dir="/var/tmp", - socket_scm_helper=None, sock_dir=None): + def __init__(self, + binary: str, + args: Sequence[str] = (), + name: Optional[str] = None, + test_dir: str = "/var/tmp", + socket_scm_helper: Optional[str] = None, + sock_dir: Optional[str] = None): if name is None: name = "qemu-%d" % os.getpid() if sock_dir is None: @@ -108,16 +122,19 @@ class QEMUQtestMachine(QEMUMachine): super().__init__(binary, args, name=name, test_dir=test_dir, socket_scm_helper=socket_scm_helper, sock_dir=sock_dir) - self._qtest = None + self._qtest: Optional[QEMUQtestProtocol] = None self._qtest_path = os.path.join(sock_dir, name + "-qtest.sock") - def _base_args(self): - args = super()._base_args() - args.extend(['-qtest', 'unix:path=' + self._qtest_path, - '-accel', 'qtest']) + @property + def _base_args(self) -> List[str]: + args = super()._base_args + args.extend([ + '-qtest', f"unix:path={self._qtest_path}", + '-accel', 'qtest' + ]) return args - def _pre_launch(self): + def _pre_launch(self) -> None: super()._pre_launch() self._qtest = QEMUQtestProtocol(self._qtest_path, server=True) @@ -126,7 +143,7 @@ class QEMUQtestMachine(QEMUMachine): super()._post_launch() self._qtest.accept() - def _post_shutdown(self): + def _post_shutdown(self) -> None: super()._post_shutdown() self._remove_if_exists(self._qtest_path) diff --git a/qapi/machine.json b/qapi/machine.json index 756dacb06f..7c9a263778 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -453,6 +453,63 @@ { 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' } ## +# @system_reset: +# +# Performs a hard reset of a guest. +# +# Since: 0.14.0 +# +# Example: +# +# -> { "execute": "system_reset" } +# <- { "return": {} } +# +## +{ 'command': 'system_reset' } + +## +# @system_powerdown: +# +# Requests that a guest perform a powerdown operation. +# +# Since: 0.14.0 +# +# Notes: A guest may or may not respond to this command. This command +# returning does not indicate that a guest has accepted the request or +# that it has shut down. Many guests will respond to this command by +# prompting the user in some way. +# Example: +# +# -> { "execute": "system_powerdown" } +# <- { "return": {} } +# +## +{ 'command': 'system_powerdown' } + +## +# @system_wakeup: +# +# Wake up guest from suspend. If the guest has wake-up from suspend +# support enabled (wakeup-suspend-support flag from +# query-current-machine), wake-up guest from suspend if the guest is +# in SUSPENDED state. Return an error otherwise. +# +# Since: 1.1 +# +# Returns: nothing. +# +# Note: prior to 4.0, this command does nothing in case the guest +# isn't suspended. +# +# Example: +# +# -> { "execute": "system_wakeup" } +# <- { "return": {} } +# +## +{ 'command': 'system_wakeup' } + +## # @LostTickPolicy: # # Policy for handling lost ticks in timer devices. Ticks end up getting @@ -485,6 +542,56 @@ 'data': ['discard', 'delay', 'slew' ] } ## +# @inject-nmi: +# +# Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or all CPUs (ppc64). +# The command fails when the guest doesn't support injecting. +# +# Returns: If successful, nothing +# +# Since: 0.14.0 +# +# Note: prior to 2.1, this command was only supported for x86 and s390 VMs +# +# Example: +# +# -> { "execute": "inject-nmi" } +# <- { "return": {} } +# +## +{ 'command': 'inject-nmi' } + +## +# @KvmInfo: +# +# Information about support for KVM acceleration +# +# @enabled: true if KVM acceleration is active +# +# @present: true if KVM acceleration is built into this executable +# +# Since: 0.14.0 +## +{ 'struct': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} } + +## +# @query-kvm: +# +# Returns information about KVM acceleration +# +# Returns: @KvmInfo +# +# Since: 0.14.0 +# +# Example: +# +# -> { "execute": "query-kvm" } +# <- { "return": { "enabled": true, "present": true } } +# +## +{ 'command': 'query-kvm', 'returns': 'KvmInfo' } + +## # @NumaOptionsType: # # @node: NUMA nodes configuration @@ -811,6 +918,67 @@ 'data': [ 'default', 'preferred', 'bind', 'interleave' ] } ## +# @memsave: +# +# Save a portion of guest memory to a file. +# +# @val: the virtual address of the guest to start from +# +# @size: the size of memory region to save +# +# @filename: the file to save the memory to as binary data +# +# @cpu-index: the index of the virtual CPU to use for translating the +# virtual address (defaults to CPU 0) +# +# Returns: Nothing on success +# +# Since: 0.14.0 +# +# Notes: Errors were not reliably returned until 1.1 +# +# Example: +# +# -> { "execute": "memsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/virtual-mem-dump" } } +# <- { "return": {} } +# +## +{ 'command': 'memsave', + 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} } + +## +# @pmemsave: +# +# Save a portion of guest physical memory to a file. +# +# @val: the physical address of the guest to start from +# +# @size: the size of memory region to save +# +# @filename: the file to save the memory to as binary data +# +# Returns: Nothing on success +# +# Since: 0.14.0 +# +# Notes: Errors were not reliably returned until 1.1 +# +# Example: +# +# -> { "execute": "pmemsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/physical-mem-dump" } } +# <- { "return": {} } +# +## +{ 'command': 'pmemsave', + 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } + +## # @Memdev: # # Information about memory backend diff --git a/qapi/migration.json b/qapi/migration.json index 974021a5c8..a5da513c9e 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1552,6 +1552,47 @@ 'data': {'filename': 'str', '*live':'bool' } } ## +# @xen-set-global-dirty-log: +# +# Enable or disable the global dirty log mode. +# +# @enable: true to enable, false to disable. +# +# Returns: nothing +# +# Since: 1.3 +# +# Example: +# +# -> { "execute": "xen-set-global-dirty-log", +# "arguments": { "enable": true } } +# <- { "return": {} } +# +## +{ 'command': 'xen-set-global-dirty-log', 'data': { 'enable': 'bool' } } + +## +# @xen-load-devices-state: +# +# Load the state of all devices from file. The RAM and the block devices +# of the VM are not loaded by this command. +# +# @filename: the file to load the state of the devices from as binary +# data. See xen-save-devices-state.txt for a description of the binary +# format. +# +# Since: 2.7 +# +# Example: +# +# -> { "execute": "xen-load-devices-state", +# "arguments": { "filename": "/tmp/resume" } } +# <- { "return": {} } +# +## +{ 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} } + +## # @xen-set-replication: # # Enable or disable replication. diff --git a/qapi/misc.json b/qapi/misc.json index 7d1e2e9aae..40df513856 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -69,36 +69,6 @@ { 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true } ## -# @KvmInfo: -# -# Information about support for KVM acceleration -# -# @enabled: true if KVM acceleration is active -# -# @present: true if KVM acceleration is built into this executable -# -# Since: 0.14.0 -## -{ 'struct': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} } - -## -# @query-kvm: -# -# Returns information about KVM acceleration -# -# Returns: @KvmInfo -# -# Since: 0.14.0 -# -# Example: -# -# -> { "execute": "query-kvm" } -# <- { "return": { "enabled": true, "present": true } } -# -## -{ 'command': 'query-kvm', 'returns': 'KvmInfo' } - -## # @IOThreadInfo: # # Information about an iothread @@ -178,101 +148,6 @@ { 'command': 'stop' } ## -# @system_reset: -# -# Performs a hard reset of a guest. -# -# Since: 0.14.0 -# -# Example: -# -# -> { "execute": "system_reset" } -# <- { "return": {} } -# -## -{ 'command': 'system_reset' } - -## -# @system_powerdown: -# -# Requests that a guest perform a powerdown operation. -# -# Since: 0.14.0 -# -# Notes: A guest may or may not respond to this command. This command -# returning does not indicate that a guest has accepted the request or -# that it has shut down. Many guests will respond to this command by -# prompting the user in some way. -# Example: -# -# -> { "execute": "system_powerdown" } -# <- { "return": {} } -# -## -{ 'command': 'system_powerdown' } - -## -# @memsave: -# -# Save a portion of guest memory to a file. -# -# @val: the virtual address of the guest to start from -# -# @size: the size of memory region to save -# -# @filename: the file to save the memory to as binary data -# -# @cpu-index: the index of the virtual CPU to use for translating the -# virtual address (defaults to CPU 0) -# -# Returns: Nothing on success -# -# Since: 0.14.0 -# -# Notes: Errors were not reliably returned until 1.1 -# -# Example: -# -# -> { "execute": "memsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/virtual-mem-dump" } } -# <- { "return": {} } -# -## -{ 'command': 'memsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} } - -## -# @pmemsave: -# -# Save a portion of guest physical memory to a file. -# -# @val: the physical address of the guest to start from -# -# @size: the size of memory region to save -# -# @filename: the file to save the memory to as binary data -# -# Returns: Nothing on success -# -# Since: 0.14.0 -# -# Notes: Errors were not reliably returned until 1.1 -# -# Example: -# -# -> { "execute": "pmemsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/physical-mem-dump" } } -# <- { "return": {} } -# -## -{ 'command': 'pmemsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } - -## # @cont: # # Resume guest VCPU execution. @@ -319,49 +194,6 @@ { 'command': 'x-exit-preconfig', 'allow-preconfig': true } ## -# @system_wakeup: -# -# Wake up guest from suspend. If the guest has wake-up from suspend -# support enabled (wakeup-suspend-support flag from -# query-current-machine), wake-up guest from suspend if the guest is -# in SUSPENDED state. Return an error otherwise. -# -# Since: 1.1 -# -# Returns: nothing. -# -# Note: prior to 4.0, this command does nothing in case the guest -# isn't suspended. -# -# Example: -# -# -> { "execute": "system_wakeup" } -# <- { "return": {} } -# -## -{ 'command': 'system_wakeup' } - -## -# @inject-nmi: -# -# Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or all CPUs (ppc64). -# The command fails when the guest doesn't support injecting. -# -# Returns: If successful, nothing -# -# Since: 0.14.0 -# -# Note: prior to 2.1, this command was only supported for x86 and s390 VMs -# -# Example: -# -# -> { "execute": "inject-nmi" } -# <- { "return": {} } -# -## -{ 'command': 'inject-nmi' } - -## # @human-monitor-command: # # Execute a command on the human monitor and return the output. @@ -456,26 +288,6 @@ 'features': [ 'deprecated' ] } ## -# @xen-set-global-dirty-log: -# -# Enable or disable the global dirty log mode. -# -# @enable: true to enable, false to disable. -# -# Returns: nothing -# -# Since: 1.3 -# -# Example: -# -# -> { "execute": "xen-set-global-dirty-log", -# "arguments": { "enable": true } } -# <- { "return": {} } -# -## -{ 'command': 'xen-set-global-dirty-log', 'data': { 'enable': 'bool' } } - -## # @getfd: # # Receive a file descriptor via SCM rights and assign it a name @@ -756,24 +568,3 @@ 'data': { '*option': 'str' }, 'returns': ['CommandLineOptionInfo'], 'allow-preconfig': true } - -## -# @xen-load-devices-state: -# -# Load the state of all devices from file. The RAM and the block devices -# of the VM are not loaded by this command. -# -# @filename: the file to load the state of the devices from as binary -# data. See xen-save-devices-state.txt for a description of the binary -# format. -# -# Since: 2.7 -# -# Example: -# -# -> { "execute": "xen-load-devices-state", -# "arguments": { "filename": "/tmp/resume" } } -# <- { "return": {} } -# -## -{ 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} } diff --git a/softmmu/cpus.c b/softmmu/cpus.c index 47cceddd80..e46ac68ad0 100644 --- a/softmmu/cpus.c +++ b/softmmu/cpus.c @@ -26,6 +26,7 @@ #include "qemu-common.h" #include "monitor/monitor.h" #include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-events-run-state.h" #include "qapi/qmp/qerror.h" diff --git a/tests/data/acpi/microvm/APIC.pcie b/tests/data/acpi/microvm/APIC.pcie new file mode 100644 index 0000000000..6c51081b50 --- /dev/null +++ b/tests/data/acpi/microvm/APIC.pcie Binary files differdiff --git a/tests/data/acpi/microvm/DSDT.rtc b/tests/data/acpi/microvm/DSDT.rtc new file mode 100644 index 0000000000..5960d6929a --- /dev/null +++ b/tests/data/acpi/microvm/DSDT.rtc Binary files differdiff --git a/tests/data/acpi/microvm/DSDT.usb b/tests/data/acpi/microvm/DSDT.usb new file mode 100644 index 0000000000..14423381ea --- /dev/null +++ b/tests/data/acpi/microvm/DSDT.usb Binary files differdiff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index f212cec446..63d2ace93c 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -605,7 +605,7 @@ class VM(qtest.QEMUQtestMachine): def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage: cmd = 'human-monitor-command' - kwargs = {'command-line': command_line} + kwargs: Dict[str, Any] = {'command-line': command_line} if use_log: return self.qmp_log(cmd, **kwargs) else: diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 5647624492..3830a40d10 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1110,6 +1110,28 @@ static void test_acpi_microvm_tcg(void) free_test_data(&data); } +static void test_acpi_microvm_usb_tcg(void) +{ + test_data data; + + test_acpi_microvm_prepare(&data); + data.variant = ".usb"; + test_acpi_one(" -machine microvm,acpi=on,usb=on,rtc=off", + &data); + free_test_data(&data); +} + +static void test_acpi_microvm_rtc_tcg(void) +{ + test_data data; + + test_acpi_microvm_prepare(&data); + data.variant = ".rtc"; + test_acpi_one(" -machine microvm,acpi=on,rtc=on", + &data); + free_test_data(&data); +} + static void test_acpi_microvm_pcie_tcg(void) { test_data data; @@ -1246,6 +1268,8 @@ int main(int argc, char *argv[]) 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); + qtest_add_func("acpi/microvm/usb", test_acpi_microvm_usb_tcg); + qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); if (strcmp(arch, "x86_64") == 0) { qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); } diff --git a/ui/cocoa.m b/ui/cocoa.m index 0910b4a716..f32adc3074 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -35,6 +35,7 @@ #include "sysemu/cpu-throttle.h" #include "qapi/error.h" #include "qapi/qapi-commands-block.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "sysemu/blockdev.h" #include "qemu-version.h" diff --git a/ui/gtk.c b/ui/gtk.c index b11594d817..a752aa22be 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -33,6 +33,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" #include "qemu/cutils.h" |