From patchwork Mon Sep 23 11:01:17 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marcel Apfelbaum X-Patchwork-Id: 277150 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DF9D62C00E8 for ; Mon, 23 Sep 2013 21:01:38 +1000 (EST) Received: from localhost ([::1]:39644 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VO3tY-0006Dk-AD for incoming@patchwork.ozlabs.org; Mon, 23 Sep 2013 07:01:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57271) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VO3tD-0006Cl-HF for qemu-devel@nongnu.org; Mon, 23 Sep 2013 07:01:21 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VO3t7-0003eh-H5 for qemu-devel@nongnu.org; Mon, 23 Sep 2013 07:01:15 -0400 Received: from mx1.redhat.com ([209.132.183.28]:20763) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VO3t7-0003ea-9A for qemu-devel@nongnu.org; Mon, 23 Sep 2013 07:01:09 -0400 Received: from int-mx01.intmail.prod.int.phx2.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r8NB16IV007975 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 23 Sep 2013 07:01:06 -0400 Received: from localhost.localdomain.com (vpn-203-229.tlv.redhat.com [10.35.203.229]) by int-mx01.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id r8NB12nM006758; Mon, 23 Sep 2013 07:01:03 -0400 From: Marcel Apfelbaum To: qemu-devel@nongnu.org Date: Mon, 23 Sep 2013 14:01:17 +0300 Message-Id: <1379934077-3188-1-git-send-email-marcel.a@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.11 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: peter.maydell@linaro.org, aliguori@us.ibm.com, mst@redhat.com Subject: [Qemu-devel] [PATCH] hw/pci: completed master-abort emulation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch is implemented on top of series: [PATCH v5 0/3] pci: implement upstream master abort protocol Added "master abort io" background region for PCIBus. Added "master abort mem" region to catch transactions initiated by pci devices targeted to unassigned addresses. Enabled "master abort" regions for PCI-2-PCI bridge's secondary bus. Set "Received Master Abort" Bit on Status/Secondary Status register as defined in the PCI Spec. Signed-off-by: Marcel Apfelbaum --- hw/pci/pci.c | 115 ++++++++++++++++++++++++++++++++++++++++++----- hw/pci/pci_bridge.c | 10 +++++ include/hw/pci/pci.h | 3 ++ include/hw/pci/pci_bus.h | 1 + 4 files changed, 118 insertions(+), 11 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index d8a1b11..1f4e707 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -283,17 +283,76 @@ const char *pci_root_bus_path(PCIDevice *dev) return rootbus->qbus.name; } +static PCIDevice *pci_bus_find_host(PCIBus *bus) +{ + PCIDevice *dev; + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + dev = bus->devices[i]; + if (dev) { + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + if (pc->class_id == PCI_CLASS_BRIDGE_HOST) { + return dev; + } + } + } + + return NULL; +} + +static void master_abort(void *opaque) +{ + PCIDevice *master_dev = NULL; + PCIDeviceClass *pc; + PCIBus *bus; + int downstream; + + if (object_dynamic_cast(OBJECT(opaque), TYPE_PCI_DEVICE)) { + master_dev = PCI_DEVICE(opaque); + bus = master_dev->bus; + while (!pci_bus_is_root(bus)) { + master_dev = bus->parent_dev; + bus = master_dev->bus; + } + downstream = 0; + } + + if (object_dynamic_cast(OBJECT(opaque), TYPE_PCI_BUS)) { + bus = PCI_BUS(opaque); + if (pci_bus_is_root(bus)) { + master_dev = pci_bus_find_host(bus); + } else { /* bus behind a PCI-2-PCI bridge */ + master_dev = bus->parent_dev; + } + downstream = 1; + } + + assert(master_dev); + pc = PCI_DEVICE_GET_CLASS(master_dev); + + if (downstream && pc->is_bridge) { + pci_word_test_and_set_mask(master_dev->config + PCI_SEC_STATUS, + PCI_STATUS_REC_MASTER_ABORT); + } else { + pci_word_test_and_set_mask(master_dev->config + PCI_STATUS, + PCI_STATUS_REC_MASTER_ABORT); + } +} + static uint64_t master_abort_mem_read(void *opaque, hwaddr addr, unsigned size) { - return -1ULL; + master_abort(opaque); + return -1ULL; } static void master_abort_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { + master_abort(opaque); } -static const MemoryRegionOps master_abort_mem_ops = { +static const MemoryRegionOps master_abort_ops = { .read = master_abort_mem_read, .write = master_abort_mem_write, .endianness = DEVICE_LITTLE_ENDIAN, @@ -301,6 +360,23 @@ static const MemoryRegionOps master_abort_mem_ops = { #define MASTER_ABORT_MEM_PRIORITY INT_MIN +void pci_bus_master_abort_init(PCIBus *bus, const char *mem, const char *io) +{ + memory_region_init_io(&bus->master_abort_mem, OBJECT(bus), + &master_abort_ops, bus, mem, + memory_region_size(bus->address_space_mem)); + memory_region_add_subregion_overlap(bus->address_space_mem, + 0, &bus->master_abort_mem, + MASTER_ABORT_MEM_PRIORITY); + + memory_region_init_io(&bus->master_abort_io, OBJECT(bus), + &master_abort_ops, bus, io, + memory_region_size(bus->address_space_io)); + memory_region_add_subregion_overlap(bus->address_space_io, + 0, &bus->master_abort_io, + MASTER_ABORT_MEM_PRIORITY); +} + static void pci_bus_init(PCIBus *bus, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, @@ -312,13 +388,8 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent, bus->address_space_mem = address_space_mem; bus->address_space_io = address_space_io; - - memory_region_init_io(&bus->master_abort_mem, OBJECT(bus), - &master_abort_mem_ops, bus, "pci-master-abort", - memory_region_size(bus->address_space_mem)); - memory_region_add_subregion_overlap(bus->address_space_mem, - 0, &bus->master_abort_mem, - MASTER_ABORT_MEM_PRIORITY); + pci_bus_master_abort_init(bus, "pci-master-abort-mem", + "pci-master-abort-io"); /* host bridge */ QLIST_INIT(&bus->child); @@ -840,9 +911,24 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, pci_dev->bus = bus; dma_as = pci_device_iommu_address_space(pci_dev); - memory_region_init_alias(&pci_dev->bus_master_enable_region, - OBJECT(pci_dev), "bus master", + memory_region_init(&pci_dev->bus_master_enable_region, NULL, + "bus master", UINT64_MAX); + + memory_region_init_io(&pci_dev->bus_master_abort_mem, OBJECT(bus), + &master_abort_ops, OBJECT(pci_dev), + "bus master master-abort", + memory_region_size(&pci_dev->bus_master_enable_region)); + memory_region_add_subregion_overlap(&pci_dev->bus_master_enable_region, + 0, &pci_dev->bus_master_abort_mem, + MASTER_ABORT_MEM_PRIORITY); + + memory_region_init_alias(&pci_dev->bus_master_dma_mem, + OBJECT(pci_dev), "bus master dma", dma_as->root, 0, memory_region_size(dma_as->root)); + memory_region_add_subregion_overlap(&pci_dev->bus_master_enable_region, + 0, &pci_dev->bus_master_dma_mem, 0); + + memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region, name); @@ -901,6 +987,13 @@ static void do_pci_unregister_device(PCIDevice *pci_dev) pci_config_free(pci_dev); address_space_destroy(&pci_dev->bus_master_as); + memory_region_del_subregion(&pci_dev->bus_master_enable_region, + &pci_dev->bus_master_dma_mem); + memory_region_del_subregion(&pci_dev->bus_master_enable_region, + &pci_dev->bus_master_abort_mem); + memory_region_destroy(&pci_dev->bus_master_dma_mem); + memory_region_destroy(&pci_dev->bus_master_abort_mem); + memory_region_destroy(&pci_dev->bus_master_enable_region); } diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index e6b22b8..56b682f 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -376,6 +376,10 @@ int pci_bridge_initfn(PCIDevice *dev, const char *typename) sec_bus->address_space_io = &br->address_space_io; memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", 65536); br->windows = pci_bridge_region_init(br); + + pci_bus_master_abort_init(sec_bus, "pci_bridge_master_abort_mem", + "pci_bridge_master_abort_io"); + QLIST_INIT(&sec_bus->child); QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); return 0; @@ -389,6 +393,12 @@ void pci_bridge_exitfn(PCIDevice *pci_dev) QLIST_REMOVE(&s->sec_bus, sibling); pci_bridge_region_del(s, s->windows); pci_bridge_region_cleanup(s, s->windows); + memory_region_del_subregion(s->sec_bus.address_space_mem, + &s->sec_bus.master_abort_mem); + memory_region_del_subregion(s->sec_bus.address_space_io, + &s->sec_bus.master_abort_io); + memory_region_destroy(&s->sec_bus.master_abort_mem); + memory_region_destroy(&s->sec_bus.master_abort_io); memory_region_destroy(&s->address_space_mem); memory_region_destroy(&s->address_space_io); /* qbus_free() is called automatically by qdev_free() */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 37979aa..d69e06d 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -242,6 +242,8 @@ struct PCIDevice { PCIIORegion io_regions[PCI_NUM_REGIONS]; AddressSpace bus_master_as; MemoryRegion bus_master_enable_region; + MemoryRegion bus_master_dma_mem; + MemoryRegion bus_master_abort_mem; /* do not access the following fields */ PCIConfigReadFunc *config_read; @@ -357,6 +359,7 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min, const char *typename); +void pci_bus_master_abort_init(PCIBus *bus, const char *mem, const char *io); void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 2ad5edb..57b1541 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -24,6 +24,7 @@ struct PCIBus { MemoryRegion *address_space_mem; MemoryRegion *address_space_io; MemoryRegion master_abort_mem; + MemoryRegion master_abort_io; QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */