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#include <stand.h>
18
19#include <sys/param.h>
20#include <sys/errno.h>
21#include <sys/diskmbr.h>
22#include <sys/vtoc.h>
23#include <sys/disk.h>
24#include <sys/reboot.h>
25#include <sys/queue.h>
26#include <sys/multiboot.h>
27
28#include <machine/bootinfo.h>
29#include <machine/elf.h>
30#include <machine/pc/bios.h>
31
32#include <stdarg.h>
33#include <stdbool.h>
34#include <stddef.h>
35
36#include <a.out.h>
37#include "bootstrap.h"
38#include "libi386.h"
39#include <btxv86.h>
40
41#include "lib.h"
42#include "rbx.h"
43#include "cons.h"
44#include "bootargs.h"
45#include "disk.h"
46#include "part.h"
47#include "paths.h"
48
49#include "libzfs.h"
50
51#define	ARGS		0x900
52#define	NOPT		15
53#define	NDEV		3
54
55#define	BIOS_NUMDRIVES	0x475
56#define	DRV_HARD	0x80
57#define	DRV_MASK	0x7f
58
59#define	TYPE_AD		0
60#define	TYPE_DA		1
61#define	TYPE_MAXHARD	TYPE_DA
62#define	TYPE_FD		2
63
64extern uint32_t _end;
65
66/*
67 * Fake multiboot header to provide versioning and to pass
68 * partition start LBA. Partition is either GPT partition or
69 * VTOC slice.
70 */
71extern const struct multiboot_header mb_header;
72extern uint64_t start_sector;
73
74static const char optstr[NOPT] = "DhaCcdgmnpqrstv"; /* Also 'P', 'S' */
75static const unsigned char flags[NOPT] = {
76    RBX_DUAL,
77    RBX_SERIAL,
78    RBX_ASKNAME,
79    RBX_CDROM,
80    RBX_CONFIG,
81    RBX_KDB,
82    RBX_GDB,
83    RBX_MUTE,
84    RBX_NOINTR,
85    RBX_PAUSE,
86    RBX_QUIET,
87    RBX_DFLTROOT,
88    RBX_SINGLE,
89    RBX_TEXT_MODE,
90    RBX_VERBOSE
91};
92uint32_t opts;
93
94/*
95 * Paths to try loading before falling back to the boot2 prompt.
96 */
97#define	PATH_ZFSLOADER "/boot/zfsloader"
98static const struct string {
99	const char *p;
100	size_t len;
101} loadpath[] = {
102	{ PATH_LOADER, sizeof (PATH_LOADER) },
103	{ PATH_ZFSLOADER, sizeof (PATH_ZFSLOADER) }
104};
105
106static const unsigned char dev_maj[NDEV] = {30, 4, 2};
107
108static struct i386_devdesc *bdev;
109static char cmd[512];
110static char cmddup[512];
111static char kname[1024];
112static int comspeed = SIOSPD;
113static struct bootinfo bootinfo;
114static uint32_t bootdev;
115static struct zfs_boot_args zfsargs;
116
117extern vm_offset_t high_heap_base;
118extern uint32_t	bios_basemem, bios_extmem, high_heap_size;
119
120static char *heap_top;
121static char *heap_bottom;
122
123static void i386_zfs_probe(void);
124static void load(void);
125static int parse_cmd(void);
126
127struct arch_switch archsw;	/* MI/MD interface boundary */
128static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
129
130struct devsw *devsw[] = {
131	&bioshd,
132	&zfs_dev,
133	NULL
134};
135
136struct fs_ops *file_system[] = {
137	&zfs_fsops,
138	&ufs_fsops,
139	&dosfs_fsops,
140	NULL
141};
142
143int
144main(void)
145{
146	unsigned i;
147	int fd;
148	bool auto_boot;
149	struct disk_devdesc devdesc;
150
151	bios_getmem();
152
153	if (high_heap_size > 0) {
154		heap_top = PTOV(high_heap_base + high_heap_size);
155		heap_bottom = PTOV(high_heap_base);
156	} else {
157		heap_bottom = (char *)
158		    (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
159		heap_top = (char *)PTOV(bios_basemem);
160	}
161	setheap(heap_bottom, heap_top);
162
163	/*
164	 * Initialise the block cache. Set the upper limit.
165	 */
166	bcache_init(32768, 512);
167
168	archsw.arch_autoload = NULL;
169	archsw.arch_getdev = i386_getdev;
170	archsw.arch_copyin = NULL;
171	archsw.arch_copyout = NULL;
172	archsw.arch_readin = NULL;
173	archsw.arch_isainb = NULL;
174	archsw.arch_isaoutb = NULL;
175	archsw.arch_zfs_probe = i386_zfs_probe;
176
177	bootinfo.bi_version = BOOTINFO_VERSION;
178	bootinfo.bi_size = sizeof (bootinfo);
179	bootinfo.bi_basemem = bios_basemem / 1024;
180	bootinfo.bi_extmem = bios_extmem / 1024;
181	bootinfo.bi_memsizes_valid++;
182	bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
183
184	/* Set up fall back device name. */
185	snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
186	    bd_bios2unit(bootinfo.bi_bios_dev));
187
188	for (i = 0; devsw[i] != NULL; i++)
189		if (devsw[i]->dv_init != NULL)
190			(devsw[i]->dv_init)();
191
192	disk_parsedev(&devdesc, boot_devname + 4, NULL);
193
194	bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc.d_slice + 1,
195	    devdesc.dd.d_unit,
196	    devdesc.d_partition >= 0 ? devdesc.d_partition : 0xff);
197
198	/*
199	 * zfs_fmtdev() can be called only after dv_init
200	 */
201	if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
202		/* set up proper device name string for ZFS */
203		strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname));
204	}
205
206	/* now make sure we have bdev on all cases */
207	if (bdev != NULL)
208		free(bdev);
209	i386_getdev((void **)&bdev, boot_devname, NULL);
210
211	env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev,
212	    env_nounset);
213
214	/* Process configuration file */
215	setenv("screen-#rows", "24", 1);
216	auto_boot = true;
217
218	fd = open(PATH_CONFIG, O_RDONLY);
219	if (fd == -1)
220		fd = open(PATH_DOTCONFIG, O_RDONLY);
221
222	if (fd != -1) {
223		ssize_t cmdlen;
224
225		if ((cmdlen = read(fd, cmd, sizeof (cmd))) > 0)
226			cmd[cmdlen] = '\0';
227		else
228			*cmd = '\0';
229		close(fd);
230	}
231
232	if (*cmd) {
233		/*
234		 * Note that parse_cmd() is destructive to cmd[] and we also
235		 * want to honor RBX_QUIET option that could be present in
236		 * cmd[].
237		 */
238		memcpy(cmddup, cmd, sizeof (cmd));
239		if (parse_cmd())
240			auto_boot = false;
241		if (!OPT_CHECK(RBX_QUIET))
242			printf("%s: %s\n", PATH_CONFIG, cmddup);
243		/* Do not process this command twice */
244		*cmd = 0;
245	}
246
247	if (auto_boot && !*kname) {
248		/*
249		 * Try to exec stage 3 boot loader. If interrupted by a
250		 * keypress, or in case of failure, drop the user to the
251		 * boot2 prompt..
252		 */
253		auto_boot = false;
254		for (i = 0; i < nitems(loadpath); i++) {
255			memcpy(kname, loadpath[i].p, loadpath[i].len);
256			if (keyhit(3))
257				break;
258			load();
259		}
260	}
261	/* Reset to default */
262	memcpy(kname, loadpath[0].p, loadpath[0].len);
263
264	/* Present the user with the boot2 prompt. */
265
266	for (;;) {
267		if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
268			printf("\nillumos/x86 boot\n");
269			printf("Default: %s%s\nboot: ", boot_devname, kname);
270		}
271		if (ioctrl & IO_SERIAL)
272			sio_flush();
273		if (!auto_boot || keyhit(5))
274			getstr(cmd, sizeof (cmd));
275		else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
276			putchar('\n');
277		auto_boot = false;
278		if (parse_cmd())
279			putchar('\a');
280		else
281			load();
282	}
283}
284
285/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
286void
287exit(int x)
288{
289	__exit(x);
290}
291
292static void
293load(void)
294{
295	union {
296		struct exec ex;
297		Elf32_Ehdr eh;
298	} hdr;
299	static Elf32_Phdr ep[2];
300	static Elf32_Shdr es[2];
301	caddr_t p;
302	uint32_t addr, x;
303	int fd, fmt, i, j;
304
305	if ((fd = open(kname, O_RDONLY)) == -1) {
306		printf("\nCan't find %s\n", kname);
307		return;
308	}
309	if (read(fd, &hdr, sizeof (hdr)) != sizeof (hdr)) {
310		close(fd);
311		return;
312	}
313	if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
314		fmt = 0;
315	} else if (IS_ELF(hdr.eh)) {
316		fmt = 1;
317	} else {
318		printf("Invalid %s\n", "format");
319		close(fd);
320		return;
321	}
322	if (fmt == 0) {
323		addr = hdr.ex.a_entry & 0xffffff;
324		p = PTOV(addr);
325		lseek(fd, PAGE_SIZE, SEEK_SET);
326		if (read(fd, p, hdr.ex.a_text) != hdr.ex.a_text) {
327			close(fd);
328			return;
329		}
330		p += roundup2(hdr.ex.a_text, PAGE_SIZE);
331		if (read(fd, p, hdr.ex.a_data) != hdr.ex.a_data) {
332			close(fd);
333			return;
334		}
335		p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
336		bootinfo.bi_symtab = VTOP(p);
337		memcpy(p, &hdr.ex.a_syms, sizeof (hdr.ex.a_syms));
338		p += sizeof (hdr.ex.a_syms);
339		if (hdr.ex.a_syms) {
340			if (read(fd, p, hdr.ex.a_syms) != hdr.ex.a_syms) {
341				close(fd);
342				return;
343			}
344			p += hdr.ex.a_syms;
345			if (read(fd, p, sizeof (int)) != sizeof (int)) {
346				close(fd);
347				return;
348			}
349			x = *(uint32_t *)p;
350			p += sizeof (int);
351			x -= sizeof (int);
352			if (read(fd, p, x) != x) {
353				close(fd);
354				return;
355			}
356			p += x;
357		}
358	} else {
359		lseek(fd, hdr.eh.e_phoff, SEEK_SET);
360		for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
361			if (read(fd, ep + j, sizeof (ep[0])) !=
362			    sizeof (ep[0])) {
363				close(fd);
364				return;
365			}
366			if (ep[j].p_type == PT_LOAD)
367				j++;
368		}
369		for (i = 0; i < 2; i++) {
370			p = PTOV(ep[i].p_paddr & 0xffffff);
371			lseek(fd, ep[i].p_offset, SEEK_SET);
372			if (read(fd, p, ep[i].p_filesz) != ep[i].p_filesz) {
373				close(fd);
374				return;
375			}
376		}
377		p += roundup2(ep[1].p_memsz, PAGE_SIZE);
378		bootinfo.bi_symtab = VTOP(p);
379		if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
380			lseek(fd, hdr.eh.e_shoff +
381			    sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
382			    SEEK_SET);
383			if (read(fd, &es, sizeof (es)) != sizeof (es)) {
384				close(fd);
385				return;
386			}
387			for (i = 0; i < 2; i++) {
388				memcpy(p, &es[i].sh_size,
389				    sizeof (es[i].sh_size));
390				p += sizeof (es[i].sh_size);
391				lseek(fd, es[i].sh_offset, SEEK_SET);
392				if (read(fd, p, es[i].sh_size) !=
393				    es[i].sh_size) {
394					close(fd);
395					return;
396				}
397				p += es[i].sh_size;
398			}
399		}
400		addr = hdr.eh.e_entry & 0xffffff;
401	}
402	close(fd);
403
404	bootinfo.bi_esymtab = VTOP(p);
405	bootinfo.bi_kernelname = VTOP(kname);
406
407	if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
408		zfsargs.size = sizeof (zfsargs);
409		zfsargs.pool = bdev->d_kind.zfs.pool_guid;
410		zfsargs.root = bdev->d_kind.zfs.root_guid;
411		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
412		    bootdev,
413		    KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
414		    (uint32_t)bdev->d_kind.zfs.pool_guid,
415		    (uint32_t)(bdev->d_kind.zfs.pool_guid >> 32),
416		    VTOP(&bootinfo),
417		    zfsargs);
418	} else {
419		__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
420		    bootdev, 0, 0, 0, VTOP(&bootinfo));
421	}
422}
423
424static int
425mount_root(char *arg)
426{
427	char *root;
428	struct i386_devdesc *ddesc;
429	uint8_t part;
430
431	if (asprintf(&root, "%s:", arg) < 0)
432		return (1);
433
434	if (i386_getdev((void **)&ddesc, root, NULL)) {
435		free(root);
436		return (1);
437	}
438
439	/* we should have new device descriptor, free old and replace it. */
440	free(bdev);
441	bdev = ddesc;
442	if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
443		if (bdev->d_kind.biosdisk.partition == -1)
444			part = 0xff;
445		else
446			part = bdev->d_kind.biosdisk.partition;
447		bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
448		    bdev->d_kind.biosdisk.slice + 1,
449		    bdev->dd.d_unit, part);
450		bootinfo.bi_bios_dev = bd_unit2bios(bdev);
451	}
452	strncpy(boot_devname, root, sizeof (boot_devname));
453	setenv("currdev", root, 1);
454	free(root);
455	return (0);
456}
457
458static void
459fs_list(char *arg)
460{
461	int fd;
462	struct dirent *d;
463	char line[80];
464
465	fd = open(arg, O_RDONLY);
466	if (fd < 0)
467		return;
468	pager_open();
469	while ((d = readdirfd(fd)) != NULL) {
470		sprintf(line, "%s\n", d->d_name);
471		if (pager_output(line))
472			break;
473	}
474	pager_close();
475	close(fd);
476}
477
478static int
479parse_cmd(void)
480{
481	char *arg = cmd;
482	char *ep, *p, *q;
483	const char *cp;
484	char line[80];
485	int c, i;
486
487	while ((c = *arg++)) {
488		if (isspace(c))
489			continue;
490
491		for (p = arg; *p != '\0' && !isspace(*p); p++)
492			;
493		ep = p;
494		if (*p != '\0')
495			*p++ = '\0';
496		if (c == '-') {
497			while ((c = *arg++)) {
498				if (isspace(c))
499					break;
500
501				if (c == 'P') {
502					if (*(uint8_t *)PTOV(0x496) & 0x10) {
503						cp = "yes";
504					} else {
505						opts |= OPT_SET(RBX_DUAL);
506						opts |= OPT_SET(RBX_SERIAL);
507						cp = "no";
508					}
509					printf("Keyboard: %s\n", cp);
510					continue;
511				} else if (c == 'S') {
512					char *end;
513
514					errno = 0;
515					i = strtol(arg, &end, 10);
516					if (errno == 0 &&
517					    *arg != '\0' &&
518					    *end == '\0' &&
519					    i > 0 &&
520					    i <= 115200) {
521						comspeed = i;
522						break;
523					} else {
524						printf("warning: bad value for "
525						    "speed: %s\n", arg);
526					}
527					arg = end;
528					/*
529					 * Fall through to error below
530					 * ('S' not in optstr[]).
531					 */
532				}
533				for (i = 0; c != optstr[i]; i++)
534					if (i == NOPT - 1)
535						return (-1);
536				opts ^= OPT_SET(flags[i]);
537			}
538			if (OPT_CHECK(RBX_DUAL))
539				ioctrl = IO_SERIAL | IO_KEYBOARD;
540			else if (OPT_CHECK(RBX_SERIAL))
541				ioctrl = IO_SERIAL;
542			else
543				ioctrl = IO_KEYBOARD;
544
545			if (ioctrl & IO_SERIAL) {
546				if (sio_init(115200 / comspeed) != 0)
547					ioctrl &= ~IO_SERIAL;
548			}
549		} else if (c == '?') {
550			printf("\n");
551			fs_list(arg);
552			zfs_list(arg);
553			return (-1);
554		} else {
555			arg--;
556
557			/*
558			 * Report pool status if the comment is 'status'. Lets
559			 * hope no-one wants to load /status as a kernel.
560			 */
561			if (strcmp(arg, "status") == 0) {
562				pager_open();
563				for (i = 0; devsw[i] != NULL; i++) {
564					if (devsw[i]->dv_print != NULL) {
565						if (devsw[i]->dv_print(1))
566							break;
567					} else {
568						sprintf(line,
569						    "%s: (unknown)\n",
570						    devsw[i]->dv_name);
571						if (pager_output(line))
572							break;
573					}
574				}
575				pager_close();
576				return (-1);
577			}
578
579			/*
580			 * If there is a colon, switch pools.
581			 */
582			if (strncmp(arg, "zfs:", 4) == 0)
583				q = strrchr(arg + 4, ':');
584			else
585				q = strrchr(arg, ':');
586
587			if (q != NULL) {
588				*q++ = '\0';
589				if (mount_root(arg) != 0)
590					return (-1);
591				arg = q;
592			}
593			if ((i = ep - arg)) {
594				if ((size_t)i >= sizeof (kname))
595					return (-1);
596				memcpy(kname, arg, i + 1);
597			}
598		}
599		arg = p;
600	}
601	return (0);
602}
603
604/*
605 * probe arguments for partition iterator (see below)
606 */
607struct probe_args {
608	int		fd;
609	char		*devname;
610	uint_t		secsz;
611	uint64_t	offset;
612};
613
614/*
615 * simple wrapper around read() to avoid using device specific
616 * strategy() directly.
617 */
618static int
619parttblread(void *arg, void *buf, size_t blocks, uint64_t offset)
620{
621	struct probe_args *ppa = arg;
622	size_t size = ppa->secsz * blocks;
623
624	lseek(ppa->fd, offset * ppa->secsz, SEEK_SET);
625	if (read(ppa->fd, buf, size) == size)
626		return (0);
627	return (EIO);
628}
629
630/*
631 * scan partition entries to find boot partition starting at start_sector.
632 * in case of MBR partition type PART_SOLARIS2, read VTOC and recurse.
633 */
634static int
635probe_partition(void *arg, const char *partname,
636    const struct ptable_entry *part)
637{
638	struct probe_args pa, *ppa = arg;
639	struct ptable *table;
640	uint64_t *pool_guid_ptr = NULL;
641	uint64_t pool_guid = 0;
642	char devname[32];
643	int len, ret = 0;
644
645	len = strlen(ppa->devname);
646	if (len > sizeof (devname))
647		len = sizeof (devname);
648
649	strncpy(devname, ppa->devname, len - 1);
650	devname[len - 1] = '\0';
651	snprintf(devname, sizeof (devname), "%s%s:", devname, partname);
652
653	/* filter out partitions *not* used by zfs */
654	switch (part->type) {
655	case PART_RESERVED:	/* efi reserverd */
656	case PART_VTOC_BOOT:	/* vtoc boot area */
657	case PART_VTOC_SWAP:
658		return (ret);
659	default:
660		break;
661	}
662
663	if (part->type == PART_SOLARIS2) {
664		pa.offset = part->start;
665		pa.fd = open(devname, O_RDONLY);
666		if (pa.fd == -1)
667			return (ret);
668		pa.devname = devname;
669		pa.secsz = ppa->secsz;
670		table = ptable_open(&pa, part->end - part->start + 1,
671		    ppa->secsz, parttblread);
672		if (table != NULL) {
673			enum ptable_type pt = ptable_gettype(table);
674
675			if (pt == PTABLE_VTOC8 || pt == PTABLE_VTOC) {
676				ret = ptable_iterate(table, &pa,
677				    probe_partition);
678				ptable_close(table);
679				close(pa.fd);
680				return (ret);
681			}
682			ptable_close(table);
683		}
684		close(pa.fd);
685	}
686
687	if (ppa->offset + part->start == start_sector) {
688		/* Ask zfs_probe_dev to provide guid. */
689		pool_guid_ptr = &pool_guid;
690		/* Set up boot device name for non-zfs case. */
691		strncpy(boot_devname, devname, sizeof (boot_devname));
692	}
693
694	ret = zfs_probe_dev(devname, pool_guid_ptr);
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		 * We can not set up zfs boot device name yet, as the
703		 * zfs dv_init() is not completed. We will set boot_devname
704		 * in main, after devsw setup.
705		 */
706	}
707
708	return (0);
709}
710
711/*
712 * open partition table on disk and scan partition entries to find
713 * boot partition starting at start_sector (recorded by installboot).
714 */
715static int
716probe_disk(char *devname)
717{
718	struct ptable *table;
719	struct probe_args pa;
720	uint64_t mediasz;
721	int ret;
722
723	pa.offset = 0;
724	pa.devname = devname;
725	pa.fd = open(devname, O_RDONLY);
726	if (pa.fd == -1) {
727		return (ENXIO);
728	}
729
730	ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
731	if (ret == 0)
732		ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
733	if (ret == 0) {
734		table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
735		    parttblread);
736		if (table != NULL) {
737			ret = ptable_iterate(table, &pa, probe_partition);
738			ptable_close(table);
739		}
740	}
741	close(pa.fd);
742	return (ret);
743}
744
745/*
746 * Probe all disks to discover ZFS pools. The idea is to walk all possible
747 * disk devices, however, we also need to identify possible boot pool.
748 * For boot pool detection we have boot disk passed us from BIOS, recorded
749 * in bootinfo.bi_bios_dev, and start_sector LBA recorded by installboot.
750 *
751 * To detect boot pool, we can not use generic zfs_probe_dev() on boot disk,
752 * but we need to walk partitions, as we have no way to pass start_sector
753 * to zfs_probe_dev(). Note we do need to detect the partition correcponding
754 * to non-zfs case, so here we can set boot_devname for both cases.
755 */
756static void
757i386_zfs_probe(void)
758{
759	char devname[32];
760	int boot_unit;
761	struct i386_devdesc dev;
762
763	dev.dd.d_dev = &bioshd;
764	/* Translate bios dev to our unit number. */
765	boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
766
767	/*
768	 * Open all the disks we can find and see if we can reconstruct
769	 * ZFS pools from them.
770	 */
771	for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
772		snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
773		    dev.dd.d_unit);
774		/* If this is not boot disk, use generic probe. */
775		if (dev.dd.d_unit != boot_unit)
776			zfs_probe_dev(devname, NULL);
777		else
778			probe_disk(devname);
779	}
780}
781