gptboot.c revision 89154
1/*
2 * Copyright (c) 1998 Robert Nordier
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are freely
6 * permitted provided that the above copyright notice and this
7 * paragraph and the following disclaimer are duplicated in all
8 * such forms.
9 *
10 * This software is provided "AS IS" and without any express or
11 * implied warranties, including, without limitation, the implied
12 * warranties of merchantability and fitness for a particular
13 * purpose.
14 */
15
16/*
17 * $FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 89154 2002-01-09 15:46:17Z iedowse $
18 */
19
20#include <sys/param.h>
21#include <sys/reboot.h>
22#include <sys/diskslice.h>
23#include <sys/disklabel.h>
24#include <sys/dirent.h>
25#include <machine/bootinfo.h>
26#include <machine/elf.h>
27
28#include <ufs/ffs/fs.h>
29#include <ufs/ufs/dinode.h>
30
31#include <stdarg.h>
32
33#include <a.out.h>
34
35#include <btxv86.h>
36
37#include "boot2.h"
38#include "lib.h"
39
40#define RBX_ASKNAME	0x0	/* -a */
41#define RBX_SINGLE	0x1	/* -s */
42#define RBX_DFLTROOT	0x5	/* -r */
43#define RBX_KDB 	0x6	/* -d */
44#define RBX_CONFIG	0xa	/* -c */
45#define RBX_VERBOSE	0xb	/* -v */
46#define RBX_SERIAL	0xc	/* -h */
47#define RBX_CDROM	0xd	/* -C */
48#define RBX_GDB 	0xf	/* -g */
49#define RBX_DUAL	0x1d	/* -D */
50#define RBX_PROBEKBD	0x1e	/* -P */
51
52#define RBX_MASK	0x2000ffff
53
54#define PATH_CONFIG	"/boot.config"
55#define PATH_BOOT3	"/boot/loader"
56#define PATH_KERNEL	"/kernel"
57
58#define ARGS		0x900
59#define NOPT		11
60#define BSIZEMAX	16384
61#define NDEV		5
62#define MEM_BASE	0x12
63#define MEM_EXT 	0x15
64#define V86_CY(x)	((x) & 1)
65#define V86_ZR(x)	((x) & 0x40)
66
67#define DRV_HARD	0x80
68#define DRV_MASK	0x7f
69
70#define TYPE_AD		0
71#define TYPE_WD		1
72#define TYPE_WFD 	2
73#define TYPE_FD		3
74#define TYPE_DA		4
75
76extern uint32_t _end;
77
78static const char optstr[NOPT] = "DhaCcdgPrsv";
79static const unsigned char flags[NOPT] = {
80    RBX_DUAL,
81    RBX_SERIAL,
82    RBX_ASKNAME,
83    RBX_CDROM,
84    RBX_CONFIG,
85    RBX_KDB,
86    RBX_GDB,
87    RBX_PROBEKBD,
88    RBX_DFLTROOT,
89    RBX_SINGLE,
90    RBX_VERBOSE
91};
92
93static const char *const dev_nm[] = {"ad", "wd", "  ", "fd", "da"};
94static const unsigned dev_maj[] = {30, 0, 1, 2, 4};
95
96static struct dsk {
97    unsigned drive;
98    unsigned type;
99    unsigned unit;
100    unsigned slice;
101    unsigned part;
102    unsigned start;
103    int init;
104    int meta;
105} dsk;
106static char cmd[512];
107static char kname[1024];
108static uint32_t opts;
109static struct bootinfo bootinfo;
110static int ls;
111static uint32_t fs_off;
112static uint8_t ioctrl = 0x1;
113
114void exit(int);
115static void load(const char *);
116static int parse(char *);
117static ino_t lookup(const char *);
118static int xfsread(ino_t, void *, size_t);
119static ssize_t fsread(ino_t, void *, size_t);
120static int dskread(void *, unsigned, unsigned);
121static int printf(const char *,...);
122static int putchar(int);
123static void *memcpy(void *, const void *, size_t);
124static void *malloc(size_t);
125static uint32_t memsize(int);
126static int drvread(void *, unsigned, unsigned);
127static int keyhit(unsigned);
128static int xputc(int);
129static int xgetc(int);
130static int getc(int);
131
132static inline void
133readfile(const char *fname, void *buf, size_t size)
134{
135    ino_t ino;
136
137    if ((ino = lookup(fname)))
138	fsread(ino, buf, size);
139}
140
141static inline int
142strcmp(const char *s1, const char *s2)
143{
144    for (; *s1 == *s2 && *s1; s1++, s2++);
145    return (u_char)*s1 - (u_char)*s2;
146}
147
148static inline int
149fsfind(const char *name, ino_t * ino)
150{
151    char buf[DEV_BSIZE];
152    struct dirent *d;
153    char *s;
154    ssize_t n;
155
156    fs_off = 0;
157    while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0)
158	for (s = buf; s < buf + DEV_BSIZE;) {
159	    d = (void *)s;
160	    if (ls)
161		printf("%s ", d->d_name);
162	    else if (!strcmp(name, d->d_name)) {
163		*ino = d->d_fileno;
164		return d->d_type;
165	    }
166	    s += d->d_reclen;
167	}
168    if (n != -1 && ls)
169	putchar('\n');
170    return 0;
171}
172
173static inline int
174getchar(void)
175{
176    int c;
177
178    c = xgetc(0);
179    if (c == '\r')
180	c = '\n';
181    return c;
182}
183
184static inline void
185getstr(char *str, int size)
186{
187    char *s;
188    int c;
189
190    s = str;
191    do {
192	switch (c = getchar()) {
193	case 0:
194	    break;
195	case '\b':
196	case '\177':
197	    if (s > str) {
198		s--;
199		putchar('\b');
200		putchar(' ');
201	    } else
202		c = 0;
203	    break;
204	case '\n':
205	    *s = 0;
206	    break;
207	default:
208	    if (s - str < size - 1)
209		*s++ = c;
210	}
211	if (c)
212	    putchar(c);
213    } while (c != '\n');
214}
215
216static inline uint32_t
217drvinfo(int drive)
218{
219    v86.addr = 0x13;
220    v86.eax = 0x800;
221    v86.edx = DRV_HARD + drive;
222    v86int();
223    if (V86_CY(v86.efl))
224	return 0x4f010f;
225    return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
226	   (v86.edx & 0xff00) | (v86.ecx & 0x3f);
227}
228
229static inline void
230putc(int c)
231{
232    v86.addr = 0x10;
233    v86.eax = 0xe00 | (c & 0xff);
234    v86.ebx = 0x7;
235    v86int();
236}
237
238int
239main(void)
240{
241    int autoboot, i;
242
243    v86.ctl = V86_FLAGS;
244    dsk.drive = *(uint8_t *)PTOV(ARGS);
245    dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
246    dsk.unit = dsk.drive & DRV_MASK;
247    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
248    bootinfo.bi_version = BOOTINFO_VERSION;
249    bootinfo.bi_size = sizeof(bootinfo);
250    bootinfo.bi_basemem = memsize(MEM_BASE);
251    bootinfo.bi_extmem = memsize(MEM_EXT);
252    bootinfo.bi_memsizes_valid++;
253    for (i = 0; i < N_BIOS_GEOM; i++)
254	bootinfo.bi_bios_geom[i] = drvinfo(i);
255    autoboot = 2;
256    readfile(PATH_CONFIG, cmd, sizeof(cmd));
257    if (*cmd) {
258	printf("%s: %s", PATH_CONFIG, cmd);
259	if (parse(cmd))
260	    autoboot = 0;
261	*cmd = 0;
262    }
263    if (autoboot && !*kname) {
264	if (autoboot == 2) {
265	    memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
266	    if (!keyhit(0x37)) {
267		load(kname);
268		autoboot = 1;
269	    }
270	}
271	if (autoboot == 1)
272	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
273    }
274    for (;;) {
275	printf(" \n>> FreeBSD/i386 BOOT\n"
276	       "Default: %u:%s(%u,%c)%s\n"
277	       "boot: ",
278	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
279	       'a' + dsk.part, kname);
280	if (ioctrl & 0x2)
281	    sio_flush();
282	if (!autoboot || keyhit(0x5a))
283	    getstr(cmd, sizeof(cmd));
284	else
285	    putchar('\n');
286	autoboot = 0;
287	if (parse(cmd))
288	    putchar('\a');
289	else
290	    load(kname);
291    }
292}
293
294/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
295void
296exit(int x)
297{
298}
299
300static void
301load(const char *fname)
302{
303    union {
304	struct exec ex;
305	Elf32_Ehdr eh;
306    } hdr;
307    Elf32_Phdr ep[2];
308    Elf32_Shdr es[2];
309    caddr_t p;
310    ino_t ino;
311    uint32_t addr, x;
312    int fmt, i, j;
313
314    if (!(ino = lookup(fname))) {
315	if (!ls)
316	    printf("No %s\n", fname);
317	return;
318    }
319    if (xfsread(ino, &hdr, sizeof(hdr)))
320	return;
321    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
322	fmt = 0;
323    else if (IS_ELF(hdr.eh))
324	fmt = 1;
325    else {
326	printf("Invalid %s\n", "format");
327	return;
328    }
329    if (fmt == 0) {
330	addr = hdr.ex.a_entry & 0xffffff;
331	p = PTOV(addr);
332	fs_off = PAGE_SIZE;
333	if (xfsread(ino, p, hdr.ex.a_text))
334	    return;
335	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
336	if (xfsread(ino, p, hdr.ex.a_data))
337	    return;
338	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
339	bootinfo.bi_symtab = VTOP(p);
340	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
341	p += sizeof(hdr.ex.a_syms);
342	if (hdr.ex.a_syms) {
343	    if (xfsread(ino, p, hdr.ex.a_syms))
344		return;
345	    p += hdr.ex.a_syms;
346	    if (xfsread(ino, p, sizeof(int)))
347		return;
348	    x = *(uint32_t *)p;
349	    p += sizeof(int);
350	    x -= sizeof(int);
351	    if (xfsread(ino, p, x))
352		return;
353	    p += x;
354	}
355    } else {
356	fs_off = hdr.eh.e_phoff;
357	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
358	    if (xfsread(ino, ep + j, sizeof(ep[0])))
359		return;
360	    if (ep[j].p_type == PT_LOAD)
361		j++;
362	}
363	for (i = 0; i < 2; i++) {
364	    p = PTOV(ep[i].p_paddr & 0xffffff);
365	    fs_off = ep[i].p_offset;
366	    if (xfsread(ino, p, ep[i].p_filesz))
367		return;
368	}
369	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
370	bootinfo.bi_symtab = VTOP(p);
371	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
372	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
373		(hdr.eh.e_shstrndx + 1);
374	    if (xfsread(ino, &es, sizeof(es)))
375		return;
376	    for (i = 0; i < 2; i++) {
377		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
378		p += sizeof(es[i].sh_size);
379		fs_off = es[i].sh_offset;
380		if (xfsread(ino, p, es[i].sh_size))
381		    return;
382		p += es[i].sh_size;
383	    }
384	}
385	addr = hdr.eh.e_entry & 0xffffff;
386    }
387    bootinfo.bi_esymtab = VTOP(p);
388    bootinfo.bi_kernelname = VTOP(fname);
389    bootinfo.bi_bios_dev = dsk.drive;
390    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
391	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
392	   0, 0, 0, VTOP(&bootinfo));
393}
394
395static int
396parse(char *arg)
397{
398    char *p, *q;
399    int drv, c, i;
400
401    while ((c = *arg++)) {
402	if (c == ' ' || c == '\t' || c == '\n')
403	    continue;
404	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
405	if (*p)
406	    *p++ = 0;
407	if (c == '-') {
408	    while ((c = *arg++)) {
409		for (i = 0; c != optstr[i]; i++)
410		    if (i == NOPT - 1)
411			return -1;
412		opts ^= 1 << flags[i];
413	    }
414	    if (opts & 1 << RBX_PROBEKBD) {
415		i = *(uint8_t *)PTOV(0x496) & 0x10;
416		printf("Keyboard: %s\n", i ? "yes" : "no");
417		if (!i)
418		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
419		opts &= ~(1 << RBX_PROBEKBD);
420	    }
421	    ioctrl = opts & 1 << RBX_DUAL ? 0x3 :
422		     opts & 1 << RBX_SERIAL ? 0x2 : 0x1;
423	    if (ioctrl & 0x2)
424	        sio_init();
425	} else {
426	    for (q = arg--; *q && *q != '('; q++);
427	    if (*q) {
428		drv = -1;
429		if (arg[1] == ':') {
430		    if (*arg < '0' || *arg > '9')
431			return -1;
432		    drv = *arg - '0';
433		    arg += 2;
434		}
435		if (q - arg != 2)
436		    return -1;
437		for (i = 0; arg[0] != dev_nm[i][0] ||
438			    arg[1] != dev_nm[i][1]; i++)
439		    if (i == NDEV - 1)
440			return -1;
441		dsk.type = i;
442		arg += 3;
443		if (arg[1] != ',' || *arg < '0' || *arg > '9')
444		    return -1;
445		dsk.unit = *arg - '0';
446		arg += 2;
447		dsk.slice = WHOLE_DISK_SLICE;
448		if (arg[1] == ',') {
449		    if (*arg < '0' || *arg > '0' + NDOSPART)
450			return -1;
451		    if ((dsk.slice = *arg - '0'))
452			dsk.slice++;
453		    arg += 2;
454		}
455		if (arg[1] != ')' || *arg < 'a' || *arg > 'p')
456		    return -1;
457		dsk.part = *arg - 'a';
458		arg += 2;
459		if (drv == -1)
460		    drv = dsk.unit;
461		dsk.drive = (dsk.type == TYPE_WD ||
462			     dsk.type == TYPE_AD ||
463			     dsk.type == TYPE_DA ? DRV_HARD : 0) + drv;
464		dsk.meta = 0;
465		fsread(0, NULL, 0);
466	    }
467	    if ((i = p - arg - !*(p - 1))) {
468		if (i >= sizeof(kname))
469		    return -1;
470		memcpy(kname, arg, i + 1);
471	    }
472	}
473	arg = p;
474    }
475    return 0;
476}
477
478static ino_t
479lookup(const char *path)
480{
481    char name[MAXNAMLEN + 1];
482    const char *s;
483    ino_t ino;
484    ssize_t n;
485    int dt;
486
487    ino = ROOTINO;
488    dt = DT_DIR;
489    for (;;) {
490	if (*path == '/')
491	    path++;
492	if (!*path)
493	    break;
494	for (s = path; *s && *s != '/'; s++);
495	if ((n = s - path) > MAXNAMLEN)
496	    return 0;
497	ls = *path == '?' && n == 1 && !*s;
498	memcpy(name, path, n);
499	name[n] = 0;
500	if ((dt = fsfind(name, &ino)) <= 0)
501	    break;
502	path = s;
503    }
504    return dt == DT_REG ? ino : 0;
505}
506static int
507xfsread(ino_t inode, void *buf, size_t nbyte)
508{
509    if (fsread(inode, buf, nbyte) != nbyte) {
510	printf("Invalid %s\n", "format");
511	return -1;
512    }
513    return 0;
514}
515
516static ssize_t
517fsread(ino_t inode, void *buf, size_t nbyte)
518{
519    static struct fs fs;
520    static struct dinode din;
521    static char *blkbuf;
522    static ufs_daddr_t *indbuf;
523    static ino_t inomap;
524    static ufs_daddr_t blkmap, indmap;
525    static unsigned fsblks;
526    char *s;
527    ufs_daddr_t lbn, addr;
528    size_t n, nb, off;
529
530    if (!dsk.meta) {
531	if (!blkbuf)
532	    blkbuf = malloc(BSIZEMAX);
533	inomap = 0;
534	if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE))
535	    return -1;
536	memcpy(&fs, blkbuf, sizeof(fs));
537	if (fs.fs_magic != FS_MAGIC) {
538	    printf("Not ufs\n");
539	    return -1;
540	}
541	fsblks = fs.fs_bsize >> DEV_BSHIFT;
542	dsk.meta++;
543    }
544    if (!inode)
545	return 0;
546    if (inomap != inode) {
547	if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)),
548		    fsblks))
549	    return -1;
550	din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)];
551	inomap = inode;
552	fs_off = 0;
553	blkmap = indmap = 0;
554    }
555    s = buf;
556    if (nbyte > (n = din.di_size - fs_off))
557	nbyte = n;
558    nb = nbyte;
559    while (nb) {
560	lbn = lblkno(&fs, fs_off);
561	if (lbn < NDADDR)
562	    addr = din.di_db[lbn];
563	else {
564	    if (indmap != din.di_ib[0]) {
565		if (!indbuf)
566		    indbuf = malloc(BSIZEMAX);
567		if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]),
568			    fsblks))
569		    return -1;
570		indmap = din.di_ib[0];
571	    }
572	    addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)];
573	}
574	n = dblksize(&fs, &din, lbn);
575	if (blkmap != addr) {
576	    if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT))
577		return -1;
578	    blkmap = addr;
579	}
580	off = blkoff(&fs, fs_off);
581	n -= off;
582	if (n > nb)
583	    n = nb;
584	memcpy(s, blkbuf + off, n);
585	s += n;
586	fs_off += n;
587	nb -= n;
588    }
589    return nbyte;
590}
591
592static int
593dskread(void *buf, unsigned lba, unsigned nblk)
594{
595    static char *sec;
596    struct dos_partition *dp;
597    struct disklabel *d;
598    unsigned sl, i;
599
600    if (!dsk.meta) {
601	if (!sec)
602	    sec = malloc(DEV_BSIZE);
603	dsk.start = 0;
604	if (drvread(sec, DOSBBSECTOR, 1))
605	    return -1;
606	dp = (void *)(sec + DOSPARTOFF);
607	sl = dsk.slice;
608	if (sl < BASE_SLICE) {
609	    for (i = 0; i < NDOSPART; i++)
610		if (dp[i].dp_typ == DOSPTYP_386BSD &&
611		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
612		    sl = BASE_SLICE + i;
613		    if (dp[i].dp_flag & 0x80 ||
614			dsk.slice == COMPATIBILITY_SLICE)
615			break;
616		}
617	    if (dsk.slice == WHOLE_DISK_SLICE)
618		dsk.slice = sl;
619	}
620	if (sl != WHOLE_DISK_SLICE) {
621	    if (sl != COMPATIBILITY_SLICE)
622		dp += sl - BASE_SLICE;
623	    if (dp->dp_typ != DOSPTYP_386BSD) {
624		printf("Invalid %s\n", "slice");
625		return -1;
626	    }
627	    dsk.start = dp->dp_start;
628	}
629	if (drvread(sec, dsk.start + LABELSECTOR, 1))
630		return -1;
631	d = (void *)(sec + LABELOFFSET);
632	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
633	    if (dsk.part != RAW_PART) {
634		printf("Invalid %s\n", "label");
635		return -1;
636	    }
637	} else {
638	    if (!dsk.init) {
639		if (d->d_type == DTYPE_SCSI)
640		    dsk.type = TYPE_DA;
641		dsk.init++;
642	    }
643	    if (dsk.part >= d->d_npartitions ||
644		!d->d_partitions[dsk.part].p_size) {
645		printf("Invalid %s\n", "partition");
646		return -1;
647	    }
648	    dsk.start = d->d_partitions[dsk.part].p_offset;
649	}
650    }
651    return drvread(buf, dsk.start + lba, nblk);
652}
653
654static int
655printf(const char *fmt,...)
656{
657    static const char digits[16] = "0123456789abcdef";
658    va_list ap;
659    char buf[10];
660    char *s;
661    unsigned r, u;
662    int c;
663
664    va_start(ap, fmt);
665    while ((c = *fmt++)) {
666	if (c == '%') {
667	    c = *fmt++;
668	    switch (c) {
669	    case 'c':
670		putchar(va_arg(ap, int));
671		continue;
672	    case 's':
673		for (s = va_arg(ap, char *); *s; s++)
674		    putchar(*s);
675		continue;
676	    case 'u':
677	    case 'x':
678		r = c == 'u' ? 10U : 16U;
679		u = va_arg(ap, unsigned);
680		s = buf;
681		do
682		    *s++ = digits[u % r];
683		while (u /= r);
684		while (--s >= buf)
685		    putchar(*s);
686		continue;
687	    }
688	}
689	putchar(c);
690    }
691    va_end(ap);
692    return 0;
693}
694
695static int
696putchar(int c)
697{
698    if (c == '\n')
699	xputc('\r');
700    return xputc(c);
701}
702
703static void *
704memcpy(void *dst, const void *src, size_t size)
705{
706    const char *s;
707    char *d;
708
709    for (d = dst, s = src; size; size--)
710	*d++ = *s++;
711    return dst;
712}
713
714static void *
715malloc(size_t size)
716{
717    static uint32_t next;
718    void *p;
719
720    if (!next)
721	next = roundup2(__base + _end, 0x10000) - __base;
722    p = (void *)next;
723    next += size;
724    return p;
725}
726
727static uint32_t
728memsize(int type)
729{
730    v86.addr = type;
731    v86.eax = 0x8800;
732    v86int();
733    return v86.eax;
734}
735
736static int
737drvread(void *buf, unsigned lba, unsigned nblk)
738{
739    static unsigned c = 0x2d5c7c2f;
740
741    printf("%c\b", c = c << 8 | c >> 24);
742    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
743    v86.addr = XREADORG;		/* call to xread in boot1 */
744    v86.es = VTOPSEG(buf);
745    v86.eax = lba;
746    v86.ebx = VTOPOFF(buf);
747    v86.ecx = lba >> 16;
748    v86.edx = nblk << 8 | dsk.drive;
749    v86int();
750    v86.ctl = V86_FLAGS;
751    if (V86_CY(v86.efl)) {
752	printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff,
753	       lba);
754	return -1;
755    }
756    return 0;
757}
758
759static int
760keyhit(unsigned ticks)
761{
762    uint32_t t0, t1;
763
764    t0 = 0;
765    for (;;) {
766	if (xgetc(1))
767	    return 1;
768	t1 = *(uint32_t *)PTOV(0x46c);
769	if (!t0)
770	    t0 = t1;
771	if (t1 < t0 || t1 >= t0 + ticks)
772	    return 0;
773    }
774}
775
776static int
777xputc(int c)
778{
779    if (ioctrl & 0x1)
780	putc(c);
781    if (ioctrl & 0x2)
782	sio_putc(c);
783    return c;
784}
785
786static int
787xgetc(int fn)
788{
789    for (;;) {
790	if (ioctrl & 0x1 && getc(1))
791	    return fn ? 1 : getc(0);
792	if (ioctrl & 0x2 && sio_ischar())
793	    return fn ? 1 : sio_getc();
794	if (fn)
795	    return 0;
796    }
797}
798
799static int
800getc(int fn)
801{
802    v86.addr = 0x16;
803    v86.eax = fn << 8;
804    v86int();
805    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
806}
807