#include "monitor.h"
#include "net.h"
#include "sysemu.h"
+#include "loader.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
-# define PCI_DPRINTF(format, ...) printf(format, __VA_ARGS__)
+# define PCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
#else
# define PCI_DPRINTF(format, ...) do { } while (0)
#endif
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 */
- /* low level pic */
- SetIRQFunc *low_set_irq;
- qemu_irq *irq_opaque;
+ void *irq_opaque;
PCIDevice *devices[256];
PCIDevice *parent_dev;
- PCIBus *next;
+
+ 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_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",
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_INT32_EQUAL(nirq, PCIBus),
- VMSTATE_INT32_VARRAY(irq_count, PCIBus, nirq),
+ VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, int32_t),
VMSTATE_END_OF_LIST()
}
};
+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)
+{
+ 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;
+
+ 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;
+ dev->config[PCI_INTERRUPT_LINE] = 0x0;
+ for (r = 0; r < PCI_NUM_REGIONS; ++r) {
+ if (!dev->io_regions[r].size) {
+ continue;
+ }
+ pci_set_long(dev->config + pci_bar(dev, r), dev->io_regions[r].type);
+ }
+ pci_update_mappings(dev);
+}
+
static void pci_bus_reset(void *opaque)
{
- PCIBus *bus = (PCIBus *)opaque;
+ PCIBus *bus = opaque;
int i;
for (i = 0; i < bus->nirq; i++) {
bus->irq_count[i] = 0;
}
- for (i = 0; i < 256; i++) {
- if (bus->devices[i])
- memset(bus->devices[i]->irq_state, 0,
- sizeof(bus->devices[i]->irq_state));
+ for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
+ if (bus->devices[i]) {
+ pci_device_reset(bus->devices[i]);
+ }
}
}
-PCIBus *pci_register_bus(DeviceState *parent, const char *name,
- pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
- qemu_irq *pic, int devfn_min, int nirq)
+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)
+{
+ qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name);
+ bus->devfn_min = devfn_min;
+
+ /* 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);
+}
+
+PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min)
{
PCIBus *bus;
- static int nbus = 0;
- bus = FROM_QBUS(PCIBus, qbus_create(&pci_bus_info, parent, name));
+ bus = qemu_mallocz(sizeof(*bus));
+ bus->qbus.qdev_allocated = 1;
+ pci_bus_new_inplace(bus, parent, name, devfn_min);
+ return bus;
+}
+
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+ void *irq_opaque, int nirq)
+{
bus->set_irq = set_irq;
bus->map_irq = map_irq;
- bus->irq_opaque = pic;
- bus->devfn_min = devfn_min;
+ bus->irq_opaque = irq_opaque;
bus->nirq = nirq;
bus->irq_count = qemu_mallocz(nirq * sizeof(bus->irq_count[0]));
- bus->next = first_bus;
- first_bus = bus;
- vmstate_register(nbus++, &vmstate_pcibus, bus);
- qemu_register_reset(pci_bus_reset, bus);
- return bus;
}
-static PCIBus *pci_register_secondary_bus(PCIDevice *dev,
- pci_map_irq_fn map_irq,
- const char *name)
+void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug)
+{
+ bus->qbus.allow_hotplug = 1;
+ bus->hotplug = hotplug;
+}
+
+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)
{
PCIBus *bus;
- bus = FROM_QBUS(PCIBus, qbus_create(&pci_bus_info, &dev->qdev, name));
+ bus = pci_bus_new(parent, name, devfn_min);
+ pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
+ 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;
- bus->next = dev->bus->next;
- dev->bus->next = bus;
- return 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[size];
+ uint8_t *config;
int 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])
+ 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, size);
pci_update_mappings(s);
+ qemu_free(config);
return 0;
}
/* just put buffer */
-static void put_pci_config_device(QEMUFile *f, const void *pv, size_t size)
+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)
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;
return 0;
}
-static PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
+PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
{
int dom, bus;
unsigned slot;
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)
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,
+ r->filtered_size,
IO_MEM_UNASSIGNED);
}
}
}
-int pci_unregister_device(PCIDevice *pci_dev)
+static int pci_unregister_device(DeviceState *dev)
{
+ PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev);
+ PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->info);
int ret = 0;
- if (pci_dev->unregister)
- ret = pci_dev->unregister(pci_dev);
+ if (info->exit)
+ ret = info->exit(pci_dev);
if (ret)
return ret;
qemu_free_irqs(pci_dev->irq);
pci_dev->bus->devices[pci_dev->devfn] = NULL;
- qdev_free(&pci_dev->qdev);
+ 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(pci_dev, region_num);
if (region_num == PCI_ROM_SLOT) {
- addr = 0x30;
/* ROM enable bit is writeable */
- wmask |= 1;
+ wmask |= PCI_ROM_ADDRESS_ENABLE;
+ }
+ 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 {
- addr = 0x10 + region_num * 4;
+ pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff);
+ pci_set_long(pci_dev->cmask + addr, 0xffffffff);
}
- *(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;
}
-static void pci_update_mappings(PCIDevice *d)
+static uint32_t pci_config_get_io_base(PCIDevice *d,
+ uint32_t base, uint32_t base_upper16)
{
- PCIIORegion *r;
- int cmd, i;
- uint32_t last_addr, new_addr, config_ofs;
+ uint32_t val;
- cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND));
- for(i = 0; i < PCI_NUM_REGIONS; i++) {
- r = &d->io_regions[i];
- if (i == PCI_ROM_SLOT) {
- config_ofs = 0x30;
+ 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 {
- config_ofs = 0x10 + i * 4;
+ base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
}
- if (r->size != 0) {
- if (r->type & PCI_ADDRESS_SPACE_IO) {
- if (cmd & PCI_COMMAND_IO) {
- new_addr = le32_to_cpu(*(uint32_t *)(d->config +
- config_ofs));
- 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;
- }
- } else {
- new_addr = -1;
- }
- } else {
- if (cmd & PCI_COMMAND_MEMORY) {
- new_addr = le32_to_cpu(*(uint32_t *)(d->config +
- config_ofs));
- /* the ROM slot has a specific enable bit */
- if (i == PCI_ROM_SLOT && !(new_addr & 1))
- 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;
- }
+ }
+
+ 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;
}
- /* 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 = d->config[0x0a] | (d->config[0x0b] << 8);
- 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);
- }
+ } 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;
}
-uint32_t pci_default_read_config(PCIDevice *d,
- uint32_t address, int len)
+static pcibus_t pci_bar_address(PCIDevice *d,
+ int reg, uint8_t type, pcibus_t size)
{
- uint32_t val;
+ pcibus_t new_addr, last_addr;
+ int bar = pci_bar(d, reg);
+ uint16_t cmd = pci_get_word(d->config + PCI_COMMAND);
- 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;
+ 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;
}
- return val;
+
+ 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;
}
-void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
+static void pci_update_mappings(PCIDevice *d)
{
- uint8_t orig[PCI_CONFIG_SPACE_SIZE];
+ PCIIORegion *r;
int i;
+ pcibus_t new_addr, filtered_size;
+
+ for(i = 0; i < PCI_NUM_REGIONS; i++) {
+ r = &d->io_regions[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);
+ /* 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 {
+ isa_unassign_ioport(r->addr, r->filtered_size);
+ }
+ } else {
+ cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
+ r->filtered_size,
+ IO_MEM_UNASSIGNED);
+ qemu_unregister_coalesced_mmio(r->addr, r->filtered_size);
+ }
+ }
+ 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.
+ */
+ r->map_func(d, i, r->addr, r->filtered_size, r->type);
+ }
}
- 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)
+static inline int pci_irq_disabled(PCIDevice *d)
{
- PCIBus *s = opaque;
- PCIDevice *pci_dev;
- int config_addr, bus_num;
+ return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
+}
-#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)
+/* 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;
- 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);
+ 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_data_read(void *opaque, uint32_t addr, 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;
- uint32_t val;
+ 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);
+}
- 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;
+void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
+{
+ int i, was_irq_disabled = pci_irq_disabled(d);
+ uint32_t config_size = pci_config_size(d);
+
+ 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);
}
/***********************************************************/
/* 0 <= irq_num <= 3. level must be 0 or 1 */
static void pci_set_irq(void *opaque, int irq_num, int level)
{
- PCIDevice *pci_dev = (PCIDevice *)opaque;
- PCIBus *bus;
+ PCIDevice *pci_dev = opaque;
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;
- if (bus) {
- for(devfn = 0; devfn < 256; devfn++) {
- d = bus->devices[devfn];
- if (d)
- fn(d);
- }
+ for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ d = bus->devices[devfn];
+ if (d)
+ fn(bus, d);
}
}
-void pci_info(Monitor *mon)
+void pci_for_each_device(PCIBus *bus, int bus_num,
+ void (*fn)(PCIBus *b, PCIDevice *d))
{
- pci_for_each_device(0, pci_info_device);
+ bus = pci_find_bus(bus, bus_num);
+
+ if (bus) {
+ pci_for_each_device_under_bus(bus, fn);
+ }
}
-PCIDevice *pci_create(const char *name, const char *devaddr)
+void pci_info(Monitor *mon)
{
- PCIBus *bus;
- int devfn;
- DeviceState *dev;
-
- bus = pci_get_bus_devfn(&devfn, devaddr);
- if (!bus) {
- fprintf(stderr, "Invalid PCI device address %s for device %s\n",
- devaddr, name);
- exit(1);
+ struct PCIHostBus *host;
+ QLIST_FOREACH(host, &host_buses, next) {
+ pci_for_each_device(host->bus, 0, pci_info_device);
}
-
- dev = qdev_create(&bus->qbus, name);
- qdev_prop_set_uint32(dev, "addr", devfn);
- return (PCIDevice *)dev;
}
static const char * const pci_nic_models[] = {
};
/* Initialize a PCI NIC. */
+/* FIXME callers should check for failure, but don't */
PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model,
const char *default_devaddr)
{
const char *devaddr = nd->devaddr ? nd->devaddr : default_devaddr;
+ PCIBus *bus;
+ int devfn;
PCIDevice *pci_dev;
DeviceState *dev;
int i;
- qemu_check_nic_model_list(nd, pci_nic_models, default_model);
-
- for (i = 0; pci_nic_models[i]; i++) {
- if (strcmp(nd->model, pci_nic_models[i]) == 0) {
- pci_dev = pci_create(pci_nic_names[i], devaddr);
- dev = &pci_dev->qdev;
- if (nd->id)
- dev->id = qemu_strdup(nd->id);
- dev->nd = nd;
- qdev_init(dev);
- nd->private = dev;
- return pci_dev;
- }
+ i = qemu_find_nic_model(nd, pci_nic_models, default_model);
+ if (i < 0)
+ return NULL;
+
+ bus = pci_get_bus_devfn(&devfn, devaddr);
+ if (!bus) {
+ qemu_error("Invalid PCI device address %s for device %s\n",
+ devaddr, pci_nic_names[i]);
+ return NULL;
}
- return NULL;
+ pci_dev = pci_create(bus, devfn, pci_nic_names[i]);
+ dev = &pci_dev->qdev;
+ if (nd->name)
+ dev->id = qemu_strdup(nd->name);
+ qdev_set_nic_properties(dev, nd);
+ if (qdev_init(dev) < 0)
+ return NULL;
+ return pci_dev;
+}
+
+PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
+ const char *default_devaddr)
+{
+ PCIDevice *res;
+
+ if (qemu_show_nic_models(nd->model, pci_nic_models))
+ exit(0);
+
+ res = pci_nic_init(nd, default_model, default_devaddr);
+ if (!res)
+ exit(1);
+ return res;
}
typedef struct {
PCIDevice dev;
- PCIBus *bus;
+ 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)
+{
+ 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;
+
+ if (!bus)
+ return NULL;
- while (bus && bus->bus_num != bus_num)
- bus = bus->next;
+ if (pci_bus_num(bus) == bus_num) {
+ return bus;
+ }
- 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;
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] = 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, uint16_t vid, uint16_t did,
pci_map_irq_fn map_irq, const char *name)
{
+ PCIDevice *dev;
PCIBridge *s;
- s = (PCIBridge *)pci_register_device(bus, name, sizeof(PCIBridge),
- devfn, NULL, pci_bridge_write_config);
- pci_config_set_vendor_id(s->dev.config, vid);
- pci_config_set_device_id(s->dev.config, did);
+ dev = pci_create(bus, devfn, "pci-bridge");
+ qdev_prop_set_uint32(&dev->qdev, "vendorid", vid);
+ qdev_prop_set_uint32(&dev->qdev, "deviceid", did);
+ qdev_init_nofail(&dev->qdev);
- 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
+ s = DO_UPCAST(PCIBridge, dev, dev);
+ pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name);
+ return &s->bus;
+}
- s->bus = pci_register_secondary_bus(&s->dev, map_irq, name);
- return s->bus;
+PCIDevice *pci_bridge_get_device(PCIBus *bus)
+{
+ return bus->parent_dev;
}
-static void pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
+static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
{
PCIDevice *pci_dev = (PCIDevice *)qdev;
PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev);
PCIBus *bus;
- int devfn;
+ 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->init(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_unplug_device(DeviceState *qdev)
+{
+ PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
+
+ dev->bus->hotplug(dev, 0);
+ return 0;
}
void pci_qdev_register(PCIDeviceInfo *info)
{
info->qdev.init = pci_qdev_init;
+ info->qdev.unplug = pci_unplug_device;
+ info->qdev.exit = pci_unregister_device;
info->qdev.bus_info = &pci_bus_info;
qdev_register(&info->qdev);
}
}
}
-PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name)
+PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name)
{
DeviceState *dev;
dev = qdev_create(&bus->qbus, name);
qdev_prop_set_uint32(dev, "addr", devfn);
- qdev_init(dev);
+ return DO_UPCAST(PCIDevice, qdev, dev);
+}
- return (PCIDevice *)dev;
+PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name)
+{
+ PCIDevice *dev = pci_create(bus, devfn, name);
+ qdev_init_nofail(&dev->qdev);
+ return dev;
}
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;
+
+ 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);
}
}
+
+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,
+ .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)