diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 50f37ae5cc..3e07360d7a 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1686,6 +1686,18 @@ qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, break; } } + /* SATA controllers aren't hot-plugged, and can be put in either a + * PCI or PCIe slot + */ + if (device->type == VIR_DOMAIN_DEVICE_CONTROLLER && + device->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) + flags = QEMU_PCI_CONNECT_TYPE_PCI | QEMU_PCI_CONNECT_TYPE_PCIE; + + /* video cards aren't hot-plugged, and can be put in either a PCI + * or PCIe slot + */ + if (device->type == VIR_DOMAIN_DEVICE_VIDEO) + flags = QEMU_PCI_CONNECT_TYPE_PCI | QEMU_PCI_CONNECT_TYPE_PCIE; /* Ignore implicit controllers on slot 0:0:1.0: * implicit IDE controller on 0:0:1.1 (no qemu command line) @@ -2258,6 +2270,12 @@ qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def, } if (def->nvideos > 0) { + /* Because the PIIX3 integrated IDE/USB controllers are + * already at slot 1, when qemu looks for the first free slot + * to place the VGA controller (which is always the first + * device added after integrated devices), it *always* ends up + * at slot 2. + */ virDomainVideoDefPtr primaryVideo = def->videos[0]; if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; @@ -2318,6 +2336,136 @@ error: } +static bool +qemuDomainMachineIsQ35(virDomainDefPtr def) +{ + return (STRPREFIX(def->os.machine, "pc-q35") || + STREQ(def->os.machine, "q35")); +} + + +static int +qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def, + virQEMUCapsPtr qemuCaps, + qemuDomainPCIAddressSetPtr addrs) +{ + size_t i; + virDevicePCIAddress tmp_addr; + bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); + virDevicePCIAddressPtr addrptr; + qemuDomainPCIConnectFlags flags = QEMU_PCI_CONNECT_TYPE_PCIE; + + /* Verify that the first SATA controller is at 00:1F.2 */ + /* the q35 machine type *always* has a SATA controller at this address */ + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA && + def->controllers[i]->idx == 0) { + if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (def->controllers[i]->info.addr.pci.domain != 0 || + def->controllers[i]->info.addr.pci.bus != 0 || + def->controllers[i]->info.addr.pci.slot != 0x1F || + def->controllers[i]->info.addr.pci.function != 2) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary SATA controller must have PCI address 0:0:1f.2")); + goto error; + } + } else { + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = 0x1F; + def->controllers[i]->info.addr.pci.function = 2; + } + } + } + + /* Reserve slot 0x1F function 0 (ISA bridge, not in config model) + * and function 3 (SMBus, also not (yet) in config model). As with + * the SATA controller, these devices are always present in a q35 + * machine; there is no way to not have them. + */ + if (addrs->nbuses) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 0x1F; + tmp_addr.function = 0; + tmp_addr.multi = 1; + if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, + false, false) < 0) + goto error; + tmp_addr.function = 3; + tmp_addr.multi = 0; + if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags, + false, false) < 0) + goto error; + } + + if (def->nvideos > 0) { + /* NB: unlike the pc machinetypes, on q35 machinetypes the + * integrated devices are at slot 0x1f, so when qemu looks for + * the first free lot for the first VGA, it will always be at + * slot 1 (which was used up by the integrated PIIX3 devices + * on pc machinetypes). + */ + virDomainVideoDefPtr primaryVideo = def->videos[0]; + if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + primaryVideo->info.addr.pci.domain = 0; + primaryVideo->info.addr.pci.bus = 0; + primaryVideo->info.addr.pci.slot = 1; + primaryVideo->info.addr.pci.function = 0; + addrptr = &primaryVideo->info.addr.pci; + + if (!qemuDomainPCIAddressValidate(addrs, addrptr, flags)) + goto error; + + if (qemuDomainPCIAddressSlotInUse(addrs, addrptr)) { + if (qemuDeviceVideoUsable) { + virResetLastError(); + if (qemuDomainPCIAddressReserveNextSlot(addrs, + &primaryVideo->info, + flags) < 0) + goto error; + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("PCI address 0:0:1.0 is in use, " + "QEMU needs it for primary video")); + goto error; + } + } else if (qemuDomainPCIAddressReserveSlot(addrs, addrptr, flags) < 0) { + goto error; + } + } else if (!qemuDeviceVideoUsable) { + if (primaryVideo->info.addr.pci.domain != 0 || + primaryVideo->info.addr.pci.bus != 0 || + primaryVideo->info.addr.pci.slot != 1 || + primaryVideo->info.addr.pci.function != 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary video card must have PCI address 0:0:1.0")); + goto error; + } + /* If TYPE==PCI, then qemuCollectPCIAddress() function + * has already reserved the address, so we must skip */ + } + } else if (addrs->nbuses && !qemuDeviceVideoUsable) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 1; + + if (qemuDomainPCIAddressSlotInUse(addrs, &tmp_addr)) { + VIR_DEBUG("PCI address 0:0:1.0 in use, future addition of a video" + " device will not be possible without manual" + " intervention"); + virResetLastError(); + } else if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) { + goto error; + } + } + return 0; + +error: + return -1; +} + + /* * This assigns static PCI slots to all configured devices. * The ordering here is chosen to match the ordering used @@ -2368,6 +2516,11 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, goto error; } + if (qemuDomainMachineIsQ35(def) && + qemuDomainValidateDevicePCISlotsQ35(def, qemuCaps, addrs) < 0) { + goto error; + } + /* PCI controllers */ for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { @@ -7679,6 +7832,9 @@ qemuBuildCommandLine(virConnectPtr conn, _("SATA is not supported with this " "QEMU binary")); goto error; + } else if (cont->idx == 0 && qemuDomainMachineIsQ35(def)) { + /* first SATA controller on Q35 machines is implicit */ + continue; } else { char *devstr; @@ -7692,6 +7848,7 @@ qemuBuildCommandLine(virConnectPtr conn, } } else if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && cont->model == -1 && + !qemuDomainMachineIsQ35(def) && (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI) || def->os.arch == VIR_ARCH_PPC64)) { if (usblegacy) { @@ -7716,7 +7873,7 @@ qemuBuildCommandLine(virConnectPtr conn, } } - if (usbcontroller == 0) + if (usbcontroller == 0 && !qemuDomainMachineIsQ35(def)) virCommandAddArg(cmd, "-usb"); for (i = 0; i < def->nhubs; i++) { diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 206d471287..d0f09ae6ad 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -700,6 +700,7 @@ qemuDomainDefPostParse(virDomainDefPtr def, void *opaque ATTRIBUTE_UNUSED) { bool addDefaultUSB = true; + bool addImplicitSATA = false; bool addPCIRoot = false; bool addPCIeRoot = false; @@ -722,6 +723,7 @@ qemuDomainDefPostParse(virDomainDefPtr def, STREQ(def->os.machine, "q35")) { addPCIeRoot = true; addDefaultUSB = false; + addImplicitSATA = true; break; } if (!STRPREFIX(def->os.machine, "pc-0.") && @@ -754,6 +756,11 @@ qemuDomainDefPostParse(virDomainDefPtr def, def, VIR_DOMAIN_CONTROLLER_TYPE_USB, 0, -1) < 0) return -1; + if (addImplicitSATA && + virDomainDefMaybeAddController( + def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, 0, -1) < 0) + return -1; + if (addPCIRoot && virDomainDefMaybeAddController( def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0, diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.args b/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.args index 23db85cbef..cecef7b5ad 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.args @@ -1,5 +1,5 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/libexec/qemu-kvm \ -S -M q35 -m 2048 -smp 2 -nographic -nodefaults \ -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \ --device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x1 \ --device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x1 -usb +-device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x2 \ +-device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.xml b/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.xml index 1aa54558de..d7fb90cde7 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-pcie-root.xml @@ -15,7 +15,6 @@ /usr/libexec/qemu-kvm - diff --git a/tests/qemuxml2argvdata/qemuxml2argv-q35.args b/tests/qemuxml2argvdata/qemuxml2argv-q35.args index ddff6f0d9e..6c24407a65 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-q35.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-q35.args @@ -1,7 +1,6 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test \ /usr/libexec/qemu-kvm -S -M q35 -m 2048 -smp 2 -nographic -nodefaults \ -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c \ --device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x1 \ +-device i82801b11-bridge,id=pci.1,bus=pci.0,addr=0x2 \ -device pci-bridge,chassis_nr=2,id=pci.2,bus=pci.1,addr=0x1 \ --usb \ -vga qxl -global qxl.ram_size=67108864 -global qxl.vram_size=18874368 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index aba0f88b66..0068d27ed2 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -995,11 +995,13 @@ mymain(void) DO_TEST("pci-bridge-many-disks", QEMU_CAPS_DEVICE, QEMU_CAPS_DRIVE, QEMU_CAPS_DEVICE_PCI_BRIDGE); DO_TEST("pcie-root", + QEMU_CAPS_ICH9_AHCI, QEMU_CAPS_DEVICE, QEMU_CAPS_DEVICE_PCI_BRIDGE, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE); DO_TEST("q35", QEMU_CAPS_DEVICE, QEMU_CAPS_DEVICE_PCI_BRIDGE, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE, + QEMU_CAPS_ICH9_AHCI, QEMU_CAPS_VGA, QEMU_CAPS_DEVICE_VIDEO_PRIMARY, QEMU_CAPS_VGA, QEMU_CAPS_VGA_QXL, QEMU_CAPS_DEVICE_QXL); diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pcie-root.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pcie-root.xml index 25c77f1443..f10e85b2f2 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pcie-root.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pcie-root.xml @@ -15,7 +15,7 @@ /usr/libexec/qemu-kvm - + diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-q35.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-q35.xml new file mode 100644 index 0000000000..2a86e61f57 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-q35.xml @@ -0,0 +1,26 @@ + + q35-test + 11dbdcdd-4c3b-482b-8903-9bdb8c0a2774 + 2097152 + 2097152 + 2 + + hvm + + + + destroy + restart + destroy + + /usr/libexec/qemu-kvm + + + + + + + + diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 8b4590af28..5c6730d25e 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -295,7 +295,7 @@ mymain(void) DO_TEST_DIFFERENT("pci-autoadd-addr"); DO_TEST_DIFFERENT("pci-autoadd-idx"); DO_TEST_DIFFERENT("pcie-root"); - DO_TEST("q35"); + DO_TEST_DIFFERENT("q35"); DO_TEST("hostdev-scsi-lsi"); DO_TEST("hostdev-scsi-virtio-scsi");