#include "monitor.h"
#include "net.h"
#include "sysemu.h"
+#include "loader.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
struct PCIBus {
BusState qbus;
- int bus_num;
int devfn_min;
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;
- PCIBus *next;
+ 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;
.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;
-static PCIBus *first_bus;
+
+struct PCIHostBus {
+ int domain;
+ struct PCIBus *bus;
+ QLIST_ENTRY(PCIHostBus) next;
+};
+static QLIST_HEAD(, PCIHostBus) host_buses;
static const VMStateDescription vmstate_pcibus = {
.name = "PCIBUS",
}
};
-static inline int pci_bar(int reg)
+static int pci_bar(PCIDevice *d, int reg)
+{
+ uint8_t type;
+
+ if (reg != PCI_ROM_SLOT)
+ return PCI_BASE_ADDRESS_0 + reg * 4;
+
+ type = d->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+ 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)
{
- return reg == PCI_ROM_SLOT ? PCI_ROM_ADDRESS : PCI_BASE_ADDRESS_0 + reg * 4;
+ 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;
if (!dev->io_regions[r].size) {
continue;
}
- pci_set_long(dev->config + pci_bar(r), dev->io_regions[r].type);
+ pci_set_long(dev->config + pci_bar(dev, r), dev->io_regions[r].type);
}
pci_update_mappings(dev);
}
}
}
+static void pci_host_bus_register(int domain, PCIBus *bus)
+{
+ struct PCIHostBus *host;
+ host = qemu_mallocz(sizeof(*host));
+ host->domain = domain;
+ host->bus = bus;
+ QLIST_INSERT_HEAD(&host_buses, host, next);
+}
+
+PCIBus *pci_find_root_bus(int domain)
+{
+ struct PCIHostBus *host;
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ if (host->domain == domain) {
+ return host->bus;
+ }
+ }
+
+ return NULL;
+}
+
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;
- bus->next = first_bus;
- first_bus = bus;
- vmstate_register(nbus++, &vmstate_pcibus, bus);
+
+ /* host bridge */
+ QLIST_INIT(&bus->child);
+ pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */
+
+ vmstate_register(-1, &vmstate_pcibus, bus);
qemu_register_reset(pci_bus_reset, bus);
}
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)
return bus;
}
-static void pci_register_secondary_bus(PCIBus *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;
- bus->next = dev->bus->next;
- dev->bus->next = bus;
+
+ 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)
{
- return s->bus_num;
+ if (!s->parent_dev)
+ return 0; /* pci host bridge */
+ return s->parent_dev->config[PCI_SECONDARY_BUS];
}
static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
{
PCIDevice *s = container_of(pv, PCIDevice, config);
- uint8_t config[PCI_CONFIG_SPACE_SIZE];
+ uint8_t *config;
int i;
- assert(size == sizeof config);
- qemu_get_buffer(f, config, sizeof config);
- for (i = 0; i < sizeof config; ++i)
- if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i])
+ assert(size == pci_config_size(s));
+ config = qemu_malloc(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]) {
+ qemu_free(config);
return -EINVAL;
- memcpy(s->config, config, sizeof config);
+ }
+ }
+ memcpy(s->config, config, size);
pci_update_mappings(s);
+ qemu_free(config);
return 0;
}
/* just put buffer */
static void put_pci_config_device(QEMUFile *f, void *pv, size_t size)
{
- const uint8_t *v = pv;
- qemu_put_buffer(f, v, size);
+ const uint8_t **v = pv;
+ assert(size == pci_config_size(container_of(pv, PCIDevice, config)));
+ qemu_put_buffer(f, *v, size);
}
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,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_INT32_LE(version_id, PCIDevice),
- VMSTATE_SINGLE(config, PCIDevice, 0, vmstate_info_pci_config,
- typeof_field(PCIDevice,config)),
- VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, 4, 2),
+ VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
+ vmstate_info_pci_config,
+ PCI_CONFIG_SPACE_SIZE),
+ VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
+ vmstate_info_pci_irq_state,
+ PCI_NUM_PINS * sizeof(int32_t)),
VMSTATE_END_OF_LIST()
}
};
+const VMStateDescription vmstate_pcie_device = {
+ .name = "PCIDevice",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32_LE(version_id, PCIDevice),
+ VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
+ vmstate_info_pci_config,
+ PCIE_CONFIG_SPACE_SIZE),
+ VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2,
+ vmstate_info_pci_irq_state,
+ PCI_NUM_PINS * sizeof(int32_t)),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s)
+{
+ return pci_is_express(s) ? &vmstate_pcie_device : &vmstate_pci_device;
+}
+
void pci_device_save(PCIDevice *s, QEMUFile *f)
{
- vmstate_save_state(f, &vmstate_pci_device, s);
+ /* 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, &vmstate_pci_device, 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;
return -1;
/* Note: QEMU doesn't implement domains other than 0 */
- if (dom != 0 || pci_find_bus(bus) == NULL)
+ if (!pci_find_bus(pci_find_root_bus(dom), bus))
return -1;
*domp = dom;
if (!devaddr) {
*devfnp = -1;
- return pci_find_bus(0);
+ return pci_find_bus(pci_find_root_bus(0), 0);
}
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) {
}
*devfnp = slot << 3;
- return pci_find_bus(bus);
+ return pci_find_bus(pci_find_root_bus(0), bus);
}
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;
- dev->wmask[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY
- | PCI_COMMAND_MASTER;
- for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i)
- dev->wmask[i] = 0xff;
+ pci_set_word(dev->wmask + PCI_COMMAND,
+ 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)
+{
+ /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and
+ PCI_SEC_LETENCY_TIMER */
+ memset(d->wmask + PCI_PRIMARY_BUS, 0xff, 4);
+
+ /* base and limit */
+ d->wmask[PCI_IO_BASE] = PCI_IO_RANGE_MASK & 0xff;
+ d->wmask[PCI_IO_LIMIT] = PCI_IO_RANGE_MASK & 0xff;
+ pci_set_word(d->wmask + PCI_MEMORY_BASE,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_set_word(d->wmask + PCI_MEMORY_LIMIT,
+ PCI_MEMORY_RANGE_MASK & 0xffff);
+ pci_set_word(d->wmask + PCI_PREF_MEMORY_BASE,
+ PCI_PREF_RANGE_MASK & 0xffff);
+ pci_set_word(d->wmask + PCI_PREF_MEMORY_LIMIT,
+ PCI_PREF_RANGE_MASK & 0xffff);
+
+ /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */
+ memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8);
+
+ pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, 0xffff);
+}
+
+static void pci_config_alloc(PCIDevice *pci_dev)
+{
+ int config_size = pci_config_size(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->used = qemu_mallocz(config_size);
+}
+
+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->used);
}
/* -1 for devfn means auto assign */
static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
const char *name, int devfn,
PCIConfigReadFunc *config_read,
- PCIConfigWriteFunc *config_write)
+ PCIConfigWriteFunc *config_write,
+ 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_set_default_subsystem_id(pci_dev);
+ pci_dev->irq_state = 0;
+ pci_config_alloc(pci_dev);
+
+ header_type &= ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+ if (header_type == PCI_HEADER_TYPE_NORMAL) {
+ pci_set_default_subsystem_id(pci_dev);
+ }
pci_init_cmask(pci_dev);
pci_init_wmask(pci_dev);
+ if (header_type == PCI_HEADER_TYPE_BRIDGE) {
+ pci_init_wmask_bridge(pci_dev);
+ }
if (!config_read)
config_read = pci_default_read_config;
pci_dev->config_read = config_read;
pci_dev->config_write = config_write;
bus->devices[devfn] = pci_dev;
- pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, 4);
+ pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, PCI_NUM_PINS);
pci_dev->version_id = 2; /* Current pci device vmstate version */
return pci_dev;
}
pci_dev = qemu_mallocz(instance_size);
pci_dev = do_pci_register_device(pci_dev, bus, name, devfn,
- config_read, config_write);
+ 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)
for(i = 0; i < PCI_NUM_REGIONS; i++) {
r = &pci_dev->io_regions[i];
- if (!r->size || r->addr == -1)
+ if (!r->size || r->addr == PCI_BAR_UNMAPPED)
continue;
- if (r->type == PCI_ADDRESS_SPACE_IO) {
- isa_unassign_ioport(r->addr, r->size);
+ if (r->type == PCI_BASE_ADDRESS_SPACE_IO) {
+ 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);
}
}
}
qemu_free_irqs(pci_dev->irq);
pci_dev->bus->devices[pci_dev->devfn] = NULL;
+ pci_config_free(pci_dev);
return 0;
}
void pci_register_bar(PCIDevice *pci_dev, int region_num,
- uint32_t size, int type,
+ pcibus_t size, int type,
PCIMapIORegionFunc *map_func)
{
PCIIORegion *r;
uint32_t addr;
- uint32_t wmask;
+ pcibus_t wmask;
if ((unsigned int)region_num >= PCI_NUM_REGIONS)
return;
if (size & (size-1)) {
fprintf(stderr, "ERROR: PCI region size must be pow2 "
- "type=0x%x, size=0x%x\n", type, size);
+ "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size);
exit(1);
}
r = &pci_dev->io_regions[region_num];
- r->addr = -1;
+ r->addr = PCI_BAR_UNMAPPED;
r->size = size;
+ r->filtered_size = size;
r->type = type;
r->map_func = map_func;
wmask = ~(size - 1);
- addr = pci_bar(region_num);
+ addr = pci_bar(pci_dev, region_num);
if (region_num == PCI_ROM_SLOT) {
/* ROM enable bit is writeable */
wmask |= PCI_ROM_ADDRESS_ENABLE;
}
- *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
- *(uint32_t *)(pci_dev->wmask + addr) = cpu_to_le32(wmask);
- *(uint32_t *)(pci_dev->cmask + addr) = 0xffffffff;
+ pci_set_long(pci_dev->config + addr, type);
+ if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) &&
+ r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+ pci_set_quad(pci_dev->wmask + addr, wmask);
+ pci_set_quad(pci_dev->cmask + addr, ~0ULL);
+ } else {
+ pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
+ pci_set_long(pci_dev->cmask + addr, 0xffffffff);
+ }
+}
+
+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;
- uint32_t last_addr, new_addr;
+ int i;
+ pcibus_t new_addr, filtered_size;
- cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND));
for(i = 0; i < PCI_NUM_REGIONS; i++) {
r = &d->io_regions[i];
- if (r->size != 0) {
- if (r->type & PCI_ADDRESS_SPACE_IO) {
- if (cmd & PCI_COMMAND_IO) {
- new_addr = pci_get_long(d->config + pci_bar(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 = -1;
- }
+
+ /* this region isn't registered */
+ if (!r->size)
+ continue;
+
+ 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 && filtered_size == r->filtered_size)
+ continue;
+
+ /* now do the real mapping */
+ if (r->addr != PCI_BAR_UNMAPPED) {
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ int class;
+ /* NOTE: specific hack for IDE in PC case:
+ only one byte must be mapped. */
+ class = pci_get_word(d->config + PCI_CLASS_DEVICE);
+ if (class == 0x0101 && r->size == 4) {
+ isa_unassign_ioport(r->addr + 2, 1);
} else {
- new_addr = -1;
+ isa_unassign_ioport(r->addr, r->filtered_size);
}
} else {
- if (cmd & PCI_COMMAND_MEMORY) {
- new_addr = pci_get_long(d->config + pci_bar(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 == -1) {
- new_addr = -1;
- }
- } else {
- no_mem_map:
- new_addr = -1;
- }
+ 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->filtered_size);
}
- /* now do the real mapping */
- if (new_addr != r->addr) {
- if (r->addr != -1) {
- if (r->type & PCI_ADDRESS_SPACE_IO) {
- int class;
- /* NOTE: specific hack for IDE in PC case:
- only one byte must be mapped. */
- class = pci_get_word(d->config + PCI_CLASS_DEVICE);
- if (class == 0x0101 && r->size == 4) {
- isa_unassign_ioport(r->addr + 2, 1);
- } else {
- isa_unassign_ioport(r->addr, r->size);
- }
- } else {
- cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
- r->size,
- IO_MEM_UNASSIGNED);
- qemu_unregister_coalesced_mmio(r->addr, r->size);
- }
- }
- r->addr = new_addr;
- if (r->addr != -1) {
- r->map_func(d, i, r->addr, r->size, r->type);
- }
+ }
+ r->addr = new_addr;
+ r->filtered_size = filtered_size;
+ if (r->addr != PCI_BAR_UNMAPPED) {
+ /*
+ * 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);
}
}
}
}
-uint32_t pci_default_read_config(PCIDevice *d,
- uint32_t address, int len)
+static inline int pci_irq_disabled(PCIDevice *d)
{
- uint32_t val;
-
- switch(len) {
- default:
- case 4:
- if (address <= 0xfc) {
- val = le32_to_cpu(*(uint32_t *)(d->config + address));
- break;
- }
- /* fall through */
- case 2:
- if (address <= 0xfe) {
- val = le16_to_cpu(*(uint16_t *)(d->config + address));
- break;
- }
- /* fall through */
- case 1:
- val = d->config[address];
- break;
- }
- return val;
+ return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
}
-void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
+/* 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)
{
- uint8_t orig[PCI_CONFIG_SPACE_SIZE];
- int i;
-
- /* not efficient, but simple */
- memcpy(orig, d->config, PCI_CONFIG_SPACE_SIZE);
- for(i = 0; i < l && addr < PCI_CONFIG_SPACE_SIZE; val >>= 8, ++i, ++addr) {
- uint8_t wmask = d->wmask[addr];
- d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask);
+ 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);
}
- if (memcmp(orig + PCI_BASE_ADDRESS_0, d->config + PCI_BASE_ADDRESS_0, 24)
- || ((orig[PCI_COMMAND] ^ d->config[PCI_COMMAND])
- & (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)))
- pci_update_mappings(d);
}
-void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
+uint32_t pci_default_read_config(PCIDevice *d,
+ uint32_t address, int len)
{
- PCIBus *s = opaque;
- PCIDevice *pci_dev;
- int config_addr, bus_num;
-
-#if 0
- PCI_DPRINTF("pci_data_write: addr=%08x val=%08x len=%d\n",
- addr, val, len);
-#endif
- bus_num = (addr >> 16) & 0xff;
- while (s && s->bus_num != bus_num)
- s = s->next;
- if (!s)
- return;
- pci_dev = s->devices[(addr >> 8) & 0xff];
- if (!pci_dev)
- return;
- config_addr = addr & 0xff;
- PCI_DPRINTF("pci_config_write: %s: addr=%02x val=%08x len=%d\n",
- pci_dev->name, config_addr, val, len);
- pci_dev->config_write(pci_dev, config_addr, val, len);
+ uint32_t val = 0;
+ assert(len == 1 || len == 2 || len == 4);
+ len = MIN(len, pci_config_size(d) - address);
+ memcpy(&val, d->config + address, len);
+ return le32_to_cpu(val);
}
-uint32_t pci_data_read(void *opaque, uint32_t addr, int len)
+void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
{
- PCIBus *s = opaque;
- PCIDevice *pci_dev;
- int config_addr, bus_num;
- uint32_t val;
+ int i, was_irq_disabled = pci_irq_disabled(d);
+ uint32_t config_size = pci_config_size(d);
- bus_num = (addr >> 16) & 0xff;
- while (s && s->bus_num != bus_num)
- s= s->next;
- if (!s)
- goto fail;
- pci_dev = s->devices[(addr >> 8) & 0xff];
- if (!pci_dev) {
- fail:
- switch(len) {
- case 1:
- val = 0xff;
- break;
- case 2:
- val = 0xffff;
- break;
- default:
- case 4:
- val = 0xffffffff;
- break;
- }
- goto the_end;
- }
- config_addr = addr & 0xff;
- val = pci_dev->config_read(pci_dev, config_addr, len);
- PCI_DPRINTF("pci_config_read: %s: addr=%02x val=%08x len=%d\n",
- pci_dev->name, config_addr, val, len);
- the_end:
-#if 0
- PCI_DPRINTF("pci_data_read: addr=%08x val=%08x len=%d\n",
- addr, val, len);
-#endif
- return val;
+ 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);
}
/***********************************************************/
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);
}
/***********************************************************/
{ 0, NULL}
};
-static void pci_info_device(PCIDevice *d)
+static void pci_info_device(PCIBus *bus, PCIDevice *d)
{
Monitor *mon = cur_mon;
int i, class;
const pci_class_desc *desc;
monitor_printf(mon, " Bus %2d, device %3d, function %d:\n",
- d->bus->bus_num, d->devfn >> 3, d->devfn & 7);
- class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE)));
+ pci_bus_num(d->bus),
+ PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
+ class = pci_get_word(d->config + PCI_CLASS_DEVICE);
monitor_printf(mon, " ");
desc = pci_class_descriptions;
while (desc->desc && class != desc->class)
monitor_printf(mon, "Class %04x", class);
}
monitor_printf(mon, ": PCI device %04x:%04x\n",
- le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))),
- le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID))));
+ pci_get_word(d->config + PCI_VENDOR_ID),
+ pci_get_word(d->config + PCI_DEVICE_ID));
if (d->config[PCI_INTERRUPT_PIN] != 0) {
monitor_printf(mon, " IRQ %d.\n",
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];
if (r->size != 0) {
monitor_printf(mon, " BAR%d: ", i);
- if (r->type & PCI_ADDRESS_SPACE_IO) {
- monitor_printf(mon, "I/O at 0x%04x [0x%04x].\n",
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS
+ " [0x%04"FMT_PCIBUS"].\n",
r->addr, r->addr + r->size - 1);
} else {
- monitor_printf(mon, "32 bit memory at 0x%08x [0x%08x].\n",
+ const char *type = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64 ?
+ "64 bit" : "32 bit";
+ const char *prefetch =
+ r->type & PCI_BASE_ADDRESS_MEM_PREFETCH ?
+ " prefetchable" : "";
+
+ monitor_printf(mon, "%s%s memory at 0x%08"FMT_PCIBUS
+ " [0x%08"FMT_PCIBUS"].\n",
+ type, prefetch,
r->addr, r->addr + r->size - 1);
}
}
}
monitor_printf(mon, " id \"%s\"\n", d->qdev.id ? d->qdev.id : "");
if (class == 0x0604 && d->config[0x19] != 0) {
- pci_for_each_device(d->config[0x19], pci_info_device);
+ pci_for_each_device(bus, d->config[0x19], pci_info_device);
}
}
-void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d))
+static void pci_for_each_device_under_bus(PCIBus *bus,
+ void (*fn)(PCIBus *b, PCIDevice *d))
{
- PCIBus *bus = first_bus;
PCIDevice *d;
int devfn;
- while (bus && bus->bus_num != bus_num)
- bus = bus->next;
+ 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(d);
- }
+ pci_for_each_device_under_bus(bus, fn);
}
}
void pci_info(Monitor *mon)
{
- pci_for_each_device(0, pci_info_device);
+ struct PCIHostBus *host;
+ QLIST_FOREACH(host, &host_buses, next) {
+ pci_for_each_device(host->bus, 0, pci_info_device);
+ }
}
static const char * const pci_nic_models[] = {
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)
{
- PCIBridge *s = (PCIBridge *)d;
-
pci_default_write_config(d, address, val, len);
- s->bus.bus_num = d->config[PCI_SECONDARY_BUS];
+
+ 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(int bus_num)
+PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
{
- PCIBus *bus = first_bus;
+ PCIBus *sec;
- while (bus && bus->bus_num != bus_num)
- bus = bus->next;
+ if (!bus)
+ return NULL;
- return bus;
+ if (pci_bus_num(bus) == bus_num) {
+ return bus;
+ }
+
+ /* try child bus */
+ QLIST_FOREACH(sec, &bus->child, sibling) {
+
+ 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);
+ }
+ }
+
+ return NULL;
}
-PCIDevice *pci_find_device(int bus_num, int slot, int function)
+PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function)
{
- PCIBus *bus = pci_find_bus(bus_num);
+ bus = pci_find_bus(bus, bus_num);
if (!bus)
return NULL;
pci_config_set_vendor_id(s->dev.config, s->vid);
pci_config_set_device_id(s->dev.config, s->did);
- s->dev.config[0x04] = 0x06; // command = bus master, pci mem
- s->dev.config[0x05] = 0x00;
- s->dev.config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
- s->dev.config[0x07] = 0x00; // status = fast devsel
- s->dev.config[0x08] = 0x00; // revision
- s->dev.config[0x09] = 0x00; // programming i/f
- pci_config_set_class(s->dev.config, PCI_CLASS_BRIDGE_PCI);
- s->dev.config[0x0D] = 0x10; // latency_timer
- s->dev.config[PCI_HEADER_TYPE] =
- PCI_HEADER_TYPE_MULTI_FUNCTION | PCI_HEADER_TYPE_BRIDGE; // header_type
- s->dev.config[0x1E] = 0xa0; // secondary status
+ 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] = 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;
}
qdev_init_nofail(&dev->qdev);
s = DO_UPCAST(PCIBridge, dev, dev);
- pci_register_secondary_bus(&s->bus, &s->dev, map_irq, name);
+ 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;
PCIBus *bus;
int devfn, rc;
+ /* initialize cap_present for pci_is_express() and pci_config_size() */
+ if (info->is_express) {
+ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
+ }
+
bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev));
devfn = pci_dev->devfn;
pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn,
- info->config_read, info->config_write);
- assert(pci_dev);
+ info->config_read, info->config_write,
+ info->header_type);
+ 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;
static int pci_find_space(PCIDevice *pdev, uint8_t size)
{
+ int config_size = pci_config_size(pdev);
int offset = PCI_CONFIG_HEADER_SIZE;
int i;
- for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i)
+ for (i = PCI_CONFIG_HEADER_SIZE; i < config_size; ++i)
if (pdev->used[i])
offset = i + 1;
else if (i - offset + 1 == size)
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)
{
PCIIORegion *r;
int i, class;
- class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE)));
+ class = pci_get_word(d->config + PCI_CLASS_DEVICE);
desc = pci_class_descriptions;
while (desc->desc && class != desc->class)
desc++;
monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, "
"pci id %04x:%04x (sub %04x:%04x)\n",
indent, "", ctxt,
- d->bus->bus_num, d->devfn >> 3, d->devfn & 7,
- le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))),
- le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID))),
- le16_to_cpu(*((uint16_t *)(d->config + PCI_SUBSYSTEM_VENDOR_ID))),
- le16_to_cpu(*((uint16_t *)(d->config + PCI_SUBSYSTEM_ID))));
+ d->config[PCI_SECONDARY_BUS],
+ PCI_SLOT(d->devfn), PCI_FUNC(d->devfn),
+ pci_get_word(d->config + PCI_VENDOR_ID),
+ pci_get_word(d->config + PCI_DEVICE_ID),
+ pci_get_word(d->config + PCI_SUBSYSTEM_VENDOR_ID),
+ pci_get_word(d->config + PCI_SUBSYSTEM_ID));
for (i = 0; i < PCI_NUM_REGIONS; i++) {
r = &d->io_regions[i];
if (!r->size)
continue;
- monitor_printf(mon, "%*sbar %d: %s at 0x%x [0x%x]\n", indent, "",
- i, r->type & PCI_ADDRESS_SPACE_IO ? "i/o" : "mem",
+ monitor_printf(mon, "%*sbar %d: %s at 0x%"FMT_PCIBUS
+ " [0x%"FMT_PCIBUS"]\n",
+ indent, "",
+ i, r->type & PCI_BASE_ADDRESS_SPACE_IO ? "i/o" : "mem",
r->addr, r->addr + r->size - 1);
}
}
.qdev.name = "pci-bridge",
.qdev.size = sizeof(PCIBridge),
.init = pci_bridge_initfn,
+ .exit = pci_bridge_exitfn,
.config_write = pci_bridge_write_config,
.qdev.props = (Property[]) {
DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),