commit a333a38064c5fcd13ecdd88bddb4a395881c979f Author: Andriy Gapon Date: Wed Sep 21 12:35:41 2011 +0300 btxldr: future-proof argument passing Place the arguments at a fixed offset of 0x800 withing the argument area (of size 0x1000). Allow variable size extended arguments first of which should be a size of the extended arguments (including the size parameter). diff --git a/sys/boot/i386/btx/btxldr/btxldr.S b/sys/boot/i386/btx/btxldr/btxldr.S index d4176eb..984f99a 100644 --- a/sys/boot/i386/btx/btxldr/btxldr.S +++ b/sys/boot/i386/btx/btxldr/btxldr.S @@ -15,8 +15,9 @@ * $FreeBSD$ */ -#define RBX_MUTE 0x10 /* -m */ -#define OPT_SET(opt) (1 << (opt)) +#define RBX_MUTE 0x10 /* -m */ +#define OPT_SET(opt) (1 << (opt)) +#define KARGS_FLAGS_EXTARG 0x8 /* * Prototype BTX loader program, written in a couple of hours. The @@ -31,6 +32,10 @@ .set MEM_TBL,0x5000 # BTX page tables .set MEM_ENTRY,0x9010 # BTX entry point .set MEM_DATA,start+0x1000 # Data segment + + .set BI_SIZE,0x48 # struct bootinfo fixed size + .set ARGOFF,0x800 # args offset within args area + /* * Segment selectors. */ @@ -97,11 +102,11 @@ start.1: movl (%ebx),%eax # Get argument and loop start.1 # Till done call putstr # End message #endif - movl $0x48,%ecx # Allocate space subl %ecx,%ebp # for bootinfo movl 0x18(%esp,1),%esi # Source: bootinfo cmpl $0x0, %esi # If the bootinfo pointer je start_null_bi # is null, don't copy it + movl $BI_SIZE,%ecx # Allocate space movl %ebp,%edi # Destination rep # Copy movsb # it @@ -112,8 +117,12 @@ start.1: movl (%ebx),%eax # Get argument and call hexout # relocation call putstr # message #endif -start_null_bi: movl $0x18,%ecx # Allocate space - subl %ecx,%ebp # for arguments + movl %edi,%ebp # Restore base pointer +start_null_bi: movl $0x18,%ecx # Fixed size of arguments + testl $KARGS_FLAGS_EXTARG,0xc(%esp) # Check for extra data + jz start_fixed # Skip if the flag is not set + addl 0x1c(%esp),%ecx # Add size of variable args +start_fixed: subl $ARGOFF,%ebp # Place args at fixed offset leal 0x4(%esp,1),%esi # Source movl %ebp,%edi # Destination rep # Copy diff --git a/sys/boot/i386/btx/lib/btxcsu.s b/sys/boot/i386/btx/lib/btxcsu.s index 6a00659..905465c 100644 --- a/sys/boot/i386/btx/lib/btxcsu.s +++ b/sys/boot/i386/btx/lib/btxcsu.s @@ -26,7 +26,8 @@ # # Constants. # - .set ARGADJ,0xfa0 # Argument adjustment + .set ARGADJ,0x800 # Argument adjustment + # 0x1000 (in btx.S) - ARGOFF (in btxldr.S) # # Client entry point. # commit cbe367b473a069a41e85c6b951eeff147798943a Author: Andriy Gapon Date: Mon Apr 30 11:09:43 2012 +0300 i386 boot: move kargs flags defintions to a shared header diff --git a/sys/boot/i386/common/common.h b/sys/boot/i386/common/common.h new file mode 100644 index 0000000..5948f1c --- /dev/null +++ b/sys/boot/i386/common/common.h @@ -0,0 +1,26 @@ +/*- + * Copyright (c) 2012 Andriy Gapon + * 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. + * + * $FreeBSD$ + */ + +#ifndef _BOOT_I386_COMMON_H_ +#define _BOOT_I386_COMMON_H_ + +#define KARGS_FLAGS_CD 0x1 +#define KARGS_FLAGS_PXE 0x2 +#define KARGS_FLAGS_ZFS 0x4 +#define KARGS_FLAGS_EXTARG 0x8 /* variably sized extended argument */ + +#endif /* !_BOOT_I386_COMMON_H_ */ diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index 75d5dbc..2b5c544 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -39,13 +39,10 @@ __FBSDID("$FreeBSD$"); #include #include "bootstrap.h" +#include "common/common.h" #include "libi386/libi386.h" #include "btxv86.h" -#define KARGS_FLAGS_CD 0x1 -#define KARGS_FLAGS_PXE 0x2 -#define KARGS_FLAGS_ZFS 0x4 - /* Arguments passed in from the boot1/boot2 loader */ static struct { diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index a6be3ee..3ba0b30 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -41,9 +41,7 @@ __FBSDID("$FreeBSD$"); #include "drv.h" #include "util.h" #include "cons.h" - -/* Hint to loader that we came from ZFS */ -#define KARGS_FLAGS_ZFS 0x4 +#include "common.h" #define PATH_DOTCONFIG "/boot.config" #define PATH_CONFIG "/boot/config" commit 796f1d8e4e9d397a7c7a1576051835e4e511e2d2 Author: Andriy Gapon Date: Mon Apr 30 11:10:48 2012 +0300 i386 boot: consolidate MAXBDDEV definition diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c index 5d20cba..ff4056b 100644 --- a/sys/boot/i386/libi386/biosdisk.c +++ b/sys/boot/i386/libi386/biosdisk.c @@ -55,7 +55,6 @@ __FBSDID("$FreeBSD$"); #define BIOS_NUMDRIVES 0x475 #define BIOSDISK_SECSIZE 512 #define BUFSIZE (1 * BIOSDISK_SECSIZE) -#define MAXBDDEV MAXDEV #define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ #define WDMAJOR 0 /* major numbers for devices we frontend for */ diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h index b8842ba..0a4621b 100644 --- a/sys/boot/i386/libi386/libi386.h +++ b/sys/boot/i386/libi386/libi386.h @@ -58,7 +58,8 @@ int i386_setcurrdev(struct env_var *ev, int flags, const void *value); extern struct devdesc currdev; /* our current device */ -#define MAXDEV 31 /* maximum number of distinct devices */ +#define MAXDEV 31 /* maximum number of distinct devices */ +#define MAXBDDEV MAXDEV /* exported devices XXX rename? */ extern struct devsw bioscd; diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index 3ba0b30..026c832 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -61,8 +61,6 @@ __FBSDID("$FreeBSD$"); #define TYPE_MAXHARD TYPE_DA #define TYPE_FD 2 -#define MAXBDDEV 31 - extern uint32_t _end; #ifdef GPT diff --git a/sys/boot/pc98/libpc98/biosdisk.c b/sys/boot/pc98/libpc98/biosdisk.c index c2e7d66..dc98145 100644 --- a/sys/boot/pc98/libpc98/biosdisk.c +++ b/sys/boot/pc98/libpc98/biosdisk.c @@ -52,7 +52,6 @@ __FBSDID("$FreeBSD$"); #define BIOS_NUMDRIVES 0x475 #define BIOSDISK_SECSIZE 512 #define BUFSIZE (1 * BIOSDISK_SECSIZE) -#define MAXBDDEV MAXDEV #define DT_ATAPI 0x10 /* disk type for ATAPI floppies */ #define WDMAJOR 0 /* major numbers for devices we frontend for */ commit ae5a9c60ce8f31604411732f571d3b56fd566276 Author: Andriy Gapon Date: Wed Apr 28 23:45:58 2010 +0300 zfsboot/zfsloader: support accessing filesystems within a pool In zfs loader zfs device name format now is "zfs:pool/fs", fully qualified file path is "zfs:pool/fs:/path/to/file" loader allows accessing files from various pools and filesystems as well as changing currdev to a different pool/filesystem. zfsboot accepts kernel/loader name in a format pool:fs:path/to/file or, as before, pool:path/to/file; in the latter case a default filesystem is used (pool root or bootfs). zfsboot passes guids of the selected pool and dataset to zfsloader to be used as its defaults. zfs support should be architecture independent and is provided in a separate library, but architectures wishing to use this zfs support still have to provide some glue code and their devdesc should be compatible with zfs_devdesc. arch_zfs_probe method is used to discover all disk devices that may be part of ZFS pool(s). libi386 unconditionally includes zfs support, but some zfs-specific functions are stubbed out as weak symbols. The strong definitions are provided in libzfsboot. This change mean that the size of i386_devspec becomes larger to match zfs_devspec. TODO: - clear up pool root filesystem vs pool bootfs filesystem distinction - set vfs.root.mountfrom based on currdev (for zfs) Mid-future TODO: - UI for selecting alternative boot environment Distant future TODO: - support accessing snapshots, using a snapshot as readonly root diff --git a/sys/boot/common/bootstrap.h b/sys/boot/common/bootstrap.h index 7f011c7..79f1e3a 100644 --- a/sys/boot/common/bootstrap.h +++ b/sys/boot/common/bootstrap.h @@ -317,6 +317,9 @@ struct arch_switch #else void (*arch_loadseg)(void *eh, void *ph, uint64_t delta); #endif + + /* Probe ZFS pool(s), if needed. */ + void (*arch_zfs_probe)(void); }; extern struct arch_switch archsw; diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile index 94a20c8..a3a9e18 100644 --- a/sys/boot/i386/libi386/Makefile +++ b/sys/boot/i386/libi386/Makefile @@ -9,6 +9,7 @@ SRCS= biosacpi.c bioscd.c biosdisk.c biosmem.c biospnp.c \ elf64_freebsd.c \ i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ smbios.c time.c vidconsole.c amd64_tramp.S spinconsole.c +SRCS+= ../../zfs/devicename_stubs.c # Enable PXE TFTP or NFS support, not both. .if defined(LOADER_TFTP_SUPPORT) diff --git a/sys/boot/i386/libi386/devicename.c b/sys/boot/i386/libi386/devicename.c index d6f3ac1..5edea4b 100644 --- a/sys/boot/i386/libi386/devicename.c +++ b/sys/boot/i386/libi386/devicename.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include #include "bootstrap.h" #include "libi386.h" +#include "../zfs/libzfs.h" static int i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path); @@ -171,7 +172,6 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) case DEVT_CD: case DEVT_NET: - case DEVT_ZFS: unit = 0; if (*np && (*np != ':')) { @@ -192,7 +192,9 @@ i386_parsedev(struct i386_devdesc **dev, const char *devspec, const char **path) if (path != NULL) *path = (*cp == 0) ? cp : cp + 1; break; - + case DEVT_ZFS: + err = zfs_parsedev((struct zfs_devdesc *)idev, np, path); + break; default: err = EINVAL; goto fail; @@ -247,9 +249,10 @@ i386_fmtdev(void *vdev) break; case DEVT_NET: - case DEVT_ZFS: sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); break; + case DEVT_ZFS: + return(zfs_fmtdev(vdev)); } return(buf); } diff --git a/sys/boot/i386/libi386/libi386.h b/sys/boot/i386/libi386/libi386.h index 0a4621b..3edeb92 100644 --- a/sys/boot/i386/libi386/libi386.h +++ b/sys/boot/i386/libi386/libi386.h @@ -30,7 +30,8 @@ /* * i386 fully-qualified device descriptor. * Note, this must match the 'struct devdesc' declaration - * in bootstrap.h. + * in bootstrap.h and also with struct zfs_devdesc for zfs + * support. */ struct i386_devdesc { @@ -49,6 +50,12 @@ struct i386_devdesc { void *data; } bioscd; + struct + { + void *data; + uint64_t pool_guid; + uint64_t root_guid; + } zfs; } d_kind; }; diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c index bd14f47..09d6a4e 100644 --- a/sys/boot/i386/loader/conf.c +++ b/sys/boot/i386/loader/conf.c @@ -30,6 +30,9 @@ __FBSDID("$FreeBSD$"); #include #include #include "libi386/libi386.h" +#if defined(LOADER_ZFS_SUPPORT) +#include "../zfs/libzfs.h" +#endif /* * We could use linker sets for some or all of these, but @@ -50,10 +53,6 @@ __FBSDID("$FreeBSD$"); extern struct devsw fwohci; #endif -#if defined(LOADER_ZFS_SUPPORT) -extern struct devsw zfs_dev; -#endif - /* Exported for libstand */ struct devsw *devsw[] = { &bioscd, @@ -70,10 +69,6 @@ struct devsw *devsw[] = { NULL }; -#if defined(LOADER_ZFS_SUPPORT) -extern struct fs_ops zfs_fsops; -#endif - struct fs_ops *file_system[] = { &ufs_fsops, &ext2fs_fsops, diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index 2b5c544..40bebac 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -43,20 +43,30 @@ __FBSDID("$FreeBSD$"); #include "libi386/libi386.h" #include "btxv86.h" +#ifdef LOADER_ZFS_SUPPORT +#include "../zfs/libzfs.h" +#endif + /* Arguments passed in from the boot1/boot2 loader */ static struct { u_int32_t howto; u_int32_t bootdev; u_int32_t bootflags; +#ifdef LOADER_ZFS_SUPPORT union { +#endif struct { u_int32_t pxeinfo; u_int32_t res2; }; +#ifdef LOADER_ZFS_SUPPORT uint64_t zfspool; }; +#endif u_int32_t bootinfo; + u_int32_t extarg; /* size of extended data, actually */ + /* ... */ } *kargs; static u_int32_t initial_howto; @@ -69,6 +79,9 @@ static void extract_currdev(void); static int isa_inb(int port); static void isa_outb(int port, int value); void exit(int code); +#ifdef LOADER_ZFS_SUPPORT +static void i386_zfs_probe(void); +#endif /* from vers.c */ extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[]; @@ -160,6 +173,9 @@ main(void) archsw.arch_readin = i386_readin; archsw.arch_isainb = isa_inb; archsw.arch_isaoutb = isa_outb; +#ifdef LOADER_ZFS_SUPPORT + archsw.arch_zfs_probe = i386_zfs_probe; +#endif /* * March through the device switch probing for things. @@ -203,8 +219,9 @@ main(void) static void extract_currdev(void) { - struct i386_devdesc new_currdev; - int biosdev = -1; + struct i386_devdesc new_currdev; + struct zfs_boot_args *zargs; + int biosdev = -1; /* Assume we are booting from a BIOS disk by default */ new_currdev.d_dev = &biosdisk; @@ -225,6 +242,24 @@ extract_currdev(void) new_currdev.d_kind.biosdisk.partition = 0; biosdev = -1; } +#ifdef LOADER_ZFS_SUPPORT + } else if ((kargs->bootflags & KARGS_FLAGS_ZFS) != 0) { + zargs = NULL; + /* check for new style extended argument */ + if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) + zargs = (struct zfs_boot_args *)&kargs->extarg; + + if (zargs != NULL && zargs->size >= sizeof(*zargs)) { + /* sufficient data is provided */ + new_currdev.d_kind.zfs.pool_guid = zargs->pool; + new_currdev.d_kind.zfs.root_guid = zargs->root; + } else { + /* old style zfsboot block */ + new_currdev.d_kind.zfs.pool_guid = kargs->zfspool; + new_currdev.d_kind.zfs.root_guid = 0; + } + new_currdev.d_dev = &zfs_dev; +#endif } else if ((initial_bootdev & B_MAGICMASK) != B_DEVMAGIC) { /* The passed-in boot device is bad */ new_currdev.d_kind.biosdisk.slice = -1; @@ -245,7 +280,7 @@ extract_currdev(void) biosdev = 0x80 + B_UNIT(initial_bootdev); /* assume harddisk */ } new_currdev.d_type = new_currdev.d_dev->dv_type; - + /* * If we are booting off of a BIOS disk and we didn't succeed in determining * which one we booted off of, just use disk0: as a reasonable default. @@ -256,33 +291,11 @@ extract_currdev(void) "Guessed BIOS device 0x%x not found by probes, defaulting to disk0:\n", biosdev); new_currdev.d_unit = 0; } + env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev), i386_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE, i386_fmtdev(&new_currdev), env_noset, env_nounset); - -#ifdef LOADER_ZFS_SUPPORT - /* - * If we were started from a ZFS-aware boot2, we can work out - * which ZFS pool we are booting from. - */ - if (kargs->bootflags & KARGS_FLAGS_ZFS) { - /* - * Dig out the pool guid and convert it to a 'unit number' - */ - uint64_t guid; - int unit; - char devname[32]; - extern int zfs_guid_to_unit(uint64_t); - - guid = kargs->zfspool; - unit = zfs_guid_to_unit(guid); - if (unit >= 0) { - sprintf(devname, "zfs%d", unit); - setenv("currdev", devname, 1); - } - } -#endif } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); @@ -349,3 +362,30 @@ isa_outb(int port, int value) } } +#ifdef LOADER_ZFS_SUPPORT +static void +i386_zfs_probe(void) +{ + char devname[32]; + int unit, slice; + + /* + * Open all the disks we can find and see if we can reconstruct + * ZFS pools from them. Bogusly assumes that the disks are named + * diskN, diskNpM or diskNsM. + */ + for (unit = 0; unit < MAXBDDEV; unit++) { + sprintf(devname, "disk%d:", unit); + if (zfs_probe_dev(devname, NULL) == ENXIO) + continue; + for (slice = 1; slice <= 128; slice++) { + sprintf(devname, "disk%dp%d:", unit, slice); + zfs_probe_dev(devname, NULL); + } + for (slice = 1; slice <= 4; slice++) { + sprintf(devname, "disk%ds%d:", unit, slice); + zfs_probe_dev(devname, NULL); + } + } +} +#endif diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c index 026c832..67f5075 100644 --- a/sys/boot/i386/zfsboot/zfsboot.c +++ b/sys/boot/i386/zfsboot/zfsboot.c @@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$"); #include "cons.h" #include "common.h" +#include "libzfs.h" + #define PATH_DOTCONFIG "/boot.config" #define PATH_CONFIG "/boot/config" #define PATH_BOOT3 "/boot/zfsloader" @@ -91,9 +93,12 @@ static const unsigned char dev_maj[NDEV] = {30, 4, 2}; static char cmd[512]; static char cmddup[512]; static char kname[1024]; +static char rootname[256]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; static uint32_t bootdev; +static struct zfs_boot_args zfsargs; +static struct zfsmount zfsmount; vm_offset_t high_heap_base; uint32_t bios_basemem, bios_extmem, high_heap_size; @@ -170,7 +175,7 @@ zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, size_t /* * Current ZFS pool */ -spa_t *spa; +static spa_t *spa; /* * A wrapper for dskread that doesn't have to worry about whether the @@ -209,7 +214,7 @@ static int xfsread(const dnode_phys_t *dnode, off_t *offp, void *buf, size_t nbyte) { if ((size_t)zfs_read(spa, dnode, offp, buf, nbyte) != nbyte) { - printf("Invalid %s\n", "format"); + printf("Invalid format\n"); return -1; } return 0; @@ -529,10 +534,12 @@ main(void) } } - zfs_mount_pool(spa); - - if (zfs_lookup(spa, PATH_CONFIG, &dn) == 0 || - zfs_lookup(spa, PATH_DOTCONFIG, &dn) == 0) { + if (zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0) { + printf("%s: failed to mount default pool %s\n", + BOOTPROG, spa->spa_name); + autoboot = 0; + } else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 || + zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) { off = 0; zfs_read(spa, &dn, &off, cmd, sizeof(cmd)); } @@ -567,11 +574,17 @@ main(void) /* Present the user with the boot2 prompt. */ for (;;) { - if (!autoboot || !OPT_CHECK(RBX_QUIET)) - printf("\nFreeBSD/x86 boot\n" - "Default: %s:%s\n" - "boot: ", - spa->spa_name, kname); + if (!autoboot || !OPT_CHECK(RBX_QUIET)) { + printf("\nFreeBSD/x86 boot\n"); + if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0) + printf("Default: %s:<0x%llx>:%s\n" + "boot: ", + spa->spa_name, zfsmount.rootobj, kname); + else + printf("Default: %s:%s:%s\n" + "boot: ", + spa->spa_name, rootname, kname); + } if (ioctrl & IO_SERIAL) sio_flush(); if (!autoboot || keyhit(5)) @@ -607,7 +620,8 @@ load(void) uint32_t addr, x; int fmt, i, j; - if (zfs_lookup(spa, kname, &dn)) { + if (zfs_lookup(&zfsmount, kname, &dn)) { + printf("\nCan't find %s\n", kname); return; } off = 0; @@ -681,12 +695,16 @@ load(void) } bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); + zfsargs.size = sizeof(zfsargs); + zfsargs.pool = zfsmount.spa->spa_guid; + zfsargs.root = zfsmount.rootobj; __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), bootdev, - KARGS_FLAGS_ZFS, + KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG, (uint32_t) spa->spa_guid, (uint32_t) (spa->spa_guid >> 32), - VTOP(&bootinfo)); + VTOP(&bootinfo), + zfsargs); } static int @@ -738,7 +756,7 @@ parse(void) } if (c == '?') { dnode_phys_t dn; - if (zfs_lookup(spa, arg, &dn) == 0) { + if (zfs_lookup(&zfsmount, arg, &dn) == 0) { zap_list(spa, &dn); } return -1; @@ -760,17 +778,32 @@ parse(void) q = (char *) strchr(arg, ':'); if (q) { spa_t *newspa; + uint64_t newroot; *q++ = 0; newspa = spa_find_by_name(arg); if (newspa) { + arg = q; spa = newspa; - zfs_mount_pool(spa); + newroot = 0; + q = (char *) strchr(arg, ':'); + if (q) { + *q++ = 0; + if (zfs_lookup_dataset(spa, arg, &newroot)) { + printf("\nCan't find dataset %s in ZFS pool %s\n", + arg, spa->spa_name); + return -1; + } + arg = q; + } + if (zfs_mount(spa, newroot, &zfsmount)) { + printf("\nCan't mount ZFS dataset\n"); + return -1; + } } else { printf("\nCan't find ZFS pool %s\n", arg); return -1; } - arg = q; } if ((i = ep - arg)) { if ((size_t)i >= sizeof(kname)) diff --git a/sys/boot/zfs/devicename_stubs.c b/sys/boot/zfs/devicename_stubs.c new file mode 100644 index 0000000..41bf907 --- /dev/null +++ b/sys/boot/zfs/devicename_stubs.c @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2012 Andriy Gapon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include "libzfs.h" + +__attribute__((weak)) +int +zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) +{ + return (EINVAL); +} + +__attribute__((weak)) +char * +zfs_fmtdev(void *vdev) +{ + static char buf[128]; + + return (buf); +} diff --git a/sys/boot/zfs/libzfs.h b/sys/boot/zfs/libzfs.h new file mode 100644 index 0000000..5c114db --- /dev/null +++ b/sys/boot/zfs/libzfs.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2012 Andriy Gapon + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _BOOT_LIBZFS_H_ +#define _BOOT_LIBZFS_H_ + +#define ZFS_MAXNAMELEN 256 + +/* + * ZFS fully-qualified device descriptor. + * Note, this must match the 'struct devdesc' declaration in bootstrap.h. + * Arch-specific device descriptors should be binary compatible with this + * structure if they are to support ZFS. + */ +struct zfs_devdesc +{ + struct devsw *d_dev; + int d_type; + int d_unit; + void *d_opendata; + uint64_t pool_guid; + uint64_t root_guid; +}; + +struct zfs_boot_args +{ + uint32_t size; + uint32_t reserved; + uint64_t pool; + uint64_t root; +}; + +int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, + const char **path); +char *zfs_fmtdev(void *vdev); +int zfs_probe_dev(const char *devname, uint64_t *pool_guid); + +extern struct devsw zfs_dev; +extern struct fs_ops zfs_fsops; + +#endif /*_BOOT_LIBZFS_H_*/ diff --git a/sys/boot/zfs/zfs.c b/sys/boot/zfs/zfs.c index 97f924f..3bc1678 100644 --- a/sys/boot/zfs/zfs.c +++ b/sys/boot/zfs/zfs.c @@ -43,9 +43,9 @@ __FBSDID("$FreeBSD$"); #include #include -#include "zfsimpl.c" +#include "libzfs.h" -#define MAXBDDEV 31 +#include "zfsimpl.c" static int zfs_open(const char *path, struct open_file *f); static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid); @@ -85,35 +85,20 @@ struct file { static int zfs_open(const char *upath, struct open_file *f) { - spa_t *spa = (spa_t *) f->f_devdata; + struct zfsmount *mount = (struct zfsmount *)f->f_devdata; struct file *fp; int rc; if (f->f_dev != &zfs_dev) return (EINVAL); - rc = zfs_mount_pool(spa); - if (rc) - return (rc); - /* allocate file system specific data structure */ fp = malloc(sizeof(struct file)); bzero(fp, sizeof(struct file)); f->f_fsdata = (void *)fp; - if (spa->spa_root_objset.os_type != DMU_OST_ZFS) { - printf("Unexpected object set type %llu\n", - spa->spa_root_objset.os_type); - rc = EIO; - goto out; - } - - rc = zfs_lookup(spa, upath, &fp->f_dnode); - if (rc) - goto out; - + rc = zfs_lookup(mount, upath, &fp->f_dnode); fp->f_seekp = 0; -out: if (rc) { f->f_fsdata = NULL; free(fp); @@ -142,7 +127,7 @@ zfs_close(struct open_file *f) static int zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { - spa_t *spa = (spa_t *) f->f_devdata; + spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; struct stat sb; size_t n; @@ -216,7 +201,7 @@ zfs_seek(struct open_file *f, off_t offset, int where) static int zfs_stat(struct open_file *f, struct stat *sb) { - spa_t *spa = (spa_t *) f->f_devdata; + spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); @@ -225,7 +210,7 @@ zfs_stat(struct open_file *f, struct stat *sb) static int zfs_readdir(struct open_file *f, struct dirent *d) { - spa_t *spa = (spa_t *) f->f_devdata; + spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; mzap_ent_phys_t mze; struct stat sb; @@ -381,68 +366,33 @@ vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size) } } -/* - * Convert a pool guid to a 'unit number' suitable for use with zfs_dev_open. - */ -int -zfs_guid_to_unit(uint64_t guid) +static int +zfs_dev_init(void) { - spa_t *spa; - int unit; - - unit = 0; - STAILQ_FOREACH(spa, &zfs_pools, spa_link) { - if (spa->spa_guid == guid) - return unit; - unit++; - } - return (-1); + zfs_init(); + if (archsw.arch_zfs_probe == NULL) + return (ENXIO); + archsw.arch_zfs_probe(); + return (0); } -#if defined(__amd64__) || defined(__i386__) -static int -zfs_dev_init(void) +int +zfs_probe_dev(const char *devname, uint64_t *pool_guid) { - char devname[512]; - int unit, slice; + spa_t *spa; int fd; + int ret; - /* - * Open all the disks we can find and see if we can reconstruct - * ZFS pools from them. Bogusly assumes that the disks are named - * diskN, diskNpM or diskNsM. - */ - zfs_init(); - for (unit = 0; unit < MAXBDDEV; unit++) { - sprintf(devname, "disk%d:", unit); - fd = open(devname, O_RDONLY); - if (fd == -1) - continue; - - /* - * If we find a vdev, the zfs code will eat the fd, otherwise - * we close it. - */ - if (vdev_probe(vdev_read, (void*) (uintptr_t) fd, 0)) - close(fd); - - for (slice = 1; slice <= 128; slice++) { - sprintf(devname, "disk%dp%d:", unit, slice); - fd = open(devname, O_RDONLY); - if (fd == -1) { - sprintf(devname, "disk%ds%d:", unit, slice); - fd = open(devname, O_RDONLY); - if (fd == -1) - continue; - } - if (vdev_probe(vdev_read, (void*) (uintptr_t) fd, 0)) - close(fd); - } - } - + fd = open(devname, O_RDONLY); + if (fd == -1) + return (ENXIO); + ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); + if (ret != 0) + close(fd); + else if (pool_guid != NULL) + *pool_guid = spa->spa_guid; return (0); } -#endif /* * Print information about ZFS pools @@ -452,17 +402,14 @@ zfs_dev_print(int verbose) { spa_t *spa; char line[80]; - int unit; if (verbose) { spa_all_status(); return; } - unit = 0; STAILQ_FOREACH(spa, &zfs_pools, spa_link) { - sprintf(line, " zfs%d: %s\n", unit, spa->spa_name); + sprintf(line, " zfs:%s\n", spa->spa_name); pager_output(line); - unit++; } } @@ -473,33 +420,42 @@ static int zfs_dev_open(struct open_file *f, ...) { va_list args; - struct devdesc *dev; - int unit, i; + struct zfs_devdesc *dev; + struct zfsmount *mount; spa_t *spa; + int rv; va_start(args, f); - dev = va_arg(args, struct devdesc*); + dev = va_arg(args, struct zfs_devdesc *); va_end(args); - /* - * We mostly ignore the stuff that devopen sends us. For now, - * use the unit to find a pool - later we will override the - * devname parsing so that we can name a pool and a fs within - * the pool. - */ - unit = dev->d_unit; - - i = 0; - STAILQ_FOREACH(spa, &zfs_pools, spa_link) { - if (i == unit) - break; - i++; - } - if (!spa) { + spa = spa_find_by_guid(dev->pool_guid); + if (!spa) return (ENXIO); + rv = zfs_spa_init(spa); + if (rv != 0) + return (rv); +#if 0 + rootobj = 0; + if (dev->rootname[0] != '\0') { + rv = zfs_lookup_dataset(spa, dev->rootname, &rootobj); + if (rv != 0) + return (rv); } - - f->f_devdata = spa; +#endif + mount = malloc(sizeof(*mount)); + rv = zfs_mount(spa, dev->root_guid, mount); + if (rv != 0) { + free(mount); + return (rv); + } + if (mount->objset.os_type != DMU_OST_ZFS) { + printf("Unexpected object set type %llu\n", + mount->objset.os_type); + free(mount); + return (EIO); + } + f->f_devdata = mount; free(dev); return (0); } @@ -508,6 +464,7 @@ static int zfs_dev_close(struct open_file *f) { + free(f->f_devdata); f->f_devdata = NULL; return (0); } @@ -530,3 +487,92 @@ struct devsw zfs_dev = { .dv_print = zfs_dev_print, .dv_cleanup = NULL }; + +int +zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) +{ + static char rootname[ZFS_MAXNAMELEN]; + static char poolname[ZFS_MAXNAMELEN]; + spa_t *spa; + const char *end; + const char *np; + const char *sep; + int rv; + + np = devspec; + if (*np != ':') + return (EINVAL); + np++; + end = strchr(np, ':'); + if (end == NULL) + return (EINVAL); + sep = strchr(np, '/'); + if (sep == NULL || sep >= end) + sep = end; + memcpy(poolname, np, sep - np); + poolname[sep - np] = '\0'; + if (sep < end) { + sep++; + memcpy(rootname, sep, end - sep); + rootname[end - sep] = '\0'; + } + else + rootname[0] = '\0'; + + spa = spa_find_by_name(poolname); + if (!spa) + return (ENXIO); + rv = zfs_spa_init(spa); + if (rv != 0) + return (rv); + dev->pool_guid = spa->spa_guid; + if (rootname[0] != '\0') { + rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); + if (rv != 0) + return (rv); + } else + dev->root_guid = 0; + if (path != NULL) + *path = (*end == '\0') ? end : end + 1; + dev->d_dev = &zfs_dev; + dev->d_type = zfs_dev.dv_type; + return (0); +} + +char * +zfs_fmtdev(void *vdev) +{ + static char rootname[ZFS_MAXNAMELEN]; + static char buf[2 * ZFS_MAXNAMELEN + 8]; + struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; + spa_t *spa; + + buf[0] = '\0'; + if (dev->d_type != DEVT_ZFS) + return (buf); + + spa = spa_find_by_guid(dev->pool_guid); + if (spa == NULL) { + printf("ZFS: can't find pool by guid\n"); + return (buf); + } + if (zfs_spa_init(spa) != 0) { + printf("ZFS: can't init pool\n"); + return (buf); + } + if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { + printf("ZFS: can't find root filesystem\n"); + return (buf); + } + if (zfs_rlookup(spa, dev->root_guid, rootname)) { + printf("ZFS: can't find filesystem by guid\n"); + return (buf); + } + + if (rootname[0] == '\0') + sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name); + else + sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name, + rootname); + return (buf); +} diff --git a/sys/boot/zfs/zfsimpl.c b/sys/boot/zfs/zfsimpl.c index 3419564..f976a1a 100644 --- a/sys/boot/zfs/zfsimpl.c +++ b/sys/boot/zfs/zfsimpl.c @@ -36,6 +36,13 @@ __FBSDID("$FreeBSD$"); #include "zfsimpl.h" #include "zfssubr.c" + +struct zfsmount { + spa_t *spa; + objset_phys_t objset; + uint64_t rootobj; +}; + /* * List of all vdevs, chained through v_alllink. */ @@ -626,8 +633,6 @@ spa_find_by_guid(uint64_t guid) return (0); } -#ifdef BOOT2 - static spa_t * spa_find_by_name(const char *name) { @@ -640,8 +645,6 @@ spa_find_by_name(const char *name) return (0); } -#endif - static spa_t * spa_create(uint64_t guid) { @@ -1452,6 +1455,259 @@ objset_get_dnode(spa_t *spa, const objset_phys_t *os, uint64_t objnum, dnode_phy dnode, sizeof(dnode_phys_t)); } +static int +mzap_rlookup(spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) +{ + const mzap_phys_t *mz; + const mzap_ent_phys_t *mze; + size_t size; + int chunks, i; + + /* + * Microzap objects use exactly one block. Read the whole + * thing. + */ + size = dnode->dn_datablkszsec * 512; + + mz = (const mzap_phys_t *) zap_scratch; + chunks = size / MZAP_ENT_LEN - 1; + + for (i = 0; i < chunks; i++) { + mze = &mz->mz_chunk[i]; + if (value == mze->mze_value) { + strcpy(name, mze->mze_name); + return (0); + } + } + + return (ENOENT); +} + +static void +fzap_name_copy(const zap_leaf_t *zl, const zap_leaf_chunk_t *zc, char *name) +{ + size_t namelen; + const zap_leaf_chunk_t *nc; + char *p; + + namelen = zc->l_entry.le_name_length; + + nc = &ZAP_LEAF_CHUNK(zl, zc->l_entry.le_name_chunk); + p = name; + while (namelen > 0) { + size_t len; + len = namelen; + if (len > ZAP_LEAF_ARRAY_BYTES) + len = ZAP_LEAF_ARRAY_BYTES; + memcpy(p, nc->l_array.la_array, len); + p += len; + namelen -= len; + nc = &ZAP_LEAF_CHUNK(zl, nc->l_array.la_next); + } + + *p = '\0'; +} + +static int +fzap_rlookup(spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) +{ + int bsize = dnode->dn_datablkszsec << SPA_MINBLOCKSHIFT; + zap_phys_t zh = *(zap_phys_t *) zap_scratch; + fat_zap_t z; + uint64_t *ptrtbl; + uint64_t hash; + int rc; + + if (zh.zap_magic != ZAP_MAGIC) + return (EIO); + + z.zap_block_shift = ilog2(bsize); + z.zap_phys = (zap_phys_t *) zap_scratch; + + /* + * Figure out where the pointer table is and read it in if necessary. + */ + if (zh.zap_ptrtbl.zt_blk) { + rc = dnode_read(spa, dnode, zh.zap_ptrtbl.zt_blk * bsize, + zap_scratch, bsize); + if (rc) + return (rc); + ptrtbl = (uint64_t *) zap_scratch; + } else { + ptrtbl = &ZAP_EMBEDDED_PTRTBL_ENT(&z, 0); + } + + hash = zap_hash(zh.zap_salt, name); + + zap_leaf_t zl; + zl.l_bs = z.zap_block_shift; + + off_t off = ptrtbl[hash >> (64 - zh.zap_ptrtbl.zt_shift)] << zl.l_bs; + zap_leaf_chunk_t *zc; + + rc = dnode_read(spa, dnode, off, zap_scratch, bsize); + if (rc) + return (rc); + + zl.l_phys = (zap_leaf_phys_t *) zap_scratch; + + /* + * Make sure this chunk matches our hash. + */ + if (zl.l_phys->l_hdr.lh_prefix_len > 0 + && zl.l_phys->l_hdr.lh_prefix + != hash >> (64 - zl.l_phys->l_hdr.lh_prefix_len)) + return (ENOENT); + + /* + * Hash within the chunk to find our entry. + */ + int shift = (64 - ZAP_LEAF_HASH_SHIFT(&zl) - zl.l_phys->l_hdr.lh_prefix_len); + int h = (hash >> shift) & ((1 << ZAP_LEAF_HASH_SHIFT(&zl)) - 1); + h = zl.l_phys->l_hash[h]; + if (h == 0xffff) + return (ENOENT); + zc = &ZAP_LEAF_CHUNK(&zl, h); + while (zc->l_entry.le_hash != hash) { + if (zc->l_entry.le_next == 0xffff) { + zc = 0; + break; + } + zc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_next); + } + if (fzap_leaf_value(&zl, zc) == value) { + fzap_name_copy(&zl, zc, name); + return (0); + } + + return (ENOENT); +} + +static int +zap_rlookup(spa_t *spa, const dnode_phys_t *dnode, char *name, uint64_t value) +{ + int rc; + uint64_t zap_type; + size_t size = dnode->dn_datablkszsec * 512; + + rc = dnode_read(spa, dnode, 0, zap_scratch, size); + if (rc) + return (rc); + + zap_type = *(uint64_t *) zap_scratch; + if (zap_type == ZBT_MICRO) + return mzap_rlookup(spa, dnode, name, value); + else + return fzap_rlookup(spa, dnode, name, value); +} + +static int +zfs_rlookup(spa_t *spa, uint64_t objnum, char *result) +{ + char name[256]; + char component[256]; + uint64_t dir_obj, parent_obj, child_dir_zapobj; + dnode_phys_t child_dir_zap, dataset, dir, parent; + dsl_dir_phys_t *dd; + dsl_dataset_phys_t *ds; + char *p; + int len; + + p = &name[sizeof(name) - 1]; + *p = '\0'; + + if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) { + printf("ZFS: can't find dataset %llu\n", objnum); + return (EIO); + } + ds = (dsl_dataset_phys_t *)&dataset.dn_bonus; + dir_obj = ds->ds_dir_obj; + + for (;;) { + if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0) + return (EIO); + dd = (dsl_dir_phys_t *)&dir.dn_bonus; + + /* Actual loop condition. */ + parent_obj = dd->dd_parent_obj; + if (parent_obj == 0) + break; + + if (objset_get_dnode(spa, &spa->spa_mos, parent_obj, &parent) != 0) + return (EIO); + dd = (dsl_dir_phys_t *)&parent.dn_bonus; + child_dir_zapobj = dd->dd_child_dir_zapobj; + if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) + return (EIO); + if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0) + return (EIO); + + len = strlen(component); + p -= len; + memcpy(p, component, len); + --p; + *p = '/'; + + /* Actual loop iteration. */ + dir_obj = parent_obj; + } + + if (*p != '\0') + ++p; + strcpy(result, p); + + return (0); +} + +static int +zfs_lookup_dataset(spa_t *spa, const char *name, uint64_t *objnum) +{ + char element[256]; + uint64_t dir_obj, child_dir_zapobj; + dnode_phys_t child_dir_zap, dir; + dsl_dir_phys_t *dd; + const char *p, *q; + + if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT, &dir)) + return (EIO); + if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, &dir_obj)) + return (EIO); + + p = name; + for (;;) { + if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) + return (EIO); + dd = (dsl_dir_phys_t *)&dir.dn_bonus; + + while (*p == '/') + p++; + /* Actual loop condition #1. */ + if (*p == '\0') + break; + + q = strchr(p, '/'); + if (q) { + memcpy(element, p, q - p); + element[q - p] = '\0'; + p = q + 1; + } else { + strcpy(element, p); + p += strlen(p); + } + + child_dir_zapobj = dd->dd_child_dir_zapobj; + if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj, &child_dir_zap) != 0) + return (EIO); + + /* Actual loop condition #2. */ + if (zap_lookup(spa, &child_dir_zap, element, &dir_obj) != 0) + return (ENOENT); + } + + *objnum = dd->dd_head_dataset_obj; + return (0); +} + /* * Find the object set given the object number of its dataset object * and return its details in *objset @@ -1481,11 +1737,13 @@ zfs_mount_dataset(spa_t *spa, uint64_t objnum, objset_phys_t *objset) * dataset if there is none and return its details in *objset */ static int -zfs_mount_root(spa_t *spa, objset_phys_t *objset) +zfs_get_root(spa_t *spa, uint64_t *objid) { dnode_phys_t dir, propdir; uint64_t props, bootfs, root; + *objid = 0; + /* * Start with the MOS directory object. */ @@ -1501,8 +1759,10 @@ zfs_mount_root(spa_t *spa, objset_phys_t *objset) && objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0 && zap_lookup(spa, &propdir, "bootfs", &bootfs) == 0 && bootfs != 0) - return zfs_mount_dataset(spa, bootfs, objset); - + { + *objid = bootfs; + return (0); + } /* * Lookup the root dataset directory */ @@ -1517,29 +1777,45 @@ zfs_mount_root(spa_t *spa, objset_phys_t *objset) * to find the dataset object and from that the object set itself. */ dsl_dir_phys_t *dd = (dsl_dir_phys_t *) &dir.dn_bonus; - return zfs_mount_dataset(spa, dd->dd_head_dataset_obj, objset); + *objid = dd->dd_head_dataset_obj; + return (0); } static int -zfs_mount_pool(spa_t *spa) +zfs_mount(spa_t *spa, uint64_t rootobj, struct zfsmount *mount) { + mount->spa = spa; + /* - * Find the MOS and work our way in from there. + * Find the root object set if not explicitly provided */ - if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) { - printf("ZFS: can't read MOS\n"); + if (rootobj == 0 && zfs_get_root(spa, &rootobj)) { + printf("ZFS: can't find root filesystem\n"); return (EIO); } - /* - * Find the root object set - */ - if (zfs_mount_root(spa, &spa->spa_root_objset)) { - printf("Can't find root filesystem - giving up\n"); + if (zfs_mount_dataset(spa, rootobj, &mount->objset)) { + printf("ZFS: can't open root filesystem\n"); return (EIO); } + mount->rootobj = rootobj; + + return (0); +} + +static int +zfs_spa_init(spa_t *spa) +{ + + if (spa->spa_inited) + return (0); + if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) { + printf("ZFS: can't read MOS of pool %s\n", spa->spa_name); + return (EIO); + } + spa->spa_inited = 1; return (0); } @@ -1599,10 +1875,11 @@ zfs_dnode_stat(spa_t *spa, dnode_phys_t *dn, struct stat *sb) * Lookup a file and return its dnode. */ static int -zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) +zfs_lookup(const struct zfsmount *mount, const char *upath, dnode_phys_t *dnode) { int rc; uint64_t objnum, rootnum, parentnum; + spa_t *spa; dnode_phys_t dn; const char *p, *q; char element[256]; @@ -1610,16 +1887,17 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) int symlinks_followed = 0; struct stat sb; - if (spa->spa_root_objset.os_type != DMU_OST_ZFS) { + spa = mount->spa; + if (mount->objset.os_type != DMU_OST_ZFS) { printf("ZFS: unexpected object set type %llu\n", - spa->spa_root_objset.os_type); + mount->objset.os_type); return (EIO); } /* * Get the root directory dnode. */ - rc = objset_get_dnode(spa, &spa->spa_root_objset, MASTER_NODE_OBJ, &dn); + rc = objset_get_dnode(spa, &mount->objset, MASTER_NODE_OBJ, &dn); if (rc) return (rc); @@ -1627,7 +1905,7 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) if (rc) return (rc); - rc = objset_get_dnode(spa, &spa->spa_root_objset, rootnum, &dn); + rc = objset_get_dnode(spa, &mount->objset, rootnum, &dn); if (rc) return (rc); @@ -1660,7 +1938,7 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) return (rc); objnum = ZFS_DIRENT_OBJ(objnum); - rc = objset_get_dnode(spa, &spa->spa_root_objset, objnum, &dn); + rc = objset_get_dnode(spa, &mount->objset, objnum, &dn); if (rc) return (rc); @@ -1702,7 +1980,7 @@ zfs_lookup(spa_t *spa, const char *upath, dnode_phys_t *dnode) objnum = rootnum; else objnum = parentnum; - objset_get_dnode(spa, &spa->spa_root_objset, objnum, &dn); + objset_get_dnode(spa, &mount->objset, objnum, &dn); } } diff --git a/sys/cddl/boot/zfs/zfsimpl.h b/sys/cddl/boot/zfs/zfsimpl.h index 9f24cc0..66379a3 100644 --- a/sys/cddl/boot/zfs/zfsimpl.h +++ b/sys/cddl/boot/zfs/zfsimpl.h @@ -1327,5 +1327,5 @@ typedef struct spa { struct uberblock spa_uberblock; /* best uberblock so far */ vdev_list_t spa_vdevs; /* list of all toplevel vdevs */ objset_phys_t spa_mos; /* MOS for this pool */ - objset_phys_t spa_root_objset; /* current mounted ZPL objset */ + int spa_inited; /* initialized */ } spa_t; diff --git a/tools/tools/zfsboottest/zfsboottest.c b/tools/tools/zfsboottest/zfsboottest.c index 4b29f94..3058046 100644 --- a/tools/tools/zfsboottest/zfsboottest.c +++ b/tools/tools/zfsboottest/zfsboottest.c @@ -89,7 +89,11 @@ main(int argc, char** argv) char buf[512], hash[33]; MD5_CTX ctx; struct stat sb; + struct zfsmount zfsmnt; dnode_phys_t dn; +#if 0 + uint64_t rootobj; +#endif spa_t *spa; off_t off; ssize_t n; @@ -138,14 +142,28 @@ main(int argc, char** argv) exit(1); } - if (zfs_mount_pool(spa)) { - fprintf(stderr, "can't mount pool\n"); + if (zfs_spa_init(spa)) { + fprintf(stderr, "can't init pool\n"); exit(1); } +#if 0 + if (zfs_get_root(spa, &rootobj)) { + fprintf(stderr, "can't get root\n"); + exit(1); + } + + if (zfs_mount(spa, rootobj, &zfsmnt)) { +#else + if (zfs_mount(spa, 0, &zfsmnt)) { + fprintf(stderr, "can't mount\n"); + exit(1); + } +#endif + printf("\n"); for (++i, failures = 0; i < argc; i++) { - if (zfs_lookup(spa, argv[i], &dn)) { + if (zfs_lookup(&zfsmnt, argv[i], &dn)) { fprintf(stderr, "%s: can't lookup\n", argv[i]); failures++; continue; commit 1f94d9a86bf9f0ed738a1c8640be28881255c50b Author: Andriy Gapon Date: Thu May 3 17:45:40 2012 +0300 zfs boot: update sparc64 support Note: this commit should be squashed into the previous one before going to the main tree. diff --git a/sys/boot/Makefile.sparc64 b/sys/boot/Makefile.sparc64 index 5723629..1064a26 100644 --- a/sys/boot/Makefile.sparc64 +++ b/sys/boot/Makefile.sparc64 @@ -1,3 +1,4 @@ # $FreeBSD$ SUBDIR+= ofw +SUBDIR+= zfs diff --git a/sys/boot/ofw/libofw/Makefile b/sys/boot/ofw/libofw/Makefile index 945d650..e207eca 100644 --- a/sys/boot/ofw/libofw/Makefile +++ b/sys/boot/ofw/libofw/Makefile @@ -6,6 +6,7 @@ INTERNALLIB= SRCS= devicename.c elf_freebsd.c ofw_console.c ofw_copy.c ofw_disk.c \ ofw_memory.c ofw_module.c ofw_net.c ofw_reboot.c \ ofw_time.c openfirm.c +SRCS+= ../../zfs/devicename_stubs.c CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ diff --git a/sys/boot/ofw/libofw/devicename.c b/sys/boot/ofw/libofw/devicename.c index c2d9ef1..bbacb84 100644 --- a/sys/boot/ofw/libofw/devicename.c +++ b/sys/boot/ofw/libofw/devicename.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include "bootstrap.h" #include "libofw.h" +#include "../zfs/libzfs.h" static int ofw_parsedev(struct ofw_devdesc **, const char *, const char **); @@ -81,6 +82,7 @@ ofw_parsedev(struct ofw_devdesc **dev, const char *devspec, const char **path) char *ep; char name[256]; char type[64]; + int err; int len; int i; @@ -114,14 +116,11 @@ found: idev->d_dev = dv; idev->d_type = dv->dv_type; if (idev->d_type == DEVT_ZFS) { - idev->d_unit = 0; - p = name + strlen(dv->dv_name); - if (*p && (*p != ':')) { - idev->d_unit = strtol(p, &ep, 0); - if (ep == p) { - free(idev); - return (EUNIT); - } + p = devspec + strlen(dv->dv_name); + err = zfs_parsedev((struct zfs_devdesc *)idev, p, path); + if (err != 0) { + free(idev); + return (err); } } diff --git a/sys/boot/ofw/libofw/libofw.h b/sys/boot/ofw/libofw/libofw.h index 5f6d7ad..87e9095 100644 --- a/sys/boot/ofw/libofw/libofw.h +++ b/sys/boot/ofw/libofw/libofw.h @@ -33,7 +33,13 @@ struct ofw_devdesc { int d_type; int d_unit; ihandle_t d_handle; - char d_path[256]; + union { + char d_path[256]; + struct { + uint64_t pool_guid; + uint64_t root_guid; + }; + }; }; extern int ofw_getdev(void **vdev, const char *devspec, const char **path); diff --git a/sys/boot/sparc64/loader/Makefile b/sys/boot/sparc64/loader/Makefile index 27359b2..6ba06de 100644 --- a/sys/boot/sparc64/loader/Makefile +++ b/sys/boot/sparc64/loader/Makefile @@ -37,6 +37,9 @@ CFLAGS+= -DLOADER_CD9660_SUPPORT CFLAGS+= -DLOADER_ZFS_SUPPORT CFLAGS+= -I${.CURDIR}/../../zfs CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs +LIBZFS= ${.OBJDIR}/../../zfs/libzfsboot.a +.else +LIBZFS= .endif .if ${LOADER_GZIP_SUPPORT} == "yes" CFLAGS+= -DLOADER_GZIP_SUPPORT @@ -83,8 +86,8 @@ CFLAGS+= -I${.CURDIR}/../../ofw/libofw/ # where to get libstand from CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ -DPADD= ${LIBFICL} ${LIBOFW} ${LIBSTAND} -LDADD= ${LIBFICL} ${LIBOFW} -lstand +DPADD= ${LIBFICL} ${LIBZFS} ${LIBOFW} ${LIBSTAND} +LDADD= ${LIBFICL} ${LIBZFS} ${LIBOFW} -lstand vers.c: ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/../loader/version sh ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/../loader/version \ diff --git a/sys/boot/sparc64/loader/main.c b/sys/boot/sparc64/loader/main.c index dbbf08d..470cf68 100644 --- a/sys/boot/sparc64/loader/main.c +++ b/sys/boot/sparc64/loader/main.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #ifdef LOADER_ZFS_SUPPORT #include +#include "../zfs/libzfs.h" #endif #include @@ -80,6 +81,8 @@ __FBSDID("$FreeBSD$"); #define __CTASSERT(x, y) typedef char __assert ## y[(x) ? 1 : -1] #endif +#define MAXDEV 31 + extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[]; enum { @@ -146,11 +149,6 @@ static vm_offset_t heapva; static phandle_t root; -#ifdef LOADER_ZFS_SUPPORT -static int zfs_dev_init(void); -#include "zfs.c" -#endif - /* * Machine dependent structures that the machine independent * loader part uses. @@ -737,39 +735,20 @@ tlb_init_sun4u(void) } #ifdef LOADER_ZFS_SUPPORT - -static int -zfs_dev_init(void) +static void +sparc64_zfs_probe(void) { struct vtoc8 vtoc; - char devname[512]; - spa_t *spa; - vdev_t *vdev; + struct zfs_devdesc zfs_currdev; + char devname[32]; uint64_t guid; int fd, part, unit; - zfs_init(); - - guid = 0; /* Get the GUID of the ZFS pool on the boot device. */ - fd = open(getenv("currdev"), O_RDONLY); - if (fd != -1) { - if (vdev_probe(vdev_read, (void *)(uintptr_t) fd, &spa) == 0) - guid = spa->spa_guid; - close(fd); - } - - /* Clean up the environment to let ZFS work. */ - while ((vdev = STAILQ_FIRST(&zfs_vdevs)) != NULL) { - STAILQ_REMOVE_HEAD(&zfs_vdevs, v_alllink); - free(vdev); - } - while ((spa = STAILQ_FIRST(&zfs_pools)) != NULL) { - STAILQ_REMOVE_HEAD(&zfs_pools, spa_link); - free(spa); - } + guid = 0; + zfs_probe_dev(getenv("currdev"), &guid); - for (unit = 0; unit < MAXBDDEV; unit++) { + for (unit = 0; unit < MAXDEV; unit++) { /* Find freebsd-zfs slices in the VTOC. */ sprintf(devname, "disk%d:", unit); fd = open(devname, O_RDONLY); @@ -787,29 +766,23 @@ zfs_dev_init(void) VTOC_TAG_FREEBSD_ZFS) continue; sprintf(devname, "disk%d:%c", unit, part + 'a'); - fd = open(devname, O_RDONLY); - if (fd == -1) + if (zfs_probe_dev(devname, NULL) == ENXIO) break; - - if (vdev_probe(vdev_read, (void*)(uintptr_t) fd, 0)) - close(fd); } } if (guid != 0) { - unit = zfs_guid_to_unit(guid); - if (unit >= 0) { - /* Update the environment for ZFS. */ - sprintf(devname, "zfs%d", unit); - env_setenv("currdev", EV_VOLATILE, devname, - ofw_setcurrdev, env_nounset); - env_setenv("loaddev", EV_VOLATILE, devname, - env_noset, env_nounset); - } + zfs_currdev.pool_guid = guid; + zfs_currdev.root_guid = 0; + zfs_currdev.d_dev = &zfs_dev; + zfs_currdev.d_type = zfs_currdev.d_dev->dv_type; + /* Update the environment for ZFS. */ + env_setenv("currdev", EV_VOLATILE, zfs_fmtdev(&zfs_currdev), + ofw_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, zfs_fmtdev(&zfs_currdev), + env_noset, env_nounset); } - return (0); } - #endif /* LOADER_ZFS_SUPPORT */ int @@ -829,6 +802,9 @@ main(int (*openfirm)(void *)) archsw.arch_copyout = ofw_copyout; archsw.arch_readin = sparc64_readin; archsw.arch_autoload = sparc64_autoload; +#ifdef LOADER_ZFS_SUPPORT + archsw.arch_zfs_probe = sparc64_zfs_probe; +#endif if (init_heap() == (vm_offset_t)-1) OF_exit(); commit 8c61d9faf4c4ec87a6d9896ca906071c4778bbf4 Author: Andriy Gapon Date: Thu Jun 10 10:18:19 2010 +0300 zfs boot: try to set vfs.root.mountfrom from currdev as a fallback diff --git a/sys/boot/common/boot.c b/sys/boot/common/boot.c index c6ab681..8ea06dd 100644 --- a/sys/boot/common/boot.c +++ b/sys/boot/common/boot.c @@ -311,12 +311,12 @@ getrootmount(char *rootdev) if (getenv("vfs.root.mountfrom") != NULL) return(0); + error = 1; sprintf(lbuf, "%s/etc/fstab", rootdev); if ((fd = open(lbuf, O_RDONLY)) < 0) - return(1); + goto notfound; /* loop reading lines from /etc/fstab What was that about sscanf again? */ - error = 1; while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) { if ((lbuf[0] == 0) || (lbuf[0] == '#')) continue; @@ -377,6 +377,20 @@ getrootmount(char *rootdev) break; } close(fd); + +notfound: + if (error) { + const char *currdev; + + currdev = getenv("currdev"); + if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) { + cp = strdup(currdev); + cp[strlen(cp) - 1] = '\0'; + setenv("vfs.root.mountfrom", cp, 0); + error = 0; + } + } + return(error); }