gptboot.c revision 135410
155714Skris/*-
255714Skris * Copyright (c) 1998 Robert Nordier
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms are freely
655714Skris * permitted provided that the above copyright notice and this
755714Skris * paragraph and the following disclaimer are duplicated in all
8280297Sjkim * such forms.
955714Skris *
1055714Skris * This software is provided "AS IS" and without any express or
1155714Skris * implied warranties, including, without limitation, the implied
1255714Skris * warranties of merchantability and fitness for a particular
1355714Skris * purpose.
1455714Skris */
15280297Sjkim
1655714Skris#include <sys/cdefs.h>
1755714Skris__FBSDID("$FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 135410 2004-09-18 02:07:00Z jhb $");
1855714Skris
1955714Skris#include <sys/param.h>
2055714Skris#include <sys/disklabel.h>
2155714Skris#include <sys/diskmbr.h>
22280297Sjkim#include <sys/dirent.h>
2355714Skris#include <sys/reboot.h>
2455714Skris
2555714Skris#include <machine/bootinfo.h>
2655714Skris#include <machine/elf.h>
2755714Skris
2855714Skris#include <stdarg.h>
2955714Skris
3055714Skris#include <a.out.h>
3155714Skris
3255714Skris#include <btxv86.h>
3355714Skris
3455714Skris#include "boot2.h"
3555714Skris#include "lib.h"
3655714Skris
37280297Sjkim#define IO_KEYBOARD	1
3855714Skris#define IO_SERIAL	2
3955714Skris
40280297Sjkim#define SECOND		18	/* Circa that many ticks in a second. */
4155714Skris
4255714Skris#define RBX_ASKNAME	0x0	/* -a */
4355714Skris#define RBX_SINGLE	0x1	/* -s */
4455714Skris/* 0x2 is reserved for log2(RB_NOSYNC). */
4555714Skris/* 0x3 is reserved for log2(RB_HALT). */
4655714Skris/* 0x4 is reserved for log2(RB_INITNAME). */
4755714Skris#define RBX_DFLTROOT	0x5	/* -r */
4855714Skris#define RBX_KDB 	0x6	/* -d */
4955714Skris/* 0x7 is reserved for log2(RB_RDONLY). */
5055714Skris/* 0x8 is reserved for log2(RB_DUMP). */
5155714Skris/* 0x9 is reserved for log2(RB_MINIROOT). */
52280297Sjkim#define RBX_CONFIG	0xa	/* -c */
5355714Skris#define RBX_VERBOSE	0xb	/* -v */
5455714Skris#define RBX_SERIAL	0xc	/* -h */
5555714Skris#define RBX_CDROM	0xd	/* -C */
5655714Skris/* 0xe is reserved for log2(RB_POWEROFF). */
5755714Skris#define RBX_GDB 	0xf	/* -g */
5855714Skris#define RBX_MUTE	0x10	/* -m */
5955714Skris/* 0x11 is reserved for log2(RB_SELFTEST). */
6055714Skris/* 0x12 is reserved for boot programs. */
6155714Skris/* 0x13 is reserved for boot programs. */
6255714Skris#define RBX_PAUSE	0x14	/* -p */
6355714Skris#define RBX_NOINTR	0x1c	/* -n */
6468651Skris/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
6568651Skris#define RBX_DUAL	0x1d	/* -D */
6668651Skris#define RBX_PROBEKBD	0x1e	/* -P */
6768651Skris/* 0x1f is reserved for log2(RB_BOOTINFO). */
6868651Skris
6955714Skris/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
7055714Skris#define RBX_MASK	0x2005ffff
71280297Sjkim
72280297Sjkim#define PATH_CONFIG	"/boot.config"
73280297Sjkim#define PATH_BOOT3	"/boot/loader"
74280297Sjkim#define PATH_KERNEL	"/kernel"
75280297Sjkim
76280297Sjkim#define ARGS		0x900
77280297Sjkim#define NOPT		12
78280297Sjkim#define NDEV		3
79280297Sjkim#define MEM_BASE	0x12
80280297Sjkim#define MEM_EXT 	0x15
81280297Sjkim#define V86_CY(x)	((x) & 1)
82280297Sjkim#define V86_ZR(x)	((x) & 0x40)
8355714Skris
84280297Sjkim#define DRV_HARD	0x80
85280297Sjkim#define DRV_MASK	0x7f
86280297Sjkim
87280297Sjkim#define TYPE_AD		0
8855714Skris#define TYPE_DA		1
8955714Skris#define TYPE_MAXHARD	TYPE_DA
90280297Sjkim#define TYPE_FD		2
91280297Sjkim
92280297Sjkimextern uint32_t _end;
9355714Skris
94296279Sjkimstatic const char optstr[NOPT] = "DhaCgmnPprsv";
95296279Sjkimstatic const unsigned char flags[NOPT] = {
9659191Skris    RBX_DUAL,
97280297Sjkim    RBX_SERIAL,
98280297Sjkim    RBX_ASKNAME,
99280297Sjkim    RBX_CDROM,
100238405Sjkim    RBX_GDB,
101280297Sjkim    RBX_MUTE,
102280297Sjkim    RBX_NOINTR,
103280297Sjkim    RBX_PROBEKBD,
104280297Sjkim    RBX_PAUSE,
105280297Sjkim    RBX_DFLTROOT,
106280297Sjkim    RBX_SINGLE,
107280297Sjkim    RBX_VERBOSE
108280297Sjkim};
109296279Sjkim
110296279Sjkimstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
111280297Sjkimstatic const unsigned char dev_maj[NDEV] = {30, 4, 2};
112280297Sjkim
113280297Sjkimstatic struct dsk {
114280297Sjkim    unsigned drive;
115280297Sjkim    unsigned type;
116280297Sjkim    unsigned unit;
11759191Skris    unsigned slice;
11859191Skris    unsigned part;
11955714Skris    unsigned start;
120280297Sjkim    int init;
121280297Sjkim} dsk;
12255714Skrisstatic char cmd[512];
123280297Sjkimstatic char kname[1024];
124280297Sjkimstatic uint32_t opts;
125280297Sjkimstatic struct bootinfo bootinfo;
126280297Sjkimstatic uint8_t ioctrl = IO_KEYBOARD;
127280297Sjkim
128280297Sjkimvoid exit(int);
129280297Sjkimstatic void load(void);
130280297Sjkimstatic int parse(void);
13155714Skrisstatic int xfsread(ino_t, void *, size_t);
13255714Skrisstatic int dskread(void *, unsigned, unsigned);
133280297Sjkimstatic void printf(const char *,...);
134280297Sjkimstatic void putchar(int);
135280297Sjkimstatic uint32_t memsize(void);
136280297Sjkimstatic int drvread(void *, unsigned, unsigned);
137280297Sjkimstatic int keyhit(unsigned);
138280297Sjkimstatic int xputc(int);
139280297Sjkimstatic int xgetc(int);
140280297Sjkimstatic int getc(int);
141280297Sjkim
142280297Sjkimstatic void memcpy(void *, const void *, int);
143280297Sjkimstatic void
144280297Sjkimmemcpy(void *dst, const void *src, int len)
145280297Sjkim{
146280297Sjkim    const char *s = src;
147280297Sjkim    char *d = dst;
148280297Sjkim
14955714Skris    while (len--)
150280297Sjkim        *d++ = *s++;
151280297Sjkim}
152280297Sjkim
15355714Skrisstatic inline int
154280297Sjkimstrcmp(const char *s1, const char *s2)
155280297Sjkim{
156280297Sjkim    for (; *s1 == *s2 && *s1; s1++, s2++);
157280297Sjkim    return (unsigned char)*s1 - (unsigned char)*s2;
158280297Sjkim}
159280297Sjkim
160280297Sjkim#include "ufsread.c"
161280297Sjkim
162280297Sjkimstatic int
163280297Sjkimxfsread(ino_t inode, void *buf, size_t nbyte)
164280297Sjkim{
165280297Sjkim    if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
166280297Sjkim	printf("Invalid %s\n", "format");
167280297Sjkim	return -1;
168280297Sjkim    }
169280297Sjkim    return 0;
170280297Sjkim}
171280297Sjkim
17255714Skrisstatic inline uint32_t
17368651Skrismemsize(void)
174280297Sjkim{
175280297Sjkim    v86.addr = MEM_EXT;
176280297Sjkim    v86.eax = 0x8800;
177280297Sjkim    v86int();
17855714Skris    return v86.eax;
179280297Sjkim}
180280297Sjkim
181280297Sjkimstatic inline void
182280297Sjkimgetstr(void)
183280297Sjkim{
18455714Skris    char *s;
185280297Sjkim    int c;
186280297Sjkim
187280297Sjkim    s = cmd;
188280297Sjkim    for (;;) {
18959191Skris	switch (c = xgetc(0)) {
190280297Sjkim	case 0:
191280297Sjkim	    break;
192280297Sjkim	case '\177':
193280297Sjkim	case '\b':
194280297Sjkim	    if (s > cmd) {
195280297Sjkim		s--;
196280297Sjkim		printf("\b \b");
197280297Sjkim	    }
198280297Sjkim	    break;
19955714Skris	case '\n':
20068651Skris	case '\r':
201280297Sjkim	    *s = 0;
202280297Sjkim	    return;
203280297Sjkim	default:
20455714Skris	    if (s - cmd < sizeof(cmd) - 1)
205280297Sjkim		*s++ = c;
20655714Skris	    putchar(c);
207280297Sjkim	}
208280297Sjkim    }
209280297Sjkim}
210280297Sjkim
211280297Sjkimstatic inline void
212280297Sjkimputc(int c)
213280297Sjkim{
214280297Sjkim    v86.addr = 0x10;
215280297Sjkim    v86.eax = 0xe00 | (c & 0xff);
216280297Sjkim    v86.ebx = 0x7;
217280297Sjkim    v86int();
218280297Sjkim}
219280297Sjkim
220280297Sjkimint
221280297Sjkimmain(void)
222280297Sjkim{
223280297Sjkim    int autoboot;
224280297Sjkim    ino_t ino;
225280297Sjkim
226280297Sjkim    dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
227280297Sjkim    v86.ctl = V86_FLAGS;
228280297Sjkim    dsk.drive = *(uint8_t *)PTOV(ARGS);
229280297Sjkim    dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
230280297Sjkim    dsk.unit = dsk.drive & DRV_MASK;
231280297Sjkim    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
232280297Sjkim    bootinfo.bi_version = BOOTINFO_VERSION;
233280297Sjkim    bootinfo.bi_size = sizeof(bootinfo);
234280297Sjkim    bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or kernel */
235280297Sjkim    bootinfo.bi_extmem = memsize();
236280297Sjkim    bootinfo.bi_memsizes_valid++;
237280297Sjkim
238280297Sjkim    /* Process configuration file */
239280297Sjkim
240280297Sjkim    autoboot = 1;
241280297Sjkim
242280297Sjkim    if ((ino = lookup(PATH_CONFIG)))
243280297Sjkim	fsread(ino, cmd, sizeof(cmd));
244280297Sjkim
245280297Sjkim    if (*cmd) {
246280297Sjkim	printf("%s: %s", PATH_CONFIG, cmd);
247280297Sjkim	if (parse())
248280297Sjkim	    autoboot = 0;
249280297Sjkim	/* Do not process this command twice */
25055714Skris	*cmd = 0;
251280297Sjkim    }
252280297Sjkim
253280297Sjkim    /*
254280297Sjkim     * Try to exec stage 3 boot loader. If interrupted by a keypress,
255280297Sjkim     * or in case of failure, try to load a kernel directly instead.
256280297Sjkim     */
257280297Sjkim
258280297Sjkim    if (autoboot && !*kname) {
259280297Sjkim	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
260280297Sjkim	if (!keyhit(3*SECOND)) {
261280297Sjkim	    load();
262280297Sjkim	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
263280297Sjkim	}
264280297Sjkim    }
265280297Sjkim
266280297Sjkim    /* Present the user with the boot2 prompt. */
267280297Sjkim
268280297Sjkim    for (;;) {
26955714Skris	printf("\nFreeBSD/i386 boot\n"
27055714Skris	       "Default: %u:%s(%u,%c)%s\n"
271280297Sjkim	       "boot: ",
272280297Sjkim	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
273280297Sjkim	       'a' + dsk.part, kname);
274280297Sjkim	if (ioctrl & IO_SERIAL)
275280297Sjkim	    sio_flush();
27655714Skris	if (!autoboot || keyhit(5*SECOND))
277280297Sjkim	    getstr();
278280297Sjkim	else
279280297Sjkim	    putchar('\n');
280280297Sjkim	autoboot = 0;
281280297Sjkim	if (parse())
282280297Sjkim	    putchar('\a');
283280297Sjkim	else
284280297Sjkim	    load();
285280297Sjkim    }
286280297Sjkim}
287280297Sjkim
288280297Sjkim/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
289280297Sjkimvoid
290280297Sjkimexit(int x)
291280297Sjkim{
292194206Ssimon}
293280297Sjkim
294280297Sjkimstatic void
295280297Sjkimload(void)
296280297Sjkim{
297194206Ssimon    union {
298280297Sjkim	struct exec ex;
299280297Sjkim	Elf32_Ehdr eh;
300280297Sjkim    } hdr;
301280297Sjkim    Elf32_Phdr ep[2];
302280297Sjkim    Elf32_Shdr es[2];
303280297Sjkim    caddr_t p;
30455714Skris    ino_t ino;
30568651Skris    uint32_t addr, x;
306280297Sjkim    int fmt, i, j;
307280297Sjkim
30855714Skris    if (!(ino = lookup(kname))) {
309280297Sjkim	if (!ls)
310280297Sjkim	    printf("No %s\n", kname);
311280297Sjkim	return;
312280297Sjkim    }
313280297Sjkim    if (xfsread(ino, &hdr, sizeof(hdr)))
314	return;
315    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
316	fmt = 0;
317    else if (IS_ELF(hdr.eh))
318	fmt = 1;
319    else {
320	printf("Invalid %s\n", "format");
321	return;
322    }
323    if (fmt == 0) {
324	addr = hdr.ex.a_entry;
325	p = PTOV(addr);
326	fs_off = PAGE_SIZE;
327	if (xfsread(ino, p, hdr.ex.a_text))
328	    return;
329	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
330	if (xfsread(ino, p, hdr.ex.a_data))
331	    return;
332	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
333	bootinfo.bi_symtab = VTOP(p);
334	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
335	p += sizeof(hdr.ex.a_syms);
336	if (hdr.ex.a_syms) {
337	    if (xfsread(ino, p, hdr.ex.a_syms))
338		return;
339	    p += hdr.ex.a_syms;
340	    if (xfsread(ino, p, sizeof(int)))
341		return;
342	    x = *(uint32_t *)p;
343	    p += sizeof(int);
344	    x -= sizeof(int);
345	    if (xfsread(ino, p, x))
346		return;
347	    p += x;
348	}
349    } else {
350	fs_off = hdr.eh.e_phoff;
351	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
352	    if (xfsread(ino, ep + j, sizeof(ep[0])))
353		return;
354	    if (ep[j].p_type == PT_LOAD)
355		j++;
356	}
357	for (i = 0; i < 2; i++) {
358	    p = PTOV(ep[i].p_paddr);
359	    fs_off = ep[i].p_offset;
360	    if (xfsread(ino, p, ep[i].p_filesz))
361		return;
362	}
363	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
364	bootinfo.bi_symtab = VTOP(p);
365	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
366	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
367		(hdr.eh.e_shstrndx + 1);
368	    if (xfsread(ino, &es, sizeof(es)))
369		return;
370	    for (i = 0; i < 2; i++) {
371		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
372		p += sizeof(es[i].sh_size);
373		fs_off = es[i].sh_offset;
374		if (xfsread(ino, p, es[i].sh_size))
375		    return;
376		p += es[i].sh_size;
377	    }
378	}
379	addr = hdr.eh.e_entry;
380    }
381    bootinfo.bi_esymtab = VTOP(p);
382    bootinfo.bi_kernelname = VTOP(kname);
383    bootinfo.bi_bios_dev = dsk.drive;
384    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
385	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
386	   0, 0, 0, VTOP(&bootinfo));
387}
388
389static int
390parse()
391{
392    char *arg = cmd;
393    char *p, *q;
394    unsigned int drv;
395    int c, i;
396
397    while ((c = *arg++)) {
398	if (c == ' ' || c == '\t' || c == '\n')
399	    continue;
400	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
401	if (*p)
402	    *p++ = 0;
403	if (c == '-') {
404	    while ((c = *arg++)) {
405		for (i = 0; c != optstr[i]; i++)
406		    if (i == NOPT - 1)
407			return -1;
408		opts ^= 1 << flags[i];
409	    }
410	    if (opts & 1 << RBX_PROBEKBD) {
411		i = *(uint8_t *)PTOV(0x496) & 0x10;
412		printf("Keyboard: %s\n", i ? "yes" : "no");
413		if (!i)
414		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
415		opts &= ~(1 << RBX_PROBEKBD);
416	    }
417	    ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
418		     opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
419	    if (ioctrl & IO_SERIAL)
420	        sio_init();
421	} else {
422	    for (q = arg--; *q && *q != '('; q++);
423	    if (*q) {
424		drv = -1;
425		if (arg[1] == ':') {
426		    drv = *arg - '0';
427		    if (drv > 9)
428			return (-1);
429		    arg += 2;
430		}
431		if (q - arg != 2)
432		    return -1;
433		for (i = 0; arg[0] != dev_nm[i][0] ||
434			    arg[1] != dev_nm[i][1]; i++)
435		    if (i == NDEV - 1)
436			return -1;
437		dsk.type = i;
438		arg += 3;
439		dsk.unit = *arg - '0';
440		if (arg[1] != ',' || dsk.unit > 9)
441		    return -1;
442		arg += 2;
443		dsk.slice = WHOLE_DISK_SLICE;
444		if (arg[1] == ',') {
445		    dsk.slice = *arg - '0' + 1;
446		    if (dsk.slice > NDOSPART)
447			return -1;
448		    arg += 2;
449		}
450		if (arg[1] != ')')
451		    return -1;
452		dsk.part = *arg - 'a';
453		if (dsk.part > 7)
454		    return (-1);
455		arg += 2;
456		if (drv == -1)
457		    drv = dsk.unit;
458		dsk.drive = (dsk.type <= TYPE_MAXHARD
459			     ? DRV_HARD : 0) + drv;
460		dsk_meta = 0;
461	    }
462	    if ((i = p - arg - !*(p - 1))) {
463		if ((size_t)i >= sizeof(kname))
464		    return -1;
465		memcpy(kname, arg, i + 1);
466	    }
467	}
468	arg = p;
469    }
470    return 0;
471}
472
473static int
474dskread(void *buf, unsigned lba, unsigned nblk)
475{
476    struct dos_partition *dp;
477    struct disklabel *d;
478    char *sec;
479    unsigned sl, i;
480
481    if (!dsk_meta) {
482	sec = dmadat->secbuf;
483	dsk.start = 0;
484	if (drvread(sec, DOSBBSECTOR, 1))
485	    return -1;
486	dp = (void *)(sec + DOSPARTOFF);
487	sl = dsk.slice;
488	if (sl < BASE_SLICE) {
489	    for (i = 0; i < NDOSPART; i++)
490		if (dp[i].dp_typ == DOSPTYP_386BSD &&
491		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
492		    sl = BASE_SLICE + i;
493		    if (dp[i].dp_flag & 0x80 ||
494			dsk.slice == COMPATIBILITY_SLICE)
495			break;
496		}
497	    if (dsk.slice == WHOLE_DISK_SLICE)
498		dsk.slice = sl;
499	}
500	if (sl != WHOLE_DISK_SLICE) {
501	    if (sl != COMPATIBILITY_SLICE)
502		dp += sl - BASE_SLICE;
503	    if (dp->dp_typ != DOSPTYP_386BSD) {
504		printf("Invalid %s\n", "slice");
505		return -1;
506	    }
507	    dsk.start = dp->dp_start;
508	}
509	if (drvread(sec, dsk.start + LABELSECTOR, 1))
510		return -1;
511	d = (void *)(sec + LABELOFFSET);
512	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
513	    if (dsk.part != RAW_PART) {
514		printf("Invalid %s\n", "label");
515		return -1;
516	    }
517	} else {
518	    if (!dsk.init) {
519		if (d->d_type == DTYPE_SCSI)
520		    dsk.type = TYPE_DA;
521		dsk.init++;
522	    }
523	    if (dsk.part >= d->d_npartitions ||
524		!d->d_partitions[dsk.part].p_size) {
525		printf("Invalid %s\n", "partition");
526		return -1;
527	    }
528	    dsk.start += d->d_partitions[dsk.part].p_offset;
529	    dsk.start -= d->d_partitions[RAW_PART].p_offset;
530	}
531    }
532    return drvread(buf, dsk.start + lba, nblk);
533}
534
535static void
536printf(const char *fmt,...)
537{
538    va_list ap;
539    char buf[10];
540    char *s;
541    unsigned u;
542    int c;
543
544    va_start(ap, fmt);
545    while ((c = *fmt++)) {
546	if (c == '%') {
547	    c = *fmt++;
548	    switch (c) {
549	    case 'c':
550		putchar(va_arg(ap, int));
551		continue;
552	    case 's':
553		for (s = va_arg(ap, char *); *s; s++)
554		    putchar(*s);
555		continue;
556	    case 'u':
557		u = va_arg(ap, unsigned);
558		s = buf;
559		do
560		    *s++ = '0' + u % 10U;
561		while (u /= 10U);
562		while (--s >= buf)
563		    putchar(*s);
564		continue;
565	    }
566	}
567	putchar(c);
568    }
569    va_end(ap);
570    return;
571}
572
573static void
574putchar(int c)
575{
576    if (c == '\n')
577	xputc('\r');
578    xputc(c);
579}
580
581static int
582drvread(void *buf, unsigned lba, unsigned nblk)
583{
584    static unsigned c = 0x2d5c7c2f;
585
586    printf("%c\b", c = c << 8 | c >> 24);
587    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
588    v86.addr = XREADORG;		/* call to xread in boot1 */
589    v86.es = VTOPSEG(buf);
590    v86.eax = lba;
591    v86.ebx = VTOPOFF(buf);
592    v86.ecx = lba >> 16;
593    v86.edx = nblk << 8 | dsk.drive;
594    v86int();
595    v86.ctl = V86_FLAGS;
596    if (V86_CY(v86.efl)) {
597	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
598	return -1;
599    }
600    return 0;
601}
602
603static int
604keyhit(unsigned ticks)
605{
606    uint32_t t0, t1;
607
608    if (opts & 1 << RBX_NOINTR)
609	return 0;
610    t0 = 0;
611    for (;;) {
612	if (xgetc(1))
613	    return 1;
614	t1 = *(uint32_t *)PTOV(0x46c);
615	if (!t0)
616	    t0 = t1;
617	if (t1 < t0 || t1 >= t0 + ticks)
618	    return 0;
619    }
620}
621
622static int
623xputc(int c)
624{
625    if (ioctrl & IO_KEYBOARD)
626	putc(c);
627    if (ioctrl & IO_SERIAL)
628	sio_putc(c);
629    return c;
630}
631
632static int
633xgetc(int fn)
634{
635    if (opts & 1 << RBX_NOINTR)
636	return 0;
637    for (;;) {
638	if (ioctrl & IO_KEYBOARD && getc(1))
639	    return fn ? 1 : getc(0);
640	if (ioctrl & IO_SERIAL && sio_ischar())
641	    return fn ? 1 : sio_getc();
642	if (fn)
643	    return 0;
644    }
645}
646
647static int
648getc(int fn)
649{
650    v86.addr = 0x16;
651    v86.eax = fn << 8;
652    v86int();
653    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
654}
655