1197383Sdelphij/*-
2197571Sdelphij * Copyright (c) 2009 Alex Keda <admin@lissyara.su>
3205297Sjkim * Copyright (c) 2009-2010 Jung-uk Kim <jkim@FreeBSD.org>
4197571Sdelphij * All rights reserved.
5197571Sdelphij *
6197571Sdelphij * Redistribution and use in source and binary forms, with or without
7197571Sdelphij * modification, are permitted provided that the following conditions
8197571Sdelphij * are met:
9197571Sdelphij * 1. Redistributions of source code must retain the above copyright
10197571Sdelphij *    notice, this list of conditions and the following disclaimer.
11197571Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
12197571Sdelphij *    notice, this list of conditions and the following disclaimer in the
13197571Sdelphij *    documentation and/or other materials provided with the distribution.
14197571Sdelphij *
15197571Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16197571Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17197571Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18197571Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19197571Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20197571Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21197571Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22197571Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23197571Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24197571Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25197571Sdelphij * SUCH DAMAGE.
26197383Sdelphij */
27197383Sdelphij
28197383Sdelphij#include <sys/cdefs.h>
29197383Sdelphij__FBSDID("$FreeBSD$");
30197383Sdelphij
31197383Sdelphij#include "opt_x86bios.h"
32197383Sdelphij
33197383Sdelphij#include <sys/param.h>
34198251Sjkim#include <sys/bus.h>
35197383Sdelphij#include <sys/kernel.h>
36197442Sjkim#include <sys/lock.h>
37198251Sjkim#include <sys/malloc.h>
38197383Sdelphij#include <sys/module.h>
39197383Sdelphij#include <sys/mutex.h>
40200591Sjkim#include <sys/sysctl.h>
41197383Sdelphij
42198251Sjkim#include <contrib/x86emu/x86emu.h>
43198251Sjkim#include <contrib/x86emu/x86emu_regs.h>
44198251Sjkim#include <compat/x86bios/x86bios.h>
45198251Sjkim
46198251Sjkim#include <dev/pci/pcireg.h>
47198251Sjkim#include <dev/pci/pcivar.h>
48198251Sjkim
49197383Sdelphij#include <vm/vm.h>
50197383Sdelphij#include <vm/pmap.h>
51197383Sdelphij
52210877Sjkim#ifdef __amd64__
53207456Sjkim#define	X86BIOS_NATIVE_ARCH
54207456Sjkim#endif
55210877Sjkim#ifdef __i386__
56210877Sjkim#define	X86BIOS_NATIVE_VM86
57210877Sjkim#endif
58207456Sjkim
59210877Sjkim#define	X86BIOS_MEM_SIZE	0x00100000	/* 1M */
60210877Sjkim
61211120Sjkim#define	X86BIOS_TRACE(h, n, r)	do {					\
62211120Sjkim	printf(__STRING(h)						\
63211120Sjkim	    " (ax=0x%04x bx=0x%04x cx=0x%04x dx=0x%04x es=0x%04x di=0x%04x)\n",\
64211120Sjkim	    (n), (r)->R_AX, (r)->R_BX, (r)->R_CX, (r)->R_DX,		\
65211120Sjkim	    (r)->R_ES, (r)->R_DI);					\
66211120Sjkim} while (0)
67211120Sjkim
68210877Sjkimstatic struct mtx x86bios_lock;
69210877Sjkim
70227309Sedstatic SYSCTL_NODE(_debug, OID_AUTO, x86bios, CTLFLAG_RD, NULL,
71227309Sed    "x86bios debugging");
72210877Sjkimstatic int x86bios_trace_call;
73267992ShselaskySYSCTL_INT(_debug_x86bios, OID_AUTO, call, CTLFLAG_RWTUN, &x86bios_trace_call, 0,
74210877Sjkim    "Trace far function calls");
75210877Sjkimstatic int x86bios_trace_int;
76267992ShselaskySYSCTL_INT(_debug_x86bios, OID_AUTO, int, CTLFLAG_RWTUN, &x86bios_trace_int, 0,
77210877Sjkim    "Trace software interrupt handlers");
78210877Sjkim
79210877Sjkim#ifdef X86BIOS_NATIVE_VM86
80210877Sjkim
81210877Sjkim#include <machine/vm86.h>
82210877Sjkim#include <machine/vmparam.h>
83210877Sjkim#include <machine/pc/bios.h>
84210877Sjkim
85210877Sjkimstruct vm86context x86bios_vmc;
86210877Sjkim
87210877Sjkimstatic void
88210877Sjkimx86bios_emu2vmf(struct x86emu_regs *regs, struct vm86frame *vmf)
89210877Sjkim{
90210877Sjkim
91210877Sjkim	vmf->vmf_ds = regs->R_DS;
92210877Sjkim	vmf->vmf_es = regs->R_ES;
93210877Sjkim	vmf->vmf_ax = regs->R_AX;
94210877Sjkim	vmf->vmf_bx = regs->R_BX;
95210877Sjkim	vmf->vmf_cx = regs->R_CX;
96210877Sjkim	vmf->vmf_dx = regs->R_DX;
97210877Sjkim	vmf->vmf_bp = regs->R_BP;
98210877Sjkim	vmf->vmf_si = regs->R_SI;
99210877Sjkim	vmf->vmf_di = regs->R_DI;
100210877Sjkim}
101210877Sjkim
102210877Sjkimstatic void
103210877Sjkimx86bios_vmf2emu(struct vm86frame *vmf, struct x86emu_regs *regs)
104210877Sjkim{
105210877Sjkim
106210877Sjkim	regs->R_DS = vmf->vmf_ds;
107210877Sjkim	regs->R_ES = vmf->vmf_es;
108210887Sjkim	regs->R_FLG = vmf->vmf_flags;
109210877Sjkim	regs->R_AX = vmf->vmf_ax;
110210877Sjkim	regs->R_BX = vmf->vmf_bx;
111210877Sjkim	regs->R_CX = vmf->vmf_cx;
112210877Sjkim	regs->R_DX = vmf->vmf_dx;
113210877Sjkim	regs->R_BP = vmf->vmf_bp;
114210877Sjkim	regs->R_SI = vmf->vmf_si;
115210877Sjkim	regs->R_DI = vmf->vmf_di;
116210877Sjkim}
117210877Sjkim
118210877Sjkimvoid *
119210877Sjkimx86bios_alloc(uint32_t *offset, size_t size, int flags)
120210877Sjkim{
121211112Sjkim	void *vaddr;
122298801Spfg	u_int i;
123210877Sjkim
124211112Sjkim	if (offset == NULL || size == 0)
125211112Sjkim		return (NULL);
126211112Sjkim	vaddr = contigmalloc(size, M_DEVBUF, flags, 0, X86BIOS_MEM_SIZE,
127211112Sjkim	    PAGE_SIZE, 0);
128211112Sjkim	if (vaddr != NULL) {
129211112Sjkim		*offset = vtophys(vaddr);
130210877Sjkim		mtx_lock(&x86bios_lock);
131210938Sjkim		for (i = 0; i < atop(round_page(size)); i++)
132210934Sjkim			vm86_addpage(&x86bios_vmc, atop(*offset) + i,
133211112Sjkim			    (vm_offset_t)vaddr + ptoa(i));
134210877Sjkim		mtx_unlock(&x86bios_lock);
135210877Sjkim	}
136210877Sjkim
137211112Sjkim	return (vaddr);
138210877Sjkim}
139210877Sjkim
140210877Sjkimvoid
141210877Sjkimx86bios_free(void *addr, size_t size)
142210877Sjkim{
143211112Sjkim	vm_paddr_t paddr;
144211112Sjkim	int i, nfree;
145210877Sjkim
146211112Sjkim	if (addr == NULL || size == 0)
147211112Sjkim		return;
148211112Sjkim	paddr = vtophys(addr);
149211112Sjkim	if (paddr >= X86BIOS_MEM_SIZE || (paddr & PAGE_MASK) != 0)
150211112Sjkim		return;
151210877Sjkim	mtx_lock(&x86bios_lock);
152211112Sjkim	for (i = 0; i < x86bios_vmc.npages; i++)
153211112Sjkim		if (x86bios_vmc.pmap[i].kva == (vm_offset_t)addr)
154211112Sjkim			break;
155211112Sjkim	if (i >= x86bios_vmc.npages) {
156210934Sjkim		mtx_unlock(&x86bios_lock);
157210934Sjkim		return;
158210934Sjkim	}
159211114Sjkim	nfree = atop(round_page(size));
160211112Sjkim	bzero(x86bios_vmc.pmap + i, sizeof(*x86bios_vmc.pmap) * nfree);
161211112Sjkim	if (i + nfree == x86bios_vmc.npages) {
162211112Sjkim		x86bios_vmc.npages -= nfree;
163211112Sjkim		while (--i >= 0 && x86bios_vmc.pmap[i].kva == 0)
164210877Sjkim			x86bios_vmc.npages--;
165210877Sjkim	}
166210877Sjkim	mtx_unlock(&x86bios_lock);
167210877Sjkim	contigfree(addr, size, M_DEVBUF);
168210877Sjkim}
169210877Sjkim
170210877Sjkimvoid
171210877Sjkimx86bios_init_regs(struct x86regs *regs)
172210877Sjkim{
173210877Sjkim
174210877Sjkim	bzero(regs, sizeof(*regs));
175210877Sjkim}
176210877Sjkim
177210877Sjkimvoid
178210877Sjkimx86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
179210877Sjkim{
180210877Sjkim	struct vm86frame vmf;
181210877Sjkim
182210877Sjkim	if (x86bios_trace_call)
183211120Sjkim		X86BIOS_TRACE(Calling 0x%06x, (seg << 4) + off, regs);
184210877Sjkim
185210877Sjkim	bzero(&vmf, sizeof(vmf));
186210877Sjkim	x86bios_emu2vmf((struct x86emu_regs *)regs, &vmf);
187210877Sjkim	vmf.vmf_cs = seg;
188210877Sjkim	vmf.vmf_ip = off;
189210877Sjkim	mtx_lock(&x86bios_lock);
190210877Sjkim	vm86_datacall(-1, &vmf, &x86bios_vmc);
191210877Sjkim	mtx_unlock(&x86bios_lock);
192210877Sjkim	x86bios_vmf2emu(&vmf, (struct x86emu_regs *)regs);
193210877Sjkim
194210877Sjkim	if (x86bios_trace_call)
195211120Sjkim		X86BIOS_TRACE(Exiting 0x%06x, (seg << 4) + off, regs);
196210877Sjkim}
197210877Sjkim
198210877Sjkimuint32_t
199210877Sjkimx86bios_get_intr(int intno)
200210877Sjkim{
201210877Sjkim
202210992Sjkim	return (readl(BIOS_PADDRTOVADDR(intno * 4)));
203210877Sjkim}
204210877Sjkim
205210877Sjkimvoid
206211824Sjkimx86bios_set_intr(int intno, uint32_t saddr)
207211824Sjkim{
208211824Sjkim
209211824Sjkim	writel(BIOS_PADDRTOVADDR(intno * 4), saddr);
210211824Sjkim}
211211824Sjkim
212211824Sjkimvoid
213210877Sjkimx86bios_intr(struct x86regs *regs, int intno)
214210877Sjkim{
215210877Sjkim	struct vm86frame vmf;
216210877Sjkim
217210877Sjkim	if (x86bios_trace_int)
218211120Sjkim		X86BIOS_TRACE(Calling INT 0x%02x, intno, regs);
219210877Sjkim
220210877Sjkim	bzero(&vmf, sizeof(vmf));
221210877Sjkim	x86bios_emu2vmf((struct x86emu_regs *)regs, &vmf);
222210877Sjkim	mtx_lock(&x86bios_lock);
223210877Sjkim	vm86_datacall(intno, &vmf, &x86bios_vmc);
224210877Sjkim	mtx_unlock(&x86bios_lock);
225210877Sjkim	x86bios_vmf2emu(&vmf, (struct x86emu_regs *)regs);
226210877Sjkim
227210877Sjkim	if (x86bios_trace_int)
228211120Sjkim		X86BIOS_TRACE(Exiting INT 0x%02x, intno, regs);
229210877Sjkim}
230210877Sjkim
231210877Sjkimvoid *
232210877Sjkimx86bios_offset(uint32_t offset)
233210877Sjkim{
234210877Sjkim	vm_offset_t addr;
235210877Sjkim
236210877Sjkim	addr = vm86_getaddr(&x86bios_vmc, X86BIOS_PHYSTOSEG(offset),
237210877Sjkim	    X86BIOS_PHYSTOOFF(offset));
238210877Sjkim	if (addr == 0)
239210877Sjkim		addr = BIOS_PADDRTOVADDR(offset);
240210877Sjkim
241210877Sjkim	return ((void *)addr);
242210877Sjkim}
243210877Sjkim
244210877Sjkimstatic int
245210877Sjkimx86bios_init(void)
246210877Sjkim{
247210877Sjkim
248210877Sjkim	mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_DEF);
249210877Sjkim	bzero(&x86bios_vmc, sizeof(x86bios_vmc));
250210877Sjkim
251210877Sjkim	return (0);
252210877Sjkim}
253210877Sjkim
254210877Sjkimstatic int
255210877Sjkimx86bios_uninit(void)
256210877Sjkim{
257210877Sjkim
258210877Sjkim	mtx_destroy(&x86bios_lock);
259210877Sjkim
260210877Sjkim	return (0);
261210877Sjkim}
262210877Sjkim
263210877Sjkim#else
264210877Sjkim
265210877Sjkim#include <machine/iodev.h>
266210877Sjkim
267198251Sjkim#define	X86BIOS_PAGE_SIZE	0x00001000	/* 4K */
268197383Sdelphij
269198251Sjkim#define	X86BIOS_IVT_SIZE	0x00000500	/* 1K + 256 (BDA) */
270197383Sdelphij
271198251Sjkim#define	X86BIOS_IVT_BASE	0x00000000
272198251Sjkim#define	X86BIOS_RAM_BASE	0x00001000
273205347Sjkim#define	X86BIOS_ROM_BASE	0x000a0000
274197383Sdelphij
275211120Sjkim#define	X86BIOS_ROM_SIZE	(X86BIOS_MEM_SIZE - x86bios_rom_phys)
276210877Sjkim#define	X86BIOS_SEG_SIZE	X86BIOS_PAGE_SIZE
277197383Sdelphij
278198251Sjkim#define	X86BIOS_PAGES		(X86BIOS_MEM_SIZE / X86BIOS_PAGE_SIZE)
279198251Sjkim
280198251Sjkim#define	X86BIOS_R_SS		_pad2
281210877Sjkim#define	X86BIOS_R_SP		_pad3.I16_reg.x_reg
282198251Sjkim
283197442Sjkimstatic struct x86emu x86bios_emu;
284197383Sdelphij
285198251Sjkimstatic void *x86bios_ivt;
286198251Sjkimstatic void *x86bios_rom;
287198251Sjkimstatic void *x86bios_seg;
288198251Sjkim
289198251Sjkimstatic vm_offset_t *x86bios_map;
290198251Sjkim
291205347Sjkimstatic vm_paddr_t x86bios_rom_phys;
292198251Sjkimstatic vm_paddr_t x86bios_seg_phys;
293198251Sjkim
294205297Sjkimstatic int x86bios_fault;
295205297Sjkimstatic uint32_t x86bios_fault_addr;
296205347Sjkimstatic uint16_t x86bios_fault_cs;
297205347Sjkimstatic uint16_t x86bios_fault_ip;
298205297Sjkim
299205297Sjkimstatic void
300205297Sjkimx86bios_set_fault(struct x86emu *emu, uint32_t addr)
301205297Sjkim{
302205297Sjkim
303205297Sjkim	x86bios_fault = 1;
304205297Sjkim	x86bios_fault_addr = addr;
305205347Sjkim	x86bios_fault_cs = emu->x86.R_CS;
306205347Sjkim	x86bios_fault_ip = emu->x86.R_IP;
307205347Sjkim	x86emu_halt_sys(emu);
308205297Sjkim}
309205297Sjkim
310198251Sjkimstatic void *
311198251Sjkimx86bios_get_pages(uint32_t offset, size_t size)
312198251Sjkim{
313210877Sjkim	vm_offset_t addr;
314198251Sjkim
315205455Sjkim	if (offset + size > X86BIOS_MEM_SIZE + X86BIOS_IVT_SIZE)
316198251Sjkim		return (NULL);
317198251Sjkim
318205455Sjkim	if (offset >= X86BIOS_MEM_SIZE)
319205455Sjkim		offset -= X86BIOS_MEM_SIZE;
320210877Sjkim	addr = x86bios_map[offset / X86BIOS_PAGE_SIZE];
321210877Sjkim	if (addr != 0)
322210877Sjkim		addr += offset % X86BIOS_PAGE_SIZE;
323198251Sjkim
324210877Sjkim	return ((void *)addr);
325198251Sjkim}
326198251Sjkim
327198251Sjkimstatic void
328198251Sjkimx86bios_set_pages(vm_offset_t va, vm_paddr_t pa, size_t size)
329198251Sjkim{
330198251Sjkim	int i, j;
331198251Sjkim
332198251Sjkim	for (i = pa / X86BIOS_PAGE_SIZE, j = 0;
333198251Sjkim	    j < howmany(size, X86BIOS_PAGE_SIZE); i++, j++)
334198251Sjkim		x86bios_map[i] = va + j * X86BIOS_PAGE_SIZE;
335198251Sjkim}
336198251Sjkim
337197383Sdelphijstatic uint8_t
338198251Sjkimx86bios_emu_rdb(struct x86emu *emu, uint32_t addr)
339198251Sjkim{
340198251Sjkim	uint8_t *va;
341198251Sjkim
342198251Sjkim	va = x86bios_get_pages(addr, sizeof(*va));
343205347Sjkim	if (va == NULL)
344205297Sjkim		x86bios_set_fault(emu, addr);
345198251Sjkim
346198251Sjkim	return (*va);
347198251Sjkim}
348198251Sjkim
349198251Sjkimstatic uint16_t
350198251Sjkimx86bios_emu_rdw(struct x86emu *emu, uint32_t addr)
351198251Sjkim{
352198251Sjkim	uint16_t *va;
353198251Sjkim
354198251Sjkim	va = x86bios_get_pages(addr, sizeof(*va));
355205347Sjkim	if (va == NULL)
356205297Sjkim		x86bios_set_fault(emu, addr);
357198251Sjkim
358205347Sjkim#ifndef __NO_STRICT_ALIGNMENT
359205347Sjkim	if ((addr & 1) != 0)
360205347Sjkim		return (le16dec(va));
361205347Sjkim	else
362205347Sjkim#endif
363198251Sjkim	return (le16toh(*va));
364198251Sjkim}
365198251Sjkim
366198251Sjkimstatic uint32_t
367198251Sjkimx86bios_emu_rdl(struct x86emu *emu, uint32_t addr)
368198251Sjkim{
369198251Sjkim	uint32_t *va;
370198251Sjkim
371198251Sjkim	va = x86bios_get_pages(addr, sizeof(*va));
372205347Sjkim	if (va == NULL)
373205297Sjkim		x86bios_set_fault(emu, addr);
374198251Sjkim
375205347Sjkim#ifndef __NO_STRICT_ALIGNMENT
376205347Sjkim	if ((addr & 3) != 0)
377205347Sjkim		return (le32dec(va));
378205347Sjkim	else
379205347Sjkim#endif
380198251Sjkim	return (le32toh(*va));
381198251Sjkim}
382198251Sjkim
383198251Sjkimstatic void
384198251Sjkimx86bios_emu_wrb(struct x86emu *emu, uint32_t addr, uint8_t val)
385198251Sjkim{
386198251Sjkim	uint8_t *va;
387198251Sjkim
388198251Sjkim	va = x86bios_get_pages(addr, sizeof(*va));
389205347Sjkim	if (va == NULL)
390205297Sjkim		x86bios_set_fault(emu, addr);
391198251Sjkim
392198251Sjkim	*va = val;
393198251Sjkim}
394198251Sjkim
395198251Sjkimstatic void
396198251Sjkimx86bios_emu_wrw(struct x86emu *emu, uint32_t addr, uint16_t val)
397198251Sjkim{
398198251Sjkim	uint16_t *va;
399198251Sjkim
400198251Sjkim	va = x86bios_get_pages(addr, sizeof(*va));
401205347Sjkim	if (va == NULL)
402205297Sjkim		x86bios_set_fault(emu, addr);
403198251Sjkim
404205347Sjkim#ifndef __NO_STRICT_ALIGNMENT
405205347Sjkim	if ((addr & 1) != 0)
406205347Sjkim		le16enc(va, val);
407205347Sjkim	else
408205347Sjkim#endif
409198251Sjkim	*va = htole16(val);
410198251Sjkim}
411198251Sjkim
412198251Sjkimstatic void
413198251Sjkimx86bios_emu_wrl(struct x86emu *emu, uint32_t addr, uint32_t val)
414198251Sjkim{
415198251Sjkim	uint32_t *va;
416198251Sjkim
417198251Sjkim	va = x86bios_get_pages(addr, sizeof(*va));
418205347Sjkim	if (va == NULL)
419205297Sjkim		x86bios_set_fault(emu, addr);
420198251Sjkim
421205347Sjkim#ifndef __NO_STRICT_ALIGNMENT
422205347Sjkim	if ((addr & 3) != 0)
423205347Sjkim		le32enc(va, val);
424205347Sjkim	else
425205347Sjkim#endif
426198251Sjkim	*va = htole32(val);
427198251Sjkim}
428198251Sjkim
429198251Sjkimstatic uint8_t
430197442Sjkimx86bios_emu_inb(struct x86emu *emu, uint16_t port)
431197383Sdelphij{
432197442Sjkim
433210993Sjkim#ifndef X86BIOS_NATIVE_ARCH
434197383Sdelphij	if (port == 0xb2) /* APM scratch register */
435197442Sjkim		return (0);
436197383Sdelphij	if (port >= 0x80 && port < 0x88) /* POST status register */
437197442Sjkim		return (0);
438210993Sjkim#endif
439198251Sjkim
440207456Sjkim	return (iodev_read_1(port));
441197383Sdelphij}
442197383Sdelphij
443197383Sdelphijstatic uint16_t
444197442Sjkimx86bios_emu_inw(struct x86emu *emu, uint16_t port)
445197383Sdelphij{
446207456Sjkim	uint16_t val;
447197442Sjkim
448210993Sjkim#ifndef X86BIOS_NATIVE_ARCH
449197383Sdelphij	if (port >= 0x80 && port < 0x88) /* POST status register */
450197442Sjkim		return (0);
451198251Sjkim
452207456Sjkim	if ((port & 1) != 0) {
453207456Sjkim		val = iodev_read_1(port);
454207456Sjkim		val |= iodev_read_1(port + 1) << 8;
455207456Sjkim	} else
456207456Sjkim#endif
457207456Sjkim	val = iodev_read_2(port);
458207456Sjkim
459207456Sjkim	return (val);
460197383Sdelphij}
461197383Sdelphij
462197383Sdelphijstatic uint32_t
463197442Sjkimx86bios_emu_inl(struct x86emu *emu, uint16_t port)
464197383Sdelphij{
465207456Sjkim	uint32_t val;
466197442Sjkim
467210993Sjkim#ifndef X86BIOS_NATIVE_ARCH
468197383Sdelphij	if (port >= 0x80 && port < 0x88) /* POST status register */
469197442Sjkim		return (0);
470198251Sjkim
471207456Sjkim	if ((port & 1) != 0) {
472207456Sjkim		val = iodev_read_1(port);
473207456Sjkim		val |= iodev_read_2(port + 1) << 8;
474207456Sjkim		val |= iodev_read_1(port + 3) << 24;
475207456Sjkim	} else if ((port & 2) != 0) {
476207456Sjkim		val = iodev_read_2(port);
477207456Sjkim		val |= iodev_read_2(port + 2) << 16;
478207456Sjkim	} else
479207456Sjkim#endif
480207456Sjkim	val = iodev_read_4(port);
481207456Sjkim
482207456Sjkim	return (val);
483197383Sdelphij}
484197383Sdelphij
485197383Sdelphijstatic void
486197442Sjkimx86bios_emu_outb(struct x86emu *emu, uint16_t port, uint8_t val)
487197383Sdelphij{
488197442Sjkim
489210993Sjkim#ifndef X86BIOS_NATIVE_ARCH
490197383Sdelphij	if (port == 0xb2) /* APM scratch register */
491197383Sdelphij		return;
492197383Sdelphij	if (port >= 0x80 && port < 0x88) /* POST status register */
493197383Sdelphij		return;
494210993Sjkim#endif
495198251Sjkim
496207456Sjkim	iodev_write_1(port, val);
497197383Sdelphij}
498197383Sdelphij
499197383Sdelphijstatic void
500197442Sjkimx86bios_emu_outw(struct x86emu *emu, uint16_t port, uint16_t val)
501197383Sdelphij{
502197442Sjkim
503210993Sjkim#ifndef X86BIOS_NATIVE_ARCH
504197383Sdelphij	if (port >= 0x80 && port < 0x88) /* POST status register */
505197383Sdelphij		return;
506198251Sjkim
507207456Sjkim	if ((port & 1) != 0) {
508207456Sjkim		iodev_write_1(port, val);
509207456Sjkim		iodev_write_1(port + 1, val >> 8);
510207456Sjkim	} else
511207456Sjkim#endif
512207456Sjkim	iodev_write_2(port, val);
513197383Sdelphij}
514197383Sdelphij
515197383Sdelphijstatic void
516197442Sjkimx86bios_emu_outl(struct x86emu *emu, uint16_t port, uint32_t val)
517197383Sdelphij{
518197442Sjkim
519210993Sjkim#ifndef X86BIOS_NATIVE_ARCH
520197383Sdelphij	if (port >= 0x80 && port < 0x88) /* POST status register */
521197383Sdelphij		return;
522198251Sjkim
523207456Sjkim	if ((port & 1) != 0) {
524207456Sjkim		iodev_write_1(port, val);
525207456Sjkim		iodev_write_2(port + 1, val >> 8);
526207456Sjkim		iodev_write_1(port + 3, val >> 24);
527207456Sjkim	} else if ((port & 2) != 0) {
528207456Sjkim		iodev_write_2(port, val);
529207456Sjkim		iodev_write_2(port + 2, val >> 16);
530207456Sjkim	} else
531207456Sjkim#endif
532207456Sjkim	iodev_write_4(port, val);
533197383Sdelphij}
534197383Sdelphij
535198251Sjkimvoid *
536209472Sjkimx86bios_alloc(uint32_t *offset, size_t size, int flags)
537198251Sjkim{
538198251Sjkim	void *vaddr;
539198251Sjkim
540198251Sjkim	if (offset == NULL || size == 0)
541198251Sjkim		return (NULL);
542209472Sjkim	vaddr = contigmalloc(size, M_DEVBUF, flags, X86BIOS_RAM_BASE,
543205347Sjkim	    x86bios_rom_phys, X86BIOS_PAGE_SIZE, 0);
544198251Sjkim	if (vaddr != NULL) {
545198251Sjkim		*offset = vtophys(vaddr);
546211131Sjkim		mtx_lock(&x86bios_lock);
547198251Sjkim		x86bios_set_pages((vm_offset_t)vaddr, *offset, size);
548211131Sjkim		mtx_unlock(&x86bios_lock);
549198251Sjkim	}
550198251Sjkim
551198251Sjkim	return (vaddr);
552198251Sjkim}
553198251Sjkim
554197383Sdelphijvoid
555198251Sjkimx86bios_free(void *addr, size_t size)
556198251Sjkim{
557198251Sjkim	vm_paddr_t paddr;
558198251Sjkim
559198251Sjkim	if (addr == NULL || size == 0)
560198251Sjkim		return;
561198251Sjkim	paddr = vtophys(addr);
562205347Sjkim	if (paddr < X86BIOS_RAM_BASE || paddr >= x86bios_rom_phys ||
563198251Sjkim	    paddr % X86BIOS_PAGE_SIZE != 0)
564198251Sjkim		return;
565211131Sjkim	mtx_lock(&x86bios_lock);
566198251Sjkim	bzero(x86bios_map + paddr / X86BIOS_PAGE_SIZE,
567198251Sjkim	    sizeof(*x86bios_map) * howmany(size, X86BIOS_PAGE_SIZE));
568211131Sjkim	mtx_unlock(&x86bios_lock);
569198251Sjkim	contigfree(addr, size, M_DEVBUF);
570198251Sjkim}
571198251Sjkim
572198251Sjkimvoid
573198251Sjkimx86bios_init_regs(struct x86regs *regs)
574198251Sjkim{
575198251Sjkim
576198251Sjkim	bzero(regs, sizeof(*regs));
577210877Sjkim	regs->X86BIOS_R_SS = X86BIOS_PHYSTOSEG(x86bios_seg_phys);
578210877Sjkim	regs->X86BIOS_R_SP = X86BIOS_PAGE_SIZE - 2;
579198251Sjkim}
580198251Sjkim
581198251Sjkimvoid
582198251Sjkimx86bios_call(struct x86regs *regs, uint16_t seg, uint16_t off)
583198251Sjkim{
584198251Sjkim
585200591Sjkim	if (x86bios_trace_call)
586211120Sjkim		X86BIOS_TRACE(Calling 0x%06x, (seg << 4) + off, regs);
587198251Sjkim
588211131Sjkim	mtx_lock(&x86bios_lock);
589295767Sjkim	memcpy((struct x86regs *)&x86bios_emu.x86, regs, sizeof(*regs));
590205297Sjkim	x86bios_fault = 0;
591211148Sjkim	spinlock_enter();
592198251Sjkim	x86emu_exec_call(&x86bios_emu, seg, off);
593211148Sjkim	spinlock_exit();
594198251Sjkim	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
595211131Sjkim	mtx_unlock(&x86bios_lock);
596198251Sjkim
597205297Sjkim	if (x86bios_trace_call) {
598211120Sjkim		X86BIOS_TRACE(Exiting 0x%06x, (seg << 4) + off, regs);
599205297Sjkim		if (x86bios_fault)
600211120Sjkim			printf("Page fault at 0x%06x from 0x%04x:0x%04x.\n",
601205347Sjkim			    x86bios_fault_addr, x86bios_fault_cs,
602205347Sjkim			    x86bios_fault_ip);
603205297Sjkim	}
604198251Sjkim}
605198251Sjkim
606198251Sjkimuint32_t
607198251Sjkimx86bios_get_intr(int intno)
608198251Sjkim{
609198251Sjkim
610211824Sjkim	return (le32toh(*((uint32_t *)x86bios_ivt + intno)));
611211824Sjkim}
612198251Sjkim
613211824Sjkimvoid
614211824Sjkimx86bios_set_intr(int intno, uint32_t saddr)
615211824Sjkim{
616211824Sjkim
617211824Sjkim	*((uint32_t *)x86bios_ivt + intno) = htole32(saddr);
618198251Sjkim}
619198251Sjkim
620198251Sjkimvoid
621197466Sjkimx86bios_intr(struct x86regs *regs, int intno)
622197383Sdelphij{
623197442Sjkim
624197383Sdelphij	if (intno < 0 || intno > 255)
625197383Sdelphij		return;
626197383Sdelphij
627200591Sjkim	if (x86bios_trace_int)
628211120Sjkim		X86BIOS_TRACE(Calling INT 0x%02x, intno, regs);
629197475Sjkim
630211131Sjkim	mtx_lock(&x86bios_lock);
631295767Sjkim	memcpy((struct x86regs *)&x86bios_emu.x86, regs, sizeof(*regs));
632205297Sjkim	x86bios_fault = 0;
633211148Sjkim	spinlock_enter();
634197442Sjkim	x86emu_exec_intr(&x86bios_emu, intno);
635211148Sjkim	spinlock_exit();
636197442Sjkim	memcpy(regs, &x86bios_emu.x86, sizeof(*regs));
637211131Sjkim	mtx_unlock(&x86bios_lock);
638197475Sjkim
639205297Sjkim	if (x86bios_trace_int) {
640211120Sjkim		X86BIOS_TRACE(Exiting INT 0x%02x, intno, regs);
641205297Sjkim		if (x86bios_fault)
642211120Sjkim			printf("Page fault at 0x%06x from 0x%04x:0x%04x.\n",
643205347Sjkim			    x86bios_fault_addr, x86bios_fault_cs,
644205347Sjkim			    x86bios_fault_ip);
645205297Sjkim	}
646197383Sdelphij}
647197383Sdelphij
648197383Sdelphijvoid *
649198251Sjkimx86bios_offset(uint32_t offset)
650197383Sdelphij{
651197442Sjkim
652198251Sjkim	return (x86bios_get_pages(offset, 1));
653197383Sdelphij}
654197383Sdelphij
655207456Sjkimstatic __inline void
656207456Sjkimx86bios_unmap_mem(void)
657207456Sjkim{
658207456Sjkim
659211131Sjkim	free(x86bios_map, M_DEVBUF);
660207456Sjkim	if (x86bios_ivt != NULL)
661207456Sjkim#ifdef X86BIOS_NATIVE_ARCH
662213458Sjkim		pmap_unmapbios((vm_offset_t)x86bios_ivt, X86BIOS_IVT_SIZE);
663205347Sjkim#else
664207456Sjkim		free(x86bios_ivt, M_DEVBUF);
665205347Sjkim#endif
666207456Sjkim	if (x86bios_rom != NULL)
667207456Sjkim		pmap_unmapdev((vm_offset_t)x86bios_rom, X86BIOS_ROM_SIZE);
668207456Sjkim	if (x86bios_seg != NULL)
669207456Sjkim		contigfree(x86bios_seg, X86BIOS_SEG_SIZE, M_DEVBUF);
670207456Sjkim}
671205347Sjkim
672198251Sjkimstatic __inline int
673198251Sjkimx86bios_map_mem(void)
674198251Sjkim{
675198251Sjkim
676211131Sjkim	x86bios_map = malloc(sizeof(*x86bios_map) * X86BIOS_PAGES, M_DEVBUF,
677211131Sjkim	    M_WAITOK | M_ZERO);
678211131Sjkim
679207456Sjkim#ifdef X86BIOS_NATIVE_ARCH
680198252Sjkim	x86bios_ivt = pmap_mapbios(X86BIOS_IVT_BASE, X86BIOS_IVT_SIZE);
681205347Sjkim
682205347Sjkim	/* Probe EBDA via BDA. */
683207456Sjkim	x86bios_rom_phys = *(uint16_t *)((caddr_t)x86bios_ivt + 0x40e);
684207456Sjkim	x86bios_rom_phys = x86bios_rom_phys << 4;
685205347Sjkim	if (x86bios_rom_phys != 0 && x86bios_rom_phys < X86BIOS_ROM_BASE &&
686205347Sjkim	    X86BIOS_ROM_BASE - x86bios_rom_phys <= 128 * 1024)
687205347Sjkim		x86bios_rom_phys =
688205347Sjkim		    rounddown(x86bios_rom_phys, X86BIOS_PAGE_SIZE);
689205347Sjkim	else
690207456Sjkim#else
691207456Sjkim	x86bios_ivt = malloc(X86BIOS_IVT_SIZE, M_DEVBUF, M_ZERO | M_WAITOK);
692207456Sjkim#endif
693207456Sjkim
694205347Sjkim	x86bios_rom_phys = X86BIOS_ROM_BASE;
695205347Sjkim	x86bios_rom = pmap_mapdev(x86bios_rom_phys, X86BIOS_ROM_SIZE);
696207456Sjkim	if (x86bios_rom == NULL)
697207456Sjkim		goto fail;
698210877Sjkim#ifdef X86BIOS_NATIVE_ARCH
699205347Sjkim	/* Change attribute for EBDA. */
700205347Sjkim	if (x86bios_rom_phys < X86BIOS_ROM_BASE &&
701205347Sjkim	    pmap_change_attr((vm_offset_t)x86bios_rom,
702207456Sjkim	    X86BIOS_ROM_BASE - x86bios_rom_phys, PAT_WRITE_BACK) != 0)
703207456Sjkim		goto fail;
704205347Sjkim#endif
705205347Sjkim
706198251Sjkim	x86bios_seg = contigmalloc(X86BIOS_SEG_SIZE, M_DEVBUF, M_WAITOK,
707205347Sjkim	    X86BIOS_RAM_BASE, x86bios_rom_phys, X86BIOS_PAGE_SIZE, 0);
708198251Sjkim	x86bios_seg_phys = vtophys(x86bios_seg);
709198251Sjkim
710211131Sjkim	x86bios_set_pages((vm_offset_t)x86bios_ivt, X86BIOS_IVT_BASE,
711211131Sjkim	    X86BIOS_IVT_SIZE);
712211131Sjkim	x86bios_set_pages((vm_offset_t)x86bios_rom, x86bios_rom_phys,
713211131Sjkim	    X86BIOS_ROM_SIZE);
714211131Sjkim	x86bios_set_pages((vm_offset_t)x86bios_seg, x86bios_seg_phys,
715211131Sjkim	    X86BIOS_SEG_SIZE);
716211131Sjkim
717205347Sjkim	if (bootverbose) {
718211120Sjkim		printf("x86bios:  IVT 0x%06jx-0x%06jx at %p\n",
719211120Sjkim		    (vm_paddr_t)X86BIOS_IVT_BASE,
720211120Sjkim		    (vm_paddr_t)X86BIOS_IVT_SIZE + X86BIOS_IVT_BASE - 1,
721205347Sjkim		    x86bios_ivt);
722211120Sjkim		printf("x86bios: SSEG 0x%06jx-0x%06jx at %p\n",
723211120Sjkim		    x86bios_seg_phys,
724211120Sjkim		    (vm_paddr_t)X86BIOS_SEG_SIZE + x86bios_seg_phys - 1,
725205347Sjkim		    x86bios_seg);
726205347Sjkim		if (x86bios_rom_phys < X86BIOS_ROM_BASE)
727211120Sjkim			printf("x86bios: EBDA 0x%06jx-0x%06jx at %p\n",
728211120Sjkim			    x86bios_rom_phys, (vm_paddr_t)X86BIOS_ROM_BASE - 1,
729205347Sjkim			    x86bios_rom);
730211120Sjkim		printf("x86bios:  ROM 0x%06jx-0x%06jx at %p\n",
731211120Sjkim		    (vm_paddr_t)X86BIOS_ROM_BASE,
732211120Sjkim		    (vm_paddr_t)X86BIOS_MEM_SIZE - X86BIOS_SEG_SIZE - 1,
733211120Sjkim		    (caddr_t)x86bios_rom + X86BIOS_ROM_BASE - x86bios_rom_phys);
734205347Sjkim	}
735205347Sjkim
736198251Sjkim	return (0);
737198251Sjkim
738207456Sjkimfail:
739207456Sjkim	x86bios_unmap_mem();
740205347Sjkim
741207456Sjkim	return (1);
742198251Sjkim}
743198251Sjkim
744207454Sjkimstatic int
745207454Sjkimx86bios_init(void)
746197383Sdelphij{
747197383Sdelphij
748211131Sjkim	mtx_init(&x86bios_lock, "x86bios lock", NULL, MTX_DEF);
749211131Sjkim
750207454Sjkim	if (x86bios_map_mem() != 0)
751207454Sjkim		return (ENOMEM);
752207454Sjkim
753198251Sjkim	bzero(&x86bios_emu, sizeof(x86bios_emu));
754198251Sjkim
755198251Sjkim	x86bios_emu.emu_rdb = x86bios_emu_rdb;
756198251Sjkim	x86bios_emu.emu_rdw = x86bios_emu_rdw;
757198251Sjkim	x86bios_emu.emu_rdl = x86bios_emu_rdl;
758198251Sjkim	x86bios_emu.emu_wrb = x86bios_emu_wrb;
759198251Sjkim	x86bios_emu.emu_wrw = x86bios_emu_wrw;
760198251Sjkim	x86bios_emu.emu_wrl = x86bios_emu_wrl;
761198251Sjkim
762197442Sjkim	x86bios_emu.emu_inb = x86bios_emu_inb;
763197442Sjkim	x86bios_emu.emu_inw = x86bios_emu_inw;
764197442Sjkim	x86bios_emu.emu_inl = x86bios_emu_inl;
765197442Sjkim	x86bios_emu.emu_outb = x86bios_emu_outb;
766197442Sjkim	x86bios_emu.emu_outw = x86bios_emu_outw;
767197442Sjkim	x86bios_emu.emu_outl = x86bios_emu_outl;
768197383Sdelphij
769207454Sjkim	return (0);
770197383Sdelphij}
771197383Sdelphij
772207454Sjkimstatic int
773207454Sjkimx86bios_uninit(void)
774197383Sdelphij{
775197442Sjkim
776211131Sjkim	x86bios_unmap_mem();
777197383Sdelphij	mtx_destroy(&x86bios_lock);
778207454Sjkim
779207454Sjkim	return (0);
780197383Sdelphij}
781197383Sdelphij
782210877Sjkim#endif
783210877Sjkim
784210877Sjkimvoid *
785210877Sjkimx86bios_get_orm(uint32_t offset)
786210877Sjkim{
787210877Sjkim	uint8_t *p;
788210877Sjkim
789210877Sjkim	/* Does the shadow ROM contain BIOS POST code for x86? */
790210877Sjkim	p = x86bios_offset(offset);
791211823Sjkim	if (p == NULL || p[0] != 0x55 || p[1] != 0xaa ||
792211823Sjkim	    (p[3] != 0xe9 && p[3] != 0xeb))
793210877Sjkim		return (NULL);
794210877Sjkim
795210877Sjkim	return (p);
796210877Sjkim}
797210877Sjkim
798210877Sjkimint
799210877Sjkimx86bios_match_device(uint32_t offset, device_t dev)
800210877Sjkim{
801210877Sjkim	uint8_t *p;
802210877Sjkim	uint16_t device, vendor;
803210877Sjkim	uint8_t class, progif, subclass;
804210877Sjkim
805210877Sjkim	/* Does the shadow ROM contain BIOS POST code for x86? */
806210877Sjkim	p = x86bios_get_orm(offset);
807210877Sjkim	if (p == NULL)
808210877Sjkim		return (0);
809210877Sjkim
810210877Sjkim	/* Does it contain PCI data structure? */
811210877Sjkim	p += le16toh(*(uint16_t *)(p + 0x18));
812210877Sjkim	if (bcmp(p, "PCIR", 4) != 0 ||
813210877Sjkim	    le16toh(*(uint16_t *)(p + 0x0a)) < 0x18 || *(p + 0x14) != 0)
814210877Sjkim		return (0);
815210877Sjkim
816210877Sjkim	/* Does it match the vendor, device, and classcode? */
817210877Sjkim	vendor = le16toh(*(uint16_t *)(p + 0x04));
818210877Sjkim	device = le16toh(*(uint16_t *)(p + 0x06));
819210877Sjkim	progif = *(p + 0x0d);
820210877Sjkim	subclass = *(p + 0x0e);
821210877Sjkim	class = *(p + 0x0f);
822210877Sjkim	if (vendor != pci_get_vendor(dev) || device != pci_get_device(dev) ||
823210877Sjkim	    class != pci_get_class(dev) || subclass != pci_get_subclass(dev) ||
824210877Sjkim	    progif != pci_get_progif(dev))
825210877Sjkim		return (0);
826210877Sjkim
827210877Sjkim	return (1);
828210877Sjkim}
829210877Sjkim
830197383Sdelphijstatic int
831197383Sdelphijx86bios_modevent(module_t mod __unused, int type, void *data __unused)
832197383Sdelphij{
833197383Sdelphij
834197383Sdelphij	switch (type) {
835197383Sdelphij	case MOD_LOAD:
836207454Sjkim		return (x86bios_init());
837197383Sdelphij	case MOD_UNLOAD:
838207454Sjkim		return (x86bios_uninit());
839197383Sdelphij	default:
840198251Sjkim		return (ENOTSUP);
841197383Sdelphij	}
842197383Sdelphij}
843197383Sdelphij
844197383Sdelphijstatic moduledata_t x86bios_mod = {
845197383Sdelphij	"x86bios",
846197383Sdelphij	x86bios_modevent,
847197383Sdelphij	NULL,
848197383Sdelphij};
849197383Sdelphij
850197388SdelphijDECLARE_MODULE(x86bios, x86bios_mod, SI_SUB_CPU, SI_ORDER_ANY);
851197383SdelphijMODULE_VERSION(x86bios, 1);
852