xref: /freebsd/sys/compat/x86bios/x86bios.c (revision eb6d21b4ca6d668cf89afd99eef7baeafa712197)
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 
42 #include <contrib/x86emu/x86emu.h>
43 #include <contrib/x86emu/x86emu_regs.h>
44 #include <compat/x86bios/x86bios.h>
45 
46 #include <dev/pci/pcireg.h>
47 #include <dev/pci/pcivar.h>
48 
49 #include <machine/cpufunc.h>
50 
51 #include <vm/vm.h>
52 #include <vm/pmap.h>
53 
54 #define	X86BIOS_PAGE_SIZE	0x00001000	/* 4K */
55 
56 #define	X86BIOS_IVT_SIZE	0x00000500	/* 1K + 256 (BDA) */
57 #define	X86BIOS_SEG_SIZE	0x00010000	/* 64K */
58 #define	X86BIOS_MEM_SIZE	0x00100000	/* 1M */
59 
60 #define	X86BIOS_IVT_BASE	0x00000000
61 #define	X86BIOS_RAM_BASE	0x00001000
62 #define	X86BIOS_ROM_BASE	0x000a0000	/* XXX EBDA? */
63 
64 #define	X86BIOS_ROM_SIZE	(X86BIOS_MEM_SIZE - X86BIOS_ROM_BASE)
65 
66 #define	X86BIOS_PAGES		(X86BIOS_MEM_SIZE / X86BIOS_PAGE_SIZE)
67 
68 #define	X86BIOS_R_DS		_pad1
69 #define	X86BIOS_R_SS		_pad2
70 
71 static struct x86emu x86bios_emu;
72 
73 static struct mtx x86bios_lock;
74 
75 static void *x86bios_ivt;
76 static void *x86bios_rom;
77 static void *x86bios_seg;
78 
79 static vm_offset_t *x86bios_map;
80 
81 static vm_paddr_t x86bios_seg_phys;
82 
83 static void *
84 x86bios_get_pages(uint32_t offset, size_t size)
85 {
86 	int i;
87 
88 	if (offset + size > X86BIOS_MEM_SIZE)
89 		return (NULL);
90 
91 	i = offset / X86BIOS_PAGE_SIZE;
92 	if (x86bios_map[i] != 0)
93 		return ((void *)(x86bios_map[i] + offset -
94 		    i * X86BIOS_PAGE_SIZE));
95 
96 	return (NULL);
97 }
98 
99 static void
100 x86bios_set_pages(vm_offset_t va, vm_paddr_t pa, size_t size)
101 {
102 	int i, j;
103 
104 	for (i = pa / X86BIOS_PAGE_SIZE, j = 0;
105 	    j < howmany(size, X86BIOS_PAGE_SIZE); i++, j++)
106 		x86bios_map[i] = va + j * X86BIOS_PAGE_SIZE;
107 }
108 
109 static uint8_t
110 x86bios_emu_rdb(struct x86emu *emu, uint32_t addr)
111 {
112 	uint8_t *va;
113 
114 	va = x86bios_get_pages(addr, sizeof(*va));
115 	if (va == NULL)
116 		x86emu_halt_sys(emu);
117 
118 	return (*va);
119 }
120 
121 static uint16_t
122 x86bios_emu_rdw(struct x86emu *emu, uint32_t addr)
123 {
124 	uint16_t *va;
125 
126 	va = x86bios_get_pages(addr, sizeof(*va));
127 	if (va == NULL)
128 		x86emu_halt_sys(emu);
129 
130 	return (le16toh(*va));
131 }
132 
133 static uint32_t
134 x86bios_emu_rdl(struct x86emu *emu, uint32_t addr)
135 {
136 	uint32_t *va;
137 
138 	va = x86bios_get_pages(addr, sizeof(*va));
139 	if (va == NULL)
140 		x86emu_halt_sys(emu);
141 
142 	return (le32toh(*va));
143 }
144 
145 static void
146 x86bios_emu_wrb(struct x86emu *emu, uint32_t addr, uint8_t val)
147 {
148 	uint8_t *va;
149 
150 	va = x86bios_get_pages(addr, sizeof(*va));
151 	if (va == NULL)
152 		x86emu_halt_sys(emu);
153 
154 	*va = val;
155 }
156 
157 static void
158 x86bios_emu_wrw(struct x86emu *emu, uint32_t addr, uint16_t val)
159 {
160 	uint16_t *va;
161 
162 	va = x86bios_get_pages(addr, sizeof(*va));
163 	if (va == NULL)
164 		x86emu_halt_sys(emu);
165 
166 	*va = htole16(val);
167 }
168 
169 static void
170 x86bios_emu_wrl(struct x86emu *emu, uint32_t addr, uint32_t val)
171 {
172 	uint32_t *va;
173 
174 	va = x86bios_get_pages(addr, sizeof(*va));
175 	if (va == NULL)
176 		x86emu_halt_sys(emu);
177 
178 	*va = htole32(val);
179 }
180 
181 static uint8_t
182 x86bios_emu_inb(struct x86emu *emu, uint16_t port)
183 {
184 
185 	if (port == 0xb2) /* APM scratch register */
186 		return (0);
187 	if (port >= 0x80 && port < 0x88) /* POST status register */
188 		return (0);
189 
190 	return (inb(port));
191 }
192 
193 static uint16_t
194 x86bios_emu_inw(struct x86emu *emu, uint16_t port)
195 {
196 
197 	if (port >= 0x80 && port < 0x88) /* POST status register */
198 		return (0);
199 
200 	return (inw(port));
201 }
202 
203 static uint32_t
204 x86bios_emu_inl(struct x86emu *emu, uint16_t port)
205 {
206 
207 	if (port >= 0x80 && port < 0x88) /* POST status register */
208 		return (0);
209 
210 	return (inl(port));
211 }
212 
213 static void
214 x86bios_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val)
215 {
216 
217 	if (port == 0xb2) /* APM scratch register */
218 		return;
219 	if (port >= 0x80 && port < 0x88) /* POST status register */
220 		return;
221 
222 	outb(port, val);
223 }
224 
225 static void
226 x86bios_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val)
227 {
228 
229 	if (port >= 0x80 && port < 0x88) /* POST status register */
230 		return;
231 
232 	outw(port, val);
233 }
234 
235 static void
236 x86bios_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val)
237 {
238 
239 	if (port >= 0x80 && port < 0x88) /* POST status register */
240 		return;
241 
242 	outl(port, val);
243 }
244 
245 static void
246 x86bios_emu_get_intr(struct x86emu *emu, int intno)
247 {
248 	uint16_t *sp;
249 	uint32_t iv;
250 
251 	emu->x86.R_SP -= 6;
252 
253 	sp = (uint16_t *)((vm_offset_t)x86bios_seg + emu->x86.R_SP);
254 	sp[0] = htole16(emu->x86.R_IP);
255 	sp[1] = htole16(emu->x86.R_CS);
256 	sp[2] = htole16(emu->x86.R_FLG);
257 
258 	iv = x86bios_get_intr(intno);
259 	emu->x86.R_IP = iv & 0x000f;
260 	emu->x86.R_CS = (iv >> 12) & 0xffff;
261 	emu->x86.R_FLG &= ~(F_IF | F_TF);
262 }
263 
264 void *
265 x86bios_alloc(uint32_t *offset, size_t size)
266 {
267 	void *vaddr;
268 
269 	if (offset == NULL || size == 0)
270 		return (NULL);
271 
272 	vaddr = contigmalloc(size, M_DEVBUF, M_NOWAIT, X86BIOS_RAM_BASE,
273 	    X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0);
274 	if (vaddr != NULL) {
275 		*offset = vtophys(vaddr);
276 		x86bios_set_pages((vm_offset_t)vaddr, *offset, size);
277 	}
278 
279 	return (vaddr);
280 }
281 
282 void
283 x86bios_free(void *addr, size_t size)
284 {
285 	vm_paddr_t paddr;
286 
287 	if (addr == NULL || size == 0)
288 		return;
289 
290 	paddr = vtophys(addr);
291 	if (paddr < X86BIOS_RAM_BASE || paddr >= X86BIOS_ROM_BASE ||
292 	    paddr % X86BIOS_PAGE_SIZE != 0)
293 		return;
294 
295 	bzero(x86bios_map + paddr / X86BIOS_PAGE_SIZE,
296 	    sizeof(*x86bios_map) * howmany(size, X86BIOS_PAGE_SIZE));
297 	contigfree(addr, size, M_DEVBUF);
298 }
299 
300 void
301 x86bios_init_regs(struct x86regs *regs)
302 {
303 
304 	bzero(regs, sizeof(*regs));
305 	regs->X86BIOS_R_DS = regs->X86BIOS_R_SS = x86bios_seg_phys >> 4;
306 }
307 
308 void
309 x86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
310 {
311 
312 	if (x86bios_map == NULL)
313 		return;
314 
315 	if (bootverbose)
316 		printf("Calling 0x%05x (ax=0x%04x bx=0x%04x "
317 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
318 		    (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX,
319 		    regs->R_DX, regs->R_ES, regs->R_DI);
320 
321 	mtx_lock_spin(&x86bios_lock);
322 	memcpy(&x86bios_emu.x86, regs, sizeof(*regs));
323 	x86emu_exec_call(&x86bios_emu, seg, off);
324 	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
325 	mtx_unlock_spin(&x86bios_lock);
326 
327 	if (bootverbose)
328 		printf("Exiting 0x%05x (ax=0x%04x bx=0x%04x "
329 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
330 		    (seg << 4) + off, regs->R_AX, regs->R_BX, regs->R_CX,
331 		    regs->R_DX, regs->R_ES, regs->R_DI);
332 }
333 
334 uint32_t
335 x86bios_get_intr(int intno)
336 {
337 	uint32_t *iv;
338 
339 	iv = (uint32_t *)((vm_offset_t)x86bios_ivt + intno * 4);
340 
341 	return (le32toh(*iv));
342 }
343 
344 void
345 x86bios_intr(struct x86regs *regs, int intno)
346 {
347 
348 	if (intno < 0 || intno > 255)
349 		return;
350 
351 	if (x86bios_map == NULL)
352 		return;
353 
354 	if (bootverbose)
355 		printf("Calling int 0x%x (ax=0x%04x bx=0x%04x "
356 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
357 		    intno, regs->R_AX, regs->R_BX, regs->R_CX,
358 		    regs->R_DX, regs->R_ES, regs->R_DI);
359 
360 	mtx_lock_spin(&x86bios_lock);
361 	memcpy(&x86bios_emu.x86, regs, sizeof(*regs));
362 	x86emu_exec_intr(&x86bios_emu, intno);
363 	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
364 	mtx_unlock_spin(&x86bios_lock);
365 
366 	if (bootverbose)
367 		printf("Exiting int 0x%x (ax=0x%04x bx=0x%04x "
368 		    "cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",
369 		    intno, regs->R_AX, regs->R_BX, regs->R_CX,
370 		    regs->R_DX, regs->R_ES, regs->R_DI);
371 }
372 
373 void *
374 x86bios_offset(uint32_t offset)
375 {
376 
377 	return (x86bios_get_pages(offset, 1));
378 }
379 
380 void *
381 x86bios_get_orm(uint32_t offset)
382 {
383 	uint8_t *p;
384 
385 	/* Does the shadow ROM contain BIOS POST code for x86? */
386 	p = x86bios_offset(offset);
387 	if (p == NULL || p[0] != 0x55 || p[1] != 0xaa || p[3] != 0xe9)
388 		return (NULL);
389 
390 	return (p);
391 }
392 
393 int
394 x86bios_match_device(uint32_t offset, device_t dev)
395 {
396 	uint8_t *p;
397 	uint16_t device, vendor;
398 	uint8_t class, progif, subclass;
399 
400 	/* Does the shadow ROM contain BIOS POST code for x86? */
401 	p = x86bios_get_orm(offset);
402 	if (p == NULL)
403 		return (0);
404 
405 	/* Does it contain PCI data structure? */
406 	p += le16toh(*(uint16_t *)(p + 0x18));
407 	if (bcmp(p, "PCIR", 4) != 0 ||
408 	    le16toh(*(uint16_t *)(p + 0x0a)) < 0x18 || *(p + 0x14) != 0)
409 		return (0);
410 
411 	/* Does it match the vendor, device, and classcode? */
412 	vendor = le16toh(*(uint16_t *)(p + 0x04));
413 	device = le16toh(*(uint16_t *)(p + 0x06));
414 	progif = *(p + 0x0d);
415 	subclass = *(p + 0x0e);
416 	class = *(p + 0x0f);
417 	if (vendor != pci_get_vendor(dev) || device != pci_get_device(dev) ||
418 	    class != pci_get_class(dev) || subclass != pci_get_subclass(dev) ||
419 	    progif != pci_get_progif(dev))
420 		return (0);
421 
422 	return (1);
423 }
424 
425 static __inline int
426 x86bios_map_mem(void)
427 {
428 
429 	x86bios_ivt = pmap_mapbios(X86BIOS_IVT_BASE, X86BIOS_IVT_SIZE);
430 	if (x86bios_ivt == NULL)
431 		return (1);
432 	x86bios_rom = pmap_mapdev(X86BIOS_ROM_BASE, X86BIOS_ROM_SIZE);
433 	if (x86bios_rom == NULL) {
434 		pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
435 		return (1);
436 	}
437 	x86bios_seg = contigmalloc(X86BIOS_SEG_SIZE, M_DEVBUF, M_WAITOK,
438 	    X86BIOS_RAM_BASE, X86BIOS_ROM_BASE, X86BIOS_PAGE_SIZE, 0);
439 	x86bios_seg_phys = vtophys(x86bios_seg);
440 
441 	return (0);
442 }
443 
444 static __inline void
445 x86bios_unmap_mem(void)
446 {
447 
448 	pmap_unmapdev((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
449 	pmap_unmapdev((vm_offset_t)x86bios_rom, X86BIOS_ROM_SIZE);
450 	contigfree(x86bios_seg, X86BIOS_SEG_SIZE, M_DEVBUF);
451 }
452 
453 static void
454 x86bios_init(void *arg __unused)
455 {
456 	int i;
457 
458 	mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_SPIN);
459 
460 	if (x86bios_map_mem() != 0)
461 		return;
462 
463 	x86bios_map = malloc(sizeof(*x86bios_map) * X86BIOS_PAGES, M_DEVBUF,
464 	    M_WAITOK | M_ZERO);
465 	x86bios_set_pages((vm_offset_t)x86bios_ivt, X86BIOS_IVT_BASE,
466 	    X86BIOS_IVT_SIZE);
467 	x86bios_set_pages((vm_offset_t)x86bios_rom, X86BIOS_ROM_BASE,
468 	    X86BIOS_ROM_SIZE);
469 	x86bios_set_pages((vm_offset_t)x86bios_seg, x86bios_seg_phys,
470 	    X86BIOS_SEG_SIZE);
471 
472 	bzero(&x86bios_emu, sizeof(x86bios_emu));
473 
474 	x86bios_emu.emu_rdb = x86bios_emu_rdb;
475 	x86bios_emu.emu_rdw = x86bios_emu_rdw;
476 	x86bios_emu.emu_rdl = x86bios_emu_rdl;
477 	x86bios_emu.emu_wrb = x86bios_emu_wrb;
478 	x86bios_emu.emu_wrw = x86bios_emu_wrw;
479 	x86bios_emu.emu_wrl = x86bios_emu_wrl;
480 
481 	x86bios_emu.emu_inb = x86bios_emu_inb;
482 	x86bios_emu.emu_inw = x86bios_emu_inw;
483 	x86bios_emu.emu_inl = x86bios_emu_inl;
484 	x86bios_emu.emu_outb = x86bios_emu_outb;
485 	x86bios_emu.emu_outw = x86bios_emu_outw;
486 	x86bios_emu.emu_outl = x86bios_emu_outl;
487 
488 	for (i = 0; i < 256; i++)
489 		x86bios_emu._x86emu_intrTab[i] = x86bios_emu_get_intr;
490 }
491 
492 static void
493 x86bios_uninit(void *arg __unused)
494 {
495 	vm_offset_t *map = x86bios_map;
496 
497 	mtx_lock_spin(&x86bios_lock);
498 	if (x86bios_map != NULL) {
499 		free(x86bios_map, M_DEVBUF);
500 		x86bios_map = NULL;
501 	}
502 	mtx_unlock_spin(&x86bios_lock);
503 
504 	if (map != NULL)
505 		x86bios_unmap_mem();
506 
507 	mtx_destroy(&x86bios_lock);
508 }
509 
510 static int
511 x86bios_modevent(module_t mod __unused, int type, void *data __unused)
512 {
513 
514 	switch (type) {
515 	case MOD_LOAD:
516 		x86bios_init(NULL);
517 		break;
518 	case MOD_UNLOAD:
519 		x86bios_uninit(NULL);
520 		break;
521 	default:
522 		return (ENOTSUP);
523 	}
524 
525 	return (0);
526 }
527 
528 static moduledata_t x86bios_mod = {
529 	"x86bios",
530 	x86bios_modevent,
531 	NULL,
532 };
533 
534 DECLARE_MODULE(x86bios, x86bios_mod, SI_SUB_CPU, SI_ORDER_ANY);
535 MODULE_VERSION(x86bios, 1);
536