boot2.c revision 40477
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.11 1998/10/17 10:35:15 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		opts &= ~(1 << RBX_PROBEKBD);
332	    }
333	    ioctrl = opts & 1 << RBX_DUAL ? 0x3 :
334		     opts & 1 << RBX_SERIAL ? 0x2 : 0x1;
335	    if (ioctrl & 0x2)
336	        sio_init();
337	} else {
338	    for (q = arg--; *q && *q != '('; q++);
339	    if (*q) {
340		drv = -1;
341		if (arg[1] == ':') {
342		    if (*arg < '0' || *arg > '9')
343			return -1;
344		    drv = *arg - '0';
345		    arg += 2;
346		}
347		if (q - arg != 2)
348		    return -1;
349		for (i = 0; arg[0] != dev_nm[i][0] ||
350			    arg[1] != dev_nm[i][1]; i++)
351		    if (i == NDEV - 1)
352			return -1;
353		dsk.type = i;
354		arg += 3;
355		if (arg[1] != ',' || *arg < '0' || *arg > '9')
356		    return -1;
357		dsk.unit = *arg - '0';
358		arg += 2;
359		dsk.slice = WHOLE_DISK_SLICE;
360		if (arg[1] == ',') {
361		    if (*arg < '0' || *arg > '0' + NDOSPART)
362			return -1;
363		    if ((dsk.slice = *arg - '0'))
364			dsk.slice++;
365		    arg += 2;
366		}
367		if (arg[1] != ')' || *arg < 'a' || *arg > 'p')
368		    return -1;
369		dsk.part = *arg - 'a';
370		arg += 2;
371		if (drv == -1)
372		    drv = dsk.unit;
373		dsk.drive = (dsk.type == MAJ_WD ||
374			     dsk.type == MAJ_DA ? DRV_HARD : 0) + drv;
375		dsk.meta = 0;
376		fsread(0, NULL, 0);
377	    }
378	    if ((i = p - arg - !*(p - 1))) {
379		if (i >= sizeof(kname))
380		    return -1;
381		memcpy(kname, arg, i + 1);
382	    }
383	}
384	arg = p;
385    }
386    return 0;
387}
388
389static void
390readfile(const char *fname, void *buf, size_t size)
391{
392    ino_t ino;
393
394    if ((ino = lookup(fname)))
395	fsread(ino, buf, size);
396}
397
398static ino_t
399lookup(const char *path)
400{
401    char name[MAXNAMLEN + 1];
402    const char *s;
403    ino_t ino;
404    ssize_t n;
405    int dt;
406
407    ino = ROOTINO;
408    dt = DT_DIR;
409    for (;;) {
410	if (*path == '/')
411	    path++;
412	if (!*path)
413	    break;
414	for (s = path; *s && *s != '/'; s++);
415	if ((n = s - path) > MAXNAMLEN)
416	    return 0;
417	ls = *path == '?' && n == 1 && !*s;
418	memcpy(name, path, n);
419	name[n] = 0;
420	if ((dt = fsfind(name, &ino)) <= 0)
421	    break;
422	path = s;
423    }
424    return dt == DT_REG ? ino : 0;
425}
426
427static int
428fsfind(const char *name, ino_t * ino)
429{
430    char buf[DEV_BSIZE];
431    struct dirent *d;
432    char *s;
433    ssize_t n;
434
435    fs_off = 0;
436    while ((n = fsread(*ino, buf, DEV_BSIZE)) > 0)
437	for (s = buf; s < buf + DEV_BSIZE;) {
438	    d = (void *)s;
439	    if (ls)
440		printf("%s ", d->d_name);
441	    else if (!strcmp(name, d->d_name)) {
442		*ino = d->d_fileno;
443		return d->d_type;
444	    }
445	    s += d->d_reclen;
446	}
447    if (n != -1 && ls)
448	putchar('\n');
449    return 0;
450}
451
452static int
453xfsread(ino_t inode, void *buf, size_t nbyte)
454{
455    if (fsread(inode, buf, nbyte) != nbyte) {
456	printf("Invalid %s\n", "format");
457	return -1;
458    }
459    return 0;
460}
461
462static ssize_t
463fsread(ino_t inode, void *buf, size_t nbyte)
464{
465    static struct fs fs;
466    static struct dinode din;
467    static char *blkbuf;
468    static ufs_daddr_t *indbuf;
469    static ino_t inomap;
470    static ufs_daddr_t blkmap, indmap;
471    static unsigned fsblks;
472    char *s;
473    ufs_daddr_t lbn, addr;
474    size_t n, nb, off;
475
476    if (!dsk.meta) {
477	if (!blkbuf)
478	    blkbuf = malloc(BSIZEMAX);
479	inomap = 0;
480	if (dskread(blkbuf, SBOFF / DEV_BSIZE, SBSIZE / DEV_BSIZE))
481	    return -1;
482	memcpy(&fs, blkbuf, sizeof(fs));
483	if (fs.fs_magic != FS_MAGIC) {
484	    printf("Not ufs\n");
485	    return -1;
486	}
487	fsblks = fs.fs_bsize >> DEV_BSHIFT;
488	dsk.meta = 1;
489    }
490    if (!inode)
491	return 0;
492    if (inomap != inode) {
493	if (dskread(blkbuf, fsbtodb(&fs, ino_to_fsba(&fs, inode)),
494		    fsblks))
495	    return -1;
496	din = ((struct dinode *)blkbuf)[inode % INOPB(&fs)];
497	inomap = inode;
498	fs_off = 0;
499	blkmap = indmap = 0;
500    }
501    s = buf;
502    if (nbyte > (n = din.di_size - fs_off))
503	nbyte = n;
504    nb = nbyte;
505    while (nb) {
506	lbn = lblkno(&fs, fs_off);
507	if (lbn < NDADDR)
508	    addr = din.di_db[lbn];
509	else {
510	    if (indmap != din.di_ib[0]) {
511		if (!indbuf)
512		    indbuf = malloc(BSIZEMAX);
513		if (dskread(indbuf, fsbtodb(&fs, din.di_ib[0]),
514			    fsblks))
515		    return -1;
516		indmap = din.di_ib[0];
517	    }
518	    addr = indbuf[(lbn - NDADDR) % NINDIR(&fs)];
519	}
520	n = dblksize(&fs, &din, lbn);
521	if (blkmap != addr) {
522	    if (dskread(blkbuf, fsbtodb(&fs, addr), n >> DEV_BSHIFT))
523		return -1;
524	    blkmap = addr;
525	}
526	off = blkoff(&fs, fs_off);
527	n -= off;
528	if (n > nb)
529	    n = nb;
530	memcpy(s, blkbuf + off, n);
531	s += n;
532	fs_off += n;
533	nb -= n;
534    }
535    return nbyte;
536}
537
538static int
539dskread(void *buf, unsigned lba, unsigned nblk)
540{
541    static char *sec;
542    struct dos_partition *dp;
543    struct disklabel *d;
544    unsigned sl, i;
545
546    if (!dsk.meta) {
547	if (!sec)
548	    sec = malloc(DEV_BSIZE);
549	dsk.start = 0;
550	if (drvread(sec, DOSBBSECTOR, 1))
551	    return -1;
552	dp = (void *)(sec + DOSPARTOFF);
553	sl = dsk.slice;
554	if (sl < BASE_SLICE) {
555	    for (i = 0; i < NDOSPART; i++)
556		if (dp[i].dp_typ == DOSPTYP_386BSD &&
557		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
558		    sl = BASE_SLICE + i;
559		    if (dp[i].dp_flag & 0x80 ||
560			dsk.slice == COMPATIBILITY_SLICE)
561			break;
562		}
563	    if (dsk.slice == WHOLE_DISK_SLICE)
564		dsk.slice = sl;
565	}
566	if (sl != WHOLE_DISK_SLICE) {
567	    if (sl != COMPATIBILITY_SLICE)
568		dp += sl - BASE_SLICE;
569	    if (dp->dp_typ != DOSPTYP_386BSD) {
570		printf("Invalid %s\n", "slice");
571		return -1;
572	    }
573	    dsk.start = dp->dp_start;
574	}
575	if (drvread(sec, dsk.start + LABELSECTOR, 1))
576		return -1;
577	d = (void *)(sec + LABELOFFSET);
578	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
579	    if (dsk.part != RAW_PART) {
580		printf("Invalid %s\n", "label");
581		return -1;
582	    }
583	} else {
584	    if (!dsk.init) {
585		if (d->d_type == DTYPE_SCSI)
586		    dsk.type = MAJ_DA;
587		dsk.init++;
588	    }
589	    if (dsk.part >= d->d_npartitions) {
590		printf("Invalid %s\n", "partition");
591		return -1;
592	    }
593	    dsk.start = d->d_partitions[dsk.part].p_offset;
594	}
595    }
596    return drvread(buf, dsk.start + lba, nblk);
597}
598
599static int
600printf(const char *fmt,...)
601{
602    static const char digits[16] = "0123456789abcdef";
603    va_list ap;
604    char buf[10];
605    char *s;
606    unsigned r, u;
607    int c;
608
609    va_start(ap, fmt);
610    while ((c = *fmt++)) {
611	if (c == '%') {
612	    c = *fmt++;
613	    switch (c) {
614	    case 'c':
615		putchar(va_arg(ap, int));
616		continue;
617	    case 's':
618		for (s = va_arg(ap, char *); *s; s++)
619		    putchar(*s);
620		continue;
621	    case 'u':
622	    case 'x':
623		r = c == 'u' ? 10U : 16U;
624		u = va_arg(ap, unsigned);
625		s = buf;
626		do
627		    *s++ = digits[u % r];
628		while (u /= r);
629		while (--s >= buf)
630		    putchar(*s);
631		continue;
632	    }
633	}
634	putchar(c);
635    }
636    va_end(ap);
637    return 0;
638}
639
640static void
641getstr(char *str, int size)
642{
643    char *s;
644    int c;
645
646    s = str;
647    do {
648	switch (c = getchar()) {
649	case 0:
650	    break;
651	case '\b':
652	    if (s > str)
653		s--;
654	    else
655		c = 0;
656	    break;
657	case '\n':
658	    *s = 0;
659	    break;
660	default:
661	    if (s - str < size - 1)
662		*s++ = c;
663	}
664	if (c)
665	    putchar(c);
666    } while (c != '\n');
667}
668
669static int
670putchar(int c)
671{
672    if (c == '\n')
673	xputc('\r');
674    return xputc(c);
675}
676
677static int
678getchar(void)
679{
680    int c;
681
682    c = xgetc(0);
683    if (c == '\r')
684	c = '\n';
685    return c;
686}
687
688static void *
689memcpy(void *dst, const void *src, size_t size)
690{
691    const char *s;
692    char *d;
693
694    for (d = dst, s = src; size; size--)
695	*d++ = *s++;
696    return dst;
697}
698
699static int
700strcmp(const char *s1, const char *s2)
701{
702    for (; *s1 == *s2 && *s1; s1++, s2++);
703    return (u_char)*s1 - (u_char)*s2;
704}
705
706static void *
707malloc(size_t size)
708{
709    static uint32_t next;
710    void *p;
711
712    if (!next)
713	next = roundup2(__base + _end, 0x10000) - __base;
714    p = (void *)next;
715    next += size;
716    return p;
717}
718
719static uint32_t
720memsize(int type)
721{
722    v86.addr = type;
723    v86.eax = 0x8800;
724    v86int();
725    return v86.eax;
726}
727
728static uint32_t
729drvinfo(int drive)
730{
731    v86.addr = 0x13;
732    v86.eax = 0x800;
733    v86.edx = DRV_HARD + drive;
734    v86int();
735    if (V86_CY(v86.efl))
736	return 0x4f010f;
737    return ((v86.ecx & 0xc0) << 18) | ((v86.ecx & 0xff00) << 8) |
738	   (v86.edx & 0xff00) | (v86.ecx & 0x3f);
739}
740
741static int
742drvread(void *buf, unsigned lba, unsigned nblk)
743{
744    static unsigned c = 0x2d5c7c2f;
745
746    printf("%c\b", c = c << 8 | c >> 24);
747    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
748    v86.addr = 0x604;
749    v86.eax = nblk;
750    v86.ebx = VTOPSEG(buf) << 16 | VTOPOFF(buf);
751    v86.ecx = lba;
752    v86.edx = 0x100 | dsk.drive;
753    v86int();
754    v86.ctl = V86_FLAGS;
755    if (V86_CY(v86.efl)) {
756	printf("Disk error 0x%x (lba=0x%x)\n", v86.eax >> 8 & 0xff,
757	       lba);
758	return -1;
759    }
760    return 0;
761}
762
763static int
764keyhit(unsigned ticks)
765{
766    uint32_t t0, t1;
767
768    t0 = 0;
769    for (;;) {
770	if (xgetc(1))
771	    return 1;
772	t1 = *(uint32_t *)PTOV(0x46c);
773	if (!t0)
774	    t0 = t1;
775	if (t1 < t0 || t1 >= t0 + ticks)
776	    return 0;
777    }
778}
779
780static int
781xputc(int c)
782{
783    if (ioctrl & 0x1)
784	putc(c);
785    if (ioctrl & 0x2)
786	sio_putc(c);
787    return c;
788}
789
790static int
791xgetc(int fn)
792{
793    for (;;) {
794	if (ioctrl & 0x1 && getc(1))
795	    return fn ? 1 : getc(0);
796	if (ioctrl & 0x2 && sio_ischar())
797	    return fn ? 1 : sio_getc();
798	if (fn)
799	    return 0;
800    }
801}
802
803static void
804putc(int c)
805{
806    v86.addr = 0x10;
807    v86.eax = 0xe00 | (c & 0xff);
808    v86.ebx = 0x7;
809    v86int();
810}
811
812static int
813getc(int fn)
814{
815    v86.addr = 0x16;
816    v86.eax = fn << 8;
817    v86int();
818    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
819}
820