X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/7101174e5cd37ed57fadbba9515f2f3e15aec47d..7e0380b9bbf7c40361e06e6e0d8675a55bd0dea0:/hw/pci-hotplug.c diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c index d40a3bd0cd..b59be2a599 100644 --- a/hw/pci-hotplug.c +++ b/hw/pci-hotplug.c @@ -26,43 +26,92 @@ #include "boards.h" #include "pci.h" #include "net.h" -#include "sysemu.h" #include "pc.h" #include "monitor.h" -#include "block_int.h" -#include "scsi-disk.h" +#include "scsi.h" #include "virtio-blk.h" +#include "qemu-config.h" +#include "blockdev.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_find_opts("net"), 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) { monitor_printf(mon, "Parameter addr not supported\n"); return NULL; } + return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); +} - if (nd_table[ret].model && !pci_nic_supported(nd_table[ret].model)) - return NULL; +static int scsi_hot_add(Monitor *mon, DeviceState *adapter, + DriveInfo *dinfo, int printinfo) +{ + SCSIBus *scsibus; + SCSIDevice *scsidev; - return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); + 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; + } + + /* + * 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); + dinfo->bus = scsibus->busnr; + scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, false); + 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, bus; + 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"); - BusState *scsibus; dinfo = add_init_drive(opts); if (!dinfo) @@ -72,24 +121,21 @@ void drive_hot_add(Monitor *mon, const QDict *qdict) goto err; } type = dinfo->type; - bus = drive_get_max_bus (type); switch (type) { case IF_SCSI: if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { goto err; } - dev = pci_find_device(pci_bus, slot, 0); + dev = pci_find_device(pci_find_root_bus(dom), pci_bus, + PCI_DEVFN(slot, 0)); if (!dev) { monitor_printf(mon, "no pci device with address %s\n", pci_addr); goto err; } - scsibus = QLIST_FIRST(&dev->qdev.child_bus); - scsi_bus_legacy_add_drive(DO_UPCAST(SCSIBus, qbus, scsibus), - dinfo, dinfo->unit); - monitor_printf(mon, "OK bus %d, unit %d\n", - dinfo->bus, - dinfo->unit); + if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) { + goto err; + } break; case IF_NONE: monitor_printf(mon, "OK\n"); @@ -102,7 +148,7 @@ void drive_hot_add(Monitor *mon, const QDict *qdict) err: if (dinfo) - drive_uninit(dinfo); + drive_put_ref(dinfo); return; } @@ -114,6 +160,8 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, 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")) @@ -141,23 +189,45 @@ 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; } @@ -180,66 +250,43 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict) 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) { 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) +static 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, PCI_DEVFN(slot, 0)); if (!d) { monitor_printf(mon, "slot %d empty\n", slot); - return; + return -1; } - qdev_unplug(&d->qdev); + return qdev_unplug(&d->qdev); } void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict) { pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr")); } - -static int pci_match_fn(void *dev_private, void *arg) -{ - PCIDevice *dev = dev_private; - PCIDevice *match = arg; - - return (dev == match); -} - -/* - * OS has executed _EJ0 method, we now can remove the device - */ -void pci_device_hot_remove_success(PCIDevice *d) -{ - int class_code; - - class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1); - - switch(class_code) { - case PCI_BASE_CLASS_NETWORK: - destroy_nic(pci_match_fn, d); - break; - } -} -