From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LzpA0-0002Ux-LR for qemu-devel@nongnu.org; Fri, 01 May 2009 05:36:00 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Lzp9v-0002TW-58 for qemu-devel@nongnu.org; Fri, 01 May 2009 05:35:59 -0400 Received: from [199.232.76.173] (port=44543 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Lzp9u-0002TL-Rc for qemu-devel@nongnu.org; Fri, 01 May 2009 05:35:54 -0400 Received: from fms-01.valinux.co.jp ([210.128.90.1]:36575 helo=mail.valinux.co.jp) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1Lzp9t-0002NV-HX for qemu-devel@nongnu.org; Fri, 01 May 2009 05:35:54 -0400 From: Isaku Yamahata Date: Fri, 1 May 2009 18:33:55 +0900 Message-Id: <1241170436-2800-4-git-send-email-yamahata@valinux.co.jp> In-Reply-To: <1241170436-2800-1-git-send-email-yamahata@valinux.co.jp> References: <1241170436-2800-1-git-send-email-yamahata@valinux.co.jp> Subject: [Qemu-devel] [PATCH] pci bus: preliminary for multi pci bus support. List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: armbru@redhat.com, mst@redhat.com This patch is preliminary for multi pci bus support to add -pci option. Signed-off-by: Isaku Yamahata --- Makefile.target | 2 +- hw/apb_pci.c | 4 +- hw/pc.c | 7 ++ hw/pci.c | 20 ++++-- hw/pci.h | 3 +- hw/pci_bridge.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/pci_bridge.h | 35 +++++++++ hw/pci_ids.h | 1 + qemu-options.hx | 10 +++ vl.c | 5 ++ 10 files changed, 285 insertions(+), 10 deletions(-) create mode 100644 hw/pci_bridge.c create mode 100644 hw/pci_bridge.h diff --git a/Makefile.target b/Makefile.target index 82ada5a..897b039 100644 --- a/Makefile.target +++ b/Makefile.target @@ -498,7 +498,7 @@ endif #CONFIG_BSD_USER # System emulator target ifndef CONFIG_USER_ONLY -OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o +OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o pci_bridge.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 36ecb55..3685330 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -267,9 +267,9 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, /* APB secondary busses */ *bus2 = pci_bridge_init(s->bus, 8, PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, pci_apb_map_irq, - "Advanced PCI Bus secondary bridge 1"); + "Advanced PCI Bus secondary bridge 1", 1); *bus3 = pci_bridge_init(s->bus, 9, PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, pci_apb_map_irq, - "Advanced PCI Bus secondary bridge 2"); + "Advanced PCI Bus secondary bridge 2", 2); return s->bus; } diff --git a/hw/pc.c b/hw/pc.c index 07b75f3..f881a30 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -25,6 +25,7 @@ #include "pc.h" #include "fdc.h" #include "pci.h" +#include "pci_bridge.h" #include "block.h" #include "sysemu.h" #include "audio/audio.h" @@ -1023,6 +1024,12 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, } } + if (pci_enabled) { + if (pci_device_init() < 0) { + exit(1); + } + } + for(i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; diff --git a/hw/pci.c b/hw/pci.c index ee7d403..0be3662 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -40,7 +40,7 @@ struct PCIBus { SetIRQFunc *low_set_irq; qemu_irq *irq_opaque; PCIDevice *devices[256]; - PCIDevice *parent_dev; + PCIDevice *self; PCIBus *next; /* The bus IRQ state is the logical OR of the connected devices. Keep a count of the number of devices with raised IRQs. */ @@ -106,12 +106,16 @@ PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, return bus; } -static PCIBus *pci_register_secondary_bus(PCIDevice *dev, pci_map_irq_fn map_irq) +/* XXX qemu_irq, devfn_min, nirq */ +static PCIBus *pci_register_secondary_bus(PCIDevice *dev, int bus_num, + pci_map_irq_fn map_irq) { PCIBus *bus; bus = qemu_mallocz(sizeof(PCIBus)); bus->map_irq = map_irq; - bus->parent_dev = dev; + bus->self = dev; + bus->bus_num = bus_num; + bus->next = dev->bus->next; dev->bus->next = bus; return bus; @@ -654,7 +658,7 @@ static void pci_set_irq(void *opaque, int irq_num, int level) irq_num = bus->map_irq(pci_dev, irq_num); if (bus->set_irq) break; - pci_dev = bus->parent_dev; + pci_dev = bus->self; } bus->irq_count[irq_num] += change; bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0); @@ -867,7 +871,8 @@ PCIDevice *pci_find_device(int bus_num, int slot, int function) } PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name) + pci_map_irq_fn map_irq, const char *name, + int secondary_bus_num) { PCIBridge *s; s = (PCIBridge *)pci_register_device(bus, name, sizeof(PCIBridge), @@ -886,8 +891,11 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, s->dev.config[0x0D] = 0x10; // latency_timer s->dev.config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_MULTI_FUNCTION | PCI_HEADER_TYPE_BRIDGE; // header_type + s->dev.config[0x18] = bus->bus_num; // primary bus number + s->dev.config[0x19] = secondary_bus_num; // secondary bus number + s->dev.config[0x1A] = secondary_bus_num; // subordinate bus number s->dev.config[0x1E] = 0xa0; // secondary status - s->bus = pci_register_secondary_bus(&s->dev, map_irq); + s->bus = pci_register_secondary_bus(&s->dev, secondary_bus_num, map_irq); return s->bus; } diff --git a/hw/pci.h b/hw/pci.h index ff858a1..5993fb3 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -197,7 +197,8 @@ int pci_assign_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp); void pci_info(Monitor *mon); PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name); + pci_map_irq_fn map_irq, const char *name, + int secondary_bus); static inline void pci_config_set_vendor_id(uint8_t *pci_config, uint16_t val) diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c new file mode 100644 index 0000000..d74737b --- /dev/null +++ b/hw/pci_bridge.c @@ -0,0 +1,208 @@ +/* + * 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. + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#include + +#include "sysemu.h" +#include "hw.h" +#include "pci.h" +#include "pci_bridge.h" + +struct PCIDeviceInfo { + int dom; + int bus; + unsigned int slot; + unsigned int func; + int secondary_bus; + + const char *model; + const char *options; +}; + +#define MAX_PCI_DEVS 256 +static int nb_pci_devices; +static struct PCIDeviceInfo pd_table[MAX_PCI_DEVS]; + +/* + * Parse [[:]:]., return -1 on error + */ +static int pci_parse_devfn(const char *addr, + int *domp, int *busp, + unsigned int *slotp, unsigned int *funcp) +{ + const char *p; + char *e; + unsigned long val; + unsigned long dom = 0; + unsigned long bus = 0; + unsigned int slot = 0; + unsigned int func = 0; + + p = addr; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + dom = bus; + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + } + } + slot = val; + + if (*e != '.') + return -1; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + func = val; + + if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) + return -1; + + /* For now, only 0 domain is supported */ + if (dom != 0) + return -1; + + *domp = dom; + *busp = bus; + *slotp = slot; + *funcp = func; + return 0; +} + +int pci_device_parse(const char* options) +{ + int ret = -1; + char buf[1024]; + char *e; + struct PCIDeviceInfo *pd = &pd_table[nb_pci_devices]; + + if (nb_pci_devices >= MAX_PCI_DEVS) { + /* XXX:WARN */ + goto out; + } + + + if (get_param_value(buf, sizeof(buf), "pci_addr", options) < 0) { + /* XXX error */ + goto out; + } + if (pci_parse_devfn(buf, &pd->dom, &pd->bus, &pd->slot, &pd->func) < 0) { + goto out; + } + + if (get_param_value(buf, sizeof(buf), "secondary", options) < 0) { + goto out; + } + pd->secondary_bus = strtoul(buf, &e, 16); + if (buf == e) { + goto out; + } + if (pd->secondary_bus > 0xff) { + goto out; + } + + if (get_param_value(buf, sizeof(buf), "model", options) < 0) { + goto out; + } + pd->model = strdup(buf); + if (pd->model == NULL) { + goto out; + } + + /* save for later use */ + pd->options = strdup(options); + if (pd->options == NULL) { + goto out; + } + + nb_pci_devices++; + ret = 0; + +out: + return ret; +} + +static int pci_bridge_irq(PCIDevice *pci_dev, int irq_num) +{ + return (PCI_SLOT(pci_dev->devfn) + irq_num + 1) % 4; +} + +static int pci_device_init_one(struct PCIDeviceInfo *pd) +{ + PCIBus *parent_bus; + PCIBus *bus; + + if (pci_find_device(pd->bus, pd->slot, pd->func) != NULL) { + return -1; + } + + parent_bus = pci_find_bus(pd->bus); + if (parent_bus == NULL) { + return -1; + } + + bus = pci_bridge_init(parent_bus, PCI_DEVFN(pd->slot, pd->func), + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_11, + &pci_bridge_irq, "PCI Bridge", pd->secondary_bus); + return 0; +} + +int pci_device_init(void) +{ + int i; + int ret = 0; + + for (i = 0; i < nb_pci_devices; i++) { + struct PCIDeviceInfo *pd = &pd_table[i]; + const char *model = pd->model; + + if (!strcmp(model, "bridge")) { + ret = pci_device_init_one(pd); + } else { + /* unknown model */ + ret = -1; + } + + if (ret < 0) + break; + } + return ret; +} + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h new file mode 100644 index 0000000..90037f0 --- /dev/null +++ b/hw/pci_bridge.h @@ -0,0 +1,35 @@ +/* + * 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. + * + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef QEMU_PCI_BRIDGE_H +#define QEMU_PCI_BRIDGE_H + +int pci_device_parse(const char* options); +int pci_device_init(void); + +#endif /* QEMU_PCI_BRIDGE_H */ +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff --git a/hw/pci_ids.h b/hw/pci_ids.h index 427fcd5..243bd55 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -86,6 +86,7 @@ #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 diff --git a/qemu-options.hx b/qemu-options.hx index aa786e3..91dc975 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -886,6 +886,16 @@ override the default configuration (@option{-net nic -net user}) which is activated if no @option{-net} options are provided. ETEXI +DEF("pci", HAS_ARG, QEMU_OPTION_pci, \ + "-pci pci_addr=pci_addr,model=type[,model specific options]\n" + "pci_addr = [[:]:].\n" + "model = bridge\n") +STEXI +@item -pci pci_addr=@var{pci_addr},model=@var{type}[,@var{model specific options}] +connect pci device of model @var{type} at pci bus address of @var{pci_addr} +with model specific options. +ETEXI + #ifdef CONFIG_SLIRP DEF("tftp", HAS_ARG, QEMU_OPTION_tftp, \ "-tftp dir allow tftp access to files in dir [-net user]\n") diff --git a/vl.c b/vl.c index ac4b32f..fcaa0a2 100644 --- a/vl.c +++ b/vl.c @@ -134,6 +134,7 @@ int main(int argc, char **argv) #include "hw/usb.h" #include "hw/pcmcia.h" #include "hw/pc.h" +#include "hw/pci_bridge.h" #include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" @@ -5118,6 +5119,10 @@ int main(int argc, char **argv, char **envp) net_clients[nb_net_clients] = optarg; nb_net_clients++; break; + case QEMU_OPTION_pci: + if (pci_device_parse(optarg) < 0) + exit(1); + break; #ifdef CONFIG_SLIRP case QEMU_OPTION_tftp: tftp_prefix = optarg; -- 1.6.0.2