xref: /titanic_52/usr/src/boot/sys/boot/i386/gptzfsboot/zfsboot.c (revision d1104d6473f5cb1e9a2a6c1d43d70e6703ab6f90)
14a5d661aSToomas Soome /*-
24a5d661aSToomas Soome  * Copyright (c) 1998 Robert Nordier
34a5d661aSToomas Soome  * All rights reserved.
44a5d661aSToomas Soome  *
54a5d661aSToomas Soome  * Redistribution and use in source and binary forms are freely
64a5d661aSToomas Soome  * permitted provided that the above copyright notice and this
74a5d661aSToomas Soome  * paragraph and the following disclaimer are duplicated in all
84a5d661aSToomas Soome  * such forms.
94a5d661aSToomas Soome  *
104a5d661aSToomas Soome  * This software is provided "AS IS" and without any express or
114a5d661aSToomas Soome  * implied warranties, including, without limitation, the implied
124a5d661aSToomas Soome  * warranties of merchantability and fitness for a particular
134a5d661aSToomas Soome  * purpose.
144a5d661aSToomas Soome  */
154a5d661aSToomas Soome 
164a5d661aSToomas Soome #include <sys/cdefs.h>
174a5d661aSToomas Soome #include <stand.h>
184a5d661aSToomas Soome 
194a5d661aSToomas Soome #include <sys/param.h>
204a5d661aSToomas Soome #include <sys/errno.h>
214a5d661aSToomas Soome #include <sys/diskmbr.h>
224a5d661aSToomas Soome #include <sys/vtoc.h>
234a5d661aSToomas Soome #include <sys/disk.h>
244a5d661aSToomas Soome #include <sys/reboot.h>
254a5d661aSToomas Soome #include <sys/queue.h>
264a5d661aSToomas Soome #include <multiboot.h>
274a5d661aSToomas Soome 
284a5d661aSToomas Soome #include <machine/bootinfo.h>
294a5d661aSToomas Soome #include <machine/elf.h>
304a5d661aSToomas Soome #include <machine/pc/bios.h>
314a5d661aSToomas Soome 
324a5d661aSToomas Soome #include <stdarg.h>
334a5d661aSToomas Soome #include <stddef.h>
344a5d661aSToomas Soome 
354a5d661aSToomas Soome #include <a.out.h>
364a5d661aSToomas Soome #include "bootstrap.h"
374a5d661aSToomas Soome #include "libi386.h"
384a5d661aSToomas Soome #include <btxv86.h>
394a5d661aSToomas Soome 
404a5d661aSToomas Soome #include "lib.h"
414a5d661aSToomas Soome #include "rbx.h"
424a5d661aSToomas Soome #include "cons.h"
434a5d661aSToomas Soome #include "bootargs.h"
444a5d661aSToomas Soome #include "disk.h"
454a5d661aSToomas Soome #include "part.h"
464a5d661aSToomas Soome #include "paths.h"
474a5d661aSToomas Soome 
484a5d661aSToomas Soome #include "libzfs.h"
494a5d661aSToomas Soome 
504a5d661aSToomas Soome #define ARGS		0x900
514a5d661aSToomas Soome #define NOPT		14
524a5d661aSToomas Soome #define NDEV		3
534a5d661aSToomas Soome 
544a5d661aSToomas Soome #define BIOS_NUMDRIVES	0x475
554a5d661aSToomas Soome #define DRV_HARD	0x80
564a5d661aSToomas Soome #define DRV_MASK	0x7f
574a5d661aSToomas Soome 
584a5d661aSToomas Soome #define TYPE_AD		0
594a5d661aSToomas Soome #define TYPE_DA		1
604a5d661aSToomas Soome #define TYPE_MAXHARD	TYPE_DA
614a5d661aSToomas Soome #define TYPE_FD		2
624a5d661aSToomas Soome 
634a5d661aSToomas Soome extern uint32_t _end;
644a5d661aSToomas Soome 
654a5d661aSToomas Soome /*
664a5d661aSToomas Soome  * Fake multiboot header to provide versioning and to pass
674a5d661aSToomas Soome  * partition start LBA. Partition is either GPT partition or
684a5d661aSToomas Soome  * VTOC slice.
694a5d661aSToomas Soome  */
704a5d661aSToomas Soome extern const struct multiboot_header mb_header;
714a5d661aSToomas Soome extern uint64_t start_sector;
724a5d661aSToomas Soome 
734a5d661aSToomas Soome static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
744a5d661aSToomas Soome static const unsigned char flags[NOPT] = {
754a5d661aSToomas Soome     RBX_DUAL,
764a5d661aSToomas Soome     RBX_SERIAL,
774a5d661aSToomas Soome     RBX_ASKNAME,
784a5d661aSToomas Soome     RBX_CDROM,
794a5d661aSToomas Soome     RBX_CONFIG,
804a5d661aSToomas Soome     RBX_KDB,
814a5d661aSToomas Soome     RBX_GDB,
824a5d661aSToomas Soome     RBX_MUTE,
834a5d661aSToomas Soome     RBX_NOINTR,
844a5d661aSToomas Soome     RBX_PAUSE,
854a5d661aSToomas Soome     RBX_QUIET,
864a5d661aSToomas Soome     RBX_DFLTROOT,
874a5d661aSToomas Soome     RBX_SINGLE,
884a5d661aSToomas Soome     RBX_VERBOSE
894a5d661aSToomas Soome };
904a5d661aSToomas Soome uint32_t opts;
914a5d661aSToomas Soome 
924a5d661aSToomas Soome static const unsigned char dev_maj[NDEV] = {30, 4, 2};
934a5d661aSToomas Soome 
944a5d661aSToomas Soome static struct i386_devdesc *bdev;
954a5d661aSToomas Soome static char cmd[512];
964a5d661aSToomas Soome static char cmddup[512];
974a5d661aSToomas Soome static char kname[1024];
984a5d661aSToomas Soome static int comspeed = SIOSPD;
994a5d661aSToomas Soome static struct bootinfo bootinfo;
1004a5d661aSToomas Soome static uint32_t bootdev;
1014a5d661aSToomas Soome static struct zfs_boot_args zfsargs;
1024a5d661aSToomas Soome 
1034a5d661aSToomas Soome extern vm_offset_t high_heap_base;
1044a5d661aSToomas Soome extern uint32_t	bios_basemem, bios_extmem, high_heap_size;
1054a5d661aSToomas Soome 
1064a5d661aSToomas Soome static char *heap_top;
1074a5d661aSToomas Soome static char *heap_bottom;
1084a5d661aSToomas Soome 
1094a5d661aSToomas Soome static void i386_zfs_probe(void);
1104a5d661aSToomas Soome void exit(int);
1114a5d661aSToomas Soome static void load(void);
1124a5d661aSToomas Soome static int parse_cmd(void);
1134a5d661aSToomas Soome 
1144a5d661aSToomas Soome struct arch_switch archsw;	/* MI/MD interface boundary */
1154a5d661aSToomas Soome static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
1164a5d661aSToomas Soome 
1174a5d661aSToomas Soome struct devsw *devsw[] = {
1184a5d661aSToomas Soome 	&biosdisk,
1194a5d661aSToomas Soome 	&zfs_dev,
1204a5d661aSToomas Soome 	NULL
1214a5d661aSToomas Soome };
1224a5d661aSToomas Soome 
1234a5d661aSToomas Soome struct fs_ops *file_system[] = {
1244a5d661aSToomas Soome 	&zfs_fsops,
1254a5d661aSToomas Soome 	&ufs_fsops,
1264a5d661aSToomas Soome 	&dosfs_fsops,
1274a5d661aSToomas Soome 	NULL
1284a5d661aSToomas Soome };
1294a5d661aSToomas Soome 
1304a5d661aSToomas Soome int
1314a5d661aSToomas Soome main(void)
1324a5d661aSToomas Soome {
133b406476aSToomas Soome     int auto_boot, i, fd;
1344a5d661aSToomas Soome     struct disk_devdesc devdesc;
1354a5d661aSToomas Soome 
1364a5d661aSToomas Soome     bios_getmem();
1374a5d661aSToomas Soome 
1384a5d661aSToomas Soome     if (high_heap_size > 0) {
1394a5d661aSToomas Soome 	heap_top = PTOV(high_heap_base + high_heap_size);
1404a5d661aSToomas Soome 	heap_bottom = PTOV(high_heap_base);
1414a5d661aSToomas Soome     } else {
1424a5d661aSToomas Soome 	heap_bottom = (char *)
1434a5d661aSToomas Soome 	    (roundup2(__base + (int32_t)&_end, 0x10000) - __base);
1444a5d661aSToomas Soome 	heap_top = (char *) PTOV(bios_basemem);
1454a5d661aSToomas Soome     }
1464a5d661aSToomas Soome     setheap(heap_bottom, heap_top);
1474a5d661aSToomas Soome 
1484a5d661aSToomas Soome     /*
1494a5d661aSToomas Soome      * Initialise the block cache. Set the upper limit.
1504a5d661aSToomas Soome      */
1514a5d661aSToomas Soome     bcache_init(32768, 512);
1524a5d661aSToomas Soome 
1534a5d661aSToomas Soome     archsw.arch_autoload = NULL;
1544a5d661aSToomas Soome     archsw.arch_getdev = i386_getdev;
1554a5d661aSToomas Soome     archsw.arch_copyin = NULL;
1564a5d661aSToomas Soome     archsw.arch_copyout = NULL;
1574a5d661aSToomas Soome     archsw.arch_readin = NULL;
1584a5d661aSToomas Soome     archsw.arch_isainb = NULL;
1594a5d661aSToomas Soome     archsw.arch_isaoutb = NULL;
1604a5d661aSToomas Soome     archsw.arch_zfs_probe = i386_zfs_probe;
1614a5d661aSToomas Soome 
1624a5d661aSToomas Soome     bootinfo.bi_version = BOOTINFO_VERSION;
1634a5d661aSToomas Soome     bootinfo.bi_size = sizeof(bootinfo);
1644a5d661aSToomas Soome     bootinfo.bi_basemem = bios_basemem / 1024;
1654a5d661aSToomas Soome     bootinfo.bi_extmem = bios_extmem / 1024;
1664a5d661aSToomas Soome     bootinfo.bi_memsizes_valid++;
1674a5d661aSToomas Soome     bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
1684a5d661aSToomas Soome 
169b406476aSToomas Soome     /* Set up fall back device name. */
170b406476aSToomas Soome     snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
171b406476aSToomas Soome 	bd_bios2unit(bootinfo.bi_bios_dev));
172b406476aSToomas Soome 
173b406476aSToomas Soome     for (i = 0; devsw[i] != NULL; i++)
174b406476aSToomas Soome 	if (devsw[i]->dv_init != NULL)
175b406476aSToomas Soome 	    (devsw[i]->dv_init)();
1764a5d661aSToomas Soome 
1774a5d661aSToomas Soome     disk_parsedev(&devdesc, boot_devname+4, NULL);
1784a5d661aSToomas Soome 
1794a5d661aSToomas Soome     bootdev = MAKEBOOTDEV(dev_maj[devdesc.d_type], devdesc.d_slice + 1,
1804a5d661aSToomas Soome 	devdesc.d_unit, devdesc.d_partition >= 0? devdesc.d_partition:0xff);
1814a5d661aSToomas Soome 
1824a5d661aSToomas Soome     /*
1834a5d661aSToomas Soome      * zfs_fmtdev() can be called only after dv_init
1844a5d661aSToomas Soome      */
1854a5d661aSToomas Soome     if (bdev != NULL && bdev->d_type == DEVT_ZFS) {
1864a5d661aSToomas Soome 	/* set up proper device name string for ZFS */
187b406476aSToomas Soome 	strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname));
1884a5d661aSToomas Soome     }
1894a5d661aSToomas Soome 
1904a5d661aSToomas Soome     /* now make sure we have bdev on all cases */
1914a5d661aSToomas Soome     if (bdev != NULL)
1924a5d661aSToomas Soome 	free(bdev);
1934a5d661aSToomas Soome     i386_getdev((void **)&bdev, boot_devname, NULL);
1944a5d661aSToomas Soome 
1954a5d661aSToomas Soome     env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev,
1964a5d661aSToomas Soome 	env_nounset);
1974a5d661aSToomas Soome 
1984a5d661aSToomas Soome     /* Process configuration file */
1994a5d661aSToomas Soome     setenv("LINES", "24", 1);
2004a5d661aSToomas Soome     auto_boot = 1;
2014a5d661aSToomas Soome 
2024a5d661aSToomas Soome     fd = open(PATH_CONFIG, O_RDONLY);
2034a5d661aSToomas Soome     if (fd == -1)
2044a5d661aSToomas Soome 	fd = open(PATH_DOTCONFIG, O_RDONLY);
2054a5d661aSToomas Soome 
2064a5d661aSToomas Soome     if (fd != -1) {
2074a5d661aSToomas Soome 	read(fd, cmd, sizeof(cmd));
2084a5d661aSToomas Soome 	close(fd);
2094a5d661aSToomas Soome     }
2104a5d661aSToomas Soome 
2114a5d661aSToomas Soome     if (*cmd) {
2124a5d661aSToomas Soome 	/*
2134a5d661aSToomas Soome 	 * Note that parse_cmd() is destructive to cmd[] and we also want
2144a5d661aSToomas Soome 	 * to honor RBX_QUIET option that could be present in cmd[].
2154a5d661aSToomas Soome 	 */
2164a5d661aSToomas Soome 	memcpy(cmddup, cmd, sizeof(cmd));
2174a5d661aSToomas Soome 	if (parse_cmd())
2184a5d661aSToomas Soome 	    auto_boot = 0;
2194a5d661aSToomas Soome 	if (!OPT_CHECK(RBX_QUIET))
2204a5d661aSToomas Soome 	    printf("%s: %s\n", PATH_CONFIG, cmddup);
2214a5d661aSToomas Soome 	/* Do not process this command twice */
2224a5d661aSToomas Soome 	*cmd = 0;
2234a5d661aSToomas Soome     }
2244a5d661aSToomas Soome 
2254a5d661aSToomas Soome     /*
2264a5d661aSToomas Soome      * Try to exec stage 3 boot loader. If interrupted by a keypress,
2274a5d661aSToomas Soome      * or in case of failure, switch off auto boot.
2284a5d661aSToomas Soome      */
2294a5d661aSToomas Soome 
2304a5d661aSToomas Soome     if (auto_boot && !*kname) {
2314a5d661aSToomas Soome 	memcpy(kname, PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS));
2324a5d661aSToomas Soome 	if (!keyhit(3)) {
2334a5d661aSToomas Soome 	    load();
2344a5d661aSToomas Soome 	    auto_boot = 0;
2354a5d661aSToomas Soome 	}
2364a5d661aSToomas Soome     }
2374a5d661aSToomas Soome 
2384a5d661aSToomas Soome     /* Present the user with the boot2 prompt. */
2394a5d661aSToomas Soome 
2404a5d661aSToomas Soome     for (;;) {
2414a5d661aSToomas Soome 	if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
2424a5d661aSToomas Soome 	    printf("\nillumos/x86 boot\n");
2434a5d661aSToomas Soome 	    printf("Default: %s%s\nboot: ", boot_devname, kname);
2444a5d661aSToomas Soome 	}
2454a5d661aSToomas Soome 	if (ioctrl & IO_SERIAL)
2464a5d661aSToomas Soome 	    sio_flush();
2474a5d661aSToomas Soome 	if (!auto_boot || keyhit(5))
2484a5d661aSToomas Soome 	    getstr(cmd, sizeof(cmd));
2494a5d661aSToomas Soome 	else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
2504a5d661aSToomas Soome 	    putchar('\n');
2514a5d661aSToomas Soome 	auto_boot = 0;
2524a5d661aSToomas Soome 	if (parse_cmd())
2534a5d661aSToomas Soome 	    putchar('\a');
2544a5d661aSToomas Soome 	else
2554a5d661aSToomas Soome 	    load();
2564a5d661aSToomas Soome     }
2574a5d661aSToomas Soome }
2584a5d661aSToomas Soome 
2594a5d661aSToomas Soome /* XXX - Needed for btxld to link the boot2 binary; do not remove. */
2604a5d661aSToomas Soome void
2614a5d661aSToomas Soome exit(int x)
2624a5d661aSToomas Soome {
2634a5d661aSToomas Soome }
2644a5d661aSToomas Soome 
2654a5d661aSToomas Soome static void
2664a5d661aSToomas Soome load(void)
2674a5d661aSToomas Soome {
2684a5d661aSToomas Soome     union {
2694a5d661aSToomas Soome 	struct exec ex;
2704a5d661aSToomas Soome 	Elf32_Ehdr eh;
2714a5d661aSToomas Soome     } hdr;
2724a5d661aSToomas Soome     static Elf32_Phdr ep[2];
2734a5d661aSToomas Soome     static Elf32_Shdr es[2];
2744a5d661aSToomas Soome     caddr_t p;
2754a5d661aSToomas Soome     uint32_t addr, x;
2764a5d661aSToomas Soome     int fd, fmt, i, j;
2774a5d661aSToomas Soome 
2784a5d661aSToomas Soome     if ((fd = open(kname, O_RDONLY)) == -1) {
2794a5d661aSToomas Soome 	printf("\nCan't find %s\n", kname);
2804a5d661aSToomas Soome 	return;
2814a5d661aSToomas Soome     }
2824a5d661aSToomas Soome     if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
2834a5d661aSToomas Soome 	close(fd);
2844a5d661aSToomas Soome 	return;
2854a5d661aSToomas Soome     }
2864a5d661aSToomas Soome     if (N_GETMAGIC(hdr.ex) == ZMAGIC)
2874a5d661aSToomas Soome 	fmt = 0;
2884a5d661aSToomas Soome     else if (IS_ELF(hdr.eh))
2894a5d661aSToomas Soome 	fmt = 1;
2904a5d661aSToomas Soome     else {
2914a5d661aSToomas Soome 	printf("Invalid %s\n", "format");
2924a5d661aSToomas Soome 	close(fd);
2934a5d661aSToomas Soome 	return;
2944a5d661aSToomas Soome     }
2954a5d661aSToomas Soome     if (fmt == 0) {
2964a5d661aSToomas Soome 	addr = hdr.ex.a_entry & 0xffffff;
2974a5d661aSToomas Soome 	p = PTOV(addr);
2984a5d661aSToomas Soome 	lseek(fd, PAGE_SIZE, SEEK_SET);
2994a5d661aSToomas Soome 	if (read(fd, p, hdr.ex.a_text) != hdr.ex.a_text) {
3004a5d661aSToomas Soome 	    close(fd);
3014a5d661aSToomas Soome 	    return;
3024a5d661aSToomas Soome 	}
3034a5d661aSToomas Soome 	p += roundup2(hdr.ex.a_text, PAGE_SIZE);
3044a5d661aSToomas Soome 	if (read(fd, p, hdr.ex.a_data) != hdr.ex.a_data) {
3054a5d661aSToomas Soome 	    close(fd);
3064a5d661aSToomas Soome 	    return;
3074a5d661aSToomas Soome 	}
3084a5d661aSToomas Soome 	p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
3094a5d661aSToomas Soome 	bootinfo.bi_symtab = VTOP(p);
3104a5d661aSToomas Soome 	memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
3114a5d661aSToomas Soome 	p += sizeof(hdr.ex.a_syms);
3124a5d661aSToomas Soome 	if (hdr.ex.a_syms) {
3134a5d661aSToomas Soome 	    if (read(fd, p, hdr.ex.a_syms) != hdr.ex.a_syms) {
3144a5d661aSToomas Soome 		close(fd);
3154a5d661aSToomas Soome 		return;
3164a5d661aSToomas Soome 	    }
3174a5d661aSToomas Soome 	    p += hdr.ex.a_syms;
3184a5d661aSToomas Soome 	    if (read(fd, p, sizeof(int)) != sizeof(int)) {
3194a5d661aSToomas Soome 		close(fd);
3204a5d661aSToomas Soome 		return;
3214a5d661aSToomas Soome 	    }
3224a5d661aSToomas Soome 	    x = *(uint32_t *)p;
3234a5d661aSToomas Soome 	    p += sizeof(int);
3244a5d661aSToomas Soome 	    x -= sizeof(int);
3254a5d661aSToomas Soome 	    if (read(fd, p, x) != x) {
3264a5d661aSToomas Soome 		close(fd);
3274a5d661aSToomas Soome 		return;
3284a5d661aSToomas Soome 	    }
3294a5d661aSToomas Soome 	    p += x;
3304a5d661aSToomas Soome 	}
3314a5d661aSToomas Soome     } else {
3324a5d661aSToomas Soome 	lseek(fd, hdr.eh.e_phoff, SEEK_SET);
3334a5d661aSToomas Soome 	for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
3344a5d661aSToomas Soome 	    if (read(fd, ep + j, sizeof(ep[0])) != sizeof(ep[0])) {
3354a5d661aSToomas Soome 		close(fd);
3364a5d661aSToomas Soome 		return;
3374a5d661aSToomas Soome 	    }
3384a5d661aSToomas Soome 	    if (ep[j].p_type == PT_LOAD)
3394a5d661aSToomas Soome 		j++;
3404a5d661aSToomas Soome 	}
3414a5d661aSToomas Soome 	for (i = 0; i < 2; i++) {
3424a5d661aSToomas Soome 	    p = PTOV(ep[i].p_paddr & 0xffffff);
3434a5d661aSToomas Soome 	    lseek(fd, ep[i].p_offset, SEEK_SET);
3444a5d661aSToomas Soome 	    if (read(fd, p, ep[i].p_filesz) != ep[i].p_filesz) {
3454a5d661aSToomas Soome 		close(fd);
3464a5d661aSToomas Soome 		return;
3474a5d661aSToomas Soome 	    }
3484a5d661aSToomas Soome 	}
3494a5d661aSToomas Soome 	p += roundup2(ep[1].p_memsz, PAGE_SIZE);
3504a5d661aSToomas Soome 	bootinfo.bi_symtab = VTOP(p);
3514a5d661aSToomas Soome 	if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
3524a5d661aSToomas Soome 	    lseek(fd, hdr.eh.e_shoff + sizeof(es[0]) * (hdr.eh.e_shstrndx + 1),
3534a5d661aSToomas Soome 		SEEK_SET);
3544a5d661aSToomas Soome 	    if (read(fd, &es, sizeof(es)) != sizeof(es)) {
3554a5d661aSToomas Soome 		close(fd);
3564a5d661aSToomas Soome 		return;
3574a5d661aSToomas Soome 	    }
3584a5d661aSToomas Soome 	    for (i = 0; i < 2; i++) {
3594a5d661aSToomas Soome 		memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size));
3604a5d661aSToomas Soome 		p += sizeof(es[i].sh_size);
3614a5d661aSToomas Soome 		lseek(fd, es[i].sh_offset, SEEK_SET);
3624a5d661aSToomas Soome 		if (read(fd, p, es[i].sh_size) != es[i].sh_size) {
3634a5d661aSToomas Soome 		    close(fd);
3644a5d661aSToomas Soome 		    return;
3654a5d661aSToomas Soome 		}
3664a5d661aSToomas Soome 		p += es[i].sh_size;
3674a5d661aSToomas Soome 	    }
3684a5d661aSToomas Soome 	}
3694a5d661aSToomas Soome 	addr = hdr.eh.e_entry & 0xffffff;
3704a5d661aSToomas Soome     }
3714a5d661aSToomas Soome     close(fd);
3724a5d661aSToomas Soome 
3734a5d661aSToomas Soome     bootinfo.bi_esymtab = VTOP(p);
3744a5d661aSToomas Soome     bootinfo.bi_kernelname = VTOP(kname);
3754a5d661aSToomas Soome 
3764a5d661aSToomas Soome     if (bdev->d_type == DEVT_ZFS) {
3774a5d661aSToomas Soome 	zfsargs.size = sizeof(zfsargs);
3784a5d661aSToomas Soome 	zfsargs.pool = bdev->d_kind.zfs.pool_guid;
3794a5d661aSToomas Soome 	zfsargs.root = bdev->d_kind.zfs.root_guid;
3804a5d661aSToomas Soome 	__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
3814a5d661aSToomas Soome 	    bootdev,
3824a5d661aSToomas Soome 	    KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
3834a5d661aSToomas Soome 	    (uint32_t) bdev->d_kind.zfs.pool_guid,
3844a5d661aSToomas Soome 	    (uint32_t) (bdev->d_kind.zfs.pool_guid >> 32),
3854a5d661aSToomas Soome 	    VTOP(&bootinfo),
3864a5d661aSToomas Soome 	    zfsargs);
3874a5d661aSToomas Soome     } else
3884a5d661aSToomas Soome 	__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
3894a5d661aSToomas Soome 	   bootdev, 0, 0, 0, VTOP(&bootinfo));
3904a5d661aSToomas Soome }
3914a5d661aSToomas Soome 
3924a5d661aSToomas Soome static int
3934a5d661aSToomas Soome mount_root(char *arg)
3944a5d661aSToomas Soome {
3954a5d661aSToomas Soome     char *root;
3964a5d661aSToomas Soome     struct i386_devdesc *ddesc;
3974a5d661aSToomas Soome     uint8_t part;
3984a5d661aSToomas Soome 
3994a5d661aSToomas Soome     root = malloc(strlen(arg) + 2);
4004a5d661aSToomas Soome     if (root == NULL)
4014a5d661aSToomas Soome 	return (1);
4024a5d661aSToomas Soome     sprintf(root, "%s:", arg);
4034a5d661aSToomas Soome     if (i386_getdev((void **)&ddesc, root, NULL)) {
4044a5d661aSToomas Soome 	free(root);
4054a5d661aSToomas Soome 	return (1);
4064a5d661aSToomas Soome     }
4074a5d661aSToomas Soome 
4084a5d661aSToomas Soome     /* we should have new device descriptor, free old and replace it. */
4094a5d661aSToomas Soome     if (bdev != NULL)
4104a5d661aSToomas Soome 	free(bdev);
4114a5d661aSToomas Soome     bdev = ddesc;
4124a5d661aSToomas Soome     if (bdev->d_type == DEVT_DISK) {
4134a5d661aSToomas Soome 	if (bdev->d_kind.biosdisk.partition == -1)
4144a5d661aSToomas Soome 	    part = 0xff;
4154a5d661aSToomas Soome 	else
4164a5d661aSToomas Soome 	    part = bdev->d_kind.biosdisk.partition;
4174a5d661aSToomas Soome 	bootdev = MAKEBOOTDEV(dev_maj[bdev->d_type],
4184a5d661aSToomas Soome 	    bdev->d_kind.biosdisk.slice + 1,
4194a5d661aSToomas Soome 	    bdev->d_unit, part);
4204a5d661aSToomas Soome 	bootinfo.bi_bios_dev = bd_unit2bios(bdev->d_unit);
4214a5d661aSToomas Soome     }
4224a5d661aSToomas Soome     setenv("currdev", root, 1);
4234a5d661aSToomas Soome     free(root);
4244a5d661aSToomas Soome     return (0);
4254a5d661aSToomas Soome }
4264a5d661aSToomas Soome 
4274a5d661aSToomas Soome static void
4284a5d661aSToomas Soome fs_list(char *arg)
4294a5d661aSToomas Soome {
4304a5d661aSToomas Soome 	int fd;
4314a5d661aSToomas Soome 	struct dirent *d;
4324a5d661aSToomas Soome 	char line[80];
4334a5d661aSToomas Soome 
4344a5d661aSToomas Soome 	fd = open(arg, O_RDONLY);
4354a5d661aSToomas Soome 	if (fd < 0)
4364a5d661aSToomas Soome 		return;
4374a5d661aSToomas Soome 	pager_open();
4384a5d661aSToomas Soome 	while ((d = readdirfd(fd)) != NULL) {
4394a5d661aSToomas Soome 		sprintf(line, "%s\n", d->d_name);
4404a5d661aSToomas Soome 		if (pager_output(line))
4414a5d661aSToomas Soome 			break;
4424a5d661aSToomas Soome 	}
4434a5d661aSToomas Soome 	pager_close();
4444a5d661aSToomas Soome 	close(fd);
4454a5d661aSToomas Soome }
4464a5d661aSToomas Soome 
4474a5d661aSToomas Soome static int
4484a5d661aSToomas Soome parse_cmd(void)
4494a5d661aSToomas Soome {
4504a5d661aSToomas Soome     char *arg = cmd;
4514a5d661aSToomas Soome     char *ep, *p, *q;
4524a5d661aSToomas Soome     const char *cp;
4534a5d661aSToomas Soome     char line[80];
4544a5d661aSToomas Soome     int c, i, j;
4554a5d661aSToomas Soome 
4564a5d661aSToomas Soome     while ((c = *arg++)) {
4574a5d661aSToomas Soome 	if (c == ' ' || c == '\t' || c == '\n')
4584a5d661aSToomas Soome 	    continue;
4594a5d661aSToomas Soome 	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
4604a5d661aSToomas Soome 	ep = p;
4614a5d661aSToomas Soome 	if (*p)
4624a5d661aSToomas Soome 	    *p++ = 0;
4634a5d661aSToomas Soome 	if (c == '-') {
4644a5d661aSToomas Soome 	    while ((c = *arg++)) {
4654a5d661aSToomas Soome 		if (c == 'P') {
4664a5d661aSToomas Soome 		    if (*(uint8_t *)PTOV(0x496) & 0x10) {
4674a5d661aSToomas Soome 			cp = "yes";
4684a5d661aSToomas Soome 		    } else {
4694a5d661aSToomas Soome 			opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL);
4704a5d661aSToomas Soome 			cp = "no";
4714a5d661aSToomas Soome 		    }
4724a5d661aSToomas Soome 		    printf("Keyboard: %s\n", cp);
4734a5d661aSToomas Soome 		    continue;
4744a5d661aSToomas Soome 		} else if (c == 'S') {
4754a5d661aSToomas Soome 		    j = 0;
4764a5d661aSToomas Soome 		    while ((unsigned int)(i = *arg++ - '0') <= 9)
4774a5d661aSToomas Soome 			j = j * 10 + i;
4784a5d661aSToomas Soome 		    if (j > 0 && i == -'0') {
4794a5d661aSToomas Soome 			comspeed = j;
4804a5d661aSToomas Soome 			break;
4814a5d661aSToomas Soome 		    }
4824a5d661aSToomas Soome 		    /* Fall through to error below ('S' not in optstr[]). */
4834a5d661aSToomas Soome 		}
4844a5d661aSToomas Soome 		for (i = 0; c != optstr[i]; i++)
4854a5d661aSToomas Soome 		    if (i == NOPT - 1)
4864a5d661aSToomas Soome 			return -1;
4874a5d661aSToomas Soome 		opts ^= OPT_SET(flags[i]);
4884a5d661aSToomas Soome 	    }
4894a5d661aSToomas Soome 	    ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
4904a5d661aSToomas Soome 		     OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
4914a5d661aSToomas Soome 	    if (ioctrl & IO_SERIAL) {
4924a5d661aSToomas Soome 	        if (sio_init(115200 / comspeed) != 0)
4934a5d661aSToomas Soome 		    ioctrl &= ~IO_SERIAL;
4944a5d661aSToomas Soome 	    }
4954a5d661aSToomas Soome 	} if (c == '?') {
4964a5d661aSToomas Soome 	    printf("\n");
4974a5d661aSToomas Soome 	    fs_list(arg);
4984a5d661aSToomas Soome 	    zfs_list(arg);
4994a5d661aSToomas Soome 	    return -1;
5004a5d661aSToomas Soome 	} else {
5014a5d661aSToomas Soome 	    arg--;
5024a5d661aSToomas Soome 
5034a5d661aSToomas Soome 	    /*
5044a5d661aSToomas Soome 	     * Report pool status if the comment is 'status'. Lets
5054a5d661aSToomas Soome 	     * hope no-one wants to load /status as a kernel.
5064a5d661aSToomas Soome 	     */
5074a5d661aSToomas Soome 	    if (!strcmp(arg, "status")) {
5084a5d661aSToomas Soome 		pager_open();
5094a5d661aSToomas Soome 		for (i = 0; devsw[i] != NULL; i++) {
5104a5d661aSToomas Soome 		    if (devsw[i]->dv_print != NULL) {
5114a5d661aSToomas Soome 			if (devsw[i]->dv_print(1))
5124a5d661aSToomas Soome 			    break;
5134a5d661aSToomas Soome 		    } else {
5144a5d661aSToomas Soome 			sprintf(line, "%s: (unknown)\n", devsw[i]->dv_name);
5154a5d661aSToomas Soome 			if (pager_output(line))
5164a5d661aSToomas Soome 			    break;
5174a5d661aSToomas Soome 		    }
5184a5d661aSToomas Soome 		}
5194a5d661aSToomas Soome 		pager_close();
5204a5d661aSToomas Soome 		return -1;
5214a5d661aSToomas Soome 	    }
5224a5d661aSToomas Soome 
5234a5d661aSToomas Soome 	    /*
5244a5d661aSToomas Soome 	     * If there is a colon, switch pools.
5254a5d661aSToomas Soome 	     */
5264a5d661aSToomas Soome 	    if (strncmp(arg, "zfs:", 4) == 0)
5274a5d661aSToomas Soome 		q = strchr(arg + 4, ':');
5284a5d661aSToomas Soome 	    else
5294a5d661aSToomas Soome 		q = strchr(arg, ':');
5304a5d661aSToomas Soome 	    if (q) {
5314a5d661aSToomas Soome 		*q++ = '\0';
5324a5d661aSToomas Soome 		if (mount_root(arg) != 0)
5334a5d661aSToomas Soome 		    return -1;
5344a5d661aSToomas Soome 		arg = q;
5354a5d661aSToomas Soome 	    }
5364a5d661aSToomas Soome 	    if ((i = ep - arg)) {
5374a5d661aSToomas Soome 		if ((size_t)i >= sizeof(kname))
5384a5d661aSToomas Soome 		    return -1;
5394a5d661aSToomas Soome 		memcpy(kname, arg, i + 1);
5404a5d661aSToomas Soome 	    }
5414a5d661aSToomas Soome 	}
5424a5d661aSToomas Soome 	arg = p;
5434a5d661aSToomas Soome     }
5444a5d661aSToomas Soome     return 0;
5454a5d661aSToomas Soome }
5464a5d661aSToomas Soome 
547b406476aSToomas Soome /*
548b406476aSToomas Soome  * probe arguments for partition iterator (see below)
549b406476aSToomas Soome  */
550b406476aSToomas Soome struct probe_args {
551b406476aSToomas Soome 	int		fd;
552b406476aSToomas Soome 	char		*devname;
553b406476aSToomas Soome 	u_int		secsz;
554b406476aSToomas Soome 	uint64_t	offset;
555b406476aSToomas Soome };
556b406476aSToomas Soome 
557b406476aSToomas Soome /*
558b406476aSToomas Soome  * simple wrapper around read() to avoid using device specific
559b406476aSToomas Soome  * strategy() directly.
560b406476aSToomas Soome  */
561b406476aSToomas Soome static int
562b406476aSToomas Soome parttblread(void *arg, void *buf, size_t blocks, uint64_t offset)
563b406476aSToomas Soome {
564b406476aSToomas Soome 	struct probe_args *ppa = arg;
565b406476aSToomas Soome 	size_t size = ppa->secsz * blocks;
566b406476aSToomas Soome 
567b406476aSToomas Soome 	lseek(ppa->fd, offset * ppa->secsz, SEEK_SET);
568b406476aSToomas Soome 	if (read(ppa->fd, buf, size) == size)
569b406476aSToomas Soome 		return (0);
570b406476aSToomas Soome 	return (EIO);
571b406476aSToomas Soome }
572b406476aSToomas Soome 
573b406476aSToomas Soome /*
574b406476aSToomas Soome  * scan partition entries to find boot partition starting at start_sector.
575b406476aSToomas Soome  * in case of MBR partition type PART_SOLARIS2, read VTOC and recurse.
576b406476aSToomas Soome  */
577b406476aSToomas Soome static int
578b406476aSToomas Soome probe_partition(void *arg, const char *partname,
579b406476aSToomas Soome     const struct ptable_entry *part)
580b406476aSToomas Soome {
581b406476aSToomas Soome 	struct probe_args pa, *ppa = arg;
582b406476aSToomas Soome 	struct ptable *table;
583b406476aSToomas Soome 	uint64_t *pool_guid_ptr = NULL;
584b406476aSToomas Soome 	uint64_t pool_guid = 0;
585b406476aSToomas Soome 	char devname[32];
586b406476aSToomas Soome 	int len, ret = 0;
587b406476aSToomas Soome 
588b406476aSToomas Soome 	len = strlen(ppa->devname);
589b406476aSToomas Soome 	if (len > sizeof (devname))
590b406476aSToomas Soome 		len = sizeof (devname);
591b406476aSToomas Soome 
592b406476aSToomas Soome 	strncpy(devname, ppa->devname, len - 1);
593b406476aSToomas Soome 	devname[len - 1] = '\0';
594b406476aSToomas Soome 	snprintf(devname, sizeof (devname), "%s%s:", devname, partname);
595b406476aSToomas Soome 
596b406476aSToomas Soome 	/* filter out partitions *not* used by zfs */
597b406476aSToomas Soome 	switch (part->type) {
598b406476aSToomas Soome 	case PART_RESERVED:	/* efi reserverd */
599b406476aSToomas Soome 	case PART_VTOC_BOOT:	/* vtoc boot area */
600b406476aSToomas Soome 	case PART_VTOC_SWAP:
601b406476aSToomas Soome 		return (ret);
602b406476aSToomas Soome 	default:
603b406476aSToomas Soome 		break;
604b406476aSToomas Soome 	}
605b406476aSToomas Soome 
606b406476aSToomas Soome 	if (part->type == PART_SOLARIS2) {
607b406476aSToomas Soome 		pa.offset = part->start;
608b406476aSToomas Soome 		pa.fd = open(devname, O_RDONLY);
609b406476aSToomas Soome 		if (pa.fd == -1)
610b406476aSToomas Soome 			return (ret);
611b406476aSToomas Soome 		pa.devname = devname;
612b406476aSToomas Soome 		pa.secsz = ppa->secsz;
613b406476aSToomas Soome 		table = ptable_open(&pa, part->end - part->start + 1,
614b406476aSToomas Soome 		    ppa->secsz, parttblread);
615b406476aSToomas Soome 		if (table != NULL) {
616b406476aSToomas Soome 			ret = ptable_iterate(table, &pa, probe_partition);
617b406476aSToomas Soome 			ptable_close(table);
618b406476aSToomas Soome 		}
619b406476aSToomas Soome 		close(pa.fd);
620b406476aSToomas Soome 		return (ret);
621b406476aSToomas Soome 	}
622b406476aSToomas Soome 
623b406476aSToomas Soome 	if (ppa->offset + part->start == start_sector) {
624b406476aSToomas Soome 		/* Ask zfs_probe_dev to provide guid. */
625b406476aSToomas Soome 		pool_guid_ptr = &pool_guid;
626b406476aSToomas Soome 		/* Set up boot device name for non-zfs case. */
627b406476aSToomas Soome 		strncpy(boot_devname, devname, sizeof (boot_devname));
628b406476aSToomas Soome 	}
629b406476aSToomas Soome 
630b406476aSToomas Soome 	ret = zfs_probe_dev(devname, pool_guid_ptr);
631b406476aSToomas Soome 	if (pool_guid != 0 && bdev == NULL) {
632b406476aSToomas Soome 		bdev = malloc(sizeof (struct i386_devdesc));
633b406476aSToomas Soome 		bzero(bdev, sizeof (struct i386_devdesc));
634b406476aSToomas Soome 		bdev->d_type = DEVT_ZFS;
635b406476aSToomas Soome 		bdev->d_dev = &zfs_dev;
636b406476aSToomas Soome 		bdev->d_kind.zfs.pool_guid = pool_guid;
637b406476aSToomas Soome 
638b406476aSToomas Soome 		/*
639b406476aSToomas Soome 		 * We can not set up zfs boot device name yet, as the
640b406476aSToomas Soome 		 * zfs dv_init() is not completed. We will set boot_devname
641b406476aSToomas Soome 		 * in main, after devsw setup.
642b406476aSToomas Soome 		 */
643b406476aSToomas Soome 	}
644b406476aSToomas Soome 
645b406476aSToomas Soome 	return (0);
646b406476aSToomas Soome }
647b406476aSToomas Soome 
648b406476aSToomas Soome /*
649b406476aSToomas Soome  * open partition table on disk and scan partition entries to find
650b406476aSToomas Soome  * boot partition starting at start_sector (recorded by installboot).
651b406476aSToomas Soome  */
652b406476aSToomas Soome static int
653b406476aSToomas Soome probe_disk(char *devname)
654b406476aSToomas Soome {
655b406476aSToomas Soome 	struct ptable *table;
656b406476aSToomas Soome 	struct probe_args pa;
657b406476aSToomas Soome 	uint64_t mediasz;
658b406476aSToomas Soome 	int ret;
659b406476aSToomas Soome 
660b406476aSToomas Soome 	pa.offset = 0;
661b406476aSToomas Soome 	pa.devname = devname;
662b406476aSToomas Soome 	pa.fd = open(devname, O_RDONLY);
663b406476aSToomas Soome 	if (pa.fd == -1) {
664b406476aSToomas Soome 		return (ENXIO);
665b406476aSToomas Soome 	}
666b406476aSToomas Soome 
667b406476aSToomas Soome 	ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
668b406476aSToomas Soome 	if (ret == 0)
669b406476aSToomas Soome 		ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz);
670b406476aSToomas Soome 	if (ret == 0) {
671b406476aSToomas Soome 		table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz,
672b406476aSToomas Soome                     parttblread);
673b406476aSToomas Soome 		if (table != NULL) {
674b406476aSToomas Soome 			ret = ptable_iterate(table, &pa, probe_partition);
675b406476aSToomas Soome 			ptable_close(table);
676b406476aSToomas Soome 		}
677b406476aSToomas Soome 	}
678b406476aSToomas Soome 	close(pa.fd);
679b406476aSToomas Soome 	return (ret);
680b406476aSToomas Soome }
681b406476aSToomas Soome 
682b406476aSToomas Soome /*
683b406476aSToomas Soome  * Probe all disks to discover ZFS pools. The idea is to walk all possible
684b406476aSToomas Soome  * disk devices, however, we also need to identify possible boot pool.
685b406476aSToomas Soome  * For boot pool detection we have boot disk passed us from BIOS, recorded
686b406476aSToomas Soome  * in bootinfo.bi_bios_dev, and start_sector LBA recorded by installboot.
687b406476aSToomas Soome  *
688b406476aSToomas Soome  * To detect boot pool, we can not use generic zfs_probe_dev() on boot disk,
689b406476aSToomas Soome  * but we need to walk partitions, as we have no way to pass start_sector
690b406476aSToomas Soome  * to zfs_probe_dev(). Note we do need to detect the partition correcponding
691b406476aSToomas Soome  * to non-zfs case, so here we can set boot_devname for both cases.
692b406476aSToomas Soome  */
6934a5d661aSToomas Soome static void
6944a5d661aSToomas Soome i386_zfs_probe(void)
6954a5d661aSToomas Soome {
6964a5d661aSToomas Soome 	char devname[32];
697b406476aSToomas Soome 	int boot_unit, unit;
698b406476aSToomas Soome 
699b406476aSToomas Soome 	/* Translate bios dev to our unit number. */
700b406476aSToomas Soome 	boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
7014a5d661aSToomas Soome 
7024a5d661aSToomas Soome 	/*
7034a5d661aSToomas Soome 	 * Open all the disks we can find and see if we can reconstruct
704b406476aSToomas Soome 	 * ZFS pools from them.
7054a5d661aSToomas Soome 	 */
7064a5d661aSToomas Soome 	for (unit = 0; unit < MAXBDDEV; unit++) {
7074a5d661aSToomas Soome 		if (bd_unit2bios(unit) == -1)
7084a5d661aSToomas Soome 			break;
709b406476aSToomas Soome 
7104a5d661aSToomas Soome 		sprintf(devname, "disk%d:", unit);
711b406476aSToomas Soome 		/* If this is not boot disk, use generic probe. */
712b406476aSToomas Soome 		if (unit != boot_unit)
7134a5d661aSToomas Soome 			zfs_probe_dev(devname, NULL);
714b406476aSToomas Soome 		else
715b406476aSToomas Soome 			probe_disk(devname);
7164a5d661aSToomas Soome 	}
7174a5d661aSToomas Soome }
718*9ab3b382SToomas Soome 
719*9ab3b382SToomas Soome uint64_t
720*9ab3b382SToomas Soome ldi_get_size(void *priv)
721*9ab3b382SToomas Soome {
722*9ab3b382SToomas Soome 	int fd = (uintptr_t) priv;
723*9ab3b382SToomas Soome 	uint64_t size;
724*9ab3b382SToomas Soome 
725*9ab3b382SToomas Soome 	ioctl(fd, DIOCGMEDIASIZE, &size);
726*9ab3b382SToomas Soome 	return (size);
727*9ab3b382SToomas Soome }
728