Patchwork [v5,9/9] pci: set PCI multi-function bit appropriately.

login
register
mail settings
Submitter Isaku Yamahata
Date June 23, 2010, 7:15 a.m.
Message ID <9a21e33d21757fb2251ded5c9eafe1313b40acb1.1277277076.git.yamahata@valinux.co.jp>
Download mbox | patch
Permalink /patch/56609/
State New
Headers show

Comments

Isaku Yamahata - June 23, 2010, 7:15 a.m.
Set PCI multi-function bit according to multifunction property.
PCI address, devfn ,is exported to users as addr property,
so users can populate pci function(PCIDevice in qemu)
at arbitrary devfn.
It means each function(PCIDevice) don't know whether pci device
(PCIDevice[8]) is multi function or not.
So this patch allows user to set multifunction bit via property
and checks whether multifunction bit is set correctly.

Cc:  Juan Quintela <quintela@redhat.com>
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>

---
chages v4 -> v5:
- split out the patch
- fix memory leak pointed out by Juan Quintela <quintela@redhat.com>
- convert multifunction uint8_t property into bit property
  by introducint QEMU_PCI_CAP_MULTIFUNCTION
- s/mf/multifunction/g
- minor style fix

changes v3 -> v4:
- introduce multifunction property.

changes v2 -> v3:
- introduce PCI_FUNC_MAX
- more commit log

changes v1 -> v2:
- don't set header type register in configuration space.
---
 hw/apb_pci.c  |    3 ---
 hw/pci.c      |   52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/piix4.c    |    2 --
 hw/piix_pci.c |    2 --
 4 files changed, 52 insertions(+), 7 deletions(-)

Patch

diff --git a/hw/apb_pci.c b/hw/apb_pci.c
index fd11459..0ecac55 100644
--- a/hw/apb_pci.c
+++ b/hw/apb_pci.c
@@ -312,9 +312,6 @@  static void apb_pci_bridge_init(PCIBus *b)
                  PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                  PCI_STATUS_DEVSEL_MEDIUM);
     pci_set_byte(dev->config + PCI_REVISION_ID, 0x11);
-    pci_set_byte(dev->config + PCI_HEADER_TYPE,
-                 pci_get_byte(dev->config + PCI_HEADER_TYPE) |
-                 PCI_HEADER_TYPE_MULTI_FUNCTION);
 }
 
 PCIBus *pci_apb_init(target_phys_addr_t special_base,
diff --git a/hw/pci.c b/hw/pci.c
index 36b3760..f4bfbea 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -577,6 +577,54 @@  static void pci_init_wmask_bridge(PCIDevice *d)
     pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, 0xffff);
 }
 
+static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev)
+{
+    uint8_t slot = PCI_SLOT(dev->devfn);
+    uint8_t func;
+
+    if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+        dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;
+    }
+
+    /*
+     * multifuction bit is interpreted in two ways as follows.
+     *   - all functions must set the bit to 1.
+     *     Example: Intel X53
+     *   - function 0 must set the bit, but the rest function (> 0)
+     *     is allowed to leave the bit to 0.
+     *     Example: PIIX3(also in qemu), PIIX4(also in qemu), ICH10,
+     *
+     * So OS (at least Linux) checks the bit of only function 0,
+     * and doesn't see the bit of function > 0.
+     *
+     * The below check allows both interpretation.
+     */
+    if (PCI_FUNC(dev->devfn)) {
+        PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)];
+        if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) {
+            /* function 0 should set multifunction bit */
+            error_report("PCI: single function device can't be populated "
+                         "in function %x.%x", slot, PCI_FUNC(dev->devfn));
+            return -1;
+        }
+        return 0;
+    }
+
+    if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
+        return 0;
+    }
+    /* function 0 indicates single function, so function > 0 must be NULL */
+    for (func = 1; func < PCI_FUNC_MAX; ++func) {
+        if (bus->devices[PCI_DEVFN(slot, func)]) {
+            error_report("PCI: %x.0 indicates single function, "
+                         "but %x.%x is already populated.",
+                         slot, slot, func);
+            return -1;
+        }
+    }
+    return 0;
+}
+
 static void pci_config_alloc(PCIDevice *pci_dev)
 {
     int config_size = pci_config_size(pci_dev);
@@ -630,6 +678,10 @@  static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
     if (is_bridge) {
         pci_init_wmask_bridge(pci_dev);
     }
+    if (pci_init_multifunction(bus, pci_dev)) {
+        pci_config_free(pci_dev);
+        return NULL;
+    }
 
     if (!config_read)
         config_read = pci_default_read_config;
diff --git a/hw/piix4.c b/hw/piix4.c
index 2298074..58e8f70 100644
--- a/hw/piix4.c
+++ b/hw/piix4.c
@@ -93,8 +93,6 @@  static int piix4_initfn(PCIDevice *d)
     pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
     pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_0); // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge
     pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
-    pci_conf[PCI_HEADER_TYPE] =
-        PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic
 
     piix4_dev = d;
     qemu_register_reset(piix4_reset, d);
diff --git a/hw/piix_pci.c b/hw/piix_pci.c
index a3185f7..8bb4507 100644
--- a/hw/piix_pci.c
+++ b/hw/piix_pci.c
@@ -336,8 +336,6 @@  static int piix3_initfn(PCIDevice *dev)
     pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
     pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1)
     pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA);
-    pci_conf[PCI_HEADER_TYPE] =
-        PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic
 
     qemu_register_reset(piix3_reset, d);
     return 0;