}
+#define QEMU_PCI_VENDOR_INTEL 0x8086
+#define QEMU_PCI_VENDOR_LSI_LOGIC 0x1000
+#define QEMU_PCI_VENDOR_REDHAT 0x1af4
+#define QEMU_PCI_VENDOR_CIRRUS 0x1013
+#define QEMU_PCI_VENDOR_REALTEK 0x10ec
+#define QEMU_PCI_VENDOR_AMD 0x1022
+#define QEMU_PCI_VENDOR_ENSONIQ 0x1274
+#define QEMU_PCI_VENDOR_VMWARE 0x15ad
+#define QEMU_PCI_VENDOR_QEMU 0x1234
+
+#define QEMU_PCI_PRODUCT_DISK_VIRTIO 0x1001
+
+#define QEMU_PCI_PRODUCT_NIC_NE2K 0x8029
+#define QEMU_PCI_PRODUCT_NIC_PCNET 0x2000
+#define QEMU_PCI_PRODUCT_NIC_RTL8139 0x8139
+#define QEMU_PCI_PRODUCT_NIC_E1000 0x100E
+#define QEMU_PCI_PRODUCT_NIC_VIRTIO 0x1000
+
+#define QEMU_PCI_PRODUCT_VGA_CIRRUS 0x00b8
+#define QEMU_PCI_PRODUCT_VGA_VMWARE 0x0405
+#define QEMU_PCI_PRODUCT_VGA_STDVGA 0x1111
+
+#define QEMU_PCI_PRODUCT_AUDIO_AC97 0x2415
+#define QEMU_PCI_PRODUCT_AUDIO_ES1370 0x5000
+
+#define QEMU_PCI_PRODUCT_CONTROLLER_PIIX 0x7010
+#define QEMU_PCI_PRODUCT_CONTROLLER_LSI 0x0012
+
+#define QEMU_PCI_PRODUCT_WATCHDOG_I63000ESB 0x25ab
+
+static int
+qemuAssignNextPCIAddress(virDomainDeviceInfo *info,
+ int vendor,
+ int product,
+ qemuMonitorPCIAddress *addrs,
+ int naddrs)
+{
+ int found = 0;
+ int i;
+
+ VIR_DEBUG("Look for %x:%x out of %d", vendor, product, naddrs);
+
+ for (i = 0 ; (i < naddrs) && !found; i++) {
+ VIR_DEBUG("Maybe %x:%x", addrs[i].vendor, addrs[i].product);
+ if (addrs[i].vendor == vendor &&
+ addrs[i].product == product) {
+ VIR_DEBUG("Match %d", i);
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ return -1;
+ }
+
+ /* Blank it out so this device isn't matched again */
+ addrs[i].vendor = 0;
+ addrs[i].product = 0;
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
+ info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ info->addr.pci.domain = addrs[i].addr.domain;
+ info->addr.pci.bus = addrs[i].addr.bus;
+ info->addr.pci.slot = addrs[i].addr.slot;
+ info->addr.pci.function = addrs[i].addr.function;
+ }
+
+ return 0;
+}
+
+static int
+qemuGetPCIDiskVendorProduct(virDomainDiskDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->bus) {
+ case VIR_DOMAIN_DISK_BUS_VIRTIO:
+ *vendor = QEMU_PCI_VENDOR_REDHAT;
+ *product = QEMU_PCI_PRODUCT_DISK_VIRTIO;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuGetPCINetVendorProduct(virDomainNetDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ if (!def->model)
+ return -1;
+
+ if (STREQ(def->model, "ne2k_pci")) {
+ *vendor = QEMU_PCI_VENDOR_REALTEK;
+ *product = QEMU_PCI_PRODUCT_NIC_NE2K;
+ } else if (STREQ(def->model, "pcnet")) {
+ *vendor = QEMU_PCI_VENDOR_AMD;
+ *product = QEMU_PCI_PRODUCT_NIC_PCNET;
+ } else if (STREQ(def->model, "rtl8139")) {
+ *vendor = QEMU_PCI_VENDOR_REALTEK;
+ *product = QEMU_PCI_PRODUCT_NIC_RTL8139;
+ } else if (STREQ(def->model, "e1000")) {
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_NIC_E1000;
+ } else if (STREQ(def->model, "virtio")) {
+ *vendor = QEMU_PCI_VENDOR_REDHAT;
+ *product = QEMU_PCI_PRODUCT_NIC_VIRTIO;
+ } else {
+ VIR_INFO("Unexpected NIC model %s, cannot get PCI address",
+ def->model);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qemuGetPCIControllerVendorProduct(virDomainControllerDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->type) {
+ case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
+ *vendor = QEMU_PCI_VENDOR_LSI_LOGIC;
+ *product = QEMU_PCI_PRODUCT_CONTROLLER_LSI;
+ break;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
+ /* XXX we could put in the ISA bridge address, but
+ that's not technically the FDC's address */
+ return -1;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_CONTROLLER_PIIX;
+ break;
+
+ default:
+ VIR_INFO("Unexpected controller type %s, cannot get PCI address",
+ virDomainControllerTypeToString(def->type));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuGetPCIVideoVendorProduct(virDomainVideoDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->type) {
+ case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
+ *vendor = QEMU_PCI_VENDOR_CIRRUS;
+ *product = QEMU_PCI_PRODUCT_VGA_CIRRUS;
+ break;
+
+ case VIR_DOMAIN_VIDEO_TYPE_VGA:
+ *vendor = QEMU_PCI_VENDOR_QEMU;
+ *product = QEMU_PCI_PRODUCT_VGA_STDVGA;
+ break;
+
+ case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
+ *vendor = QEMU_PCI_VENDOR_VMWARE;
+ *product = QEMU_PCI_PRODUCT_VGA_VMWARE;
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qemuGetPCISoundVendorProduct(virDomainSoundDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->model) {
+ case VIR_DOMAIN_SOUND_MODEL_ES1370:
+ *vendor = QEMU_PCI_VENDOR_ENSONIQ;
+ *product = QEMU_PCI_PRODUCT_AUDIO_ES1370;
+ break;
+
+ case VIR_DOMAIN_SOUND_MODEL_AC97:
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_AUDIO_AC97;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuGetPCIWatchdogVendorProduct(virDomainWatchdogDefPtr def,
+ unsigned *vendor,
+ unsigned *product)
+{
+ switch (def->model) {
+ case VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB:
+ *vendor = QEMU_PCI_VENDOR_INTEL;
+ *product = QEMU_PCI_PRODUCT_WATCHDOG_I63000ESB;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * This entire method assumes that PCI devices in 'info pci'
+ * match ordering of devices specified on the command line
+ * wrt to devices of matching vendor+product
+ *
+ * XXXX this might not be a valid assumption if we assign
+ * some static addrs on CLI. Have to check that...
+ */
+static int
+qemuAssignPCIAddresses(virDomainObjPtr vm,
+ qemuMonitorPCIAddress *addrs,
+ int naddrs)
+{
+ unsigned int vendor = 0, product = 0;
+ int i;
+
+ /* XXX should all these vendor/product IDs be kept in the
+ * actual device data structure instead ?
+ */
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (qemuGetPCIDiskVendorProduct(vm->def->disks[i], &vendor, &product) < 0)
+ continue;
+
+ if (qemuAssignNextPCIAddress(&(vm->def->disks[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for VirtIO disk %s"),
+ vm->def->disks[i]->dst);
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->nnets ; i++) {
+ if (qemuGetPCINetVendorProduct(vm->def->nets[i], &vendor, &product) < 0)
+ continue;
+
+ if (qemuAssignNextPCIAddress(&(vm->def->nets[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for %s NIC"),
+ vm->def->nets[i]->model);
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+ if (qemuGetPCIControllerVendorProduct(vm->def->controllers[i], &vendor, &product) < 0)
+ continue;
+
+ if (qemuAssignNextPCIAddress(&(vm->def->controllers[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for controller %s"),
+ virDomainControllerTypeToString(vm->def->controllers[i]->type));
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->nvideos ; i++) {
+ if (qemuGetPCIVideoVendorProduct(vm->def->videos[i], &vendor, &product) < 0)
+ continue;
+
+ if (qemuAssignNextPCIAddress(&(vm->def->videos[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for video adapter %s"),
+ virDomainVideoTypeToString(vm->def->videos[i]->type));
+ return -1;
+ }
+ }
+
+ for (i = 0 ; i < vm->def->nsounds ; i++) {
+ if (qemuGetPCISoundVendorProduct(vm->def->sounds[i], &vendor, &product) < 0)
+ continue;
+
+ if (qemuAssignNextPCIAddress(&(vm->def->sounds[i]->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for sound adapter %s"),
+ virDomainSoundModelTypeToString(vm->def->sounds[i]->model));
+ return -1;
+ }
+ }
+
+
+ if (vm->def->watchdog &&
+ qemuGetPCIWatchdogVendorProduct(vm->def->watchdog, &vendor, &product) == 0) {
+ if (qemuAssignNextPCIAddress(&(vm->def->watchdog->info),
+ vendor, product,
+ addrs, naddrs) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("cannot find PCI address for watchdog %s"),
+ virDomainWatchdogModelTypeToString(vm->def->watchdog->model));
+ return -1;
+ }
+ }
+
+ /* XXX console (virtio) */
+
+
+ /* ... and now things we don't have in our xml */
+
+ /* XXX USB controller ? */
+
+ /* XXXX virtio balloon ? */
+
+ /* XXX what about other PCI devices (ie bridges) */
+
+ return 0;
+}
+
+static int
+qemuInitPCIAddresses(struct qemud_driver *driver,
+ virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int naddrs;
+ int ret;
+ qemuMonitorPCIAddress *addrs = NULL;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ naddrs = qemuMonitorGetAllPCIAddresses(priv->mon,
+ &addrs);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ ret = qemuAssignPCIAddresses(vm, addrs, naddrs);
+
+ VIR_FREE(addrs);
+
+ return ret;
+}
+
static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
int i;
if (qemuInitPasswords(driver, vm) < 0)
goto abort;
+ if (qemuInitPCIAddresses(driver, vm) < 0)
+ goto abort;
+
qemuDomainObjEnterMonitorWithDriver(driver, vm);
if (qemuMonitorSetBalloon(priv->mon, vm->def->memory) < 0) {
qemuDomainObjExitMonitorWithDriver(driver, vm);
}
+/*
+ * The format we're after looks like this
+ *
+ * (qemu) info pci
+ * Bus 0, device 0, function 0:
+ * Host bridge: PCI device 8086:1237
+ * id ""
+ * Bus 0, device 1, function 0:
+ * ISA bridge: PCI device 8086:7000
+ * id ""
+ * Bus 0, device 1, function 1:
+ * IDE controller: PCI device 8086:7010
+ * BAR4: I/O at 0xc000 [0xc00f].
+ * id ""
+ * Bus 0, device 1, function 3:
+ * Bridge: PCI device 8086:7113
+ * IRQ 9.
+ * id ""
+ * Bus 0, device 2, function 0:
+ * VGA controller: PCI device 1013:00b8
+ * BAR0: 32 bit prefetchable memory at 0xf0000000 [0xf1ffffff].
+ * BAR1: 32 bit memory at 0xf2000000 [0xf2000fff].
+ * id ""
+ * Bus 0, device 3, function 0:
+ * Ethernet controller: PCI device 8086:100e
+ * IRQ 11.
+ * BAR0: 32 bit memory at 0xf2020000 [0xf203ffff].
+ * BAR1: I/O at 0xc040 [0xc07f].
+ * id ""
+ *
+ * Of this, we're interesting in the vendor/product ID
+ * and the bus/device/function data.
+ */
+#define CHECK_END(p) if (!(p)) break;
+#define SKIP_TO(p, lbl) \
+ (p) = strstr((p), (lbl)); \
+ if (p) \
+ (p) += strlen(lbl);
+#define GET_INT(p, base, val) \
+ if (virStrToLong_ui((p), &(p), (base), &(val)) < 0) { \
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED, \
+ _("cannot parse value for %s"), #val); \
+ break; \
+ }
+#define SKIP_SPACE(p) \
+ while (*(p) == ' ') (p)++;
+
+int qemuMonitorTextGetAllPCIAddresses(qemuMonitorPtr mon,
+ qemuMonitorPCIAddress **retaddrs)
+{
+ char *reply;
+ qemuMonitorPCIAddress *addrs = NULL;
+ int naddrs = 0;
+ char *p;
+
+ *retaddrs = NULL;
+
+ if (qemuMonitorCommand(mon, "info pci", &reply) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+ _("cannot query PCI addresses"));
+ return -1;
+ }
+
+ p = reply;
+
+
+ while (p) {
+ unsigned int bus, slot, func, vendor, product;
+
+ SKIP_TO(p, " Bus");
+ CHECK_END(p);
+ SKIP_SPACE(p);
+ GET_INT(p, 10, bus);
+ CHECK_END(p);
+
+ SKIP_TO(p, ", device");
+ CHECK_END(p);
+ SKIP_SPACE(p);
+ GET_INT(p, 10, slot);
+ CHECK_END(p);
+
+ SKIP_TO(p, ", function");
+ CHECK_END(p);
+ SKIP_SPACE(p);
+ GET_INT(p, 10, func);
+ CHECK_END(p);
+
+ SKIP_TO(p, "PCI device");
+ CHECK_END(p);
+ SKIP_SPACE(p);
+ GET_INT(p, 16, vendor);
+ CHECK_END(p);
+
+ if (*p != ':')
+ break;
+ p++;
+ GET_INT(p, 16, product);
+
+ if (VIR_REALLOC_N(addrs, naddrs+1) < 0) {
+ virReportOOMError(NULL);
+ goto error;
+ }
+
+ addrs[naddrs].addr.domain = 0;
+ addrs[naddrs].addr.bus = bus;
+ addrs[naddrs].addr.slot = slot;
+ addrs[naddrs].addr.function = func;
+ addrs[naddrs].vendor = vendor;
+ addrs[naddrs].product = product;
+ naddrs++;
+
+ VIR_DEBUG("Got dev %d:%d:%d %x:%x", bus, slot, func, vendor, product);
+ }
+
+ VIR_FREE(reply);
+
+ *retaddrs = addrs;
+
+ return naddrs;
+
+error:
+ VIR_FREE(addrs);
+ VIR_FREE(reply);
+ return -1;
+}
+#undef GET_INT
+#undef SKIP_SPACE
+#undef CHECK_END
+#undef SKIP_TO