summaryrefslogtreecommitdiffstats
path: root/qemu/hw/xen/xen_pt.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/hw/xen/xen_pt.c')
-rw-r--r--qemu/hw/xen/xen_pt.c253
1 files changed, 171 insertions, 82 deletions
diff --git a/qemu/hw/xen/xen_pt.c b/qemu/hw/xen/xen_pt.c
index ed5fcaec0..f593b046e 100644
--- a/qemu/hw/xen/xen_pt.c
+++ b/qemu/hw/xen/xen_pt.c
@@ -52,10 +52,13 @@
* - Set entry->pirq to '-1'.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include <sys/ioctl.h>
#include "hw/pci/pci.h"
#include "hw/xen/xen.h"
+#include "hw/i386/pc.h"
#include "hw/xen/xen_backend.h"
#include "xen_pt.h"
#include "qemu/range.h"
@@ -378,7 +381,7 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
}
}
- /* need to shift back before passing them to xen_host_pci_device */
+ /* need to shift back before passing them to xen_host_pci_set_block. */
val >>= (addr & 3) << 3;
memory_region_transaction_commit();
@@ -406,7 +409,7 @@ out:
(uint8_t *)&val + index, len);
if (rc < 0) {
- XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
+ XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc);
}
}
}
@@ -502,6 +505,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd)
d->rom.size, d->rom.base_addr);
}
+ xen_pt_register_vga_regions(d);
return 0;
}
@@ -683,15 +687,89 @@ static const MemoryListener xen_pt_io_listener = {
.priority = 10,
};
+static void
+xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s,
+ XenHostPCIDevice *dev)
+{
+ uint16_t gpu_dev_id;
+ PCIDevice *d = &s->dev;
+
+ gpu_dev_id = dev->device_id;
+ igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id);
+}
+
+/* destroy. */
+static void xen_pt_destroy(PCIDevice *d) {
+
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
+ XenHostPCIDevice *host_dev = &s->real_device;
+ uint8_t machine_irq = s->machine_irq;
+ uint8_t intx;
+ int rc;
+
+ if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) {
+ intx = xen_pt_pci_intx(s);
+ rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
+ PT_IRQ_TYPE_PCI,
+ pci_bus_num(d->bus),
+ PCI_SLOT(s->dev.devfn),
+ intx,
+ 0 /* isa_irq */);
+ if (rc < 0) {
+ XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
+ " (machine irq: %i, err: %d)"
+ " But bravely continuing on..\n",
+ 'a' + intx, machine_irq, errno);
+ }
+ }
+
+ /* N.B. xen_pt_config_delete takes care of freeing them. */
+ if (s->msi) {
+ xen_pt_msi_disable(s);
+ }
+ if (s->msix) {
+ xen_pt_msix_disable(s);
+ }
+
+ if (machine_irq) {
+ xen_pt_mapped_machine_irq[machine_irq]--;
+
+ if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
+ rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
+
+ if (rc < 0) {
+ XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
+ " But bravely continuing on..\n",
+ machine_irq, errno);
+ }
+ }
+ s->machine_irq = 0;
+ }
+
+ /* delete all emulated config registers */
+ xen_pt_config_delete(s);
+
+ xen_pt_unregister_vga_regions(host_dev);
+
+ if (s->listener_set) {
+ memory_listener_unregister(&s->memory_listener);
+ memory_listener_unregister(&s->io_listener);
+ s->listener_set = false;
+ }
+ if (!xen_host_pci_device_closed(&s->real_device)) {
+ xen_host_pci_device_put(&s->real_device);
+ }
+}
/* init */
-static int xen_pt_initfn(PCIDevice *d)
+static void xen_pt_realize(PCIDevice *d, Error **errp)
{
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
- int rc = 0;
- uint8_t machine_irq = 0;
+ int i, rc = 0;
+ uint8_t machine_irq = 0, scratch;
uint16_t cmd = 0;
int pirq = XEN_PT_UNASSIGNED_PIRQ;
+ Error *err = NULL;
/* register real device */
XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d"
@@ -699,12 +777,14 @@ static int xen_pt_initfn(PCIDevice *d)
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function,
s->dev.devfn);
- rc = xen_host_pci_device_get(&s->real_device,
- s->hostaddr.domain, s->hostaddr.bus,
- s->hostaddr.slot, s->hostaddr.function);
- if (rc) {
- XEN_PT_ERR(d, "Failed to \"open\" the real pci device. rc: %i\n", rc);
- return -1;
+ xen_host_pci_device_get(&s->real_device,
+ s->hostaddr.domain, s->hostaddr.bus,
+ s->hostaddr.slot, s->hostaddr.function,
+ &err);
+ if (err) {
+ error_append_hint(&err, "Failed to \"open\" the real pci device");
+ error_propagate(errp, err);
+ return;
}
s->is_virtfn = s->real_device.is_virtfn;
@@ -715,37 +795,62 @@ static int xen_pt_initfn(PCIDevice *d)
}
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
- if (xen_host_pci_get_block(&s->real_device, 0, d->config,
- PCI_CONFIG_SPACE_SIZE) == -1) {
- xen_host_pci_device_put(&s->real_device);
- return -1;
- }
+ memset(d->config, 0, PCI_CONFIG_SPACE_SIZE);
s->memory_listener = xen_pt_memory_listener;
s->io_listener = xen_pt_io_listener;
+ /* Setup VGA bios for passthrough GFX */
+ if ((s->real_device.domain == 0) && (s->real_device.bus == 0) &&
+ (s->real_device.dev == 2) && (s->real_device.func == 0)) {
+ if (!is_igd_vga_passthrough(&s->real_device)) {
+ error_setg(errp, "Need to enable igd-passthru if you're trying"
+ " to passthrough IGD GFX");
+ xen_host_pci_device_put(&s->real_device);
+ return;
+ }
+
+ xen_pt_setup_vga(s, &s->real_device, &err);
+ if (err) {
+ error_append_hint(&err, "Setup VGA BIOS of passthrough"
+ " GFX failed");
+ error_propagate(errp, err);
+ xen_host_pci_device_put(&s->real_device);
+ return;
+ }
+
+ /* Register ISA bridge for passthrough GFX. */
+ xen_igd_passthrough_isa_bridge_create(s, &s->real_device);
+ }
+
/* Handle real device's MMIO/PIO BARs */
xen_pt_register_regions(s, &cmd);
/* reinitialize each config register to be emulated */
- if (xen_pt_config_init(s)) {
- XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
- xen_host_pci_device_put(&s->real_device);
- return -1;
+ xen_pt_config_init(s, &err);
+ if (err) {
+ error_append_hint(&err, "PCI Config space initialisation failed");
+ error_report_err(err);
+ rc = -1;
+ goto err_out;
}
/* Bind interrupt */
- if (!s->dev.config[PCI_INTERRUPT_PIN]) {
- XEN_PT_LOG(d, "no pin interrupt\n");
+ rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch);
+ if (rc) {
+ error_setg_errno(errp, errno, "Failed to read PCI_INTERRUPT_PIN");
+ goto err_out;
+ }
+ if (!scratch) {
+ error_setg(errp, "no pin interrupt");
goto out;
}
machine_irq = s->real_device.irq;
rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq);
-
if (rc < 0) {
- XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (err: %d)\n",
- machine_irq, pirq, errno);
+ error_setg_errno(errp, errno, "Mapping machine irq %u to"
+ " pirq %i failed", machine_irq, pirq);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
cmd |= PCI_COMMAND_INTX_DISABLE;
@@ -766,8 +871,8 @@ static int xen_pt_initfn(PCIDevice *d)
PCI_SLOT(d->devfn),
e_intx);
if (rc < 0) {
- XEN_PT_ERR(d, "Binding of interrupt %i failed! (err: %d)\n",
- e_intx, errno);
+ error_setg_errno(errp, errno, "Binding of interrupt %u failed",
+ e_intx);
/* Disable PCI intx assertion (turn on bit10 of devctl) */
cmd |= PCI_COMMAND_INTX_DISABLE;
@@ -775,8 +880,8 @@ static int xen_pt_initfn(PCIDevice *d)
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
- XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
- " (err: %d)\n", machine_irq, errno);
+ error_setg_errno(errp, errno, "Unmapping of machine"
+ " interrupt %u failed", machine_irq);
}
}
s->machine_irq = 0;
@@ -785,69 +890,45 @@ static int xen_pt_initfn(PCIDevice *d)
out:
if (cmd) {
- xen_host_pci_set_word(&s->real_device, PCI_COMMAND,
- pci_get_word(d->config + PCI_COMMAND) | cmd);
+ uint16_t val;
+
+ rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val);
+ if (rc) {
+ error_setg_errno(errp, errno, "Failed to read PCI_COMMAND");
+ goto err_out;
+ } else {
+ val |= cmd;
+ rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val);
+ if (rc) {
+ error_setg_errno(errp, errno, "Failed to write PCI_COMMAND"
+ " val = 0x%x", val);
+ goto err_out;
+ }
+ }
}
memory_listener_register(&s->memory_listener, &s->dev.bus_master_as);
memory_listener_register(&s->io_listener, &address_space_io);
+ s->listener_set = true;
XEN_PT_LOG(d,
- "Real physical device %02x:%02x.%d registered successfully!\n",
+ "Real physical device %02x:%02x.%d registered successfully\n",
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
- return 0;
-}
-
-static void xen_pt_unregister_device(PCIDevice *d)
-{
- XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
- uint8_t machine_irq = s->machine_irq;
- uint8_t intx = xen_pt_pci_intx(s);
- int rc;
-
- if (machine_irq) {
- rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
- PT_IRQ_TYPE_PCI,
- pci_bus_num(d->bus),
- PCI_SLOT(s->dev.devfn),
- intx,
- 0 /* isa_irq */);
- if (rc < 0) {
- XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
- " (machine irq: %i, err: %d)"
- " But bravely continuing on..\n",
- 'a' + intx, machine_irq, errno);
- }
- }
-
- if (s->msi) {
- xen_pt_msi_disable(s);
- }
- if (s->msix) {
- xen_pt_msix_disable(s);
- }
-
- if (machine_irq) {
- xen_pt_mapped_machine_irq[machine_irq]--;
+ return;
- if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
- rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
-
- if (rc < 0) {
- XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
- " But bravely continuing on..\n",
- machine_irq, errno);
- }
- }
+err_out:
+ for (i = 0; i < PCI_ROM_SLOT; i++) {
+ object_unparent(OBJECT(&s->bar[i]));
}
+ object_unparent(OBJECT(&s->rom));
- /* delete all emulated config registers */
- xen_pt_config_delete(s);
-
- memory_listener_unregister(&s->memory_listener);
- memory_listener_unregister(&s->io_listener);
+ xen_pt_destroy(d);
+ assert(rc);
+}
- xen_host_pci_device_put(&s->real_device);
+static void xen_pt_unregister_device(PCIDevice *d)
+{
+ xen_pt_destroy(d);
}
static Property xen_pci_passthrough_properties[] = {
@@ -861,7 +942,7 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
- k->init = xen_pt_initfn;
+ k->realize = xen_pt_realize;
k->exit = xen_pt_unregister_device;
k->config_read = xen_pt_pci_read_config;
k->config_write = xen_pt_pci_write_config;
@@ -870,10 +951,18 @@ static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data)
dc->props = xen_pci_passthrough_properties;
};
+static void xen_pci_passthrough_finalize(Object *obj)
+{
+ XenPCIPassthroughState *s = XEN_PT_DEVICE(obj);
+
+ xen_pt_msix_delete(s);
+}
+
static const TypeInfo xen_pci_passthrough_info = {
.name = TYPE_XEN_PT_DEVICE,
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XenPCIPassthroughState),
+ .instance_finalize = xen_pci_passthrough_finalize,
.class_init = xen_pci_passthrough_class_init,
};