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