diff --git a/Makefile.objs b/Makefile.objs index 67f1b215b1..594894bbed 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -139,7 +139,7 @@ user-obj-y += cutils.o cache-utils.o hw-obj-y = hw-obj-y += vl.o loader.o hw-obj-y += virtio.o virtio-console.o -hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o +hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o hw-obj-y += watchdog.o hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o hw-obj-$(CONFIG_ECC) += ecc.o diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 0ecac55792..88ee4a9d92 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -29,6 +29,7 @@ #include "sysbus.h" #include "pci.h" #include "pci_host.h" +#include "pci_bridge.h" #include "rwhandler.h" #include "apb_pci.h" #include "sysemu.h" diff --git a/hw/dec_pci.c b/hw/dec_pci.c index ee49d5adf0..f7a9cdcfc3 100644 --- a/hw/dec_pci.c +++ b/hw/dec_pci.c @@ -27,6 +27,7 @@ #include "sysbus.h" #include "pci.h" #include "pci_host.h" +#include "pci_bridge.h" /* debug DEC */ //#define DEBUG_DEC diff --git a/hw/pci.c b/hw/pci.c index 9c83d747ea..2dc157724a 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -23,6 +23,7 @@ */ #include "hw.h" #include "pci.h" +#include "pci_bridge.h" #include "pci_internals.h" #include "monitor.h" #include "net.h" @@ -272,26 +273,6 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, 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; - - 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) { if (!s->parent_dev) @@ -799,75 +780,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, } } -static uint32_t pci_config_get_io_base(PCIDevice *d, - uint32_t base, uint32_t base_upper16) -{ - uint32_t val; - - 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 { - base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); - } - } - - 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) { @@ -1518,7 +1430,7 @@ static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d) pci_update_mappings(d); } -static void pci_bridge_update_mappings(PCIBus *b) +void pci_bridge_update_mappings(PCIBus *b) { PCIBus *child; @@ -1529,23 +1441,6 @@ static void pci_bridge_update_mappings(PCIBus *b) } } -static void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(d, address, val, len); - - 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)) { - PCIBridge *s = container_of(d, PCIBridge, dev); - PCIBus *secondary_bus = &s->bus; - pci_bridge_update_mappings(secondary_bus); - } -} - PCIBus *pci_find_bus(PCIBus *bus, int bus_num) { PCIBus *sec; @@ -1589,54 +1484,6 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function) 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] = - (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | - 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, bool multifunction, - uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name) -{ - PCIDevice *dev; - PCIBridge *s; - - dev = pci_create_multifunction(bus, devfn, multifunction, "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(bus, &s->bus, &s->dev, map_irq, name); - return &s->bus; -} - -PCIDevice *pci_bridge_get_device(PCIBus *bus) -{ - return bus->parent_dev; -} - static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) { PCIDevice *pci_dev = (PCIDevice *)qdev; @@ -1942,23 +1789,3 @@ static char *pcibus_get_dev_path(DeviceState *dev) return strdup(path); } -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, - .is_bridge = 1, - .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) diff --git a/hw/pci.h b/hw/pci.h index 1eab7e7dda..c551f9661c 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -233,10 +233,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, void do_pci_info_print(Monitor *mon, const QObject *data); void do_pci_info(Monitor *mon, QObject **ret_data); -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction, - uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name); -PCIDevice *pci_bridge_get_device(PCIBus *bus); +void pci_bridge_update_mappings(PCIBus *b); static inline void pci_set_byte(uint8_t *config, uint8_t val) diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c new file mode 100644 index 0000000000..7f2787091c --- /dev/null +++ b/hw/pci_bridge.c @@ -0,0 +1,208 @@ +/* + * QEMU PCI bus manager + * + * Copyright (c) 2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to dea + + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM + + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * split out from pci.c + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + */ + +#include "pci_bridge.h" +#include "pci_internals.h" + +PCIDevice *pci_bridge_get_device(PCIBus *bus) +{ + return bus->parent_dev; +} + +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; + + 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); +} + +static uint32_t pci_config_get_io_base(PCIDevice *d, + uint32_t base, uint32_t base_upper16) +{ + uint32_t val; + + 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; +} + +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 { + base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); + } + } + + return base; +} + +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_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + pci_default_write_config(d, address, val, len); + + 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)) { + PCIBridge *s = container_of(d, PCIBridge, dev); + PCIBus *secondary_bus = &s->bus; + pci_bridge_update_mappings(secondary_bus); + } +} + +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] = + (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | + 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, bool multifunction, + uint16_t vid, uint16_t did, + pci_map_irq_fn map_irq, const char *name) +{ + PCIDevice *dev; + PCIBridge *s; + + dev = pci_create_multifunction(bus, devfn, multifunction, "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(bus, &s->bus, &s->dev, map_irq, name); + return &s->bus; +} + +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, + .is_bridge = 1, + .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) diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h new file mode 100644 index 0000000000..ddb2c82e25 --- /dev/null +++ b/hw/pci_bridge.h @@ -0,0 +1,48 @@ +/* + * QEMU PCI bridge + * + * Copyright (c) 2004 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc] + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef QEMU_PCI_BRIDGE_H +#define QEMU_PCI_BRIDGE_H + +#include "pci.h" + +PCIDevice *pci_bridge_get_device(PCIBus *bus); + +pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type); +pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type); + +PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction, + uint16_t vid, uint16_t did, + pci_map_irq_fn map_irq, const char *name); + +#endif /* QEMU_PCI_BRIDGE_H */ +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */