]> Git Repo - qemu.git/blobdiff - hw/pci.c
pci: allow loading roms via fw_cfg.
[qemu.git] / hw / pci.c
index b43f6c668b14c1b55ec0213c1219ca1c15490bc1..2bdf27e04d787f7b05f8011d1ad93a18f5152b4e 100644 (file)
--- a/hw/pci.c
+++ b/hw/pci.c
  */
 #include "hw.h"
 #include "pci.h"
-#include "pci_host.h"
 #include "monitor.h"
 #include "net.h"
 #include "sysemu.h"
+#include "loader.h"
 
 //#define DEBUG_PCI
 #ifdef DEBUG_PCI
@@ -41,10 +41,10 @@ struct PCIBus {
     pci_set_irq_fn set_irq;
     pci_map_irq_fn map_irq;
     pci_hotplug_fn hotplug;
-    uint32_t config_reg; /* XXX: suppress */
     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 */
@@ -63,14 +63,16 @@ static struct BusInfo pci_bus_info = {
     .print_dev  = pcibus_dev_print,
     .props      = (Property[]) {
         DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
+        DEFINE_PROP_STRING("romfile", PCIDevice, romfile),
+        DEFINE_PROP_UINT32("rombar",  PCIDevice, rom_bar, 1),
         DEFINE_PROP_END_OF_LIST()
     }
 };
 
 static void pci_update_mappings(PCIDevice *d);
 static void pci_set_irq(void *opaque, int irq_num, int level);
+static int pci_add_option_rom(PCIDevice *pdev);
 
-target_phys_addr_t pci_mem_base;
 static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET;
 static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU;
 
@@ -104,11 +106,48 @@ static int pci_bar(PCIDevice *d, int reg)
     return type == PCI_HEADER_TYPE_BRIDGE ? PCI_ROM_ADDRESS1 : PCI_ROM_ADDRESS;
 }
 
+static inline int pci_irq_state(PCIDevice *d, int irq_num)
+{
+       return (d->irq_state >> irq_num) & 0x1;
+}
+
+static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level)
+{
+       d->irq_state &= ~(0x1 << irq_num);
+       d->irq_state |= level << irq_num;
+}
+
+static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change)
+{
+    PCIBus *bus;
+    for (;;) {
+        bus = pci_dev->bus;
+        irq_num = bus->map_irq(pci_dev, irq_num);
+        if (bus->set_irq)
+            break;
+        pci_dev = bus->parent_dev;
+    }
+    bus->irq_count[irq_num] += change;
+    bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
+}
+
+/* Update interrupt status bit in config space on interrupt
+ * state change. */
+static void pci_update_irq_status(PCIDevice *dev)
+{
+    if (dev->irq_state) {
+        dev->config[PCI_STATUS] |= PCI_STATUS_INTERRUPT;
+    } else {
+        dev->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
+    }
+}
+
 static void pci_device_reset(PCIDevice *dev)
 {
     int r;
 
-    memset(dev->irq_state, 0, sizeof dev->irq_state);
+    dev->irq_state = 0;
+    pci_update_irq_status(dev);
     dev->config[PCI_COMMAND] &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
                                   PCI_COMMAND_MASTER);
     dev->config[PCI_CACHE_LINE_SIZE] = 0x0;
@@ -146,7 +185,7 @@ static void pci_host_bus_register(int domain, PCIBus *bus)
     QLIST_INSERT_HEAD(&host_buses, host, next);
 }
 
-PCIBus *pci_find_host_bus(int domain)
+PCIBus *pci_find_root_bus(int domain)
 {
     struct PCIHostBus *host;
 
@@ -162,8 +201,6 @@ PCIBus *pci_find_host_bus(int domain)
 void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
                          const char *name, int devfn_min)
 {
-    static int nbus = 0;
-
     qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name);
     bus->devfn_min = devfn_min;
 
@@ -171,7 +208,7 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
     QLIST_INIT(&bus->child);
     pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */
 
-    vmstate_register(nbus++, &vmstate_pcibus, bus);
+    vmstate_register(-1, &vmstate_pcibus, bus);
     qemu_register_reset(pci_bus_reset, bus);
 }
 
@@ -201,6 +238,11 @@ void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug)
     bus->hotplug = hotplug;
 }
 
+void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base)
+{
+    bus->mem_base = base;
+}
+
 PCIBus *pci_register_bus(DeviceState *parent, const char *name,
                          pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
                          void *irq_opaque, int devfn_min, int nirq)
@@ -239,13 +281,6 @@ int pci_bus_num(PCIBus *s)
     return s->parent_dev->config[PCI_SECONDARY_BUS];
 }
 
-static uint8_t pci_sub_bus(PCIBus *s)
-{
-    if (!s->parent_dev)
-        return 255;     /* pci host bridge */
-    return s->parent_dev->config[PCI_SUBORDINATE_BUS];
-}
-
 static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
 {
     PCIDevice *s = container_of(pv, PCIDevice, config);
@@ -273,9 +308,9 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
 /* just put buffer */
 static void put_pci_config_device(QEMUFile *f, void *pv, size_t size)
 {
-    const uint8_t *v = pv;
+    const uint8_t **v = pv;
     assert(size == pci_config_size(container_of(pv, PCIDevice, config)));
-    qemu_put_buffer(f, v, size);
+    qemu_put_buffer(f, *v, size);
 }
 
 static VMStateInfo vmstate_info_pci_config = {
@@ -284,6 +319,43 @@ static VMStateInfo vmstate_info_pci_config = {
     .put  = put_pci_config_device,
 };
 
+static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size)
+{
+    PCIDevice *s = container_of(pv, PCIDevice, config);
+    uint32_t irq_state[PCI_NUM_PINS];
+    int i;
+    for (i = 0; i < PCI_NUM_PINS; ++i) {
+        irq_state[i] = qemu_get_be32(f);
+        if (irq_state[i] != 0x1 && irq_state[i] != 0) {
+            fprintf(stderr, "irq state %d: must be 0 or 1.\n",
+                    irq_state[i]);
+            return -EINVAL;
+        }
+    }
+
+    for (i = 0; i < PCI_NUM_PINS; ++i) {
+        pci_set_irq_state(s, i, irq_state[i]);
+    }
+
+    return 0;
+}
+
+static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size)
+{
+    int i;
+    PCIDevice *s = container_of(pv, PCIDevice, config);
+
+    for (i = 0; i < PCI_NUM_PINS; ++i) {
+        qemu_put_be32(f, pci_irq_state(s, i));
+    }
+}
+
+static VMStateInfo vmstate_info_pci_irq_state = {
+    .name = "pci irq state",
+    .get  = get_pci_irq_state,
+    .put  = put_pci_irq_state,
+};
+
 const VMStateDescription vmstate_pci_device = {
     .name = "PCIDevice",
     .version_id = 2,
@@ -294,7 +366,9 @@ const VMStateDescription vmstate_pci_device = {
         VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
                                    vmstate_info_pci_config,
                                    PCI_CONFIG_SPACE_SIZE),
-        VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, PCI_NUM_PINS, 2),
+        VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
+                                  vmstate_info_pci_irq_state,
+                                  PCI_NUM_PINS * sizeof(int32_t)),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -309,7 +383,9 @@ const VMStateDescription vmstate_pcie_device = {
         VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
                                    vmstate_info_pci_config,
                                    PCIE_CONFIG_SPACE_SIZE),
-        VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, PCI_NUM_PINS, 2),
+        VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
+                                  vmstate_info_pci_irq_state,
+                                  PCI_NUM_PINS * sizeof(int32_t)),
         VMSTATE_END_OF_LIST()
     }
 };
@@ -321,19 +397,30 @@ static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s)
 
 void pci_device_save(PCIDevice *s, QEMUFile *f)
 {
+    /* Clear interrupt status bit: it is implicit
+     * in irq_state which we are saving.
+     * This makes us compatible with old devices
+     * which never set or clear this bit. */
+    s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
     vmstate_save_state(f, pci_get_vmstate(s), s);
+    /* Restore the interrupt status bit. */
+    pci_update_irq_status(s);
 }
 
 int pci_device_load(PCIDevice *s, QEMUFile *f)
 {
-    return vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id);
+    int ret;
+    ret = vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id);
+    /* Restore the interrupt status bit. */
+    pci_update_irq_status(s);
+    return ret;
 }
 
 static int pci_set_default_subsystem_id(PCIDevice *pci_dev)
 {
     uint16_t *id;
 
-    id = (void*)(&pci_dev->config[PCI_SUBVENDOR_ID]);
+    id = (void*)(&pci_dev->config[PCI_SUBSYSTEM_VENDOR_ID]);
     id[0] = cpu_to_le16(pci_default_sub_vendor_id);
     id[1] = cpu_to_le16(pci_default_sub_device_id);
     return 0;
@@ -379,7 +466,7 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
        return -1;
 
     /* Note: QEMU doesn't implement domains other than 0 */
-    if (!pci_find_bus(pci_find_host_bus(dom), bus))
+    if (!pci_find_bus(pci_find_root_bus(dom), bus))
        return -1;
 
     *domp = dom;
@@ -409,7 +496,7 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
 
     if (!devaddr) {
         *devfnp = -1;
-        return pci_find_bus(pci_find_host_bus(0), 0);
+        return pci_find_bus(pci_find_root_bus(0), 0);
     }
 
     if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) {
@@ -417,7 +504,7 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
     }
 
     *devfnp = slot << 3;
-    return pci_find_bus(pci_find_host_bus(0), bus);
+    return pci_find_bus(pci_find_root_bus(0), bus);
 }
 
 static void pci_init_cmask(PCIDevice *dev)
@@ -434,15 +521,16 @@ static void pci_init_cmask(PCIDevice *dev)
 
 static void pci_init_wmask(PCIDevice *dev)
 {
-    int i;
     int config_size = pci_config_size(dev);
 
     dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff;
     dev->wmask[PCI_INTERRUPT_LINE] = 0xff;
     pci_set_word(dev->wmask + PCI_COMMAND,
-                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
-    for (i = PCI_CONFIG_HEADER_SIZE; i < config_size; ++i)
-        dev->wmask[i] = 0xff;
+                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+                 PCI_COMMAND_INTX_DISABLE);
+
+    memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff,
+           config_size - PCI_CONFIG_HEADER_SIZE);
 }
 
 static void pci_init_wmask_bridge(PCIDevice *d)
@@ -495,19 +583,23 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
                                          uint8_t header_type)
 {
     if (devfn < 0) {
-        for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) {
+        for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices);
+            devfn += 8) {
             if (!bus->devices[devfn])
                 goto found;
         }
+        qemu_error("PCI: no devfn available for %s, all in use\n", name);
         return NULL;
     found: ;
     } else if (bus->devices[devfn]) {
+        qemu_error("PCI: devfn %d not available for %s, in use by %s\n", devfn,
+                 name, bus->devices[devfn]->name);
         return NULL;
     }
     pci_dev->bus = bus;
     pci_dev->devfn = devfn;
     pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
-    memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state));
+    pci_dev->irq_state = 0;
     pci_config_alloc(pci_dev);
 
     header_type &= ~PCI_HEADER_TYPE_MULTI_FUNCTION;
@@ -543,11 +635,16 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
     pci_dev = do_pci_register_device(pci_dev, bus, name, devfn,
                                      config_read, config_write,
                                      PCI_HEADER_TYPE_NORMAL);
+    if (pci_dev == NULL) {
+        hw_error("PCI: can't register device\n");
+    }
     return pci_dev;
 }
-static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
+
+static target_phys_addr_t pci_to_cpu_addr(PCIBus *bus,
+                                          target_phys_addr_t addr)
 {
-    return addr + pci_mem_base;
+    return addr + bus->mem_base;
 }
 
 static void pci_unregister_io_regions(PCIDevice *pci_dev)
@@ -560,11 +657,12 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev)
         if (!r->size || r->addr == PCI_BAR_UNMAPPED)
             continue;
         if (r->type == PCI_BASE_ADDRESS_SPACE_IO) {
-            isa_unassign_ioport(r->addr, r->size);
+            isa_unassign_ioport(r->addr, r->filtered_size);
         } else {
-            cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
-                                                     r->size,
-                                                     IO_MEM_UNASSIGNED);
+            cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus,
+                                                         r->addr),
+                                         r->filtered_size,
+                                         IO_MEM_UNASSIGNED);
         }
     }
 }
@@ -608,6 +706,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
     r = &pci_dev->io_regions[region_num];
     r->addr = PCI_BAR_UNMAPPED;
     r->size = size;
+    r->filtered_size = size;
     r->type = type;
     r->map_func = map_func;
 
@@ -628,76 +727,198 @@ 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)
+{
+    pcibus_t base = *addr;
+    pcibus_t limit = *addr + *size - 1;
+    PCIDevice *br;
+
+    for (br = d->bus->parent_dev; br; br = br->bus->parent_dev) {
+        uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+
+        if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+            if (!(cmd & PCI_COMMAND_IO)) {
+                goto no_map;
+            }
+        } else {
+            if (!(cmd & PCI_COMMAND_MEMORY)) {
+                goto no_map;
+            }
+        }
+
+        base = MAX(base, pci_bridge_get_base(br, type));
+        limit = MIN(limit, pci_bridge_get_limit(br, type));
+    }
+
+    if (base > limit) {
+        goto no_map;
+    }
+    *addr = base;
+    *size = limit - base + 1;
+    return;
+no_map:
+    *addr = PCI_BAR_UNMAPPED;
+    *size = 0;
+}
+
+static pcibus_t pci_bar_address(PCIDevice *d,
+                               int reg, uint8_t type, pcibus_t size)
+{
+    pcibus_t new_addr, last_addr;
+    int bar = pci_bar(d, reg);
+    uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
+
+    if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+        if (!(cmd & PCI_COMMAND_IO)) {
+            return PCI_BAR_UNMAPPED;
+        }
+        new_addr = pci_get_long(d->config + bar) & ~(size - 1);
+        last_addr = new_addr + size - 1;
+        /* NOTE: we have only 64K ioports on PC */
+        if (last_addr <= new_addr || new_addr == 0 || last_addr > UINT16_MAX) {
+            return PCI_BAR_UNMAPPED;
+        }
+        return new_addr;
+    }
+
+    if (!(cmd & PCI_COMMAND_MEMORY)) {
+        return PCI_BAR_UNMAPPED;
+    }
+    if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+        new_addr = pci_get_quad(d->config + bar);
+    } else {
+        new_addr = pci_get_long(d->config + bar);
+    }
+    /* the ROM slot has a specific enable bit */
+    if (reg == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE)) {
+        return PCI_BAR_UNMAPPED;
+    }
+    new_addr &= ~(size - 1);
+    last_addr = new_addr + size - 1;
+    /* NOTE: we do not support wrapping */
+    /* XXX: as we cannot support really dynamic
+       mappings, we handle specific values as invalid
+       mappings. */
+    if (last_addr <= new_addr || new_addr == 0 ||
+        last_addr == PCI_BAR_UNMAPPED) {
+        return PCI_BAR_UNMAPPED;
+    }
+
+    /* Now pcibus_t is 64bit.
+     * Check if 32 bit BAR wraps around explicitly.
+     * Without this, PC ide doesn't work well.
+     * TODO: remove this work around.
+     */
+    if  (!(type & PCI_BASE_ADDRESS_MEM_TYPE_64) && last_addr >= UINT32_MAX) {
+        return PCI_BAR_UNMAPPED;
+    }
+
+    /*
+     * OS is allowed to set BAR beyond its addressable
+     * bits. For example, 32 bit OS can set 64bit bar
+     * to >4G. Check it. TODO: we might need to support
+     * it in the future for e.g. PAE.
+     */
+    if (last_addr >= TARGET_PHYS_ADDR_MAX) {
+        return PCI_BAR_UNMAPPED;
+    }
+
+    return new_addr;
+}
+
 static void pci_update_mappings(PCIDevice *d)
 {
     PCIIORegion *r;
-    int cmd, i;
-    pcibus_t last_addr, new_addr;
+    int i;
+    pcibus_t new_addr, filtered_size;
 
-    cmd = pci_get_word(d->config + PCI_COMMAND);
     for(i = 0; i < PCI_NUM_REGIONS; i++) {
         r = &d->io_regions[i];
 
         /* this region isn't registered */
-        if (r->size == 0)
+        if (!r->size)
             continue;
 
-        if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
-            if (cmd & PCI_COMMAND_IO) {
-                new_addr = pci_get_long(d->config + pci_bar(d, i));
-                new_addr = new_addr & ~(r->size - 1);
-                last_addr = new_addr + r->size - 1;
-                /* NOTE: we have only 64K ioports on PC */
-                if (last_addr <= new_addr || new_addr == 0 ||
-                    last_addr >= 0x10000) {
-                    new_addr = PCI_BAR_UNMAPPED;
-                }
-            } else {
-                new_addr = PCI_BAR_UNMAPPED;
-            }
-        } else {
-            if (cmd & PCI_COMMAND_MEMORY) {
-                if (r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
-                    new_addr = pci_get_quad(d->config + pci_bar(d, i));
-                } else {
-                    new_addr = pci_get_long(d->config + pci_bar(d, i));
-                }
-                /* the ROM slot has a specific enable bit */
-                if (i == PCI_ROM_SLOT && !(new_addr & PCI_ROM_ADDRESS_ENABLE))
-                    goto no_mem_map;
-                new_addr = new_addr & ~(r->size - 1);
-                last_addr = new_addr + r->size - 1;
-                /* NOTE: we do not support wrapping */
-                /* XXX: as we cannot support really dynamic
-                   mappings, we handle specific values as invalid
-                   mappings. */
-                if (last_addr <= new_addr || new_addr == 0 ||
-                    last_addr == PCI_BAR_UNMAPPED ||
-
-                    /* Now pcibus_t is 64bit.
-                     * Check if 32 bit BAR wrap around explicitly.
-                     * Without this, PC ide doesn't work well.
-                     * TODO: remove this work around.
-                     */
-                    (!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) &&
-                     last_addr >= UINT32_MAX) ||
-
-                    /*
-                     * OS is allowed to set BAR beyond its addressable
-                     * bits. For example, 32 bit OS can set 64bit bar
-                     * to >4G. Check it.
-                     */
-                    last_addr >= TARGET_PHYS_ADDR_MAX) {
-                    new_addr = PCI_BAR_UNMAPPED;
-                }
-            } else {
-            no_mem_map:
-                new_addr = PCI_BAR_UNMAPPED;
-            }
+        new_addr = pci_bar_address(d, i, r->type, r->size);
+
+        /* bridge filtering */
+        filtered_size = r->size;
+        if (new_addr != PCI_BAR_UNMAPPED) {
+            pci_bridge_filter(d, &new_addr, &filtered_size, r->type);
         }
 
         /* This bar isn't changed */
-        if (new_addr == r->addr)
+        if (new_addr == r->addr && filtered_size == r->filtered_size)
             continue;
 
         /* now do the real mapping */
@@ -710,22 +931,54 @@ static void pci_update_mappings(PCIDevice *d)
                 if (class == 0x0101 && r->size == 4) {
                     isa_unassign_ioport(r->addr + 2, 1);
                 } else {
-                    isa_unassign_ioport(r->addr, r->size);
+                    isa_unassign_ioport(r->addr, r->filtered_size);
                 }
             } else {
-                cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
-                                             r->size,
+                cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr),
+                                             r->filtered_size,
                                              IO_MEM_UNASSIGNED);
-                qemu_unregister_coalesced_mmio(r->addr, r->size);
+                qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
             }
         }
         r->addr = new_addr;
+        r->filtered_size = filtered_size;
         if (r->addr != PCI_BAR_UNMAPPED) {
-            r->map_func(d, i, r->addr, r->size, r->type);
+            /*
+             * TODO: currently almost all the map funcions assumes
+             * filtered_size == size and addr & ~(size - 1) == addr.
+             * However with bridge filtering, they aren't always true.
+             * Teach them such cases, such that filtered_size < size and
+             * addr & (size - 1) != 0.
+             */
+            if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+                r->map_func(d, i, r->addr, r->filtered_size, r->type);
+            } else {
+                r->map_func(d, i, pci_to_cpu_addr(d->bus, r->addr),
+                            r->filtered_size, r->type);
+            }
         }
     }
 }
 
+static inline int pci_irq_disabled(PCIDevice *d)
+{
+    return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
+}
+
+/* Called after interrupt disabled field update in config space,
+ * assert/deassert interrupts if necessary.
+ * Gets original interrupt disable bit value (before update). */
+static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled)
+{
+    int i, disabled = pci_irq_disabled(d);
+    if (disabled == was_irq_disabled)
+        return;
+    for (i = 0; i < PCI_NUM_PINS; ++i) {
+        int state = pci_irq_state(d, i);
+        pci_change_irq_level(d, i, disabled ? -state : state);
+    }
+}
+
 uint32_t pci_default_read_config(PCIDevice *d,
                                  uint32_t address, int len)
 {
@@ -738,18 +991,21 @@ uint32_t pci_default_read_config(PCIDevice *d,
 
 void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
 {
-    int i;
+    int i, was_irq_disabled = pci_irq_disabled(d);
     uint32_t config_size = pci_config_size(d);
 
-    for(i = 0; i < l && addr < config_size; val >>= 8, ++i, ++addr) {
-        uint8_t wmask = d->wmask[addr];
-        d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask);
+    for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) {
+        uint8_t wmask = d->wmask[addr + i];
+        d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
     }
     if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
         ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
         ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) ||
         range_covers_byte(addr, l, PCI_COMMAND))
         pci_update_mappings(d);
+
+    if (range_covers_byte(addr, l, PCI_COMMAND))
+        pci_update_irq_disabled(d, was_irq_disabled);
 }
 
 /***********************************************************/
@@ -759,23 +1015,17 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
 static void pci_set_irq(void *opaque, int irq_num, int level)
 {
     PCIDevice *pci_dev = opaque;
-    PCIBus *bus;
     int change;
 
-    change = level - pci_dev->irq_state[irq_num];
+    change = level - pci_irq_state(pci_dev, irq_num);
     if (!change)
         return;
 
-    pci_dev->irq_state[irq_num] = level;
-    for (;;) {
-        bus = pci_dev->bus;
-        irq_num = bus->map_irq(pci_dev, irq_num);
-        if (bus->set_irq)
-            break;
-        pci_dev = bus->parent_dev;
-    }
-    bus->irq_count[irq_num] += change;
-    bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
+    pci_set_irq_state(pci_dev, irq_num, level);
+    pci_update_irq_status(pci_dev);
+    if (pci_irq_disabled(pci_dev))
+        return;
+    pci_change_irq_level(pci_dev, irq_num, change);
 }
 
 /***********************************************************/
@@ -855,7 +1105,32 @@ static void pci_info_device(PCIBus *bus, PCIDevice *d)
                        d->config[PCI_INTERRUPT_LINE]);
     }
     if (class == 0x0604) {
+        uint64_t base;
+        uint64_t limit;
+
         monitor_printf(mon, "      BUS %d.\n", d->config[0x19]);
+        monitor_printf(mon, "      secondary bus %d.\n",
+                       d->config[PCI_SECONDARY_BUS]);
+        monitor_printf(mon, "      subordinate bus %d.\n",
+                       d->config[PCI_SUBORDINATE_BUS]);
+
+        base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_IO);
+        limit = pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_IO);
+        monitor_printf(mon, "      IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
+                       base, limit);
+
+        base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_MEMORY);
+        limit= pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_MEMORY);
+        monitor_printf(mon,
+                       "      memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
+                       base, limit);
+
+        base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_MEMORY |
+                                   PCI_BASE_ADDRESS_MEM_PREFETCH);
+        limit = pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_MEMORY |
+                                     PCI_BASE_ADDRESS_MEM_PREFETCH);
+        monitor_printf(mon, "      prefetchable memory range "
+                       "[0x%08"PRIx64", 0x%08"PRIx64"]\n", base, limit);
     }
     for(i = 0;i < PCI_NUM_REGIONS; i++) {
         r = &d->io_regions[i];
@@ -885,19 +1160,26 @@ static void pci_info_device(PCIBus *bus, PCIDevice *d)
     }
 }
 
-void pci_for_each_device(PCIBus *bus, int bus_num,
-                         void (*fn)(PCIBus *b, PCIDevice *d))
+static void pci_for_each_device_under_bus(PCIBus *bus,
+                                          void (*fn)(PCIBus *b, PCIDevice *d))
 {
     PCIDevice *d;
     int devfn;
 
+    for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+        d = bus->devices[devfn];
+        if (d)
+            fn(bus, d);
+    }
+}
+
+void pci_for_each_device(PCIBus *bus, int bus_num,
+                         void (*fn)(PCIBus *b, PCIDevice *d))
+{
     bus = pci_find_bus(bus, bus_num);
+
     if (bus) {
-        for(devfn = 0; devfn < 256; devfn++) {
-            d = bus->devices[devfn];
-            if (d)
-                fn(bus, d);
-        }
+        pci_for_each_device_under_bus(bus, fn);
     }
 }
 
@@ -987,10 +1269,36 @@ typedef struct {
     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)
+{
+    PCIBus *child;
+
+    pci_for_each_device_under_bus(b, pci_bridge_update_mappings_fn);
+
+    QLIST_FOREACH(child, &b->child, sibling) {
+        pci_bridge_update_mappings(child);
+    }
+}
+
 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)) {
+        pci_bridge_update_mappings(d->bus);
+    }
 }
 
 PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
@@ -1006,7 +1314,10 @@ PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
 
     /* try child bus */
     QLIST_FOREACH(sec, &bus->child, sibling) {
-        if (pci_bus_num(sec) <= bus_num && bus_num <= pci_sub_bus(sec)) {
+
+        if (!bus->parent_dev /* pci host bridge */
+            || (pci_bus_num(sec) <= bus_num &&
+                bus->parent_dev->config[PCI_SUBORDINATE_BUS])) {
             return pci_find_bus(sec, bus_num);
         }
     }
@@ -1031,29 +1342,10 @@ static int pci_bridge_initfn(PCIDevice *dev)
     pci_config_set_vendor_id(s->dev.config, s->vid);
     pci_config_set_device_id(s->dev.config, s->did);
 
-    /* TODO: intial value
-     * command register:
-     * According to PCI bridge spec, after reset
-     *   bus master bit is off
-     *   memory space enable bit is off
-     * According to manual (805-1251.pdf).(See abp_pbi.c for its links.)
-     *   the reset value should be zero unless the boot pin is tied high
-     *   (which is tru) and thus it should be PCI_COMMAND_MEMORY.
-     *
-     * For now, don't touch the value.
-     * Later command register will be set to zero and apb_pci.c will
-     * override the value.
-     * Same for latency timer, and multi function bit of header type.
-     */
-    pci_set_word(dev->config + PCI_COMMAND,
-                 PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
-
     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_LATENCY_TIMER] = 0x10;
-    dev->config[PCI_HEADER_TYPE] =
-        PCI_HEADER_TYPE_MULTI_FUNCTION | PCI_HEADER_TYPE_BRIDGE;
+    dev->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_BRIDGE;
     pci_set_word(dev->config + PCI_SEC_STATUS,
                  PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
     return 0;
@@ -1083,6 +1375,11 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did,
     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;
@@ -1100,10 +1397,17 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
     pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn,
                                      info->config_read, info->config_write,
                                      info->header_type);
-    assert(pci_dev);
+    if (pci_dev == NULL)
+        return -1;
     rc = info->init(pci_dev);
     if (rc != 0)
         return rc;
+
+    /* rom loading */
+    if (pci_dev->romfile == NULL && info->romfile != NULL)
+        pci_dev->romfile = qemu_strdup(info->romfile);
+    pci_add_option_rom(pci_dev);
+
     if (qdev->hotplugged)
         bus->hotplug(pci_dev, 1);
     return 0;
@@ -1181,6 +1485,64 @@ static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id,
     return next;
 }
 
+static void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, pcibus_t size, int type)
+{
+    cpu_register_physical_memory(addr, size, pdev->rom_offset);
+}
+
+/* Add an option rom for the device */
+static int pci_add_option_rom(PCIDevice *pdev)
+{
+    int size;
+    char *path;
+    void *ptr;
+
+    if (!pdev->romfile)
+        return 0;
+    if (strlen(pdev->romfile) == 0)
+        return 0;
+
+    if (!pdev->rom_bar) {
+        /*
+         * Load rom via fw_cfg instead of creating a rom bar,
+         * for 0.11 compatibility.
+         */
+        int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
+        if (class == 0x0300) {
+            rom_add_vga(pdev->romfile);
+        } else {
+            rom_add_option(pdev->romfile);
+        }
+        return 0;
+    }
+
+    path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile);
+    if (path == NULL) {
+        path = qemu_strdup(pdev->romfile);
+    }
+
+    size = get_image_size(path);
+    if (size < 0) {
+        qemu_error("%s: failed to find romfile \"%s\"\n", __FUNCTION__,
+                   pdev->romfile);
+        return -1;
+    }
+    if (size & (size - 1)) {
+        size = 1 << qemu_fls(size);
+    }
+
+    pdev->rom_offset = qemu_ram_alloc(size);
+
+    ptr = qemu_get_ram_ptr(pdev->rom_offset);
+    load_image(path, ptr);
+    qemu_free(path);
+
+    pci_register_bar(pdev, PCI_ROM_SLOT, size,
+                     0, pci_map_option_rom);
+
+    return 0;
+}
+
 /* 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)
 {
This page took 0.052424 seconds and 4 git commands to generate.