From: Gerd Hoffmann <[email protected]>
Also cleanup while being at it. Changes:
* Converted driver to qdev, named it 'pci-assign'.
* Killed the pointless AssignedDevInfo struct.
* Killed a bunch of hooks which are not needed any more now
that qdev can handle hotplug.
qdev way of doing device assignment is:
-device pci-assign,host=<hostaddr>,addr=<guestaddr>,id=<name>
Hotplug via monitor:
device_add $same_syntax_as_above
device_del $id
Old command line + monitor syntax continues to work. Mixing
pci_* and device_* monitor commands works too.
Signed-off-by: Gerd Hoffmann <[email protected]>
Signed-off-by: Avi Kivity <[email protected]>
diff --git a/hw/device-assignment.c b/hw/device-assignment.c
index 1f86dd9..237060f 100644
--- a/hw/device-assignment.c
+++ b/hw/device-assignment.c
@@ -554,7 +554,7 @@ again:
return 0;
}
-static QLIST_HEAD(, AssignedDevInfo) adev_head;
+static QLIST_HEAD(, AssignedDevice) devs = QLIST_HEAD_INITIALIZER(devs);
#ifdef KVM_CAP_IRQ_ROUTING
static void free_dev_irq_entries(AssignedDevice *dev)
@@ -569,10 +569,8 @@ static void free_dev_irq_entries(AssignedDevice *dev)
}
#endif
-static void free_assigned_device(AssignedDevInfo *adev)
+static void free_assigned_device(AssignedDevice *dev)
{
- AssignedDevice *dev = adev->assigned_dev;
-
if (dev) {
int i;
@@ -607,16 +605,10 @@ static void free_assigned_device(AssignedDevInfo *adev)
dev->real_device.config_fd = 0;
}
- printf("warning: assigned device hotunplug is broken\n");
- //pci_unregister_device(&dev->dev, 1);
#ifdef KVM_CAP_IRQ_ROUTING
free_dev_irq_entries(dev);
#endif
- adev->assigned_dev = dev = NULL;
}
-
- QLIST_REMOVE(adev, next);
- qemu_free(adev);
}
static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t devfn)
@@ -624,10 +616,9 @@ static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t
devfn)
return (uint32_t)bus << 8 | (uint32_t)devfn;
}
-static int assign_device(AssignedDevInfo *adev)
+static int assign_device(AssignedDevice *dev)
{
struct kvm_assigned_pci_dev assigned_dev_data;
- AssignedDevice *dev = adev->assigned_dev;
int r;
memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
@@ -641,21 +632,24 @@ static int assign_device(AssignedDevInfo *adev)
* (or when not disabled on the command line)
*/
r = kvm_check_extension(kvm_state, KVM_CAP_IOMMU);
- if (r && !adev->disable_iommu)
+ if (!r)
+ dev->use_iommu = 0;
+ if (dev->use_iommu)
assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU;
+#else
+ dev->use_iommu = 0;
#endif
r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
if (r < 0)
fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
- adev->name, strerror(-r));
+ dev->dev.qdev.id, strerror(-r));
return r;
}
-static int assign_irq(AssignedDevInfo *adev)
+static int assign_irq(AssignedDevice *dev)
{
struct kvm_assigned_irq assigned_irq_data;
- AssignedDevice *dev = adev->assigned_dev;
int irq, r = 0;
/* Interrupt PIN 0 means don't use INTx */
@@ -696,7 +690,7 @@ static int assign_irq(AssignedDevInfo *adev)
r = kvm_assign_irq(kvm_context, &assigned_irq_data);
if (r < 0) {
fprintf(stderr, "Failed to assign irq for \"%s\": %s\n",
- adev->name, strerror(-r));
+ dev->dev.qdev.id, strerror(-r));
fprintf(stderr, "Perhaps you are assigning a device "
"that shares an IRQ with another device?\n");
return r;
@@ -707,11 +701,10 @@ static int assign_irq(AssignedDevInfo *adev)
return r;
}
-static void deassign_device(AssignedDevInfo *adev)
+static void deassign_device(AssignedDevice *dev)
{
#ifdef KVM_CAP_DEVICE_DEASSIGNMENT
struct kvm_assigned_pci_dev assigned_dev_data;
- AssignedDevice *dev = adev->assigned_dev;
int r;
memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
@@ -721,16 +714,11 @@ static void deassign_device(AssignedDevInfo *adev)
r = kvm_deassign_pci_device(kvm_context, &assigned_dev_data);
if (r < 0)
fprintf(stderr, "Failed to deassign device \"%s\" : %s\n",
- adev->name, strerror(-r));
+ dev->dev.qdev.id, strerror(-r));
#endif
}
-void remove_assigned_device(AssignedDevInfo *adev)
-{
- deassign_device(adev);
- free_assigned_device(adev);
-}
-
+#if 0
AssignedDevInfo *get_assigned_device(int pcibus, int slot)
{
AssignedDevice *assigned_dev = NULL;
@@ -745,24 +733,23 @@ AssignedDevInfo *get_assigned_device(int pcibus, int slot)
return NULL;
}
+#endif
/* The pci config space got updated. Check if irq numbers have changed
* for our devices
*/
void assigned_dev_update_irqs(void)
{
- AssignedDevInfo *adev;
-
- adev = QLIST_FIRST(&adev_head);
- while (adev) {
- AssignedDevInfo *next = QLIST_NEXT(adev, next);
- int r;
+ AssignedDevice *dev, *next;
+ int r;
- r = assign_irq(adev);
+ dev = QLIST_FIRST(&devs);
+ while (dev) {
+ next = QLIST_NEXT(dev, next);
+ r = assign_irq(dev);
if (r < 0)
- remove_assigned_device(adev);
-
- adev = next;
+ qdev_unplug(&dev->dev.qdev);
+ dev = next;
}
}
@@ -1124,37 +1111,21 @@ static int
assigned_dev_register_msix_mmio(AssignedDevice *dev)
return 0;
}
-struct PCIDevice *init_assigned_device(AssignedDevInfo *adev,
- const char *devaddr)
+static int assigned_initfn(struct PCIDevice *pci_dev)
{
- PCIBus *bus;
- int devfn;
- int r;
- AssignedDevice *dev;
- PCIDevice *pci_dev;
+ AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
struct pci_access *pacc;
uint8_t e_device, e_intx;
+ int r;
- DEBUG("Registering real physical device %s (bus=%x dev=%x func=%x)\n",
- adev->name, adev->bus, adev->dev, adev->func);
-
- bus = pci_get_bus_devfn(&devfn, devaddr);
- pci_dev = pci_register_device(bus, adev->name,
- sizeof(AssignedDevice), devfn, assigned_dev_pci_read_config,
- assigned_dev_pci_write_config);
- dev = container_of(pci_dev, AssignedDevice, dev);
-
- if (NULL == dev) {
- fprintf(stderr, "%s: Error: Couldn't register real device %s\n",
- __func__, adev->name);
- return NULL;
+ if (!dev->host.bus && !dev->host.dev && !dev->host.func) {
+ qemu_error("pci-assign: error: no host device specified\n");
+ goto out;
}
- adev->assigned_dev = dev;
-
- if (get_real_device(dev, adev->bus, adev->dev, adev->func)) {
- fprintf(stderr, "%s: Error: Couldn't get real device (%s)!\n",
- __func__, adev->name);
+ if (get_real_device(dev, dev->host.bus, dev->host.dev, dev->host.func)) {
+ qemu_error("pci-assign: Error: Couldn't get real device (%s)!\n",
+ dev->dev.qdev.id);
goto out;
}
@@ -1170,12 +1141,12 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo
*adev,
dev->intpin = e_intx;
dev->run = 0;
dev->girq = 0;
- dev->h_busnr = adev->bus;
- dev->h_devfn = PCI_DEVFN(adev->dev, adev->func);
+ dev->h_busnr = dev->host.bus;
+ dev->h_devfn = PCI_DEVFN(dev->host.dev, dev->host.func);
pacc = pci_alloc();
pci_init(pacc);
- dev->pdev = pci_get_dev(pacc, 0, adev->bus, adev->dev, adev->func);
+ dev->pdev = pci_get_dev(pacc, 0, dev->host.bus, dev->host.dev,
dev->host.func);
if (pci_enable_capability_support(pci_dev, 0, NULL,
assigned_device_pci_cap_write_config,
@@ -1183,29 +1154,87 @@ struct PCIDevice *init_assigned_device(AssignedDevInfo
*adev,
goto assigned_out;
/* assign device to guest */
- r = assign_device(adev);
+ r = assign_device(dev);
if (r < 0)
goto assigned_out;
/* assign irq for the device */
- r = assign_irq(adev);
+ r = assign_irq(dev);
if (r < 0)
goto assigned_out;
/* intercept MSI-X entry page in the MMIO */
if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX)
if (assigned_dev_register_msix_mmio(dev))
- return NULL;
+ goto assigned_out;
- return &dev->dev;
+ return 0;
assigned_out:
- deassign_device(adev);
+ deassign_device(dev);
out:
- free_assigned_device(adev);
- return NULL;
+ free_assigned_device(dev);
+ return -1;
+}
+
+static int assigned_exitfn(struct PCIDevice *pci_dev)
+{
+ AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
+
+ deassign_device(dev);
+ free_assigned_device(dev);
+ return 0;
}
+static int parse_hostaddr(DeviceState *dev, Property *prop, const char *str)
+{
+ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop);
+ int rc;
+
+ rc = pci_parse_host_devaddr(str, &ptr->bus, &ptr->dev, &ptr->func);
+ if (rc != 0)
+ return -1;
+ return 0;
+}
+
+static int print_hostaddr(DeviceState *dev, Property *prop, char *dest, size_t
len)
+{
+ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop);
+
+ return snprintf(dest, len, "%02x:%02x.%x", ptr->bus, ptr->dev, ptr->func);
+}
+
+PropertyInfo qdev_prop_hostaddr = {
+ .name = "pci-hostaddr",
+ .type = -1,
+ .size = sizeof(PCIHostDevice),
+ .parse = parse_hostaddr,
+ .print = print_hostaddr,
+};
+
+static PCIDeviceInfo assign_info = {
+ .qdev.name = "pci-assign",
+ .qdev.desc = "pass through host pci devices to the guest",
+ .qdev.size = sizeof(AssignedDevice),
+ .init = assigned_initfn,
+ .exit = assigned_exitfn,
+ .config_read = assigned_dev_pci_read_config,
+ .config_write = assigned_dev_pci_write_config,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP("host", AssignedDevice, host, qdev_prop_hostaddr,
PCIHostDevice),
+ DEFINE_PROP_UINT32("iommu", AssignedDevice, use_iommu, 1),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void assign_register_devices(void)
+{
+ pci_qdev_register(&assign_info);
+}
+
+device_init(assign_register_devices)
+
+
/*
* Syntax to assign device:
*
@@ -1216,63 +1245,55 @@ out:
*
* dma can currently only be 'none' to disable iommu support.
*/
-AssignedDevInfo *add_assigned_device(const char *arg)
+QemuOpts *add_assigned_device(const char *arg)
{
- char device[16];
- char dma[6];
+ QemuOpts *opts = NULL;
+ char host[64], id[64], dma[8];
int r;
- AssignedDevInfo *adev;
- adev = qemu_mallocz(sizeof(AssignedDevInfo));
- if (adev == NULL) {
- fprintf(stderr, "%s: Out of memory\n", __func__);
- return NULL;
- }
- r = get_param_value(device, sizeof(device), "host", arg);
+ r = get_param_value(host, sizeof(host), "host", arg);
if (!r)
goto bad;
+ r = get_param_value(id, sizeof(id), "id", arg);
+ if (!r)
+ r = get_param_value(id, sizeof(id), "name", arg);
+ if (!r)
+ r = get_param_value(id, sizeof(id), "host", arg);
- r = pci_parse_host_devaddr(device, &adev->bus, &adev->dev, &adev->func);
- if (r)
+ opts = qemu_opts_create(&qemu_device_opts, id, 0);
+ if (!opts)
goto bad;
-
- r = get_param_value(adev->name, sizeof(adev->name), "name", arg);
- if (!r)
- snprintf(adev->name, sizeof(adev->name), "%s", device);
+ qemu_opt_set(opts, "driver", "pci-assign");
+ qemu_opt_set(opts, "host", host);
#ifdef KVM_CAP_IOMMU
r = get_param_value(dma, sizeof(dma), "dma", arg);
if (r && !strncmp(dma, "none", 4))
- adev->disable_iommu = 1;
+ qemu_opt_set(opts, "iommu", "0");
#endif
+ qemu_opts_print(opts, NULL);
+ return opts;
- QLIST_INSERT_HEAD(&adev_head, adev, next);
- return adev;
bad:
fprintf(stderr, "pcidevice argument parse error; "
"please check the help text for usage\n");
- qemu_free(adev);
+ if (opts)
+ qemu_opts_del(opts);
return NULL;
}
void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices)
{
+ QemuOpts *opts;
int i;
for (i = 0; i < n_devices; i++) {
- struct AssignedDevInfo *adev;
-
- adev = add_assigned_device(devices[i]);
- if (!adev) {
+ opts = add_assigned_device(devices[i]);
+ if (opts == NULL) {
fprintf(stderr, "Could not add assigned device %s\n", devices[i]);
exit(1);
}
-
- if (!init_assigned_device(adev, NULL)) {
- fprintf(stderr, "Failed to initialize assigned device %s\n",
- devices[i]);
- exit(1);
- }
+ /* generic code will call qdev_device_add() for the device */
}
}
@@ -1374,9 +1395,9 @@ static int scan_option_rom(uint8_t devfn, void *roms,
ram_addr_t offset)
ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset)
{
ram_addr_t offset = rom_base_offset;
- AssignedDevInfo *adev;
+ AssignedDevice *dev;
- QLIST_FOREACH(adev, &adev_head, next) {
+ QLIST_FOREACH(dev, &devs, next) {
int size, len;
void *buf;
FILE *fp;
@@ -1385,7 +1406,7 @@ ram_addr_t assigned_dev_load_option_roms(ram_addr_t
rom_base_offset)
snprintf(rom_file, sizeof(rom_file),
"/sys/bus/pci/devices/0000:%02x:%02x.%01x/rom",
- adev->bus, adev->dev, adev->func);
+ dev->host.bus, dev->host.dev, dev->host.func);
if (access(rom_file, F_OK))
continue;
@@ -1422,18 +1443,20 @@ ram_addr_t assigned_dev_load_option_roms(ram_addr_t
rom_base_offset)
}
/* Copy ROM contents into the space backing the ROM BAR */
- if (adev->assigned_dev->v_addrs[PCI_ROM_SLOT].r_size >= size &&
- adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase) {
- mprotect(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase,
+ if (dev->v_addrs[PCI_ROM_SLOT].r_size >= size &&
+ dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase) {
+ mprotect(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase,
size, PROT_READ | PROT_WRITE);
- memcpy(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase,
+ memcpy(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase,
buf, size);
- mprotect(adev->assigned_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase,
+ mprotect(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase,
size, PROT_READ);
}
- /* Scan the buffer for suitable ROMs and increase the offset */
- offset += scan_option_rom(adev->assigned_dev->dev.devfn, buf, offset);
+ if (!dev->dev.qdev.hotplugged) {
+ /* Scan the buffer for suitable ROMs and increase the offset */
+ offset += scan_option_rom(dev->dev.devfn, buf, offset);
+ }
free(buf);
fclose(fp);
diff --git a/hw/device-assignment.h b/hw/device-assignment.h
index a7b34e8..ef92474 100644
--- a/hw/device-assignment.h
+++ b/hw/device-assignment.h
@@ -36,6 +36,12 @@
/* From include/linux/pci.h in the kernel sources */
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+typedef struct PCIHostDevice {
+ int bus;
+ int dev;
+ int func;
+} PCIHostDevice;
+
typedef struct {
int type; /* Memory or port I/O */
int valid;
@@ -66,8 +72,10 @@ typedef struct {
uint32_t r_size; /* real size of region in bytes */
} AssignedDevRegion;
-typedef struct {
+typedef struct AssignedDevice {
PCIDevice dev;
+ PCIHostDevice host;
+ uint32_t use_iommu;
int intpin;
uint8_t debug_flags;
AssignedDevRegion v_addrs[PCI_NUM_REGIONS];
@@ -94,25 +102,11 @@ typedef struct {
target_phys_addr_t msix_table_addr;
int mmio_index;
int need_emulate_cmd;
+ QLIST_ENTRY(AssignedDevice) next;
} AssignedDevice;
-typedef struct AssignedDevInfo AssignedDevInfo;
-
-struct AssignedDevInfo {
- char name[15];
- int bus;
- int dev;
- int func;
- AssignedDevice *assigned_dev;
- QLIST_ENTRY(AssignedDevInfo) next;
- int disable_iommu;
-};
-
-PCIDevice *init_assigned_device(AssignedDevInfo *adev, const char *devaddr);
-AssignedDevInfo *add_assigned_device(const char *arg);
+QemuOpts *add_assigned_device(const char *arg);
void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices);
-void remove_assigned_device(AssignedDevInfo *adev);
-AssignedDevInfo *get_assigned_device(int pcibus, int slot);
ram_addr_t assigned_dev_load_option_roms(ram_addr_t rom_base_offset);
void assigned_dev_update_irqs(void);
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index 4a6b12c..81b9ff9 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -173,39 +173,18 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
static PCIDevice *qemu_pci_hot_assign_device(Monitor *mon,
const char *devaddr,
- const char *opts)
+ const char *opts_str)
{
- AssignedDevInfo *adev;
- PCIDevice *ret;
+ QemuOpts *opts;
+ DeviceState *dev;
- adev = add_assigned_device(opts);
- if (adev == NULL) {
+ opts = add_assigned_device(opts_str);
+ if (opts == NULL) {
monitor_printf(mon, "Error adding device; check syntax\n");
return NULL;
}
-
- ret = init_assigned_device(adev, devaddr);
- if (ret == NULL) {
- monitor_printf(mon, "Failed to assign device\n");
- return NULL;
- }
-
- monitor_printf(mon,
- "Registered host PCI device %02x:%02x.%1x "
- "(\"%s\") as guest device %s\n",
- adev->bus, adev->dev, adev->func, adev->name, devaddr);
-
- return ret;
-}
-
-static void qemu_pci_hot_deassign_device(Monitor *mon, AssignedDevInfo *adev)
-{
- remove_assigned_device(adev);
-
- monitor_printf(mon,
- "Unregister host PCI device %02x:%02x.%1x "
- "(\"%s\") from guest\n",
- adev->bus, adev->dev, adev->func, adev->name);
+ dev = qdev_device_add(opts);
+ return DO_UPCAST(PCIDevice, qdev, dev);
}
#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */
@@ -285,17 +264,6 @@ static int pci_match_fn(void *dev_private, void *arg)
void pci_device_hot_remove_success(PCIDevice *d)
{
int class_code;
-#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
- AssignedDevInfo *adev;
-#endif
-
-#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
- adev = get_assigned_device(pci_bus_num(d->bus), d->devfn >> 3);
- if (adev) {
- qemu_pci_hot_deassign_device(cur_mon, adev);
- return;
- }
-#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */
class_code = d->config_read(d, PCI_CLASS_DEVICE+1, 1);
@@ -305,4 +273,3 @@ void pci_device_hot_remove_success(PCIDevice *d)
break;
}
}
-
--
To unsubscribe from this list: send the line "unsubscribe kvm-commits" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html