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