zfsboot.c revision 200309
1185029Spjd/*-
2185029Spjd * Copyright (c) 1998 Robert Nordier
3185029Spjd * All rights reserved.
4185029Spjd *
5185029Spjd * Redistribution and use in source and binary forms are freely
6185029Spjd * permitted provided that the above copyright notice and this
7185029Spjd * paragraph and the following disclaimer are duplicated in all
8185029Spjd * such forms.
9185029Spjd *
10185029Spjd * This software is provided "AS IS" and without any express or
11185029Spjd * implied warranties, including, without limitation, the implied
12185029Spjd * warranties of merchantability and fitness for a particular
13185029Spjd * purpose.
14185029Spjd */
15185029Spjd
16185029Spjd#include <sys/cdefs.h>
17185029Spjd__FBSDID("$FreeBSD: head/sys/boot/i386/zfsboot/zfsboot.c 200309 2009-12-09 20:36:56Z jhb $");
18185029Spjd
19185029Spjd#include <sys/param.h>
20185029Spjd#include <sys/errno.h>
21185029Spjd#include <sys/diskmbr.h>
22185096Sdfr#ifdef GPT
23185096Sdfr#include <sys/gpt.h>
24185096Sdfr#endif
25185029Spjd#include <sys/reboot.h>
26185029Spjd#include <sys/queue.h>
27185029Spjd
28185029Spjd#include <machine/bootinfo.h>
29185029Spjd#include <machine/elf.h>
30200309Sjhb#include <machine/pc/bios.h>
31185029Spjd
32185029Spjd#include <stdarg.h>
33185029Spjd#include <stddef.h>
34185029Spjd
35185029Spjd#include <a.out.h>
36185029Spjd
37185029Spjd#include <btxv86.h>
38185029Spjd
39185096Sdfr#ifndef GPT
40185029Spjd#include "zfsboot.h"
41185096Sdfr#endif
42185029Spjd#include "lib.h"
43185029Spjd
44185029Spjd#define IO_KEYBOARD	1
45185029Spjd#define IO_SERIAL	2
46185029Spjd
47185029Spjd#define SECOND		18	/* Circa that many ticks in a second. */
48185029Spjd
49185029Spjd#define RBX_ASKNAME	0x0	/* -a */
50185029Spjd#define RBX_SINGLE	0x1	/* -s */
51185029Spjd/* 0x2 is reserved for log2(RB_NOSYNC). */
52185029Spjd/* 0x3 is reserved for log2(RB_HALT). */
53185029Spjd/* 0x4 is reserved for log2(RB_INITNAME). */
54185029Spjd#define RBX_DFLTROOT	0x5	/* -r */
55185029Spjd#define RBX_KDB 	0x6	/* -d */
56185029Spjd/* 0x7 is reserved for log2(RB_RDONLY). */
57185029Spjd/* 0x8 is reserved for log2(RB_DUMP). */
58185029Spjd/* 0x9 is reserved for log2(RB_MINIROOT). */
59185029Spjd#define RBX_CONFIG	0xa	/* -c */
60185029Spjd#define RBX_VERBOSE	0xb	/* -v */
61185029Spjd#define RBX_SERIAL	0xc	/* -h */
62185029Spjd#define RBX_CDROM	0xd	/* -C */
63185029Spjd/* 0xe is reserved for log2(RB_POWEROFF). */
64185029Spjd#define RBX_GDB 	0xf	/* -g */
65185029Spjd#define RBX_MUTE	0x10	/* -m */
66185029Spjd/* 0x11 is reserved for log2(RB_SELFTEST). */
67185029Spjd/* 0x12 is reserved for boot programs. */
68185029Spjd/* 0x13 is reserved for boot programs. */
69185029Spjd#define RBX_PAUSE	0x14	/* -p */
70185029Spjd#define RBX_QUIET	0x15	/* -q */
71185029Spjd#define RBX_NOINTR	0x1c	/* -n */
72185029Spjd/* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
73185029Spjd#define RBX_DUAL	0x1d	/* -D */
74185029Spjd/* 0x1f is reserved for log2(RB_BOOTINFO). */
75185029Spjd
76185029Spjd/* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
77185029Spjd#define RBX_MASK	(OPT_SET(RBX_ASKNAME) | OPT_SET(RBX_SINGLE) | \
78185029Spjd			OPT_SET(RBX_DFLTROOT) | OPT_SET(RBX_KDB ) | \
79185029Spjd			OPT_SET(RBX_CONFIG) | OPT_SET(RBX_VERBOSE) | \
80185029Spjd			OPT_SET(RBX_SERIAL) | OPT_SET(RBX_CDROM) | \
81185029Spjd			OPT_SET(RBX_GDB ) | OPT_SET(RBX_MUTE) | \
82185029Spjd			OPT_SET(RBX_PAUSE) | OPT_SET(RBX_DUAL))
83185029Spjd
84185029Spjd/* Hint to loader that we came from ZFS */
85185029Spjd#define	KARGS_FLAGS_ZFS		0x4
86185029Spjd
87185029Spjd#define PATH_CONFIG	"/boot.config"
88199714Srnoland#define PATH_BOOT3	"/boot/zfsloader"
89185029Spjd#define PATH_KERNEL	"/boot/kernel/kernel"
90185029Spjd
91185029Spjd#define ARGS		0x900
92185029Spjd#define NOPT		14
93185029Spjd#define NDEV		3
94185029Spjd#define V86_CY(x)	((x) & 1)
95185029Spjd#define V86_ZR(x)	((x) & 0x40)
96185029Spjd
97185029Spjd#define DRV_HARD	0x80
98185029Spjd#define DRV_MASK	0x7f
99185029Spjd
100185029Spjd#define TYPE_AD		0
101185029Spjd#define TYPE_DA		1
102185029Spjd#define TYPE_MAXHARD	TYPE_DA
103185029Spjd#define TYPE_FD		2
104185029Spjd
105185029Spjd#define OPT_SET(opt)	(1 << (opt))
106185029Spjd#define OPT_CHECK(opt)	((opts) & OPT_SET(opt))
107185029Spjd
108185029Spjdextern uint32_t _end;
109185029Spjd
110185096Sdfr#ifdef GPT
111185096Sdfrstatic const uuid_t freebsd_zfs_uuid = GPT_ENT_TYPE_FREEBSD_ZFS;
112185096Sdfr#endif
113185029Spjdstatic const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
114185029Spjdstatic const unsigned char flags[NOPT] = {
115185029Spjd    RBX_DUAL,
116185029Spjd    RBX_SERIAL,
117185029Spjd    RBX_ASKNAME,
118185029Spjd    RBX_CDROM,
119185029Spjd    RBX_CONFIG,
120185029Spjd    RBX_KDB,
121185029Spjd    RBX_GDB,
122185029Spjd    RBX_MUTE,
123185029Spjd    RBX_NOINTR,
124185029Spjd    RBX_PAUSE,
125185029Spjd    RBX_QUIET,
126185029Spjd    RBX_DFLTROOT,
127185029Spjd    RBX_SINGLE,
128185029Spjd    RBX_VERBOSE
129185029Spjd};
130185029Spjd
131185029Spjdstatic const char *const dev_nm[NDEV] = {"ad", "da", "fd"};
132185029Spjdstatic const unsigned char dev_maj[NDEV] = {30, 4, 2};
133185029Spjd
134185029Spjdstruct dsk {
135185029Spjd    unsigned drive;
136185029Spjd    unsigned type;
137185029Spjd    unsigned unit;
138185029Spjd    unsigned slice;
139185029Spjd    unsigned part;
140185029Spjd    int init;
141199579Sjhb    daddr_t start;
142185029Spjd};
143185029Spjdstatic char cmd[512];
144185029Spjdstatic char kname[1024];
145185029Spjdstatic uint32_t opts;
146185029Spjdstatic int comspeed = SIOSPD;
147185029Spjdstatic struct bootinfo bootinfo;
148185029Spjdstatic uint32_t bootdev;
149185029Spjdstatic uint8_t ioctrl = IO_KEYBOARD;
150185029Spjd
151200309Sjhbvm_offset_t	high_heap_base;
152200309Sjhbuint32_t	bios_basemem, bios_extmem, high_heap_size;
153200309Sjhb
154200309Sjhbstatic struct bios_smap smap;
155200309Sjhb
156200309Sjhb/*
157200309Sjhb * The minimum amount of memory to reserve in bios_extmem for the heap.
158200309Sjhb */
159200309Sjhb#define	HEAP_MIN	(3 * 1024 * 1024)
160200309Sjhb
161200309Sjhbstatic char *heap_next;
162200309Sjhbstatic char *heap_end;
163200309Sjhb
164185029Spjd/* Buffers that must not span a 64k boundary. */
165185029Spjd#define READ_BUF_SIZE	8192
166185029Spjdstruct dmadat {
167185029Spjd	char rdbuf[READ_BUF_SIZE];	/* for reading large things */
168185029Spjd	char secbuf[READ_BUF_SIZE];	/* for MBR/disklabel */
169185029Spjd};
170185029Spjdstatic struct dmadat *dmadat;
171185029Spjd
172185029Spjdvoid exit(int);
173185029Spjdstatic void load(void);
174185029Spjdstatic int parse(void);
175185029Spjdstatic void printf(const char *,...);
176185029Spjdstatic void putchar(int);
177200309Sjhbstatic void bios_getmem(void);
178199579Sjhbstatic int drvread(struct dsk *, void *, daddr_t, unsigned);
179185029Spjdstatic int keyhit(unsigned);
180185029Spjdstatic int xputc(int);
181185029Spjdstatic int xgetc(int);
182185029Spjdstatic int getc(int);
183185029Spjd
184185029Spjdstatic void memcpy(void *, const void *, int);
185185029Spjdstatic void
186185029Spjdmemcpy(void *dst, const void *src, int len)
187185029Spjd{
188185029Spjd    const char *s = src;
189185029Spjd    char *d = dst;
190185029Spjd
191185029Spjd    while (len--)
192185029Spjd        *d++ = *s++;
193185029Spjd}
194185029Spjd
195185029Spjdstatic void
196185029Spjdstrcpy(char *dst, const char *src)
197185029Spjd{
198185029Spjd    while (*src)
199185029Spjd	*dst++ = *src++;
200185029Spjd    *dst++ = 0;
201185029Spjd}
202185029Spjd
203185029Spjdstatic void
204185029Spjdstrcat(char *dst, const char *src)
205185029Spjd{
206185029Spjd    while (*dst)
207185029Spjd	dst++;
208185029Spjd    while (*src)
209185029Spjd	*dst++ = *src++;
210185029Spjd    *dst++ = 0;
211185029Spjd}
212185029Spjd
213185029Spjdstatic int
214185029Spjdstrcmp(const char *s1, const char *s2)
215185029Spjd{
216185029Spjd    for (; *s1 == *s2 && *s1; s1++, s2++);
217185029Spjd    return (unsigned char)*s1 - (unsigned char)*s2;
218185029Spjd}
219185029Spjd
220185029Spjdstatic const char *
221185029Spjdstrchr(const char *s, char ch)
222185029Spjd{
223185029Spjd    for (; *s; s++)
224185029Spjd	if (*s == ch)
225185029Spjd		return s;
226185029Spjd    return 0;
227185029Spjd}
228185029Spjd
229185029Spjdstatic int
230185029Spjdmemcmp(const void *p1, const void *p2, size_t n)
231185029Spjd{
232185029Spjd    const char *s1 = (const char *) p1;
233185029Spjd    const char *s2 = (const char *) p2;
234185029Spjd    for (; n > 0 && *s1 == *s2; s1++, s2++, n--);
235185029Spjd    if (n)
236185029Spjd        return (unsigned char)*s1 - (unsigned char)*s2;
237185029Spjd    else
238185029Spjd	return 0;
239185029Spjd}
240185029Spjd
241185029Spjdstatic void
242185029Spjdmemset(void *p, char val, size_t n)
243185029Spjd{
244185029Spjd    char *s = (char *) p;
245185029Spjd    while (n--)
246185029Spjd	*s++ = val;
247185029Spjd}
248185029Spjd
249185029Spjdstatic void *
250185029Spjdmalloc(size_t n)
251185029Spjd{
252185029Spjd	char *p = heap_next;
253185029Spjd	if (p + n > heap_end) {
254185029Spjd		printf("malloc failure\n");
255185029Spjd		for (;;)
256185029Spjd		    ;
257185029Spjd		return 0;
258185029Spjd	}
259185029Spjd	heap_next += n;
260185029Spjd	return p;
261185029Spjd}
262185029Spjd
263185029Spjdstatic size_t
264185029Spjdstrlen(const char *s)
265185029Spjd{
266185029Spjd	size_t len = 0;
267185029Spjd	while (*s++)
268185029Spjd		len++;
269185029Spjd	return len;
270185029Spjd}
271185029Spjd
272185029Spjdstatic char *
273185029Spjdstrdup(const char *s)
274185029Spjd{
275185029Spjd	char *p = malloc(strlen(s) + 1);
276185029Spjd	strcpy(p, s);
277185029Spjd	return p;
278185029Spjd}
279185029Spjd
280185029Spjd#include "zfsimpl.c"
281185029Spjd
282185029Spjd/*
283185029Spjd * Read from a dnode (which must be from a ZPL filesystem).
284185029Spjd */
285185029Spjdstatic int
286185029Spjdzfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t size)
287185029Spjd{
288185029Spjd	const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus;
289185029Spjd	size_t n;
290185029Spjd	int rc;
291185029Spjd
292185029Spjd	n = size;
293185029Spjd	if (*offp + n > zp->zp_size)
294185029Spjd		n = zp->zp_size - *offp;
295185029Spjd
296185029Spjd	rc = dnode_read(spa, dnode, *offp, start, n);
297185029Spjd	if (rc)
298185029Spjd		return (-1);
299185029Spjd	*offp += n;
300185029Spjd
301185029Spjd	return (n);
302185029Spjd}
303185029Spjd
304185029Spjd/*
305185029Spjd * Current ZFS pool
306185029Spjd */
307185029Spjdspa_t *spa;
308185029Spjd
309185029Spjd/*
310185029Spjd * A wrapper for dskread that doesn't have to worry about whether the
311185029Spjd * buffer pointer crosses a 64k boundary.
312185029Spjd */
313185029Spjdstatic int
314185029Spjdvdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
315185029Spjd{
316185029Spjd	char *p;
317199579Sjhb	daddr_t lba;
318199579Sjhb	unsigned int nb;
319185029Spjd	struct dsk *dsk = (struct dsk *) priv;
320185029Spjd
321185029Spjd	if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
322185029Spjd		return -1;
323185029Spjd
324185029Spjd	p = buf;
325185029Spjd	lba = off / DEV_BSIZE;
326185029Spjd	while (bytes > 0) {
327185029Spjd		nb = bytes / DEV_BSIZE;
328185029Spjd		if (nb > READ_BUF_SIZE / DEV_BSIZE)
329185029Spjd			nb = READ_BUF_SIZE / DEV_BSIZE;
330185029Spjd		if (drvread(dsk, dmadat->rdbuf, lba, nb))
331185029Spjd			return -1;
332185029Spjd		memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE);
333185029Spjd		p += nb * DEV_BSIZE;
334185029Spjd		lba += nb;
335185029Spjd		bytes -= nb * DEV_BSIZE;
336185029Spjd	}
337185029Spjd
338185029Spjd	return 0;
339185029Spjd}
340185029Spjd
341185029Spjdstatic int
342185029Spjdxfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte)
343185029Spjd{
344185029Spjd    if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) {
345185029Spjd	printf("Invalid %s\n", "format");
346185029Spjd	return -1;
347185029Spjd    }
348185029Spjd    return 0;
349185029Spjd}
350185029Spjd
351200309Sjhbstatic void
352200309Sjhbbios_getmem(void)
353185029Spjd{
354200309Sjhb    uint64_t size;
355185029Spjd
356200309Sjhb    /* Parse system memory map */
357200309Sjhb    v86.ebx = 0;
358200309Sjhb    do {
359200309Sjhb	v86.ctl = V86_FLAGS;
360200309Sjhb	v86.addr = 0x15;		/* int 0x15 function 0xe820*/
361200309Sjhb	v86.eax = 0xe820;
362200309Sjhb	v86.ecx = sizeof(struct bios_smap);
363200309Sjhb	v86.edx = SMAP_SIG;
364200309Sjhb	v86.es = VTOPSEG(&smap);
365200309Sjhb	v86.edi = VTOPOFF(&smap);
366200309Sjhb	v86int();
367200309Sjhb	if ((v86.efl & 1) || (v86.eax != SMAP_SIG))
368200309Sjhb	    break;
369200309Sjhb	/* look for a low-memory segment that's large enough */
370200309Sjhb	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) &&
371200309Sjhb	    (smap.length >= (512 * 1024)))
372200309Sjhb	    bios_basemem = smap.length;
373200309Sjhb	/* look for the first segment in 'extended' memory */
374200309Sjhb	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) {
375200309Sjhb	    bios_extmem = smap.length;
376200309Sjhb	}
377200309Sjhb
378200309Sjhb	/*
379200309Sjhb	 * Look for the largest segment in 'extended' memory beyond
380200309Sjhb	 * 1MB but below 4GB.
381200309Sjhb	 */
382200309Sjhb	if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) &&
383200309Sjhb	    (smap.base < 0x100000000ull)) {
384200309Sjhb	    size = smap.length;
385200309Sjhb
386200309Sjhb	    /*
387200309Sjhb	     * If this segment crosses the 4GB boundary, truncate it.
388200309Sjhb	     */
389200309Sjhb	    if (smap.base + size > 0x100000000ull)
390200309Sjhb		size = 0x100000000ull - smap.base;
391200309Sjhb
392200309Sjhb	    if (size > high_heap_size) {
393200309Sjhb		high_heap_size = size;
394200309Sjhb		high_heap_base = smap.base;
395200309Sjhb	    }
396200309Sjhb	}
397200309Sjhb    } while (v86.ebx != 0);
398200309Sjhb
399200309Sjhb    /* Fall back to the old compatibility function for base memory */
400200309Sjhb    if (bios_basemem == 0) {
401200309Sjhb	v86.ctl = 0;
402200309Sjhb	v86.addr = 0x12;		/* int 0x12 */
403200309Sjhb	v86int();
404200309Sjhb
405200309Sjhb	bios_basemem = (v86.eax & 0xffff) * 1024;
406200309Sjhb    }
407200309Sjhb
408200309Sjhb    /* Fall back through several compatibility functions for extended memory */
409200309Sjhb    if (bios_extmem == 0) {
410200309Sjhb	v86.ctl = V86_FLAGS;
411200309Sjhb	v86.addr = 0x15;		/* int 0x15 function 0xe801*/
412200309Sjhb	v86.eax = 0xe801;
413200309Sjhb	v86int();
414200309Sjhb	if (!(v86.efl & 1)) {
415200309Sjhb	    bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024;
416200309Sjhb	}
417200309Sjhb    }
418200309Sjhb    if (bios_extmem == 0) {
419200309Sjhb	v86.ctl = 0;
420200309Sjhb	v86.addr = 0x15;		/* int 0x15 function 0x88*/
421200309Sjhb	v86.eax = 0x8800;
422200309Sjhb	v86int();
423200309Sjhb	bios_extmem = (v86.eax & 0xffff) * 1024;
424200309Sjhb    }
425200309Sjhb
426200309Sjhb    /*
427200309Sjhb     * If we have extended memory and did not find a suitable heap
428200309Sjhb     * region in the SMAP, use the last 3MB of 'extended' memory as a
429200309Sjhb     * high heap candidate.
430200309Sjhb     */
431200309Sjhb    if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) {
432200309Sjhb	high_heap_size = HEAP_MIN;
433200309Sjhb	high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
434200309Sjhb    }
435200309Sjhb}
436200309Sjhb
437185029Spjdstatic inline void
438185029Spjdgetstr(void)
439185029Spjd{
440185029Spjd    char *s;
441185029Spjd    int c;
442185029Spjd
443185029Spjd    s = cmd;
444185029Spjd    for (;;) {
445185029Spjd	switch (c = xgetc(0)) {
446185029Spjd	case 0:
447185029Spjd	    break;
448185029Spjd	case '\177':
449185029Spjd	case '\b':
450185029Spjd	    if (s > cmd) {
451185029Spjd		s--;
452185029Spjd		printf("\b \b");
453185029Spjd	    }
454185029Spjd	    break;
455185029Spjd	case '\n':
456185029Spjd	case '\r':
457185029Spjd	    *s = 0;
458185029Spjd	    return;
459185029Spjd	default:
460185029Spjd	    if (s - cmd < sizeof(cmd) - 1)
461185029Spjd		*s++ = c;
462185029Spjd	    putchar(c);
463185029Spjd	}
464185029Spjd    }
465185029Spjd}
466185029Spjd
467185029Spjdstatic inline void
468185029Spjdputc(int c)
469185029Spjd{
470185029Spjd    v86.addr = 0x10;
471185029Spjd    v86.eax = 0xe00 | (c & 0xff);
472185029Spjd    v86.ebx = 0x7;
473185029Spjd    v86int();
474185029Spjd}
475185029Spjd
476185029Spjd/*
477185029Spjd * Try to detect a device supported by the legacy int13 BIOS
478185029Spjd */
479185029Spjdstatic int
480185029Spjdint13probe(int drive)
481185029Spjd{
482185029Spjd    v86.ctl = V86_FLAGS;
483185029Spjd    v86.addr = 0x13;
484185029Spjd    v86.eax = 0x800;
485185029Spjd    v86.edx = drive;
486185029Spjd    v86int();
487185029Spjd
488185029Spjd    if (!(v86.efl & 0x1) &&				/* carry clear */
489185029Spjd	((v86.edx & 0xff) != (drive & DRV_MASK))) {	/* unit # OK */
490185029Spjd	if ((v86.ecx & 0x3f) == 0) {			/* absurd sector size */
491185029Spjd		return(0);				/* skip device */
492185029Spjd	}
493185029Spjd	return (1);
494185029Spjd    }
495185029Spjd    return(0);
496185029Spjd}
497185029Spjd
498192194Sdfr/*
499192194Sdfr * We call this when we find a ZFS vdev - ZFS consumes the dsk
500192194Sdfr * structure so we must make a new one.
501192194Sdfr */
502192194Sdfrstatic struct dsk *
503192194Sdfrcopy_dsk(struct dsk *dsk)
504192194Sdfr{
505192194Sdfr    struct dsk *newdsk;
506192194Sdfr
507192194Sdfr    newdsk = malloc(sizeof(struct dsk));
508192194Sdfr    *newdsk = *dsk;
509192194Sdfr    return (newdsk);
510192194Sdfr}
511192194Sdfr
512185029Spjdstatic void
513185029Spjdprobe_drive(struct dsk *dsk, spa_t **spap)
514185029Spjd{
515185096Sdfr#ifdef GPT
516185096Sdfr    struct gpt_hdr hdr;
517185096Sdfr    struct gpt_ent *ent;
518185096Sdfr    daddr_t slba, elba;
519185096Sdfr    unsigned part, entries_per_sec;
520185096Sdfr#endif
521185029Spjd    struct dos_partition *dp;
522185029Spjd    char *sec;
523185029Spjd    unsigned i;
524185029Spjd
525185029Spjd    /*
526185029Spjd     * If we find a vdev on the whole disk, stop here. Otherwise dig
527185029Spjd     * out the MBR and probe each slice in turn for a vdev.
528185029Spjd     */
529185029Spjd    if (vdev_probe(vdev_read, dsk, spap) == 0)
530185029Spjd	return;
531185029Spjd
532185029Spjd    sec = dmadat->secbuf;
533185029Spjd    dsk->start = 0;
534185096Sdfr
535185096Sdfr#ifdef GPT
536185096Sdfr    /*
537185096Sdfr     * First check for GPT.
538185096Sdfr     */
539185096Sdfr    if (drvread(dsk, sec, 1, 1)) {
540185096Sdfr	return;
541185096Sdfr    }
542185096Sdfr    memcpy(&hdr, sec, sizeof(hdr));
543185096Sdfr    if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 ||
544185096Sdfr	hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 ||
545185096Sdfr	hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) {
546185096Sdfr	goto trymbr;
547185096Sdfr    }
548185096Sdfr
549185096Sdfr    /*
550185096Sdfr     * Probe all GPT partitions for the presense of ZFS pools. We
551185096Sdfr     * return the spa_t for the first we find (if requested). This
552185096Sdfr     * will have the effect of booting from the first pool on the
553185096Sdfr     * disk.
554185096Sdfr     */
555185096Sdfr    entries_per_sec = DEV_BSIZE / hdr.hdr_entsz;
556185096Sdfr    slba = hdr.hdr_lba_table;
557185096Sdfr    elba = slba + hdr.hdr_entries / entries_per_sec;
558185096Sdfr    while (slba < elba) {
559198420Srnoland	dsk->start = 0;
560185096Sdfr	if (drvread(dsk, sec, slba, 1))
561185096Sdfr	    return;
562185096Sdfr	for (part = 0; part < entries_per_sec; part++) {
563185096Sdfr	    ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz);
564185096Sdfr	    if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
565185096Sdfr		     sizeof(uuid_t)) == 0) {
566185096Sdfr		dsk->start = ent->ent_lba_start;
567185096Sdfr		if (vdev_probe(vdev_read, dsk, spap) == 0) {
568185096Sdfr		    /*
569185096Sdfr		     * We record the first pool we find (we will try
570192194Sdfr		     * to boot from that one).
571185096Sdfr		     */
572185096Sdfr		    spap = 0;
573185096Sdfr
574185096Sdfr		    /*
575185096Sdfr		     * This slice had a vdev. We need a new dsk
576185096Sdfr		     * structure now since the vdev now owns this one.
577185096Sdfr		     */
578192194Sdfr		    dsk = copy_dsk(dsk);
579185096Sdfr		}
580185096Sdfr	    }
581185096Sdfr	}
582185096Sdfr	slba++;
583185096Sdfr    }
584185096Sdfr    return;
585185096Sdfrtrymbr:
586185096Sdfr#endif
587185096Sdfr
588185029Spjd    if (drvread(dsk, sec, DOSBBSECTOR, 1))
589185029Spjd	return;
590185029Spjd    dp = (void *)(sec + DOSPARTOFF);
591185029Spjd
592185029Spjd    for (i = 0; i < NDOSPART; i++) {
593185029Spjd	if (!dp[i].dp_typ)
594185029Spjd	    continue;
595185029Spjd	dsk->start = dp[i].dp_start;
596185029Spjd	if (vdev_probe(vdev_read, dsk, spap) == 0) {
597185029Spjd	    /*
598185029Spjd	     * We record the first pool we find (we will try to boot
599185029Spjd	     * from that one.
600185029Spjd	     */
601185029Spjd	    spap = 0;
602185029Spjd
603185029Spjd	    /*
604185029Spjd	     * This slice had a vdev. We need a new dsk structure now
605185096Sdfr	     * since the vdev now owns this one.
606185029Spjd	     */
607192194Sdfr	    dsk = copy_dsk(dsk);
608185029Spjd	}
609185029Spjd    }
610185029Spjd}
611185029Spjd
612185029Spjdint
613185029Spjdmain(void)
614185029Spjd{
615185029Spjd    int autoboot, i;
616185029Spjd    dnode_phys_t dn;
617185029Spjd    off_t off;
618185029Spjd    struct dsk *dsk;
619185029Spjd
620200309Sjhb    bios_getmem();
621200309Sjhb
622200309Sjhb    if (high_heap_size > 0) {
623200309Sjhb	heap_end = PTOV(high_heap_base + high_heap_size);
624200309Sjhb	heap_next = PTOV(high_heap_base);
625200309Sjhb    } else {
626200309Sjhb	heap_next = (char *) dmadat + sizeof(*dmadat);
627200309Sjhb	heap_end = (char *) PTOV(bios_basemem);
628200309Sjhb    }
629200309Sjhb
630185029Spjd    dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
631185029Spjd    v86.ctl = V86_FLAGS;
632185029Spjd
633185029Spjd    dsk = malloc(sizeof(struct dsk));
634185029Spjd    dsk->drive = *(uint8_t *)PTOV(ARGS);
635185029Spjd    dsk->type = dsk->drive & DRV_HARD ? TYPE_AD : TYPE_FD;
636185029Spjd    dsk->unit = dsk->drive & DRV_MASK;
637185029Spjd    dsk->slice = *(uint8_t *)PTOV(ARGS + 1) + 1;
638185029Spjd    dsk->part = 0;
639185029Spjd    dsk->start = 0;
640185029Spjd    dsk->init = 0;
641185029Spjd
642185029Spjd    bootinfo.bi_version = BOOTINFO_VERSION;
643185029Spjd    bootinfo.bi_size = sizeof(bootinfo);
644200309Sjhb    bootinfo.bi_basemem = bios_basemem / 1024;
645200309Sjhb    bootinfo.bi_extmem = bios_extmem / 1024;
646185029Spjd    bootinfo.bi_memsizes_valid++;
647185029Spjd    bootinfo.bi_bios_dev = dsk->drive;
648185029Spjd
649185029Spjd    bootdev = MAKEBOOTDEV(dev_maj[dsk->type],
650185029Spjd			  dsk->slice, dsk->unit, dsk->part),
651185029Spjd
652185029Spjd    /* Process configuration file */
653185029Spjd
654185029Spjd    autoboot = 1;
655185029Spjd
656185029Spjd    zfs_init();
657185029Spjd
658185029Spjd    /*
659185029Spjd     * Probe the boot drive first - we will try to boot from whatever
660185029Spjd     * pool we find on that drive.
661185029Spjd     */
662185029Spjd    probe_drive(dsk, &spa);
663185029Spjd
664185029Spjd    /*
665185029Spjd     * Probe the rest of the drives that the bios knows about. This
666185029Spjd     * will find any other available pools and it may fill in missing
667185029Spjd     * vdevs for the boot pool.
668185029Spjd     */
669192194Sdfr    for (i = 0; i < 128; i++) {
670185029Spjd	if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS))
671185029Spjd	    continue;
672185029Spjd
673192194Sdfr	if (!int13probe(i | DRV_HARD))
674192194Sdfr	    break;
675192194Sdfr
676185029Spjd	dsk = malloc(sizeof(struct dsk));
677185029Spjd	dsk->drive = i | DRV_HARD;
678185029Spjd	dsk->type = dsk->drive & TYPE_AD;
679185029Spjd	dsk->unit = i;
680185029Spjd	dsk->slice = 0;
681185029Spjd	dsk->part = 0;
682185029Spjd	dsk->start = 0;
683185029Spjd	dsk->init = 0;
684185029Spjd	probe_drive(dsk, 0);
685185029Spjd    }
686185029Spjd
687185029Spjd    /*
688185029Spjd     * If we didn't find a pool on the boot drive, default to the
689185029Spjd     * first pool we found, if any.
690185029Spjd     */
691185029Spjd    if (!spa) {
692185029Spjd	spa = STAILQ_FIRST(&zfs_pools);
693185029Spjd	if (!spa) {
694185029Spjd	    printf("No ZFS pools located, can't boot\n");
695185029Spjd	    for (;;)
696185029Spjd		;
697185029Spjd	}
698185029Spjd    }
699185029Spjd
700185029Spjd    zfs_mount_pool(spa);
701185029Spjd
702185029Spjd    if (zfs_lookup(spa, PATH_CONFIG, &dn) == 0) {
703185029Spjd	off = 0;
704198079Sjhb	zfs_read(spa, &dn, &off, cmd, sizeof(cmd));
705185029Spjd    }
706185029Spjd
707185029Spjd    if (*cmd) {
708185029Spjd	if (parse())
709185029Spjd	    autoboot = 0;
710185029Spjd	if (!OPT_CHECK(RBX_QUIET))
711185029Spjd	    printf("%s: %s", PATH_CONFIG, cmd);
712185029Spjd	/* Do not process this command twice */
713185029Spjd	*cmd = 0;
714185029Spjd    }
715185029Spjd
716185029Spjd    /*
717185029Spjd     * Try to exec stage 3 boot loader. If interrupted by a keypress,
718185029Spjd     * or in case of failure, try to load a kernel directly instead.
719185029Spjd     */
720185029Spjd
721185029Spjd    if (autoboot && !*kname) {
722185029Spjd	memcpy(kname, PATH_BOOT3, sizeof(PATH_BOOT3));
723185029Spjd	if (!keyhit(3*SECOND)) {
724185029Spjd	    load();
725185029Spjd	    memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL));
726185029Spjd	}
727185029Spjd    }
728185029Spjd
729185029Spjd    /* Present the user with the boot2 prompt. */
730185029Spjd
731185029Spjd    for (;;) {
732185029Spjd	if (!autoboot || !OPT_CHECK(RBX_QUIET))
733185029Spjd	    printf("\nFreeBSD/i386 boot\n"
734185029Spjd		   "Default: %s:%s\n"
735185029Spjd		   "boot: ",
736185029Spjd		   spa->spa_name, kname);
737185029Spjd	if (ioctrl & IO_SERIAL)
738185029Spjd	    sio_flush();
739185029Spjd	if (!autoboot || keyhit(5*SECOND))
740185029Spjd	    getstr();
741185029Spjd	else if (!autoboot || !OPT_CHECK(RBX_QUIET))
742185029Spjd	    putchar('\n');
743185029Spjd	autoboot = 0;
744185029Spjd	if (parse())
745185029Spjd	    putchar('\a');
746185029Spjd	else
747185029Spjd	    load();
748185029Spjd    }
749185029Spjd}
750185029Spjd
751185029Spjd/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
752185029Spjdvoid
753185029Spjdexit(int x)
754185029Spjd{
755185029Spjd}
756185029Spjd
757185029Spjdstatic void
758185029Spjdload(void)
759185029Spjd{
760185029Spjd    union {
761185029Spjd	struct exec ex;
762185029Spjd	Elf32_Ehdr eh;
763185029Spjd    } hdr;
764185029Spjd    static Elf32_Phdr ep[2];
765185029Spjd    static Elf32_Shdr es[2];
766185029Spjd    caddr_t p;
767185029Spjd    dnode_phys_t dn;
768185029Spjd    off_t off;
769185029Spjd    uint32_t addr, x;
770185029Spjd    int fmt, i, j;
771185029Spjd
772185029Spjd    if (zfs_lookup(spa, kname, &dn)) {
773185029Spjd	return;
774185029Spjd    }
775185029Spjd    off = 0;
776185029Spjd    if (xfsread(&dn, &off, &hdr, sizeof(hdr)))
777185029Spjd	return;
778185029Spjd    if (N_GETMAGIC(hdr.ex) == ZMAGIC)
779185029Spjd	fmt = 0;
780185029Spjd    else if (IS_ELF(hdr.eh))
781185029Spjd	fmt = 1;
782185029Spjd    else {
783185029Spjd	printf("Invalid %s\n", "format");
784185029Spjd	return;
785185029Spjd    }
786185029Spjd    if (fmt == 0) {
787185029Spjd	addr = hdr.ex.a_entry & 0xffffff;
788185029Spjd	p = PTOV(addr);
789185029Spjd	off = PAGE_SIZE;
790185029Spjd	if (xfsread(&dn, &off, p, hdr.ex.a_text))
791185029Spjd	    return;
792185029Spjd	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
793185029Spjd	if (xfsread(&dn, &off, p, hdr.ex.a_data))
794185029Spjd	    return;
795185029Spjd	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
796185029Spjd	bootinfo.bi_symtab = VTOP(p);
797185029Spjd	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
798185029Spjd	p += sizeof(hdr.ex.a_syms);
799185029Spjd	if (hdr.ex.a_syms) {
800185029Spjd	    if (xfsread(&dn, &off, p, hdr.ex.a_syms))
801185029Spjd		return;
802185029Spjd	    p += hdr.ex.a_syms;
803185029Spjd	    if (xfsread(&dn, &off, p, sizeof(int)))
804185029Spjd		return;
805185029Spjd	    x = *(uint32_t *)p;
806185029Spjd	    p += sizeof(int);
807185029Spjd	    x -= sizeof(int);
808185029Spjd	    if (xfsread(&dn, &off, p, x))
809185029Spjd		return;
810185029Spjd	    p += x;
811185029Spjd	}
812185029Spjd    } else {
813185029Spjd	off = hdr.eh.e_phoff;
814185029Spjd	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
815185029Spjd	    if (xfsread(&dn, &off, ep + j, sizeof(ep[0])))
816185029Spjd		return;
817185029Spjd	    if (ep[j].p_type == PT_LOAD)
818185029Spjd		j++;
819185029Spjd	}
820185029Spjd	for (i = 0; i < 2; i++) {
821185029Spjd	    p = PTOV(ep[i].p_paddr & 0xffffff);
822185029Spjd	    off = ep[i].p_offset;
823185029Spjd	    if (xfsread(&dn, &off, p, ep[i].p_filesz))
824185029Spjd		return;
825185029Spjd	}
826185029Spjd	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
827185029Spjd	bootinfo.bi_symtab = VTOP(p);
828185029Spjd	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
829185029Spjd	    off = hdr.eh.e_shoff + sizeof(es[0]) *
830185029Spjd		(hdr.eh.e_shstrndx + 1);
831185029Spjd	    if (xfsread(&dn, &off, &es, sizeof(es)))
832185029Spjd		return;
833185029Spjd	    for (i = 0; i < 2; i++) {
834185029Spjd		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
835185029Spjd		p += sizeof(es[i].sh_size);
836185029Spjd		off = es[i].sh_offset;
837185029Spjd		if (xfsread(&dn, &off, p, es[i].sh_size))
838185029Spjd		    return;
839185029Spjd		p += es[i].sh_size;
840185029Spjd	    }
841185029Spjd	}
842185029Spjd	addr = hdr.eh.e_entry & 0xffffff;
843185029Spjd    }
844185029Spjd    bootinfo.bi_esymtab = VTOP(p);
845185029Spjd    bootinfo.bi_kernelname = VTOP(kname);
846185029Spjd    __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
847185029Spjd	   bootdev,
848185029Spjd	   KARGS_FLAGS_ZFS,
849185029Spjd	   (uint32_t) spa->spa_guid,
850185029Spjd	   (uint32_t) (spa->spa_guid >> 32),
851185029Spjd	   VTOP(&bootinfo));
852185029Spjd}
853185029Spjd
854185029Spjdstatic int
855185029Spjdparse()
856185029Spjd{
857185029Spjd    char *arg = cmd;
858185029Spjd    char *ep, *p, *q;
859185029Spjd    const char *cp;
860185029Spjd    //unsigned int drv;
861185029Spjd    int c, i, j;
862185029Spjd
863185029Spjd    while ((c = *arg++)) {
864185029Spjd	if (c == ' ' || c == '\t' || c == '\n')
865185029Spjd	    continue;
866185029Spjd	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
867185029Spjd	ep = p;
868185029Spjd	if (*p)
869185029Spjd	    *p++ = 0;
870185029Spjd	if (c == '-') {
871185029Spjd	    while ((c = *arg++)) {
872185029Spjd		if (c == 'P') {
873185029Spjd		    if (*(uint8_t *)PTOV(0x496) & 0x10) {
874185029Spjd			cp = "yes";
875185029Spjd		    } else {
876185029Spjd			opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
877185029Spjd			cp = "no";
878185029Spjd		    }
879185029Spjd		    printf("Keyboard: %s\n", cp);
880185029Spjd		    continue;
881185029Spjd		} else if (c == 'S') {
882185029Spjd		    j = 0;
883185029Spjd		    while ((unsigned int)(i = *arg++ - '0') <= 9)
884185029Spjd			j = j * 10 + i;
885185029Spjd		    if (j > 0 && i == -'0') {
886185029Spjd			comspeed = j;
887185029Spjd			break;
888185029Spjd		    }
889185029Spjd		    /* Fall through to error below ('S' not in optstr[]). */
890185029Spjd		}
891185029Spjd		for (i = 0; c != optstr[i]; i++)
892185029Spjd		    if (i == NOPT - 1)
893185029Spjd			return -1;
894185029Spjd		opts ^= OPT_SET(flags[i]);
895185029Spjd	    }
896185029Spjd	    ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
897185029Spjd		     OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
898185029Spjd	    if (ioctrl & IO_SERIAL)
899185029Spjd	        sio_init(115200 / comspeed);
900185029Spjd	} if (c == '?') {
901185029Spjd	    dnode_phys_t dn;
902185029Spjd
903185029Spjd	    if (zfs_lookup(spa, arg, &dn) == 0) {
904185029Spjd		zap_list(spa, &dn);
905185029Spjd	    }
906185029Spjd	    return -1;
907185029Spjd	} else {
908185029Spjd	    arg--;
909185029Spjd
910185029Spjd	    /*
911185029Spjd	     * Report pool status if the comment is 'status'. Lets
912185029Spjd	     * hope no-one wants to load /status as a kernel.
913185029Spjd	     */
914185029Spjd	    if (!strcmp(arg, "status")) {
915185029Spjd		spa_all_status();
916185029Spjd		return -1;
917185029Spjd	    }
918185029Spjd
919185029Spjd	    /*
920185029Spjd	     * If there is a colon, switch pools.
921185029Spjd	     */
922185029Spjd	    q = (char *) strchr(arg, ':');
923185029Spjd	    if (q) {
924185029Spjd		spa_t *newspa;
925185029Spjd
926185029Spjd		*q++ = 0;
927185029Spjd		newspa = spa_find_by_name(arg);
928185029Spjd		if (newspa) {
929185029Spjd		    spa = newspa;
930185029Spjd		    zfs_mount_pool(spa);
931185029Spjd		} else {
932185029Spjd		    printf("\nCan't find ZFS pool %s\n", arg);
933185029Spjd		    return -1;
934185029Spjd		}
935185029Spjd		arg = q;
936185029Spjd	    }
937185029Spjd	    if ((i = ep - arg)) {
938185029Spjd		if ((size_t)i >= sizeof(kname))
939185029Spjd		    return -1;
940185029Spjd		memcpy(kname, arg, i + 1);
941185029Spjd	    }
942185029Spjd	}
943185029Spjd	arg = p;
944185029Spjd    }
945185029Spjd    return 0;
946185029Spjd}
947185029Spjd
948185029Spjdstatic void
949185029Spjdprintf(const char *fmt,...)
950185029Spjd{
951185029Spjd    va_list ap;
952198420Srnoland    char buf[20];
953185029Spjd    char *s;
954198420Srnoland    unsigned long long u;
955185029Spjd    int c;
956185029Spjd    int minus;
957185029Spjd    int prec;
958198420Srnoland    int l;
959185029Spjd    int len;
960185029Spjd    int pad;
961185029Spjd
962185029Spjd    va_start(ap, fmt);
963185029Spjd    while ((c = *fmt++)) {
964185029Spjd	if (c == '%') {
965185029Spjd	    minus = 0;
966185029Spjd	    prec = 0;
967198420Srnoland	    l = 0;
968185029Spjd	nextfmt:
969185029Spjd	    c = *fmt++;
970185029Spjd	    switch (c) {
971185029Spjd	    case '-':
972185029Spjd		minus = 1;
973185029Spjd		goto nextfmt;
974185029Spjd	    case '0':
975185029Spjd	    case '1':
976185029Spjd	    case '2':
977185029Spjd	    case '3':
978185029Spjd	    case '4':
979185029Spjd	    case '5':
980185029Spjd	    case '6':
981185029Spjd	    case '7':
982185029Spjd	    case '8':
983185029Spjd	    case '9':
984185029Spjd		prec = 10 * prec + (c - '0');
985185029Spjd		goto nextfmt;
986185029Spjd	    case 'c':
987185029Spjd		putchar(va_arg(ap, int));
988185029Spjd		continue;
989198420Srnoland	    case 'l':
990198420Srnoland		l++;
991198420Srnoland		goto nextfmt;
992185029Spjd	    case 's':
993185029Spjd		s = va_arg(ap, char *);
994185029Spjd		if (prec) {
995185029Spjd		    len = strlen(s);
996185029Spjd		    if (len < prec)
997185029Spjd			pad = prec - len;
998185029Spjd		    else
999185029Spjd			pad = 0;
1000185029Spjd		    if (minus)
1001185029Spjd			while (pad--)
1002185029Spjd			    putchar(' ');
1003185029Spjd		    for (; *s; s++)
1004185029Spjd			putchar(*s);
1005185029Spjd		    if (!minus)
1006185029Spjd			while (pad--)
1007185029Spjd			    putchar(' ');
1008185029Spjd		} else {
1009185029Spjd		    for (; *s; s++)
1010185029Spjd			putchar(*s);
1011185029Spjd		}
1012185029Spjd		continue;
1013185029Spjd	    case 'u':
1014198420Srnoland		switch (l) {
1015198420Srnoland		case 2:
1016198420Srnoland		    u = va_arg(ap, unsigned long long);
1017198420Srnoland		    break;
1018198420Srnoland		case 1:
1019198420Srnoland		    u = va_arg(ap, unsigned long);
1020198420Srnoland		    break;
1021198420Srnoland		default:
1022198420Srnoland		    u = va_arg(ap, unsigned);
1023198420Srnoland		    break;
1024198420Srnoland		}
1025185029Spjd		s = buf;
1026185029Spjd		do
1027185029Spjd		    *s++ = '0' + u % 10U;
1028185029Spjd		while (u /= 10U);
1029185029Spjd		while (--s >= buf)
1030185029Spjd		    putchar(*s);
1031185029Spjd		continue;
1032185029Spjd	    }
1033185029Spjd	}
1034185029Spjd	putchar(c);
1035185029Spjd    }
1036185029Spjd    va_end(ap);
1037185029Spjd    return;
1038185029Spjd}
1039185029Spjd
1040185029Spjdstatic void
1041185029Spjdputchar(int c)
1042185029Spjd{
1043185029Spjd    if (c == '\n')
1044185029Spjd	xputc('\r');
1045185029Spjd    xputc(c);
1046185029Spjd}
1047185029Spjd
1048185096Sdfr#ifdef GPT
1049185096Sdfrstatic struct {
1050185096Sdfr	uint16_t len;
1051185096Sdfr	uint16_t count;
1052185096Sdfr	uint16_t seg;
1053185096Sdfr	uint16_t off;
1054185096Sdfr	uint64_t lba;
1055185096Sdfr} packet;
1056185096Sdfr#endif
1057185096Sdfr
1058185029Spjdstatic int
1059199579Sjhbdrvread(struct dsk *dsk, void *buf, daddr_t lba, unsigned nblk)
1060185029Spjd{
1061185096Sdfr#ifdef GPT
1062192194Sdfr    static unsigned c = 0x2d5c7c2f;
1063185096Sdfr
1064185096Sdfr    if (!OPT_CHECK(RBX_QUIET))
1065185096Sdfr	printf("%c\b", c = c << 8 | c >> 24);
1066185096Sdfr    packet.len = 0x10;
1067185096Sdfr    packet.count = nblk;
1068185096Sdfr    packet.seg = VTOPOFF(buf);
1069185096Sdfr    packet.off = VTOPSEG(buf);
1070185096Sdfr    packet.lba = lba + dsk->start;
1071185096Sdfr    v86.ctl = V86_FLAGS;
1072185096Sdfr    v86.addr = 0x13;
1073185096Sdfr    v86.eax = 0x4200;
1074185096Sdfr    v86.edx = dsk->drive;
1075185096Sdfr    v86.ds = VTOPSEG(&packet);
1076185096Sdfr    v86.esi = VTOPOFF(&packet);
1077185096Sdfr    v86int();
1078185096Sdfr    if (V86_CY(v86.efl)) {
1079185096Sdfr	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
1080185096Sdfr	return -1;
1081185096Sdfr    }
1082185096Sdfr    return 0;
1083185096Sdfr#else
1084185029Spjd    static unsigned c = 0x2d5c7c2f;
1085185029Spjd
1086185029Spjd    lba += dsk->start;
1087185029Spjd    if (!OPT_CHECK(RBX_QUIET))
1088185029Spjd	printf("%c\b", c = c << 8 | c >> 24);
1089185029Spjd    v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS;
1090185029Spjd    v86.addr = XREADORG;		/* call to xread in boot1 */
1091185029Spjd    v86.es = VTOPSEG(buf);
1092185029Spjd    v86.eax = lba;
1093185029Spjd    v86.ebx = VTOPOFF(buf);
1094199579Sjhb    v86.ecx = lba >> 32;
1095185029Spjd    v86.edx = nblk << 8 | dsk->drive;
1096185029Spjd    v86int();
1097185029Spjd    v86.ctl = V86_FLAGS;
1098185029Spjd    if (V86_CY(v86.efl)) {
1099185029Spjd	printf("error %u lba %u\n", v86.eax >> 8 & 0xff, lba);
1100185029Spjd	return -1;
1101185029Spjd    }
1102185029Spjd    return 0;
1103185096Sdfr#endif
1104185029Spjd}
1105185029Spjd
1106185029Spjdstatic int
1107185029Spjdkeyhit(unsigned ticks)
1108185029Spjd{
1109185029Spjd    uint32_t t0, t1;
1110185029Spjd
1111185029Spjd    if (OPT_CHECK(RBX_NOINTR))
1112185029Spjd	return 0;
1113185029Spjd    t0 = 0;
1114185029Spjd    for (;;) {
1115185029Spjd	if (xgetc(1))
1116185029Spjd	    return 1;
1117185029Spjd	t1 = *(uint32_t *)PTOV(0x46c);
1118185029Spjd	if (!t0)
1119185029Spjd	    t0 = t1;
1120185029Spjd	if (t1 < t0 || t1 >= t0 + ticks)
1121185029Spjd	    return 0;
1122185029Spjd    }
1123185029Spjd}
1124185029Spjd
1125185029Spjdstatic int
1126185029Spjdxputc(int c)
1127185029Spjd{
1128185029Spjd    if (ioctrl & IO_KEYBOARD)
1129185029Spjd	putc(c);
1130185029Spjd    if (ioctrl & IO_SERIAL)
1131185029Spjd	sio_putc(c);
1132185029Spjd    return c;
1133185029Spjd}
1134185029Spjd
1135185029Spjdstatic int
1136185029Spjdxgetc(int fn)
1137185029Spjd{
1138185029Spjd    if (OPT_CHECK(RBX_NOINTR))
1139185029Spjd	return 0;
1140185029Spjd    for (;;) {
1141185029Spjd	if (ioctrl & IO_KEYBOARD && getc(1))
1142185029Spjd	    return fn ? 1 : getc(0);
1143185029Spjd	if (ioctrl & IO_SERIAL && sio_ischar())
1144185029Spjd	    return fn ? 1 : sio_getc();
1145185029Spjd	if (fn)
1146185029Spjd	    return 0;
1147185029Spjd    }
1148185029Spjd}
1149185029Spjd
1150185029Spjdstatic int
1151185029Spjdgetc(int fn)
1152185029Spjd{
1153185029Spjd    /*
1154185029Spjd     * The extra comparison against zero is an attempt to work around
1155185029Spjd     * what appears to be a bug in QEMU and Bochs. Both emulators
1156185029Spjd     * sometimes report a key-press with scancode one and ascii zero
1157185029Spjd     * when no such key is pressed in reality. As far as I can tell,
1158185029Spjd     * this only happens shortly after a reboot.
1159185029Spjd     */
1160185029Spjd    v86.addr = 0x16;
1161185029Spjd    v86.eax = fn << 8;
1162185029Spjd    v86int();
1163185029Spjd    return fn == 0 ? v86.eax & 0xff : (!V86_ZR(v86.efl) && (v86.eax & 0xff));
1164185029Spjd}
1165