fc7cf7241f
Some compilers are complaining about missing prototype. PR: 251150 Reported by: markiyan.kushnir@gmail.com
718 lines
16 KiB
C
718 lines
16 KiB
C
/*-
|
|
* Copyright (c) 1998 Robert Nordier
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are freely
|
|
* permitted provided that the above copyright notice and this
|
|
* paragraph and the following disclaimer are duplicated in all
|
|
* such forms.
|
|
*
|
|
* This software is provided "AS IS" and without any express or
|
|
* implied warranties, including, without limitation, the implied
|
|
* warranties of merchantability and fitness for a particular
|
|
* purpose.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <stand.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/diskmbr.h>
|
|
#ifdef GPT
|
|
#include <sys/gpt.h>
|
|
#endif
|
|
#include <sys/reboot.h>
|
|
#include <sys/queue.h>
|
|
#ifdef LOADER_ZFS_SUPPORT
|
|
#include <sys/zfs_bootenv.h>
|
|
#endif
|
|
|
|
#include <machine/bootinfo.h>
|
|
#include <machine/elf.h>
|
|
#include <machine/pc/bios.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
|
|
#include <a.out.h>
|
|
#include "bootstrap.h"
|
|
#include "libi386.h"
|
|
#include <btxv86.h>
|
|
|
|
#include "lib.h"
|
|
#include "rbx.h"
|
|
#include "cons.h"
|
|
#include "bootargs.h"
|
|
#include "disk.h"
|
|
#include "part.h"
|
|
#include "paths.h"
|
|
|
|
#include "libzfs.h"
|
|
|
|
#define ARGS 0x900
|
|
#define NOPT 14
|
|
#define NDEV 3
|
|
|
|
#define BIOS_NUMDRIVES 0x475
|
|
#define DRV_HARD 0x80
|
|
#define DRV_MASK 0x7f
|
|
|
|
#define TYPE_AD 0
|
|
#define TYPE_DA 1
|
|
#define TYPE_MAXHARD TYPE_DA
|
|
#define TYPE_FD 2
|
|
|
|
extern uint32_t _end;
|
|
|
|
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */
|
|
static const unsigned char flags[NOPT] = {
|
|
RBX_DUAL,
|
|
RBX_SERIAL,
|
|
RBX_ASKNAME,
|
|
RBX_CDROM,
|
|
RBX_CONFIG,
|
|
RBX_KDB,
|
|
RBX_GDB,
|
|
RBX_MUTE,
|
|
RBX_NOINTR,
|
|
RBX_PAUSE,
|
|
RBX_QUIET,
|
|
RBX_DFLTROOT,
|
|
RBX_SINGLE,
|
|
RBX_VERBOSE
|
|
};
|
|
uint32_t opts;
|
|
|
|
/*
|
|
* Paths to try loading before falling back to the boot2 prompt.
|
|
*
|
|
* /boot/zfsloader must be tried before /boot/loader in order to remain
|
|
* backward compatible with ZFS boot environments where /boot/loader exists
|
|
* but does not have ZFS support, which was the case before FreeBSD 12.
|
|
*
|
|
* If no loader is found, try to load a kernel directly instead.
|
|
*/
|
|
static const struct string {
|
|
const char *p;
|
|
size_t len;
|
|
} loadpath[] = {
|
|
{ PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) },
|
|
{ PATH_LOADER, sizeof(PATH_LOADER) },
|
|
{ PATH_KERNEL, sizeof(PATH_KERNEL) },
|
|
};
|
|
|
|
static const unsigned char dev_maj[NDEV] = {30, 4, 2};
|
|
|
|
static struct i386_devdesc *bdev;
|
|
static char cmd[512];
|
|
static char cmddup[512];
|
|
static char kname[1024];
|
|
static int comspeed = SIOSPD;
|
|
static struct bootinfo bootinfo;
|
|
static uint32_t bootdev;
|
|
static struct zfs_boot_args zfsargs;
|
|
#ifdef LOADER_GELI_SUPPORT
|
|
static struct geli_boot_args geliargs;
|
|
#endif
|
|
|
|
extern vm_offset_t high_heap_base;
|
|
extern uint32_t bios_basemem, bios_extmem, high_heap_size;
|
|
|
|
static char *heap_top;
|
|
static char *heap_bottom;
|
|
|
|
void exit(int);
|
|
static void i386_zfs_probe(void);
|
|
static void load(void);
|
|
static int parse_cmd(void);
|
|
|
|
#ifdef LOADER_GELI_SUPPORT
|
|
#include "geliboot.h"
|
|
static char gelipw[GELI_PW_MAXLEN];
|
|
#endif
|
|
|
|
struct arch_switch archsw; /* MI/MD interface boundary */
|
|
static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */
|
|
|
|
struct devsw *devsw[] = {
|
|
&bioshd,
|
|
#if defined(LOADER_ZFS_SUPPORT)
|
|
&zfs_dev,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
struct fs_ops *file_system[] = {
|
|
#if defined(LOADER_ZFS_SUPPORT)
|
|
&zfs_fsops,
|
|
#endif
|
|
#if defined(LOADER_UFS_SUPPORT)
|
|
&ufs_fsops,
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
caddr_t
|
|
ptov(uintptr_t x)
|
|
{
|
|
return (PTOV(x));
|
|
}
|
|
|
|
int main(void);
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
unsigned i;
|
|
int auto_boot, fd, nextboot = 0;
|
|
struct disk_devdesc devdesc;
|
|
|
|
bios_getmem();
|
|
|
|
if (high_heap_size > 0) {
|
|
heap_top = PTOV(high_heap_base + high_heap_size);
|
|
heap_bottom = PTOV(high_heap_base);
|
|
} else {
|
|
heap_bottom = (char *)
|
|
(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
|
|
heap_top = (char *)PTOV(bios_basemem);
|
|
}
|
|
setheap(heap_bottom, heap_top);
|
|
|
|
/*
|
|
* Initialise the block cache. Set the upper limit.
|
|
*/
|
|
bcache_init(32768, 512);
|
|
|
|
archsw.arch_autoload = NULL;
|
|
archsw.arch_getdev = i386_getdev;
|
|
archsw.arch_copyin = NULL;
|
|
archsw.arch_copyout = NULL;
|
|
archsw.arch_readin = NULL;
|
|
archsw.arch_isainb = NULL;
|
|
archsw.arch_isaoutb = NULL;
|
|
archsw.arch_zfs_probe = i386_zfs_probe;
|
|
|
|
bootinfo.bi_version = BOOTINFO_VERSION;
|
|
bootinfo.bi_size = sizeof(bootinfo);
|
|
bootinfo.bi_basemem = bios_basemem / 1024;
|
|
bootinfo.bi_extmem = bios_extmem / 1024;
|
|
bootinfo.bi_memsizes_valid++;
|
|
bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS);
|
|
|
|
/* Set up fall back device name. */
|
|
snprintf(boot_devname, sizeof (boot_devname), "disk%d:",
|
|
bd_bios2unit(bootinfo.bi_bios_dev));
|
|
|
|
for (i = 0; devsw[i] != NULL; i++)
|
|
if (devsw[i]->dv_init != NULL)
|
|
(devsw[i]->dv_init)();
|
|
|
|
disk_parsedev(&devdesc, boot_devname + 4, NULL);
|
|
|
|
bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc.d_slice + 1,
|
|
devdesc.dd.d_unit,
|
|
devdesc.d_partition >= 0 ? devdesc.d_partition : 0xff);
|
|
|
|
/*
|
|
* zfs_fmtdev() can be called only after dv_init
|
|
*/
|
|
if (bdev != NULL && bdev->dd.d_dev->dv_type == DEVT_ZFS) {
|
|
/* set up proper device name string for ZFS */
|
|
strncpy(boot_devname, zfs_fmtdev(bdev), sizeof (boot_devname));
|
|
if (zfs_get_bootonce(bdev, OS_BOOTONCE, cmd,
|
|
sizeof(cmd)) == 0) {
|
|
nvlist_t *benv;
|
|
|
|
nextboot = 1;
|
|
memcpy(cmddup, cmd, sizeof(cmd));
|
|
if (parse_cmd()) {
|
|
if (!OPT_CHECK(RBX_QUIET))
|
|
printf("failed to parse bootonce "
|
|
"command\n");
|
|
exit(0);
|
|
}
|
|
if (!OPT_CHECK(RBX_QUIET))
|
|
printf("zfs bootonce: %s\n", cmddup);
|
|
|
|
if (zfs_get_bootenv(bdev, &benv) == 0) {
|
|
nvlist_add_string(benv, OS_BOOTONCE_USED,
|
|
cmddup);
|
|
zfs_set_bootenv(bdev, benv);
|
|
}
|
|
/* Do not process this command twice */
|
|
*cmd = 0;
|
|
}
|
|
}
|
|
|
|
/* now make sure we have bdev on all cases */
|
|
free(bdev);
|
|
i386_getdev((void **)&bdev, boot_devname, NULL);
|
|
|
|
env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev,
|
|
env_nounset);
|
|
|
|
/* Process configuration file */
|
|
auto_boot = 1;
|
|
|
|
fd = open(PATH_CONFIG, O_RDONLY);
|
|
if (fd == -1)
|
|
fd = open(PATH_DOTCONFIG, O_RDONLY);
|
|
|
|
if (fd != -1) {
|
|
ssize_t cmdlen;
|
|
|
|
if ((cmdlen = read(fd, cmd, sizeof(cmd))) > 0)
|
|
cmd[cmdlen] = '\0';
|
|
else
|
|
*cmd = '\0';
|
|
close(fd);
|
|
}
|
|
|
|
if (*cmd) {
|
|
/*
|
|
* Note that parse_cmd() is destructive to cmd[] and we also
|
|
* want to honor RBX_QUIET option that could be present in
|
|
* cmd[].
|
|
*/
|
|
memcpy(cmddup, cmd, sizeof(cmd));
|
|
if (parse_cmd())
|
|
auto_boot = 0;
|
|
if (!OPT_CHECK(RBX_QUIET))
|
|
printf("%s: %s\n", PATH_CONFIG, cmddup);
|
|
/* Do not process this command twice */
|
|
*cmd = 0;
|
|
}
|
|
|
|
/* Do not risk waiting at the prompt forever. */
|
|
if (nextboot && !auto_boot)
|
|
exit(0);
|
|
|
|
if (auto_boot && !*kname) {
|
|
/*
|
|
* Iterate through the list of loader and kernel paths,
|
|
* trying to load. If interrupted by a keypress, or in case of
|
|
* failure, drop the user to the boot2 prompt.
|
|
*/
|
|
for (i = 0; i < nitems(loadpath); i++) {
|
|
memcpy(kname, loadpath[i].p, loadpath[i].len);
|
|
if (keyhit(3))
|
|
break;
|
|
load();
|
|
}
|
|
}
|
|
|
|
/* Present the user with the boot2 prompt. */
|
|
|
|
for (;;) {
|
|
if (!auto_boot || !OPT_CHECK(RBX_QUIET)) {
|
|
printf("\nFreeBSD/x86 boot\n");
|
|
printf("Default: %s%s\nboot: ", boot_devname, kname);
|
|
}
|
|
if (ioctrl & IO_SERIAL)
|
|
sio_flush();
|
|
if (!auto_boot || keyhit(5))
|
|
getstr(cmd, sizeof(cmd));
|
|
else if (!auto_boot || !OPT_CHECK(RBX_QUIET))
|
|
putchar('\n');
|
|
auto_boot = 0;
|
|
if (parse_cmd())
|
|
putchar('\a');
|
|
else
|
|
load();
|
|
}
|
|
}
|
|
|
|
/* XXX - Needed for btxld to link the boot2 binary; do not remove. */
|
|
void
|
|
exit(int x)
|
|
{
|
|
__exit(x);
|
|
}
|
|
|
|
static void
|
|
load(void)
|
|
{
|
|
union {
|
|
struct exec ex;
|
|
Elf32_Ehdr eh;
|
|
} hdr;
|
|
static Elf32_Phdr ep[2];
|
|
static Elf32_Shdr es[2];
|
|
caddr_t p;
|
|
uint32_t addr, x;
|
|
int fd, fmt, i, j;
|
|
ssize_t size;
|
|
|
|
if ((fd = open(kname, O_RDONLY)) == -1) {
|
|
printf("\nCan't find %s\n", kname);
|
|
return;
|
|
}
|
|
|
|
size = sizeof(hdr);
|
|
if (read(fd, &hdr, sizeof (hdr)) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
if (N_GETMAGIC(hdr.ex) == ZMAGIC) {
|
|
fmt = 0;
|
|
} else if (IS_ELF(hdr.eh)) {
|
|
fmt = 1;
|
|
} else {
|
|
printf("Invalid %s\n", "format");
|
|
close(fd);
|
|
return;
|
|
}
|
|
if (fmt == 0) {
|
|
addr = hdr.ex.a_entry & 0xffffff;
|
|
p = PTOV(addr);
|
|
lseek(fd, PAGE_SIZE, SEEK_SET);
|
|
size = hdr.ex.a_text;
|
|
if (read(fd, p, hdr.ex.a_text) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
p += roundup2(hdr.ex.a_text, PAGE_SIZE);
|
|
size = hdr.ex.a_data;
|
|
if (read(fd, p, hdr.ex.a_data) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE);
|
|
bootinfo.bi_symtab = VTOP(p);
|
|
memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms));
|
|
p += sizeof(hdr.ex.a_syms);
|
|
if (hdr.ex.a_syms) {
|
|
size = hdr.ex.a_syms;
|
|
if (read(fd, p, hdr.ex.a_syms) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
p += hdr.ex.a_syms;
|
|
size = sizeof (int);
|
|
if (read(fd, p, sizeof (int)) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
x = *(uint32_t *)p;
|
|
p += sizeof(int);
|
|
x -= sizeof(int);
|
|
size = x;
|
|
if (read(fd, p, x) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
p += x;
|
|
}
|
|
} else {
|
|
lseek(fd, hdr.eh.e_phoff, SEEK_SET);
|
|
for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) {
|
|
size = sizeof (ep[0]);
|
|
if (read(fd, ep + j, sizeof (ep[0])) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
if (ep[j].p_type == PT_LOAD)
|
|
j++;
|
|
}
|
|
for (i = 0; i < 2; i++) {
|
|
p = PTOV(ep[i].p_paddr & 0xffffff);
|
|
lseek(fd, ep[i].p_offset, SEEK_SET);
|
|
size = ep[i].p_filesz;
|
|
if (read(fd, p, ep[i].p_filesz) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
}
|
|
p += roundup2(ep[1].p_memsz, PAGE_SIZE);
|
|
bootinfo.bi_symtab = VTOP(p);
|
|
if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) {
|
|
lseek(fd, hdr.eh.e_shoff +
|
|
sizeof (es[0]) * (hdr.eh.e_shstrndx + 1),
|
|
SEEK_SET);
|
|
size = sizeof(es);
|
|
if (read(fd, &es, sizeof (es)) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
for (i = 0; i < 2; i++) {
|
|
memcpy(p, &es[i].sh_size,
|
|
sizeof(es[i].sh_size));
|
|
p += sizeof(es[i].sh_size);
|
|
lseek(fd, es[i].sh_offset, SEEK_SET);
|
|
size = es[i].sh_size;
|
|
if (read(fd, p, es[i].sh_size) != size) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
p += es[i].sh_size;
|
|
}
|
|
}
|
|
addr = hdr.eh.e_entry & 0xffffff;
|
|
}
|
|
close(fd);
|
|
|
|
bootinfo.bi_esymtab = VTOP(p);
|
|
bootinfo.bi_kernelname = VTOP(kname);
|
|
#ifdef LOADER_GELI_SUPPORT
|
|
explicit_bzero(gelipw, sizeof(gelipw));
|
|
#endif
|
|
|
|
if (bdev->dd.d_dev->dv_type == DEVT_ZFS) {
|
|
zfsargs.size = sizeof(zfsargs);
|
|
zfsargs.pool = bdev->d_kind.zfs.pool_guid;
|
|
zfsargs.root = bdev->d_kind.zfs.root_guid;
|
|
#ifdef LOADER_GELI_SUPPORT
|
|
export_geli_boot_data(&zfsargs.gelidata);
|
|
#endif
|
|
/*
|
|
* Note that the zfsargs struct is passed by value, not by
|
|
* pointer. Code in btxldr.S copies the values from the entry
|
|
* stack to a fixed location within loader(8) at startup due
|
|
* to the presence of KARGS_FLAGS_EXTARG.
|
|
*/
|
|
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
|
|
bootdev,
|
|
KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG,
|
|
(uint32_t)bdev->d_kind.zfs.pool_guid,
|
|
(uint32_t)(bdev->d_kind.zfs.pool_guid >> 32),
|
|
VTOP(&bootinfo),
|
|
zfsargs);
|
|
} else {
|
|
#ifdef LOADER_GELI_SUPPORT
|
|
geliargs.size = sizeof(geliargs);
|
|
export_geli_boot_data(&geliargs.gelidata);
|
|
#endif
|
|
|
|
/*
|
|
* Note that the geliargs struct is passed by value, not by
|
|
* pointer. Code in btxldr.S copies the values from the entry
|
|
* stack to a fixed location within loader(8) at startup due
|
|
* to the presence of the KARGS_FLAGS_EXTARG flag.
|
|
*/
|
|
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
|
|
bootdev,
|
|
#ifdef LOADER_GELI_SUPPORT
|
|
KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0,
|
|
VTOP(&bootinfo), geliargs
|
|
#else
|
|
0, 0, 0, VTOP(&bootinfo)
|
|
#endif
|
|
);
|
|
}
|
|
}
|
|
|
|
static int
|
|
mount_root(char *arg)
|
|
{
|
|
char *root;
|
|
struct i386_devdesc *ddesc;
|
|
uint8_t part;
|
|
|
|
if (asprintf(&root, "%s:", arg) < 0)
|
|
return (1);
|
|
|
|
if (i386_getdev((void **)&ddesc, root, NULL)) {
|
|
free(root);
|
|
return (1);
|
|
}
|
|
|
|
/* we should have new device descriptor, free old and replace it. */
|
|
free(bdev);
|
|
bdev = ddesc;
|
|
if (bdev->dd.d_dev->dv_type == DEVT_DISK) {
|
|
if (bdev->d_kind.biosdisk.partition == -1)
|
|
part = 0xff;
|
|
else
|
|
part = bdev->d_kind.biosdisk.partition;
|
|
bootdev = MAKEBOOTDEV(dev_maj[bdev->dd.d_dev->dv_type],
|
|
bdev->d_kind.biosdisk.slice + 1,
|
|
bdev->dd.d_unit, part);
|
|
bootinfo.bi_bios_dev = bd_unit2bios(bdev);
|
|
}
|
|
strncpy(boot_devname, root, sizeof (boot_devname));
|
|
setenv("currdev", root, 1);
|
|
free(root);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
fs_list(char *arg)
|
|
{
|
|
int fd;
|
|
struct dirent *d;
|
|
char line[80];
|
|
|
|
fd = open(arg, O_RDONLY);
|
|
if (fd < 0)
|
|
return;
|
|
pager_open();
|
|
while ((d = readdirfd(fd)) != NULL) {
|
|
sprintf(line, "%s\n", d->d_name);
|
|
if (pager_output(line))
|
|
break;
|
|
}
|
|
pager_close();
|
|
close(fd);
|
|
}
|
|
|
|
static int
|
|
parse_cmd(void)
|
|
{
|
|
char *arg = cmd;
|
|
char *ep, *p, *q;
|
|
const char *cp;
|
|
char line[80];
|
|
int c, i, j;
|
|
|
|
while ((c = *arg++)) {
|
|
if (c == ' ' || c == '\t' || c == '\n')
|
|
continue;
|
|
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++)
|
|
;
|
|
ep = p;
|
|
if (*p)
|
|
*p++ = 0;
|
|
if (c == '-') {
|
|
while ((c = *arg++)) {
|
|
if (c == 'P') {
|
|
if (*(uint8_t *)PTOV(0x496) & 0x10) {
|
|
cp = "yes";
|
|
} else {
|
|
opts |= OPT_SET(RBX_DUAL);
|
|
opts |= OPT_SET(RBX_SERIAL);
|
|
cp = "no";
|
|
}
|
|
printf("Keyboard: %s\n", cp);
|
|
continue;
|
|
} else if (c == 'S') {
|
|
j = 0;
|
|
while ((unsigned int)
|
|
(i = *arg++ - '0') <= 9)
|
|
j = j * 10 + i;
|
|
if (j > 0 && i == -'0') {
|
|
comspeed = j;
|
|
break;
|
|
}
|
|
/*
|
|
* Fall through to error below
|
|
* ('S' not in optstr[]).
|
|
*/
|
|
}
|
|
for (i = 0; c != optstr[i]; i++)
|
|
if (i == NOPT - 1)
|
|
return (-1);
|
|
opts ^= OPT_SET(flags[i]);
|
|
}
|
|
ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) :
|
|
OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD;
|
|
if (ioctrl & IO_SERIAL) {
|
|
if (sio_init(115200 / comspeed) != 0)
|
|
ioctrl &= ~IO_SERIAL;
|
|
}
|
|
} if (c == '?') {
|
|
printf("\n");
|
|
if (*arg == '\0')
|
|
arg = (char *)"/";
|
|
fs_list(arg);
|
|
zfs_list(arg);
|
|
return (-1);
|
|
} else {
|
|
char *ptr;
|
|
printf("\n");
|
|
arg--;
|
|
|
|
/*
|
|
* Report pool status if the comment is 'status'. Lets
|
|
* hope no-one wants to load /status as a kernel.
|
|
*/
|
|
if (strcmp(arg, "status") == 0) {
|
|
pager_open();
|
|
for (i = 0; devsw[i] != NULL; i++) {
|
|
if (devsw[i]->dv_print != NULL) {
|
|
if (devsw[i]->dv_print(1))
|
|
break;
|
|
} else {
|
|
snprintf(line, sizeof(line),
|
|
"%s: (unknown)\n",
|
|
devsw[i]->dv_name);
|
|
if (pager_output(line))
|
|
break;
|
|
}
|
|
}
|
|
pager_close();
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* If there is "zfs:" prefix simply ignore it.
|
|
*/
|
|
ptr = arg;
|
|
if (strncmp(ptr, "zfs:", 4) == 0)
|
|
ptr += 4;
|
|
|
|
/*
|
|
* If there is a colon, switch pools.
|
|
*/
|
|
q = strchr(ptr, ':');
|
|
if (q) {
|
|
*q++ = '\0';
|
|
if (mount_root(arg) != 0) {
|
|
return (-1);
|
|
}
|
|
arg = q;
|
|
}
|
|
if ((i = ep - arg)) {
|
|
if ((size_t)i >= sizeof(kname))
|
|
return (-1);
|
|
memcpy(kname, arg, i + 1);
|
|
}
|
|
}
|
|
arg = p;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Probe all disks to discover ZFS pools. The idea is to walk all possible
|
|
* disk devices, however, we also need to identify possible boot pool.
|
|
* For boot pool detection we have boot disk passed us from BIOS, recorded
|
|
* in bootinfo.bi_bios_dev.
|
|
*/
|
|
static void
|
|
i386_zfs_probe(void)
|
|
{
|
|
char devname[32];
|
|
int boot_unit;
|
|
struct i386_devdesc dev;
|
|
uint64_t pool_guid = 0;
|
|
|
|
dev.dd.d_dev = &bioshd;
|
|
/* Translate bios dev to our unit number. */
|
|
boot_unit = bd_bios2unit(bootinfo.bi_bios_dev);
|
|
|
|
/*
|
|
* Open all the disks we can find and see if we can reconstruct
|
|
* ZFS pools from them.
|
|
*/
|
|
for (dev.dd.d_unit = 0; bd_unit2bios(&dev) >= 0; dev.dd.d_unit++) {
|
|
snprintf(devname, sizeof (devname), "%s%d:", bioshd.dv_name,
|
|
dev.dd.d_unit);
|
|
/* If this is not boot disk, use generic probe. */
|
|
if (dev.dd.d_unit != boot_unit)
|
|
zfs_probe_dev(devname, NULL);
|
|
else
|
|
zfs_probe_dev(devname, &pool_guid);
|
|
|
|
if (pool_guid != 0 && bdev == NULL) {
|
|
bdev = malloc(sizeof (struct i386_devdesc));
|
|
bzero(bdev, sizeof (struct i386_devdesc));
|
|
bdev->dd.d_dev = &zfs_dev;
|
|
bdev->d_kind.zfs.pool_guid = pool_guid;
|
|
}
|
|
}
|
|
}
|