1 // Code to maintain and access the pci_device cache
2 //
3 // Copyright (C) 2008-2016  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6 
7 #include "malloc.h" // malloc_tmp
8 #include "output.h" // dprintf
9 #include "pci.h" // pci_config_writel
10 #include "pcidevice.h" // pci_probe_devices
11 #include "pci_regs.h" // PCI_VENDOR_ID
12 #include "romfile.h" // romfile_loadint
13 #include "stacks.h" // wait_preempt
14 #include "string.h" // memset
15 
16 struct hlist_head PCIDevices VARVERIFY32INIT;
17 int MaxPCIBus VARFSEG;
18 
19 // Find all PCI devices and populate PCIDevices linked list.
20 void
pci_probe_devices(void)21 pci_probe_devices(void)
22 {
23     dprintf(3, "PCI probe\n");
24     struct pci_device *busdevs[256];
25     memset(busdevs, 0, sizeof(busdevs));
26     struct hlist_node **pprev = &PCIDevices.first;
27     int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
28     int bus = -1, lastbus = 0, rootbuses = 0, count=0;
29     while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
30         bus++;
31         int bdf;
32         foreachbdf(bdf, bus) {
33             // Create new pci_device struct and add to list.
34             struct pci_device *dev = malloc_tmp(sizeof(*dev));
35             if (!dev) {
36                 warn_noalloc();
37                 return;
38             }
39             memset(dev, 0, sizeof(*dev));
40             hlist_add(&dev->node, pprev);
41             pprev = &dev->node.next;
42             count++;
43 
44             // Find parent device.
45             int rootbus;
46             struct pci_device *parent = busdevs[bus];
47             if (!parent) {
48                 if (bus != lastbus)
49                     rootbuses++;
50                 lastbus = bus;
51                 rootbus = rootbuses;
52                 if (bus > MaxPCIBus)
53                     MaxPCIBus = bus;
54             } else {
55                 rootbus = parent->rootbus;
56             }
57 
58             // Populate pci_device info.
59             dev->bdf = bdf;
60             dev->parent = parent;
61             dev->rootbus = rootbus;
62             u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
63             dev->vendor = vendev & 0xffff;
64             dev->device = vendev >> 16;
65             u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
66             dev->class = classrev >> 16;
67             dev->prog_if = classrev >> 8;
68             dev->revision = classrev & 0xff;
69             dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
70             u8 v = dev->header_type & 0x7f;
71             if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
72                 u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
73                 dev->secondary_bus = secbus;
74                 if (secbus > bus && !busdevs[secbus])
75                     busdevs[secbus] = dev;
76                 if (secbus > MaxPCIBus)
77                     MaxPCIBus = secbus;
78             }
79             dprintf(4, "PCI device %pP (vd=%04x:%04x c=%04x)\n"
80                     , dev, dev->vendor, dev->device, dev->class);
81         }
82     }
83     dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
84 }
85 
86 // Search for a device with the specified vendor and device ids.
87 struct pci_device *
pci_find_device(u16 vendid,u16 devid)88 pci_find_device(u16 vendid, u16 devid)
89 {
90     struct pci_device *pci;
91     foreachpci(pci) {
92         if (pci->vendor == vendid && pci->device == devid)
93             return pci;
94     }
95     return NULL;
96 }
97 
98 // Search for a device with the specified class id.
99 struct pci_device *
pci_find_class(u16 classid)100 pci_find_class(u16 classid)
101 {
102     struct pci_device *pci;
103     foreachpci(pci) {
104         if (pci->class == classid)
105             return pci;
106     }
107     return NULL;
108 }
109 
pci_init_device(const struct pci_device_id * ids,struct pci_device * pci,void * arg)110 int pci_init_device(const struct pci_device_id *ids
111                     , struct pci_device *pci, void *arg)
112 {
113     while (ids->vendid || ids->class_mask) {
114         if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
115             (ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
116             !((ids->class ^ pci->class) & ids->class_mask)) {
117             if (ids->func)
118                 ids->func(pci, arg);
119             return 0;
120         }
121         ids++;
122     }
123     return -1;
124 }
125 
126 struct pci_device *
pci_find_init_device(const struct pci_device_id * ids,void * arg)127 pci_find_init_device(const struct pci_device_id *ids, void *arg)
128 {
129     struct pci_device *pci;
130     foreachpci(pci) {
131         if (pci_init_device(ids, pci, arg) == 0)
132             return pci;
133     }
134     return NULL;
135 }
136 
137 // Enable PCI bus-mastering (ie, DMA) support on a pci device
138 void
pci_enable_busmaster(struct pci_device * pci)139 pci_enable_busmaster(struct pci_device *pci)
140 {
141     wait_preempt();
142     pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
143     pci->have_driver = 1;
144 }
145 
146 // Verify an IO bar and return it to the caller
147 u16
pci_enable_iobar(struct pci_device * pci,u32 addr)148 pci_enable_iobar(struct pci_device *pci, u32 addr)
149 {
150     wait_preempt();
151     u32 bar = pci_config_readl(pci->bdf, addr);
152     if (!(bar & PCI_BASE_ADDRESS_SPACE_IO)) {
153         warn_internalerror();
154         return 0;
155     }
156     bar &= PCI_BASE_ADDRESS_IO_MASK;
157     if (bar == 0 || bar > 0xffff) {
158         warn_internalerror();
159         return 0;
160     }
161     pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_IO);
162     pci->have_driver = 1;
163     return bar;
164 }
165 
166 // Verify a memory bar and return it to the caller
167 void *
pci_enable_membar(struct pci_device * pci,u32 addr)168 pci_enable_membar(struct pci_device *pci, u32 addr)
169 {
170     wait_preempt();
171     u32 bar = pci_config_readl(pci->bdf, addr);
172     if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
173         warn_internalerror();
174         return NULL;
175     }
176     if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) {
177         u32 high = pci_config_readl(pci->bdf, addr+4);
178         if (high) {
179             dprintf(1, "Can not map memory bar over 4Gig\n");
180             return NULL;
181         }
182     }
183     bar &= PCI_BASE_ADDRESS_MEM_MASK;
184     if (bar + 4*1024*1024 < 20*1024*1024) {
185         // Bar doesn't look valid (it is in last 4M or first 16M)
186         warn_internalerror();
187         return NULL;
188     }
189     pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
190     pci->have_driver = 1;
191     return (void*)bar;
192 }
193