xref: /freebsd-src/sys/compat/x86bios/x86bios.c (revision d1bdc2821fcd416ab9b238580386eb605a6128d0)
119de5df5SJung-uk Kim /*-
23d0cd005SXin LI  * Copyright (c) 2009 Alex Keda <admin@lissyara.su>
3b92184ecSJung-uk Kim  * Copyright (c) 2009-2010 Jung-uk Kim <jkim@FreeBSD.org>
43d0cd005SXin LI  * All rights reserved.
53d0cd005SXin LI  *
63d0cd005SXin LI  * Redistribution and use in source and binary forms, with or without
73d0cd005SXin LI  * modification, are permitted provided that the following conditions
83d0cd005SXin LI  * are met:
93d0cd005SXin LI  * 1. Redistributions of source code must retain the above copyright
103d0cd005SXin LI  *    notice, this list of conditions and the following disclaimer.
113d0cd005SXin LI  * 2. Redistributions in binary form must reproduce the above copyright
123d0cd005SXin LI  *    notice, this list of conditions and the following disclaimer in the
133d0cd005SXin LI  *    documentation and/or other materials provided with the distribution.
143d0cd005SXin LI  *
153d0cd005SXin LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
163d0cd005SXin LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
173d0cd005SXin LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
183d0cd005SXin LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
193d0cd005SXin LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
203d0cd005SXin LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
213d0cd005SXin LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
223d0cd005SXin LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
233d0cd005SXin LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
243d0cd005SXin LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
253d0cd005SXin LI  * SUCH DAMAGE.
2619de5df5SJung-uk Kim  */
2719de5df5SJung-uk Kim 
2819de5df5SJung-uk Kim #include <sys/cdefs.h>
2919de5df5SJung-uk Kim #include "opt_x86bios.h"
3019de5df5SJung-uk Kim 
3119de5df5SJung-uk Kim #include <sys/param.h>
323219f535SJung-uk Kim #include <sys/bus.h>
3319de5df5SJung-uk Kim #include <sys/kernel.h>
3419de5df5SJung-uk Kim #include <sys/lock.h>
353219f535SJung-uk Kim #include <sys/malloc.h>
3619de5df5SJung-uk Kim #include <sys/module.h>
3719de5df5SJung-uk Kim #include <sys/mutex.h>
383afa8e56SJung-uk Kim #include <sys/sysctl.h>
3919de5df5SJung-uk Kim 
4019de5df5SJung-uk Kim #include <contrib/x86emu/x86emu.h>
4119de5df5SJung-uk Kim #include <contrib/x86emu/x86emu_regs.h>
4219de5df5SJung-uk Kim #include <compat/x86bios/x86bios.h>
4319de5df5SJung-uk Kim 
443219f535SJung-uk Kim #include <dev/pci/pcireg.h>
453219f535SJung-uk Kim #include <dev/pci/pcivar.h>
4619de5df5SJung-uk Kim 
473219f535SJung-uk Kim #include <vm/vm.h>
483219f535SJung-uk Kim #include <vm/pmap.h>
493219f535SJung-uk Kim 
50439f3d8bSJung-uk Kim #ifdef __amd64__
5148319187SJung-uk Kim #define	X86BIOS_NATIVE_ARCH
5248319187SJung-uk Kim #endif
53439f3d8bSJung-uk Kim #ifdef __i386__
54439f3d8bSJung-uk Kim #define	X86BIOS_NATIVE_VM86
55439f3d8bSJung-uk Kim #endif
56439f3d8bSJung-uk Kim 
57439f3d8bSJung-uk Kim #define	X86BIOS_MEM_SIZE	0x00100000	/* 1M */
58439f3d8bSJung-uk Kim 
59f2c73cefSJung-uk Kim #define	X86BIOS_TRACE(h, n, r)	do {					\
60f2c73cefSJung-uk Kim 	printf(__STRING(h)						\
61f2c73cefSJung-uk Kim 	    " (ax=0x%04x bx=0x%04x cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",\
62f2c73cefSJung-uk Kim 	    (n), (r)->R_AX, (r)->R_BX, (r)->R_CX, (r)->R_DX,		\
63f2c73cefSJung-uk Kim 	    (r)->R_ES, (r)->R_DI);					\
64f2c73cefSJung-uk Kim } while (0)
65f2c73cefSJung-uk Kim 
66439f3d8bSJung-uk Kim static struct mtx x86bios_lock;
67439f3d8bSJung-uk Kim 
687029da5cSPawel Biernacki static SYSCTL_NODE(_debug, OID_AUTO, x86bios, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
696472ac3dSEd Schouten     "x86bios debugging");
70439f3d8bSJung-uk Kim static int x86bios_trace_call;
71af3b2549SHans Petter Selasky SYSCTL_INT(_debug_x86bios, OID_AUTO, call, CTLFLAG_RWTUN, &x86bios_trace_call, 0,
72439f3d8bSJung-uk Kim     "Trace far function calls");
73439f3d8bSJung-uk Kim static int x86bios_trace_int;
74af3b2549SHans Petter Selasky SYSCTL_INT(_debug_x86bios, OID_AUTO, int, CTLFLAG_RWTUN, &x86bios_trace_int, 0,
75439f3d8bSJung-uk Kim     "Trace software interrupt handlers");
76439f3d8bSJung-uk Kim 
77439f3d8bSJung-uk Kim #ifdef X86BIOS_NATIVE_VM86
78439f3d8bSJung-uk Kim 
79439f3d8bSJung-uk Kim #include <machine/vm86.h>
80439f3d8bSJung-uk Kim #include <machine/vmparam.h>
81439f3d8bSJung-uk Kim #include <machine/pc/bios.h>
82439f3d8bSJung-uk Kim 
83439f3d8bSJung-uk Kim struct vm86context x86bios_vmc;
84439f3d8bSJung-uk Kim 
85439f3d8bSJung-uk Kim static void
86439f3d8bSJung-uk Kim x86bios_emu2vmf(struct x86emu_regs *regs, struct vm86frame *vmf)
87439f3d8bSJung-uk Kim {
88439f3d8bSJung-uk Kim 
89439f3d8bSJung-uk Kim 	vmf->vmf_ds = regs->R_DS;
90439f3d8bSJung-uk Kim 	vmf->vmf_es = regs->R_ES;
91439f3d8bSJung-uk Kim 	vmf->vmf_ax = regs->R_AX;
92439f3d8bSJung-uk Kim 	vmf->vmf_bx = regs->R_BX;
93439f3d8bSJung-uk Kim 	vmf->vmf_cx = regs->R_CX;
94439f3d8bSJung-uk Kim 	vmf->vmf_dx = regs->R_DX;
95439f3d8bSJung-uk Kim 	vmf->vmf_bp = regs->R_BP;
96439f3d8bSJung-uk Kim 	vmf->vmf_si = regs->R_SI;
97439f3d8bSJung-uk Kim 	vmf->vmf_di = regs->R_DI;
98439f3d8bSJung-uk Kim }
99439f3d8bSJung-uk Kim 
100439f3d8bSJung-uk Kim static void
101439f3d8bSJung-uk Kim x86bios_vmf2emu(struct vm86frame *vmf, struct x86emu_regs *regs)
102439f3d8bSJung-uk Kim {
103439f3d8bSJung-uk Kim 
104439f3d8bSJung-uk Kim 	regs->R_DS = vmf->vmf_ds;
105439f3d8bSJung-uk Kim 	regs->R_ES = vmf->vmf_es;
1060a3493e5SJung-uk Kim 	regs->R_FLG = vmf->vmf_flags;
107439f3d8bSJung-uk Kim 	regs->R_AX = vmf->vmf_ax;
108439f3d8bSJung-uk Kim 	regs->R_BX = vmf->vmf_bx;
109439f3d8bSJung-uk Kim 	regs->R_CX = vmf->vmf_cx;
110439f3d8bSJung-uk Kim 	regs->R_DX = vmf->vmf_dx;
111439f3d8bSJung-uk Kim 	regs->R_BP = vmf->vmf_bp;
112439f3d8bSJung-uk Kim 	regs->R_SI = vmf->vmf_si;
113439f3d8bSJung-uk Kim 	regs->R_DI = vmf->vmf_di;
114439f3d8bSJung-uk Kim }
115439f3d8bSJung-uk Kim 
116439f3d8bSJung-uk Kim void *
117439f3d8bSJung-uk Kim x86bios_alloc(uint32_t *offset, size_t size, int flags)
118439f3d8bSJung-uk Kim {
119b41f3f4cSJung-uk Kim 	void *vaddr;
1205cb9d60eSPedro F. Giffuni 	u_int i;
121439f3d8bSJung-uk Kim 
122b41f3f4cSJung-uk Kim 	if (offset == NULL || size == 0)
123b41f3f4cSJung-uk Kim 		return (NULL);
124b41f3f4cSJung-uk Kim 	vaddr = contigmalloc(size, M_DEVBUF, flags, 0, X86BIOS_MEM_SIZE,
125b41f3f4cSJung-uk Kim 	    PAGE_SIZE, 0);
126b41f3f4cSJung-uk Kim 	if (vaddr != NULL) {
127b41f3f4cSJung-uk Kim 		*offset = vtophys(vaddr);
128439f3d8bSJung-uk Kim 		mtx_lock(&x86bios_lock);
129fc82156fSJung-uk Kim 		for (i = 0; i < atop(round_page(size)); i++)
130f1077673SJung-uk Kim 			vm86_addpage(&x86bios_vmc, atop(*offset) + i,
131b41f3f4cSJung-uk Kim 			    (vm_offset_t)vaddr + ptoa(i));
132439f3d8bSJung-uk Kim 		mtx_unlock(&x86bios_lock);
133439f3d8bSJung-uk Kim 	}
134439f3d8bSJung-uk Kim 
135b41f3f4cSJung-uk Kim 	return (vaddr);
136439f3d8bSJung-uk Kim }
137439f3d8bSJung-uk Kim 
138439f3d8bSJung-uk Kim void
139439f3d8bSJung-uk Kim x86bios_free(void *addr, size_t size)
140439f3d8bSJung-uk Kim {
141b41f3f4cSJung-uk Kim 	vm_paddr_t paddr;
142b41f3f4cSJung-uk Kim 	int i, nfree;
143439f3d8bSJung-uk Kim 
144b41f3f4cSJung-uk Kim 	if (addr == NULL || size == 0)
145b41f3f4cSJung-uk Kim 		return;
146b41f3f4cSJung-uk Kim 	paddr = vtophys(addr);
147b41f3f4cSJung-uk Kim 	if (paddr >= X86BIOS_MEM_SIZE || (paddr & PAGE_MASK) != 0)
148b41f3f4cSJung-uk Kim 		return;
149439f3d8bSJung-uk Kim 	mtx_lock(&x86bios_lock);
150b41f3f4cSJung-uk Kim 	for (i = 0; i < x86bios_vmc.npages; i++)
151b41f3f4cSJung-uk Kim 		if (x86bios_vmc.pmap[i].kva == (vm_offset_t)addr)
152b41f3f4cSJung-uk Kim 			break;
153b41f3f4cSJung-uk Kim 	if (i >= x86bios_vmc.npages) {
154f1077673SJung-uk Kim 		mtx_unlock(&x86bios_lock);
155f1077673SJung-uk Kim 		return;
156f1077673SJung-uk Kim 	}
157b3165075SJung-uk Kim 	nfree = atop(round_page(size));
158b41f3f4cSJung-uk Kim 	bzero(x86bios_vmc.pmap + i, sizeof(*x86bios_vmc.pmap) * nfree);
159b41f3f4cSJung-uk Kim 	if (i + nfree == x86bios_vmc.npages) {
160b41f3f4cSJung-uk Kim 		x86bios_vmc.npages -= nfree;
161b41f3f4cSJung-uk Kim 		while (--i >= 0 && x86bios_vmc.pmap[i].kva == 0)
162439f3d8bSJung-uk Kim 			x86bios_vmc.npages--;
163439f3d8bSJung-uk Kim 	}
164439f3d8bSJung-uk Kim 	mtx_unlock(&x86bios_lock);
165*d1bdc282SBjoern A. Zeeb 	free(addr, M_DEVBUF);
166439f3d8bSJung-uk Kim }
167439f3d8bSJung-uk Kim 
168439f3d8bSJung-uk Kim void
169439f3d8bSJung-uk Kim x86bios_init_regs(struct x86regs *regs)
170439f3d8bSJung-uk Kim {
171439f3d8bSJung-uk Kim 
172439f3d8bSJung-uk Kim 	bzero(regs, sizeof(*regs));
173439f3d8bSJung-uk Kim }
174439f3d8bSJung-uk Kim 
175439f3d8bSJung-uk Kim void
176439f3d8bSJung-uk Kim x86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
177439f3d8bSJung-uk Kim {
178439f3d8bSJung-uk Kim 	struct vm86frame vmf;
179439f3d8bSJung-uk Kim 
180439f3d8bSJung-uk Kim 	if (x86bios_trace_call)
181f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Calling 0x%06x, (seg << 4) + off, regs);
182439f3d8bSJung-uk Kim 
183439f3d8bSJung-uk Kim 	bzero(&vmf, sizeof(vmf));
184439f3d8bSJung-uk Kim 	x86bios_emu2vmf((struct x86emu_regs *)regs, &vmf);
185439f3d8bSJung-uk Kim 	vmf.vmf_cs = seg;
186439f3d8bSJung-uk Kim 	vmf.vmf_ip = off;
187439f3d8bSJung-uk Kim 	mtx_lock(&x86bios_lock);
188439f3d8bSJung-uk Kim 	vm86_datacall(-1, &vmf, &x86bios_vmc);
189439f3d8bSJung-uk Kim 	mtx_unlock(&x86bios_lock);
190439f3d8bSJung-uk Kim 	x86bios_vmf2emu(&vmf, (struct x86emu_regs *)regs);
191439f3d8bSJung-uk Kim 
192439f3d8bSJung-uk Kim 	if (x86bios_trace_call)
193f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Exiting 0x%06x, (seg << 4) + off, regs);
194439f3d8bSJung-uk Kim }
195439f3d8bSJung-uk Kim 
196439f3d8bSJung-uk Kim uint32_t
197439f3d8bSJung-uk Kim x86bios_get_intr(int intno)
198439f3d8bSJung-uk Kim {
199439f3d8bSJung-uk Kim 
200d7a5fb63SJung-uk Kim 	return (readl(BIOS_PADDRTOVADDR(intno * 4)));
201439f3d8bSJung-uk Kim }
202439f3d8bSJung-uk Kim 
203439f3d8bSJung-uk Kim void
20497e6525dSJung-uk Kim x86bios_set_intr(int intno, uint32_t saddr)
20597e6525dSJung-uk Kim {
20697e6525dSJung-uk Kim 
20797e6525dSJung-uk Kim 	writel(BIOS_PADDRTOVADDR(intno * 4), saddr);
20897e6525dSJung-uk Kim }
20997e6525dSJung-uk Kim 
21097e6525dSJung-uk Kim void
211439f3d8bSJung-uk Kim x86bios_intr(struct x86regs *regs, int intno)
212439f3d8bSJung-uk Kim {
213439f3d8bSJung-uk Kim 	struct vm86frame vmf;
214439f3d8bSJung-uk Kim 
215439f3d8bSJung-uk Kim 	if (x86bios_trace_int)
216f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Calling INT 0x%02x, intno, regs);
217439f3d8bSJung-uk Kim 
218439f3d8bSJung-uk Kim 	bzero(&vmf, sizeof(vmf));
219439f3d8bSJung-uk Kim 	x86bios_emu2vmf((struct x86emu_regs *)regs, &vmf);
220439f3d8bSJung-uk Kim 	mtx_lock(&x86bios_lock);
221439f3d8bSJung-uk Kim 	vm86_datacall(intno, &vmf, &x86bios_vmc);
222439f3d8bSJung-uk Kim 	mtx_unlock(&x86bios_lock);
223439f3d8bSJung-uk Kim 	x86bios_vmf2emu(&vmf, (struct x86emu_regs *)regs);
224439f3d8bSJung-uk Kim 
225439f3d8bSJung-uk Kim 	if (x86bios_trace_int)
226f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Exiting INT 0x%02x, intno, regs);
227439f3d8bSJung-uk Kim }
228439f3d8bSJung-uk Kim 
229439f3d8bSJung-uk Kim void *
230439f3d8bSJung-uk Kim x86bios_offset(uint32_t offset)
231439f3d8bSJung-uk Kim {
232439f3d8bSJung-uk Kim 	vm_offset_t addr;
233439f3d8bSJung-uk Kim 
234439f3d8bSJung-uk Kim 	addr = vm86_getaddr(&x86bios_vmc, X86BIOS_PHYSTOSEG(offset),
235439f3d8bSJung-uk Kim 	    X86BIOS_PHYSTOOFF(offset));
236439f3d8bSJung-uk Kim 	if (addr == 0)
237439f3d8bSJung-uk Kim 		addr = BIOS_PADDRTOVADDR(offset);
238439f3d8bSJung-uk Kim 
239439f3d8bSJung-uk Kim 	return ((void *)addr);
240439f3d8bSJung-uk Kim }
241439f3d8bSJung-uk Kim 
242439f3d8bSJung-uk Kim static int
243439f3d8bSJung-uk Kim x86bios_init(void)
244439f3d8bSJung-uk Kim {
245439f3d8bSJung-uk Kim 
246439f3d8bSJung-uk Kim 	mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_DEF);
247439f3d8bSJung-uk Kim 	bzero(&x86bios_vmc, sizeof(x86bios_vmc));
248439f3d8bSJung-uk Kim 
249439f3d8bSJung-uk Kim 	return (0);
250439f3d8bSJung-uk Kim }
251439f3d8bSJung-uk Kim 
252439f3d8bSJung-uk Kim static int
253439f3d8bSJung-uk Kim x86bios_uninit(void)
254439f3d8bSJung-uk Kim {
255439f3d8bSJung-uk Kim 
256439f3d8bSJung-uk Kim 	mtx_destroy(&x86bios_lock);
257439f3d8bSJung-uk Kim 
258439f3d8bSJung-uk Kim 	return (0);
259439f3d8bSJung-uk Kim }
260439f3d8bSJung-uk Kim 
261439f3d8bSJung-uk Kim #else
262439f3d8bSJung-uk Kim 
263439f3d8bSJung-uk Kim #include <machine/iodev.h>
26448319187SJung-uk Kim 
2653219f535SJung-uk Kim #define	X86BIOS_PAGE_SIZE	0x00001000	/* 4K */
2663219f535SJung-uk Kim 
2673219f535SJung-uk Kim #define	X86BIOS_IVT_SIZE	0x00000500	/* 1K + 256 (BDA) */
2683219f535SJung-uk Kim 
2693219f535SJung-uk Kim #define	X86BIOS_IVT_BASE	0x00000000
2703219f535SJung-uk Kim #define	X86BIOS_RAM_BASE	0x00001000
271ef8201d3SJung-uk Kim #define	X86BIOS_ROM_BASE	0x000a0000
2723219f535SJung-uk Kim 
273f2c73cefSJung-uk Kim #define	X86BIOS_ROM_SIZE	(X86BIOS_MEM_SIZE - x86bios_rom_phys)
274439f3d8bSJung-uk Kim #define	X86BIOS_SEG_SIZE	X86BIOS_PAGE_SIZE
2753219f535SJung-uk Kim 
2763219f535SJung-uk Kim #define	X86BIOS_PAGES		(X86BIOS_MEM_SIZE / X86BIOS_PAGE_SIZE)
2773219f535SJung-uk Kim 
2783219f535SJung-uk Kim #define	X86BIOS_R_SS		_pad2
279439f3d8bSJung-uk Kim #define	X86BIOS_R_SP		_pad3.I16_reg.x_reg
28019de5df5SJung-uk Kim 
28119de5df5SJung-uk Kim static struct x86emu x86bios_emu;
28219de5df5SJung-uk Kim 
2833219f535SJung-uk Kim static void *x86bios_ivt;
2843219f535SJung-uk Kim static void *x86bios_rom;
2853219f535SJung-uk Kim static void *x86bios_seg;
2863219f535SJung-uk Kim 
2873219f535SJung-uk Kim static vm_offset_t *x86bios_map;
2883219f535SJung-uk Kim 
289ef8201d3SJung-uk Kim static vm_paddr_t x86bios_rom_phys;
2903219f535SJung-uk Kim static vm_paddr_t x86bios_seg_phys;
2913219f535SJung-uk Kim 
292b92184ecSJung-uk Kim static int x86bios_fault;
293b92184ecSJung-uk Kim static uint32_t x86bios_fault_addr;
294ef8201d3SJung-uk Kim static uint16_t x86bios_fault_cs;
295ef8201d3SJung-uk Kim static uint16_t x86bios_fault_ip;
296b92184ecSJung-uk Kim 
297b92184ecSJung-uk Kim static void
298b92184ecSJung-uk Kim x86bios_set_fault(struct x86emu *emu, uint32_t addr)
299b92184ecSJung-uk Kim {
300b92184ecSJung-uk Kim 
301b92184ecSJung-uk Kim 	x86bios_fault = 1;
302b92184ecSJung-uk Kim 	x86bios_fault_addr = addr;
303ef8201d3SJung-uk Kim 	x86bios_fault_cs = emu->x86.R_CS;
304ef8201d3SJung-uk Kim 	x86bios_fault_ip = emu->x86.R_IP;
305ef8201d3SJung-uk Kim 	x86emu_halt_sys(emu);
306b92184ecSJung-uk Kim }
307b92184ecSJung-uk Kim 
3083219f535SJung-uk Kim static void *
3093219f535SJung-uk Kim x86bios_get_pages(uint32_t offset, size_t size)
3103219f535SJung-uk Kim {
311439f3d8bSJung-uk Kim 	vm_offset_t addr;
3123219f535SJung-uk Kim 
313c9cefec1SJung-uk Kim 	if (offset + size > X86BIOS_MEM_SIZE + X86BIOS_IVT_SIZE)
3143219f535SJung-uk Kim 		return (NULL);
3153219f535SJung-uk Kim 
316c9cefec1SJung-uk Kim 	if (offset >= X86BIOS_MEM_SIZE)
317c9cefec1SJung-uk Kim 		offset -= X86BIOS_MEM_SIZE;
318439f3d8bSJung-uk Kim 	addr = x86bios_map[offset / X86BIOS_PAGE_SIZE];
319439f3d8bSJung-uk Kim 	if (addr != 0)
320439f3d8bSJung-uk Kim 		addr += offset % X86BIOS_PAGE_SIZE;
3213219f535SJung-uk Kim 
322439f3d8bSJung-uk Kim 	return ((void *)addr);
3233219f535SJung-uk Kim }
3243219f535SJung-uk Kim 
3253219f535SJung-uk Kim static void
3263219f535SJung-uk Kim x86bios_set_pages(vm_offset_t va, vm_paddr_t pa, size_t size)
3273219f535SJung-uk Kim {
3283219f535SJung-uk Kim 	int i, j;
3293219f535SJung-uk Kim 
3303219f535SJung-uk Kim 	for (i = pa / X86BIOS_PAGE_SIZE, j = 0;
3313219f535SJung-uk Kim 	    j < howmany(size, X86BIOS_PAGE_SIZE); i++, j++)
3323219f535SJung-uk Kim 		x86bios_map[i] = va + j * X86BIOS_PAGE_SIZE;
3333219f535SJung-uk Kim }
3343219f535SJung-uk Kim 
3353219f535SJung-uk Kim static uint8_t
3363219f535SJung-uk Kim x86bios_emu_rdb(struct x86emu *emu, uint32_t addr)
3373219f535SJung-uk Kim {
3383219f535SJung-uk Kim 	uint8_t *va;
3393219f535SJung-uk Kim 
3403219f535SJung-uk Kim 	va = x86bios_get_pages(addr, sizeof(*va));
341ef8201d3SJung-uk Kim 	if (va == NULL)
342b92184ecSJung-uk Kim 		x86bios_set_fault(emu, addr);
3433219f535SJung-uk Kim 
3443219f535SJung-uk Kim 	return (*va);
3453219f535SJung-uk Kim }
3463219f535SJung-uk Kim 
3473219f535SJung-uk Kim static uint16_t
3483219f535SJung-uk Kim x86bios_emu_rdw(struct x86emu *emu, uint32_t addr)
3493219f535SJung-uk Kim {
3503219f535SJung-uk Kim 	uint16_t *va;
3513219f535SJung-uk Kim 
3523219f535SJung-uk Kim 	va = x86bios_get_pages(addr, sizeof(*va));
353ef8201d3SJung-uk Kim 	if (va == NULL)
354b92184ecSJung-uk Kim 		x86bios_set_fault(emu, addr);
3553219f535SJung-uk Kim 
356ef8201d3SJung-uk Kim #ifndef __NO_STRICT_ALIGNMENT
357ef8201d3SJung-uk Kim 	if ((addr & 1) != 0)
358ef8201d3SJung-uk Kim 		return (le16dec(va));
359ef8201d3SJung-uk Kim 	else
360ef8201d3SJung-uk Kim #endif
3613219f535SJung-uk Kim 	return (le16toh(*va));
3623219f535SJung-uk Kim }
3633219f535SJung-uk Kim 
3643219f535SJung-uk Kim static uint32_t
3653219f535SJung-uk Kim x86bios_emu_rdl(struct x86emu *emu, uint32_t addr)
3663219f535SJung-uk Kim {
3673219f535SJung-uk Kim 	uint32_t *va;
3683219f535SJung-uk Kim 
3693219f535SJung-uk Kim 	va = x86bios_get_pages(addr, sizeof(*va));
370ef8201d3SJung-uk Kim 	if (va == NULL)
371b92184ecSJung-uk Kim 		x86bios_set_fault(emu, addr);
3723219f535SJung-uk Kim 
373ef8201d3SJung-uk Kim #ifndef __NO_STRICT_ALIGNMENT
374ef8201d3SJung-uk Kim 	if ((addr & 3) != 0)
375ef8201d3SJung-uk Kim 		return (le32dec(va));
376ef8201d3SJung-uk Kim 	else
377ef8201d3SJung-uk Kim #endif
3783219f535SJung-uk Kim 	return (le32toh(*va));
3793219f535SJung-uk Kim }
3803219f535SJung-uk Kim 
3813219f535SJung-uk Kim static void
3823219f535SJung-uk Kim x86bios_emu_wrb(struct x86emu *emu, uint32_t addr, uint8_t val)
3833219f535SJung-uk Kim {
3843219f535SJung-uk Kim 	uint8_t *va;
3853219f535SJung-uk Kim 
3863219f535SJung-uk Kim 	va = x86bios_get_pages(addr, sizeof(*va));
387ef8201d3SJung-uk Kim 	if (va == NULL)
388b92184ecSJung-uk Kim 		x86bios_set_fault(emu, addr);
3893219f535SJung-uk Kim 
3903219f535SJung-uk Kim 	*va = val;
3913219f535SJung-uk Kim }
3923219f535SJung-uk Kim 
3933219f535SJung-uk Kim static void
3943219f535SJung-uk Kim x86bios_emu_wrw(struct x86emu *emu, uint32_t addr, uint16_t val)
3953219f535SJung-uk Kim {
3963219f535SJung-uk Kim 	uint16_t *va;
3973219f535SJung-uk Kim 
3983219f535SJung-uk Kim 	va = x86bios_get_pages(addr, sizeof(*va));
399ef8201d3SJung-uk Kim 	if (va == NULL)
400b92184ecSJung-uk Kim 		x86bios_set_fault(emu, addr);
4013219f535SJung-uk Kim 
402ef8201d3SJung-uk Kim #ifndef __NO_STRICT_ALIGNMENT
403ef8201d3SJung-uk Kim 	if ((addr & 1) != 0)
404ef8201d3SJung-uk Kim 		le16enc(va, val);
405ef8201d3SJung-uk Kim 	else
406ef8201d3SJung-uk Kim #endif
4073219f535SJung-uk Kim 	*va = htole16(val);
4083219f535SJung-uk Kim }
4093219f535SJung-uk Kim 
4103219f535SJung-uk Kim static void
4113219f535SJung-uk Kim x86bios_emu_wrl(struct x86emu *emu, uint32_t addr, uint32_t val)
4123219f535SJung-uk Kim {
4133219f535SJung-uk Kim 	uint32_t *va;
4143219f535SJung-uk Kim 
4153219f535SJung-uk Kim 	va = x86bios_get_pages(addr, sizeof(*va));
416ef8201d3SJung-uk Kim 	if (va == NULL)
417b92184ecSJung-uk Kim 		x86bios_set_fault(emu, addr);
4183219f535SJung-uk Kim 
419ef8201d3SJung-uk Kim #ifndef __NO_STRICT_ALIGNMENT
420ef8201d3SJung-uk Kim 	if ((addr & 3) != 0)
421ef8201d3SJung-uk Kim 		le32enc(va, val);
422ef8201d3SJung-uk Kim 	else
423ef8201d3SJung-uk Kim #endif
4243219f535SJung-uk Kim 	*va = htole32(val);
4253219f535SJung-uk Kim }
4263219f535SJung-uk Kim 
42719de5df5SJung-uk Kim static uint8_t
42819de5df5SJung-uk Kim x86bios_emu_inb(struct x86emu *emu, uint16_t port)
42919de5df5SJung-uk Kim {
43019de5df5SJung-uk Kim 
431a6d613a5SJung-uk Kim #ifndef X86BIOS_NATIVE_ARCH
43219de5df5SJung-uk Kim 	if (port == 0xb2) /* APM scratch register */
43319de5df5SJung-uk Kim 		return (0);
43419de5df5SJung-uk Kim 	if (port >= 0x80 && port < 0x88) /* POST status register */
43519de5df5SJung-uk Kim 		return (0);
436a6d613a5SJung-uk Kim #endif
4373219f535SJung-uk Kim 
43848319187SJung-uk Kim 	return (iodev_read_1(port));
43919de5df5SJung-uk Kim }
44019de5df5SJung-uk Kim 
44119de5df5SJung-uk Kim static uint16_t
44219de5df5SJung-uk Kim x86bios_emu_inw(struct x86emu *emu, uint16_t port)
44319de5df5SJung-uk Kim {
44448319187SJung-uk Kim 	uint16_t val;
44519de5df5SJung-uk Kim 
446a6d613a5SJung-uk Kim #ifndef X86BIOS_NATIVE_ARCH
44719de5df5SJung-uk Kim 	if (port >= 0x80 && port < 0x88) /* POST status register */
44819de5df5SJung-uk Kim 		return (0);
4493219f535SJung-uk Kim 
45048319187SJung-uk Kim 	if ((port & 1) != 0) {
45148319187SJung-uk Kim 		val = iodev_read_1(port);
45248319187SJung-uk Kim 		val |= iodev_read_1(port + 1) << 8;
45348319187SJung-uk Kim 	} else
45448319187SJung-uk Kim #endif
45548319187SJung-uk Kim 	val = iodev_read_2(port);
45648319187SJung-uk Kim 
45748319187SJung-uk Kim 	return (val);
45819de5df5SJung-uk Kim }
45919de5df5SJung-uk Kim 
46019de5df5SJung-uk Kim static uint32_t
46119de5df5SJung-uk Kim x86bios_emu_inl(struct x86emu *emu, uint16_t port)
46219de5df5SJung-uk Kim {
46348319187SJung-uk Kim 	uint32_t val;
46419de5df5SJung-uk Kim 
465a6d613a5SJung-uk Kim #ifndef X86BIOS_NATIVE_ARCH
46619de5df5SJung-uk Kim 	if (port >= 0x80 && port < 0x88) /* POST status register */
46719de5df5SJung-uk Kim 		return (0);
4683219f535SJung-uk Kim 
46948319187SJung-uk Kim 	if ((port & 1) != 0) {
47048319187SJung-uk Kim 		val = iodev_read_1(port);
47148319187SJung-uk Kim 		val |= iodev_read_2(port + 1) << 8;
47248319187SJung-uk Kim 		val |= iodev_read_1(port + 3) << 24;
47348319187SJung-uk Kim 	} else if ((port & 2) != 0) {
47448319187SJung-uk Kim 		val = iodev_read_2(port);
47548319187SJung-uk Kim 		val |= iodev_read_2(port + 2) << 16;
47648319187SJung-uk Kim 	} else
47748319187SJung-uk Kim #endif
47848319187SJung-uk Kim 	val = iodev_read_4(port);
47948319187SJung-uk Kim 
48048319187SJung-uk Kim 	return (val);
48119de5df5SJung-uk Kim }
48219de5df5SJung-uk Kim 
48319de5df5SJung-uk Kim static void
48419de5df5SJung-uk Kim x86bios_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val)
48519de5df5SJung-uk Kim {
48619de5df5SJung-uk Kim 
487a6d613a5SJung-uk Kim #ifndef X86BIOS_NATIVE_ARCH
48819de5df5SJung-uk Kim 	if (port == 0xb2) /* APM scratch register */
48919de5df5SJung-uk Kim 		return;
49019de5df5SJung-uk Kim 	if (port >= 0x80 && port < 0x88) /* POST status register */
49119de5df5SJung-uk Kim 		return;
492a6d613a5SJung-uk Kim #endif
4933219f535SJung-uk Kim 
49448319187SJung-uk Kim 	iodev_write_1(port, val);
49519de5df5SJung-uk Kim }
49619de5df5SJung-uk Kim 
49719de5df5SJung-uk Kim static void
49819de5df5SJung-uk Kim x86bios_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val)
49919de5df5SJung-uk Kim {
50019de5df5SJung-uk Kim 
501a6d613a5SJung-uk Kim #ifndef X86BIOS_NATIVE_ARCH
50219de5df5SJung-uk Kim 	if (port >= 0x80 && port < 0x88) /* POST status register */
50319de5df5SJung-uk Kim 		return;
5043219f535SJung-uk Kim 
50548319187SJung-uk Kim 	if ((port & 1) != 0) {
50648319187SJung-uk Kim 		iodev_write_1(port, val);
50748319187SJung-uk Kim 		iodev_write_1(port + 1, val >> 8);
50848319187SJung-uk Kim 	} else
50948319187SJung-uk Kim #endif
51048319187SJung-uk Kim 	iodev_write_2(port, val);
51119de5df5SJung-uk Kim }
51219de5df5SJung-uk Kim 
51319de5df5SJung-uk Kim static void
51419de5df5SJung-uk Kim x86bios_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val)
51519de5df5SJung-uk Kim {
51619de5df5SJung-uk Kim 
517a6d613a5SJung-uk Kim #ifndef X86BIOS_NATIVE_ARCH
51819de5df5SJung-uk Kim 	if (port >= 0x80 && port < 0x88) /* POST status register */
51919de5df5SJung-uk Kim 		return;
5203219f535SJung-uk Kim 
52148319187SJung-uk Kim 	if ((port & 1) != 0) {
52248319187SJung-uk Kim 		iodev_write_1(port, val);
52348319187SJung-uk Kim 		iodev_write_2(port + 1, val >> 8);
52448319187SJung-uk Kim 		iodev_write_1(port + 3, val >> 24);
52548319187SJung-uk Kim 	} else if ((port & 2) != 0) {
52648319187SJung-uk Kim 		iodev_write_2(port, val);
52748319187SJung-uk Kim 		iodev_write_2(port + 2, val >> 16);
52848319187SJung-uk Kim 	} else
52948319187SJung-uk Kim #endif
53048319187SJung-uk Kim 	iodev_write_4(port, val);
53119de5df5SJung-uk Kim }
53219de5df5SJung-uk Kim 
5333219f535SJung-uk Kim void *
534362487c0SJung-uk Kim x86bios_alloc(uint32_t *offset, size_t size, int flags)
5353219f535SJung-uk Kim {
5363219f535SJung-uk Kim 	void *vaddr;
5373219f535SJung-uk Kim 
5383219f535SJung-uk Kim 	if (offset == NULL || size == 0)
5393219f535SJung-uk Kim 		return (NULL);
540362487c0SJung-uk Kim 	vaddr = contigmalloc(size, M_DEVBUF, flags, X86BIOS_RAM_BASE,
541ef8201d3SJung-uk Kim 	    x86bios_rom_phys, X86BIOS_PAGE_SIZE, 0);
5423219f535SJung-uk Kim 	if (vaddr != NULL) {
5433219f535SJung-uk Kim 		*offset = vtophys(vaddr);
544449918b1SJung-uk Kim 		mtx_lock(&x86bios_lock);
5453219f535SJung-uk Kim 		x86bios_set_pages((vm_offset_t)vaddr, *offset, size);
546449918b1SJung-uk Kim 		mtx_unlock(&x86bios_lock);
5473219f535SJung-uk Kim 	}
5483219f535SJung-uk Kim 
5493219f535SJung-uk Kim 	return (vaddr);
5503219f535SJung-uk Kim }
5513219f535SJung-uk Kim 
5523219f535SJung-uk Kim void
5533219f535SJung-uk Kim x86bios_free(void *addr, size_t size)
5543219f535SJung-uk Kim {
5553219f535SJung-uk Kim 	vm_paddr_t paddr;
5563219f535SJung-uk Kim 
5573219f535SJung-uk Kim 	if (addr == NULL || size == 0)
5583219f535SJung-uk Kim 		return;
5593219f535SJung-uk Kim 	paddr = vtophys(addr);
560ef8201d3SJung-uk Kim 	if (paddr < X86BIOS_RAM_BASE || paddr >= x86bios_rom_phys ||
5613219f535SJung-uk Kim 	    paddr % X86BIOS_PAGE_SIZE != 0)
5623219f535SJung-uk Kim 		return;
563449918b1SJung-uk Kim 	mtx_lock(&x86bios_lock);
5643219f535SJung-uk Kim 	bzero(x86bios_map + paddr / X86BIOS_PAGE_SIZE,
5653219f535SJung-uk Kim 	    sizeof(*x86bios_map) * howmany(size, X86BIOS_PAGE_SIZE));
566449918b1SJung-uk Kim 	mtx_unlock(&x86bios_lock);
567*d1bdc282SBjoern A. Zeeb 	free(addr, M_DEVBUF);
5683219f535SJung-uk Kim }
5693219f535SJung-uk Kim 
5703219f535SJung-uk Kim void
5713219f535SJung-uk Kim x86bios_init_regs(struct x86regs *regs)
5723219f535SJung-uk Kim {
5733219f535SJung-uk Kim 
5743219f535SJung-uk Kim 	bzero(regs, sizeof(*regs));
575439f3d8bSJung-uk Kim 	regs->X86BIOS_R_SS = X86BIOS_PHYSTOSEG(x86bios_seg_phys);
576439f3d8bSJung-uk Kim 	regs->X86BIOS_R_SP = X86BIOS_PAGE_SIZE - 2;
5773219f535SJung-uk Kim }
5783219f535SJung-uk Kim 
5793219f535SJung-uk Kim void
5803219f535SJung-uk Kim x86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
5813219f535SJung-uk Kim {
5823219f535SJung-uk Kim 
5833afa8e56SJung-uk Kim 	if (x86bios_trace_call)
584f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Calling 0x%06x, (seg << 4) + off, regs);
5853219f535SJung-uk Kim 
586449918b1SJung-uk Kim 	mtx_lock(&x86bios_lock);
587c2a9e596SJung-uk Kim 	memcpy((struct x86regs *)&x86bios_emu.x86, regs, sizeof(*regs));
588b92184ecSJung-uk Kim 	x86bios_fault = 0;
589077c4b48SJung-uk Kim 	spinlock_enter();
5903219f535SJung-uk Kim 	x86emu_exec_call(&x86bios_emu, seg, off);
591449918b1SJung-uk Kim 	spinlock_exit();
592077c4b48SJung-uk Kim 	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
593449918b1SJung-uk Kim 	mtx_unlock(&x86bios_lock);
5943219f535SJung-uk Kim 
595b92184ecSJung-uk Kim 	if (x86bios_trace_call) {
596f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Exiting 0x%06x, (seg << 4) + off, regs);
597b92184ecSJung-uk Kim 		if (x86bios_fault)
598f2c73cefSJung-uk Kim 			printf("Page fault at 0x%06x from 0x%04x:0x%04x.\n",
599ef8201d3SJung-uk Kim 			    x86bios_fault_addr, x86bios_fault_cs,
600ef8201d3SJung-uk Kim 			    x86bios_fault_ip);
601b92184ecSJung-uk Kim 	}
6023219f535SJung-uk Kim }
6033219f535SJung-uk Kim 
6043219f535SJung-uk Kim uint32_t
6053219f535SJung-uk Kim x86bios_get_intr(int intno)
6063219f535SJung-uk Kim {
6073219f535SJung-uk Kim 
60897e6525dSJung-uk Kim 	return (le32toh(*((uint32_t *)x86bios_ivt + intno)));
60997e6525dSJung-uk Kim }
6103219f535SJung-uk Kim 
61197e6525dSJung-uk Kim void
61297e6525dSJung-uk Kim x86bios_set_intr(int intno, uint32_t saddr)
61397e6525dSJung-uk Kim {
61497e6525dSJung-uk Kim 
61597e6525dSJung-uk Kim 	*((uint32_t *)x86bios_ivt + intno) = htole32(saddr);
6163219f535SJung-uk Kim }
6173219f535SJung-uk Kim 
61819de5df5SJung-uk Kim void
619a8672748SJung-uk Kim x86bios_intr(struct x86regs *regs, int intno)
62019de5df5SJung-uk Kim {
62119de5df5SJung-uk Kim 
62219de5df5SJung-uk Kim 	if (intno < 0 || intno > 255)
62319de5df5SJung-uk Kim 		return;
62419de5df5SJung-uk Kim 
6253afa8e56SJung-uk Kim 	if (x86bios_trace_int)
626f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Calling INT 0x%02x, intno, regs);
6275ec510d8SJung-uk Kim 
628449918b1SJung-uk Kim 	mtx_lock(&x86bios_lock);
629c2a9e596SJung-uk Kim 	memcpy((struct x86regs *)&x86bios_emu.x86, regs, sizeof(*regs));
630b92184ecSJung-uk Kim 	x86bios_fault = 0;
631077c4b48SJung-uk Kim 	spinlock_enter();
63219de5df5SJung-uk Kim 	x86emu_exec_intr(&x86bios_emu, intno);
633449918b1SJung-uk Kim 	spinlock_exit();
634077c4b48SJung-uk Kim 	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
635449918b1SJung-uk Kim 	mtx_unlock(&x86bios_lock);
6365ec510d8SJung-uk Kim 
637b92184ecSJung-uk Kim 	if (x86bios_trace_int) {
638f2c73cefSJung-uk Kim 		X86BIOS_TRACE(Exiting INT 0x%02x, intno, regs);
639b92184ecSJung-uk Kim 		if (x86bios_fault)
640f2c73cefSJung-uk Kim 			printf("Page fault at 0x%06x from 0x%04x:0x%04x.\n",
641ef8201d3SJung-uk Kim 			    x86bios_fault_addr, x86bios_fault_cs,
642ef8201d3SJung-uk Kim 			    x86bios_fault_ip);
643b92184ecSJung-uk Kim 	}
64419de5df5SJung-uk Kim }
64519de5df5SJung-uk Kim 
64619de5df5SJung-uk Kim void *
6473219f535SJung-uk Kim x86bios_offset(uint32_t offset)
64819de5df5SJung-uk Kim {
64919de5df5SJung-uk Kim 
6503219f535SJung-uk Kim 	return (x86bios_get_pages(offset, 1));
6513219f535SJung-uk Kim }
6523219f535SJung-uk Kim 
65348319187SJung-uk Kim static __inline void
65448319187SJung-uk Kim x86bios_unmap_mem(void)
65548319187SJung-uk Kim {
65648319187SJung-uk Kim 
657d01d12deSRoger Pau Monné 	if (x86bios_map != NULL) {
658449918b1SJung-uk Kim 		free(x86bios_map, M_DEVBUF);
659d01d12deSRoger Pau Monné 		x86bios_map = NULL;
660d01d12deSRoger Pau Monné 	}
661d01d12deSRoger Pau Monné 	if (x86bios_ivt != NULL) {
66248319187SJung-uk Kim #ifdef X86BIOS_NATIVE_ARCH
6637ae99f80SJohn Baldwin 		pmap_unmapbios(x86bios_ivt, X86BIOS_IVT_SIZE);
664ef8201d3SJung-uk Kim #else
66548319187SJung-uk Kim 		free(x86bios_ivt, M_DEVBUF);
666d01d12deSRoger Pau Monné 		x86bios_ivt = NULL;
667ef8201d3SJung-uk Kim #endif
668d01d12deSRoger Pau Monné 	}
66948319187SJung-uk Kim 	if (x86bios_rom != NULL)
6707ae99f80SJohn Baldwin 		pmap_unmapdev(x86bios_rom, X86BIOS_ROM_SIZE);
671d01d12deSRoger Pau Monné 	if (x86bios_seg != NULL) {
672*d1bdc282SBjoern A. Zeeb 		free(x86bios_seg, M_DEVBUF);
673d01d12deSRoger Pau Monné 		x86bios_seg = NULL;
674d01d12deSRoger Pau Monné 	}
67548319187SJung-uk Kim }
676ef8201d3SJung-uk Kim 
6773219f535SJung-uk Kim static __inline int
6783219f535SJung-uk Kim x86bios_map_mem(void)
6793219f535SJung-uk Kim {
6803219f535SJung-uk Kim 
681449918b1SJung-uk Kim 	x86bios_map = malloc(sizeof(*x86bios_map) * X86BIOS_PAGES, M_DEVBUF,
682d01d12deSRoger Pau Monné 	    M_NOWAIT | M_ZERO);
683d01d12deSRoger Pau Monné 	if (x86bios_map == NULL)
684d01d12deSRoger Pau Monné 		goto fail;
685449918b1SJung-uk Kim 
68648319187SJung-uk Kim #ifdef X86BIOS_NATIVE_ARCH
6875a0a9182SJung-uk Kim 	x86bios_ivt = pmap_mapbios(X86BIOS_IVT_BASE, X86BIOS_IVT_SIZE);
688ef8201d3SJung-uk Kim 
689ef8201d3SJung-uk Kim 	/* Probe EBDA via BDA. */
69048319187SJung-uk Kim 	x86bios_rom_phys = *(uint16_t *)((caddr_t)x86bios_ivt + 0x40e);
69148319187SJung-uk Kim 	x86bios_rom_phys = x86bios_rom_phys << 4;
692ef8201d3SJung-uk Kim 	if (x86bios_rom_phys != 0 && x86bios_rom_phys < X86BIOS_ROM_BASE &&
693ef8201d3SJung-uk Kim 	    X86BIOS_ROM_BASE - x86bios_rom_phys <= 128 * 1024)
694ef8201d3SJung-uk Kim 		x86bios_rom_phys =
695ef8201d3SJung-uk Kim 		    rounddown(x86bios_rom_phys, X86BIOS_PAGE_SIZE);
696ef8201d3SJung-uk Kim 	else
69748319187SJung-uk Kim #else
698d01d12deSRoger Pau Monné 	x86bios_ivt = malloc(X86BIOS_IVT_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO);
699d01d12deSRoger Pau Monné 	if (x86bios_ivt == NULL)
700d01d12deSRoger Pau Monné 		goto fail;
70148319187SJung-uk Kim #endif
70248319187SJung-uk Kim 
703ef8201d3SJung-uk Kim 	x86bios_rom_phys = X86BIOS_ROM_BASE;
704ef8201d3SJung-uk Kim 	x86bios_rom = pmap_mapdev(x86bios_rom_phys, X86BIOS_ROM_SIZE);
70548319187SJung-uk Kim 	if (x86bios_rom == NULL)
70648319187SJung-uk Kim 		goto fail;
707439f3d8bSJung-uk Kim #ifdef X86BIOS_NATIVE_ARCH
708ef8201d3SJung-uk Kim 	/* Change attribute for EBDA. */
709ef8201d3SJung-uk Kim 	if (x86bios_rom_phys < X86BIOS_ROM_BASE &&
710ef8201d3SJung-uk Kim 	    pmap_change_attr((vm_offset_t)x86bios_rom,
71148319187SJung-uk Kim 	    X86BIOS_ROM_BASE - x86bios_rom_phys, PAT_WRITE_BACK) != 0)
71248319187SJung-uk Kim 		goto fail;
713ef8201d3SJung-uk Kim #endif
714ef8201d3SJung-uk Kim 
715d01d12deSRoger Pau Monné 	x86bios_seg = contigmalloc(X86BIOS_SEG_SIZE, M_DEVBUF, M_NOWAIT,
716ef8201d3SJung-uk Kim 	    X86BIOS_RAM_BASE, x86bios_rom_phys, X86BIOS_PAGE_SIZE, 0);
717d01d12deSRoger Pau Monné 	if (x86bios_seg == NULL)
718d01d12deSRoger Pau Monné 	    goto fail;
7193219f535SJung-uk Kim 	x86bios_seg_phys = vtophys(x86bios_seg);
7203219f535SJung-uk Kim 
721449918b1SJung-uk Kim 	x86bios_set_pages((vm_offset_t)x86bios_ivt, X86BIOS_IVT_BASE,
722449918b1SJung-uk Kim 	    X86BIOS_IVT_SIZE);
723449918b1SJung-uk Kim 	x86bios_set_pages((vm_offset_t)x86bios_rom, x86bios_rom_phys,
724449918b1SJung-uk Kim 	    X86BIOS_ROM_SIZE);
725449918b1SJung-uk Kim 	x86bios_set_pages((vm_offset_t)x86bios_seg, x86bios_seg_phys,
726449918b1SJung-uk Kim 	    X86BIOS_SEG_SIZE);
727449918b1SJung-uk Kim 
728ef8201d3SJung-uk Kim 	if (bootverbose) {
729f2c73cefSJung-uk Kim 		printf("x86bios:  IVT 0x%06jx-0x%06jx at %p\n",
730f2c73cefSJung-uk Kim 		    (vm_paddr_t)X86BIOS_IVT_BASE,
731f2c73cefSJung-uk Kim 		    (vm_paddr_t)X86BIOS_IVT_SIZE + X86BIOS_IVT_BASE - 1,
732ef8201d3SJung-uk Kim 		    x86bios_ivt);
733f2c73cefSJung-uk Kim 		printf("x86bios: SSEG 0x%06jx-0x%06jx at %p\n",
734f2c73cefSJung-uk Kim 		    x86bios_seg_phys,
735f2c73cefSJung-uk Kim 		    (vm_paddr_t)X86BIOS_SEG_SIZE + x86bios_seg_phys - 1,
736ef8201d3SJung-uk Kim 		    x86bios_seg);
737ef8201d3SJung-uk Kim 		if (x86bios_rom_phys < X86BIOS_ROM_BASE)
738f2c73cefSJung-uk Kim 			printf("x86bios: EBDA 0x%06jx-0x%06jx at %p\n",
739f2c73cefSJung-uk Kim 			    x86bios_rom_phys, (vm_paddr_t)X86BIOS_ROM_BASE - 1,
740ef8201d3SJung-uk Kim 			    x86bios_rom);
741f2c73cefSJung-uk Kim 		printf("x86bios:  ROM 0x%06jx-0x%06jx at %p\n",
742f2c73cefSJung-uk Kim 		    (vm_paddr_t)X86BIOS_ROM_BASE,
743f2c73cefSJung-uk Kim 		    (vm_paddr_t)X86BIOS_MEM_SIZE - X86BIOS_SEG_SIZE - 1,
744f2c73cefSJung-uk Kim 		    (caddr_t)x86bios_rom + X86BIOS_ROM_BASE - x86bios_rom_phys);
745ef8201d3SJung-uk Kim 	}
746ef8201d3SJung-uk Kim 
7473219f535SJung-uk Kim 	return (0);
7483219f535SJung-uk Kim 
74948319187SJung-uk Kim fail:
75048319187SJung-uk Kim 	x86bios_unmap_mem();
751ef8201d3SJung-uk Kim 
75248319187SJung-uk Kim 	return (1);
75319de5df5SJung-uk Kim }
75419de5df5SJung-uk Kim 
7552083bca5SJung-uk Kim static int
7562083bca5SJung-uk Kim x86bios_init(void)
75719de5df5SJung-uk Kim {
75819de5df5SJung-uk Kim 
759449918b1SJung-uk Kim 	mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_DEF);
760449918b1SJung-uk Kim 
7613219f535SJung-uk Kim 	if (x86bios_map_mem() != 0)
7622083bca5SJung-uk Kim 		return (ENOMEM);
7632083bca5SJung-uk Kim 
7643219f535SJung-uk Kim 	bzero(&x86bios_emu, sizeof(x86bios_emu));
7653219f535SJung-uk Kim 
7663219f535SJung-uk Kim 	x86bios_emu.emu_rdb = x86bios_emu_rdb;
7673219f535SJung-uk Kim 	x86bios_emu.emu_rdw = x86bios_emu_rdw;
7683219f535SJung-uk Kim 	x86bios_emu.emu_rdl = x86bios_emu_rdl;
7693219f535SJung-uk Kim 	x86bios_emu.emu_wrb = x86bios_emu_wrb;
7703219f535SJung-uk Kim 	x86bios_emu.emu_wrw = x86bios_emu_wrw;
7713219f535SJung-uk Kim 	x86bios_emu.emu_wrl = x86bios_emu_wrl;
77219de5df5SJung-uk Kim 
77319de5df5SJung-uk Kim 	x86bios_emu.emu_inb = x86bios_emu_inb;
77419de5df5SJung-uk Kim 	x86bios_emu.emu_inw = x86bios_emu_inw;
77519de5df5SJung-uk Kim 	x86bios_emu.emu_inl = x86bios_emu_inl;
77619de5df5SJung-uk Kim 	x86bios_emu.emu_outb = x86bios_emu_outb;
77719de5df5SJung-uk Kim 	x86bios_emu.emu_outw = x86bios_emu_outw;
77819de5df5SJung-uk Kim 	x86bios_emu.emu_outl = x86bios_emu_outl;
77919de5df5SJung-uk Kim 
7802083bca5SJung-uk Kim 	return (0);
78119de5df5SJung-uk Kim }
78219de5df5SJung-uk Kim 
7832083bca5SJung-uk Kim static int
7842083bca5SJung-uk Kim x86bios_uninit(void)
78519de5df5SJung-uk Kim {
78619de5df5SJung-uk Kim 
7873219f535SJung-uk Kim 	x86bios_unmap_mem();
78819de5df5SJung-uk Kim 	mtx_destroy(&x86bios_lock);
7892083bca5SJung-uk Kim 
7902083bca5SJung-uk Kim 	return (0);
79119de5df5SJung-uk Kim }
79219de5df5SJung-uk Kim 
793439f3d8bSJung-uk Kim #endif
794439f3d8bSJung-uk Kim 
795439f3d8bSJung-uk Kim void *
796439f3d8bSJung-uk Kim x86bios_get_orm(uint32_t offset)
797439f3d8bSJung-uk Kim {
798439f3d8bSJung-uk Kim 	uint8_t *p;
799439f3d8bSJung-uk Kim 
800439f3d8bSJung-uk Kim 	/* Does the shadow ROM contain BIOS POST code for x86? */
801439f3d8bSJung-uk Kim 	p = x86bios_offset(offset);
802bc339276SJung-uk Kim 	if (p == NULL || p[0] != 0x55 || p[1] != 0xaa ||
803bc339276SJung-uk Kim 	    (p[3] != 0xe9 && p[3] != 0xeb))
804439f3d8bSJung-uk Kim 		return (NULL);
805439f3d8bSJung-uk Kim 
806439f3d8bSJung-uk Kim 	return (p);
807439f3d8bSJung-uk Kim }
808439f3d8bSJung-uk Kim 
809439f3d8bSJung-uk Kim int
810439f3d8bSJung-uk Kim x86bios_match_device(uint32_t offset, device_t dev)
811439f3d8bSJung-uk Kim {
812439f3d8bSJung-uk Kim 	uint8_t *p;
813439f3d8bSJung-uk Kim 	uint16_t device, vendor;
814439f3d8bSJung-uk Kim 	uint8_t class, progif, subclass;
815439f3d8bSJung-uk Kim 
816439f3d8bSJung-uk Kim 	/* Does the shadow ROM contain BIOS POST code for x86? */
817439f3d8bSJung-uk Kim 	p = x86bios_get_orm(offset);
818439f3d8bSJung-uk Kim 	if (p == NULL)
819439f3d8bSJung-uk Kim 		return (0);
820439f3d8bSJung-uk Kim 
821439f3d8bSJung-uk Kim 	/* Does it contain PCI data structure? */
822439f3d8bSJung-uk Kim 	p += le16toh(*(uint16_t *)(p + 0x18));
823439f3d8bSJung-uk Kim 	if (bcmp(p, "PCIR", 4) != 0 ||
824439f3d8bSJung-uk Kim 	    le16toh(*(uint16_t *)(p + 0x0a)) < 0x18 || *(p + 0x14) != 0)
825439f3d8bSJung-uk Kim 		return (0);
826439f3d8bSJung-uk Kim 
827439f3d8bSJung-uk Kim 	/* Does it match the vendor, device, and classcode? */
828439f3d8bSJung-uk Kim 	vendor = le16toh(*(uint16_t *)(p + 0x04));
829439f3d8bSJung-uk Kim 	device = le16toh(*(uint16_t *)(p + 0x06));
830439f3d8bSJung-uk Kim 	progif = *(p + 0x0d);
831439f3d8bSJung-uk Kim 	subclass = *(p + 0x0e);
832439f3d8bSJung-uk Kim 	class = *(p + 0x0f);
833439f3d8bSJung-uk Kim 	if (vendor != pci_get_vendor(dev) || device != pci_get_device(dev) ||
834439f3d8bSJung-uk Kim 	    class != pci_get_class(dev) || subclass != pci_get_subclass(dev) ||
835439f3d8bSJung-uk Kim 	    progif != pci_get_progif(dev))
836439f3d8bSJung-uk Kim 		return (0);
837439f3d8bSJung-uk Kim 
838439f3d8bSJung-uk Kim 	return (1);
839439f3d8bSJung-uk Kim }
840439f3d8bSJung-uk Kim 
84119de5df5SJung-uk Kim static int
84219de5df5SJung-uk Kim x86bios_modevent(module_t mod __unused, int type, void *data __unused)
84319de5df5SJung-uk Kim {
84419de5df5SJung-uk Kim 
84519de5df5SJung-uk Kim 	switch (type) {
84619de5df5SJung-uk Kim 	case MOD_LOAD:
8472083bca5SJung-uk Kim 		return (x86bios_init());
84819de5df5SJung-uk Kim 	case MOD_UNLOAD:
8492083bca5SJung-uk Kim 		return (x86bios_uninit());
85019de5df5SJung-uk Kim 	default:
8513219f535SJung-uk Kim 		return (ENOTSUP);
85219de5df5SJung-uk Kim 	}
85319de5df5SJung-uk Kim }
85419de5df5SJung-uk Kim 
85519de5df5SJung-uk Kim static moduledata_t x86bios_mod = {
85619de5df5SJung-uk Kim 	"x86bios",
85719de5df5SJung-uk Kim 	x86bios_modevent,
85819de5df5SJung-uk Kim 	NULL,
85919de5df5SJung-uk Kim };
86019de5df5SJung-uk Kim 
86119de5df5SJung-uk Kim DECLARE_MODULE(x86bios, x86bios_mod, SI_SUB_CPU, SI_ORDER_ANY);
86219de5df5SJung-uk Kim MODULE_VERSION(x86bios, 1);
863