rtoss

Subversion Repositories:
Compare Path: Rev
With Path: Rev
/qemu-pc9821/qemu/hw/pci.c @ 76  →  /qemu-pc9821/qemu/hw/pci.c @ 77
/qemu-pc9821/qemu/hw/pci.c
@@ -40,6 +40,7 @@
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];
@@ -83,6 +84,29 @@
}
};
 
static inline int pci_bar(int reg)
{
return reg == PCI_ROM_SLOT ? PCI_ROM_ADDRESS : PCI_BASE_ADDRESS_0 + reg * 4;
}
 
static void pci_device_reset(PCIDevice *dev)
{
int r;
 
memset(dev->irq_state, 0, sizeof dev->irq_state);
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(r), dev->io_regions[r].type);
}
pci_update_mappings(dev);
}
 
static void pci_bus_reset(void *opaque)
{
PCIBus *bus = opaque;
@@ -91,46 +115,73 @@
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,
void *irq_opaque, int devfn_min, int nirq)
void pci_bus_new_inplace(PCIBus *bus, 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->set_irq = set_irq;
bus->map_irq = map_irq;
bus->irq_opaque = irq_opaque;
qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name);
bus->devfn_min = devfn_min;
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);
}
 
PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min)
{
PCIBus *bus;
 
bus = qemu_mallocz(sizeof(*bus));
bus->qbus.qdev_allocated = 1;
pci_bus_new_inplace(bus, parent, name, devfn_min);
return bus;
}
 
static PCIBus *pci_register_secondary_bus(PCIDevice *dev,
pci_map_irq_fn map_irq,
const char *name)
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 = irq_opaque;
bus->nirq = nirq;
bus->irq_count = qemu_mallocz(nirq * sizeof(bus->irq_count[0]));
}
 
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 *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;
}
 
int pci_bus_num(PCIBus *s)
@@ -141,14 +192,15 @@
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[PCI_CONFIG_SPACE_SIZE];
int i;
 
qemu_get_buffer(f, config, size);
for (i = 0; i < size; ++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])
return -EINVAL;
memcpy(s->config, config, size);
memcpy(s->config, config, sizeof config);
 
pci_update_mappings(s);
 
@@ -265,7 +317,7 @@
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;
@@ -378,12 +430,14 @@
}
}
 
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;
 
@@ -391,7 +445,6 @@
 
qemu_free_irqs(pci_dev->irq);
pci_dev->bus->devices[pci_dev->devfn] = NULL;
qdev_free(&pci_dev->qdev);
return 0;
}
 
@@ -419,12 +472,10 @@
r->map_func = map_func;
 
wmask = ~(size - 1);
addr = pci_bar(region_num);
if (region_num == PCI_ROM_SLOT) {
addr = 0x30;
/* ROM enable bit is writeable */
wmask |= 1;
} else {
addr = 0x10 + region_num * 4;
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);
@@ -435,21 +486,15 @@
{
PCIIORegion *r;
int cmd, i;
uint32_t last_addr, new_addr, config_ofs;
uint32_t last_addr, new_addr;
 
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;
} else {
config_ofs = 0x10 + i * 4;
}
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 = 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 */
@@ -462,10 +507,9 @@
}
} else {
if (cmd & PCI_COMMAND_MEMORY) {
new_addr = le32_to_cpu(*(uint32_t *)(d->config +
config_ofs));
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 & 1))
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;
@@ -489,7 +533,7 @@
int class;
/* NOTE: specific hack for IDE in PC case:
only one byte must be mapped. */
class = d->config[0x0a] | (d->config[0x0b] << 8);
class = pci_get_word(d->config + PCI_CLASS_DEVICE);
if (class == 0x0101 && r->size == 4) {
isa_unassign_ioport(r->addr + 2, 1);
} else {
@@ -764,24 +808,6 @@
pci_for_each_device(0, pci_info_device);
}
 
PCIDevice *pci_create(const char *name, const char *devaddr)
{
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);
}
 
dev = qdev_create(&bus->qbus, name);
qdev_prop_set_uint32(dev, "addr", devfn);
return (PCIDevice *)dev;
}
 
static const char * const pci_nic_models[] = {
"ne2k_pci",
"i82551",
@@ -807,35 +833,58 @@
};
 
/* 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);
i = qemu_find_nic_model(nd, pci_nic_models, default_model);
if (i < 0)
return NULL;
 
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;
}
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);
dev->nd = nd;
if (qdev_init(dev) < 0)
return NULL;
nd->private = dev;
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_write_config(PCIDevice *d,
@@ -844,7 +893,7 @@
PCIBridge *s = (PCIBridge *)d;
 
pci_default_write_config(d, address, val, len);
s->bus->bus_num = d->config[PCI_SECONDARY_BUS];
s->bus.bus_num = d->config[PCI_SECONDARY_BUS];
}
 
PCIBus *pci_find_bus(int bus_num)
@@ -867,15 +916,12 @@
return bus->devices[PCI_DEVFN(slot, function)];
}
 
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did,
pci_map_irq_fn map_irq, const char *name)
static int pci_bridge_initfn(PCIDevice *dev)
{
PCIBridge *s;
s = (PCIBridge *)pci_register_device(bus, name, sizeof(PCIBridge),
devfn, NULL, pci_bridge_write_config);
PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev);
 
pci_config_set_vendor_id(s->dev.config, vid);
pci_config_set_device_id(s->dev.config, did);
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;
@@ -888,9 +934,23 @@
s->dev.config[PCI_HEADER_TYPE] =
PCI_HEADER_TYPE_MULTI_FUNCTION | PCI_HEADER_TYPE_BRIDGE; // header_type
s->dev.config[0x1E] = 0xa0; // secondary status
return 0;
}
 
s->bus = pci_register_secondary_bus(&s->dev, map_irq, name);
return s->bus;
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;
 
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 = DO_UPCAST(PCIBridge, dev, dev);
pci_register_secondary_bus(&s->bus, &s->dev, map_irq, name);
return &s->bus;
}
 
static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
@@ -898,19 +958,34 @@
PCIDevice *pci_dev = (PCIDevice *)qdev;
PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev);
PCIBus *bus;
int devfn;
int devfn, rc;
 
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);
return info->init(pci_dev);
rc = info->init(pci_dev);
if (rc != 0)
return rc;
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);
}
@@ -923,7 +998,7 @@
}
}
 
PCIDevice *pci_create_noinit(PCIBus *bus, int devfn, const char *name)
PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name)
{
DeviceState *dev;
 
@@ -934,8 +1009,8 @@
 
PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name)
{
PCIDevice *dev = pci_create_noinit(bus, devfn, name);
qdev_init(&dev->qdev);
PCIDevice *dev = pci_create(bus, devfn, name);
qdev_init_nofail(&dev->qdev);
return dev;
}
 
@@ -1051,3 +1126,22 @@
r->addr, r->addr + r->size - 1);
}
}
 
static PCIDeviceInfo bridge_info = {
.qdev.name = "pci-bridge",
.qdev.size = sizeof(PCIBridge),
.init = pci_bridge_initfn,
.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)