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