xref: /freebsd/stand/i386/zfsboot/zfsboot.c (revision 6132212808e8dccedc9e5d85fea4390c2f38059a)
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 <sys/cdefs.h>
17 __FBSDID("$FreeBSD$");
18 
19 #include <stand.h>
20 
21 #include <sys/param.h>
22 #include <sys/errno.h>
23 #include <sys/diskmbr.h>
24 #ifdef GPT
25 #include <sys/gpt.h>
26 #endif
27 #include <sys/reboot.h>
28 #include <sys/queue.h>
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 struct arch_switch archsw;	/* MI/MD interface boundary */
135 static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
136 
137 struct devsw *devsw[] = {
138 	&bioshd,
139 #if defined(LOADER_ZFS_SUPPORT)
140 	&zfs_dev,
141 #endif
142 	NULL
143 };
144 
145 struct fs_ops *file_system[] = {
146 #if defined(LOADER_ZFS_SUPPORT)
147 	&zfs_fsops,
148 #endif
149 #if defined(LOADER_UFS_SUPPORT)
150 	&ufs_fsops,
151 #endif
152 	NULL
153 };
154 
155 caddr_t
156 ptov(uintptr_t x)
157 {
158 	return (PTOV(x));
159 }
160 
161 int
162 main(void)
163 {
164 	unsigned i;
165 	int auto_boot, fd, nextboot = 0;
166 	struct disk_devdesc devdesc;
167 
168 	bios_getmem();
169 
170 	if (high_heap_size > 0) {
171 		heap_top = PTOV(high_heap_base + high_heap_size);
172 		heap_bottom = PTOV(high_heap_base);
173 	} else {
174 		heap_bottom = (char *)
175 		    (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
176 		heap_top = (char *)PTOV(bios_basemem);
177 	}
178 	setheap(heap_bottom, heap_top);
179 
180 	/*
181 	 * Initialise the block cache. Set the upper limit.
182 	 */
183 	bcache_init(32768, 512);
184 
185 	archsw.arch_autoload = NULL;
186 	archsw.arch_getdev = i386_getdev;
187 	archsw.arch_copyin = NULL;
188 	archsw.arch_copyout = NULL;
189 	archsw.arch_readin = NULL;
190 	archsw.arch_isainb = NULL;
191 	archsw.arch_isaoutb = NULL;
192 	archsw.arch_zfs_probe = i386_zfs_probe;
193 
194 	bootinfo.bi_version = BOOTINFO_VERSION;
195 	bootinfo.bi_size = sizeof(bootinfo);
196 	bootinfo.bi_basemem = bios_basemem / 1024;
197 	bootinfo.bi_extmem = bios_extmem / 1024;
198 	bootinfo.bi_memsizes_valid++;
199 	bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
200 
201 	/* Set up fall back device name. */
202 	snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
203 	    bd_bios2unit(bootinfo.bi_bios_dev));
204 
205 	for (i = 0; devsw[i] != NULL; i++)
206 		if (devsw[i]->dv_init != NULL)
207 			(devsw[i]->dv_init)();
208 
209 	disk_parsedev(&devdesc, boot_devname + 4, NULL);
210 
211 	bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc.d_slice + 1,
212 	    devdesc.dd.d_unit,
213 	    devdesc.d_partition >= 0 ? devdesc.d_partition : 0xff);
214 
215 	/*
216 	 * zfs_fmtdev() can be called only after dv_init
217 	 */
218 	if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
219 		/* set up proper device name string for ZFS */
220 		strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname));
221 		if (zfs_nextboot(bdev, cmd, sizeof(cmd)) == 0) {
222 			nextboot = 1;
223 			memcpy(cmddup, cmd, sizeof(cmd));
224 			if (parse_cmd()) {
225 				if (!OPT_CHECK(RBX_QUIET))
226 					printf("failed to parse pad2 area\n");
227 				exit(0);
228 			}
229 			if (!OPT_CHECK(RBX_QUIET))
230 				printf("zfs nextboot: %s\n", cmddup);
231 			/* Do not process this command twice */
232 			*cmd = 0;
233 		}
234 	}
235 
236 	/* now make sure we have bdev on all cases */
237 	free(bdev);
238 	i386_getdev((void **)&bdev, boot_devname, NULL);
239 
240 	env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev,
241 	    env_nounset);
242 
243 	/* Process configuration file */
244 	auto_boot = 1;
245 
246 	fd = open(PATH_CONFIG, O_RDONLY);
247 	if (fd == -1)
248 		fd = open(PATH_DOTCONFIG, O_RDONLY);
249 
250 	if (fd != -1) {
251 		ssize_t cmdlen;
252 
253 		if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0)
254 			cmd[cmdlen] = '\0';
255 		else
256 			*cmd = '\0';
257 		close(fd);
258 	}
259 
260 	if (*cmd) {
261 		/*
262 		 * Note that parse_cmd() is destructive to cmd[] and we also
263 		 * want to honor RBX_QUIET option that could be present in
264 		 * cmd[].
265 		 */
266 		memcpy(cmddup, cmd, sizeof(cmd));
267 		if (parse_cmd())
268 			auto_boot = 0;
269 		if (!OPT_CHECK(RBX_QUIET))
270 			printf("%s: %s\n", PATH_CONFIG, cmddup);
271 		/* Do not process this command twice */
272 		*cmd = 0;
273 	}
274 
275 	/* Do not risk waiting at the prompt forever. */
276 	if (nextboot && !auto_boot)
277 		exit(0);
278 
279 	if (auto_boot && !*kname) {
280 		/*
281 		 * Iterate through the list of loader and kernel paths,
282 		 * trying to load. If interrupted by a keypress, or in case of
283 		 * failure, drop the user to the boot2 prompt.
284 		 */
285 		for (i = 0; i < nitems(loadpath); i++) {
286 			memcpy(kname, loadpath[i].p, loadpath[i].len);
287 			if (keyhit(3))
288 				break;
289 			load();
290 		}
291 	}
292 
293 	/* Present the user with the boot2 prompt. */
294 
295 	for (;;) {
296 		if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
297 			printf("\nFreeBSD/x86 boot\n");
298 			printf("Default: %s%s\nboot: ", boot_devname, kname);
299 		}
300 		if (ioctrl & IO_SERIAL)
301 			sio_flush();
302 		if (!auto_boot || keyhit(5))
303 			getstr(cmd, sizeof(cmd));
304 		else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
305 			putchar('\n');
306 		auto_boot = 0;
307 		if (parse_cmd())
308 			putchar('\a');
309 		else
310 			load();
311 	}
312 }
313 
314 /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
315 void
316 exit(int x)
317 {
318 	__exit(x);
319 }
320 
321 static void
322 load(void)
323 {
324 	union {
325 		struct exec ex;
326 		Elf32_Ehdr eh;
327 	} hdr;
328 	static Elf32_Phdr ep[2];
329 	static Elf32_Shdr es[2];
330 	caddr_t p;
331 	uint32_t addr, x;
332 	int fd, fmt, i, j;
333 	ssize_t size;
334 
335 	if ((fd = open(kname, O_RDONLY)) == -1) {
336 		printf("\nCan't find %s\n", kname);
337 		return;
338 	}
339 
340 	size = sizeof(hdr);
341 	if (read(fd, &hdr, sizeof (hdr)) != size) {
342 		close(fd);
343 		return;
344 	}
345 	if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
346 		fmt = 0;
347 	} else if (IS_ELF(hdr.eh)) {
348 		fmt = 1;
349 	} else {
350 		printf("Invalid %s\n", "format");
351 		close(fd);
352 		return;
353 	}
354 	if (fmt == 0) {
355 		addr = hdr.ex.a_entry & 0xffffff;
356 		p = PTOV(addr);
357 		lseek(fd, PAGE_SIZE, SEEK_SET);
358 		size = hdr.ex.a_text;
359 		if (read(fd, p, hdr.ex.a_text) != size) {
360 			close(fd);
361 			return;
362 		}
363 		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
364 		size = hdr.ex.a_data;
365 		if (read(fd, p, hdr.ex.a_data) != size) {
366 			close(fd);
367 			return;
368 		}
369 		p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
370 		bootinfo.bi_symtab = VTOP(p);
371 		memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
372 		p += sizeof(hdr.ex.a_syms);
373 		if (hdr.ex.a_syms) {
374 			size = hdr.ex.a_syms;
375 			if (read(fd, p, hdr.ex.a_syms) != size) {
376 				close(fd);
377 				return;
378 			}
379 			p += hdr.ex.a_syms;
380 			size = sizeof (int);
381 			if (read(fd, p, sizeof (int)) != size) {
382 				close(fd);
383 				return;
384 			}
385 			x = *(uint32_t *)p;
386 			p += sizeof(int);
387 			x -= sizeof(int);
388 			size = x;
389 			if (read(fd, p, x) != size) {
390 				close(fd);
391 				return;
392 			}
393 			p += x;
394 		}
395 	} else {
396 		lseek(fd, hdr.eh.e_phoff, SEEK_SET);
397 		for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
398 			size = sizeof (ep[0]);
399 			if (read(fd, ep + j, sizeof (ep[0])) != size) {
400 				close(fd);
401 				return;
402 			}
403 			if (ep[j].p_type == PT_LOAD)
404 				j++;
405 		}
406 		for (i = 0; i < 2; i++) {
407 			p = PTOV(ep[i].p_paddr & 0xffffff);
408 			lseek(fd, ep[i].p_offset, SEEK_SET);
409 			size = ep[i].p_filesz;
410 			if (read(fd, p, ep[i].p_filesz) != size) {
411 				close(fd);
412 				return;
413 			}
414 		}
415 		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
416 		bootinfo.bi_symtab = VTOP(p);
417 		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
418 			lseek(fd, hdr.eh.e_shoff +
419 			    sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
420 			    SEEK_SET);
421 			size = sizeof(es);
422 			if (read(fd, &es, sizeof (es)) != size) {
423 				close(fd);
424 				return;
425 			}
426 			for (i = 0; i < 2; i++) {
427 				memcpy(p, &es[i].sh_size,
428 				    sizeof(es[i].sh_size));
429 				p += sizeof(es[i].sh_size);
430 				lseek(fd, es[i].sh_offset, SEEK_SET);
431 				size = es[i].sh_size;
432 				if (read(fd, p, es[i].sh_size) != size) {
433 					close(fd);
434 					return;
435 				}
436 				p += es[i].sh_size;
437 			}
438 		}
439 		addr = hdr.eh.e_entry & 0xffffff;
440 	}
441 	close(fd);
442 
443 	bootinfo.bi_esymtab = VTOP(p);
444 	bootinfo.bi_kernelname = VTOP(kname);
445 #ifdef LOADER_GELI_SUPPORT
446 	explicit_bzero(gelipw, sizeof(gelipw));
447 #endif
448 
449 	if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
450 		zfsargs.size = sizeof(zfsargs);
451 		zfsargs.pool = bdev->d_kind.zfs.pool_guid;
452 		zfsargs.root = bdev->d_kind.zfs.root_guid;
453 #ifdef LOADER_GELI_SUPPORT
454 		export_geli_boot_data(&zfsargs.gelidata);
455 #endif
456 		/*
457 		 * Note that the zfsargs struct is passed by value, not by
458 		 * pointer. Code in btxldr.S copies the values from the entry
459 		 * stack to a fixed location within loader(8) at startup due
460 		 * to the presence of KARGS_FLAGS_EXTARG.
461 		 */
462 		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
463 		    bootdev,
464 		    KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
465 		    (uint32_t)bdev->d_kind.zfs.pool_guid,
466 		    (uint32_t)(bdev->d_kind.zfs.pool_guid >> 32),
467 		    VTOP(&bootinfo),
468 		    zfsargs);
469 	} else {
470 #ifdef LOADER_GELI_SUPPORT
471 		geliargs.size = sizeof(geliargs);
472 		export_geli_boot_data(&geliargs.gelidata);
473 #endif
474 
475 		/*
476 		 * Note that the geliargs struct is passed by value, not by
477 		 * pointer. Code in btxldr.S copies the values from the entry
478 		 * stack to a fixed location within loader(8) at startup due
479 		 * to the presence of the KARGS_FLAGS_EXTARG flag.
480 		 */
481 		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
482 		    bootdev,
483 #ifdef LOADER_GELI_SUPPORT
484 		    KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0,
485 		    VTOP(&bootinfo), geliargs
486 #else
487 		    0, 0, 0, VTOP(&bootinfo)
488 #endif
489 		    );
490 	}
491 }
492 
493 static int
494 mount_root(char *arg)
495 {
496 	char *root;
497 	struct i386_devdesc *ddesc;
498 	uint8_t part;
499 
500 	if (asprintf(&root, "%s:", arg) < 0)
501 		return (1);
502 
503 	if (i386_getdev((void **)&ddesc, root, NULL)) {
504 		free(root);
505 		return (1);
506 	}
507 
508 	/* we should have new device descriptor, free old and replace it. */
509 	free(bdev);
510 	bdev = ddesc;
511 	if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
512 		if (bdev->d_kind.biosdisk.partition == -1)
513 			part = 0xff;
514 		else
515 			part = bdev->d_kind.biosdisk.partition;
516 		bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
517 		    bdev->d_kind.biosdisk.slice + 1,
518 		    bdev->dd.d_unit, part);
519 		bootinfo.bi_bios_dev = bd_unit2bios(bdev);
520 	}
521 	strncpy(boot_devname, root, sizeof (boot_devname));
522 	setenv("currdev", root, 1);
523 	free(root);
524 	return (0);
525 }
526 
527 static void
528 fs_list(char *arg)
529 {
530 	int fd;
531 	struct dirent *d;
532 	char line[80];
533 
534 	fd = open(arg, O_RDONLY);
535 	if (fd < 0)
536 		return;
537 	pager_open();
538 	while ((d = readdirfd(fd)) != NULL) {
539 		sprintf(line, "%s\n", d->d_name);
540 		if (pager_output(line))
541 			break;
542 	}
543 	pager_close();
544 	close(fd);
545 }
546 
547 static int
548 parse_cmd(void)
549 {
550 	char *arg = cmd;
551 	char *ep, *p, *q;
552 	const char *cp;
553 	char line[80];
554 	int c, i, j;
555 
556 	while ((c = *arg++)) {
557 		if (c == ' ' || c == '\t' || c == '\n')
558 			continue;
559 		for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
560 			;
561 		ep = p;
562 		if (*p)
563 			*p++ = 0;
564 		if (c == '-') {
565 			while ((c = *arg++)) {
566 				if (c == 'P') {
567 					if (*(uint8_t *)PTOV(0x496) & 0x10) {
568 						cp = "yes";
569 					} else {
570 						opts |= OPT_SET(RBX_DUAL);
571 						opts |= OPT_SET(RBX_SERIAL);
572 						cp = "no";
573 					}
574 					printf("Keyboard: %s\n", cp);
575 					continue;
576 				} else if (c == 'S') {
577 					j = 0;
578 					while ((unsigned int)
579 					    (i = *arg++ - '0') <= 9)
580 						j = j * 10 + i;
581 					if (j > 0 && i == -'0') {
582 						comspeed = j;
583 						break;
584 					}
585 					/*
586 					 * Fall through to error below
587 					 * ('S' not in optstr[]).
588 					 */
589 				}
590 				for (i = 0; c != optstr[i]; i++)
591 					if (i == NOPT - 1)
592 						return (-1);
593 				opts ^= OPT_SET(flags[i]);
594 			}
595 			ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
596 			    OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
597 			if (ioctrl & IO_SERIAL) {
598 				if (sio_init(115200 / comspeed) != 0)
599 					ioctrl &= ~IO_SERIAL;
600 			}
601 		} if (c == '?') {
602 			printf("\n");
603 			if (*arg == '\0')
604 				arg = (char *)"/";
605 			fs_list(arg);
606 			zfs_list(arg);
607 			return (-1);
608 		} else {
609 			char *ptr;
610 			printf("\n");
611 			arg--;
612 
613 			/*
614 			 * Report pool status if the comment is 'status'. Lets
615 			 * hope no-one wants to load /status as a kernel.
616 			 */
617 			if (strcmp(arg, "status") == 0) {
618 				pager_open();
619 				for (i = 0; devsw[i] != NULL; i++) {
620 					if (devsw[i]->dv_print != NULL) {
621 						if (devsw[i]->dv_print(1))
622 							break;
623 					} else {
624 						snprintf(line, sizeof(line),
625 						    "%s: (unknown)\n",
626 						    devsw[i]->dv_name);
627 						if (pager_output(line))
628 							break;
629 					}
630 				}
631 				pager_close();
632 				return (-1);
633 			}
634 
635 			/*
636 			 * If there is "zfs:" prefix simply ignore it.
637 			 */
638 			ptr = arg;
639 			if (strncmp(ptr, "zfs:", 4) == 0)
640 				ptr += 4;
641 
642 			/*
643 			 * If there is a colon, switch pools.
644 			 */
645 			q = strchr(ptr, ':');
646 			if (q) {
647 				*q++ = '\0';
648 				if (mount_root(arg) != 0) {
649 					return (-1);
650 				}
651 				arg = q;
652 			}
653 			if ((i = ep - arg)) {
654 				if ((size_t)i >= sizeof(kname))
655 					return (-1);
656 				memcpy(kname, arg, i + 1);
657 			}
658 		}
659 		arg = p;
660 	}
661 	return (0);
662 }
663 
664 /*
665  * Probe all disks to discover ZFS pools. The idea is to walk all possible
666  * disk devices, however, we also need to identify possible boot pool.
667  * For boot pool detection we have boot disk passed us from BIOS, recorded
668  * in bootinfo.bi_bios_dev.
669  */
670 static void
671 i386_zfs_probe(void)
672 {
673 	char devname[32];
674 	int boot_unit;
675 	struct i386_devdesc dev;
676 	uint64_t pool_guid = 0;
677 
678 	dev.dd.d_dev = &bioshd;
679 	/* Translate bios dev to our unit number. */
680 	boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
681 
682 	/*
683 	 * Open all the disks we can find and see if we can reconstruct
684 	 * ZFS pools from them.
685 	 */
686 	for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
687 		snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
688 		    dev.dd.d_unit);
689 		/* If this is not boot disk, use generic probe. */
690 		if (dev.dd.d_unit != boot_unit)
691 			zfs_probe_dev(devname, NULL);
692 		else
693 			zfs_probe_dev(devname, &pool_guid);
694 
695 		if (pool_guid != 0 && bdev == NULL) {
696 			bdev = malloc(sizeof (struct i386_devdesc));
697 			bzero(bdev, sizeof (struct i386_devdesc));
698 			bdev->dd.d_dev = &zfs_dev;
699 			bdev->d_kind.zfs.pool_guid = pool_guid;
700 		}
701 	}
702 }
703