diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3d43571403..bc80900106 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1691,6 +1691,364 @@ qemuInitPasswords(struct qemud_driver *driver, } +#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; @@ -2566,6 +2924,9 @@ static int qemudStartVMDaemon(virConnectPtr conn, 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); diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b6ffc2656e..031df3049b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1286,3 +1286,16 @@ int qemuMonitorAttachDrive(qemuMonitorPtr mon, return ret; } + +int qemuMonitorGetAllPCIAddresses(qemuMonitorPtr mon, + qemuMonitorPCIAddress **addrs) +{ + DEBUG("mon=%p, fd=%d addrs=%p", mon, mon->fd, addrs); + int ret; + + if (mon->json) + ret = qemuMonitorJSONGetAllPCIAddresses(mon, addrs); + else + ret = qemuMonitorTextGetAllPCIAddresses(mon, addrs); + return ret; +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index e270299688..8a405ce3d2 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -274,4 +274,15 @@ int qemuMonitorAttachDrive(qemuMonitorPtr mon, virDomainDevicePCIAddress *controllerAddr, virDomainDeviceDriveAddress *driveAddr); + +typedef struct _qemuMonitorPCIAddress qemuMonitorPCIAddress; +struct _qemuMonitorPCIAddress { + unsigned int vendor; + unsigned int product; + virDomainDevicePCIAddress addr; +}; + +int qemuMonitorGetAllPCIAddresses(qemuMonitorPtr mon, + qemuMonitorPCIAddress **addrs); + #endif /* QEMU_MONITOR_H */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 0ae8cd61f0..cfea376f05 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -1589,3 +1589,10 @@ int qemuMonitorJSONAttachDrive(qemuMonitorPtr mon, virJSONValueFree(reply); return ret; } + + +int qemuMonitorJSONGetAllPCIAddresses(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + qemuMonitorPCIAddress **addrs ATTRIBUTE_UNUSED) +{ + return -1; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index fcab483389..7db978562f 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -150,4 +150,7 @@ int qemuMonitorJSONAttachDrive(qemuMonitorPtr mon, virDomainDevicePCIAddress *controllerAddr, virDomainDeviceDriveAddress *driveAddr); +int qemuMonitorJSONGetAllPCIAddresses(qemuMonitorPtr mon, + qemuMonitorPCIAddress **addrs); + #endif /* QEMU_MONITOR_JSON_H */ diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index c2c37b4178..52cd97ce1d 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -1897,3 +1897,132 @@ cleanup: } +/* + * 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 diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index 4648ba15c0..d6e9ca1402 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -157,5 +157,7 @@ int qemuMonitorTextAttachDrive(qemuMonitorPtr mon, virDomainDevicePCIAddress *controllerAddr, virDomainDeviceDriveAddress *driveAddr); +int qemuMonitorTextGetAllPCIAddresses(qemuMonitorPtr mon, + qemuMonitorPCIAddress **addrs); #endif /* QEMU_MONITOR_TEXT_H */