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