qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 7/7] [RFC] pci bus: preliminary for multi pci bus su


From: Isaku Yamahata
Subject: [Qemu-devel] [PATCH 7/7] [RFC] pci bus: preliminary for multi pci bus support.
Date: Tue, 2 Jun 2009 15:42:50 +0900

This patch is preliminary for multi pci bus support to
add -pci option.

Signed-off-by: Isaku Yamahata <address@hidden>

---
changes
- qdevfied
---
 Makefile.target |    2 +-
 hw/apb_pci.c    |    4 +-
 hw/pc.c         |    7 ++
 hw/pci.c        |   12 +++-
 hw/pci.h        |    2 +-
 hw/pci_bridge.c |  232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pci_bridge.h |   35 ++++++++
 hw/pci_ids.h    |    1 +
 qemu-options.hx |   10 +++
 vl.c            |    5 +
 10 files changed, 304 insertions(+), 6 deletions(-)
 create mode 100644 hw/pci_bridge.c
 create mode 100644 hw/pci_bridge.h

diff --git a/Makefile.target b/Makefile.target
index 445d55f..5eb1338 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -494,7 +494,7 @@ endif #CONFIG_BSD_USER
 ifndef CONFIG_USER_ONLY
 
 OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \
-     gdbstub.o gdbstub-xml.o
+     gdbstub.o gdbstub-xml.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-blk.o virtio-balloon.o virtio-net.o virtio-console.o
diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index e85e28c..21667a6 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -268,9 +268,9 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
     /* APB secondary busses */
     *bus2 = pci_bridge_create_simple(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_create_simple(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 66cc780..894396f 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"
@@ -1041,6 +1042,12 @@ static void pc_init1(ram_addr_t ram_size,
         }
     }
 
+    if (pci_enabled) {
+        if (pci_device_init() < 0) {
+            exit(1);
+        }
+    }
+
     watchdog_pc_init(pci_bus);
 
     for(i = 0; i < nb_nics; i++) {
diff --git a/hw/pci.c b/hw/pci.c
index 0ffdfef..41e9930 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -129,7 +129,7 @@ PCIBus *pci_register_bus(DeviceState *parent, const char 
*name,
     return bus;
 }
 
-/* XXX qemu_irq, nirq */
+/* XXX qemu_irq */
 static PCIBus *pci_register_secondary_bus(PCIDevice *dev,
                                           int bus_num, int devfn_min, int nirq)
 {
@@ -141,6 +141,13 @@ static PCIBus *pci_register_secondary_bus(PCIDevice *dev,
                                         &dev->qdev, "pci"));
     bus->bus_num = bus_num;
     bus->devfn_min = devfn_min;
+    bus->nirq = nirq;
+
+#if 0
+    bus->set_irq = set_irq;
+    bus->irq_opaque = pic;
+    register_savevm("PCIBUS", nbus++, 1, pcibus_save, pcibus_load, bus);
+#endif
 
     parent_bus = pci_get_parent_bus(dev);
     LIST_INSERT_AFTER(parent_bus, bus, next);
@@ -1130,7 +1137,7 @@ device_init(pci_brdige_register_device);
 
 PCIBus *pci_bridge_create_simple(PCIBus *bus, int devfn, uint16_t vid,
                                  uint16_t did, pci_map_irq_fn map_irq,
-                                 const char *pci_name)
+                                 const char *pci_name, int sec_bus_num)
 {
     DeviceState *qdev;
     uint8_t* pci_conf;
@@ -1142,6 +1149,7 @@ PCIBus *pci_bridge_create_simple(PCIBus *bus, int devfn, 
uint16_t vid,
 
     qdev_set_prop_int(qdev, "devfn", devfn);
     qdev_set_prop_ptr(qdev, "pci_name", pci_name_dup);
+    qdev_set_prop_int(qdev, "sec_bus_num", sec_bus_num);
 
     qdev_init(qdev);
 
diff --git a/hw/pci.h b/hw/pci.h
index 76f3f28..95ff3d2 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -344,7 +344,7 @@ void pci_bridge_set_map_irq(PCIBus *bus, pci_map_irq_fn 
map_irq);
 
 PCIBus *pci_bridge_create_simple(PCIBus *bus, int devfn, uint16_t vid,
                                  uint16_t did, pci_map_irq_fn map_irq,
-                                 const char *pci_name);
+                                 const char *pci_name, int sec_bus_num);
 
 /* lsi53c895a.c */
 #define LSI_MAX_DEVS 7
diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c
new file mode 100644
index 0000000..3aedaa1
--- /dev/null
+++ b/hw/pci_bridge.c
@@ -0,0 +1,232 @@
+/*
+ * 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 <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "sysemu.h"
+#include "hw.h"
+#include "pci.h"
+#include "pci_bridge.h"
+
+#define PCI_BRIDGE_INTEL_82801BA_11     "i82801ba11"
+static int i82801ba11_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    return (PCI_SLOT(pci_dev->devfn) + irq_num + 1) % 4;
+}
+
+static void i82801ba11_qdev_init(PCIDevice *d)
+{
+    PCIBus *b;
+    uint8_t *pci_conf;
+
+    pci_conf = d->config;
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82801BA_11);
+    b = pci_get_parent_bus(d);
+    pci_bridge_set_map_irq(b, i82801ba11_map_irq);
+}
+
+static void i82801ba11_register_device(void)
+{
+    pci_bridge_qdev_register(PCI_BRIDGE_INTEL_82801BA_11, sizeof(PCIDevice),
+                             i82801ba11_qdev_init);
+}
+
+device_init(i82801ba11_register_device);
+
+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 [[<domain>:]<bus>:]<slot>.<func>, 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_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_create_simple(parent_bus, PCI_DEVFN(pd->slot, pd->func),
+                                   PCI_VENDOR_ID_INTEL,
+                                   PCI_DEVICE_ID_INTEL_82801BA_11,
+                                   &i82801ba11_map_irq, "PCI to 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 <yamahata at valinux co jp>
+ *                    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 7bc4853..b226373 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 87af798..c5be603 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 = [[<domain>:]<bus>:]<slot>.<func>\n"
+    "model = bridge\n")
+STEXI
address@hidden -pci address@hidden,address@hidden,@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 f8c0d00..0082250 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"
@@ -5139,6 +5140,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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]