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