boot2.c revision 274041
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: stable/10/sys/boot/pc98/boot2/boot2.c 274041 2014-11-03 11:58:32Z nyan $");
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
111extern uint32_t _end;
112
113static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
114static 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
131static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
132static const unsigned char dev_maj[NDEV] = {30, 4, 2};
133static const unsigned char dev_daua[NDEV] = {0x80, 0xa0, 0x90};
134
135static 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;
146static char cmd[512], cmddup[512], knamebuf[1024];
147static const char *kname;
148static uint32_t opts;
149static struct bootinfo bootinfo;
150#if SERIAL
151static int comspeed = SIOSPD;
152static uint8_t ioctrl = IO_KEYBOARD;
153#endif
154
155void exit(int);
156static void load(void);
157static int parse(void);
158static int dskread(void *, unsigned, unsigned);
159static void printf(const char *,...);
160static void putchar(int);
161static int drvread(void *, unsigned);
162static int keyhit(unsigned);
163static int xputc(int);
164static int xgetc(int);
165static inline int getc(int);
166
167static void memcpy(void *, const void *, int);
168static void
169memcpy(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
178static inline int
179strcmp(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
188static inline int
189xfsread(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
198static inline void
199getstr(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
228static inline void
229putc(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
239static inline int
240is_scsi_hd(void)
241{
242
243    if ((*(u_char *)PTOV(0x482) >> dsk.unit) & 0x01)
244	return 1;
245
246    return 0;
247}
248
249static inline void
250fix_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
264static inline uint32_t
265get_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
288static void
289set_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
301static uint32_t
302bd_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
335static int
336check_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
361int
362main(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. */
448void
449exit(int x)
450{
451}
452
453static void
454load(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
528static int
529parse()
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
633static int
634dskread(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
695static void
696printf(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
733static void
734putchar(int c)
735{
736    if (c == '\n')
737	xputc('\r');
738    xputc(c);
739}
740
741static int
742drvread(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
773static inline void
774delay(void)
775{
776    int i;
777
778    i = 800;
779    do {
780	outb(0x5f, 0);	/* about 600ns */
781    } while (--i >= 0);
782}
783
784static int
785keyhit(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
799static int
800xputc(int c)
801{
802    if (DO_KBD)
803	putc(c);
804    if (DO_SIO)
805	sio_putc(c);
806    return c;
807}
808
809static int
810getc(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
821static int
822xgetc(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}
835