xref: /freebsd/stand/i386/gptzfsboot/zfsboot.c (revision c1c97f18b5b9110b3222816ed9129a882b6b5931)
1 /*-
2  * Copyright (c) 1998 Robert Nordier
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are freely
6  * permitted provided that the above copyright notice and this
7  * paragraph and the following disclaimer are duplicated in all
8  * such forms.
9  *
10  * This software is provided "AS IS" and without any express or
11  * implied warranties, including, without limitation, the implied
12  * warranties of merchantability and fitness for a particular
13  * purpose.
14  */
15 
16 #include <stand.h>
17 
18 #include <sys/param.h>
19 #include <sys/errno.h>
20 #include <sys/diskmbr.h>
21 #ifdef GPT
22 #include <sys/gpt.h>
23 #endif
24 #include <sys/reboot.h>
25 #include <sys/queue.h>
26 #ifdef LOADER_ZFS_SUPPORT
27 #include <sys/zfs_bootenv.h>
28 #endif
29 
30 #include <machine/bootinfo.h>
31 #include <machine/elf.h>
32 #include <machine/pc/bios.h>
33 
34 #include <stdarg.h>
35 #include <stddef.h>
36 
37 #include <a.out.h>
38 #include "bootstrap.h"
39 #include "libi386.h"
40 #include <btxv86.h>
41 
42 #include "lib.h"
43 #include "rbx.h"
44 #include "cons.h"
45 #include "bootargs.h"
46 #include "disk.h"
47 #include "part.h"
48 #include "paths.h"
49 
50 #include "libzfs.h"
51 
52 #define	ARGS			0x900
53 #define	NOPT			14
54 #define	NDEV			3
55 
56 #define	BIOS_NUMDRIVES		0x475
57 #define	DRV_HARD		0x80
58 #define	DRV_MASK		0x7f
59 
60 #define	TYPE_AD			0
61 #define	TYPE_DA			1
62 #define	TYPE_MAXHARD		TYPE_DA
63 #define	TYPE_FD			2
64 
65 extern uint32_t _end;
66 
67 static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
68 static const unsigned char flags[NOPT] = {
69     RBX_DUAL,
70     RBX_SERIAL,
71     RBX_ASKNAME,
72     RBX_CDROM,
73     RBX_CONFIG,
74     RBX_KDB,
75     RBX_GDB,
76     RBX_MUTE,
77     RBX_NOINTR,
78     RBX_PAUSE,
79     RBX_QUIET,
80     RBX_DFLTROOT,
81     RBX_SINGLE,
82     RBX_VERBOSE
83 };
84 uint32_t opts;
85 
86 /*
87  * Paths to try loading before falling back to the boot2 prompt.
88  *
89  * /boot/zfsloader must be tried before /boot/loader in order to remain
90  * backward compatible with ZFS boot environments where /boot/loader exists
91  * but does not have ZFS support, which was the case before FreeBSD 12.
92  *
93  * If no loader is found, try to load a kernel directly instead.
94  */
95 static const struct string {
96 	const char *p;
97 	size_t len;
98 } loadpath[] = {
99 	{ PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) },
100 	{ PATH_LOADER, sizeof(PATH_LOADER) },
101 	{ PATH_KERNEL, sizeof(PATH_KERNEL) },
102 };
103 
104 static const unsigned char dev_maj[NDEV] = {30, 4, 2};
105 
106 static struct i386_devdesc *bdev;
107 static char cmd[512];
108 static char cmddup[512];
109 static char kname[1024];
110 static int comspeed = SIOSPD;
111 static struct bootinfo bootinfo;
112 static uint32_t bootdev;
113 static struct zfs_boot_args zfsargs;
114 #ifdef LOADER_GELI_SUPPORT
115 static struct geli_boot_args geliargs;
116 #endif
117 
118 extern vm_offset_t high_heap_base;
119 extern uint32_t	bios_basemem, bios_extmem, high_heap_size;
120 
121 static char *heap_top;
122 static char *heap_bottom;
123 
124 void exit(int);
125 static void i386_zfs_probe(void);
126 static void load(void);
127 static int parse_cmd(void);
128 
129 #ifdef LOADER_GELI_SUPPORT
130 #include "geliboot.h"
131 static char gelipw[GELI_PW_MAXLEN];
132 #endif
133 
134 /*
135  * Only because the zfs code requires access through archsw, otherwise the
136  * 'boot' programs don't need archsw. This is less than ideal, but this
137  * workaround is easier than many of the alternatives.
138  */
139 struct arch_switch archsw = {	/* MI/MD interface boundary */
140 	.arch_getdev = i386_getdev,
141 	.arch_zfs_probe = i386_zfs_probe,
142 };
143 
144 static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
145 
146 struct devsw *devsw[] = {
147 	&bioshd,
148 #if defined(LOADER_ZFS_SUPPORT)
149 	&zfs_dev,
150 #endif
151 	NULL
152 };
153 
154 struct fs_ops *file_system[] = {
155 #if defined(LOADER_ZFS_SUPPORT)
156 	&zfs_fsops,
157 #endif
158 #if defined(LOADER_UFS_SUPPORT)
159 	&ufs_fsops,
160 #endif
161 	NULL
162 };
163 
164 caddr_t
ptov(uintptr_t x)165 ptov(uintptr_t x)
166 {
167 	return (PTOV(x));
168 }
169 
170 int main(void);
171 
172 int
main(void)173 main(void)
174 {
175 	unsigned i;
176 	int auto_boot, fd, nextboot = 0;
177 	struct disk_devdesc *devdesc;
178 
179 	bios_getmem();
180 
181 	if (high_heap_size > 0) {
182 		heap_top = PTOV(high_heap_base + high_heap_size);
183 		heap_bottom = PTOV(high_heap_base);
184 	} else {
185 		heap_bottom = (char *)
186 		    (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
187 		heap_top = (char *)PTOV(bios_basemem);
188 	}
189 	setheap(heap_bottom, heap_top);
190 
191 	/*
192 	 * Initialise the block cache. Set the upper limit.
193 	 */
194 	bcache_init(32768, 512);
195 
196 	bootinfo.bi_version = BOOTINFO_VERSION;
197 	bootinfo.bi_size = sizeof(bootinfo);
198 	bootinfo.bi_basemem = bios_basemem / 1024;
199 	bootinfo.bi_extmem = bios_extmem / 1024;
200 	bootinfo.bi_memsizes_valid++;
201 	bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
202 
203 	/* Set up fall back device name. */
204 	snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
205 	    bd_bios2unit(bootinfo.bi_bios_dev));
206 
207 	/* Set up currdev variable to have hooks in place. */
208 	env_setenv("currdev", EV_VOLATILE, "", gen_setcurrdev,
209 	    env_nounset);
210 
211 	devinit();
212 
213 	/* XXX assumes this will be a disk, but it looks likely give above */
214 	disk_parsedev((struct devdesc **)&devdesc, boot_devname, NULL);
215 
216 	bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc->d_slice + 1,
217 	    devdesc->dd.d_unit,
218 	    devdesc->d_partition >= 0 ? devdesc->d_partition : 0xff);
219 	free(devdesc);
220 
221 	/*
222 	 * devformat() can be called only after dv_init
223 	 */
224 	if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
225 		/* set up proper device name string for ZFS */
226 		strncpy(boot_devname, devformat(&bdev->dd), sizeof (boot_devname));
227 		if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd,
228 		    sizeof(cmd)) == 0) {
229 			nvlist_t *benv;
230 
231 			nextboot = 1;
232 			memcpy(cmddup, cmd, sizeof(cmd));
233 			if (parse_cmd()) {
234 				if (!OPT_CHECK(RBX_QUIET))
235 					printf("failed to parse bootonce "
236 					    "command\n");
237 				exit(0);
238 			}
239 			if (!OPT_CHECK(RBX_QUIET))
240 				printf("zfs bootonce: %s\n", cmddup);
241 
242 			if (zfs_get_bootenv(bdev, &benv) == 0) {
243 				nvlist_add_string(benv, OS_BOOTONCE_USED,
244 				    cmddup);
245 				zfs_set_bootenv(bdev, benv);
246 			}
247 			/* Do not process this command twice */
248 			*cmd = 0;
249 		}
250 	}
251 
252 	/* now make sure we have bdev on all cases */
253 	free(bdev);
254 	i386_getdev((void **)&bdev, boot_devname, NULL);
255 
256 	env_setenv("currdev", EV_VOLATILE, boot_devname, gen_setcurrdev,
257 	    env_nounset);
258 
259 	/* Process configuration file */
260 	auto_boot = 1;
261 
262 	fd = open(PATH_CONFIG, O_RDONLY);
263 	if (fd == -1)
264 		fd = open(PATH_DOTCONFIG, O_RDONLY);
265 
266 	if (fd != -1) {
267 		ssize_t cmdlen;
268 
269 		if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0)
270 			cmd[cmdlen] = '\0';
271 		else
272 			*cmd = '\0';
273 		close(fd);
274 	}
275 
276 	if (*cmd) {
277 		/*
278 		 * Note that parse_cmd() is destructive to cmd[] and we also
279 		 * want to honor RBX_QUIET option that could be present in
280 		 * cmd[].
281 		 */
282 		memcpy(cmddup, cmd, sizeof(cmd));
283 		if (parse_cmd())
284 			auto_boot = 0;
285 		if (!OPT_CHECK(RBX_QUIET))
286 			printf("%s: %s\n", PATH_CONFIG, cmddup);
287 		/* Do not process this command twice */
288 		*cmd = 0;
289 	}
290 
291 	/* Do not risk waiting at the prompt forever. */
292 	if (nextboot && !auto_boot)
293 		exit(0);
294 
295 	if (auto_boot && !*kname) {
296 		/*
297 		 * Iterate through the list of loader and kernel paths,
298 		 * trying to load. If interrupted by a keypress, or in case of
299 		 * failure, drop the user to the boot2 prompt.
300 		 */
301 		for (i = 0; i < nitems(loadpath); i++) {
302 			memcpy(kname, loadpath[i].p, loadpath[i].len);
303 			if (keyhit(3))
304 				break;
305 			load();
306 		}
307 	}
308 
309 	/* Present the user with the boot2 prompt. */
310 
311 	for (;;) {
312 		if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
313 			printf("\nFreeBSD/x86 boot\n");
314 			printf("Default: %s%s\nboot: ", boot_devname, kname);
315 		}
316 		if (ioctrl & IO_SERIAL)
317 			sio_flush();
318 		if (!auto_boot || keyhit(5))
319 			getstr(cmd, sizeof(cmd));
320 		else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
321 			putchar('\n');
322 		auto_boot = 0;
323 		if (parse_cmd()) {
324 			putchar('\a');
325 		} else {
326 			putchar('\n');
327 			load();
328 		}
329 	}
330 }
331 
332 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
333 void
exit(int x)334 exit(int x)
335 {
336 	__exit(x);
337 }
338 
339 static void
load(void)340 load(void)
341 {
342 	union {
343 		struct exec ex;
344 		Elf32_Ehdr eh;
345 	} hdr;
346 	static Elf32_Phdr ep[2];
347 	static Elf32_Shdr es[2];
348 	caddr_t p;
349 	uint32_t addr, x;
350 	int fd, fmt, i, j;
351 	ssize_t size;
352 
353 	if ((fd = open(kname, O_RDONLY)) == -1) {
354 		printf("\nCan't find %s\n", kname);
355 		return;
356 	}
357 
358 	size = sizeof(hdr);
359 	if (read(fd, &hdr, sizeof (hdr)) != size) {
360 		close(fd);
361 		return;
362 	}
363 	if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
364 		fmt = 0;
365 	} else if (IS_ELF(hdr.eh)) {
366 		fmt = 1;
367 	} else {
368 		printf("Invalid %s\n", "format");
369 		close(fd);
370 		return;
371 	}
372 	if (fmt == 0) {
373 		addr = hdr.ex.a_entry & 0xffffff;
374 		p = PTOV(addr);
375 		lseek(fd, PAGE_SIZE, SEEK_SET);
376 		size = hdr.ex.a_text;
377 		if (read(fd, p, hdr.ex.a_text) != size) {
378 			close(fd);
379 			return;
380 		}
381 		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
382 		size = hdr.ex.a_data;
383 		if (read(fd, p, hdr.ex.a_data) != size) {
384 			close(fd);
385 			return;
386 		}
387 		p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
388 		bootinfo.bi_symtab = VTOP(p);
389 		memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
390 		p += sizeof(hdr.ex.a_syms);
391 		if (hdr.ex.a_syms) {
392 			size = hdr.ex.a_syms;
393 			if (read(fd, p, hdr.ex.a_syms) != size) {
394 				close(fd);
395 				return;
396 			}
397 			p += hdr.ex.a_syms;
398 			size = sizeof (int);
399 			if (read(fd, p, sizeof (int)) != size) {
400 				close(fd);
401 				return;
402 			}
403 			x = *(uint32_t *)p;
404 			p += sizeof(int);
405 			x -= sizeof(int);
406 			size = x;
407 			if (read(fd, p, x) != size) {
408 				close(fd);
409 				return;
410 			}
411 			p += x;
412 		}
413 	} else {
414 		lseek(fd, hdr.eh.e_phoff, SEEK_SET);
415 		for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
416 			size = sizeof (ep[0]);
417 			if (read(fd, ep + j, sizeof (ep[0])) != size) {
418 				close(fd);
419 				return;
420 			}
421 			if (ep[j].p_type == PT_LOAD)
422 				j++;
423 		}
424 		for (i = 0; i < 2; i++) {
425 			p = PTOV(ep[i].p_paddr & 0xffffff);
426 			lseek(fd, ep[i].p_offset, SEEK_SET);
427 			size = ep[i].p_filesz;
428 			if (read(fd, p, ep[i].p_filesz) != size) {
429 				close(fd);
430 				return;
431 			}
432 		}
433 		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
434 		bootinfo.bi_symtab = VTOP(p);
435 		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
436 			lseek(fd, hdr.eh.e_shoff +
437 			    sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
438 			    SEEK_SET);
439 			size = sizeof(es);
440 			if (read(fd, &es, sizeof (es)) != size) {
441 				close(fd);
442 				return;
443 			}
444 			for (i = 0; i < 2; i++) {
445 				memcpy(p, &es[i].sh_size,
446 				    sizeof(es[i].sh_size));
447 				p += sizeof(es[i].sh_size);
448 				lseek(fd, es[i].sh_offset, SEEK_SET);
449 				size = es[i].sh_size;
450 				if (read(fd, p, es[i].sh_size) != size) {
451 					close(fd);
452 					return;
453 				}
454 				p += es[i].sh_size;
455 			}
456 		}
457 		addr = hdr.eh.e_entry & 0xffffff;
458 	}
459 	close(fd);
460 
461 	bootinfo.bi_esymtab = VTOP(p);
462 	bootinfo.bi_kernelname = VTOP(kname);
463 #ifdef LOADER_GELI_SUPPORT
464 	explicit_bzero(gelipw, sizeof(gelipw));
465 #endif
466 
467 	if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
468 		zfsargs.size = sizeof(zfsargs);
469 		zfsargs.pool = bdev->zfs.pool_guid;
470 		zfsargs.root = bdev->zfs.root_guid;
471 #ifdef LOADER_GELI_SUPPORT
472 		export_geli_boot_data(&zfsargs.gelidata);
473 #endif
474 		/*
475 		 * Note that the zfsargs struct is passed by value, not by
476 		 * pointer. Code in btxldr.S copies the values from the entry
477 		 * stack to a fixed location within loader(8) at startup due
478 		 * to the presence of KARGS_FLAGS_EXTARG.
479 		 */
480 		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
481 		    bootdev,
482 		    KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
483 		    (uint32_t)bdev->zfs.pool_guid,
484 		    (uint32_t)(bdev->zfs.pool_guid >> 32),
485 		    VTOP(&bootinfo),
486 		    zfsargs);
487 	} else {
488 #ifdef LOADER_GELI_SUPPORT
489 		geliargs.size = sizeof(geliargs);
490 		export_geli_boot_data(&geliargs.gelidata);
491 #endif
492 
493 		/*
494 		 * Note that the geliargs struct is passed by value, not by
495 		 * pointer. Code in btxldr.S copies the values from the entry
496 		 * stack to a fixed location within loader(8) at startup due
497 		 * to the presence of the KARGS_FLAGS_EXTARG flag.
498 		 */
499 		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
500 		    bootdev,
501 #ifdef LOADER_GELI_SUPPORT
502 		    KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0,
503 		    VTOP(&bootinfo), geliargs
504 #else
505 		    0, 0, 0, VTOP(&bootinfo)
506 #endif
507 		    );
508 	}
509 }
510 
511 static int
mount_root(char * arg)512 mount_root(char *arg)
513 {
514 	char *root;
515 	struct i386_devdesc *ddesc;
516 	uint8_t part;
517 
518 	if (asprintf(&root, "%s:", arg) < 0)
519 		return (1);
520 
521 	if (i386_getdev((void **)&ddesc, root, NULL)) {
522 		free(root);
523 		return (1);
524 	}
525 
526 	/* we should have new device descriptor, free old and replace it. */
527 	free(bdev);
528 	bdev = ddesc;
529 	if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
530 		if (bdev->disk.d_partition == -1)
531 			part = 0xff;
532 		else
533 			part = bdev->disk.d_partition;
534 		bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
535 		    bdev->disk.d_slice + 1, bdev->dd.d_unit, part);
536 		bootinfo.bi_bios_dev = bd_unit2bios(bdev);
537 	}
538 	strncpy(boot_devname, root, sizeof (boot_devname));
539 	setenv("currdev", root, 1);
540 	free(root);
541 	return (0);
542 }
543 
544 static void
fs_list(char * arg)545 fs_list(char *arg)
546 {
547 	int fd;
548 	struct dirent *d;
549 	char line[80];
550 
551 	fd = open(arg, O_RDONLY);
552 	if (fd < 0)
553 		return;
554 	pager_open();
555 	while ((d = readdirfd(fd)) != NULL) {
556 		sprintf(line, "%s\n", d->d_name);
557 		if (pager_output(line))
558 			break;
559 	}
560 	pager_close();
561 	close(fd);
562 }
563 
564 static int
parse_cmd(void)565 parse_cmd(void)
566 {
567 	char *arg = cmd;
568 	char *ep, *p, *q;
569 	const char *cp;
570 	char line[80];
571 	int c, i, j;
572 
573 	while ((c = *arg++)) {
574 		if (c == ' ' || c == '\t' || c == '\n')
575 			continue;
576 		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
577 			;
578 		ep = p;
579 		if (*p)
580 			*p++ = 0;
581 		if (c == '-') {
582 			while ((c = *arg++)) {
583 				if (c == 'P') {
584 					if (*(uint8_t *)PTOV(0x496) & 0x10) {
585 						cp = "yes";
586 					} else {
587 						opts |= OPT_SET(RBX_DUAL);
588 						opts |= OPT_SET(RBX_SERIAL);
589 						cp = "no";
590 					}
591 					printf("Keyboard: %s\n", cp);
592 					continue;
593 				} else if (c == 'S') {
594 					j = 0;
595 					while ((unsigned int)
596 					    (i = *arg++ - '0') <= 9)
597 						j = j * 10 + i;
598 					if (j > 0 && i == -'0') {
599 						comspeed = j;
600 						break;
601 					}
602 					/*
603 					 * Fall through to error below
604 					 * ('S' not in optstr[]).
605 					 */
606 				}
607 				for (i = 0; c != optstr[i]; i++)
608 					if (i == NOPT - 1)
609 						return (-1);
610 				opts ^= OPT_SET(flags[i]);
611 			}
612 			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
613 			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
614 			if (ioctrl & IO_SERIAL) {
615 				if (sio_init(115200 / comspeed) != 0)
616 					ioctrl &= ~IO_SERIAL;
617 			}
618 		} if (c == '?') {
619 			printf("\n");
620 			if (*arg == '\0')
621 				arg = (char *)"/";
622 			fs_list(arg);
623 			zfs_list(arg);
624 			return (-1);
625 		} else {
626 			char *ptr;
627 			printf("\n");
628 			arg--;
629 
630 			/*
631 			 * Report pool status if the comment is 'status'. Lets
632 			 * hope no-one wants to load /status as a kernel.
633 			 */
634 			if (strcmp(arg, "status") == 0) {
635 				pager_open();
636 				for (i = 0; devsw[i] != NULL; i++) {
637 					if (devsw[i]->dv_print != NULL) {
638 						if (devsw[i]->dv_print(1))
639 							break;
640 					} else {
641 						snprintf(line, sizeof(line),
642 						    "%s: (unknown)\n",
643 						    devsw[i]->dv_name);
644 						if (pager_output(line))
645 							break;
646 					}
647 				}
648 				pager_close();
649 				return (-1);
650 			}
651 
652 			/*
653 			 * If there is "zfs:" prefix simply ignore it.
654 			 */
655 			ptr = arg;
656 			if (strncmp(ptr, "zfs:", 4) == 0)
657 				ptr += 4;
658 
659 			/*
660 			 * If there is a colon, switch pools.
661 			 */
662 			q = strchr(ptr, ':');
663 			if (q) {
664 				*q++ = '\0';
665 				if (mount_root(arg) != 0) {
666 					return (-1);
667 				}
668 				arg = q;
669 			}
670 			if ((i = ep - arg)) {
671 				if ((size_t)i >= sizeof(kname))
672 					return (-1);
673 				memcpy(kname, arg, i + 1);
674 			}
675 		}
676 		arg = p;
677 	}
678 	return (0);
679 }
680 
681 /*
682  * Probe all disks to discover ZFS pools. The idea is to walk all possible
683  * disk devices, however, we also need to identify possible boot pool.
684  * For boot pool detection we have boot disk passed us from BIOS, recorded
685  * in bootinfo.bi_bios_dev.
686  */
687 static void
i386_zfs_probe(void)688 i386_zfs_probe(void)
689 {
690 	char devname[32];
691 	int boot_unit;
692 	struct i386_devdesc dev;
693 	uint64_t pool_guid = 0;
694 
695 	dev.dd.d_dev = &bioshd;
696 	/* Translate bios dev to our unit number. */
697 	boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
698 
699 	/*
700 	 * Open all the disks we can find and see if we can reconstruct
701 	 * ZFS pools from them.
702 	 */
703 	for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
704 		snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
705 		    dev.dd.d_unit);
706 		/* If this is not boot disk, use generic probe. */
707 		if (dev.dd.d_unit != boot_unit)
708 			zfs_probe_dev(devname, NULL, true);
709 		else
710 			zfs_probe_dev(devname, &pool_guid, true);
711 
712 		if (pool_guid != 0 && bdev == NULL) {
713 			bdev = malloc(sizeof (struct i386_devdesc));
714 			bzero(bdev, sizeof (struct i386_devdesc));
715 			bdev->zfs.dd.d_dev = &zfs_dev;
716 			bdev->zfs.pool_guid = pool_guid;
717 		}
718 	}
719 }
720