-usb gives the PIIX3 UHCI on pc and the ICH9 EHCI with three UHCI companions on q35, so the one-switch way to get USB still picks a USB 1.1 or 2.0 controller. libvirt disables machine-created USB controllers with usb=off and adds controllers explicitly.
>From pc-i440fx-12.0 and pc-q35-12.0 on, create qemu-xhci at 1d.0 instead. On q35 that is where the first UHCI sat, so every other device keeps its PCI address. On pc the PIIX3's own USB function is out: the i440fx DSDT routes slot 1 INTA to the SCI link and qemu-xhci interrupts on INTA, so a guest that does not use MSI gets "PCI INT A: no GSI" and never enumerates its devices; the UHCI there uses INTD. Slot 1d is free on pc and keeps the two machines alike. The device stays optional in Kconfig. A build without it fails -usb with a message instead of a fatal "unknown type" error. Guests without an xHCI driver (Windows 7 and earlier) need an older machine type or an explicit -device piix3-usb-uhci. Suggested-by: Peter Maydell <[email protected]> Link: https://lore.kernel.org/qemu-devel/cafeaca9b2oezaztgkn5ypmkoxwddzqevdobj5mgvh51aki-...@mail.gmail.com/ Link: https://lore.kernel.org/qemu-devel/[email protected]/ Signed-off-by: Junjie Cao <[email protected]> --- hw/i386/Kconfig | 2 ++ hw/i386/pc.c | 18 ++++++++++++++++++ hw/i386/pc_piix.c | 11 ++++++++++- hw/i386/pc_q35.c | 12 ++++++++++-- include/hw/i386/pc.h | 4 ++++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index e27d8816e5..1d88726618 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -78,6 +78,7 @@ config I440FX imply E1000_PCI imply VMPORT imply VMMOUSE + imply USB_XHCI_PCI select ACPI_PIIX4 select PC_PCI select PC_ACPI @@ -108,6 +109,7 @@ config Q35 imply VMPORT imply VMMOUSE imply IOMMUFD + imply USB_XHCI_PCI select PC_PCI select PC_ACPI select PCI_EXPRESS_Q35 diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 8a6b7978cc..6b8fc2914e 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -55,6 +55,7 @@ #include "hw/mem/nvdimm.h" #include "hw/cxl/cxl_host.h" #include "hw/usb/usb.h" +#include "hw/usb/xhci.h" #include "hw/i386/intel_iommu.h" #include "hw/net/ne2000-isa.h" #include "hw/virtio/virtio-iommu.h" @@ -1164,6 +1165,23 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) } } +/* + * 1d.0 is where q35's first UHCI sat. On pc the PIIX3's own USB function + * is out: the DSDT routes slot 1 INTA to the SCI link and qemu-xhci + * interrupts on INTA. + */ +void pc_usb_xhci_create(PCIBus *pci_bus) +{ + DeviceState *dev = qdev_try_new(TYPE_QEMU_XHCI); + + if (!dev) { + error_report("-usb: this build has no %s device", TYPE_QEMU_XHCI); + exit(1); + } + qdev_prop_set_int32(dev, "addr", PCI_DEVFN(0x1d, 0)); + qdev_realize_and_unref(dev, BUS(pci_bus), &error_fatal); +} + void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) { qemu_irq *i8259; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c0ddf6011f..845092f5f1 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -245,7 +245,8 @@ static void pc_init1(MachineState *machine, const char *pci_type) pci_dev = pci_new_multifunction(-1, pc_south_bridge_type[pcms->south_bridge]); object_property_set_bool(OBJECT(pci_dev), "has-usb", - machine_usb(machine), &error_abort); + machine_usb(machine) && !pcmc->usb_xhci, + &error_abort); object_property_set_bool(OBJECT(pci_dev), "has-acpi", x86_machine_is_acpi_enabled(x86ms), &error_abort); @@ -305,6 +306,10 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_nic_init(pcmc, isa_bus, pcms->pcibus); + if (machine_usb(machine) && pcmc->usb_xhci) { + pc_usb_xhci_create(pcms->pcibus); + } + if (piix4_pm) { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); @@ -375,6 +380,7 @@ static void pc_i440fx_machine_options(MachineClass *m) pcmc->default_south_bridge = PC_SOUTH_BRIDGE_OPTION_PIIX3; pcmc->pci_root_uid = 0; pcmc->default_cpu_version = 1; + pcmc->usb_xhci = true; m->family = "pc_piix"; m->desc = "Standard PC (i440FX + PIIX, 1996)"; @@ -406,7 +412,10 @@ DEFINE_I440FX_MACHINE_AS_LATEST(12, 0); static void pc_i440fx_machine_11_2_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_i440fx_machine_12_0_options(m); + pcmc->usb_xhci = false; compat_props_add(m->compat_props, hw_compat_11_2, hw_compat_11_2_len); compat_props_add(m->compat_props, pc_compat_11_2, pc_compat_11_2_len); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index e302ff283c..8a856bf199 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -298,8 +298,12 @@ static void pc_q35_init(MachineState *machine) } if (machine_usb(machine)) { - /* Should we create 6 UHCI according to ich9 spec? */ - ehci_create_ich9_with_companions(pcms->pcibus, 0x1d); + if (pcmc->usb_xhci) { + pc_usb_xhci_create(pcms->pcibus); + } else { + /* Should we create 6 UHCI according to ich9 spec? */ + ehci_create_ich9_with_companions(pcms->pcibus, 0x1d); + } } if (pcms->smbus_enabled) { @@ -353,6 +357,7 @@ static void pc_q35_machine_options(MachineClass *m) ObjectClass *oc = OBJECT_CLASS(m); pcmc->pci_root_uid = 0; pcmc->default_cpu_version = 1; + pcmc->usb_xhci = true; m->family = "pc_q35"; m->desc = "Standard PC (Q35 + ICH9, 2009)"; @@ -387,7 +392,10 @@ DEFINE_Q35_MACHINE_AS_LATEST(12, 0); static void pc_q35_machine_11_2_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_machine_12_0_options(m); + pcmc->usb_xhci = false; compat_props_add(m->compat_props, hw_compat_11_2, hw_compat_11_2_len); compat_props_add(m->compat_props, pc_compat_11_2, pc_compat_11_2_len); } diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index caceddc74b..c378c804f8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -119,6 +119,9 @@ struct PCMachineClass { /* create kvmclock device even when KVM PV features are not exposed */ bool kvmclock_create_always; + /* -usb creates qemu-xhci instead of the chipset's UHCI/EHCI */ + bool usb_xhci; + /* * whether the machine type implements broken 32-bit address space bound * check for memory. @@ -195,6 +198,7 @@ void pc_basic_device_init(struct PCMachineState *pcms, bool create_fdctrl, uint32_t hpet_irqs); void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus); +void pc_usb_xhci_create(PCIBus *pci_bus); void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs); -- 2.43.0
