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 /*
30 * PCI BIOS access return codes
31 */
32 // TODO: write routine to convert these to LK errors
33 #define _PCI_SUCCESSFUL 0x00
34 #define _PCI_FUNC_NOT_SUPPORTED 0x81
35 #define _PCI_BAD_VENDOR_ID 0x83
36 #define _PCI_DEVICE_NOT_FOUND 0x86
37 #define _PCI_BAD_REGISTER_NUMBER 0x87
38 #define _PCI_SET_FAILED 0x88
39 #define _PCI_BUFFER_TOO_SMALL 0x89
40
41
42 #define PCIBIOS_PRESENT 0xB101
43 #define PCIBIOS_FIND_PCI_DEVICE 0xB102
44 #define PCIBIOS_FIND_PCI_CLASS_CODE 0xB103
45 #define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xB106
46 #define PCIBIOS_READ_CONFIG_BYTE 0xB108
47 #define PCIBIOS_READ_CONFIG_WORD 0xB109
48 #define PCIBIOS_READ_CONFIG_DWORD 0xB10A
49 #define PCIBIOS_WRITE_CONFIG_BYTE 0xB10B
50 #define PCIBIOS_WRITE_CONFIG_WORD 0xB10C
51 #define PCIBIOS_WRITE_CONFIG_DWORD 0xB10D
52 #define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0xB10E
53 #define PCIBIOS_PCI_SET_IRQ_HW_INT 0xB10F
54
55 #define PCIBIOS_SUCCESSFUL 0x00
56 #define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
57 #define PCIBIOS_BAD_VENDOR_ID 0x83
58 #define PCIBIOS_DEVICE_NOT_FOUND 0x86
59 #define PCIBIOS_BAD_REGISTER_NUMBER 0x87
60 #define PCIBIOS_SET_FAILED 0x88
61 #define PCIBIOS_BUFFER_TOO_SMALL 0x89
62
63 /*
64 * BIOS32 entry header
65 */
66 typedef struct {
67 uint8_t magic[4]; // "_32_"
68 void *entry; // entry point
69 uint8_t revision;
70 uint8_t length;
71 uint8_t checksum;
72 uint8_t reserved[5];
73 } __PACKED pci_bios_info;
74
75 /*
76 * scan for pci bios
77 */
78 static const char *pci_bios_magic = "_32_";
find_pci_bios_info(void)79 static pci_bios_info *find_pci_bios_info(void) {
80 uint32_t *head = (uint32_t *) (0x000e0000 + KERNEL_BASE);
81 int8_t sum, *b;
82 uint i;
83
84 while (head < (uint32_t *) (0x000ffff0 + KERNEL_BASE)) {
85 if (*head == *(uint32_t *) pci_bios_magic) {
86 // perform the checksum
87 sum = 0;
88 b = (int8_t *) head;
89 for (i=0; i < sizeof(pci_bios_info); i++) {
90 sum += b[i];
91 }
92
93 if (sum == 0) {
94 return (pci_bios_info *) head;
95 }
96 }
97
98 head += 4;
99 }
100
101 return NULL;
102 }
103
104 /*
105 * local BIOS32 PCI routines
106 */
107 static const char *pci_signature = "PCI ";
108
109 // new C++ version
detect()110 pci_bios32 *pci_bios32::detect() {
111 LTRACE_ENTRY;
112
113 pci_bios_info *pci = find_pci_bios_info();
114 if (!pci) {
115 return nullptr;
116 }
117
118 dprintf(INFO, "PCI: found BIOS32 structure at %p\n", pci);
119
120 LTRACEF("BIOS32 header info:\n");
121 LTRACEF("magic '%c%c%c%c' entry %p len %d checksum %#hhx\n",
122 pci->magic[0], pci->magic[1], pci->magic[2], pci->magic[3],
123 pci->entry, pci->length * 16, pci->checksum);
124
125 uint32_t adr, temp, len;
126 uint8_t err;
127
128 bios32_entry b32_entry;
129 b32_entry.offset = (uint32_t)(uintptr_t)pci->entry + KERNEL_BASE;
130 b32_entry.selector = CODE_SELECTOR;
131
132 __asm__(
133 "lcall *(%%edi)"
134 : "=a"(err), /* AL out=status */
135 "=b"(adr), /* EBX out=code segment base adr */
136 "=c"(len), /* ECX out=code segment size */
137 "=d"(temp) /* EDX out=entry pt offset in code */
138 : "0"(0x49435024),/* EAX in=service="$PCI" */
139 "1"(0), /* EBX in=0=get service entry pt */
140 "D"(&b32_entry)
141 : "cc", "memory"
142 );
143
144 if (err == 0x80) {
145 dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
146 return nullptr;
147 }
148
149 if (err != 0) {
150 dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
151 return nullptr;
152 }
153
154 LTRACEF("BIOS32 entry segment base %#x offset %#x\n", adr, temp);
155
156 b32_entry.offset = adr + temp + KERNEL_BASE;
157
158 // now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
159 uint16_t present, version, busses;
160 uint32_t signature;
161 __asm__(
162 "lcall *(%%edi) \n\t"
163 "jc 1f \n\t"
164 "xor %%ah,%%ah \n"
165 "1:"
166 : "=a"(present),
167 "=b"(version),
168 "=c"(busses),
169 "=d"(signature)
170 : "0"(PCIBIOS_PRESENT),
171 "D"(&b32_entry)
172 : "cc", "memory"
173 );
174 LTRACEF("PCI_BIOS_PRESENT returns present %#x, version %#x, busses %#x, signature %#x\n",
175 present, version, busses, signature);
176
177 if (present & 0xff00) {
178 dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%#02x\n", present >> 8);
179 return nullptr;
180 }
181
182 if (signature != *(uint32_t *)pci_signature) {
183 dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%#08x\n", signature);
184 return nullptr;
185 }
186
187 int last_bus = busses & 0xff;
188
189 auto b32 = new pci_bios32(b32_entry);
190 b32->set_last_bus(last_bus);
191
192 return b32;
193 }
194
read_config_byte(const pci_location_t state,uint32_t reg,uint8_t * value)195 int pci_bios32::read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
196 uint32_t bx, ret;
197
198 bx = state.bus;
199 bx <<= 8;
200 bx |= (state.dev << 3) | state.fn;
201 __asm__(
202 "lcall *(%%esi) \n\t"
203 "jc 1f \n\t"
204 "xor %%ah,%%ah \n"
205 "1:"
206 : "=c"(*value),
207 "=a"(ret)
208 : "1"(PCIBIOS_READ_CONFIG_BYTE),
209 "b"(bx),
210 "D"(reg),
211 "S"(&bios32_entry_)
212 : "cc", "memory");
213 ret >>= 8;
214 return ret & 0xFF;
215 }
216
read_config_half(const pci_location_t state,uint32_t reg,uint16_t * value)217 int pci_bios32::read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
218 uint32_t bx, ret;
219
220 bx = state.bus;
221 bx <<= 8;
222 bx |= (state.dev << 3) | state.fn;
223 __asm__(
224 "lcall *(%%esi) \n\t"
225 "jc 1f \n\t"
226 "xor %%ah,%%ah \n"
227 "1:"
228 : "=c"(*value),
229 "=a"(ret)
230 : "1"(PCIBIOS_READ_CONFIG_WORD),
231 "b"(bx),
232 "D"(reg),
233 "S"(&bios32_entry_)
234 : "cc", "memory");
235 ret >>= 8;
236 return ret & 0xFF;
237 }
238
read_config_word(const pci_location_t state,uint32_t reg,uint32_t * value)239 int pci_bios32::read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
240 uint32_t bx, ret;
241
242 bx = state.bus;
243 bx <<= 8;
244 bx |= (state.dev << 3) | state.fn;
245 __asm__(
246 "lcall *(%%esi) \n\t"
247 "jc 1f \n\t"
248 "xor %%ah,%%ah \n"
249 "1:"
250 : "=c"(*value),
251 "=a"(ret)
252 : "1"(PCIBIOS_READ_CONFIG_DWORD),
253 "b"(bx),
254 "D"(reg),
255 "S"(&bios32_entry_)
256 : "cc", "memory");
257 ret >>= 8;
258 return ret & 0xFF;
259 }
260
write_config_byte(const pci_location_t state,uint32_t reg,uint8_t value)261 int pci_bios32::write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
262 uint32_t bx, ret;
263
264 bx = state.bus;
265 bx <<= 8;
266 bx |= (state.dev << 3) | state.fn;
267 __asm__(
268 "lcall *(%%esi) \n\t"
269 "jc 1f \n\t"
270 "xor %%ah,%%ah \n"
271 "1:"
272 : "=a"(ret)
273 : "0"(PCIBIOS_WRITE_CONFIG_BYTE),
274 "c"(value),
275 "b"(bx),
276 "D"(reg),
277 "S"(&bios32_entry_)
278 : "cc", "memory");
279 ret >>= 8;
280 return ret & 0xFF;
281 }
282
write_config_half(const pci_location_t state,uint32_t reg,uint16_t value)283 int pci_bios32::write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
284 uint32_t bx, ret;
285
286 bx = state.bus;
287 bx <<= 8;
288 bx |= (state.dev << 3) | state.fn;
289 __asm__(
290 "lcall *(%%esi) \n\t"
291 "jc 1f \n\t"
292 "xor %%ah,%%ah \n"
293 "1:"
294 : "=a"(ret)
295 : "0"(PCIBIOS_WRITE_CONFIG_WORD),
296 "c"(value),
297 "b"(bx),
298 "D"(reg),
299 "S"(&bios32_entry_)
300 : "cc", "memory");
301 ret >>= 8;
302 return ret & 0xFF;
303 }
304
write_config_word(const pci_location_t state,uint32_t reg,uint32_t value)305 int pci_bios32::write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
306 uint32_t bx, ret;
307
308 bx = state.bus;
309 bx <<= 8;
310 bx |= (state.dev << 3) | state.fn;
311 __asm__(
312 "lcall *(%%esi) \n\t"
313 "jc 1f \n\t"
314 "xor %%ah,%%ah \n"
315 "1:"
316 : "=a"(ret)
317 : "0"(PCIBIOS_WRITE_CONFIG_DWORD),
318 "c"(value),
319 "b"(bx),
320 "D"(reg),
321 "S"(&bios32_entry_)
322 : "cc", "memory");
323 ret >>= 8;
324 return ret & 0xFF;
325 }
326
327 #endif
328