1 /*
2 * Copyright (c) 2009 Corey Tabaka
3 * Copyright (c) 2020 Travis Geiselbrecht
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9 #include "bios32.h"
10
11 #include <lk/debug.h>
12 #include <lk/err.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <kernel/thread.h>
16 #include <kernel/spinlock.h>
17 #include <dev/bus/pci.h>
18 #include <lk/trace.h>
19
20 #include "pci_priv.h"
21
22 #if ARCH_X86_32
23 // Only actually supported on x86-32
24
25 #include <arch/x86/descriptor.h>
26
27 #define LOCAL_TRACE 0
28
29 #define PCIBIOS_PRESENT 0xB101
30 #define PCIBIOS_FIND_PCI_DEVICE 0xB102
31 #define PCIBIOS_FIND_PCI_CLASS_CODE 0xB103
32 #define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xB106
33 #define PCIBIOS_READ_CONFIG_BYTE 0xB108
34 #define PCIBIOS_READ_CONFIG_WORD 0xB109
35 #define PCIBIOS_READ_CONFIG_DWORD 0xB10A
36 #define PCIBIOS_WRITE_CONFIG_BYTE 0xB10B
37 #define PCIBIOS_WRITE_CONFIG_WORD 0xB10C
38 #define PCIBIOS_WRITE_CONFIG_DWORD 0xB10D
39 #define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0xB10E
40 #define PCIBIOS_PCI_SET_IRQ_HW_INT 0xB10F
41
42 #define PCIBIOS_SUCCESSFUL 0x00
43 #define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
44 #define PCIBIOS_BAD_VENDOR_ID 0x83
45 #define PCIBIOS_DEVICE_NOT_FOUND 0x86
46 #define PCIBIOS_BAD_REGISTER_NUMBER 0x87
47 #define PCIBIOS_SET_FAILED 0x88
48 #define PCIBIOS_BUFFER_TOO_SMALL 0x89
49
50 /*
51 * BIOS32 entry header
52 */
53 typedef struct {
54 uint8_t magic[4]; // "_32_"
55 void *entry; // entry point
56 uint8_t revision;
57 uint8_t length;
58 uint8_t checksum;
59 uint8_t reserved[5];
60 } __PACKED pci_bios_info;
61
62 /*
63 * scan for pci bios
64 */
65 static const char *pci_bios_magic = "_32_";
find_pci_bios_info(void)66 static pci_bios_info *find_pci_bios_info(void) {
67 uint32_t *head = (uint32_t *) (0x000e0000 + KERNEL_BASE);
68 int8_t sum, *b;
69 uint i;
70
71 while (head < (uint32_t *) (0x000ffff0 + KERNEL_BASE)) {
72 if (*head == *(uint32_t *) pci_bios_magic) {
73 // perform the checksum
74 sum = 0;
75 b = (int8_t *) head;
76 for (i=0; i < sizeof(pci_bios_info); i++) {
77 sum += b[i];
78 }
79
80 if (sum == 0) {
81 return (pci_bios_info *) head;
82 }
83 }
84
85 head += 4;
86 }
87
88 return NULL;
89 }
90
91 /*
92 * local BIOS32 PCI routines
93 */
94 static const char *pci_signature = "PCI ";
95
96 // new C++ version
detect()97 pci_bios32 *pci_bios32::detect() {
98 LTRACE_ENTRY;
99
100 pci_bios_info *pci = find_pci_bios_info();
101 if (!pci) {
102 return nullptr;
103 }
104
105 dprintf(INFO, "PCI: found BIOS32 structure at %p\n", pci);
106
107 LTRACEF("BIOS32 header info:\n");
108 LTRACEF("magic '%c%c%c%c' entry %p len %d checksum %#hhx\n",
109 pci->magic[0], pci->magic[1], pci->magic[2], pci->magic[3],
110 pci->entry, pci->length * 16, pci->checksum);
111
112 uint32_t adr, temp, len;
113 uint8_t err;
114
115 bios32_entry b32_entry;
116 b32_entry.offset = (uint32_t)(uintptr_t)pci->entry + KERNEL_BASE;
117 b32_entry.selector = CODE_SELECTOR;
118
119 __asm__(
120 "lcall *(%%edi)"
121 : "=a"(err), /* AL out=status */
122 "=b"(adr), /* EBX out=code segment base adr */
123 "=c"(len), /* ECX out=code segment size */
124 "=d"(temp) /* EDX out=entry pt offset in code */
125 : "0"(0x49435024),/* EAX in=service="$PCI" */
126 "1"(0), /* EBX in=0=get service entry pt */
127 "D"(&b32_entry)
128 : "cc", "memory"
129 );
130
131 if (err == 0x80) {
132 dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
133 return nullptr;
134 }
135
136 if (err != 0) {
137 dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
138 return nullptr;
139 }
140
141 LTRACEF("BIOS32 entry segment base %#x offset %#x\n", adr, temp);
142
143 b32_entry.offset = adr + temp + KERNEL_BASE;
144
145 // now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
146 uint16_t present, version, busses;
147 uint32_t signature;
148 __asm__(
149 "lcall *(%%edi) \n\t"
150 "jc 1f \n\t"
151 "xor %%ah,%%ah \n"
152 "1:"
153 : "=a"(present),
154 "=b"(version),
155 "=c"(busses),
156 "=d"(signature)
157 : "0"(PCIBIOS_PRESENT),
158 "D"(&b32_entry)
159 : "cc", "memory"
160 );
161 LTRACEF("PCI_BIOS_PRESENT returns present %#x, version %#x, busses %#x, signature %#x\n",
162 present, version, busses, signature);
163
164 if (present & 0xff00) {
165 dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%#02x\n", present >> 8);
166 return nullptr;
167 }
168
169 if (signature != *(uint32_t *)pci_signature) {
170 dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%#08x\n", signature);
171 return nullptr;
172 }
173
174 int last_bus = busses & 0xff;
175
176 auto b32 = new pci_bios32(b32_entry);
177 b32->set_last_bus(last_bus);
178
179 return b32;
180 }
181
find_pci_device(pci_location_t * state,uint16_t device_id,uint16_t vendor_id,uint16_t index)182 int pci_bios32::find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index) {
183 uint32_t bx, ret;
184
185 __asm__(
186 "lcall *(%%edi) \n\t"
187 "jc 1f \n\t"
188 "xor %%ah,%%ah \n"
189 "1:"
190 : "=b"(bx),
191 "=a"(ret)
192 : "1"(PCIBIOS_FIND_PCI_DEVICE),
193 "c"(device_id),
194 "d"(vendor_id),
195 "S"(index),
196 "D"(&bios32_entry_)
197 : "cc", "memory");
198
199 state->bus = bx >> 8;
200 state->dev_fn = bx & 0xFF;
201
202 ret >>= 8;
203 return ret & 0xFF;
204 }
205
find_pci_class_code(pci_location_t * state,uint32_t class_code,uint16_t index)206 int pci_bios32::find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index) {
207 uint32_t bx, ret;
208
209 __asm__(
210 "lcall *(%%edi) \n\t"
211 "jc 1f \n\t"
212 "xor %%ah,%%ah \n"
213 "1:"
214 : "=b"(bx),
215 "=a"(ret)
216 : "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
217 "c"(class_code),
218 "S"(index),
219 "D"(&bios32_entry_)
220 : "cc", "memory");
221
222 state->bus = bx >> 8;
223 state->dev_fn = bx & 0xFF;
224
225 ret >>= 8;
226 return ret & 0xFF;
227 }
228
read_config_byte(const pci_location_t * state,uint32_t reg,uint8_t * value)229 int pci_bios32::read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value) {
230 uint32_t bx, ret;
231
232 bx = state->bus;
233 bx <<= 8;
234 bx |= state->dev_fn;
235 __asm__(
236 "lcall *(%%esi) \n\t"
237 "jc 1f \n\t"
238 "xor %%ah,%%ah \n"
239 "1:"
240 : "=c"(*value),
241 "=a"(ret)
242 : "1"(PCIBIOS_READ_CONFIG_BYTE),
243 "b"(bx),
244 "D"(reg),
245 "S"(&bios32_entry_)
246 : "cc", "memory");
247 ret >>= 8;
248 return ret & 0xFF;
249 }
250
read_config_half(const pci_location_t * state,uint32_t reg,uint16_t * value)251 int pci_bios32::read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value) {
252 uint32_t bx, ret;
253
254 bx = state->bus;
255 bx <<= 8;
256 bx |= state->dev_fn;
257 __asm__(
258 "lcall *(%%esi) \n\t"
259 "jc 1f \n\t"
260 "xor %%ah,%%ah \n"
261 "1:"
262 : "=c"(*value),
263 "=a"(ret)
264 : "1"(PCIBIOS_READ_CONFIG_WORD),
265 "b"(bx),
266 "D"(reg),
267 "S"(&bios32_entry_)
268 : "cc", "memory");
269 ret >>= 8;
270 return ret & 0xFF;
271 }
272
read_config_word(const pci_location_t * state,uint32_t reg,uint32_t * value)273 int pci_bios32::read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value) {
274 uint32_t bx, ret;
275
276 bx = state->bus;
277 bx <<= 8;
278 bx |= state->dev_fn;
279 __asm__(
280 "lcall *(%%esi) \n\t"
281 "jc 1f \n\t"
282 "xor %%ah,%%ah \n"
283 "1:"
284 : "=c"(*value),
285 "=a"(ret)
286 : "1"(PCIBIOS_READ_CONFIG_DWORD),
287 "b"(bx),
288 "D"(reg),
289 "S"(&bios32_entry_)
290 : "cc", "memory");
291 ret >>= 8;
292 return ret & 0xFF;
293 }
294
write_config_byte(const pci_location_t * state,uint32_t reg,uint8_t value)295 int pci_bios32::write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value) {
296 uint32_t bx, ret;
297
298 bx = state->bus;
299 bx <<= 8;
300 bx |= state->dev_fn;
301 __asm__(
302 "lcall *(%%esi) \n\t"
303 "jc 1f \n\t"
304 "xor %%ah,%%ah \n"
305 "1:"
306 : "=a"(ret)
307 : "0"(PCIBIOS_WRITE_CONFIG_BYTE),
308 "c"(value),
309 "b"(bx),
310 "D"(reg),
311 "S"(&bios32_entry_)
312 : "cc", "memory");
313 ret >>= 8;
314 return ret & 0xFF;
315 }
316
write_config_half(const pci_location_t * state,uint32_t reg,uint16_t value)317 int pci_bios32::write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value) {
318 uint32_t bx, ret;
319
320 bx = state->bus;
321 bx <<= 8;
322 bx |= state->dev_fn;
323 __asm__(
324 "lcall *(%%esi) \n\t"
325 "jc 1f \n\t"
326 "xor %%ah,%%ah \n"
327 "1:"
328 : "=a"(ret)
329 : "0"(PCIBIOS_WRITE_CONFIG_WORD),
330 "c"(value),
331 "b"(bx),
332 "D"(reg),
333 "S"(&bios32_entry_)
334 : "cc", "memory");
335 ret >>= 8;
336 return ret & 0xFF;
337 }
338
write_config_word(const pci_location_t * state,uint32_t reg,uint32_t value)339 int pci_bios32::write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value) {
340 uint32_t bx, ret;
341
342 bx = state->bus;
343 bx <<= 8;
344 bx |= state->dev_fn;
345 __asm__(
346 "lcall *(%%esi) \n\t"
347 "jc 1f \n\t"
348 "xor %%ah,%%ah \n"
349 "1:"
350 : "=a"(ret)
351 : "0"(PCIBIOS_WRITE_CONFIG_DWORD),
352 "c"(value),
353 "b"(bx),
354 "D"(reg),
355 "S"(&bios32_entry_)
356 : "cc", "memory");
357 ret >>= 8;
358 return ret & 0xFF;
359 }
360
get_irq_routing_options(irq_routing_options_t * options,uint16_t * pci_irqs)361 int pci_bios32::get_irq_routing_options(irq_routing_options_t *options, uint16_t *pci_irqs) {
362 uint32_t ret;
363
364 __asm__(
365 "lcall *(%%esi) \n\t"
366 "jc 1f \n\t"
367 "xor %%ah,%%ah \n"
368 "1:"
369 : "=b"(*pci_irqs),
370 "=a"(ret)
371 : "1"(PCIBIOS_GET_IRQ_ROUTING_OPTIONS),
372 "b"(0),
373 "D"(options),
374 "S"(&bios32_entry_)
375 : "cc", "memory");
376 ret >>= 8;
377 return ret & 0xff;
378 }
379
set_irq_hw_int(const pci_location_t * state,uint8_t int_pin,uint8_t irq)380 int pci_bios32::set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq) {
381 uint32_t bx, cx, ret;
382
383 bx = state->bus;
384 bx <<= 8;
385 bx |= state->dev_fn;
386 cx = irq;
387 cx <<= 8;
388 cx |= int_pin;
389 __asm__(
390 "lcall *(%%esi) \n\t"
391 "jc 1f \n\t"
392 "xor %%ah,%%ah \n"
393 "1:"
394 : "=a"(ret)
395 : "0"(PCIBIOS_PCI_SET_IRQ_HW_INT),
396 "b"(bx),
397 "c"(cx),
398 "S"(&bios32_entry_)
399 : "cc", "memory");
400 ret >>= 8;
401 return ret & 0xFF;
402 }
403
404 #endif
405