xref: /freebsd/sys/compat/x86bios/x86bios.c (revision 4ed925457ab06e83238a5db33e89ccc94b99a713)
1 /*-
2  * Copyright (c) 2009 Alex Keda <admin@lissyara.su>
3  * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include "opt_x86bios.h"
32 
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/lock.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/proc.h>
41 #include <sys/sysctl.h>
42 
43 #include <contrib/x86emu/x86emu.h>
44 #include <contrib/x86emu/x86emu_regs.h>
45 #include <compat/x86bios/x86bios.h>
46 
47 #include <dev/pci/pcireg.h>
48 #include <dev/pci/pcivar.h>
49 
50 #include <machine/cpufunc.h>
51 
52 #include <vm/vm.h>
53 #include <vm/pmap.h>
54 
55 #define	X86BIOS_PAGE_SIZE	0x00001000	/* 4K */
56 
57 #define	X86BIOS_IVT_SIZE	0x00000500	/* 1K + 256 (BDA) */
58 #define	X86BIOS_SEG_SIZE	0x00010000	/* 64K */
59 #define	X86BIOS_MEM_SIZE	0x00100000	/* 1M */
60 
61 #define	X86BIOS_IVT_BASE	0x00000000
62 #define	X86BIOS_RAM_BASE	0x00001000
63 #define	X86BIOS_ROM_BASE	0x000a0000	/* XXX EBDA? */
64 
65 #define	X86BIOS_ROM_SIZE	(X86BIOS_MEM_SIZE - X86BIOS_ROM_BASE)
66 
67 #define	X86BIOS_PAGES		(X86BIOS_MEM_SIZE / X86BIOS_PAGE_SIZE)
68 
69 #define	X86BIOS_R_DS		_pad1
70 #define	X86BIOS_R_SS		_pad2
71 
72 static struct x86emu x86bios_emu;
73 
74 static struct mtx x86bios_lock;
75 
76 static void *x86bios_ivt;
77 static void *x86bios_rom;
78 static void *x86bios_seg;
79 
80 static vm_offset_t *x86bios_map;
81 
82 static vm_paddr_t x86bios_seg_phys;
83 
84 SYSCTL_NODE(_debug, OID_AUTO, x86bios, CTLFLAG_RD, NULL, "x86bios debugging");
85 static int x86bios_trace_call;
86 TUNABLE_INT("debug.x86bios.call", &x86bios_trace_call);
87 SYSCTL_INT(_debug_x86bios, OID_AUTO, call, CTLFLAG_RW, &x86bios_trace_call, 0,
88     "Trace far function calls");
89 static int x86bios_trace_int;
90 TUNABLE_INT("debug.x86bios.int", &x86bios_trace_int);
91 SYSCTL_INT(_debug_x86bios, OID_AUTO, int, CTLFLAG_RW, &x86bios_trace_int, 0,
92     "Trace software interrupt handlers");
93 
94 static void *
95 x86bios_get_pages(uint32_t offset, size_t size)
96 {
97 	int i;
98 
99 	if (offset + size > X86BIOS_MEM_SIZE)
100 		return (NULL);
101 
102 	i = offset / X86BIOS_PAGE_SIZE;
103 	if (x86bios_map[i] != 0)
104 		return ((void *)(x86bios_map[i] + offset -
105 		    i * X86BIOS_PAGE_SIZE));
106 
107 	return (NULL);
108 }
109 
110 static void
111 x86bios_set_pages(vm_offset_t va, vm_paddr_t pa, size_t size)
112 {
113 	int i, j;
114 
115 	for (i = pa / X86BIOS_PAGE_SIZE, j = 0;
116 	    j < howmany(size, X86BIOS_PAGE_SIZE); i++, j++)
117 		x86bios_map[i] = va + j * X86BIOS_PAGE_SIZE;
118 }
119 
120 static uint8_t
121 x86bios_emu_rdb(struct x86emu *emu, uint32_t addr)
122 {
123 	uint8_t *va;
124 
125 	va = x86bios_get_pages(addr, sizeof(*va));
126 	if (va == NULL)
127 		x86emu_halt_sys(emu);
128 
129 	return (*va);
130 }
131 
132 static uint16_t
133 x86bios_emu_rdw(struct x86emu *emu, uint32_t addr)
134 {
135 	uint16_t *va;
136 
137 	va = x86bios_get_pages(addr, sizeof(*va));
138 	if (va == NULL)
139 		x86emu_halt_sys(emu);
140 
141 	return (le16toh(*va));
142 }
143 
144 static uint32_t
145 x86bios_emu_rdl(struct x86emu *emu, uint32_t addr)
146 {
147 	uint32_t *va;
148 
149 	va = x86bios_get_pages(addr, sizeof(*va));
150 	if (va == NULL)
151 		x86emu_halt_sys(emu);
152 
153 	return (le32toh(*va));
154 }
155 
156 static void
157 x86bios_emu_wrb(struct x86emu *emu, uint32_t addr, uint8_t val)
158 {
159 	uint8_t *va;
160 
161 	va = x86bios_get_pages(addr, sizeof(*va));
162 	if (va == NULL)
163 		x86emu_halt_sys(emu);
164 
165 	*va = val;
166 }
167 
168 static void
169 x86bios_emu_wrw(struct x86emu *emu, uint32_t addr, uint16_t val)
170 {
171 	uint16_t *va;
172 
173 	va = x86bios_get_pages(addr, sizeof(*va));
174 	if (va == NULL)
175 		x86emu_halt_sys(emu);
176 
177 	*va = htole16(val);
178 }
179 
180 static void
181 x86bios_emu_wrl(struct x86emu *emu, uint32_t addr, uint32_t val)
182 {
183 	uint32_t *va;
184 
185 	va = x86bios_get_pages(addr, sizeof(*va));
186 	if (va == NULL)
187 		x86emu_halt_sys(emu);
188 
189 	*va = htole32(val);
190 }
191 
192 static uint8_t
193 x86bios_emu_inb(struct x86emu *emu, uint16_t port)
194 {
195 
196 	if (port == 0xb2) /* APM scratch register */
197 		return (0);
198 	if (port >= 0x80 && port < 0x88) /* POST status register */
199 		return (0);
200 
201 	return (inb(port));
202 }
203 
204 static uint16_t
205 x86bios_emu_inw(struct x86emu *emu, uint16_t port)
206 {
207 
208 	if (port >= 0x80 && port < 0x88) /* POST status register */
209 		return (0);
210 
211 	return (inw(port));
212 }
213 
214 static uint32_t
215 x86bios_emu_inl(struct x86emu *emu, uint16_t port)
216 {
217 
218 	if (port >= 0x80 && port < 0x88) /* POST status register */
219 		return (0);
220 
221 	return (inl(port));
222 }
223 
224 static void
225 x86bios_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val)
226 {
227 
228 	if (port == 0xb2) /* APM scratch register */
229 		return;
230 	if (port >= 0x80 && port < 0x88) /* POST status register */
231 		return;
232 
233 	outb(port, val);
234 }
235 
236 static void
237 x86bios_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val)
238 {
239 
240 	if (port >= 0x80 && port < 0x88) /* POST status register */
241 		return;
242 
243 	outw(port, val);
244 }
245 
246 static void
247 x86bios_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val)
248 {
249 
250 	if (port >= 0x80 && port < 0x88) /* POST status register */
251 		return;
252 
253 	outl(port, val);
254 }
255 
256 static void
257 x86bios_emu_get_intr(struct x86emu *emu, int intno)
258 {
259 	uint16_t *sp;
260 	uint32_t iv;
261 
262 	emu->x86.R_SP -= 6;
263 
264 	sp = (uint16_t *)((vm_offset_t)x86bios_seg + emu->x86.R_SP);
265 	sp[0] = htole16(emu->x86.R_IP);
266 	sp[1] = htole16(emu->x86.R_CS);
267 	sp[2] = htole16(emu->x86.R_FLG);
268 
269 	iv = x86bios_get_intr(intno);
270 	emu->x86.R_IP = iv & 0x000f;
271 	emu->x86.R_CS = (iv >> 12) & 0xffff;
272 	emu->x86.R_FLG &= ~(F_IF | F_TF);
273 }
274 
275 void *
276 x86bios_alloc(uint32_t *offset, size_t size)
277 {
278 	void *vaddr;
279 
280 	if (offset == NULL || size == 0)
281 		return (NULL);
282 
283 	vaddr = contigmalloc(size, M_DEVBUF, M_NOWAIT, X86BIOS_RAM_BASE,
284 	    X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0);
285 	if (vaddr != NULL) {
286 		*offset = vtophys(vaddr);
287 		x86bios_set_pages((vm_offset_t)vaddr, *offset, size);
288 	}
289 
290 	return (vaddr);
291 }
292 
293 void
294 x86bios_free(void *addr, size_t size)
295 {
296 	vm_paddr_t paddr;
297 
298 	if (addr == NULL || size == 0)
299 		return;
300 
301 	paddr = vtophys(addr);
302 	if (paddr < X86BIOS_RAM_BASE || paddr >= X86BIOS_ROM_BASE ||
303 	    paddr % X86BIOS_PAGE_SIZE != 0)
304 		return;
305 
306 	bzero(x86bios_map + paddr / X86BIOS_PAGE_SIZE,
307 	    sizeof(*x86bios_map) * howmany(size, X86BIOS_PAGE_SIZE));
308 	contigfree(addr, size, M_DEVBUF);
309 }
310 
311 void
312 x86bios_init_regs(struct x86regs *regs)
313 {
314 
315 	bzero(regs, sizeof(*regs));
316 	regs->X86BIOS_R_DS = regs->X86BIOS_R_SS = x86bios_seg_phys >> 4;
317 }
318 
319 void
320 x86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
321 {
322 
323 	if (x86bios_map == NULL)
324 		return;
325 
326 	if (x86bios_trace_call)
327 		printf("Calling 0x%05x (ax=0x%04x bx=0x%04x "
328 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
329 		    (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX,
330 		    regs->R_DX, regs->R_ES, regs->R_DI);
331 
332 	mtx_lock_spin(&x86bios_lock);
333 	memcpy(&x86bios_emu.x86, regs, sizeof(*regs));
334 	x86emu_exec_call(&x86bios_emu, seg, off);
335 	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
336 	mtx_unlock_spin(&x86bios_lock);
337 
338 	if (x86bios_trace_call)
339 		printf("Exiting 0x%05x (ax=0x%04x bx=0x%04x "
340 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
341 		    (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX,
342 		    regs->R_DX, regs->R_ES, regs->R_DI);
343 }
344 
345 uint32_t
346 x86bios_get_intr(int intno)
347 {
348 	uint32_t *iv;
349 
350 	iv = (uint32_t *)((vm_offset_t)x86bios_ivt + intno * 4);
351 
352 	return (le32toh(*iv));
353 }
354 
355 void
356 x86bios_intr(struct x86regs *regs, int intno)
357 {
358 
359 	if (intno < 0 || intno > 255)
360 		return;
361 
362 	if (x86bios_map == NULL)
363 		return;
364 
365 	if (x86bios_trace_int)
366 		printf("Calling int 0x%x (ax=0x%04x bx=0x%04x "
367 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
368 		    intno, regs->R_AX, regs->R_BX, regs->R_CX,
369 		    regs->R_DX, regs->R_ES, regs->R_DI);
370 
371 	mtx_lock_spin(&x86bios_lock);
372 	memcpy(&x86bios_emu.x86, regs, sizeof(*regs));
373 	x86emu_exec_intr(&x86bios_emu, intno);
374 	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
375 	mtx_unlock_spin(&x86bios_lock);
376 
377 	if (x86bios_trace_int)
378 		printf("Exiting int 0x%x (ax=0x%04x bx=0x%04x "
379 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
380 		    intno, regs->R_AX, regs->R_BX, regs->R_CX,
381 		    regs->R_DX, regs->R_ES, regs->R_DI);
382 }
383 
384 void *
385 x86bios_offset(uint32_t offset)
386 {
387 
388 	return (x86bios_get_pages(offset, 1));
389 }
390 
391 void *
392 x86bios_get_orm(uint32_t offset)
393 {
394 	uint8_t *p;
395 
396 	/* Does the shadow ROM contain BIOS POST code for x86? */
397 	p = x86bios_offset(offset);
398 	if (p == NULL || p[0] != 0x55 || p[1] != 0xaa || p[3] != 0xe9)
399 		return (NULL);
400 
401 	return (p);
402 }
403 
404 int
405 x86bios_match_device(uint32_t offset, device_t dev)
406 {
407 	uint8_t *p;
408 	uint16_t device, vendor;
409 	uint8_t class, progif, subclass;
410 
411 	/* Does the shadow ROM contain BIOS POST code for x86? */
412 	p = x86bios_get_orm(offset);
413 	if (p == NULL)
414 		return (0);
415 
416 	/* Does it contain PCI data structure? */
417 	p += le16toh(*(uint16_t *)(p + 0x18));
418 	if (bcmp(p, "PCIR", 4) != 0 ||
419 	    le16toh(*(uint16_t *)(p + 0x0a)) < 0x18 || *(p + 0x14) != 0)
420 		return (0);
421 
422 	/* Does it match the vendor, device, and classcode? */
423 	vendor = le16toh(*(uint16_t *)(p + 0x04));
424 	device = le16toh(*(uint16_t *)(p + 0x06));
425 	progif = *(p + 0x0d);
426 	subclass = *(p + 0x0e);
427 	class = *(p + 0x0f);
428 	if (vendor != pci_get_vendor(dev) || device != pci_get_device(dev) ||
429 	    class != pci_get_class(dev) || subclass != pci_get_subclass(dev) ||
430 	    progif != pci_get_progif(dev))
431 		return (0);
432 
433 	return (1);
434 }
435 
436 static __inline int
437 x86bios_map_mem(void)
438 {
439 
440 	x86bios_ivt = pmap_mapbios(X86BIOS_IVT_BASE, X86BIOS_IVT_SIZE);
441 	if (x86bios_ivt == NULL)
442 		return (1);
443 	x86bios_rom = pmap_mapdev(X86BIOS_ROM_BASE, X86BIOS_ROM_SIZE);
444 	if (x86bios_rom == NULL) {
445 		pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
446 		return (1);
447 	}
448 	x86bios_seg = contigmalloc(X86BIOS_SEG_SIZE, M_DEVBUF, M_WAITOK,
449 	    X86BIOS_RAM_BASE, X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0);
450 	x86bios_seg_phys = vtophys(x86bios_seg);
451 
452 	return (0);
453 }
454 
455 static __inline void
456 x86bios_unmap_mem(void)
457 {
458 
459 	pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
460 	pmap_unmapdev((vm_offset_t)x86bios_rom, X86BIOS_ROM_SIZE);
461 	contigfree(x86bios_seg, X86BIOS_SEG_SIZE, M_DEVBUF);
462 }
463 
464 static void
465 x86bios_init(void *arg __unused)
466 {
467 	int i;
468 
469 	mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_SPIN);
470 
471 	if (x86bios_map_mem() != 0)
472 		return;
473 
474 	x86bios_map = malloc(sizeof(*x86bios_map) * X86BIOS_PAGES, M_DEVBUF,
475 	    M_WAITOK | M_ZERO);
476 	x86bios_set_pages((vm_offset_t)x86bios_ivt, X86BIOS_IVT_BASE,
477 	    X86BIOS_IVT_SIZE);
478 	x86bios_set_pages((vm_offset_t)x86bios_rom, X86BIOS_ROM_BASE,
479 	    X86BIOS_ROM_SIZE);
480 	x86bios_set_pages((vm_offset_t)x86bios_seg, x86bios_seg_phys,
481 	    X86BIOS_SEG_SIZE);
482 
483 	bzero(&x86bios_emu, sizeof(x86bios_emu));
484 
485 	x86bios_emu.emu_rdb = x86bios_emu_rdb;
486 	x86bios_emu.emu_rdw = x86bios_emu_rdw;
487 	x86bios_emu.emu_rdl = x86bios_emu_rdl;
488 	x86bios_emu.emu_wrb = x86bios_emu_wrb;
489 	x86bios_emu.emu_wrw = x86bios_emu_wrw;
490 	x86bios_emu.emu_wrl = x86bios_emu_wrl;
491 
492 	x86bios_emu.emu_inb = x86bios_emu_inb;
493 	x86bios_emu.emu_inw = x86bios_emu_inw;
494 	x86bios_emu.emu_inl = x86bios_emu_inl;
495 	x86bios_emu.emu_outb = x86bios_emu_outb;
496 	x86bios_emu.emu_outw = x86bios_emu_outw;
497 	x86bios_emu.emu_outl = x86bios_emu_outl;
498 
499 	for (i = 0; i < 256; i++)
500 		x86bios_emu._x86emu_intrTab[i] = x86bios_emu_get_intr;
501 }
502 
503 static void
504 x86bios_uninit(void *arg __unused)
505 {
506 	vm_offset_t *map = x86bios_map;
507 
508 	mtx_lock_spin(&x86bios_lock);
509 	if (x86bios_map != NULL) {
510 		free(x86bios_map, M_DEVBUF);
511 		x86bios_map = NULL;
512 	}
513 	mtx_unlock_spin(&x86bios_lock);
514 
515 	if (map != NULL)
516 		x86bios_unmap_mem();
517 
518 	mtx_destroy(&x86bios_lock);
519 }
520 
521 static int
522 x86bios_modevent(module_t mod __unused, int type, void *data __unused)
523 {
524 
525 	switch (type) {
526 	case MOD_LOAD:
527 		x86bios_init(NULL);
528 		break;
529 	case MOD_UNLOAD:
530 		x86bios_uninit(NULL);
531 		break;
532 	default:
533 		return (ENOTSUP);
534 	}
535 
536 	return (0);
537 }
538 
539 static moduledata_t x86bios_mod = {
540 	"x86bios",
541 	x86bios_modevent,
542 	NULL,
543 };
544 
545 DECLARE_MODULE(x86bios, x86bios_mod, SI_SUB_CPU, SI_ORDER_ANY);
546 MODULE_VERSION(x86bios, 1);
547