]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/boot/pc98/boot2/boot2.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / boot / pc98 / boot2 / boot2.c
1 /*-
2  * Copyright (c) 2008-2009 TAKAHASHI Yoshihiro
3  * Copyright (c) 1998 Robert Nordier
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are freely
7  * permitted provided that the above copyright notice and this
8  * paragraph and the following disclaimer are duplicated in all
9  * such forms.
10  *
11  * This software is provided "AS IS" and without any express or
12  * implied warranties, including, without limitation, the implied
13  * warranties of merchantability and fitness for a particular
14  * purpose.
15  */
16
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
19
20 #include <sys/param.h>
21 #include <sys/disklabel.h>
22 #include <sys/diskpc98.h>
23 #include <sys/dirent.h>
24 #include <sys/reboot.h>
25
26 #include <machine/bootinfo.h>
27 #include <machine/cpufunc.h>
28 #include <machine/elf.h>
29
30 #include <stdarg.h>
31
32 #include <a.out.h>
33
34 #include <btxv86.h>
35
36 #include "boot2.h"
37 #include "lib.h"
38
39 /* Define to 0 to omit serial support */
40 #ifndef SERIAL
41 #define SERIAL 0
42 #endif
43
44 #define IO_KEYBOARD     1
45 #define IO_SERIAL       2
46
47 #if SERIAL
48 #define DO_KBD (ioctrl & IO_KEYBOARD)
49 #define DO_SIO (ioctrl & IO_SERIAL)
50 #else
51 #define DO_KBD (1)
52 #define DO_SIO (0)
53 #endif
54
55 #define SECOND          1       /* Circa that many ticks in a second. */
56
57 #define RBX_ASKNAME     0x0     /* -a */
58 #define RBX_SINGLE      0x1     /* -s */
59 /* 0x2 is reserved for log2(RB_NOSYNC). */
60 /* 0x3 is reserved for log2(RB_HALT). */
61 /* 0x4 is reserved for log2(RB_INITNAME). */
62 #define RBX_DFLTROOT    0x5     /* -r */
63 #define RBX_KDB         0x6     /* -d */
64 /* 0x7 is reserved for log2(RB_RDONLY). */
65 /* 0x8 is reserved for log2(RB_DUMP). */
66 /* 0x9 is reserved for log2(RB_MINIROOT). */
67 #define RBX_CONFIG      0xa     /* -c */
68 #define RBX_VERBOSE     0xb     /* -v */
69 #define RBX_SERIAL      0xc     /* -h */
70 #define RBX_CDROM       0xd     /* -C */
71 /* 0xe is reserved for log2(RB_POWEROFF). */
72 #define RBX_GDB         0xf     /* -g */
73 #define RBX_MUTE        0x10    /* -m */
74 /* 0x11 is reserved for log2(RB_SELFTEST). */
75 /* 0x12 is reserved for boot programs. */
76 /* 0x13 is reserved for boot programs. */
77 #define RBX_PAUSE       0x14    /* -p */
78 #define RBX_QUIET       0x15    /* -q */
79 #define RBX_NOINTR      0x1c    /* -n */
80 /* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
81 #define RBX_DUAL        0x1d    /* -D */
82 /* 0x1f is reserved for log2(RB_BOOTINFO). */
83
84 /* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
85 #define RBX_MASK        (OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
86                         OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \
87                         OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \
88                         OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \
89                         OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \
90                         OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL))
91
92 #define PATH_DOTCONFIG  "/boot.config"
93 #define PATH_CONFIG     "/boot/config"
94 #define PATH_BOOT3      "/boot/loader"
95 #define PATH_KERNEL     "/boot/kernel/kernel"
96
97 #define ARGS            0x900
98 #define NOPT            14
99 #define NDEV            3
100
101 #define DRV_DISK        0xf0
102 #define DRV_UNIT        0x0f
103
104 #define TYPE_AD         0
105 #define TYPE_DA         1
106 #define TYPE_FD         2
107
108 #define OPT_SET(opt)    (1 << (opt))
109 #define OPT_CHECK(opt)  ((opts) & OPT_SET(opt))
110
111 extern uint32_t _end;
112
113 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
114 static const unsigned char flags[NOPT] = {
115     RBX_DUAL,
116     RBX_SERIAL,
117     RBX_ASKNAME,
118     RBX_CDROM,
119     RBX_CONFIG,
120     RBX_KDB,
121     RBX_GDB,
122     RBX_MUTE,
123     RBX_NOINTR,
124     RBX_PAUSE,
125     RBX_QUIET,
126     RBX_DFLTROOT,
127     RBX_SINGLE,
128     RBX_VERBOSE
129 };
130
131 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
132 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
133 static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90};
134
135 static struct dsk {
136     unsigned daua;
137     unsigned type;
138     unsigned disk;
139     unsigned unit;
140     unsigned head;
141     unsigned sec;
142     uint8_t slice;
143     uint8_t part;
144     unsigned start;
145 } dsk;
146 static char cmd[512], cmddup[512], knamebuf[1024];
147 static const char *kname;
148 static uint32_t opts;
149 static struct bootinfo bootinfo;
150 #if SERIAL
151 static int comspeed = SIOSPD;
152 static uint8_t ioctrl = IO_KEYBOARD;
153 #endif
154
155 void exit(int);
156 static void load(void);
157 static int parse(void);
158 static int dskread(void *, unsigned, unsigned);
159 static void printf(const char *,...);
160 static void putchar(int);
161 static int drvread(void *, unsigned);
162 static int keyhit(unsigned);
163 static int xputc(int);
164 static int xgetc(int);
165 static inline int getc(int);
166
167 static void memcpy(void *, const void *, int);
168 static void
169 memcpy(void *dst, const void *src, int len)
170 {
171     const char *s = src;
172     char *d = dst;
173
174     while (len--)
175         *d++ = *s++;
176 }
177
178 static inline int
179 strcmp(const char *s1, const char *s2)
180 {
181     for (; *s1 == *s2 && *s1; s1++, s2++);
182     return (unsigned char)*s1 - (unsigned char)*s2;
183 }
184
185 #define UFS_SMALL_CGBASE
186 #include "ufsread.c"
187
188 static inline int
189 xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
190 {
191     if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
192         printf("Invalid %s\n", "format");
193         return -1;
194     }
195     return 0;
196 }
197
198 static inline void
199 getstr(void)
200 {
201     char *s;
202     int c;
203
204     s = cmd;
205     for (;;) {
206         switch (c = xgetc(0)) {
207         case 0:
208             break;
209         case '\177':
210         case '\b':
211             if (s > cmd) {
212                 s--;
213                 printf("\b \b");
214             }
215             break;
216         case '\n':
217         case '\r':
218             *s = 0;
219             return;
220         default:
221             if (s - cmd < sizeof(cmd) - 1)
222                 *s++ = c;
223             putchar(c);
224         }
225     }
226 }
227
228 static inline void
229 putc(int c)
230 {
231
232     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
233     v86.addr = PUTCORG;         /* call to putc in boot1 */
234     v86.eax = c;
235     v86int();
236     v86.ctl = V86_FLAGS;
237 }
238
239 static inline int
240 is_scsi_hd(void)
241 {
242
243     if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01)
244         return 1;
245
246     return 0;
247 }
248
249 static inline void
250 fix_sector_size(void)
251 {
252     u_char *p;
253
254     p = (u_char *)PTOV(0x460 + dsk.unit * 4);   /* SCSI equipment parameter */
255
256     if ((p[0] & 0x1f) == 7) {           /* SCSI MO */
257         if (!(p[3] & 0x30)) {           /* 256B / sector */
258             p[3] |= 0x10;               /* forced set 512B / sector */
259             p[3 + 0xa1000] |= 0x10;
260         }
261     }
262 }
263
264 static inline uint32_t
265 get_diskinfo(void)
266 {
267
268     if (dsk.disk == 0x30) {                             /* 1440KB FD */
269         /* 80 cylinders, 2 heads, 18 sectors */
270         return (80 << 16) | (2 << 8) | 18;
271     } else if (dsk.disk == 0x90) {                      /* 1200KB FD */
272         /* 80 cylinders, 2 heads, 15 sectors */
273         return (80 << 16) | (2 << 8) | 15;
274     } else if (dsk.disk == 0x80 || is_scsi_hd()) {      /* IDE or SCSI HDD */
275         v86.addr = 0x1b;
276         v86.eax = 0x8400 | dsk.daua;
277         v86int();
278         return (v86.ecx << 16) | v86.edx;
279     }
280
281     /* SCSI MO or CD */
282     fix_sector_size();  /* SCSI MO */
283
284     /* other SCSI devices */
285     return (65535 << 16) | (8 << 8) | 32;
286 }
287
288 static void
289 set_dsk(void)
290 {
291     uint32_t di;
292
293     di = get_diskinfo();
294
295     dsk.head = (di >> 8) & 0xff;
296     dsk.sec = di & 0xff;
297     dsk.start = 0;
298 }
299
300 #ifdef GET_BIOSGEOM
301 static uint32_t
302 bd_getbigeom(int bunit)
303 {
304     int hds = 0;
305     int unit = 0x80;            /* IDE HDD */
306     u_int addr = 0x55d;
307
308     while (unit < 0xa7) {
309         if (*(u_char *)PTOV(addr) & (1 << (unit & 0x0f)))
310             if (hds++ == bunit)
311                 break;
312
313         if (unit >= 0xA0) {
314             int media = ((unsigned *)PTOV(0x460))[unit & 0x0F] & 0x1F;
315
316             if (media == 7 && hds++ == bunit)   /* SCSI MO */
317                 return(0xFFFE0820); /* C:65535 H:8 S:32 */
318         }
319         if (++unit == 0x84) {
320             unit = 0xA0;        /* SCSI HDD */
321             addr = 0x482;
322         }
323     }
324     if (unit == 0xa7)
325         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
326     v86.addr = 0x1b;
327     v86.eax = 0x8400 | unit;
328     v86int();
329     if (v86.efl & 0x1)
330         return 0x4F020F;        /* 1200KB FD C:80 H:2 S:15 */
331     return ((v86.ecx & 0xffff) << 16) | (v86.edx & 0xffff);
332 }
333 #endif
334
335 static int
336 check_slice(void)
337 {
338     struct pc98_partition *dp;
339     char *sec;
340     unsigned i, cyl;
341
342     sec = dmadat->secbuf;
343     cyl = *(uint16_t *)PTOV(ARGS);
344     set_dsk();
345
346     if (dsk.type == TYPE_FD)
347         return (WHOLE_DISK_SLICE);
348     if (drvread(sec, PC98_BBSECTOR))
349         return (WHOLE_DISK_SLICE);      /* Read error */
350     dp = (void *)(sec + PC98_PARTOFF);
351     for (i = 0; i < PC98_NPARTS; i++) {
352         if (dp[i].dp_mid == DOSMID_386BSD) {
353             if (dp[i].dp_scyl <= cyl && cyl <= dp[i].dp_ecyl)
354                 return (BASE_SLICE + i);
355         }
356     }
357
358     return (WHOLE_DISK_SLICE);
359 }
360
361 int
362 main(void)
363 {
364 #ifdef GET_BIOSGEOM
365     int i;
366 #endif
367     uint8_t autoboot;
368     ufs_ino_t ino;
369     size_t nbyte;
370
371     dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
372     v86.ctl = V86_FLAGS;
373     v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
374     dsk.daua = *(uint8_t *)PTOV(0x584);
375     dsk.disk = dsk.daua & DRV_DISK;
376     dsk.unit = dsk.daua & DRV_UNIT;
377     if (dsk.disk == 0x80)
378         dsk.type = TYPE_AD;
379     else if (dsk.disk == 0xa0)
380         dsk.type = TYPE_DA;
381     else /* if (dsk.disk == 0x30 || dsk.disk == 0x90) */
382         dsk.type = TYPE_FD;
383     dsk.slice = check_slice();
384 #ifdef GET_BIOSGEOM
385     for (i = 0; i < N_BIOS_GEOM; i++)
386         bootinfo.bi_bios_geom[i] = bd_getbigeom(i);
387 #endif
388     bootinfo.bi_version = BOOTINFO_VERSION;
389     bootinfo.bi_size = sizeof(bootinfo);
390
391     /* Process configuration file */
392
393     autoboot = 1;
394
395     if ((ino = lookup(PATH_CONFIG)) ||
396         (ino = lookup(PATH_DOTCONFIG))) {
397         nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
398         cmd[nbyte] = '\0';
399     }
400
401     if (*cmd) {
402         memcpy(cmddup, cmd, sizeof(cmd));
403         if (parse())
404             autoboot = 0;
405         if (!OPT_CHECK(RBX_QUIET))
406             printf("%s: %s", PATH_CONFIG, cmddup);
407         /* Do not process this command twice */
408         *cmd = 0;
409     }
410
411     /*
412      * Try to exec stage 3 boot loader. If interrupted by a keypress,
413      * or in case of failure, try to load a kernel directly instead.
414      */
415
416     if (!kname) {
417         kname = PATH_BOOT3;
418         if (autoboot && !keyhit(3*SECOND)) {
419             load();
420             kname = PATH_KERNEL;
421         }
422     }
423
424     /* Present the user with the boot2 prompt. */
425
426     for (;;) {
427         if (!autoboot || !OPT_CHECK(RBX_QUIET))
428             printf("\nFreeBSD/pc98 boot\n"
429                    "Default: %u:%s(%u,%c)%s\n"
430                    "boot: ",
431                    dsk.unit, dev_nm[dsk.type], dsk.unit,
432                    'a' + dsk.part, kname);
433         if (DO_SIO)
434             sio_flush();
435         if (!autoboot || keyhit(3*SECOND))
436             getstr();
437         else if (!autoboot || !OPT_CHECK(RBX_QUIET))
438             putchar('\n');
439         autoboot = 0;
440         if (parse())
441             putchar('\a');
442         else
443             load();
444     }
445 }
446
447 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
448 void
449 exit(int x)
450 {
451 }
452
453 static void
454 load(void)
455 {
456     union {
457         struct exec ex;
458         Elf32_Ehdr eh;
459     } hdr;
460     static Elf32_Phdr ep[2];
461     static Elf32_Shdr es[2];
462     caddr_t p;
463     ufs_ino_t ino;
464     uint32_t addr;
465     int i, j;
466
467     if (!(ino = lookup(kname))) {
468         if (!ls)
469             printf("No %s\n", kname);
470         return;
471     }
472     if (xfsread(ino, &hdr, sizeof(hdr)))
473         return;
474
475     if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
476         addr = hdr.ex.a_entry & 0xffffff;
477         p = PTOV(addr);
478         fs_off = PAGE_SIZE;
479         if (xfsread(ino, p, hdr.ex.a_text))
480             return;
481         p += roundup2(hdr.ex.a_text, PAGE_SIZE);
482         if (xfsread(ino, p, hdr.ex.a_data))
483             return;
484     } else if (IS_ELF(hdr.eh)) {
485         fs_off = hdr.eh.e_phoff;
486         for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
487             if (xfsread(ino, ep + j, sizeof(ep[0])))
488                 return;
489             if (ep[j].p_type == PT_LOAD)
490                 j++;
491         }
492         for (i = 0; i < 2; i++) {
493             p = PTOV(ep[i].p_paddr & 0xffffff);
494             fs_off = ep[i].p_offset;
495             if (xfsread(ino, p, ep[i].p_filesz))
496                 return;
497         }
498         p += roundup2(ep[1].p_memsz, PAGE_SIZE);
499         bootinfo.bi_symtab = VTOP(p);
500         if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
501             fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
502                 (hdr.eh.e_shstrndx + 1);
503             if (xfsread(ino, &es, sizeof(es)))
504                 return;
505             for (i = 0; i < 2; i++) {
506                 *(Elf32_Word *)p = es[i].sh_size;
507                 p += sizeof(es[i].sh_size);
508                 fs_off = es[i].sh_offset;
509                 if (xfsread(ino, p, es[i].sh_size))
510                     return;
511                 p += es[i].sh_size;
512             }
513         }
514         addr = hdr.eh.e_entry & 0xffffff;
515         bootinfo.bi_esymtab = VTOP(p);
516     } else {
517         printf("Invalid %s\n", "format");
518         return;
519     }
520
521     bootinfo.bi_kernelname = VTOP(kname);
522     bootinfo.bi_bios_dev = dsk.daua;
523     __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
524            MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
525            0, 0, 0, VTOP(&bootinfo));
526 }
527
528 static int
529 parse()
530 {
531     char *arg = cmd;
532     char *ep, *p, *q;
533     const char *cp;
534     unsigned int drv;
535     int c, i, j;
536
537     while ((c = *arg++)) {
538         if (c == ' ' || c == '\t' || c == '\n')
539             continue;
540         for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
541         ep = p;
542         if (*p)
543             *p++ = 0;
544         if (c == '-') {
545             while ((c = *arg++)) {
546                 if (c == 'P') {
547                     if (*(uint8_t *)PTOV(0x481) & 0x48) {
548                         cp = "yes";
549                     } else {
550                         opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
551                         cp = "no";
552                     }
553                     printf("Keyboard: %s\n", cp);
554                     continue;
555 #if SERIAL
556                 } else if (c == 'S') {
557                     j = 0;
558                     while ((unsigned int)(i = *arg++ - '0') <= 9)
559                         j = j * 10 + i;
560                     if (j > 0 && i == -'0') {
561                         comspeed = j;
562                         break;
563                     }
564                     /* Fall through to error below ('S' not in optstr[]). */
565 #endif
566                 }
567                 for (i = 0; c != optstr[i]; i++)
568                     if (i == NOPT - 1)
569                         return -1;
570                 opts ^= OPT_SET(flags[i]);
571             }
572 #if SERIAL
573             ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
574                      OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
575             if (DO_SIO) {
576                 if (sio_init(115200 / comspeed) != 0)
577                     ioctrl &= ~IO_SERIAL;
578             }
579 #endif
580         } else {
581             for (q = arg--; *q && *q != '('; q++);
582             if (*q) {
583                 drv = -1;
584                 if (arg[1] == ':') {
585                     drv = *arg - '0';
586                     if (drv > 9)
587                         return (-1);
588                     arg += 2;
589                 }
590                 if (q - arg != 2)
591                     return -1;
592                 for (i = 0; arg[0] != dev_nm[i][0] ||
593                             arg[1] != dev_nm[i][1]; i++)
594                     if (i == NDEV - 1)
595                         return -1;
596                 dsk.type = i;
597                 arg += 3;
598                 dsk.unit = *arg - '0';
599                 if (arg[1] != ',' || dsk.unit > 9)
600                     return -1;
601                 arg += 2;
602                 dsk.slice = WHOLE_DISK_SLICE;
603                 if (arg[1] == ',') {
604                     dsk.slice = *arg - '0' + 1;
605                     if (dsk.slice > PC98_NPARTS + 1)
606                         return -1;
607                     arg += 2;
608                 }
609                 if (arg[1] != ')')
610                     return -1;
611                 dsk.part = *arg - 'a';
612                 if (dsk.part > 7)
613                     return (-1);
614                 arg += 2;
615                 if (drv == -1)
616                     drv = dsk.unit;
617                 dsk.disk = dev_daua[dsk.type];
618                 dsk.daua = dsk.disk | dsk.unit;
619                 dsk_meta = 0;
620             }
621             if ((i = ep - arg)) {
622                 if ((size_t)i >= sizeof(knamebuf))
623                     return -1;
624                 memcpy(knamebuf, arg, i + 1);
625                 kname = knamebuf;
626             }
627         }
628         arg = p;
629     }
630     return 0;
631 }
632
633 static int
634 dskread(void *buf, unsigned lba, unsigned nblk)
635 {
636     struct pc98_partition *dp;
637     struct disklabel *d;
638     char *sec;
639     unsigned i;
640     uint8_t sl;
641     u_char *p;
642
643     if (!dsk_meta) {
644         sec = dmadat->secbuf;
645         set_dsk();
646         if (dsk.type == TYPE_FD)
647             goto unsliced;
648         if (drvread(sec, PC98_BBSECTOR))
649             return -1;
650         dp = (void *)(sec + PC98_PARTOFF);
651         sl = dsk.slice;
652         if (sl < BASE_SLICE) {
653             for (i = 0; i < PC98_NPARTS; i++)
654                 if (dp[i].dp_mid == DOSMID_386BSD) {
655                     sl = BASE_SLICE + i;
656                     break;
657                 }
658             dsk.slice = sl;
659         }
660         if (sl != WHOLE_DISK_SLICE) {
661             dp += sl - BASE_SLICE;
662             if (dp->dp_mid != DOSMID_386BSD) {
663                 printf("Invalid %s\n", "slice");
664                 return -1;
665             }
666             dsk.start = dp->dp_scyl * dsk.head * dsk.sec +
667                 dp->dp_shd * dsk.sec + dp->dp_ssect;
668         }
669         if (drvread(sec, dsk.start + LABELSECTOR))
670                 return -1;
671         d = (void *)(sec + LABELOFFSET);
672         if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
673             if (dsk.part != RAW_PART) {
674                 printf("Invalid %s\n", "label");
675                 return -1;
676             }
677         } else {
678             if (dsk.part >= d->d_npartitions ||
679                 !d->d_partitions[dsk.part].p_size) {
680                 printf("Invalid %s\n", "partition");
681                 return -1;
682             }
683             dsk.start += d->d_partitions[dsk.part].p_offset;
684             dsk.start -= d->d_partitions[RAW_PART].p_offset;
685         }
686     unsliced: ;
687     }
688     for (p = buf; nblk; p += 512, lba++, nblk--) {
689         if ((i = drvread(p, dsk.start + lba)))
690             return i;
691     }
692     return 0;
693 }
694
695 static void
696 printf(const char *fmt,...)
697 {
698     va_list ap;
699     static char buf[10];
700     char *s;
701     unsigned u;
702     int c;
703
704     va_start(ap, fmt);
705     while ((c = *fmt++)) {
706         if (c == '%') {
707             c = *fmt++;
708             switch (c) {
709             case 'c':
710                 putchar(va_arg(ap, int));
711                 continue;
712             case 's':
713                 for (s = va_arg(ap, char *); *s; s++)
714                     putchar(*s);
715                 continue;
716             case 'u':
717                 u = va_arg(ap, unsigned);
718                 s = buf;
719                 do
720                     *s++ = '0' + u % 10U;
721                 while (u /= 10U);
722                 while (--s >= buf)
723                     putchar(*s);
724                 continue;
725             }
726         }
727         putchar(c);
728     }
729     va_end(ap);
730     return;
731 }
732
733 static void
734 putchar(int c)
735 {
736     if (c == '\n')
737         xputc('\r');
738     xputc(c);
739 }
740
741 static int
742 drvread(void *buf, unsigned lba)
743 {
744     static unsigned c = 0x2d5c7c2f;
745     unsigned bpc, x, cyl, head, sec;
746
747     bpc = dsk.sec * dsk.head;
748     cyl = lba / bpc;
749     x = lba % bpc;
750     head = x / dsk.sec;
751     sec = x % dsk.sec;
752
753     if (!OPT_CHECK(RBX_QUIET))
754         printf("%c\b", c = c << 8 | c >> 24);
755     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
756     v86.addr = READORG;         /* call to read in boot1 */
757     v86.ecx = cyl;
758     v86.edx = (head << 8) | sec;
759     v86.edi = lba;
760     v86.ebx = 512;
761     v86.es = VTOPSEG(buf);
762     v86.ebp = VTOPOFF(buf);
763     v86int();
764     v86.ctl = V86_FLAGS;
765     if (V86_CY(v86.efl)) {
766         printf("error %u c/h/s %u/%u/%u lba %u\n", v86.eax >> 8 & 0xff,
767                cyl, head, sec, lba);
768         return -1;
769     }
770     return 0;
771 }
772
773 static inline void
774 delay(void)
775 {
776     int i;
777
778     i = 800;
779     do {
780         outb(0x5f, 0);  /* about 600ns */
781     } while (--i >= 0);
782 }
783
784 static int
785 keyhit(unsigned sec)
786 {
787     unsigned i;
788
789     if (OPT_CHECK(RBX_NOINTR))
790         return 0;
791     for (i = 0; i < sec * 1000; i++) {
792         if (xgetc(1))
793             return 1;
794         delay();
795     }
796     return 0;
797 }
798
799 static int
800 xputc(int c)
801 {
802     if (DO_KBD)
803         putc(c);
804     if (DO_SIO)
805         sio_putc(c);
806     return c;
807 }
808
809 static int
810 getc(int fn)
811 {
812     v86.addr = 0x18;
813     v86.eax = fn << 8;
814     v86int();
815     if (fn)
816         return (v86.ebx >> 8) & 0x01;
817     else
818         return v86.eax & 0xff;
819 }
820
821 static int
822 xgetc(int fn)
823 {
824     if (OPT_CHECK(RBX_NOINTR))
825         return 0;
826     for (;;) {
827         if (DO_KBD && getc(1))
828             return fn ? 1 : getc(0);
829         if (DO_SIO && sio_ischar())
830             return fn ? 1 : sio_getc();
831         if (fn)
832             return 0;
833     }
834 }