Merge remote-tracking branch 'remotes/philmd-gitlab/tags/sd-next-20201021' into staging
SD/MMC patches
Fix two heap-overflow reported by Alexander Bulekov while fuzzing:
- https://bugs.launchpad.net/qemu/+bug/1892960
- https://bugs.launchpad.net/qemu/+bug/1895310
CI jobs results:
. https://cirrus-ci.com/build/6399328187056128
. https://gitlab.com/philmd/qemu/-/pipelines/205701966
. https://travis-ci.org/github/philmd/qemu/builds/737708930
# gpg: Signature made Wed 21 Oct 2020 18:33:08 BST
# gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE
* remotes/philmd-gitlab/tags/sd-next-20201021:
hw/sd/sdcard: Assert if accessing an illegal group
hw/sd/sdcard: Do not attempt to erase out of range addresses
hw/sd/sdcard: Reset both start/end addresses on error
hw/sd/sdcard: Do not use legal address '0' for INVALID_ADDRESS
hw/sd/sdcard: Introduce the INVALID_ADDRESS definition
hw/sd/sdcard: Add trace event for ERASE command (CMD38)
hw/sd/sdhci: Yield if interrupt delivered during multiple transfer
hw/sd/sdhci: Let sdhci_update_irq() return if IRQ was delivered
hw/sd/sdhci: Resume pending DMA transfers on MMIO accesses
hw/sd/sdhci: Stop multiple transfers when block count is cleared
hw/sd/sdhci: Fix DMA Transfer Block Size field
hw/sd/sdhci: Document the datasheet used
hw/sd/sdhci: Fix qemu_log_mask() format string
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index a7f0acf..6a197bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2373,11 +2373,18 @@
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 7ba0b69..7054965 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 58b2e16..8d8ad1a 100644
--- a/hw/acpi/aml-build-stub.c
+++ b/hw/acpi/aml-build-stub.c
@@ -57,6 +57,20 @@
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 32aa155..eea059f 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -105,6 +105,7 @@
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 45ad2f9..e3a4bc2 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -2477,7 +2477,7 @@
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 ab9b005..8a76965 100644
--- a/hw/i386/acpi-common.c
+++ b/hw/i386/acpi-common.c
@@ -72,8 +72,7 @@
}
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 @@
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 9cac18d..c30e461 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 f16f231..d34a301 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_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 @@
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 @@
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 68a7f42..5428448 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 @@
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 @@
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 3137a20..5944fc4 100644
--- a/hw/i386/x86.c
+++ b/hw/i386/x86.c
@@ -1178,6 +1178,7 @@
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 f3ababf..9519c33 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 4dd2ba9..a674ce4 100644
--- a/hw/usb/Kconfig
+++ b/hw/usb/Kconfig
@@ -32,8 +32,6 @@
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_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 2efa6fa..5707b2c 100644
--- a/hw/usb/hcd-xhci-nec.c
+++ b/hw/usb/hcd-xhci-nec.c
@@ -34,8 +34,8 @@
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 a6d746e..b78fcd2 100644
--- a/hw/usb/hcd-xhci-pci.c
+++ b/hw/usb/hcd-xhci-pci.c
@@ -240,8 +240,8 @@
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 852ca51..29185d2 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 @@
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 a308753..fdfcbbe 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 5e8bed9..79ce5c4 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 f859a17..ccf50ae 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 @@
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 91b0645..0fc2160 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 d5dcf7a..bfa9cb2 100644
--- a/include/hw/i386/x86.h
+++ b/include/hw/i386/x86.h
@@ -58,6 +58,7 @@
/* 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 @@
/* 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 0000000..5c90e13
--- /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 d2e141f..ff33e21 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 0000000..1a581c5
--- /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 0000000..6d0fd6c
--- /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 7fabe62..297933d 100644
--- a/python/qemu/accel.py
+++ b/python/qemu/accel.py
@@ -17,6 +17,8 @@
import logging
import os
import subprocess
+from typing import List, Optional
+
LOG = logging.getLogger(__name__)
@@ -29,7 +31,7 @@
}
-def list_accel(qemu_bin):
+def list_accel(qemu_bin: str) -> List[str]:
"""
List accelerators enabled in the QEMU binary.
@@ -49,7 +51,8 @@
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 @@
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 70869fb..f060d79 100644
--- a/python/qemu/console_socket.py
+++ b/python/qemu/console_socket.py
@@ -13,10 +13,11 @@
# 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 @@
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 @@
# 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 @@
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 @@
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 82f3731..6420f01 100644
--- a/python/qemu/machine.py
+++ b/python/qemu/machine.py
@@ -18,17 +18,29 @@
#
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 qmp
+from . import console_socket, qmp
+from .qmp import QMPMessage, QMPReturnValue, SocketAddrT
+
LOG = logging.getLogger(__name__)
@@ -57,7 +69,7 @@
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 @@
# 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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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._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)
+ if self._console_set:
+ self._remove_files.append(self._console_address)
- def _post_launch(self):
- if self._qmp:
+ if self._qmp_set:
+ 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 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
"""
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 @@
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 @@
"""
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 @@
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 @@
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 @@
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 @@
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 @@
"""
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 @@
# 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 @@
"""
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 @@
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 7935dab..2cd4d43 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 @@
#: 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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
# 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 @@
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 @@
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 @@
@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 @@
)
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 @@
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 @@
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 @@
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 @@
"""
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 888c8bd..39a0cf6 100644
--- a/python/qemu/qtest.py
+++ b/python/qemu/qtest.py
@@ -17,11 +17,17 @@
# 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 756dacb..7c9a263 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 974021a..a5da513 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 7d1e2e9..40df513 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 47ccedd..e46ac68 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 0000000..6c51081
--- /dev/null
+++ b/tests/data/acpi/microvm/APIC.pcie
Binary files differ
diff --git a/tests/data/acpi/microvm/DSDT.rtc b/tests/data/acpi/microvm/DSDT.rtc
new file mode 100644
index 0000000..5960d69
--- /dev/null
+++ b/tests/data/acpi/microvm/DSDT.rtc
Binary files differ
diff --git a/tests/data/acpi/microvm/DSDT.usb b/tests/data/acpi/microvm/DSDT.usb
new file mode 100644
index 0000000..1442338
--- /dev/null
+++ b/tests/data/acpi/microvm/DSDT.usb
Binary files differ
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index f212cec..63d2ace 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -605,7 +605,7 @@
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 5647624..3830a40 100644
--- a/tests/qtest/bios-tables-test.c
+++ b/tests/qtest/bios-tables-test.c
@@ -1110,6 +1110,28 @@
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 @@
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 0910b4a..f32adc3 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 b11594d..a752aa2 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"