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