From patchwork Fri Oct 29 15:27:20 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alex Williamson X-Patchwork-Id: 69604 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DD5F1B70D6 for ; Sat, 30 Oct 2010 02:28:49 +1100 (EST) Received: from localhost ([127.0.0.1]:57927 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PBqsm-0001UA-Rm for incoming@patchwork.ozlabs.org; Fri, 29 Oct 2010 11:28:44 -0400 Received: from [140.186.70.92] (port=51995 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PBqrV-0001Rc-V7 for qemu-devel@nongnu.org; Fri, 29 Oct 2010 11:27:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PBqrT-00036L-VX for qemu-devel@nongnu.org; Fri, 29 Oct 2010 11:27:25 -0400 Received: from mx1.redhat.com ([209.132.183.28]:65109) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PBqrT-000368-I8 for qemu-devel@nongnu.org; Fri, 29 Oct 2010 11:27:23 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o9TFRLDm013174 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 29 Oct 2010 11:27:21 -0400 Received: from s20.home (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id o9TFRKLF010325; Fri, 29 Oct 2010 11:27:20 -0400 From: Alex Williamson To: qemu-devel@nongnu.org, mst@redhat.com Date: Fri, 29 Oct 2010 09:27:20 -0600 Message-ID: <20101029152703.9046.17453.stgit@s20.home> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: chrisw@redhat.com, alex.williamson@redhat.com, ddutile@redhat.com, kvm@vger.kernel.org Subject: [Qemu-devel] [PATCH] pci: Add callbacks to support retreiving and updating interrupts X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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(-) diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 0ecac55..47ff0d9 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -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++) { diff --git a/hw/bonito.c b/hw/bonito.c index dcf0311..d2869bb 100644 --- a/hw/bonito.c +++ b/hw/bonito.c @@ -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); diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index 91c755f..e747d7e 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -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); diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index cabf7ea..2a0fc4a 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -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); diff --git a/hw/pci.c b/hw/pci.c index 1280d4d..645b119 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -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) { diff --git a/hw/pci.h b/hw/pci.h index 3d23f03..1502dc1 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -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); diff --git a/hw/piix_pci.c b/hw/piix_pci.c index b5589b9..8c3f845 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -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 */ } diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c index 6e437e7..5f3faf7 100644 --- a/hw/ppc4xx_pci.c +++ b/hw/ppc4xx_pci.c @@ -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); diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index 8ac99f2..a6e64d4 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -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); diff --git a/hw/prep_pci.c b/hw/prep_pci.c index 0c2afe9..d2e5c4b 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -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); diff --git a/hw/sh_pci.c b/hw/sh_pci.c index cc2f190..b39c78b 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -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); diff --git a/hw/unin_pci.c b/hw/unin_pci.c index 1310211..ec2f246 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -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); diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index a76bdfa..7f85e77 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -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. */