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