Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / src / pci.c
1 // PCI config space access functions.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "pci.h" // pci_config_writel
9 #include "ioport.h" // outl
10 #include "util.h" // dprintf
11 #include "paravirt.h" // romfile_loadint
12 #include "farptr.h" // MAKE_FLATPTR
13 #include "pci_regs.h" // PCI_VENDOR_ID
14 #include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA
15
16 void pci_config_writel(u16 bdf, u32 addr, u32 val)
17 {
18     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
19     outl(val, PORT_PCI_DATA);
20 }
21
22 void pci_config_writew(u16 bdf, u32 addr, u16 val)
23 {
24     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
25     outw(val, PORT_PCI_DATA + (addr & 2));
26 }
27
28 void pci_config_writeb(u16 bdf, u32 addr, u8 val)
29 {
30     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
31     outb(val, PORT_PCI_DATA + (addr & 3));
32 }
33
34 u32 pci_config_readl(u16 bdf, u32 addr)
35 {
36     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
37     return inl(PORT_PCI_DATA);
38 }
39
40 u16 pci_config_readw(u16 bdf, u32 addr)
41 {
42     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
43     return inw(PORT_PCI_DATA + (addr & 2));
44 }
45
46 u8 pci_config_readb(u16 bdf, u32 addr)
47 {
48     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
49     return inb(PORT_PCI_DATA + (addr & 3));
50 }
51
52 void
53 pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
54 {
55     u16 val = pci_config_readw(bdf, addr);
56     val = (val & ~off) | on;
57     pci_config_writew(bdf, addr, val);
58 }
59
60 // Helper function for foreachbdf() macro - return next device
61 int
62 pci_next(int bdf, int bus)
63 {
64     if (pci_bdf_to_fn(bdf) == 0
65         && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
66         // Last found device wasn't a multi-function device - skip to
67         // the next device.
68         bdf += 8;
69     else
70         bdf += 1;
71
72     for (;;) {
73         if (pci_bdf_to_bus(bdf) != bus)
74             return -1;
75
76         u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
77         if (v != 0x0000 && v != 0xffff)
78             // Device is present.
79             return bdf;
80
81         if (pci_bdf_to_fn(bdf) == 0)
82             bdf += 8;
83         else
84             bdf += 1;
85     }
86 }
87
88 struct pci_device *PCIDevices;
89 int MaxPCIBus VAR16VISIBLE;
90
91 // Check if PCI is available at all
92 int
93 pci_probe_host(void)
94 {
95     outl(0x80000000, PORT_PCI_CMD);
96     if (inl(PORT_PCI_CMD) != 0x80000000) {
97         dprintf(1, "Detected non-PCI system\n");
98         return -1;
99     }
100     return 0;
101 }
102
103 // Find all PCI devices and populate PCIDevices linked list.
104 void
105 pci_probe_devices(void)
106 {
107     dprintf(3, "PCI probe\n");
108     struct pci_device *busdevs[256];
109     memset(busdevs, 0, sizeof(busdevs));
110     struct pci_device **pprev = &PCIDevices;
111     int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
112     int bus = -1, lastbus = 0, rootbuses = 0, count=0;
113     while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
114         bus++;
115         int bdf;
116         foreachbdf(bdf, bus) {
117             // Create new pci_device struct and add to list.
118             struct pci_device *dev = malloc_tmp(sizeof(*dev));
119             if (!dev) {
120                 warn_noalloc();
121                 return;
122             }
123             memset(dev, 0, sizeof(*dev));
124             *pprev = dev;
125             pprev = &dev->next;
126             count++;
127
128             // Find parent device.
129             int rootbus;
130             struct pci_device *parent = busdevs[bus];
131             if (!parent) {
132                 if (bus != lastbus)
133                     rootbuses++;
134                 lastbus = bus;
135                 rootbus = rootbuses;
136                 if (bus > MaxPCIBus)
137                     MaxPCIBus = bus;
138             } else {
139                 rootbus = parent->rootbus;
140             }
141
142             // Populate pci_device info.
143             dev->bdf = bdf;
144             dev->parent = parent;
145             dev->rootbus = rootbus;
146             u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
147             dev->vendor = vendev & 0xffff;
148             dev->device = vendev >> 16;
149             u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
150             dev->class = classrev >> 16;
151             dev->prog_if = classrev >> 8;
152             dev->revision = classrev & 0xff;
153             dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
154             u8 v = dev->header_type & 0x7f;
155             if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
156                 u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
157                 dev->secondary_bus = secbus;
158                 if (secbus > bus && !busdevs[secbus])
159                     busdevs[secbus] = dev;
160                 if (secbus > MaxPCIBus)
161                     MaxPCIBus = secbus;
162             }
163             dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n"
164                     , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
165                     , pci_bdf_to_fn(bdf)
166                     , dev->vendor, dev->device, dev->class);
167         }
168     }
169     dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
170 }
171
172 // Search for a device with the specified vendor and device ids.
173 struct pci_device *
174 pci_find_device(u16 vendid, u16 devid)
175 {
176     struct pci_device *pci;
177     foreachpci(pci) {
178         if (pci->vendor == vendid && pci->device == devid)
179             return pci;
180     }
181     return NULL;
182 }
183
184 // Search for a device with the specified class id.
185 struct pci_device *
186 pci_find_class(u16 classid)
187 {
188     struct pci_device *pci;
189     foreachpci(pci) {
190         if (pci->class == classid)
191             return pci;
192     }
193     return NULL;
194 }
195
196 int pci_init_device(const struct pci_device_id *ids
197                     , struct pci_device *pci, void *arg)
198 {
199     while (ids->vendid || ids->class_mask) {
200         if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
201             (ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
202             !((ids->class ^ pci->class) & ids->class_mask)) {
203             if (ids->func)
204                 ids->func(pci, arg);
205             return 0;
206         }
207         ids++;
208     }
209     return -1;
210 }
211
212 struct pci_device *
213 pci_find_init_device(const struct pci_device_id *ids, void *arg)
214 {
215     struct pci_device *pci;
216     foreachpci(pci) {
217         if (pci_init_device(ids, pci, arg) == 0)
218             return pci;
219     }
220     return NULL;
221 }
222
223 void
224 pci_reboot(void)
225 {
226     u8 v = inb(PORT_PCI_REBOOT) & ~6;
227     outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
228     udelay(50);
229     outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
230     udelay(50);
231 }
232
233 // helper functions to access pci mmio bars from real mode
234
235 u32 VISIBLE32FLAT
236 pci_readl_32(u32 addr)
237 {
238     dprintf(3, "32: pci read : %x\n", addr);
239     return readl((void*)addr);
240 }
241
242 u32 pci_readl(u32 addr)
243 {
244     if (MODESEGMENT) {
245         dprintf(3, "16: pci read : %x\n", addr);
246         extern void _cfunc32flat_pci_readl_32(u32 addr);
247         return call32(_cfunc32flat_pci_readl_32, addr, -1);
248     } else {
249         return pci_readl_32(addr);
250     }
251 }
252
253 struct reg32 {
254     u32 addr;
255     u32 data;
256 };
257
258 void VISIBLE32FLAT
259 pci_writel_32(struct reg32 *reg32)
260 {
261     dprintf(3, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
262     writel((void*)(reg32->addr), reg32->data);
263 }
264
265 void pci_writel(u32 addr, u32 val)
266 {
267     struct reg32 reg32 = { .addr = addr, .data = val };
268     if (MODESEGMENT) {
269         dprintf(3, "16: pci write: %x, %x (%x:%p)\n",
270                 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
271         void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
272         extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
273         call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
274     } else {
275         pci_writel_32(&reg32);
276     }
277 }