X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/7ef6e71c599aebd066ab1d29be8944bd5c0eb0c2..dce9e92834cc4f962e547cae46b73ca559d05b0c:/hw/pci-hotplug.c diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index 1f92e09f87..fe468d646e 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -26,19 +26,39 @@ #include "boards.h" #include "pci.h" #include "net.h" -#include "sysemu.h" #include "pc.h" #include "monitor.h" -#include "block_int.h" +#include "scsi.h" #include "virtio-blk.h" +#include "qemu-config.h" -#if defined(TARGET_I386) || defined(TARGET_X86_64) +#if defined(TARGET_I386) static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, - const char *devaddr, const char *opts) + const char *devaddr, + const char *opts_str) { - int ret; + QemuOpts *opts; + PCIBus *bus; + int ret, devfn; - ret = net_client_init(mon, "nic", opts); + bus = pci_get_bus_devfn(&devfn, devaddr); + if (!bus) { + monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); + return NULL; + } + if (!((BusState*)bus)->allow_hotplug) { + monitor_printf(mon, "PCI bus doesn't support hotplug\n"); + return NULL; + } + + opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", 0); + if (!opts) { + return NULL; + } + + qemu_opt_set(opts, "type", "nic"); + + ret = net_client_init(mon, opts, 0); if (ret < 0) return NULL; if (nd_table[ret].devaddr) { @@ -48,49 +68,84 @@ static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); } -void drive_hot_add(Monitor *mon, const char *pci_addr, const char *opts) +static int scsi_hot_add(Monitor *mon, DeviceState *adapter, + DriveInfo *dinfo, int printinfo) { - int dom, pci_bus; - unsigned slot; - int type, bus; - int success = 0; - PCIDevice *dev; - DriveInfo *dinfo; + SCSIBus *scsibus; + SCSIDevice *scsidev; - if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { - return; + scsibus = DO_UPCAST(SCSIBus, qbus, QLIST_FIRST(&adapter->child_bus)); + if (!scsibus || strcmp(scsibus->qbus.info->name, "SCSI") != 0) { + error_report("Device is not a SCSI adapter"); + return -1; } - dev = pci_find_device(pci_bus, slot, 0); - if (!dev) { - monitor_printf(mon, "no pci device with address %s\n", pci_addr); - return; + /* + * drive_init() tries to find a default for dinfo->unit. Doesn't + * work at all for hotplug though as we assign the device to a + * specific bus instead of the first bus with spare scsi ids. + * + * Ditch the calculated value and reload from option string (if + * specified). + */ + dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); + scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit); + if (!scsidev) { + return -1; } + dinfo->unit = scsidev->id; + + if (printinfo) + monitor_printf(mon, "OK bus %d, unit %d\n", + scsibus->busnr, scsidev->id); + return 0; +} + +void drive_hot_add(Monitor *mon, const QDict *qdict) +{ + int dom, pci_bus; + unsigned slot; + int type; + PCIDevice *dev; + DriveInfo *dinfo = NULL; + const char *pci_addr = qdict_get_str(qdict, "pci_addr"); + const char *opts = qdict_get_str(qdict, "opts"); dinfo = add_init_drive(opts); if (!dinfo) - return; + goto err; if (dinfo->devaddr) { monitor_printf(mon, "Parameter addr not supported\n"); - return; + goto err; } type = dinfo->type; - bus = drive_get_max_bus (type); switch (type) { case IF_SCSI: - success = 1; - lsi_scsi_attach(&dev->qdev, dinfo->bdrv, - dinfo->unit); + if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { + goto err; + } + dev = pci_find_device(pci_find_root_bus(dom), pci_bus, slot, 0); + if (!dev) { + monitor_printf(mon, "no pci device with address %s\n", pci_addr); + goto err; + } + if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) { + goto err; + } + break; + case IF_NONE: + monitor_printf(mon, "OK\n"); break; default: monitor_printf(mon, "Can't hot-add drive to type %d\n", type); + goto err; } + return; - if (success) - monitor_printf(mon, "OK bus %d, unit %d\n", - dinfo->bus, - dinfo->unit); +err: + if (dinfo) + drive_uninit(dinfo); return; } @@ -99,9 +154,11 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, const char *opts) { PCIDevice *dev; - DriveInfo *dinfo; + DriveInfo *dinfo = NULL; int type = -1; char buf[128]; + PCIBus *bus; + int devfn; if (get_param_value(buf, sizeof(buf), "if", opts)) { if (!strcmp(buf, "scsi")) @@ -129,30 +186,54 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, dinfo = NULL; } + bus = pci_get_bus_devfn(&devfn, devaddr); + if (!bus) { + monitor_printf(mon, "Invalid PCI device address %s\n", devaddr); + return NULL; + } + if (!((BusState*)bus)->allow_hotplug) { + monitor_printf(mon, "PCI bus doesn't support hotplug\n"); + return NULL; + } + switch (type) { case IF_SCSI: - dev = pci_create("lsi53c895a", devaddr); + dev = pci_create(bus, devfn, "lsi53c895a"); + if (qdev_init(&dev->qdev) < 0) + dev = NULL; + if (dev && dinfo) { + if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) { + qdev_unplug(&dev->qdev); + dev = NULL; + } + } break; case IF_VIRTIO: if (!dinfo) { monitor_printf(mon, "virtio requires a backing file/device.\n"); return NULL; } - dev = pci_create("virtio-blk-pci", devaddr); - qdev_prop_set_drive(&dev->qdev, "drive", dinfo); + dev = pci_create(bus, devfn, "virtio-blk-pci"); + if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { + qdev_free(&dev->qdev); + dev = NULL; + break; + } + if (qdev_init(&dev->qdev) < 0) + dev = NULL; break; default: dev = NULL; } - if (dev) - qdev_init(&dev->qdev); return dev; } -void pci_device_hot_add(Monitor *mon, const char *pci_addr, const char *type, - const char *opts) +void pci_device_hot_add(Monitor *mon, const QDict *qdict) { PCIDevice *dev = NULL; + const char *pci_addr = qdict_get_str(qdict, "pci_addr"); + const char *type = qdict_get_str(qdict, "type"); + const char *opts = qdict_get_try_str(qdict, "opts"); /* strip legacy tag */ if (!strncmp(pci_addr, "pci_addr=", 9)) { @@ -166,75 +247,43 @@ void pci_device_hot_add(Monitor *mon, const char *pci_addr, const char *type, if (!strcmp(pci_addr, "auto")) pci_addr = NULL; - if (strcmp(type, "nic") == 0) + if (strcmp(type, "nic") == 0) { dev = qemu_pci_hot_add_nic(mon, pci_addr, opts); - else if (strcmp(type, "storage") == 0) + } else if (strcmp(type, "storage") == 0) { dev = qemu_pci_hot_add_storage(mon, pci_addr, opts); - else + } else { monitor_printf(mon, "invalid type: %s\n", type); + } if (dev) { - qemu_system_device_hot_add(pci_bus_num(dev->bus), - PCI_SLOT(dev->devfn), 1); monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", - 0, pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), + pci_find_domain(dev->bus), + pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); } else monitor_printf(mon, "failed to add %s\n", opts); } #endif -void pci_device_hot_remove(Monitor *mon, const char *pci_addr) +int pci_device_hot_remove(Monitor *mon, const char *pci_addr) { PCIDevice *d; int dom, bus; unsigned slot; if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) { - return; + return -1; } - d = pci_find_device(bus, slot, 0); + d = pci_find_device(pci_find_root_bus(dom), bus, slot, 0); if (!d) { monitor_printf(mon, "slot %d empty\n", slot); - return; + return -1; } - - qemu_system_device_hot_add(bus, slot, 0); -} - -static int pci_match_fn(void *dev_private, void *arg) -{ - PCIDevice *dev = dev_private; - PCIDevice *match = arg; - - return (dev == match); + return qdev_unplug(&d->qdev); } -/* - * OS has executed _EJ0 method, we now can remove the device - */ -void pci_device_hot_remove_success(int pcibus, int slot) +void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict) { - PCIDevice *d = pci_find_device(pcibus, slot, 0); - int class_code; - - if (!d) { - monitor_printf(cur_mon, "invalid slot %d\n", slot); - return; - } - - class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1); - - switch(class_code) { - case PCI_BASE_CLASS_STORAGE: - destroy_bdrvs(pci_match_fn, d); - break; - case PCI_BASE_CLASS_NETWORK: - destroy_nic(pci_match_fn, d); - break; - } - - pci_unregister_device(d); + pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr")); } -