xref: /dragonfly/stand/boot/pc32/boot2/boot2.c (revision 7d3e9a5b)
1 /*
2  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * Copyright (c) 1998 Robert Nordier
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms are freely
38  * permitted provided that the above copyright notice and this
39  * paragraph and the following disclaimer are duplicated in all
40  * such forms.
41  *
42  * This software is provided "AS IS" and without any express or
43  * implied warranties, including, without limitation, the implied
44  * warranties of merchantability and fitness for a particular
45  * purpose.
46  *
47  * $FreeBSD: src/sys/boot/i386/boot2/boot2.c,v 1.64 2003/08/25 23:28:31 obrien Exp $
48  */
49 
50 #define AOUT_H_FORCE32
51 #include <sys/param.h>
52 #ifdef DISKLABEL64
53 #include <sys/disklabel64.h>
54 #else
55 #include <sys/disklabel32.h>
56 #endif
57 #include <sys/diskslice.h>
58 #include <sys/diskmbr.h>
59 #include <sys/dtype.h>
60 #include <sys/dirent.h>
61 #include <machine/bootinfo.h>
62 #include <machine/elf.h>
63 #include <machine/psl.h>
64 
65 #include <stdarg.h>
66 
67 #include <a.out.h>
68 
69 #include <btxv86.h>
70 
71 #ifdef DISKLABEL64
72 #include "boot2_64.h"
73 #else
74 #include "boot2_32.h"
75 #endif
76 #include "boot2.h"
77 #include "lib.h"
78 #include "../bootasm.h"
79 
80 #define SECOND		18	/* Circa that many ticks in a second. */
81 
82 #define RBX_ASKNAME	0x0	/* -a */
83 #define RBX_SINGLE	0x1	/* -s */
84 #define RBX_DFLTROOT	0x5	/* -r */
85 #define RBX_KDB 	0x6	/* -d */
86 #define RBX_CONFIG	0xa	/* -c */
87 #define RBX_VERBOSE	0xb	/* -v */
88 #define RBX_SERIAL	0xc	/* -h */
89 #define RBX_CDROM	0xd	/* -C */
90 #define RBX_GDB 	0xf	/* -g */
91 #define RBX_MUTE	0x10	/* -m */
92 #define RBX_PAUSE	0x12	/* -p */
93 #define RBX_NOINTR	0x1c	/* -n */
94 #define RBX_VIDEO	0x1d	/* -V */
95 #define RBX_PROBEKBD	0x1e	/* -P */
96 /* 0x1f is reserved for the historical RB_BOOTINFO option */
97 
98 #define RBF_MUTE	(1 << RBX_MUTE)
99 #define RBF_SERIAL	(1 << RBX_SERIAL)
100 #define RBF_VIDEO	(1 << RBX_VIDEO)
101 #define RBF_NOINTR	(1 << RBX_NOINTR)
102 #define RBF_PROBEKBD	(1 << RBX_PROBEKBD)
103 
104 /* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -V */
105 #define RBX_MASK	0x2005ffff
106 
107 #define PATH_CONFIG	"/boot.config"
108 #define PATH_BOOT3	"/loader"		/* /boot is dedicated */
109 #define PATH_BOOT3_ALT	"/boot/loader"		/* /boot in root */
110 #define PATH_KERNEL	"/kernel"
111 
112 #define NOPT		12
113 #define NDEV		3
114 #define MEM_BASE	0x12
115 #define MEM_EXT 	0x15
116 #define V86_CY(x)	((x) & PSL_C)
117 #define V86_ZR(x)	((x) & PSL_Z)
118 
119 #define DRV_HARD	0x80
120 #define DRV_MASK	0x7f
121 
122 #define TYPE_AD		0
123 #define TYPE_DA		1
124 #define TYPE_MAXHARD	TYPE_DA
125 #define TYPE_FD		2
126 
127 #define INVALID_S	"Bad %s\n"
128 
129 extern uint32_t _end;
130 
131 static const char optstr[NOPT] = { "VhaCgmnPprsv" };
132 static const unsigned char flags[NOPT] = {
133     RBX_VIDEO,
134     RBX_SERIAL,
135     RBX_ASKNAME,
136     RBX_CDROM,
137     RBX_GDB,
138     RBX_MUTE,
139     RBX_NOINTR,
140     RBX_PROBEKBD,
141     RBX_PAUSE,
142     RBX_DFLTROOT,
143     RBX_SINGLE,
144     RBX_VERBOSE
145 };
146 
147 static const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
148 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
149 
150 static struct dsk {
151     unsigned drive;
152     unsigned type;
153     unsigned unit;
154     uint8_t slice;
155     uint8_t part;
156     unsigned start;
157     int init;
158 } dsk;
159 
160 static char cmd[512];
161 static const char *kname;
162 static uint32_t opts = RBF_VIDEO;
163 static struct bootinfo bootinfo;
164 
165 /*
166  * boot2 encapsulated ABI elements provided to *fsread.c
167  *
168  * NOTE: boot2_dmadat is extended by per-filesystem APIs
169  */
170 uint32_t fs_off;
171 int	no_io_error;
172 int	ls;
173 struct boot2_dmadat *boot2_dmadat;
174 
175 void exit(int);
176 static void load(void);
177 static int parse(void);
178 static int dskprobe(void);
179 static int xfsread(boot2_ino_t, void *, size_t);
180 static int drvread(void *, unsigned, unsigned);
181 static int keyhit(unsigned);
182 static int xputc(int);
183 static int xgetc(int);
184 static int getc(int);
185 
186 void
187 memcpy(void *d, const void *s, int len)
188 {
189     char *dd = d;
190     const char *ss = s;
191 
192 	while (--len >= 0)
193 	    dd[len] = ss[len];
194 }
195 
196 int
197 strcmp(const char *s1, const char *s2)
198 {
199     for (; *s1 == *s2 && *s1; s1++, s2++)
200 	;
201     return ((int)((unsigned char)*s1 - (unsigned char)*s2));
202 }
203 
204 #if defined(UFS) && defined(HAMMER2FS)
205 
206 const struct boot2_fsapi *fsapi;
207 
208 #elif defined(UFS)
209 
210 #define fsapi	(&boot2_ufs_api)
211 
212 #elif defined(HAMMER2FS)
213 
214 #define fsapi	(&boot2_hammer2_api)
215 
216 #endif
217 
218 static int
219 xfsread(boot2_ino_t inode, void *buf, size_t nbyte)
220 {
221     if ((size_t)fsapi->fsread(inode, buf, nbyte) != nbyte) {
222 	printf(INVALID_S, "format");
223 	return -1;
224     }
225     return 0;
226 }
227 
228 static inline void
229 getstr(void)
230 {
231     char *s;
232     int c;
233 
234     s = cmd;
235     for (;;) {
236 	switch (c = xgetc(0)) {
237 	case 0:
238 	    break;
239 	case '\177':
240 	case '\b':
241 	    if (s > cmd) {
242 		s--;
243 		printf("\b \b");
244 	    }
245 	    break;
246 	case '\n':
247 	case '\r':
248 	    *s = 0;
249 	    return;
250 	default:
251 	    if (s - cmd < sizeof(cmd) - 1)
252 		*s++ = c;
253 	    putchar(c);
254 	}
255     }
256 }
257 
258 static inline void
259 putc(int c)
260 {
261     v86.addr = 0x10;
262     v86.eax = 0xe00 | (c & 0xff);
263     v86.ebx = 0x7;
264     v86int();
265 }
266 
267 int
268 main(void)
269 {
270     uint8_t autoboot;
271     boot2_ino_t ino;
272 
273     kname = NULL;
274     boot2_dmadat =
275 		(void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
276     v86.ctl = V86_FLAGS;
277     v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
278     dsk.drive = *(uint8_t *)PTOV(MEM_BTX_USR_ARG);
279     dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
280     dsk.unit = dsk.drive & DRV_MASK;
281     dsk.slice = *(uint8_t *)PTOV(MEM_BTX_USR_ARG + 1) + 1;
282     bootinfo.bi_version = BOOTINFO_VERSION;
283     bootinfo.bi_size = sizeof(bootinfo);
284 
285     autoboot = 1;
286 
287     /*
288      * Probe the default disk and process the configuration file if
289      * successful.
290      */
291     if (dskprobe() == 0) {
292 	if ((ino = fsapi->fslookup(PATH_CONFIG)))
293 	    fsapi->fsread(ino, cmd, sizeof(cmd));
294     }
295 
296     /*
297      * Parse config file if present.  parse() will re-probe if necessary.
298      */
299     if (cmd[0]) {
300 	printf("%s: %s", PATH_CONFIG, cmd);
301 	if (parse())
302 	    autoboot = 0;
303 	/* Do not process this command twice */
304 	*cmd = 0;
305     }
306 
307     /*
308      * Setup our (serial) console after processing the config file.  If
309      * the initialization fails, don't try to use the serial port.  This
310      * can happen if the serial port is unmaped (happens on new laptops a lot).
311      */
312     if ((opts & (RBF_MUTE|RBF_SERIAL|RBF_VIDEO)) == 0)
313 	opts |= RBF_SERIAL|RBF_VIDEO;
314     if (opts & RBF_SERIAL) {
315 	if (sio_init())
316 	    opts = RBF_VIDEO;
317     }
318 
319 
320     /*
321      * Try to exec stage 3 boot loader. If interrupted by a keypress,
322      * or in case of failure, try to load a kernel directly instead.
323      *
324      * We have to try boot /boot/loader and /loader to support booting
325      * from a /boot partition instead of a root partition.
326      */
327     if (autoboot && !kname) {
328         kname = PATH_BOOT3;
329 	if (!keyhit(3*SECOND)) {
330 	    load();
331 	    kname = PATH_BOOT3_ALT;
332 	    load();
333 	    kname = PATH_KERNEL;
334 	}
335     }
336 
337     /* Present the user with the boot2 prompt. */
338 
339     for (;;) {
340 	printf("\nDragonFly boot\n"
341 	       "%u:%s(%u,%c)%s: ",
342 	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
343 	       'a' + dsk.part, kname);
344 	if (!autoboot || keyhit(5*SECOND))
345 	    getstr();
346 	else
347 	    putchar('\n');
348 	autoboot = 0;
349 	if (parse())
350 	    putchar('\a');
351 	else
352 	    load();
353     }
354 }
355 
356 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
357 void
358 exit(int x)
359 {
360 }
361 
362 static void
363 load(void)
364 {
365     union {
366 	struct exec ex;
367 	Elf32_Ehdr eh;
368     } hdr;
369     static Elf32_Phdr ep[2];
370     static Elf32_Shdr es[2];
371     caddr_t p;
372     boot2_ino_t ino;
373     uint32_t addr;
374     int i, j;
375 
376     if (!(ino = fsapi->fslookup(kname))) {
377 	if (!ls)
378 	    printf("No %s\n", kname);
379 	return;
380     }
381     if (xfsread(ino, &hdr, sizeof(hdr)))
382 	return;
383 
384     if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
385 	addr = hdr.ex.a_entry & 0xffffff;
386 	p = PTOV(addr);
387 	fs_off = PAGE_SIZE;
388 	if (xfsread(ino, p, hdr.ex.a_text))
389 	    return;
390 	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
391 	if (xfsread(ino, p, hdr.ex.a_data))
392 	    return;
393     } else if (IS_ELF(hdr.eh)) {
394 	fs_off = hdr.eh.e_phoff;
395 	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
396 	    if (xfsread(ino, ep + j, sizeof(ep[0])))
397 		return;
398 	    if (ep[j].p_type == PT_LOAD)
399 		j++;
400 	}
401 	for (i = 0; i < 2; i++) {
402 	    p = PTOV(ep[i].p_paddr & 0xffffff);
403 	    fs_off = ep[i].p_offset;
404 	    if (xfsread(ino, p, ep[i].p_filesz))
405 		return;
406 	}
407 	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
408 	bootinfo.bi_symtab = VTOP(p);
409 	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
410 	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
411 		(hdr.eh.e_shstrndx + 1);
412 	    if (xfsread(ino, &es, sizeof(es)))
413 		return;
414 	    for (i = 0; i < 2; i++) {
415 		*(Elf32_Word *)p = es[i].sh_size;
416 		p += sizeof(es[i].sh_size);
417 		fs_off = es[i].sh_offset;
418 		if (xfsread(ino, p, es[i].sh_size))
419 		    return;
420 		p += es[i].sh_size;
421 	    }
422 	}
423 	addr = hdr.eh.e_entry & 0xffffff;
424         bootinfo.bi_esymtab = VTOP(p);
425     } else {
426 	printf(INVALID_S, "format");
427         return;
428     }
429 
430     bootinfo.bi_kernelname = VTOP(kname);
431     bootinfo.bi_bios_dev = dsk.drive;
432     __exec((caddr_t)addr, opts & RBX_MASK,
433 	   MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
434 	   0, 0, 0, VTOP(&bootinfo));
435 }
436 
437 static int
438 parse(void)
439 {
440     char *arg = cmd;
441     char *p, *q;
442     unsigned int drv;
443     int c, i;
444 
445     while ((c = *arg++)) {
446 	if (c == ' ' || c == '\t' || c == '\n')
447 	    continue;
448 	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
449 	    ;
450 	if (*p)
451 	    *p++ = 0;
452 	if (c == '-') {
453 	    while ((c = *arg++)) {
454 		for (i = NOPT - 1; i >= 0; --i) {
455 		    if (optstr[i] == c) {
456 			opts ^= 1 << flags[i];
457 			goto ok;
458 		    }
459 		}
460 		return(-1);
461 		ok: ;	/* ugly but save space */
462 	    }
463 	    if (opts & RBF_PROBEKBD) {
464 		i = *(uint8_t *)PTOV(0x496) & 0x10;
465 		if (!i) {
466 		    printf("NO KB\n");
467 		    opts |= RBF_VIDEO | RBF_SERIAL;
468 		}
469 		opts &= ~RBF_PROBEKBD;
470 	    }
471 	} else {
472 	    for (q = arg--; *q && *q != '('; q++);
473 	    if (*q) {
474 		drv = -1;
475 		if (arg[1] == ':') {
476 		    drv = *arg - '0';
477 		    if (drv > 9)
478 			return (-1);
479 		    arg += 2;
480 		}
481 		if (q - arg != 2)
482 		    return -1;
483 		for (i = 0; arg[0] != dev_nm[i][0] ||
484 			    arg[1] != dev_nm[i][1]; i++)
485 		    if (i == NDEV - 1)
486 			return -1;
487 		dsk.type = i;
488 		arg += 3;
489 		dsk.unit = *arg - '0';
490 		if (arg[1] != ',' || dsk.unit > 9)
491 		    return -1;
492 		arg += 2;
493 		dsk.slice = WHOLE_DISK_SLICE;
494 		if (arg[1] == ',') {
495 		    dsk.slice = *arg - '0' + 1;
496 		    if (dsk.slice > NDOSPART + 1)
497 			return -1;
498 		    arg += 2;
499 		}
500 		if (arg[1] != ')')
501 		    return -1;
502 		dsk.part = *arg - 'a';
503 		if (dsk.part > 7)
504 		    return (-1);
505 		arg += 2;
506 		if (drv == -1)
507 		    drv = dsk.unit;
508 		dsk.drive = (dsk.type <= TYPE_MAXHARD
509 			     ? DRV_HARD : 0) + drv;
510 	    }
511 	    kname = arg;
512 	}
513 	arg = p;
514     }
515     return dskprobe();
516 }
517 
518 static int
519 dskprobe(void)
520 {
521     struct dos_partition *dp;
522 #ifdef DISKLABEL64
523     struct disklabel64 *d;
524 #else
525     struct disklabel32 *d;
526 #endif
527     char *sec;
528     unsigned i;
529     uint8_t sl;
530 
531     /*
532      * Probe slice table
533      */
534     sec = boot2_dmadat->secbuf;
535     dsk.start = 0;
536     if (drvread(sec, DOSBBSECTOR, 1))
537 	return -1;
538     dp = (void *)(sec + DOSPARTOFF);
539     sl = dsk.slice;
540     if (sl < BASE_SLICE) {
541 	for (i = 0; i < NDOSPART; i++)
542 	    if ((dp[i].dp_typ == DOSPTYP_386BSD ||
543 		 dp[i].dp_typ == DOSPTYP_DFLYBSD) &&
544 		(dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
545 		sl = BASE_SLICE + i;
546 		if (dp[i].dp_flag & 0x80 ||
547 		    dsk.slice == COMPATIBILITY_SLICE)
548 		    break;
549 	    }
550 	if (dsk.slice == WHOLE_DISK_SLICE)
551 	    dsk.slice = sl;
552     }
553     if (sl != WHOLE_DISK_SLICE) {
554 	if (sl != COMPATIBILITY_SLICE)
555 	    dp += sl - BASE_SLICE;
556 	if (dp->dp_typ != DOSPTYP_386BSD && dp->dp_typ != DOSPTYP_DFLYBSD) {
557 	    printf(INVALID_S, "slice");
558 	    return -1;
559 	}
560 	dsk.start = dp->dp_start;
561     }
562 
563     /*
564      * Probe label and partition table
565      */
566 #ifdef DISKLABEL64
567     if (drvread(sec, dsk.start, (sizeof(struct disklabel64) + 511) / 512))
568 	    return -1;
569     d = (void *)sec;
570     if (d->d_magic != DISKMAGIC64) {
571 	printf(INVALID_S, "label");
572 	return -1;
573     } else {
574 	if (dsk.part >= d->d_npartitions || d->d_partitions[dsk.part].p_bsize == 0) {
575 	    printf(INVALID_S, "partition");
576 	    return -1;
577 	}
578 	dsk.start += d->d_partitions[dsk.part].p_boffset / 512;
579     }
580 #else
581     if (drvread(sec, dsk.start + LABELSECTOR32, 1))
582 	    return -1;
583     d = (void *)(sec + LABELOFFSET32);
584     if (d->d_magic != DISKMAGIC32 || d->d_magic2 != DISKMAGIC32) {
585 	if (dsk.part != RAW_PART) {
586 	    printf(INVALID_S, "label");
587 	    return -1;
588 	}
589     } else {
590 	if (!dsk.init) {
591 	    if (d->d_type == DTYPE_SCSI)
592 		dsk.type = TYPE_DA;
593 	    dsk.init++;
594 	}
595 	if (dsk.part >= d->d_npartitions ||
596 	    !d->d_partitions[dsk.part].p_size) {
597 	    printf(INVALID_S, "partition");
598 	    return -1;
599 	}
600 	dsk.start += d->d_partitions[dsk.part].p_offset;
601 	dsk.start -= d->d_partitions[RAW_PART].p_offset;
602     }
603 #endif
604     /*
605      * Probe filesystem
606      */
607 #if defined(UFS) && defined(HAMMER2FS)
608     if (boot2_ufs_api.fsinit() == 0) {
609 	fsapi = &boot2_ufs_api;
610     } else if (boot2_hammer2_api.fsinit() == 0) {
611 	fsapi = &boot2_hammer2_api;
612     } else {
613 	printf("fs probe failed\n");
614 	fsapi = &boot2_ufs_api;
615 	return -1;
616     }
617     return 0;
618 #else
619     return fsapi->fsinit();
620 #endif
621 }
622 
623 
624 /*
625  * Read from the probed disk.  We have established the slice and partition
626  * base sector.
627  */
628 int
629 dskread(void *buf, unsigned lba, unsigned nblk)
630 {
631     return drvread(buf, dsk.start + lba, nblk);
632 }
633 
634 /*
635  * boot encapsulated ABI
636  */
637 void
638 printf(const char *fmt,...)
639 {
640     va_list ap;
641     static char buf[10];
642     char *s;
643     unsigned u;
644 #ifdef HAMMER2FS
645     uint64_t q;
646 #endif
647     int c;
648 
649     va_start(ap, fmt);
650     while ((c = *fmt++)) {
651 	if (c == '%') {
652 	    c = *fmt++;
653 	    switch (c) {
654 	    case 'c':
655 		putchar(va_arg(ap, int));
656 		continue;
657 	    case 's':
658 		for (s = va_arg(ap, char *); *s; s++)
659 		    putchar(*s);
660 		continue;
661 #ifdef HAMMER2FS
662 	    case 'q':
663 		++fmt;	/* skip the 'x' */
664 		q = va_arg(ap, uint64_t);
665 		s = buf;
666 		do {
667 		    if ((q & 15) < 10)
668 			*s++ = '0' + (q & 15);
669 		    else
670 			*s++ = 'a' + (q & 15) - 10;
671 		} while (q >>= 4);
672 		while (--s >= buf)
673 		    putchar(*s);
674 		continue;
675 	    case 'x':
676 		u = va_arg(ap, unsigned);
677 		s = buf;
678 		do {
679 		    if ((u & 15) < 10)
680 			*s++ = '0' + (u & 15);
681 		    else
682 			*s++ = 'a' + (u & 15) - 10;
683 		} while (u >>= 4);
684 		while (--s >= buf)
685 		    putchar(*s);
686 		continue;
687 #endif
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 /*
706  * boot encapsulated ABI
707  */
708 void
709 putchar(int c)
710 {
711     if (c == '\n')
712 	xputc('\r');
713     xputc(c);
714 }
715 
716 /*
717  * boot encapsulated ABI
718  */
719 static int
720 drvread(void *buf, unsigned lba, unsigned nblk)
721 {
722     static unsigned c = 0x2d5c7c2f;	/* twiddle */
723 
724     c = (c << 8) | (c >> 24);
725     xputc(c);
726     xputc('\b');
727     v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
728     v86.addr = XREADORG;		/* call to xread in boot1 */
729     v86.es = VTOPSEG(buf);
730     v86.eax = lba;
731     v86.ebx = VTOPOFF(buf);
732     v86.ecx = lba >> 16;
733     v86.edx = nblk << 8 | dsk.drive;
734     v86int();
735     v86.ctl = V86_FLAGS;
736     if (V86_CY(v86.efl) && !no_io_error) {
737 	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
738 	return -1;
739     }
740     return 0;
741 }
742 
743 static int
744 keyhit(unsigned ticks)
745 {
746     uint32_t t0, t1;
747 
748     if (opts & RBF_NOINTR)
749 	return 0;
750     t0 = 0;
751     for (;;) {
752 	if (xgetc(1))
753 	    return 1;
754 	t1 = *(uint32_t *)PTOV(0x46c);
755 	if (!t0)
756 	    t0 = t1;
757         if ((uint32_t)(t1 - t0) >= ticks)
758 	    return 0;
759     }
760 }
761 
762 static int
763 xputc(int c)
764 {
765     if (opts & RBF_VIDEO)
766 	putc(c);
767     if (opts & RBF_SERIAL)
768 	sio_putc(c);
769     return c;
770 }
771 
772 static int
773 getc(int fn)
774 {
775     v86.addr = 0x16;
776     v86.eax = fn << 8;
777     v86int();
778     return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
779 }
780 
781 static int
782 xgetc(int fn)
783 {
784     if (opts & RBF_NOINTR)
785 	return 0;
786     for (;;) {
787 	if ((opts & RBF_VIDEO) && getc(1))
788 	    return fn ? 1 : getc(0);
789 	if ((opts & RBF_SERIAL) && sio_ischar())
790 	    return fn ? 1 : sio_getc();
791 	if (fn)
792 	    return 0;
793     }
794 }
795