@@ -336,8 +336,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
d = FROM_SYSBUS(APBState, s);
d->bus = pci_register_bus(&d->busdev.qdev, "pci",
- pci_apb_set_irq, pci_pbm_map_irq, d,
- 0, 32);
+ pci_apb_set_irq, NULL,
+ pci_pbm_map_irq, d, 0, 32);
pci_bus_set_mem_base(d->bus, mem_base);
for (i = 0; i < 32; i++) {
@@ -772,7 +772,7 @@ PCIBus *bonito_init(qemu_irq *pic)
dev = qdev_create(NULL, "Bonito-pcihost");
pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev));
b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq,
- pci_bonito_map_irq, pic, 0x28, 32);
+ NULL, pci_bonito_map_irq, pic, 0x28, 32);
pcihost->bus = b;
qdev_init_nofail(dev);
@@ -89,7 +89,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic)
s = sysbus_from_qdev(dev);
d = FROM_SYSBUS(GrackleState, s);
d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
- pci_grackle_set_irq,
+ pci_grackle_set_irq, NULL,
pci_grackle_map_irq,
pic, 0, 4);
@@ -1114,8 +1114,9 @@ PCIBus *pci_gt64120_init(qemu_irq *pic)
s->pci = qemu_mallocz(sizeof(GT64120PCIState));
s->pci->bus = pci_register_bus(NULL, "pci",
- pci_gt64120_set_irq, pci_gt64120_map_irq,
- pic, PCI_DEVFN(18, 0), 4);
+ pci_gt64120_set_irq, NULL,
+ pci_gt64120_map_irq, pic,
+ PCI_DEVFN(18, 0), 4);
s->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, s);
d = pci_register_device(s->pci->bus, "GT64120 PCI Bus", sizeof(PCIDevice),
0, NULL, NULL);
@@ -41,6 +41,7 @@ struct PCIBus {
BusState qbus;
int devfn_min;
pci_set_irq_fn set_irq;
+ pci_get_irq_fn get_irq;
pci_map_irq_fn map_irq;
pci_hotplug_fn hotplug;
DeviceState *hotplug_qdev;
@@ -139,6 +140,23 @@ static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change)
bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
}
+int pci_get_irq(PCIDevice *pci_dev, int pin)
+{
+ PCIBus *bus;
+ for (;;) {
+ if (!pci_dev)
+ return -ENOSYS;
+ bus = pci_dev->bus;
+ if (!bus)
+ return -ENOSYS;
+ pin = bus->map_irq(pci_dev, pin);
+ if (bus->get_irq)
+ break;
+ pci_dev = bus->parent_dev;
+ }
+ return bus->get_irq(bus->irq_opaque, pin);
+}
+
/* Update interrupt status bit in config space on interrupt
* state change. */
static void pci_update_irq_status(PCIDevice *dev)
@@ -260,10 +278,11 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name, int 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)
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+ pci_map_irq_fn map_irq, void *irq_opaque, int nirq)
{
bus->set_irq = set_irq;
+ bus->get_irq = get_irq;
bus->map_irq = map_irq;
bus->irq_opaque = irq_opaque;
bus->nirq = nirq;
@@ -283,13 +302,14 @@ void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t 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)
+ pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+ pci_map_irq_fn map_irq, void *irq_opaque,
+ int devfn_min, int nirq)
{
PCIBus *bus;
bus = pci_bus_new(parent, name, devfn_min);
- pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq);
+ pci_bus_irqs(bus, set_irq, get_irq, map_irq, irq_opaque, nirq);
return bus;
}
@@ -1541,6 +1561,28 @@ typedef struct {
uint32_t did;
} PCIBridge;
+void pci_register_update_irqs(PCIDevice *d, PCIUpdateIRQs *update_irqs)
+{
+ d->update_irqs = update_irqs;
+}
+
+static void pci_bridge_update_irqs_fn(PCIBus *b, PCIDevice *d)
+{
+ if (d->update_irqs) {
+ d->update_irqs(d);
+ }
+}
+
+void pci_bridge_update_irqs(PCIBus *b)
+{
+ PCIBus *child;
+
+ pci_for_each_device_under_bus(b, pci_bridge_update_irqs_fn);
+
+ QLIST_FOREACH(child, &b->child, sibling) {
+ pci_bridge_update_irqs(child);
+ }
+}
static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d)
{
@@ -77,6 +77,7 @@ typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
uint32_t address, uint32_t data, int len);
typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
uint32_t address, int len);
+typedef void PCIUpdateIRQs(PCIDevice *pci_dev);
typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
pcibus_t addr, pcibus_t size, int type);
typedef int PCIUnregisterFunc(PCIDevice *pci_dev);
@@ -141,6 +142,7 @@ struct PCIDevice {
/* do not access the following fields */
PCIConfigReadFunc *config_read;
PCIConfigWriteFunc *config_write;
+ PCIUpdateIRQs *update_irqs;
/* IRQ objects for the INTA-INTD pins. */
qemu_irq *irq;
@@ -201,18 +203,24 @@ void pci_default_write_config(PCIDevice *d,
void pci_device_save(PCIDevice *s, QEMUFile *f);
int pci_device_load(PCIDevice *s, QEMUFile *f);
+int pci_get_irq(PCIDevice *pci_dev, int pin);
+
typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level);
+typedef int (*pci_get_irq_fn)(void *opaque, int irq_num);
typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, int state);
void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
const char *name, int devfn_min);
PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min);
-void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
- void *irq_opaque, int nirq);
+void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+ pci_map_irq_fn map_irq, void *irq_opaque, int nirq);
+void pci_bridge_update_irqs(PCIBus *bus);
+void pci_register_update_irqs(PCIDevice *pci_dev, PCIUpdateIRQs *update_irqs);
void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
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);
+ pci_set_irq_fn set_irq, pci_get_irq_fn get_irq,
+ pci_map_irq_fn map_irq, void *irq_opaque,
+ int devfn_min, int nirq);
void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base);
@@ -55,7 +55,10 @@ struct PCII440FXState {
#define I440FX_PAM_SIZE 7
#define I440FX_SMRAM 0x72
+#define PIIX_CONFIG_IRQ_ROUTE 0x60
+
static void piix3_set_irq(void *opaque, int irq_num, int level);
+static int piix3_get_irq(void *opaque, int irq_num);
/* return the global irq number corresponding to a given device irq
pin. We could also use the bus number to have a more precise
@@ -236,7 +239,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *
piix3 = DO_UPCAST(PIIX3State, dev,
pci_create_simple_multifunction(b, -1, true, "PIIX3"));
piix3->pic = pic;
- pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, 4);
+ pci_bus_irqs(b, piix3_set_irq, piix3_get_irq, pci_slot_get_pirq, piix3, 4);
(*pi440fx_state)->piix3 = piix3;
*piix3_devfn = piix3->dev.devfn;
@@ -273,6 +276,13 @@ static void piix3_set_irq(void *opaque, int irq_num, int level)
}
}
+static int piix3_get_irq(void *opaque, int irq_num)
+{
+ PIIX3State *piix3 = opaque;
+
+ return piix3->dev.config[0x60 + irq_num];
+}
+
static void piix3_reset(void *opaque)
{
PIIX3State *d = opaque;
@@ -311,6 +321,16 @@ static void piix3_reset(void *opaque)
pci_conf[0xae] = 0x00;
memset(d->pci_irq_levels, 0, sizeof(d->pci_irq_levels));
+ pci_bridge_update_irqs(d->dev.bus);
+}
+
+static void piix3_write_config(PCIDevice *dev,
+ uint32_t address, uint32_t val, int len)
+{
+ pci_default_write_config(dev, address, val, len);
+ if (ranges_overlap(address, len, PIIX_CONFIG_IRQ_ROUTE, 4)) {
+ pci_bridge_update_irqs(dev->bus);
+ }
}
static const VMStateDescription vmstate_piix3 = {
@@ -357,6 +377,7 @@ static PCIDeviceInfo i440fx_info[] = {
.qdev.vmsd = &vmstate_piix3,
.qdev.no_user = 1,
.init = piix3_initfn,
+ .config_write = piix3_write_config,
},{
/* end of list */
}
@@ -358,7 +358,7 @@ PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
controller = qemu_mallocz(sizeof(PPC4xxPCIState));
controller->pci_state.bus = pci_register_bus(NULL, "pci",
- ppc4xx_pci_set_irq,
+ ppc4xx_pci_set_irq, NULL,
ppc4xx_pci_map_irq,
pci_irqs, 0, 4);
@@ -277,7 +277,7 @@ PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers)
controller = qemu_mallocz(sizeof(PPCE500PCIState));
controller->pci_state.bus = pci_register_bus(NULL, "pci",
- mpc85xx_pci_set_irq,
+ mpc85xx_pci_set_irq, NULL,
mpc85xx_pci_map_irq,
pci_irqs, PCI_DEVFN(0x11, 0),
4);
@@ -118,7 +118,7 @@ PCIBus *pci_prep_init(qemu_irq *pic)
s = qemu_mallocz(sizeof(PREPPCIState));
s->bus = pci_register_bus(NULL, "pci",
- prep_set_irq, prep_map_irq, pic, 0, 4);
+ prep_set_irq, NULL, prep_map_irq, pic, 0, 4);
pci_host_conf_register_ioport(0xcf8, s);
@@ -99,7 +99,7 @@ PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
p = qemu_mallocz(sizeof(SHPCIC));
p->bus = pci_register_bus(NULL, "pci",
- set_irq, map_irq, opaque, devfn_min, nirq);
+ set_irq, NULL, map_irq, opaque, devfn_min, nirq);
p->dev = pci_register_device(p->bus, "SH PCIC", sizeof(PCIDevice),
-1, NULL, NULL);
@@ -229,8 +229,9 @@ PCIBus *pci_pmac_init(qemu_irq *pic)
s = sysbus_from_qdev(dev);
d = FROM_SYSBUS(UNINState, s);
d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
- pci_unin_set_irq, pci_unin_map_irq,
- pic, PCI_DEVFN(11, 0), 4);
+ pci_unin_set_irq, NULL,
+ pci_unin_map_irq, pic,
+ PCI_DEVFN(11, 0), 4);
#if 0
pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north");
@@ -281,8 +282,9 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic)
d = FROM_SYSBUS(UNINState, s);
d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
- pci_unin_set_irq, pci_unin_map_irq,
- pic, PCI_DEVFN(11, 0), 4);
+ pci_unin_set_irq, NULL,
+ pci_unin_map_irq, pic,
+ PCI_DEVFN(11, 0), 4);
sysbus_mmio_map(s, 0, 0xf0800000);
sysbus_mmio_map(s, 1, 0xf0c00000);
@@ -126,7 +126,7 @@ static int pci_vpb_init(SysBusDevice *dev)
sysbus_init_irq(dev, &s->irq[i]);
}
bus = pci_register_bus(&dev->qdev, "pci",
- pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
+ pci_vpb_set_irq, NULL, pci_vpb_map_irq, s->irq,
PCI_DEVFN(11, 0), 4);
/* ??? Register memory space. */
For device assignment, we need to be able to retreive the IRQ that the device interrupt pin is assigned to and be notified if that mapping changes (this happens via ACPI interrupt link updates on x86). We can then make use of the IRQ for EOI notification. Current qemu-kvm device assignment code invades common code with some hard coded hacks to achieve this. This attempts to architect the solution. Chipset components responsible for interrupt mapping can call pci_bridge_update_irqs() to signal when interrupt mapping may have changed. The bridge for those chipsets should then implement a get_irq callback. This is stubbed out for everybody as I only know how PIIX3 works. Devices wishing to be notified about IRQ updates can register via pci_register_update_irqs(), where they can then check mappings with pci_get_irq(). Signed-off-by: Alex Williamson <alex.williamson@redhat.com> --- hw/apb_pci.c | 4 ++-- hw/bonito.c | 2 +- hw/grackle_pci.c | 2 +- hw/gt64xxx.c | 5 +++-- hw/pci.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- hw/pci.h | 16 ++++++++++++---- hw/piix_pci.c | 23 ++++++++++++++++++++++- hw/ppc4xx_pci.c | 2 +- hw/ppce500_pci.c | 2 +- hw/prep_pci.c | 2 +- hw/sh_pci.c | 2 +- hw/unin_pci.c | 10 ++++++---- hw/versatile_pci.c | 2 +- 13 files changed, 99 insertions(+), 25 deletions(-)