diff options
Diffstat (limited to 'hw/pci.c')
-rw-r--r-- | hw/pci.c | 322 |
1 files changed, 84 insertions, 238 deletions
@@ -23,6 +23,10 @@ */ #include "hw.h" #include "pci.h" +#include "pci_bridge.h" +#include "pci_internals.h" +#include "msix.h" +#include "msi.h" #include "monitor.h" #include "net.h" #include "sysemu.h" @@ -37,31 +41,10 @@ # define PCI_DPRINTF(format, ...) do { } while (0) #endif -struct PCIBus { - BusState qbus; - int devfn_min; - pci_set_irq_fn set_irq; - pci_map_irq_fn map_irq; - pci_hotplug_fn hotplug; - DeviceState *hotplug_qdev; - void *irq_opaque; - PCIDevice *devices[256]; - PCIDevice *parent_dev; - target_phys_addr_t mem_base; - - QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ - QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ - - /* The bus IRQ state is the logical OR of the connected devices. - Keep a count of the number of devices with raised IRQs. */ - int nirq; - int *irq_count; -}; - static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *pcibus_get_dev_path(DeviceState *dev); -static struct BusInfo pci_bus_info = { +struct BusInfo pci_bus_info = { .name = "PCI", .size = sizeof(PCIBus), .print_dev = pcibus_dev_print, @@ -157,9 +140,9 @@ static void pci_device_reset(PCIDevice *dev) dev->irq_state = 0; pci_update_irq_status(dev); /* Clear all writeable bits */ - pci_set_word(dev->config + PCI_COMMAND, - pci_get_word(dev->config + PCI_COMMAND) & - ~pci_get_word(dev->wmask + PCI_COMMAND)); + pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, + pci_get_word(dev->wmask + PCI_COMMAND) | + pci_get_word(dev->w1cmask + PCI_COMMAND)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; dev->config[PCI_INTERRUPT_LINE] = 0x0; for (r = 0; r < PCI_NUM_REGIONS; ++r) { @@ -293,26 +276,6 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, return bus; } -static void pci_register_secondary_bus(PCIBus *parent, - PCIBus *bus, - PCIDevice *dev, - pci_map_irq_fn map_irq, - const char *name) -{ - qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name); - bus->map_irq = map_irq; - bus->parent_dev = dev; - - QLIST_INIT(&bus->child); - QLIST_INSERT_HEAD(&parent->child, bus, sibling); -} - -static void pci_unregister_secondary_bus(PCIBus *bus) -{ - assert(QLIST_EMPTY(&bus->child)); - QLIST_REMOVE(bus, sibling); -} - int pci_bus_num(PCIBus *s) { if (!s->parent_dev) @@ -331,7 +294,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) qemu_get_buffer(f, config, size); for (i = 0; i < size; ++i) { - if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) { + if ((config[i] ^ s->config[i]) & + s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) { qemu_free(config); return -EINVAL; } @@ -464,15 +428,18 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev) } /* - * Parse [[<domain>:]<bus>:]<slot>, return -1 on error + * Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL + * [[<domain>:]<bus>:]<slot>.<func>, return -1 on error */ -static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp) +int pci_parse_devaddr(const char *addr, int *domp, int *busp, + unsigned int *slotp, unsigned int *funcp) { const char *p; char *e; unsigned long val; unsigned long dom = 0, bus = 0; - unsigned slot = 0; + unsigned int slot = 0; + unsigned int func = 0; p = addr; val = strtoul(p, &e, 16); @@ -494,11 +461,24 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s } } - if (dom > 0xffff || bus > 0xff || val > 0x1f) - return -1; - slot = val; + if (funcp != NULL) { + if (*e != '.') + return -1; + + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + + func = val; + } + + /* if funcp == NULL func is 0 */ + if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) + return -1; + if (*e) return -1; @@ -509,6 +489,8 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s *domp = dom; *busp = bus; *slotp = slot; + if (funcp != NULL) + *funcp = func; return 0; } @@ -519,7 +501,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, if (!strncmp(addr, "pci_addr=", 9)) { addr += 9; } - if (pci_parse_devaddr(addr, domp, busp, slotp)) { + if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) { monitor_printf(mon, "Invalid pci address\n"); return -1; } @@ -536,7 +518,7 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr) return pci_find_bus(pci_find_root_bus(0), 0); } - if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) { + if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) { return NULL; } @@ -649,6 +631,7 @@ static void pci_config_alloc(PCIDevice *pci_dev) pci_dev->config = qemu_mallocz(config_size); pci_dev->cmask = qemu_mallocz(config_size); pci_dev->wmask = qemu_mallocz(config_size); + pci_dev->w1cmask = qemu_mallocz(config_size); pci_dev->used = qemu_mallocz(config_size); } @@ -657,6 +640,7 @@ static void pci_config_free(PCIDevice *pci_dev) qemu_free(pci_dev->config); qemu_free(pci_dev->cmask); qemu_free(pci_dev->wmask); + qemu_free(pci_dev->w1cmask); qemu_free(pci_dev->used); } @@ -780,16 +764,15 @@ static int pci_unregister_device(DeviceState *dev) } void pci_register_bar(PCIDevice *pci_dev, int region_num, - pcibus_t size, int type, + pcibus_t size, uint8_t type, PCIMapIORegionFunc *map_func) { PCIIORegion *r; uint32_t addr; - pcibus_t wmask; - - if ((unsigned int)region_num >= PCI_NUM_REGIONS) - return; + uint64_t wmask; + assert(region_num >= 0); + assert(region_num < PCI_NUM_REGIONS); if (size & (size-1)) { fprintf(stderr, "ERROR: PCI region size must be pow2 " "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size); @@ -820,75 +803,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, } } -static uint32_t pci_config_get_io_base(PCIDevice *d, - uint32_t base, uint32_t base_upper16) -{ - uint32_t val; - - val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; - if (d->config[base] & PCI_IO_RANGE_TYPE_32) { - val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; - } - return val; -} - -static pcibus_t pci_config_get_memory_base(PCIDevice *d, uint32_t base) -{ - return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) - << 16; -} - -static pcibus_t pci_config_get_pref_base(PCIDevice *d, - uint32_t base, uint32_t upper) -{ - pcibus_t tmp; - pcibus_t val; - - tmp = (pcibus_t)pci_get_word(d->config + base); - val = (tmp & PCI_PREF_RANGE_MASK) << 16; - if (tmp & PCI_PREF_RANGE_TYPE_64) { - val |= (pcibus_t)pci_get_long(d->config + upper) << 32; - } - return val; -} - -static pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type) -{ - pcibus_t base; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - base = pci_config_get_io_base(bridge, - PCI_IO_BASE, PCI_IO_BASE_UPPER16); - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - base = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32); - } else { - base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); - } - } - - return base; -} - -static pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type) -{ - pcibus_t limit; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - limit = pci_config_get_io_base(bridge, - PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16); - limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */ - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - limit = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32); - } else { - limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT); - } - limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */ - } - return limit; -} - static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size, uint8_t type) { @@ -1089,7 +1003,10 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) { uint8_t wmask = d->wmask[addr + i]; + uint8_t w1cmask = d->w1cmask[addr + i]; + assert(!(wmask & w1cmask)); d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); + d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ } if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) || ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) || @@ -1121,6 +1038,23 @@ static void pci_set_irq(void *opaque, int irq_num, int level) pci_change_irq_level(pci_dev, irq_num, change); } +bool pci_msi_enabled(PCIDevice *dev) +{ + return msix_enabled(dev) || msi_enabled(dev); +} + +void pci_msi_notify(PCIDevice *dev, unsigned int vector) +{ + if (msix_enabled(dev)) { + msix_notify(dev, vector); + } else if (msi_enabled(dev)) { + msi_notify(dev, vector); + } else { + /* MSI/MSI-X must be enabled */ + abort(); + } +} + /***********************************************************/ /* monitor info on PCI */ @@ -1534,20 +1468,12 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model, return res; } -typedef struct { - PCIDevice dev; - PCIBus bus; - uint32_t vid; - uint32_t did; -} PCIBridge; - - static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d) { pci_update_mappings(d); } -static void pci_bridge_update_mappings(PCIBus *b) +void pci_bridge_update_mappings(PCIBus *b) { PCIBus *child; @@ -1558,23 +1484,6 @@ static void pci_bridge_update_mappings(PCIBus *b) } } -static void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(d, address, val, len); - - if (/* io base/limit */ - ranges_overlap(address, len, PCI_IO_BASE, 2) || - - /* memory base/limit, prefetchable base/limit and - io base/limit upper 16 */ - ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { - PCIBridge *s = container_of(d, PCIBridge, dev); - PCIBus *secondary_bus = &s->bus; - pci_bridge_update_mappings(secondary_bus); - } -} - PCIBus *pci_find_bus(PCIBus *bus, int bus_num) { PCIBus *sec; @@ -1618,54 +1527,6 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function) return bus->devices[PCI_DEVFN(slot, function)]; } -static int pci_bridge_initfn(PCIDevice *dev) -{ - PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev); - - pci_config_set_vendor_id(s->dev.config, s->vid); - pci_config_set_device_id(s->dev.config, s->did); - - pci_set_word(dev->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); - dev->config[PCI_HEADER_TYPE] = - (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | - PCI_HEADER_TYPE_BRIDGE; - pci_set_word(dev->config + PCI_SEC_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - return 0; -} - -static int pci_bridge_exitfn(PCIDevice *pci_dev) -{ - PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); - PCIBus *bus = &s->bus; - pci_unregister_secondary_bus(bus); - return 0; -} - -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction, - uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name) -{ - PCIDevice *dev; - PCIBridge *s; - - dev = pci_create_multifunction(bus, devfn, multifunction, "pci-bridge"); - qdev_prop_set_uint32(&dev->qdev, "vendorid", vid); - qdev_prop_set_uint32(&dev->qdev, "deviceid", did); - qdev_init_nofail(&dev->qdev); - - s = DO_UPCAST(PCIBridge, dev, dev); - pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name); - return &s->bus; -} - -PCIDevice *pci_bridge_get_device(PCIBus *bus) -{ - return bus->parent_dev; -} - static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) { PCIDevice *pci_dev = (PCIDevice *)qdev; @@ -1696,7 +1557,8 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) pci_dev->romfile = qemu_strdup(info->romfile); pci_add_option_rom(pci_dev); - if (qdev->hotplugged) { + if (bus->hotplug) { + /* lower layer must check qdev->hotplugged */ rc = bus->hotplug(bus->hotplug_qdev, pci_dev, 1); if (rc != 0) { int r = pci_unregister_device(&pci_dev->qdev); @@ -1864,11 +1726,25 @@ static void pci_del_option_rom(PCIDevice *pdev) pdev->rom_offset = 0; } -/* Reserve space and add capability to the linked list in pci config space */ -int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id, - uint8_t offset, uint8_t size) +/* + * if !offset + * Reserve space and add capability to the linked list in pci config space + * + * if offset = 0, + * Find and reserve space and add capability to the linked list + * in pci config space */ +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, + uint8_t offset, uint8_t size) { - uint8_t *config = pdev->config + offset; + uint8_t *config; + if (!offset) { + offset = pci_find_space(pdev, size); + if (!offset) { + return -ENOSPC; + } + } + + config = pdev->config + offset; config[PCI_CAP_LIST_ID] = cap_id; config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; pdev->config[PCI_CAPABILITY_LIST] = offset; @@ -1881,17 +1757,6 @@ int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id, return offset; } -/* Find and reserve space and add capability to the linked list - * in pci config space */ -int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) -{ - uint8_t offset = pci_find_space(pdev, size); - if (!offset) { - return -ENOSPC; - } - return pci_add_capability_at_offset(pdev, cap_id, offset, size); -} - /* Unlink capability from the pci config space. */ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) { @@ -1901,6 +1766,7 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; /* Make capability writeable again */ memset(pdev->wmask + offset, 0xff, size); + memset(pdev->w1cmask + offset, 0, size); /* Clear cmask as device-specific registers can't be checked */ memset(pdev->cmask + offset, 0, size); memset(pdev->used + offset, 0, size); @@ -1971,23 +1837,3 @@ static char *pcibus_get_dev_path(DeviceState *dev) return strdup(path); } -static PCIDeviceInfo bridge_info = { - .qdev.name = "pci-bridge", - .qdev.size = sizeof(PCIBridge), - .init = pci_bridge_initfn, - .exit = pci_bridge_exitfn, - .config_write = pci_bridge_write_config, - .is_bridge = 1, - .qdev.props = (Property[]) { - DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0), - DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void pci_register_devices(void) -{ - pci_qdev_register(&bridge_info); -} - -device_init(pci_register_devices) |