1119482Sobrien/*-
240269Srnordier * Copyright (c) 1998 Robert Nordier
340269Srnordier * All rights reserved.
440269Srnordier *
540269Srnordier * Redistribution and use in source and binary forms are freely
640269Srnordier * permitted provided that the above copyright notice and this
740269Srnordier * paragraph and the following disclaimer are duplicated in all
840269Srnordier * such forms.
940269Srnordier *
1040269Srnordier * This software is provided "AS IS" and without any express or
1140269Srnordier * implied warranties, including, without limitation, the implied
1240269Srnordier * warranties of merchantability and fitness for a particular
1340269Srnordier * purpose.
1440269Srnordier */
1540269Srnordier
16119482Sobrien#include <sys/cdefs.h>
17119482Sobrien__FBSDID("$FreeBSD: stable/11/stand/i386/boot2/boot2.c 332130 2018-04-06 18:55:02Z kevans $");
1840269Srnordier
1940269Srnordier#include <sys/param.h>
2040269Srnordier#include <sys/disklabel.h>
21104272Sphk#include <sys/diskmbr.h>
2240269Srnordier#include <sys/dirent.h>
23122463Sbde#include <sys/reboot.h>
24122463Sbde
2540269Srnordier#include <machine/bootinfo.h>
2676224Sobrien#include <machine/elf.h>
2740269Srnordier
2840269Srnordier#include <stdarg.h>
2940269Srnordier
3040269Srnordier#include <a.out.h>
3140269Srnordier
3240269Srnordier#include <btxv86.h>
3340269Srnordier
3480751Sjhb#include "boot2.h"
3540404Srnordier#include "lib.h"
36294765Simp#include "paths.h"
37294766Simp#include "rbx.h"
3840404Srnordier
39268475Simp/* Define to 0 to omit serial support */
40268475Simp#ifndef SERIAL
41268475Simp#define SERIAL 1
42268475Simp#endif
43268475Simp
4494411Spb#define IO_KEYBOARD	1
4594411Spb#define IO_SERIAL	2
4694411Spb
47268475Simp#if SERIAL
48268475Simp#define DO_KBD (ioctrl & IO_KEYBOARD)
49268475Simp#define DO_SIO (ioctrl & IO_SERIAL)
50268475Simp#else
51268475Simp#define DO_KBD (1)
52268475Simp#define DO_SIO (0)
53268475Simp#endif
54268475Simp
5594411Spb#define SECOND		18	/* Circa that many ticks in a second. */
5694411Spb
5742478Speter#define ARGS		0x900
58163707Sru#define NOPT		14
59108000Simp#define NDEV		3
6040269Srnordier#define MEM_BASE	0x12
6140269Srnordier#define MEM_EXT 	0x15
6240269Srnordier
6340307Srnordier#define DRV_HARD	0x80
6440307Srnordier#define DRV_MASK	0x7f
6540307Srnordier
6657090Sru#define TYPE_AD		0
67108000Simp#define TYPE_DA		1
6898542Smckusick#define TYPE_MAXHARD	TYPE_DA
69108000Simp#define TYPE_FD		2
7040307Srnordier
7140269Srnordierextern uint32_t _end;
7240269Srnordier
73163707Srustatic const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
7440269Srnordierstatic const unsigned char flags[NOPT] = {
75332130Skevans	RBX_DUAL,
76332130Skevans	RBX_SERIAL,
77332130Skevans	RBX_ASKNAME,
78332130Skevans	RBX_CDROM,
79332130Skevans	RBX_CONFIG,
80332130Skevans	RBX_KDB,
81332130Skevans	RBX_GDB,
82332130Skevans	RBX_MUTE,
83332130Skevans	RBX_NOINTR,
84332130Skevans	RBX_PAUSE,
85332130Skevans	RBX_QUIET,
86332130Skevans	RBX_DFLTROOT,
87332130Skevans	RBX_SINGLE,
88332130Skevans	RBX_VERBOSE
8940269Srnordier};
9040269Srnordier
91108000Simpstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
92108000Simpstatic const unsigned char dev_maj[NDEV] = {30, 4, 2};
9340269Srnordier
9440269Srnordierstatic struct dsk {
95332130Skevans	unsigned drive;
96332130Skevans	unsigned type;
97332130Skevans	unsigned unit;
98332130Skevans	uint8_t slice;
99332130Skevans	uint8_t part;
100332130Skevans	unsigned start;
101332130Skevans	int init;
10240269Srnordier} dsk;
103232570Sjhbstatic char cmd[512], cmddup[512], knamebuf[1024];
104236405Sjhbstatic const char *kname;
105294766Simpuint32_t opts;
106268475Simpstatic struct bootinfo bootinfo;
107268475Simp#if SERIAL
108149212Siedowsestatic int comspeed = SIOSPD;
109219452Srdivackystatic uint8_t ioctrl = IO_KEYBOARD;
110268475Simp#endif
11140269Srnordier
112284878Sdelphijint main(void);
11340269Srnordiervoid exit(int);
114108000Simpstatic void load(void);
115108000Simpstatic int parse(void);
11640269Srnordierstatic int dskread(void *, unsigned, unsigned);
117104679Sphkstatic void printf(const char *,...);
118104679Sphkstatic void putchar(int);
11940269Srnordierstatic int drvread(void *, unsigned, unsigned);
12040269Srnordierstatic int keyhit(unsigned);
12140404Srnordierstatic int xputc(int);
12240404Srnordierstatic int xgetc(int);
123220389Srdivackystatic inline int getc(int);
12440269Srnordier
125132864Skanstatic void memcpy(void *, const void *, int);
126126891Strhodesstatic void
127132864Skanmemcpy(void *dst, const void *src, int len)
128126891Strhodes{
129332130Skevans	const char *s;
130332130Skevans	char *d;
131132864Skan
132332130Skevans	s = src;
133332130Skevans	d = dst;
134332130Skevans
135332130Skevans	while (len--)
136332130Skevans		*d++ = *s++;
137126891Strhodes}
13862665Sjhb
13962665Sjhbstatic inline int
14062665Sjhbstrcmp(const char *s1, const char *s2)
14162665Sjhb{
142332130Skevans
143332130Skevans	for (; *s1 == *s2 && *s1; s1++, s2++);
144332130Skevans	return ((unsigned char)*s1 - (unsigned char)*s2);
14562665Sjhb}
14662665Sjhb
147173040Sjhb#define	UFS_SMALL_CGBASE
14897860Sphk#include "ufsread.c"
14962665Sjhb
150329099Skevansstatic int
151235988Sglebxfsread(ufs_ino_t inode, void *buf, size_t nbyte)
15262665Sjhb{
153332130Skevans
154332130Skevans	if ((size_t)fsread(inode, buf, nbyte) != nbyte) {
155332130Skevans		printf("Invalid %s\n", "format");
156332130Skevans		return (-1);
157332130Skevans	}
158332130Skevans	return (0);
15962665Sjhb}
16062665Sjhb
16162665Sjhbstatic inline void
162108000Simpgetstr(void)
16362665Sjhb{
164332130Skevans	char *s;
165332130Skevans	int c;
16662665Sjhb
167332130Skevans	s = cmd;
168332130Skevans	for (;;) {
169332130Skevans		switch (c = xgetc(0)) {
170332130Skevans		case 0:
171332130Skevans			break;
172332130Skevans		case '\177':
173332130Skevans		case '\b':
174332130Skevans			if (s > cmd) {
175332130Skevans				s--;
176332130Skevans				printf("\b \b");
177332130Skevans			}
178332130Skevans			break;
179332130Skevans		case '\n':
180332130Skevans		case '\r':
181332130Skevans			*s = 0;
182332130Skevans			return;
183332130Skevans		default:
184332130Skevans			if (s - cmd < sizeof(cmd) - 1)
185332130Skevans				*s++ = c;
186332130Skevans			putchar(c);
187332130Skevans		}
18862665Sjhb	}
18962665Sjhb}
19062665Sjhb
19162665Sjhbstatic inline void
19262665Sjhbputc(int c)
19362665Sjhb{
194332130Skevans
195332130Skevans	v86.addr = 0x10;
196332130Skevans	v86.eax = 0xe00 | (c & 0xff);
197332130Skevans	v86.ebx = 0x7;
198332130Skevans	v86int();
19962665Sjhb}
20062665Sjhb
20140269Srnordierint
20240269Srnordiermain(void)
20340269Srnordier{
204332130Skevans	uint8_t autoboot;
205332130Skevans	ufs_ino_t ino;
206332130Skevans	size_t nbyte;
20740269Srnordier
208332130Skevans	dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
209332130Skevans	v86.ctl = V86_FLAGS;
210332130Skevans	v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
211332130Skevans	dsk.drive = *(uint8_t *)PTOV(ARGS);
212332130Skevans	dsk.type = dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD;
213332130Skevans	dsk.unit = dsk.drive & DRV_MASK;
214332130Skevans	dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
215332130Skevans	bootinfo.bi_version = BOOTINFO_VERSION;
216332130Skevans	bootinfo.bi_size = sizeof(bootinfo);
21794411Spb
218332130Skevans	/* Process configuration file */
21994411Spb
220332130Skevans	autoboot = 1;
22198542Smckusick
222332130Skevans	if ((ino = lookup(PATH_CONFIG)) ||
223332130Skevans	    (ino = lookup(PATH_DOTCONFIG))) {
224332130Skevans		nbyte = fsread(ino, cmd, sizeof(cmd) - 1);
225332130Skevans		cmd[nbyte] = '\0';
226332130Skevans	}
22798542Smckusick
228332130Skevans	if (*cmd) {
229332130Skevans		memcpy(cmddup, cmd, sizeof(cmd));
230332130Skevans		if (parse())
231332130Skevans			autoboot = 0;
232332130Skevans		if (!OPT_CHECK(RBX_QUIET))
233332130Skevans			printf("%s: %s", PATH_CONFIG, cmddup);
234332130Skevans		/* Do not process this command twice */
235332130Skevans		*cmd = 0;
236332130Skevans	}
23794411Spb
238332130Skevans	/*
239332130Skevans	 * Try to exec stage 3 boot loader. If interrupted by a keypress,
240332130Skevans	 * or in case of failure, try to load a kernel directly instead.
241332130Skevans	 */
24294411Spb
243332130Skevans	if (!kname) {
244332130Skevans		kname = PATH_LOADER;
245332130Skevans		if (autoboot && !keyhit(3*SECOND)) {
246332130Skevans			load();
247332130Skevans			kname = PATH_KERNEL;
248332130Skevans		}
24940269Srnordier	}
25094411Spb
251332130Skevans	/* Present the user with the boot2 prompt. */
25294411Spb
253332130Skevans	for (;;) {
254332130Skevans		if (!autoboot || !OPT_CHECK(RBX_QUIET))
255332130Skevans			printf("\nFreeBSD/x86 boot\n"
256332130Skevans				 "Default: %u:%s(%u,%c)%s\n"
257332130Skevans				 "boot: ",
258332130Skevans			    dsk.drive & DRV_MASK, dev_nm[dsk.type], dsk.unit,
259332130Skevans			    'a' + dsk.part, kname);
260332130Skevans		if (DO_SIO)
261332130Skevans			sio_flush();
262332130Skevans		if (!autoboot || keyhit(3*SECOND))
263332130Skevans			getstr();
264332130Skevans		else if (!autoboot || !OPT_CHECK(RBX_QUIET))
265332130Skevans			putchar('\n');
266332130Skevans		autoboot = 0;
267332130Skevans		if (parse())
268332130Skevans			putchar('\a');
269332130Skevans		else
270332130Skevans			load();
271332130Skevans	}
27240269Srnordier}
27340269Srnordier
27462665Sjhb/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
27540269Srnordiervoid
27640269Srnordierexit(int x)
27740269Srnordier{
278332130Skevans
27940269Srnordier}
28040269Srnordier
28140269Srnordierstatic void
282108000Simpload(void)
28340269Srnordier{
284332130Skevans	union {
285332130Skevans		struct exec ex;
286332130Skevans		Elf32_Ehdr eh;
287332130Skevans	} hdr;
288332130Skevans	static Elf32_Phdr ep[2];
289332130Skevans	static Elf32_Shdr es[2];
290332130Skevans	caddr_t p;
291332130Skevans	ufs_ino_t ino;
292332130Skevans	uint32_t addr;
293332130Skevans	int k;
294332130Skevans	uint8_t i, j;
29540269Srnordier
296332130Skevans	if (!(ino = lookup(kname))) {
297332130Skevans		if (!ls)
298332130Skevans			printf("No %s\n", kname);
29940269Srnordier		return;
30040269Srnordier	}
301332130Skevans	if (xfsread(ino, &hdr, sizeof(hdr)))
30240269Srnordier		return;
303332130Skevans
304332130Skevans	if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
305332130Skevans		addr = hdr.ex.a_entry & 0xffffff;
306332130Skevans		p = PTOV(addr);
307332130Skevans		fs_off = PAGE_SIZE;
308332130Skevans		if (xfsread(ino, p, hdr.ex.a_text))
309332130Skevans			return;
310332130Skevans		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
311332130Skevans		if (xfsread(ino, p, hdr.ex.a_data))
312332130Skevans			return;
313332130Skevans	} else if (IS_ELF(hdr.eh)) {
314332130Skevans		fs_off = hdr.eh.e_phoff;
315332130Skevans		for (j = k = 0; k < hdr.eh.e_phnum && j < 2; k++) {
316332130Skevans			if (xfsread(ino, ep + j, sizeof(ep[0])))
317332130Skevans				return;
318332130Skevans			if (ep[j].p_type == PT_LOAD)
319332130Skevans				j++;
320332130Skevans		}
321332130Skevans		for (i = 0; i < 2; i++) {
322332130Skevans			p = PTOV(ep[i].p_paddr & 0xffffff);
323332130Skevans			fs_off = ep[i].p_offset;
324332130Skevans			if (xfsread(ino, p, ep[i].p_filesz))
325332130Skevans				return;
326332130Skevans		}
327332130Skevans		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
328332130Skevans		bootinfo.bi_symtab = VTOP(p);
329332130Skevans		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
330332130Skevans			fs_off = hdr.eh.e_shoff + sizeof(es[0]) *
331332130Skevans			    (hdr.eh.e_shstrndx + 1);
332332130Skevans			if (xfsread(ino, &es, sizeof(es)))
333332130Skevans				return;
334332130Skevans			for (i = 0; i < 2; i++) {
335332130Skevans				*(Elf32_Word *)p = es[i].sh_size;
336332130Skevans				p += sizeof(es[i].sh_size);
337332130Skevans				fs_off = es[i].sh_offset;
338332130Skevans				if (xfsread(ino, p, es[i].sh_size))
339332130Skevans					return;
340332130Skevans				p += es[i].sh_size;
341332130Skevans			}
342332130Skevans		}
343332130Skevans		addr = hdr.eh.e_entry & 0xffffff;
344332130Skevans		bootinfo.bi_esymtab = VTOP(p);
345332130Skevans	} else {
346332130Skevans		printf("Invalid %s\n", "format");
34740269Srnordier		return;
34840269Srnordier	}
349219452Srdivacky
350332130Skevans	bootinfo.bi_kernelname = VTOP(kname);
351332130Skevans	bootinfo.bi_bios_dev = dsk.drive;
352332130Skevans	__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
353332130Skevans	    MAKEBOOTDEV(dev_maj[dsk.type], dsk.slice, dsk.unit, dsk.part),
354332130Skevans	    0, 0, 0, VTOP(&bootinfo));
35540269Srnordier}
35640269Srnordier
35740269Srnordierstatic int
358108000Simpparse()
35940269Srnordier{
360332130Skevans	char *arg, *ep, *p, *q;
361332130Skevans	const char *cp;
362332130Skevans	unsigned int drv;
363332130Skevans	int c, i, j;
364332130Skevans	size_t k;
36540269Srnordier
366332130Skevans	arg = cmd;
367332130Skevans
368332130Skevans	while ((c = *arg++)) {
369332130Skevans		if (c == ' ' || c == '\t' || c == '\n')
370332130Skevans			continue;
371332130Skevans		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
372332130Skevans		ep = p;
373332130Skevans		if (*p)
374332130Skevans			*p++ = 0;
375332130Skevans		if (c == '-') {
376332130Skevans			while ((c = *arg++)) {
377332130Skevans				if (c == 'P') {
378332130Skevans					if (*(uint8_t *)PTOV(0x496) & 0x10) {
379332130Skevans						cp = "yes";
380332130Skevans					} else {
381332130Skevans						opts |= OPT_SET(RBX_DUAL) |
382332130Skevans						    OPT_SET(RBX_SERIAL);
383332130Skevans						cp = "no";
384332130Skevans					}
385332130Skevans					printf("Keyboard: %s\n", cp);
386332130Skevans					continue;
387268475Simp#if SERIAL
388332130Skevans				} else if (c == 'S') {
389332130Skevans					j = 0;
390332130Skevans					while ((u_int)(i = *arg++ - '0') <= 9)
391332130Skevans						j = j * 10 + i;
392332130Skevans					if (j > 0 && i == -'0') {
393332130Skevans						comspeed = j;
394332130Skevans						break;
395332130Skevans					}
396332130Skevans					/*
397332130Skevans					 * Fall through to error below
398332130Skevans					 * ('S' not in optstr[]).
399332130Skevans					 */
400268475Simp#endif
401332130Skevans				}
402332130Skevans				for (i = 0; c != optstr[i]; i++)
403332130Skevans					if (i == NOPT - 1)
404332130Skevans						return (-1);
405332130Skevans				opts ^= OPT_SET(flags[i]);
406332130Skevans			}
407268475Simp#if SERIAL
408332130Skevans			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
409332130Skevans			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
410332130Skevans			if (DO_SIO) {
411332130Skevans				if (sio_init(115200 / comspeed) != 0)
412332130Skevans					ioctrl &= ~IO_SERIAL;
413332130Skevans			}
414268475Simp#endif
415332130Skevans		} else {
416332130Skevans			for (q = arg--; *q && *q != '('; q++);
417332130Skevans			if (*q) {
418332130Skevans				drv = -1;
419332130Skevans				if (arg[1] == ':') {
420332130Skevans					drv = *arg - '0';
421332130Skevans					if (drv > 9)
422332130Skevans						return (-1);
423332130Skevans					arg += 2;
424332130Skevans				}
425332130Skevans				if (q - arg != 2)
426332130Skevans					return (-1);
427332130Skevans				for (i = 0; arg[0] != dev_nm[i][0] ||
428332130Skevans				    arg[1] != dev_nm[i][1]; i++)
429332130Skevans					if (i == NDEV - 1)
430332130Skevans						return (-1);
431332130Skevans				dsk.type = i;
432332130Skevans				arg += 3;
433332130Skevans				dsk.unit = *arg - '0';
434332130Skevans				if (arg[1] != ',' || dsk.unit > 9)
435332130Skevans					return (-1);
436332130Skevans				arg += 2;
437332130Skevans				dsk.slice = WHOLE_DISK_SLICE;
438332130Skevans				if (arg[1] == ',') {
439332130Skevans					dsk.slice = *arg - '0' + 1;
440332130Skevans					if (dsk.slice > NDOSPART + 1)
441332130Skevans						return (-1);
442332130Skevans					arg += 2;
443332130Skevans				}
444332130Skevans				if (arg[1] != ')')
445332130Skevans					return (-1);
446332130Skevans				dsk.part = *arg - 'a';
447332130Skevans				if (dsk.part > 7)
448332130Skevans					return (-1);
449332130Skevans				arg += 2;
450332130Skevans				if (drv == -1)
451332130Skevans					drv = dsk.unit;
452332130Skevans				dsk.drive = (dsk.type <= TYPE_MAXHARD
453332130Skevans				    ? DRV_HARD : 0) + drv;
454332130Skevans				dsk_meta = 0;
455332130Skevans			}
456332130Skevans			k = ep - arg;
457332130Skevans			if (k > 0) {
458332130Skevans				if (k >= sizeof(knamebuf))
459332130Skevans					return (-1);
460332130Skevans				memcpy(knamebuf, arg, k + 1);
461332130Skevans				kname = knamebuf;
462332130Skevans			}
46340269Srnordier		}
464332130Skevans		arg = p;
46540269Srnordier	}
466332130Skevans	return (0);
46740269Srnordier}
46840269Srnordier
46940269Srnordierstatic int
47040269Srnordierdskread(void *buf, unsigned lba, unsigned nblk)
47140269Srnordier{
472332130Skevans	struct dos_partition *dp;
473332130Skevans	struct disklabel *d;
474332130Skevans	char *sec;
475332130Skevans	unsigned i;
476332130Skevans	uint8_t sl;
477332130Skevans	const char *reason;
47840269Srnordier
479332130Skevans	if (!dsk_meta) {
480332130Skevans		sec = dmadat->secbuf;
481332130Skevans		dsk.start = 0;
482332130Skevans		if (drvread(sec, DOSBBSECTOR, 1))
483332130Skevans			return (-1);
484332130Skevans		dp = (void *)(sec + DOSPARTOFF);
485332130Skevans		sl = dsk.slice;
486332130Skevans		if (sl < BASE_SLICE) {
487332130Skevans			for (i = 0; i < NDOSPART; i++)
488332130Skevans				if (dp[i].dp_typ == DOSPTYP_386BSD &&
489332130Skevans				    (dp[i].dp_flag & 0x80 || sl < BASE_SLICE)) {
490332130Skevans					sl = BASE_SLICE + i;
491332130Skevans					if (dp[i].dp_flag & 0x80 ||
492332130Skevans					    dsk.slice == COMPATIBILITY_SLICE)
493332130Skevans						break;
494332130Skevans				}
495332130Skevans			if (dsk.slice == WHOLE_DISK_SLICE)
496332130Skevans				dsk.slice = sl;
49740314Srnordier		}
498332130Skevans		if (sl != WHOLE_DISK_SLICE) {
499332130Skevans			if (sl != COMPATIBILITY_SLICE)
500332130Skevans				dp += sl - BASE_SLICE;
501332130Skevans			if (dp->dp_typ != DOSPTYP_386BSD) {
502332130Skevans				reason = "slice";
503332130Skevans				goto error;
504332130Skevans			}
505332130Skevans			dsk.start = dp->dp_start;
506332130Skevans		}
507332130Skevans		if (drvread(sec, dsk.start + LABELSECTOR, 1))
508332130Skevans			return (-1);
509332130Skevans		d = (void *)(sec + LABELOFFSET);
510332130Skevans		if (d->d_magic != DISKMAGIC || d->d_magic2 != DISKMAGIC) {
511332130Skevans			if (dsk.part != RAW_PART) {
512332130Skevans				reason = "label";
513332130Skevans				goto error;
514332130Skevans			}
515332130Skevans		} else {
516332130Skevans			if (!dsk.init) {
517332130Skevans				if (d->d_type == DTYPE_SCSI)
518332130Skevans					dsk.type = TYPE_DA;
519332130Skevans				dsk.init++;
520332130Skevans			}
521332130Skevans			if (dsk.part >= d->d_npartitions ||
522332130Skevans			    !d->d_partitions[dsk.part].p_size) {
523332130Skevans				reason = "partition";
524332130Skevans				goto error;
525332130Skevans			}
526332130Skevans			dsk.start += d->d_partitions[dsk.part].p_offset;
527332130Skevans			dsk.start -= d->d_partitions[RAW_PART].p_offset;
528332130Skevans		}
52940314Srnordier	}
530332130Skevans	return (drvread(buf, dsk.start + lba, nblk));
531275237Srdivackyerror:
532332130Skevans	printf("Invalid %s\n", reason);
533332130Skevans	return (-1);
53440269Srnordier}
53540269Srnordier
536104679Sphkstatic void
53740269Srnordierprintf(const char *fmt,...)
53840269Srnordier{
539332130Skevans	va_list ap;
540332130Skevans	static char buf[10];
541332130Skevans	char *s;
542332130Skevans	unsigned u;
543332130Skevans	int c;
54440269Srnordier
545332130Skevans	va_start(ap, fmt);
546332130Skevans	while ((c = *fmt++)) {
547332130Skevans		if (c == '%') {
548332130Skevans			c = *fmt++;
549332130Skevans			switch (c) {
550332130Skevans			case 'c':
551332130Skevans				putchar(va_arg(ap, int));
552332130Skevans				continue;
553332130Skevans			case 's':
554332130Skevans				for (s = va_arg(ap, char *); *s; s++)
555332130Skevans					putchar(*s);
556332130Skevans				continue;
557332130Skevans			case 'u':
558332130Skevans				u = va_arg(ap, unsigned);
559332130Skevans				s = buf;
560332130Skevans				do
561332130Skevans					*s++ = '0' + u % 10U;
562332130Skevans				while (u /= 10U);
563332130Skevans				while (--s >= buf)
564332130Skevans					putchar(*s);
565332130Skevans				continue;
566332130Skevans			}
567332130Skevans		}
568332130Skevans		putchar(c);
56940269Srnordier	}
570332130Skevans	va_end(ap);
571332130Skevans	return;
57240269Srnordier}
57340269Srnordier
574104679Sphkstatic void
57540269Srnordierputchar(int c)
57640269Srnordier{
577332130Skevans
578332130Skevans	if (c == '\n')
579332130Skevans		xputc('\r');
580332130Skevans	xputc(c);
58140269Srnordier}
58240269Srnordier
58340269Srnordierstatic int
58440269Srnordierdrvread(void *buf, unsigned lba, unsigned nblk)
58540269Srnordier{
586332130Skevans	static unsigned c = 0x2d5c7c2f;
58740323Srnordier
588332130Skevans	if (!OPT_CHECK(RBX_QUIET)) {
589332130Skevans		xputc(c = c << 8 | c >> 24);
590332130Skevans		xputc('\b');
591332130Skevans	}
592332130Skevans	v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
593332130Skevans	v86.addr = XREADORG;		/* call to xread in boot1 */
594332130Skevans	v86.es = VTOPSEG(buf);
595332130Skevans	v86.eax = lba;
596332130Skevans	v86.ebx = VTOPOFF(buf);
597332130Skevans	v86.ecx = lba >> 16;
598332130Skevans	v86.edx = nblk << 8 | dsk.drive;
599332130Skevans	v86int();
600332130Skevans	v86.ctl = V86_FLAGS;
601332130Skevans	if (V86_CY(v86.efl)) {
602332130Skevans		printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
603332130Skevans		return (-1);
604332130Skevans	}
605332130Skevans	return (0);
60640269Srnordier}
60740269Srnordier
60840269Srnordierstatic int
60940269Srnordierkeyhit(unsigned ticks)
61040269Srnordier{
611332130Skevans	uint32_t t0, t1;
61240269Srnordier
613332130Skevans	if (OPT_CHECK(RBX_NOINTR))
614332130Skevans		return (0);
615332130Skevans	t0 = 0;
616332130Skevans	for (;;) {
617332130Skevans		if (xgetc(1))
618332130Skevans			return (1);
619332130Skevans		t1 = *(uint32_t *)PTOV(0x46c);
620332130Skevans		if (!t0)
621332130Skevans			t0 = t1;
622332130Skevans		if ((uint32_t)(t1 - t0) >= ticks)
623332130Skevans			return (0);
624332130Skevans	}
62540269Srnordier}
62640269Srnordier
62740269Srnordierstatic int
62840404Srnordierxputc(int c)
62940269Srnordier{
630332130Skevans
631332130Skevans	if (DO_KBD)
632332130Skevans		putc(c);
633332130Skevans	if (DO_SIO)
634332130Skevans		sio_putc(c);
635332130Skevans	return (c);
63640404Srnordier}
63740404Srnordier
63840404Srnordierstatic int
639220392Srdivackygetc(int fn)
640220392Srdivacky{
641332130Skevans
642332130Skevans	v86.addr = 0x16;
643332130Skevans	v86.eax = fn << 8;
644332130Skevans	v86int();
645332130Skevans	return (fn == 0 ? v86.eax & 0xff : !V86_ZR(v86.efl));
646220392Srdivacky}
647220392Srdivacky
648220392Srdivackystatic int
64940404Srnordierxgetc(int fn)
65040404Srnordier{
651332130Skevans
652332130Skevans	if (OPT_CHECK(RBX_NOINTR))
653332130Skevans		return (0);
654332130Skevans	for (;;) {
655332130Skevans		if (DO_KBD && getc(1))
656332130Skevans			return (fn ? 1 : getc(0));
657332130Skevans		if (DO_SIO && sio_ischar())
658332130Skevans			return (fn ? 1 : sio_getc());
659332130Skevans		if (fn)
660332130Skevans			return (0);
661332130Skevans	}
66240404Srnordier}
663