xref: /freebsd/stand/i386/zfsboot/zfsboot.c (revision 1dc762d4)
1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh  * Copyright (c) 1998 Robert Nordier
3ca987d46SWarner Losh  * All rights reserved.
4ca987d46SWarner Losh  *
5ca987d46SWarner Losh  * Redistribution and use in source and binary forms are freely
6ca987d46SWarner Losh  * permitted provided that the above copyright notice and this
7ca987d46SWarner Losh  * paragraph and the following disclaimer are duplicated in all
8ca987d46SWarner Losh  * such forms.
9ca987d46SWarner Losh  *
10ca987d46SWarner Losh  * This software is provided "AS IS" and without any express or
11ca987d46SWarner Losh  * implied warranties, including, without limitation, the implied
12ca987d46SWarner Losh  * warranties of merchantability and fitness for a particular
13ca987d46SWarner Losh  * purpose.
14ca987d46SWarner Losh  */
15ca987d46SWarner Losh 
16ca987d46SWarner Losh #include <sys/cdefs.h>
17ca987d46SWarner Losh __FBSDID("$FreeBSD$");
18ca987d46SWarner Losh 
193830659eSToomas Soome #include <stand.h>
2065628439SWarner Losh 
21ca987d46SWarner Losh #include <sys/param.h>
22ca987d46SWarner Losh #include <sys/errno.h>
23ca987d46SWarner Losh #include <sys/diskmbr.h>
24ca987d46SWarner Losh #ifdef GPT
25ca987d46SWarner Losh #include <sys/gpt.h>
26ca987d46SWarner Losh #endif
27ca987d46SWarner Losh #include <sys/reboot.h>
28ca987d46SWarner Losh #include <sys/queue.h>
291dc762d4SToomas Soome #ifdef LOADER_ZFS_SUPPORT
30e307eb94SToomas Soome #include <sys/zfs_bootenv.h>
311dc762d4SToomas Soome #endif
32ca987d46SWarner Losh 
33ca987d46SWarner Losh #include <machine/bootinfo.h>
34ca987d46SWarner Losh #include <machine/elf.h>
35ca987d46SWarner Losh #include <machine/pc/bios.h>
36ca987d46SWarner Losh 
37ca987d46SWarner Losh #include <stdarg.h>
38ca987d46SWarner Losh #include <stddef.h>
39ca987d46SWarner Losh 
40ca987d46SWarner Losh #include <a.out.h>
413830659eSToomas Soome #include "bootstrap.h"
423830659eSToomas Soome #include "libi386.h"
43ca987d46SWarner Losh #include <btxv86.h>
44ca987d46SWarner Losh 
45ca987d46SWarner Losh #include "lib.h"
46ca987d46SWarner Losh #include "rbx.h"
47ca987d46SWarner Losh #include "cons.h"
48ca987d46SWarner Losh #include "bootargs.h"
493830659eSToomas Soome #include "disk.h"
503830659eSToomas Soome #include "part.h"
51ca987d46SWarner Losh #include "paths.h"
52ca987d46SWarner Losh 
53ca987d46SWarner Losh #include "libzfs.h"
54ca987d46SWarner Losh 
55ca987d46SWarner Losh #define	ARGS			0x900
56ca987d46SWarner Losh #define	NOPT			14
57ca987d46SWarner Losh #define	NDEV			3
58ca987d46SWarner Losh 
59ca987d46SWarner Losh #define	BIOS_NUMDRIVES		0x475
60ca987d46SWarner Losh #define	DRV_HARD		0x80
61ca987d46SWarner Losh #define	DRV_MASK		0x7f
62ca987d46SWarner Losh 
63ca987d46SWarner Losh #define	TYPE_AD			0
64ca987d46SWarner Losh #define	TYPE_DA			1
65ca987d46SWarner Losh #define	TYPE_MAXHARD		TYPE_DA
66ca987d46SWarner Losh #define	TYPE_FD			2
67ca987d46SWarner Losh 
68ca987d46SWarner Losh extern uint32_t _end;
69ca987d46SWarner Losh 
70ca987d46SWarner Losh static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
71ca987d46SWarner Losh static const unsigned char flags[NOPT] = {
72ca987d46SWarner Losh     RBX_DUAL,
73ca987d46SWarner Losh     RBX_SERIAL,
74ca987d46SWarner Losh     RBX_ASKNAME,
75ca987d46SWarner Losh     RBX_CDROM,
76ca987d46SWarner Losh     RBX_CONFIG,
77ca987d46SWarner Losh     RBX_KDB,
78ca987d46SWarner Losh     RBX_GDB,
79ca987d46SWarner Losh     RBX_MUTE,
80ca987d46SWarner Losh     RBX_NOINTR,
81ca987d46SWarner Losh     RBX_PAUSE,
82ca987d46SWarner Losh     RBX_QUIET,
83ca987d46SWarner Losh     RBX_DFLTROOT,
84ca987d46SWarner Losh     RBX_SINGLE,
85ca987d46SWarner Losh     RBX_VERBOSE
86ca987d46SWarner Losh };
87ca987d46SWarner Losh uint32_t opts;
88ca987d46SWarner Losh 
8963acab6aSWarner Losh /*
9063acab6aSWarner Losh  * Paths to try loading before falling back to the boot2 prompt.
9163acab6aSWarner Losh  *
9263acab6aSWarner Losh  * /boot/zfsloader must be tried before /boot/loader in order to remain
9363acab6aSWarner Losh  * backward compatible with ZFS boot environments where /boot/loader exists
9463acab6aSWarner Losh  * but does not have ZFS support, which was the case before FreeBSD 12.
9563acab6aSWarner Losh  *
9663acab6aSWarner Losh  * If no loader is found, try to load a kernel directly instead.
9763acab6aSWarner Losh  */
9863acab6aSWarner Losh static const struct string {
9963acab6aSWarner Losh 	const char *p;
10063acab6aSWarner Losh 	size_t len;
10163acab6aSWarner Losh } loadpath[] = {
10263acab6aSWarner Losh 	{ PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) },
10363acab6aSWarner Losh 	{ PATH_LOADER, sizeof(PATH_LOADER) },
10463acab6aSWarner Losh 	{ PATH_KERNEL, sizeof(PATH_KERNEL) },
10563acab6aSWarner Losh };
10663acab6aSWarner Losh 
107ca987d46SWarner Losh static const unsigned char dev_maj[NDEV] = {30, 4, 2};
108ca987d46SWarner Losh 
1093830659eSToomas Soome static struct i386_devdesc *bdev;
110ca987d46SWarner Losh static char cmd[512];
111ca987d46SWarner Losh static char cmddup[512];
112ca987d46SWarner Losh static char kname[1024];
113ca987d46SWarner Losh static int comspeed = SIOSPD;
114ca987d46SWarner Losh static struct bootinfo bootinfo;
115ca987d46SWarner Losh static uint32_t bootdev;
116ca987d46SWarner Losh static struct zfs_boot_args zfsargs;
1173830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
1183830659eSToomas Soome static struct geli_boot_args geliargs;
1193830659eSToomas Soome #endif
120ca987d46SWarner Losh 
1213830659eSToomas Soome extern vm_offset_t high_heap_base;
1223830659eSToomas Soome extern uint32_t	bios_basemem, bios_extmem, high_heap_size;
123ca987d46SWarner Losh 
1243830659eSToomas Soome static char *heap_top;
1253830659eSToomas Soome static char *heap_bottom;
126ca987d46SWarner Losh 
127ca987d46SWarner Losh void exit(int);
1283830659eSToomas Soome static void i386_zfs_probe(void);
129ca987d46SWarner Losh static void load(void);
130ca987d46SWarner Losh static int parse_cmd(void);
131ca987d46SWarner Losh 
132ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
133c1418270SIan Lepore #include "geliboot.h"
134ca987d46SWarner Losh static char gelipw[GELI_PW_MAXLEN];
135ca987d46SWarner Losh #endif
136ca987d46SWarner Losh 
1373830659eSToomas Soome struct arch_switch archsw;	/* MI/MD interface boundary */
1383830659eSToomas Soome static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
1393830659eSToomas Soome 
1403830659eSToomas Soome struct devsw *devsw[] = {
1413830659eSToomas Soome 	&bioshd,
1423830659eSToomas Soome #if defined(LOADER_ZFS_SUPPORT)
1433830659eSToomas Soome 	&zfs_dev,
144c1418270SIan Lepore #endif
1453830659eSToomas Soome 	NULL
146c1418270SIan Lepore };
147c1418270SIan Lepore 
1483830659eSToomas Soome struct fs_ops *file_system[] = {
1493830659eSToomas Soome #if defined(LOADER_ZFS_SUPPORT)
1503830659eSToomas Soome 	&zfs_fsops,
151ca987d46SWarner Losh #endif
1523830659eSToomas Soome #if defined(LOADER_UFS_SUPPORT)
1533830659eSToomas Soome 	&ufs_fsops,
1543a1f80e2SToomas Soome #endif
1553830659eSToomas Soome 	NULL
1563830659eSToomas Soome };
1573a1f80e2SToomas Soome 
1583830659eSToomas Soome caddr_t
1593830659eSToomas Soome ptov(uintptr_t x)
160ca987d46SWarner Losh {
1613830659eSToomas Soome 	return (PTOV(x));
162ca987d46SWarner Losh }
163ca987d46SWarner Losh 
164ca987d46SWarner Losh int
165ca987d46SWarner Losh main(void)
166ca987d46SWarner Losh {
1673830659eSToomas Soome 	unsigned i;
1683830659eSToomas Soome 	int auto_boot, fd, nextboot = 0;
1693830659eSToomas Soome 	struct disk_devdesc devdesc;
170ca987d46SWarner Losh 
171ca987d46SWarner Losh 	bios_getmem();
172ca987d46SWarner Losh 
173ca987d46SWarner Losh 	if (high_heap_size > 0) {
1743830659eSToomas Soome 		heap_top = PTOV(high_heap_base + high_heap_size);
1753830659eSToomas Soome 		heap_bottom = PTOV(high_heap_base);
176ca987d46SWarner Losh 	} else {
1773830659eSToomas Soome 		heap_bottom = (char *)
1783830659eSToomas Soome 		    (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
1793830659eSToomas Soome 		heap_top = (char *)PTOV(bios_basemem);
180ca987d46SWarner Losh 	}
1813830659eSToomas Soome 	setheap(heap_bottom, heap_top);
182ca987d46SWarner Losh 
1833830659eSToomas Soome 	/*
1843830659eSToomas Soome 	 * Initialise the block cache. Set the upper limit.
1853830659eSToomas Soome 	 */
1863830659eSToomas Soome 	bcache_init(32768, 512);
1873830659eSToomas Soome 
1883830659eSToomas Soome 	archsw.arch_autoload = NULL;
1893830659eSToomas Soome 	archsw.arch_getdev = i386_getdev;
1903830659eSToomas Soome 	archsw.arch_copyin = NULL;
1913830659eSToomas Soome 	archsw.arch_copyout = NULL;
1923830659eSToomas Soome 	archsw.arch_readin = NULL;
1933830659eSToomas Soome 	archsw.arch_isainb = NULL;
1943830659eSToomas Soome 	archsw.arch_isaoutb = NULL;
1953830659eSToomas Soome 	archsw.arch_zfs_probe = i386_zfs_probe;
196ca987d46SWarner Losh 
197ca987d46SWarner Losh 	bootinfo.bi_version = BOOTINFO_VERSION;
198ca987d46SWarner Losh 	bootinfo.bi_size = sizeof(bootinfo);
199ca987d46SWarner Losh 	bootinfo.bi_basemem = bios_basemem / 1024;
200ca987d46SWarner Losh 	bootinfo.bi_extmem = bios_extmem / 1024;
201ca987d46SWarner Losh 	bootinfo.bi_memsizes_valid++;
2023830659eSToomas Soome 	bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
203ca987d46SWarner Losh 
2043830659eSToomas Soome 	/* Set up fall back device name. */
2053830659eSToomas Soome 	snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
2063830659eSToomas Soome 	    bd_bios2unit(bootinfo.bi_bios_dev));
207ca987d46SWarner Losh 
2083830659eSToomas Soome 	for (i = 0; devsw[i] != NULL; i++)
2093830659eSToomas Soome 		if (devsw[i]->dv_init != NULL)
2103830659eSToomas Soome 			(devsw[i]->dv_init)();
211ca987d46SWarner Losh 
2123830659eSToomas Soome 	disk_parsedev(&devdesc, boot_devname + 4, NULL);
213ca987d46SWarner Losh 
2143830659eSToomas Soome 	bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc.d_slice + 1,
2153830659eSToomas Soome 	    devdesc.dd.d_unit,
2163830659eSToomas Soome 	    devdesc.d_partition >= 0 ? devdesc.d_partition : 0xff);
217ca987d46SWarner Losh 
218ca987d46SWarner Losh 	/*
2193830659eSToomas Soome 	 * zfs_fmtdev() can be called only after dv_init
220ca987d46SWarner Losh 	 */
2213830659eSToomas Soome 	if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
2223830659eSToomas Soome 		/* set up proper device name string for ZFS */
2233830659eSToomas Soome 		strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname));
224e307eb94SToomas Soome 		if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd,
225e307eb94SToomas Soome 		    sizeof(cmd)) == 0) {
226e307eb94SToomas Soome 			nvlist_t *benv;
227e307eb94SToomas Soome 
228ca987d46SWarner Losh 			nextboot = 1;
229ca987d46SWarner Losh 			memcpy(cmddup, cmd, sizeof(cmd));
230ca987d46SWarner Losh 			if (parse_cmd()) {
2313830659eSToomas Soome 				if (!OPT_CHECK(RBX_QUIET))
232e307eb94SToomas Soome 					printf("failed to parse bootonce "
233e307eb94SToomas Soome 					    "command\n");
2343830659eSToomas Soome 				exit(0);
235ca987d46SWarner Losh 			}
236ca987d46SWarner Losh 			if (!OPT_CHECK(RBX_QUIET))
237e307eb94SToomas Soome 				printf("zfs bootonce: %s\n", cmddup);
238e307eb94SToomas Soome 
239e307eb94SToomas Soome 			if (zfs_get_bootenv(bdev, &benv) == 0) {
240e307eb94SToomas Soome 				nvlist_add_string(benv, OS_BOOTONCE_USED,
241e307eb94SToomas Soome 				    cmddup);
242e307eb94SToomas Soome 				zfs_set_bootenv(bdev, benv);
243e307eb94SToomas Soome 			}
244ca987d46SWarner Losh 			/* Do not process this command twice */
245ca987d46SWarner Losh 			*cmd = 0;
246ca987d46SWarner Losh 		}
2473830659eSToomas Soome 	}
248ca987d46SWarner Losh 
2493830659eSToomas Soome 	/* now make sure we have bdev on all cases */
2503830659eSToomas Soome 	free(bdev);
2513830659eSToomas Soome 	i386_getdev((void **)&bdev, boot_devname, NULL);
2523830659eSToomas Soome 
2533830659eSToomas Soome 	env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev,
2543830659eSToomas Soome 	    env_nounset);
2553830659eSToomas Soome 
2563830659eSToomas Soome 	/* Process configuration file */
2573830659eSToomas Soome 	auto_boot = 1;
2583830659eSToomas Soome 
2593830659eSToomas Soome 	fd = open(PATH_CONFIG, O_RDONLY);
2603830659eSToomas Soome 	if (fd == -1)
2613830659eSToomas Soome 		fd = open(PATH_DOTCONFIG, O_RDONLY);
2623830659eSToomas Soome 
2633830659eSToomas Soome 	if (fd != -1) {
264c7dd069cSGleb Smirnoff 		ssize_t cmdlen;
265c7dd069cSGleb Smirnoff 
266c7dd069cSGleb Smirnoff 		if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0)
267c7dd069cSGleb Smirnoff 			cmd[cmdlen] = '\0';
268c7dd069cSGleb Smirnoff 		else
269c7dd069cSGleb Smirnoff 			*cmd = '\0';
2703830659eSToomas Soome 		close(fd);
271ca987d46SWarner Losh 	}
272ca987d46SWarner Losh 
273ca987d46SWarner Losh 	if (*cmd) {
274ca987d46SWarner Losh 		/*
275dfdeb454SToomas Soome 		 * Note that parse_cmd() is destructive to cmd[] and we also
276dfdeb454SToomas Soome 		 * want to honor RBX_QUIET option that could be present in
277dfdeb454SToomas Soome 		 * cmd[].
278ca987d46SWarner Losh 		 */
279ca987d46SWarner Losh 		memcpy(cmddup, cmd, sizeof(cmd));
280ca987d46SWarner Losh 		if (parse_cmd())
2813830659eSToomas Soome 			auto_boot = 0;
282ca987d46SWarner Losh 		if (!OPT_CHECK(RBX_QUIET))
283ca987d46SWarner Losh 			printf("%s: %s\n", PATH_CONFIG, cmddup);
284ca987d46SWarner Losh 		/* Do not process this command twice */
285ca987d46SWarner Losh 		*cmd = 0;
286ca987d46SWarner Losh 	}
287ca987d46SWarner Losh 
288ca987d46SWarner Losh 	/* Do not risk waiting at the prompt forever. */
2893830659eSToomas Soome 	if (nextboot && !auto_boot)
2903830659eSToomas Soome 		exit(0);
291ca987d46SWarner Losh 
2923830659eSToomas Soome 	if (auto_boot && !*kname) {
29363acab6aSWarner Losh 		/*
294dfdeb454SToomas Soome 		 * Iterate through the list of loader and kernel paths,
295dfdeb454SToomas Soome 		 * trying to load. If interrupted by a keypress, or in case of
296dfdeb454SToomas Soome 		 * failure, drop the user to the boot2 prompt.
29763acab6aSWarner Losh 		 */
29863acab6aSWarner Losh 		for (i = 0; i < nitems(loadpath); i++) {
29963acab6aSWarner Losh 			memcpy(kname, loadpath[i].p, loadpath[i].len);
30063acab6aSWarner Losh 			if (keyhit(3))
30163acab6aSWarner Losh 				break;
302ca987d46SWarner Losh 			load();
303ca987d46SWarner Losh 		}
304ca987d46SWarner Losh 	}
305ca987d46SWarner Losh 
306ca987d46SWarner Losh 	/* Present the user with the boot2 prompt. */
307ca987d46SWarner Losh 
308ca987d46SWarner Losh 	for (;;) {
3093830659eSToomas Soome 		if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
310ca987d46SWarner Losh 			printf("\nFreeBSD/x86 boot\n");
3113830659eSToomas Soome 			printf("Default: %s%s\nboot: ", boot_devname, kname);
312ca987d46SWarner Losh 		}
313ca987d46SWarner Losh 		if (ioctrl & IO_SERIAL)
314ca987d46SWarner Losh 			sio_flush();
3153830659eSToomas Soome 		if (!auto_boot || keyhit(5))
316ca987d46SWarner Losh 			getstr(cmd, sizeof(cmd));
3173830659eSToomas Soome 		else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
318ca987d46SWarner Losh 			putchar('\n');
3193830659eSToomas Soome 		auto_boot = 0;
320ca987d46SWarner Losh 		if (parse_cmd())
321ca987d46SWarner Losh 			putchar('\a');
322ca987d46SWarner Losh 		else
323ca987d46SWarner Losh 			load();
324ca987d46SWarner Losh 	}
325ca987d46SWarner Losh }
326ca987d46SWarner Losh 
327ca987d46SWarner Losh /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
328ca987d46SWarner Losh void
329ca987d46SWarner Losh exit(int x)
330ca987d46SWarner Losh {
331ca987d46SWarner Losh 	__exit(x);
332ca987d46SWarner Losh }
333ca987d46SWarner Losh 
334ca987d46SWarner Losh static void
335ca987d46SWarner Losh load(void)
336ca987d46SWarner Losh {
337ca987d46SWarner Losh 	union {
338ca987d46SWarner Losh 		struct exec ex;
339ca987d46SWarner Losh 		Elf32_Ehdr eh;
340ca987d46SWarner Losh 	} hdr;
341ca987d46SWarner Losh 	static Elf32_Phdr ep[2];
342ca987d46SWarner Losh 	static Elf32_Shdr es[2];
343ca987d46SWarner Losh 	caddr_t p;
344ca987d46SWarner Losh 	uint32_t addr, x;
3453830659eSToomas Soome 	int fd, fmt, i, j;
3463830659eSToomas Soome 	ssize_t size;
347ca987d46SWarner Losh 
3483830659eSToomas Soome 	if ((fd = open(kname, O_RDONLY)) == -1) {
349ca987d46SWarner Losh 		printf("\nCan't find %s\n", kname);
350ca987d46SWarner Losh 		return;
351ca987d46SWarner Losh 	}
3523830659eSToomas Soome 
3533830659eSToomas Soome 	size = sizeof(hdr);
3543830659eSToomas Soome 	if (read(fd, &hdr, sizeof (hdr)) != size) {
3553830659eSToomas Soome 		close(fd);
356ca987d46SWarner Losh 		return;
3573830659eSToomas Soome 	}
3583830659eSToomas Soome 	if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
359ca987d46SWarner Losh 		fmt = 0;
3603830659eSToomas Soome 	} else if (IS_ELF(hdr.eh)) {
361ca987d46SWarner Losh 		fmt = 1;
3623830659eSToomas Soome 	} else {
363ca987d46SWarner Losh 		printf("Invalid %s\n", "format");
3643830659eSToomas Soome 		close(fd);
365ca987d46SWarner Losh 		return;
366ca987d46SWarner Losh 	}
367ca987d46SWarner Losh 	if (fmt == 0) {
368ca987d46SWarner Losh 		addr = hdr.ex.a_entry & 0xffffff;
369ca987d46SWarner Losh 		p = PTOV(addr);
3703830659eSToomas Soome 		lseek(fd, PAGE_SIZE, SEEK_SET);
3713830659eSToomas Soome 		size = hdr.ex.a_text;
3723830659eSToomas Soome 		if (read(fd, p, hdr.ex.a_text) != size) {
3733830659eSToomas Soome 			close(fd);
374ca987d46SWarner Losh 			return;
3753830659eSToomas Soome 		}
376ca987d46SWarner Losh 		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
3773830659eSToomas Soome 		size = hdr.ex.a_data;
3783830659eSToomas Soome 		if (read(fd, p, hdr.ex.a_data) != size) {
3793830659eSToomas Soome 			close(fd);
380ca987d46SWarner Losh 			return;
3813830659eSToomas Soome 		}
382ca987d46SWarner Losh 		p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
383ca987d46SWarner Losh 		bootinfo.bi_symtab = VTOP(p);
384ca987d46SWarner Losh 		memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
385ca987d46SWarner Losh 		p += sizeof(hdr.ex.a_syms);
386ca987d46SWarner Losh 		if (hdr.ex.a_syms) {
3873830659eSToomas Soome 			size = hdr.ex.a_syms;
3883830659eSToomas Soome 			if (read(fd, p, hdr.ex.a_syms) != size) {
3893830659eSToomas Soome 				close(fd);
390ca987d46SWarner Losh 				return;
3913830659eSToomas Soome 			}
392ca987d46SWarner Losh 			p += hdr.ex.a_syms;
3933830659eSToomas Soome 			size = sizeof (int);
3943830659eSToomas Soome 			if (read(fd, p, sizeof (int)) != size) {
3953830659eSToomas Soome 				close(fd);
396ca987d46SWarner Losh 				return;
3973830659eSToomas Soome 			}
398ca987d46SWarner Losh 			x = *(uint32_t *)p;
399ca987d46SWarner Losh 			p += sizeof(int);
400ca987d46SWarner Losh 			x -= sizeof(int);
4013830659eSToomas Soome 			size = x;
4023830659eSToomas Soome 			if (read(fd, p, x) != size) {
4033830659eSToomas Soome 				close(fd);
404ca987d46SWarner Losh 				return;
4053830659eSToomas Soome 			}
406ca987d46SWarner Losh 			p += x;
407ca987d46SWarner Losh 		}
408ca987d46SWarner Losh 	} else {
4093830659eSToomas Soome 		lseek(fd, hdr.eh.e_phoff, SEEK_SET);
410ca987d46SWarner Losh 		for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
4113830659eSToomas Soome 			size = sizeof (ep[0]);
4123830659eSToomas Soome 			if (read(fd, ep + j, sizeof (ep[0])) != size) {
4133830659eSToomas Soome 				close(fd);
414ca987d46SWarner Losh 				return;
4153830659eSToomas Soome 			}
416ca987d46SWarner Losh 			if (ep[j].p_type == PT_LOAD)
417ca987d46SWarner Losh 				j++;
418ca987d46SWarner Losh 		}
419ca987d46SWarner Losh 		for (i = 0; i < 2; i++) {
420ca987d46SWarner Losh 			p = PTOV(ep[i].p_paddr & 0xffffff);
4213830659eSToomas Soome 			lseek(fd, ep[i].p_offset, SEEK_SET);
4223830659eSToomas Soome 			size = ep[i].p_filesz;
4233830659eSToomas Soome 			if (read(fd, p, ep[i].p_filesz) != size) {
4243830659eSToomas Soome 				close(fd);
425ca987d46SWarner Losh 				return;
426ca987d46SWarner Losh 			}
4273830659eSToomas Soome 		}
428ca987d46SWarner Losh 		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
429ca987d46SWarner Losh 		bootinfo.bi_symtab = VTOP(p);
430ca987d46SWarner Losh 		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
4313830659eSToomas Soome 			lseek(fd, hdr.eh.e_shoff +
4323830659eSToomas Soome 			    sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
4333830659eSToomas Soome 			    SEEK_SET);
4343830659eSToomas Soome 			size = sizeof(es);
4353830659eSToomas Soome 			if (read(fd, &es, sizeof (es)) != size) {
4363830659eSToomas Soome 				close(fd);
437ca987d46SWarner Losh 				return;
4383830659eSToomas Soome 			}
439ca987d46SWarner Losh 			for (i = 0; i < 2; i++) {
440dfdeb454SToomas Soome 				memcpy(p, &es[i].sh_size,
441dfdeb454SToomas Soome 				    sizeof(es[i].sh_size));
442ca987d46SWarner Losh 				p += sizeof(es[i].sh_size);
4433830659eSToomas Soome 				lseek(fd, es[i].sh_offset, SEEK_SET);
4443830659eSToomas Soome 				size = es[i].sh_size;
4453830659eSToomas Soome 				if (read(fd, p, es[i].sh_size) != size) {
4463830659eSToomas Soome 					close(fd);
447ca987d46SWarner Losh 					return;
4483830659eSToomas Soome 				}
449ca987d46SWarner Losh 				p += es[i].sh_size;
450ca987d46SWarner Losh 			}
451ca987d46SWarner Losh 		}
452ca987d46SWarner Losh 		addr = hdr.eh.e_entry & 0xffffff;
453ca987d46SWarner Losh 	}
4543830659eSToomas Soome 	close(fd);
4553830659eSToomas Soome 
456ca987d46SWarner Losh 	bootinfo.bi_esymtab = VTOP(p);
457ca987d46SWarner Losh 	bootinfo.bi_kernelname = VTOP(kname);
458ca987d46SWarner Losh #ifdef LOADER_GELI_SUPPORT
459ca987d46SWarner Losh 	explicit_bzero(gelipw, sizeof(gelipw));
4603830659eSToomas Soome #endif
4613830659eSToomas Soome 
4623830659eSToomas Soome 	if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
4633830659eSToomas Soome 		zfsargs.size = sizeof(zfsargs);
4643830659eSToomas Soome 		zfsargs.pool = bdev->d_kind.zfs.pool_guid;
4653830659eSToomas Soome 		zfsargs.root = bdev->d_kind.zfs.root_guid;
4663830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
467df108aafSIan Lepore 		export_geli_boot_data(&zfsargs.gelidata);
468ca987d46SWarner Losh #endif
469b92c2c90SIan Lepore 		/*
4703830659eSToomas Soome 		 * Note that the zfsargs struct is passed by value, not by
4713830659eSToomas Soome 		 * pointer. Code in btxldr.S copies the values from the entry
4723830659eSToomas Soome 		 * stack to a fixed location within loader(8) at startup due
4733830659eSToomas Soome 		 * to the presence of KARGS_FLAGS_EXTARG.
474b92c2c90SIan Lepore 		 */
475ca987d46SWarner Losh 		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
476ca987d46SWarner Losh 		    bootdev,
477ca987d46SWarner Losh 		    KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
4783830659eSToomas Soome 		    (uint32_t)bdev->d_kind.zfs.pool_guid,
4793830659eSToomas Soome 		    (uint32_t)(bdev->d_kind.zfs.pool_guid >> 32),
480ca987d46SWarner Losh 		    VTOP(&bootinfo),
481ca987d46SWarner Losh 		    zfsargs);
4823830659eSToomas Soome 	} else {
4833830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
4843830659eSToomas Soome 		geliargs.size = sizeof(geliargs);
4853830659eSToomas Soome 		export_geli_boot_data(&geliargs.gelidata);
4863830659eSToomas Soome #endif
4873830659eSToomas Soome 
4883830659eSToomas Soome 		/*
4893830659eSToomas Soome 		 * Note that the geliargs struct is passed by value, not by
4903830659eSToomas Soome 		 * pointer. Code in btxldr.S copies the values from the entry
4913830659eSToomas Soome 		 * stack to a fixed location within loader(8) at startup due
4923830659eSToomas Soome 		 * to the presence of the KARGS_FLAGS_EXTARG flag.
4933830659eSToomas Soome 		 */
4943830659eSToomas Soome 		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
4953830659eSToomas Soome 		    bootdev,
4963830659eSToomas Soome #ifdef LOADER_GELI_SUPPORT
4973830659eSToomas Soome 		    KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0,
4983830659eSToomas Soome 		    VTOP(&bootinfo), geliargs
4993830659eSToomas Soome #else
5003830659eSToomas Soome 		    0, 0, 0, VTOP(&bootinfo)
5013830659eSToomas Soome #endif
5023830659eSToomas Soome 		    );
5033830659eSToomas Soome 	}
504ca987d46SWarner Losh }
505ca987d46SWarner Losh 
506ca987d46SWarner Losh static int
5073830659eSToomas Soome mount_root(char *arg)
508ca987d46SWarner Losh {
5093830659eSToomas Soome 	char *root;
5103830659eSToomas Soome 	struct i386_devdesc *ddesc;
5113830659eSToomas Soome 	uint8_t part;
512ca987d46SWarner Losh 
5133830659eSToomas Soome 	if (asprintf(&root, "%s:", arg) < 0)
5143830659eSToomas Soome 		return (1);
5153830659eSToomas Soome 
5163830659eSToomas Soome 	if (i386_getdev((void **)&ddesc, root, NULL)) {
5173830659eSToomas Soome 		free(root);
5183830659eSToomas Soome 		return (1);
519ca987d46SWarner Losh 	}
520ca987d46SWarner Losh 
5213830659eSToomas Soome 	/* we should have new device descriptor, free old and replace it. */
5223830659eSToomas Soome 	free(bdev);
5233830659eSToomas Soome 	bdev = ddesc;
5243830659eSToomas Soome 	if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
5253830659eSToomas Soome 		if (bdev->d_kind.biosdisk.partition == -1)
5263830659eSToomas Soome 			part = 0xff;
5273830659eSToomas Soome 		else
5283830659eSToomas Soome 			part = bdev->d_kind.biosdisk.partition;
5293830659eSToomas Soome 		bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
5303830659eSToomas Soome 		    bdev->d_kind.biosdisk.slice + 1,
5313830659eSToomas Soome 		    bdev->dd.d_unit, part);
5323830659eSToomas Soome 		bootinfo.bi_bios_dev = bd_unit2bios(bdev);
533ca987d46SWarner Losh 	}
5343830659eSToomas Soome 	strncpy(boot_devname, root, sizeof (boot_devname));
5353830659eSToomas Soome 	setenv("currdev", root, 1);
5363830659eSToomas Soome 	free(root);
537ca987d46SWarner Losh 	return (0);
538ca987d46SWarner Losh }
539ca987d46SWarner Losh 
5403830659eSToomas Soome static void
5413830659eSToomas Soome fs_list(char *arg)
5423830659eSToomas Soome {
5433830659eSToomas Soome 	int fd;
5443830659eSToomas Soome 	struct dirent *d;
5453830659eSToomas Soome 	char line[80];
5463830659eSToomas Soome 
5473830659eSToomas Soome 	fd = open(arg, O_RDONLY);
5483830659eSToomas Soome 	if (fd < 0)
5493830659eSToomas Soome 		return;
5503830659eSToomas Soome 	pager_open();
5513830659eSToomas Soome 	while ((d = readdirfd(fd)) != NULL) {
5523830659eSToomas Soome 		sprintf(line, "%s\n", d->d_name);
5533830659eSToomas Soome 		if (pager_output(line))
5543830659eSToomas Soome 			break;
5553830659eSToomas Soome 	}
5563830659eSToomas Soome 	pager_close();
5573830659eSToomas Soome 	close(fd);
5583830659eSToomas Soome }
5593830659eSToomas Soome 
560ca987d46SWarner Losh static int
561ca987d46SWarner Losh parse_cmd(void)
562ca987d46SWarner Losh {
563ca987d46SWarner Losh 	char *arg = cmd;
564ca987d46SWarner Losh 	char *ep, *p, *q;
565ca987d46SWarner Losh 	const char *cp;
5663830659eSToomas Soome 	char line[80];
567ca987d46SWarner Losh 	int c, i, j;
568ca987d46SWarner Losh 
569ca987d46SWarner Losh 	while ((c = *arg++)) {
570ca987d46SWarner Losh 		if (c == ' ' || c == '\t' || c == '\n')
571ca987d46SWarner Losh 			continue;
572dfdeb454SToomas Soome 		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
573dfdeb454SToomas Soome 			;
574ca987d46SWarner Losh 		ep = p;
575ca987d46SWarner Losh 		if (*p)
576ca987d46SWarner Losh 			*p++ = 0;
577ca987d46SWarner Losh 		if (c == '-') {
578ca987d46SWarner Losh 			while ((c = *arg++)) {
579ca987d46SWarner Losh 				if (c == 'P') {
580ca987d46SWarner Losh 					if (*(uint8_t *)PTOV(0x496) & 0x10) {
581ca987d46SWarner Losh 						cp = "yes";
582ca987d46SWarner Losh 					} else {
583dfdeb454SToomas Soome 						opts |= OPT_SET(RBX_DUAL);
584dfdeb454SToomas Soome 						opts |= OPT_SET(RBX_SERIAL);
585ca987d46SWarner Losh 						cp = "no";
586ca987d46SWarner Losh 					}
587ca987d46SWarner Losh 					printf("Keyboard: %s\n", cp);
588ca987d46SWarner Losh 					continue;
589ca987d46SWarner Losh 				} else if (c == 'S') {
590ca987d46SWarner Losh 					j = 0;
591dfdeb454SToomas Soome 					while ((unsigned int)
592dfdeb454SToomas Soome 					    (i = *arg++ - '0') <= 9)
593ca987d46SWarner Losh 						j = j * 10 + i;
594ca987d46SWarner Losh 					if (j > 0 && i == -'0') {
595ca987d46SWarner Losh 						comspeed = j;
596ca987d46SWarner Losh 						break;
597ca987d46SWarner Losh 					}
598dfdeb454SToomas Soome 					/*
599dfdeb454SToomas Soome 					 * Fall through to error below
600dfdeb454SToomas Soome 					 * ('S' not in optstr[]).
601dfdeb454SToomas Soome 					 */
602ca987d46SWarner Losh 				}
603ca987d46SWarner Losh 				for (i = 0; c != optstr[i]; i++)
604ca987d46SWarner Losh 					if (i == NOPT - 1)
605dfdeb454SToomas Soome 						return (-1);
606ca987d46SWarner Losh 				opts ^= OPT_SET(flags[i]);
607ca987d46SWarner Losh 			}
608ca987d46SWarner Losh 			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
609ca987d46SWarner Losh 			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
610ca987d46SWarner Losh 			if (ioctrl & IO_SERIAL) {
611ca987d46SWarner Losh 				if (sio_init(115200 / comspeed) != 0)
612ca987d46SWarner Losh 					ioctrl &= ~IO_SERIAL;
613ca987d46SWarner Losh 			}
614ca987d46SWarner Losh 		} if (c == '?') {
6153830659eSToomas Soome 			printf("\n");
6163830659eSToomas Soome 			if (*arg == '\0')
6173830659eSToomas Soome 				arg = (char *)"/";
6183830659eSToomas Soome 			fs_list(arg);
6193830659eSToomas Soome 			zfs_list(arg);
620dfdeb454SToomas Soome 			return (-1);
621ca987d46SWarner Losh 		} else {
6223830659eSToomas Soome 			char *ptr;
6233830659eSToomas Soome 			printf("\n");
624ca987d46SWarner Losh 			arg--;
625ca987d46SWarner Losh 
626ca987d46SWarner Losh 			/*
627ca987d46SWarner Losh 			 * Report pool status if the comment is 'status'. Lets
628ca987d46SWarner Losh 			 * hope no-one wants to load /status as a kernel.
629ca987d46SWarner Losh 			 */
630dfdeb454SToomas Soome 			if (strcmp(arg, "status") == 0) {
6313830659eSToomas Soome 				pager_open();
6323830659eSToomas Soome 				for (i = 0; devsw[i] != NULL; i++) {
6333830659eSToomas Soome 					if (devsw[i]->dv_print != NULL) {
6343830659eSToomas Soome 						if (devsw[i]->dv_print(1))
6353830659eSToomas Soome 							break;
6363830659eSToomas Soome 					} else {
6373830659eSToomas Soome 						snprintf(line, sizeof(line),
6383830659eSToomas Soome 						    "%s: (unknown)\n",
6393830659eSToomas Soome 						    devsw[i]->dv_name);
6403830659eSToomas Soome 						if (pager_output(line))
6413830659eSToomas Soome 							break;
6423830659eSToomas Soome 					}
6433830659eSToomas Soome 				}
6443830659eSToomas Soome 				pager_close();
645dfdeb454SToomas Soome 				return (-1);
646ca987d46SWarner Losh 			}
647ca987d46SWarner Losh 
648ca987d46SWarner Losh 			/*
649ca987d46SWarner Losh 			 * If there is "zfs:" prefix simply ignore it.
650ca987d46SWarner Losh 			 */
6513830659eSToomas Soome 			ptr = arg;
6523830659eSToomas Soome 			if (strncmp(ptr, "zfs:", 4) == 0)
6533830659eSToomas Soome 				ptr += 4;
654ca987d46SWarner Losh 
655ca987d46SWarner Losh 			/*
656ca987d46SWarner Losh 			 * If there is a colon, switch pools.
657ca987d46SWarner Losh 			 */
6583830659eSToomas Soome 			q = strchr(ptr, ':');
659ca987d46SWarner Losh 			if (q) {
660ca987d46SWarner Losh 				*q++ = '\0';
6613830659eSToomas Soome 				if (mount_root(arg) != 0) {
662dfdeb454SToomas Soome 					return (-1);
6633830659eSToomas Soome 				}
664ca987d46SWarner Losh 				arg = q;
665ca987d46SWarner Losh 			}
666ca987d46SWarner Losh 			if ((i = ep - arg)) {
667ca987d46SWarner Losh 				if ((size_t)i >= sizeof(kname))
668dfdeb454SToomas Soome 					return (-1);
669ca987d46SWarner Losh 				memcpy(kname, arg, i + 1);
670ca987d46SWarner Losh 			}
671ca987d46SWarner Losh 		}
672ca987d46SWarner Losh 		arg = p;
673ca987d46SWarner Losh 	}
674dfdeb454SToomas Soome 	return (0);
675ca987d46SWarner Losh }
6763830659eSToomas Soome 
6773830659eSToomas Soome /*
6783830659eSToomas Soome  * Probe all disks to discover ZFS pools. The idea is to walk all possible
6793830659eSToomas Soome  * disk devices, however, we also need to identify possible boot pool.
6803830659eSToomas Soome  * For boot pool detection we have boot disk passed us from BIOS, recorded
6813830659eSToomas Soome  * in bootinfo.bi_bios_dev.
6823830659eSToomas Soome  */
6833830659eSToomas Soome static void
6843830659eSToomas Soome i386_zfs_probe(void)
6853830659eSToomas Soome {
6863830659eSToomas Soome 	char devname[32];
6873830659eSToomas Soome 	int boot_unit;
6883830659eSToomas Soome 	struct i386_devdesc dev;
6893830659eSToomas Soome 	uint64_t pool_guid = 0;
6903830659eSToomas Soome 
6913830659eSToomas Soome 	dev.dd.d_dev = &bioshd;
6923830659eSToomas Soome 	/* Translate bios dev to our unit number. */
6933830659eSToomas Soome 	boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
6943830659eSToomas Soome 
6953830659eSToomas Soome 	/*
6963830659eSToomas Soome 	 * Open all the disks we can find and see if we can reconstruct
6973830659eSToomas Soome 	 * ZFS pools from them.
6983830659eSToomas Soome 	 */
6993830659eSToomas Soome 	for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
7003830659eSToomas Soome 		snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
7013830659eSToomas Soome 		    dev.dd.d_unit);
7023830659eSToomas Soome 		/* If this is not boot disk, use generic probe. */
7033830659eSToomas Soome 		if (dev.dd.d_unit != boot_unit)
7043830659eSToomas Soome 			zfs_probe_dev(devname, NULL);
7053830659eSToomas Soome 		else
7063830659eSToomas Soome 			zfs_probe_dev(devname, &pool_guid);
7073830659eSToomas Soome 
7083830659eSToomas Soome 		if (pool_guid != 0 && bdev == NULL) {
7093830659eSToomas Soome 			bdev = malloc(sizeof (struct i386_devdesc));
7103830659eSToomas Soome 			bzero(bdev, sizeof (struct i386_devdesc));
7113830659eSToomas Soome 			bdev->dd.d_dev = &zfs_dev;
7123830659eSToomas Soome 			bdev->d_kind.zfs.pool_guid = pool_guid;
7133830659eSToomas Soome 		}
7143830659eSToomas Soome 	}
7153830659eSToomas Soome }
716