diff options
Diffstat (limited to 'qemu/hw/pci-host/piix.c')
-rw-r--r-- | qemu/hw/pci-host/piix.c | 137 |
1 files changed, 120 insertions, 17 deletions
diff --git a/qemu/hw/pci-host/piix.c b/qemu/hw/pci-host/piix.c index ad55f9966..df2b0e26f 100644 --- a/qemu/hw/pci-host/piix.c +++ b/qemu/hw/pci-host/piix.c @@ -22,25 +22,27 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" +#include "qapi/error.h" #include "qemu/range.h" #include "hw/xen/xen.h" #include "hw/pci-host/pam.h" #include "sysemu/sysemu.h" #include "hw/i386/ioapic.h" #include "qapi/visitor.h" +#include "qemu/error-report.h" /* * I440FX chipset data sheet. * http://download.intel.com/design/chipsets/datashts/29054901.pdf */ -#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost" #define I440FX_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE) @@ -95,7 +97,6 @@ typedef struct PIIX3State { #define PIIX3_PCI_DEVICE(obj) \ OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE) -#define TYPE_I440FX_PCI_DEVICE "i440FX" #define I440FX_PCI_DEVICE(obj) \ OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) @@ -117,6 +118,11 @@ struct PCII440FXState { #define I440FX_PAM_SIZE 7 #define I440FX_SMRAM 0x72 +/* Older coreboot versions (4.0 and older) read a config register that doesn't + * exist in real hardware, to get the RAM size from QEMU. + */ +#define I440FX_COREBOOT_RAM_SIZE 0x57 + static void piix3_set_irq(void *opaque, int pirq, int level); static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); static void piix3_write_config_xen(PCIDevice *dev, @@ -211,39 +217,39 @@ static const VMStateDescription vmstate_i440fx = { }; static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); uint32_t value = s->pci_info.w32.begin; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); uint32_t value = s->pci_info.w32.end; - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, - void *opaque, const char *name, - Error **errp) + const char *name, + void *opaque, Error **errp) { PCIHostState *h = PCI_HOST_BRIDGE(obj); Range w64; pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &w64.begin, name, errp); + visit_type_uint64(v, name, &w64.begin, errp); } static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, - void *opaque, const char *name, + const char *name, void *opaque, Error **errp) { PCIHostState *h = PCI_HOST_BRIDGE(obj); @@ -251,7 +257,7 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, pci_bus_get_w64_range(h->bus, &w64); - visit_type_uint64(v, &w64.end, name, errp); + visit_type_uint64(v, name, &w64.end, errp); } static void i440fx_pcihost_initfn(Object *obj) @@ -298,9 +304,14 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) static void i440fx_realize(PCIDevice *dev, Error **errp) { dev->config[I440FX_SMRAM] = 0x02; + + if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) { + error_report("warning: i440fx doesn't support emulated iommu"); + } } -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, +PCIBus *i440fx_init(const char *host_type, const char *pci_type, + PCII440FXState **pi440fx_state, int *piix3_devfn, ISABus **isa_bus, qemu_irq *pic, MemoryRegion *address_space_mem, @@ -320,7 +331,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, unsigned i; I440FXState *i440fx; - dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE); + dev = qdev_create(NULL, host_type); s = PCI_HOST_BRIDGE(dev); b = pci_bus_new(dev, NULL, pci_address_space, address_space_io, 0, TYPE_PCI_BUS); @@ -328,7 +339,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL); qdev_init_nofail(dev); - d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE); + d = pci_create_simple(b, 0, pci_type); *pi440fx_state = I440FX_PCI_DEVICE(d); f = *pi440fx_state; f->system_memory = address_space_mem; @@ -394,7 +405,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, if (ram_size > 255) { ram_size = 255; } - d->config[0x57] = ram_size; + d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size; i440fx_update_memory_mappings(f); @@ -642,8 +653,10 @@ static void piix3_realize(PCIDevice *dev, Error **errp) { PIIX3State *d = PIIX3_PCI_DEVICE(dev); - isa_bus_new(DEVICE(d), get_system_memory(), - pci_address_space_io(dev)); + if (!isa_bus_new(DEVICE(d), get_system_memory(), + pci_address_space_io(dev), errp)) { + return; + } memory_region_init_io(&d->rcr_mem, OBJECT(dev), &rcr_ops, d, "piix3-reset-control", 1); @@ -735,6 +748,95 @@ static const TypeInfo i440fx_info = { .class_init = i440fx_class_init, }; +/* IGD Passthrough Host Bridge. */ +typedef struct { + uint8_t offset; + uint8_t len; +} IGDHostInfo; + +/* Here we just expose minimal host bridge offset subset. */ +static const IGDHostInfo igd_host_bridge_infos[] = { + {0x08, 2}, /* revision id */ + {0x2c, 2}, /* sybsystem vendor id */ + {0x2e, 2}, /* sybsystem id */ + {0x50, 2}, /* SNB: processor graphics control register */ + {0x52, 2}, /* processor graphics control register */ + {0xa4, 4}, /* SNB: graphics base of stolen memory */ + {0xa8, 4}, /* SNB: base of GTT stolen memory */ +}; + +static int host_pci_config_read(int pos, int len, uint32_t *val) +{ + char path[PATH_MAX]; + int config_fd; + ssize_t size = sizeof(path); + /* Access real host bridge. */ + int rc = snprintf(path, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", + 0, 0, 0, 0, "config"); + int ret = 0; + + if (rc >= size || rc < 0) { + return -ENODEV; + } + + config_fd = open(path, O_RDWR); + if (config_fd < 0) { + return -ENODEV; + } + + if (lseek(config_fd, pos, SEEK_SET) != pos) { + ret = -errno; + goto out; + } + + do { + rc = read(config_fd, (uint8_t *)val, len); + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc != len) { + ret = -errno; + } + +out: + close(config_fd); + return ret; +} + +static int igd_pt_i440fx_initfn(struct PCIDevice *pci_dev) +{ + uint32_t val = 0; + int rc, i, num; + int pos, len; + + num = ARRAY_SIZE(igd_host_bridge_infos); + for (i = 0; i < num; i++) { + pos = igd_host_bridge_infos[i].offset; + len = igd_host_bridge_infos[i].len; + rc = host_pci_config_read(pos, len, &val); + if (rc) { + return -ENODEV; + } + pci_default_write_config(pci_dev, pos, val, len); + } + + return 0; +} + +static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = igd_pt_i440fx_initfn; + dc->desc = "IGD Passthrough Host bridge"; +} + +static const TypeInfo igd_passthrough_i440fx_info = { + .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, + .parent = TYPE_I440FX_PCI_DEVICE, + .instance_size = sizeof(PCII440FXState), + .class_init = igd_passthrough_i440fx_class_init, +}; + static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { @@ -776,6 +878,7 @@ static const TypeInfo i440fx_pcihost_info = { static void i440fx_register_types(void) { type_register_static(&i440fx_info); + type_register_static(&igd_passthrough_i440fx_info); type_register_static(&piix3_pci_type_info); type_register_static(&piix3_info); type_register_static(&piix3_xen_info); |