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