census logo
[bootcensus] / src / pci.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include <inttypes.h>
21 #include "pci.h"
22 #include "intr.h"
23 #include "int86.h"
24 #include "asmops.h"
25 #include "panic.h"
26
27 #define CONFIG_ADDR_PORT        0xcf8
28 #define CONFIG_DATA_PORT        0xcfc
29
30 #define ADDR_ENABLE             0x80000000
31 #define ADDR_BUSID(x)   (((uint32_t)(x) & 0xff) << 16)
32 #define ADDR_DEVID(x)   (((uint32_t)(x) & 0x1f) << 11)
33 #define ADDR_FUNC(x)    (((uint32_t)(x) & 3) << 8)
34
35 /* signature returned in edx by the PCI BIOS present function: FOURCC "PCI " */
36 #define PCI_SIG         0x20494350
37
38 #define TYPE_MULTIFUNC  0x80
39
40 struct config_data {
41         uint16_t vendor, device;
42         uint16_t cmd, status;
43         uint8_t rev, iface, subclass, class;
44         uint8_t cacheline_size;
45         uint8_t latency_timer;
46         uint8_t hdr_type;
47         uint8_t bist;
48         uint32_t base_addr[6];
49         uint32_t cardbus_cis;
50         uint16_t subsys_vendor;
51         uint16_t subsys;
52         uint32_t rom_addr;
53         uint32_t reserved1, reserved2;
54         uint8_t intr_line, intr_pin;
55         uint8_t min_grant, max_latency;
56 } __attribute__((packed));
57
58 static int enum_bus(int busid);
59 static int enum_dev(int busid, int dev);
60 static int read_dev_info(struct config_data *res, int bus, int dev, int func);
61 static void print_dev_info(struct config_data *info, int bus, int dev, int func);
62
63 static uint32_t cfg_read32_m1(int bus, int dev, int func, int reg);
64 static uint32_t cfg_read32_m2(int bus, int dev, int func, int reg);
65 static const char *class_str(int cc);
66 static const char *subclass_str(int cc, int sub);
67
68 static uint32_t (*cfg_read32)(int, int, int, int);
69
70 void init_pci(void)
71 {
72         int i, count = 0;
73         int intrflag;
74         struct int86regs regs;
75
76         intrflag = get_intr_flag();
77
78         memset(&regs, 0, sizeof regs);
79         regs.eax = 0xb101;
80         int86(0x1a, &regs);
81
82         /* restore interrupt state in case bios changed it */
83         set_intr_flag(intrflag);
84
85         /* PCI BIOS present if CF=0, AH=0, and EDX has the "PCI " sig FOURCC */
86         if((regs.flags & FLAGS_CARRY) || (regs.eax & 0xff00) || regs.edx != PCI_SIG) {
87                 printf("No PCI BIOS present\n");
88                 return;
89         }
90
91         printf("PCI BIOS v%x.%x found\n", (regs.ebx & 0xff00) >> 8, regs.ebx & 0xff);
92         if(regs.eax & 1) {
93                 cfg_read32 = cfg_read32_m1;
94         } else {
95                 if(!(regs.eax & 2)) {
96                         printf("Failed to find supported PCI mess mechanism\n");
97                         return;
98                 }
99                 printf("PCI mess mechanism #1 unsupported, falling back to mechanism #2\n");
100                 cfg_read32 = cfg_read32_m2;
101         }
102
103         for(i=0; i<256; i++) {
104                 count += enum_bus(i);
105         }
106         printf("found %d PCI devices\n\n", count);
107 }
108
109 static int enum_bus(int busid)
110 {
111         int i, count = 0;
112
113         for(i=0; i<32; i++) {
114                 count += enum_dev(busid, i);
115         }
116
117         return count;
118 }
119
120 static int enum_dev(int busid, int dev)
121 {
122         int i, count;
123         struct config_data info;
124
125         /* vendor id ffff is invalid */
126         if((cfg_read32(busid, dev, 0, 0) & 0xffff) == 0xffff) {
127                 return 0;
128         }
129         if(read_dev_info(&info, busid, dev, 0) == -1) {
130                 return 0;
131         }
132         /*print_dev_info(&info, busid, dev, 0);*/
133
134         count = 1;
135
136         if(info.hdr_type & TYPE_MULTIFUNC) {
137                 for(i=1; i<8; i++) {
138                         if(read_dev_info(&info, busid, dev, i) == -1) {
139                                 continue;
140                         }
141                         /*print_dev_info(&info, busid, dev, i);*/
142                         count++;
143                 }
144         }
145         return count;
146 }
147
148 static int read_dev_info(struct config_data *res, int bus, int dev, int func)
149 {
150         int i;
151         uint32_t *ptr = (uint32_t*)res;
152
153         *ptr++ = cfg_read32(bus, dev, func, 0);
154         if(res->vendor == 0xffff) {
155                 return -1;
156         }
157
158         for(i=1; i<16; i++) {
159                 *ptr++ = cfg_read32(bus, dev, func, i * 4);
160         }
161         return 0;
162 }
163
164 static void print_dev_info(struct config_data *info, int bus, int dev, int func)
165 {
166         printf("- (%d:%d,%d) Device %04x:%04x: ", bus, dev, func, info->vendor, info->device);
167         printf("\"%s\" (%d) - %s-func\n", class_str(info->class), info->class,
168                         info->hdr_type & TYPE_MULTIFUNC ? "multi" : "single");
169         printf("    subclass: \"%s\" (%d), iface: %d\n", subclass_str(info->class, info->subclass),
170                         info->subclass, info->iface);
171 }
172
173 static uint32_t cfg_read32_m1(int bus, int dev, int func, int reg)
174 {
175         uint32_t addr = ADDR_ENABLE | ADDR_BUSID(bus) | ADDR_DEVID(dev) |
176                 ADDR_FUNC(func) | reg;
177
178         outl(addr, CONFIG_ADDR_PORT);
179         return inl(CONFIG_DATA_PORT);
180 }
181
182 static uint32_t cfg_read32_m2(int bus, int dev, int func, int reg)
183 {
184         panic("BUG: PCI mess mechanism #2 not implemented yet!");
185         return 0;
186 }
187
188 static const char *class_names[] = {
189         "unknown",
190         "mass storage controller",
191         "network controller",
192         "display controller",
193         "multimedia device",
194         "memory controller",
195         "bridge device",
196         "simple communication controller",
197         "base system peripheral",
198         "input device",
199         "docking station",
200         "processor",
201         "serial bus controller",
202         "wireless controller",
203         "intelligent I/O controller",
204         "satellite communication controller",
205         "encryption/decryption controller",
206         "data acquisition & signal processing controller"
207 };
208
209 static const char *class_mass_names[] = {
210         "SCSI bus controller",
211         "IDE controller",
212         "floppy disk controller",
213         "IPI bus controller",
214         "RAID controller"
215 };
216
217 static const char *class_net_names[] = {
218         "ethernet controller",
219         "token ring controller",
220         "FDDI controller",
221         "ATM controller",
222         "ISDN controller"
223 };
224
225 static const char *class_disp_names[] = {
226         "VGA-compatible controller",
227         "XGA controller",
228         "3D controller"
229 };
230
231 static const char *class_mm_names[] = {
232         "video device",
233         "audio device",
234         "telephony device"
235 };
236
237 static const char *class_bridge_names[] = {
238         "host bridge",
239         "ISA bridge",
240         "EISA bridge",
241         "MCA bridge",
242         "PCI-to-PCI bridge",
243         "Subtractive decode PCI-to-PCI bridge",
244         "PCMCIA bridge",
245         "NuBus bridge",
246         "CardBus bridge",
247         "RACEway bridge"
248 };
249
250 static const char *class_comm_names[] = {
251         "serial controller",
252         "parallel/IEEE1284",
253         "multiport serial controller",
254         "modem"
255 };
256
257 static const char *class_base_names[] = {
258         "interrupt controller",
259         "DMA controller",
260         "timer",
261         "RTC",
262         "PCI hot-plug controller"
263 };
264
265 static const char *class_input_names[] = {
266         "keyboard controller",
267         "digitizer",
268         "mouse controller",
269         "scanner controller",
270         "gameport controller"
271 };
272
273 static const char *class_ser_names[] = {
274         "firewire",
275         "ACCESS.bus",
276         "SSA",
277         "USB",
278         "Fibre Channel",
279         "SMBus"
280 };
281
282 static const char *class_sat_names[] = {
283         "TV",
284         "audio",
285         "voice",
286         "data"
287 };
288
289
290 static const char *class_str(int cc)
291 {
292         if(cc == 0xff) {
293                 return "other";
294         }
295         if(cc >= 0x12) {
296                 return "unknown";
297         }
298         return class_names[cc];
299 }
300
301 static const char *subclass_str(int cc, int sub)
302 {
303         if(sub == 0x80) return "other";
304
305         switch(cc) {
306         case 0:
307                 if(sub == 1) return "VGA-compatible device";
308                 return "unknown";
309
310         case 1:
311                 if(sub > 4) return "unknown";
312                 return class_mass_names[sub];
313
314         case 2:
315                 if(sub > 4) return "unknown";
316                 return class_net_names[sub];
317
318         case 3:
319                 if(sub > 2) return "unknown";
320                 return class_disp_names[sub];
321
322         case 4:
323                 if(sub > 2) return "unknown";
324                 return class_mm_names[sub];
325
326         case 5:
327                 if(sub == 0) return "RAM";
328                 if(sub == 1) return "flash";
329                 return "unknown";
330
331         case 6:
332                 if(sub > 8) return "unknown";
333                 return class_bridge_names[sub];
334
335         case 7:
336                 if(sub > 3) return "unknown";
337                 return class_comm_names[sub];
338
339         case 8:
340                 if(sub > 4) return "unknown";
341                 return class_base_names[sub];
342
343         case 9:
344                 if(sub > 4) return "unknown";
345                 return class_input_names[sub];
346
347         case 10:
348                 if(sub == 0) return "generic docking station";
349                 return "unknown";
350
351         case 11:
352                 switch(sub) {
353                 case 0: return "386";
354                 case 1: return "486";
355                 case 2: return "pentium";
356                 case 0x10: return "alpha";
357                 case 0x20: return "powerpc";
358                 case 0x30: return "mips";
359                 case 0x40: return "co-processor";
360                 default:
361                         break;
362                 }
363                 return "unknown";
364
365         case 12:
366                 if(sub > 5) return "unknown";
367                 return class_ser_names[sub];
368
369         case 13:
370                 if(sub == 0) return "irda controller";
371                 if(sub == 1) return "IR controller";
372                 if(sub == 0x10) return "RF controller";
373                 return "unknonw";
374
375         case 15:
376                 if(sub > 4) return "unknown";
377                 return class_sat_names[sub];
378
379         case 16:
380                 if(sub == 0) return "network & computing crypto";
381                 if(sub == 1) return "entertainment crypto";
382                 return "unknown";
383
384         case 17:
385                 if(sub == 0) return "DPIO module";
386                 return "unknown";
387
388         default:
389                 break;
390         }
391         return "unknown";
392 }