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