PCI: Fixes capability detection
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Apr 2014 19:42:33 +0000 (12:42 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 2 Apr 2014 19:47:00 +0000 (12:47 -0700)
Caps are a linked list; the old code seemed to just incrementally scan,
which was checking arbitrary spots in the PCI/cap space.

If someone wants to make fancy cap_id-to-strings for the output, be my
guest.

kern/arch/x86/msi.c
kern/arch/x86/pci.c
kern/arch/x86/pci.h

index c08d9ce..588e8c5 100644 (file)
@@ -57,52 +57,12 @@ enum{
        Msixsize        = 0x3ff,
 };
 
-/* Find an arbitrary capability. This should move to pci.c? */
-int pci_cap(struct pci_device *p, int cap)
-{
-       int i, c, off;
-
-       /* status register bit 4 has capabilities */
-       if((pcidev_read16(p, PciPSR) & 1<<4) == 0)
-               return -1;
-       switch(pcidev_read8(p, PciHDT) & 0x7f){
-       default:
-               return -1;
-       case 0:                         /* etc */
-       case 1:                         /* pci to pci bridge */
-               off = 0x34;
-               break;
-       case 2:                         /* cardbus bridge */
-               off = 0x14;
-               break;
-       }
-       for(i = 48; i--;){
-               off = pcidev_read8(p, off);
-               if(off < 0x40 || (off & 3))
-                       break;
-               off &= ~3;
-               c = pcidev_read8(p, off);
-               if(c == 0xff)
-                       break;
-               if(c == cap)
-                       return off;
-               off++;
-       }
-       return -1;
-}
-
 /* Find the offset in config space of this function of the msi capability.
- * It is defined in 6.8.1 and is variable-sized.
- */
+ * It is defined in 6.8.1 and is variable-sized.  Returns 0 on failure. */
 static int
 msicap(struct pci_device *p)
 {
-       int c;
-
-       c = pci_cap(p, PciCapMSI);
-       if(c == -1)
-               return 0;
-       return c;
+       return p->caps[PCI_CAP_ID_MSI];
 }
 
 /* Find the offset in config space of this function of the msi-x capability.
@@ -111,12 +71,7 @@ msicap(struct pci_device *p)
 static int
 msixcap(struct pci_device *p)
 {
-       int c;
-
-       c = pci_cap(p, PciCapMSIX);
-       if(c == -1)
-               return 0;
-       return c;
+       return p->caps[PCI_CAP_ID_MSIX];
 }
 
 static int
index 1f87009..baafeab 100644 (file)
@@ -81,6 +81,42 @@ static void pci_handle_bars(struct pci_device *pcidev)
        }
 }
 
+static void pci_parse_caps(struct pci_device *pcidev)
+{
+       uint32_t cap_off;       /* not sure if this can be extended from u8 */
+       uint8_t cap_id;
+       if (!(pcidev_read16(pcidev, PCI_STATUS_REG) & (1 << 4)))
+               return;
+       switch (pcidev_read8(pcidev, PCI_HEADER_REG) & 0x7f) {
+               case 0:                         /* etc */
+               case 1:                         /* pci to pci bridge */
+                       cap_off = 0x34;
+                       break;
+               case 2:                         /* cardbus bridge */
+                       cap_off = 0x14;
+                       break;
+               default:
+                       return;
+       }
+       /* initial offset points to the addr of the first cap */
+       cap_off = pcidev_read8(pcidev, cap_off);
+       cap_off &= ~0x3;        /* osdev says the lower 2 bits are reserved */
+       while (cap_off) {
+               cap_id = pcidev_read8(pcidev, cap_off);
+               if (cap_id > PCI_CAP_ID_MAX) {
+                       printk("PCI %x:%x:%x had bad cap 0x%x\n", pcidev->bus, pcidev->dev,
+                              pcidev->func, cap_id);
+                       return;
+               }
+               pcidev->caps[cap_id] = cap_off;
+               cap_off = pcidev_read8(pcidev, cap_off + 1);
+               /* not sure if subsequent caps must be aligned or not */
+               if (cap_off & 0x3)
+                       printk("PCI %x:%x:%x had unaligned cap offset 0x%x\n", pcidev->bus,
+                              pcidev->dev, pcidev->func, cap_off);
+       }
+}
+
 /* Scans the PCI bus.  Won't actually work for anything other than bus 0, til we
  * sort out how to handle bridge devices. */
 void pci_init(void) {
@@ -128,6 +164,7 @@ void pci_init(void) {
                                                pcidev->header_type = "Unknown Header Type";
                                }
                                pci_handle_bars(pcidev);
+                               pci_parse_caps(pcidev);
                                STAILQ_INSERT_TAIL(&pci_devices, pcidev, all_dev);
                                #ifdef CONFIG_PCI_VERBOSE
                                pcidev_print_info(pcidev, 4);
@@ -398,6 +435,12 @@ void pcidev_print_info(struct pci_device *pcidev, int verbosity)
                        }
                }
        }
+       printk("\tCapabilities:");
+       for (int i = 0; i < PCI_CAP_ID_MAX + 1; i++) {
+               if (pcidev->caps[i])
+                       printk(" 0x%02x", i);
+       }
+       printk("\n");
 }
 
 void pci_set_bus_master(struct pci_device *pcidev)
index d7a440a..c121c5c 100644 (file)
@@ -359,6 +359,7 @@ struct pci_device {
        uint32_t                                        msi_lo;
        uint8_t                                         nr_bars;
        struct pci_bar                          bar[MAX_PCI_BAR];
+       uint32_t                                        caps[PCI_CAP_ID_MAX + 1];
        /* for MSI-X we might have allocated two physically contiguous pages. */
        void *                                          msix;
        int                                             msixbar;
@@ -408,7 +409,6 @@ void pci_clr_bus_master(struct pci_device *pcidev);
 struct pci_device *pci_match_tbdf(int tbdf);
 
 /* MSI functions, msi.c */
-int pci_cap(struct pci_device *p, int cap);
 int pci_msi_enable(struct pci_device *p, uint64_t vec);
 int pci_msix_enable(struct pci_device *p, uint64_t vec);