gptboot.c revision 122749
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1998 Robert Nordier
31590Srgrimes * All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms are freely
61590Srgrimes * permitted provided that the above copyright notice and this
71590Srgrimes * paragraph and the following disclaimer are duplicated in all
81590Srgrimes * such forms.
91590Srgrimes *
101590Srgrimes * This software is provided "AS IS" and without any express or
111590Srgrimes * implied warranties, including, without limitation, the implied
121590Srgrimes * warranties of merchantability and fitness for a particular
131590Srgrimes * purpose.
141590Srgrimes */
151590Srgrimes
161590Srgrimes#include <sys/cdefs.h>
171590Srgrimes__FBSDID("$FreeBSD: head/sys/boot/i386/gptboot/gptboot.c 122749 2003-11-15 10:04:06Z bde $");
181590Srgrimes
191590Srgrimes#include <sys/param.h>
201590Srgrimes#include <sys/disklabel.h>
211590Srgrimes#include <sys/diskmbr.h>
221590Srgrimes#include <sys/dirent.h>
231590Srgrimes#include <sys/reboot.h>
241590Srgrimes
251590Srgrimes#include <machine/bootinfo.h>
261590Srgrimes#include <machine/elf.h>
271590Srgrimes
281590Srgrimes#include <stdarg.h>
291590Srgrimes
301590Srgrimes#include <a.out.h>
311590Srgrimes
321590Srgrimes#include <btxv86.h>
331590Srgrimes
341590Srgrimes#include "boot2.h"
351590Srgrimes#include "lib.h"
361590Srgrimes
371590Srgrimes#define IO_KEYBOARD	1
381590Srgrimes#define IO_SERIAL	2
391590Srgrimes
401590Srgrimes#define SECOND		18	/* Circa that many ticks in a second. */
411590Srgrimes
421590Srgrimes#define RBX_ASKNAME	0x0	/* -a */
431590Srgrimes#define RBX_SINGLE	0x1	/* -s */
441590Srgrimes/* 0x2 is reserved for log2(RB_NOSYNC). */
451590Srgrimes/* 0x3 is reserved for log2(RB_HALT). */
461590Srgrimes/* 0x4 is reserved for log2(RB_INITNAME). */
471590Srgrimes#define RBX_DFLTROOT	0x5	/* -r */
481590Srgrimes#define RBX_KDB 	0x6	/* -d */
491590Srgrimes/* 0x7 is reserved for log2(RB_RDONLY). */
501590Srgrimes/* 0x8 is reserved for log2(RB_DUMP). */
511590Srgrimes/* 0x9 is reserved for log2(RB_MINIROOT). */
521590Srgrimes#define RBX_CONFIG	0xa	/* -c */
531590Srgrimes#define RBX_VERBOSE	0xb	/* -v */
541590Srgrimes#define RBX_SERIAL	0xc	/* -h */
551590Srgrimes#define RBX_CDROM	0xd	/* -C */
561590Srgrimes/* 0xe is reserved for log2(RB_POWEROFF). */
571590Srgrimes#define RBX_GDB 	0xf	/* -g */
581590Srgrimes#define RBX_MUTE	0x10	/* -m */
591590Srgrimes/* 0x11 is reserved for log2(RB_SELFTEST). */
601590Srgrimes/* 0x12 is reserved for boot programs. */
611590Srgrimes/* 0x13 is reserved for boot programs. */
621590Srgrimes#define RBX_PAUSE	0x14	/* -p */
631590Srgrimes#define RBX_NOINTR	0x1c	/* -n */
641590Srgrimes/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
651590Srgrimes#define RBX_DUAL	0x1d	/* -D */
661590Srgrimes#define RBX_PROBEKBD	0x1e	/* -P */
671590Srgrimes/* 0x1f is reserved for log2(RB_BOOTINFO). */
681590Srgrimes
691590Srgrimes/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
701590Srgrimes#define RBX_MASK	0x2005ffff
711590Srgrimes
721590Srgrimes#define PATH_CONFIG	"/boot.config"
731590Srgrimes#define PATH_BOOT3	"/boot/loader"
741590Srgrimes#define PATH_KERNEL	"/kernel"
751590Srgrimes
761590Srgrimes#define ARGS		0x900
771590Srgrimes#define NOPT		12
781590Srgrimes#define NDEV		3
791590Srgrimes#define MEM_BASE	0x12
801590Srgrimes#define MEM_EXT 	0x15
811590Srgrimes#define V86_CY(x)	((x) & 1)
821590Srgrimes#define V86_ZR(x)	((x) & 0x40)
831590Srgrimes
841590Srgrimes#define DRV_HARD	0x80
851590Srgrimes#define DRV_MASK	0x7f
861590Srgrimes
871590Srgrimes#define TYPE_AD		0
881590Srgrimes#define TYPE_DA		1
891590Srgrimes#define TYPE_MAXHARD	TYPE_DA
901590Srgrimes#define TYPE_FD		2
911590Srgrimes
921590Srgrimesextern uint32_t _end;
931590Srgrimes
941590Srgrimesstatic const char optstr[NOPT] = "DhaCgmnPprsv";
951590Srgrimesstatic const unsigned char flags[NOPT] = {
961590Srgrimes    RBX_DUAL,
971590Srgrimes    RBX_SERIAL,
981590Srgrimes    RBX_ASKNAME,
991590Srgrimes    RBX_CDROM,
1001590Srgrimes    RBX_GDB,
1011590Srgrimes    RBX_MUTE,
1021590Srgrimes    RBX_NOINTR,
1031590Srgrimes    RBX_PROBEKBD,
1041590Srgrimes    RBX_PAUSE,
1051590Srgrimes    RBX_DFLTROOT,
1061590Srgrimes    RBX_SINGLE,
1071590Srgrimes    RBX_VERBOSE
1081590Srgrimes};
1091590Srgrimes
1101590Srgrimesstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
1111590Srgrimesstatic const unsigned char dev_maj[NDEV] = {30, 4, 2};
1121590Srgrimes
1131590Srgrimesstatic struct dsk {
1141590Srgrimes    unsigned drive;
1151590Srgrimes    unsigned type;
1161590Srgrimes    unsigned unit;
1171590Srgrimes    unsigned slice;
1181590Srgrimes    unsigned part;
1191590Srgrimes    unsigned start;
1201590Srgrimes    int init;
1211590Srgrimes} dsk;
1221590Srgrimesstatic char cmd[512];
1231590Srgrimesstatic char kname[1024];
1241590Srgrimesstatic uint32_t opts;
1251590Srgrimesstatic struct bootinfo bootinfo;
1261590Srgrimesstatic uint8_t ioctrl = IO_KEYBOARD;
1271590Srgrimes
1281590Srgrimesvoid exit(int);
1291590Srgrimesstatic void load(void);
1301590Srgrimesstatic int parse(void);
1311590Srgrimesstatic int xfsread(ino_t, void *, size_t);
1321590Srgrimesstatic int dskread(void *, unsigned, unsigned);
1331590Srgrimesstatic void printf(const char *,...);
1341590Srgrimesstatic void putchar(int);
1351590Srgrimesstatic uint32_t memsize(void);
1361590Srgrimesstatic int drvread(void *, unsigned, unsigned);
1371590Srgrimesstatic int keyhit(unsigned);
1381590Srgrimesstatic int xputc(int);
1391590Srgrimesstatic int xgetc(int);
1401590Srgrimesstatic int getc(int);
1411590Srgrimes
1421590Srgrimes#define memcpy __builtin_memcpy
1431590Srgrimes
1441590Srgrimesstatic inline int
1451590Srgrimesstrcmp(const char *s1, const char *s2)
1461590Srgrimes{
1471590Srgrimes    for (; *s1 == *s2 && *s1; s1++, s2++);
1481590Srgrimes    return (unsigned char)*s1 - (unsigned char)*s2;
1491590Srgrimes}
1501590Srgrimes
1511590Srgrimes#include "ufsread.c"
1521590Srgrimes
1531590Srgrimesstatic int
1541590Srgrimesxfsread(ino_t inode, void *buf, size_t nbyte)
1551590Srgrimes{
1561590Srgrimes    if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
1571590Srgrimes	printf("Invalid %s\n", "format");
1581590Srgrimes	return -1;
1591590Srgrimes    }
1601590Srgrimes    return 0;
1611590Srgrimes}
1621590Srgrimes
1631590Srgrimesstatic inline uint32_t
1641590Srgrimesmemsize(void)
1651590Srgrimes{
1661590Srgrimes    v86.addr = MEM_EXT;
1671590Srgrimes    v86.eax = 0x8800;
1681590Srgrimes    v86int();
1691590Srgrimes    return v86.eax;
1701590Srgrimes}
1711590Srgrimes
1721590Srgrimesstatic inline void
1731590Srgrimesgetstr(void)
1741590Srgrimes{
1751590Srgrimes    char *s;
1761590Srgrimes    int c;
1771590Srgrimes
1781590Srgrimes    s = cmd;
1791590Srgrimes    for (;;) {
1801590Srgrimes	switch (c = xgetc(0)) {
1811590Srgrimes	case 0:
1821590Srgrimes	    break;
1831590Srgrimes	case '\177':
1841590Srgrimes	case '\b':
1851590Srgrimes	    if (s > cmd) {
1861590Srgrimes		s--;
1871590Srgrimes		printf("\b \b");
1881590Srgrimes	    }
1891590Srgrimes	    break;
1901590Srgrimes	case '\n':
1911590Srgrimes	case '\r':
1921590Srgrimes	    *s = 0;
1931590Srgrimes	    return;
1941590Srgrimes	default:
1951590Srgrimes	    if (s - cmd < sizeof(cmd) - 1)
1961590Srgrimes		*s++ = c;
1971590Srgrimes	    putchar(c);
1981590Srgrimes	}
1991590Srgrimes    }
2001590Srgrimes}
2011590Srgrimes
2021590Srgrimesstatic inline void
2031590Srgrimesputc(int c)
2041590Srgrimes{
2051590Srgrimes    v86.addr = 0x10;
2061590Srgrimes    v86.eax = 0xe00 | (c & 0xff);
2071590Srgrimes    v86.ebx = 0x7;
2081590Srgrimes    v86int();
2091590Srgrimes}
2101590Srgrimes
2111590Srgrimesint
2121590Srgrimesmain(void)
2131590Srgrimes{
2141590Srgrimes    int autoboot;
2151590Srgrimes    ino_t ino;
2161590Srgrimes
2171590Srgrimes    dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
2181590Srgrimes    v86.ctl = V86_FLAGS;
2191590Srgrimes    dsk.drive = *(uint8_t *)PTOV(ARGS);
2201590Srgrimes    dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
2211590Srgrimes    dsk.unit = dsk.drive & DRV_MASK;
2221590Srgrimes    dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
2231590Srgrimes    bootinfo.bi_version = BOOTINFO_VERSION;
2241590Srgrimes    bootinfo.bi_size = sizeof(bootinfo);
2251590Srgrimes    bootinfo.bi_basemem = 0;	/* XXX will be filled by loader or kernel */
2261590Srgrimes    bootinfo.bi_extmem = memsize();
2271590Srgrimes    bootinfo.bi_memsizes_valid++;
2281590Srgrimes
2291590Srgrimes    /* Process configuration file */
2301590Srgrimes
2311590Srgrimes    autoboot = 1;
2321590Srgrimes
2331590Srgrimes    if ((ino = lookup(PATH_CONFIG)))
2341590Srgrimes	fsread(ino, cmd, sizeof(cmd));
2351590Srgrimes
2361590Srgrimes    if (*cmd) {
2371590Srgrimes	printf("%s: %s", PATH_CONFIG, cmd);
2381590Srgrimes	if (parse())
2391590Srgrimes	    autoboot = 0;
2401590Srgrimes	/* Do not process this command twice */
2411590Srgrimes	*cmd = 0;
2421590Srgrimes    }
2431590Srgrimes
2441590Srgrimes    /*
2451590Srgrimes     * Try to exec stage 3 boot loader. If interrupted by a keypress,
2461590Srgrimes     * or in case of failure, try to load a kernel directly instead.
2471590Srgrimes     */
2481590Srgrimes
2491590Srgrimes    if (autoboot && !*kname) {
2501590Srgrimes	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
2511590Srgrimes	if (!keyhit(3*SECOND)) {
2521590Srgrimes	    load();
2531590Srgrimes	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
2541590Srgrimes	}
2551590Srgrimes    }
2561590Srgrimes
2571590Srgrimes    /* Present the user with the boot2 prompt. */
2581590Srgrimes
2591590Srgrimes    for (;;) {
2601590Srgrimes	printf("\nFreeBSD/i386 boot\n"
2611590Srgrimes	       "Default: %u:%s(%u,%c)%s\n"
2621590Srgrimes	       "boot: ",
2631590Srgrimes	       dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
2641590Srgrimes	       'a' + dsk.part, kname);
2651590Srgrimes	if (ioctrl & IO_SERIAL)
2661590Srgrimes	    sio_flush();
2671590Srgrimes	if (!autoboot || keyhit(5*SECOND))
2681590Srgrimes	    getstr();
2691590Srgrimes	else
2701590Srgrimes	    putchar('\n');
2711590Srgrimes	autoboot = 0;
2721590Srgrimes	if (parse())
2731590Srgrimes	    putchar('\a');
2741590Srgrimes	else
2751590Srgrimes	    load();
2761590Srgrimes    }
2771590Srgrimes}
2781590Srgrimes
2791590Srgrimes/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
2801590Srgrimesvoid
2811590Srgrimesexit(int x)
2821590Srgrimes{
2831590Srgrimes}
2841590Srgrimes
2851590Srgrimesstatic void
2861590Srgrimesload(void)
2871590Srgrimes{
2881590Srgrimes    union {
2891590Srgrimes	struct exec ex;
2901590Srgrimes	Elf32_Ehdr eh;
2911590Srgrimes    } hdr;
2921590Srgrimes    Elf32_Phdr ep[2];
2931590Srgrimes    Elf32_Shdr es[2];
2941590Srgrimes    caddr_t p;
2951590Srgrimes    ino_t ino;
2961590Srgrimes    uint32_t addr, x;
2971590Srgrimes    int fmt, i, j;
2981590Srgrimes
2991590Srgrimes    if (!(ino = lookup(kname))) {
3001590Srgrimes	if (!ls)
3011590Srgrimes	    printf("No %s\n", kname);
3021590Srgrimes	return;
3031590Srgrimes    }
3041590Srgrimes    if (xfsread(ino, &hdr, sizeof(hdr)))
3051590Srgrimes	return;
3061590Srgrimes    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
3071590Srgrimes	fmt = 0;
3081590Srgrimes    else if (IS_ELF(hdr.eh))
3091590Srgrimes	fmt = 1;
3101590Srgrimes    else {
3111590Srgrimes	printf("Invalid %s\n", "format");
3121590Srgrimes	return;
3131590Srgrimes    }
3141590Srgrimes    if (fmt == 0) {
3151590Srgrimes	addr = hdr.ex.a_entry & 0xffffff;
3161590Srgrimes	p = PTOV(addr);
3171590Srgrimes	fs_off = PAGE_SIZE;
3181590Srgrimes	if (xfsread(ino, p, hdr.ex.a_text))
3191590Srgrimes	    return;
3201590Srgrimes	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
3211590Srgrimes	if (xfsread(ino, p, hdr.ex.a_data))
3221590Srgrimes	    return;
3231590Srgrimes	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
3241590Srgrimes	bootinfo.bi_symtab = VTOP(p);
3251590Srgrimes	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
3261590Srgrimes	p += sizeof(hdr.ex.a_syms);
3271590Srgrimes	if (hdr.ex.a_syms) {
3281590Srgrimes	    if (xfsread(ino, p, hdr.ex.a_syms))
3291590Srgrimes		return;
3301590Srgrimes	    p += hdr.ex.a_syms;
3311590Srgrimes	    if (xfsread(ino, p, sizeof(int)))
3321590Srgrimes		return;
3331590Srgrimes	    x = *(uint32_t *)p;
3341590Srgrimes	    p += sizeof(int);
3351590Srgrimes	    x -= sizeof(int);
3361590Srgrimes	    if (xfsread(ino, p, x))
3371590Srgrimes		return;
3381590Srgrimes	    p += x;
3391590Srgrimes	}
3401590Srgrimes    } else {
3411590Srgrimes	fs_off = hdr.eh.e_phoff;
3421590Srgrimes	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
3431590Srgrimes	    if (xfsread(ino, ep + j, sizeof(ep[0])))
3441590Srgrimes		return;
3451590Srgrimes	    if (ep[j].p_type == PT_LOAD)
3461590Srgrimes		j++;
3471590Srgrimes	}
348	for (i = 0; i < 2; i++) {
349	    p = PTOV(ep[i].p_paddr & 0xffffff);
350	    fs_off = ep[i].p_offset;
351	    if (xfsread(ino, p, ep[i].p_filesz))
352		return;
353	}
354	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
355	bootinfo.bi_symtab = VTOP(p);
356	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
357	    fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
358		(hdr.eh.e_shstrndx + 1);
359	    if (xfsread(ino, &es, sizeof(es)))
360		return;
361	    for (i = 0; i < 2; i++) {
362		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
363		p += sizeof(es[i].sh_size);
364		fs_off = es[i].sh_offset;
365		if (xfsread(ino, p, es[i].sh_size))
366		    return;
367		p += es[i].sh_size;
368	    }
369	}
370	addr = hdr.eh.e_entry & 0xffffff;
371    }
372    bootinfo.bi_esymtab = VTOP(p);
373    bootinfo.bi_kernelname = VTOP(kname);
374    bootinfo.bi_bios_dev = dsk.drive;
375    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
376	   MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part),
377	   0, 0, 0, VTOP(&bootinfo));
378}
379
380static int
381parse()
382{
383    char *arg = cmd;
384    char *p, *q;
385    unsigned int drv;
386    int c, i;
387
388    while ((c = *arg++)) {
389	if (c == ' ' || c == '\t' || c == '\n')
390	    continue;
391	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
392	if (*p)
393	    *p++ = 0;
394	if (c == '-') {
395	    while ((c = *arg++)) {
396		for (i = 0; c != optstr[i]; i++)
397		    if (i == NOPT - 1)
398			return -1;
399		opts ^= 1 << flags[i];
400	    }
401	    if (opts & 1 << RBX_PROBEKBD) {
402		i = *(uint8_t *)PTOV(0x496) & 0x10;
403		printf("Keyboard: %s\n", i ? "yes" : "no");
404		if (!i)
405		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
406		opts &= ~(1 << RBX_PROBEKBD);
407	    }
408	    ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
409		     opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
410	    if (ioctrl & IO_SERIAL)
411	        sio_init();
412	} else {
413	    for (q = arg--; *q && *q != '('; q++);
414	    if (*q) {
415		drv = -1;
416		if (arg[1] == ':') {
417		    drv = *arg - '0';
418		    if (drv > 9)
419			return (-1);
420		    arg += 2;
421		}
422		if (q - arg != 2)
423		    return -1;
424		for (i = 0; arg[0] != dev_nm[i][0] ||
425			    arg[1] != dev_nm[i][1]; i++)
426		    if (i == NDEV - 1)
427			return -1;
428		dsk.type = i;
429		arg += 3;
430		dsk.unit = *arg - '0';
431		if (arg[1] != ',' || dsk.unit > 9)
432		    return -1;
433		arg += 2;
434		dsk.slice = WHOLE_DISK_SLICE;
435		if (arg[1] == ',') {
436		    dsk.slice = *arg - '0' + 1;
437		    if (dsk.slice > NDOSPART)
438			return -1;
439		    arg += 2;
440		}
441		if (arg[1] != ')')
442		    return -1;
443		dsk.part = *arg - 'a';
444		if (dsk.part > 7)
445		    return (-1);
446		arg += 2;
447		if (drv == -1)
448		    drv = dsk.unit;
449		dsk.drive = (dsk.type <= TYPE_MAXHARD
450			     ? DRV_HARD : 0) + drv;
451		dsk_meta = 0;
452	    }
453	    if ((i = p - arg - !*(p - 1))) {
454		if ((size_t)i >= sizeof(kname))
455		    return -1;
456		memcpy(kname, arg, i + 1);
457	    }
458	}
459	arg = p;
460    }
461    return 0;
462}
463
464static int
465dskread(void *buf, unsigned lba, unsigned nblk)
466{
467    struct dos_partition *dp;
468    struct disklabel *d;
469    char *sec;
470    unsigned sl, i;
471
472    if (!dsk_meta) {
473	sec = dmadat->secbuf;
474	dsk.start = 0;
475	if (drvread(sec, DOSBBSECTOR, 1))
476	    return -1;
477	dp = (void *)(sec + DOSPARTOFF);
478	sl = dsk.slice;
479	if (sl < BASE_SLICE) {
480	    for (i = 0; i < NDOSPART; i++)
481		if (dp[i].dp_typ == DOSPTYP_386BSD &&
482		    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
483		    sl = BASE_SLICE + i;
484		    if (dp[i].dp_flag & 0x80 ||
485			dsk.slice == COMPATIBILITY_SLICE)
486			break;
487		}
488	    if (dsk.slice == WHOLE_DISK_SLICE)
489		dsk.slice = sl;
490	}
491	if (sl != WHOLE_DISK_SLICE) {
492	    if (sl != COMPATIBILITY_SLICE)
493		dp += sl - BASE_SLICE;
494	    if (dp->dp_typ != DOSPTYP_386BSD) {
495		printf("Invalid %s\n", "slice");
496		return -1;
497	    }
498	    dsk.start = dp->dp_start;
499	}
500	if (drvread(sec, dsk.start + LABELSECTOR, 1))
501		return -1;
502	d = (void *)(sec + LABELOFFSET);
503	if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
504	    if (dsk.part != RAW_PART) {
505		printf("Invalid %s\n", "label");
506		return -1;
507	    }
508	} else {
509	    if (!dsk.init) {
510		if (d->d_type == DTYPE_SCSI)
511		    dsk.type = TYPE_DA;
512		dsk.init++;
513	    }
514	    if (dsk.part >= d->d_npartitions ||
515		!d->d_partitions[dsk.part].p_size) {
516		printf("Invalid %s\n", "partition");
517		return -1;
518	    }
519	    dsk.start += d->d_partitions[dsk.part].p_offset;
520	    dsk.start -= d->d_partitions[RAW_PART].p_offset;
521	}
522    }
523    return drvread(buf, dsk.start + lba, nblk);
524}
525
526static void
527printf(const char *fmt,...)
528{
529    va_list ap;
530    char buf[10];
531    char *s;
532    unsigned u;
533    int c;
534
535    va_start(ap, fmt);
536    while ((c = *fmt++)) {
537	if (c == '%') {
538	    c = *fmt++;
539	    switch (c) {
540	    case 'c':
541		putchar(va_arg(ap, int));
542		continue;
543	    case 's':
544		for (s = va_arg(ap, char *); *s; s++)
545		    putchar(*s);
546		continue;
547	    case 'u':
548		u = va_arg(ap, unsigned);
549		s = buf;
550		do
551		    *s++ = '0' + u % 10U;
552		while (u /= 10U);
553		while (--s >= buf)
554		    putchar(*s);
555		continue;
556	    }
557	}
558	putchar(c);
559    }
560    va_end(ap);
561    return;
562}
563
564static void
565putchar(int c)
566{
567    if (c == '\n')
568	xputc('\r');
569    xputc(c);
570}
571
572static int
573drvread(void *buf, unsigned lba, unsigned nblk)
574{
575    static unsigned c = 0x2d5c7c2f;
576
577    printf("%c\b", c = c << 8 | c >> 24);
578    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
579    v86.addr = XREADORG;		/* call to xread in boot1 */
580    v86.es = VTOPSEG(buf);
581    v86.eax = lba;
582    v86.ebx = VTOPOFF(buf);
583    v86.ecx = lba >> 16;
584    v86.edx = nblk << 8 | dsk.drive;
585    v86int();
586    v86.ctl = V86_FLAGS;
587    if (V86_CY(v86.efl)) {
588	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
589	return -1;
590    }
591    return 0;
592}
593
594static int
595keyhit(unsigned ticks)
596{
597    uint32_t t0, t1;
598
599    if (opts & 1 << RBX_NOINTR)
600	return 0;
601    t0 = 0;
602    for (;;) {
603	if (xgetc(1))
604	    return 1;
605	t1 = *(uint32_t *)PTOV(0x46c);
606	if (!t0)
607	    t0 = t1;
608	if (t1 < t0 || t1 >= t0 + ticks)
609	    return 0;
610    }
611}
612
613static int
614xputc(int c)
615{
616    if (ioctrl & IO_KEYBOARD)
617	putc(c);
618    if (ioctrl & IO_SERIAL)
619	sio_putc(c);
620    return c;
621}
622
623static int
624xgetc(int fn)
625{
626    if (opts & 1 << RBX_NOINTR)
627	return 0;
628    for (;;) {
629	if (ioctrl & IO_KEYBOARD && getc(1))
630	    return fn ? 1 : getc(0);
631	if (ioctrl & IO_SERIAL && sio_ischar())
632	    return fn ? 1 : sio_getc();
633	if (fn)
634	    return 0;
635    }
636}
637
638static int
639getc(int fn)
640{
641    v86.addr = 0x16;
642    v86.eax = fn << 8;
643    v86int();
644    return fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl);
645}
646