Changeset View
Changeset View
Standalone View
Standalone View
stand/i386/zfsboot/zfsboot.c
Show All 10 Lines | |||||
* implied warranties, including, without limitation, the implied | * implied warranties, including, without limitation, the implied | ||||
* warranties of merchantability and fitness for a particular | * warranties of merchantability and fitness for a particular | ||||
* purpose. | * purpose. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "stand.h" | #include <stand.h> | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/diskmbr.h> | #include <sys/diskmbr.h> | ||||
#ifdef GPT | #ifdef GPT | ||||
#include <sys/gpt.h> | #include <sys/gpt.h> | ||||
#endif | #endif | ||||
#include <sys/reboot.h> | #include <sys/reboot.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <machine/bootinfo.h> | #include <machine/bootinfo.h> | ||||
#include <machine/elf.h> | #include <machine/elf.h> | ||||
#include <machine/pc/bios.h> | #include <machine/pc/bios.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <stddef.h> | #include <stddef.h> | ||||
#include <a.out.h> | #include <a.out.h> | ||||
#include "bootstrap.h" | |||||
#include "libi386.h" | |||||
#include <btxv86.h> | #include <btxv86.h> | ||||
#include "lib.h" | #include "lib.h" | ||||
#include "rbx.h" | #include "rbx.h" | ||||
#include "drv.h" | |||||
#include "edd.h" | |||||
#include "cons.h" | #include "cons.h" | ||||
#include "bootargs.h" | #include "bootargs.h" | ||||
#include "disk.h" | |||||
#include "part.h" | |||||
#include "paths.h" | #include "paths.h" | ||||
#include "libzfs.h" | #include "libzfs.h" | ||||
#define ARGS 0x900 | #define ARGS 0x900 | ||||
#define NOPT 14 | #define NOPT 14 | ||||
#define NDEV 3 | #define NDEV 3 | ||||
#define BIOS_NUMDRIVES 0x475 | #define BIOS_NUMDRIVES 0x475 | ||||
#define DRV_HARD 0x80 | #define DRV_HARD 0x80 | ||||
#define DRV_MASK 0x7f | #define DRV_MASK 0x7f | ||||
#define TYPE_AD 0 | #define TYPE_AD 0 | ||||
#define TYPE_DA 1 | #define TYPE_DA 1 | ||||
#define TYPE_MAXHARD TYPE_DA | #define TYPE_MAXHARD TYPE_DA | ||||
#define TYPE_FD 2 | #define TYPE_FD 2 | ||||
#define DEV_GELIBOOT_BSIZE 4096 | |||||
extern uint32_t _end; | extern uint32_t _end; | ||||
#ifdef GPT | |||||
static const uuid_t freebsd_zfs_uuid = GPT_ENT_TYPE_FREEBSD_ZFS; | |||||
#endif | |||||
static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ | static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ | ||||
static const unsigned char flags[NOPT] = { | static const unsigned char flags[NOPT] = { | ||||
RBX_DUAL, | RBX_DUAL, | ||||
RBX_SERIAL, | RBX_SERIAL, | ||||
RBX_ASKNAME, | RBX_ASKNAME, | ||||
RBX_CDROM, | RBX_CDROM, | ||||
RBX_CONFIG, | RBX_CONFIG, | ||||
RBX_KDB, | RBX_KDB, | ||||
Show All 23 Lines | |||||
} loadpath[] = { | } loadpath[] = { | ||||
{ PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) }, | { PATH_LOADER_ZFS, sizeof(PATH_LOADER_ZFS) }, | ||||
{ PATH_LOADER, sizeof(PATH_LOADER) }, | { PATH_LOADER, sizeof(PATH_LOADER) }, | ||||
{ PATH_KERNEL, sizeof(PATH_KERNEL) }, | { PATH_KERNEL, sizeof(PATH_KERNEL) }, | ||||
}; | }; | ||||
static const unsigned char dev_maj[NDEV] = {30, 4, 2}; | static const unsigned char dev_maj[NDEV] = {30, 4, 2}; | ||||
static struct i386_devdesc *bdev; | |||||
static char cmd[512]; | static char cmd[512]; | ||||
static char cmddup[512]; | static char cmddup[512]; | ||||
static char kname[1024]; | static char kname[1024]; | ||||
static char rootname[256]; | |||||
static int comspeed = SIOSPD; | static int comspeed = SIOSPD; | ||||
static struct bootinfo bootinfo; | static struct bootinfo bootinfo; | ||||
static uint32_t bootdev; | static uint32_t bootdev; | ||||
static struct zfs_boot_args zfsargs; | static struct zfs_boot_args zfsargs; | ||||
#ifdef LOADER_GELI_SUPPORT | |||||
static struct geli_boot_args geliargs; | |||||
#endif | |||||
vm_offset_t high_heap_base; | extern vm_offset_t high_heap_base; | ||||
uint32_t bios_basemem, bios_extmem, high_heap_size; | extern uint32_t bios_basemem, bios_extmem, high_heap_size; | ||||
static struct bios_smap smap; | static char *heap_top; | ||||
static char *heap_bottom; | |||||
/* | |||||
* The minimum amount of memory to reserve in bios_extmem for the heap. | |||||
*/ | |||||
#define HEAP_MIN (64 * 1024 * 1024) | |||||
static char *heap_next; | |||||
static char *heap_end; | |||||
/* Buffers that must not span a 64k boundary. */ | |||||
#define READ_BUF_SIZE 8192 | |||||
struct dmadat { | |||||
char rdbuf[READ_BUF_SIZE]; /* for reading large things */ | |||||
char secbuf[READ_BUF_SIZE]; /* for MBR/disklabel */ | |||||
}; | |||||
static struct dmadat *dmadat; | |||||
void exit(int); | void exit(int); | ||||
void reboot(void); | static void i386_zfs_probe(void); | ||||
static void load(void); | static void load(void); | ||||
static int parse_cmd(void); | static int parse_cmd(void); | ||||
static void bios_getmem(void); | |||||
int main(void); | |||||
#ifdef LOADER_GELI_SUPPORT | #ifdef LOADER_GELI_SUPPORT | ||||
#include "geliboot.h" | #include "geliboot.h" | ||||
static char gelipw[GELI_PW_MAXLEN]; | static char gelipw[GELI_PW_MAXLEN]; | ||||
#endif | #endif | ||||
struct zfsdsk { | struct arch_switch archsw; /* MI/MD interface boundary */ | ||||
struct dsk dsk; | static char boot_devname[2 * ZFS_MAXNAMELEN + 8]; /* disk or pool:dataset */ | ||||
#ifdef LOADER_GELI_SUPPORT | |||||
struct geli_dev *gdev; | struct devsw *devsw[] = { | ||||
&bioshd, | |||||
#if defined(LOADER_ZFS_SUPPORT) | |||||
&zfs_dev, | |||||
#endif | #endif | ||||
NULL | |||||
}; | }; | ||||
#include "zfsimpl.c" | struct fs_ops *file_system[] = { | ||||
#if defined(LOADER_ZFS_SUPPORT) | |||||
/* | &zfs_fsops, | ||||
* Read from a dnode (which must be from a ZPL filesystem). | |||||
*/ | |||||
static int | |||||
zfs_read(spa_t *spa, const dnode_phys_t *dnode, off_t *offp, void *start, | |||||
size_t size) | |||||
{ | |||||
const znode_phys_t *zp = (const znode_phys_t *) dnode->dn_bonus; | |||||
size_t n; | |||||
int rc; | |||||
n = size; | |||||
if (*offp + n > zp->zp_size) | |||||
n = zp->zp_size - *offp; | |||||
rc = dnode_read(spa, dnode, *offp, start, n); | |||||
if (rc) | |||||
return (-1); | |||||
*offp += n; | |||||
return (n); | |||||
} | |||||
/* | |||||
* Current ZFS pool | |||||
*/ | |||||
static spa_t *spa; | |||||
static spa_t *primary_spa; | |||||
static vdev_t *primary_vdev; | |||||
/* | |||||
* A wrapper for dskread that doesn't have to worry about whether the | |||||
* buffer pointer crosses a 64k boundary. | |||||
*/ | |||||
static int | |||||
vdev_read(void *xvdev, void *priv, off_t off, void *buf, size_t bytes) | |||||
{ | |||||
char *p; | |||||
daddr_t lba, alignlba; | |||||
off_t diff; | |||||
unsigned int nb, alignnb; | |||||
struct zfsdsk *zdsk = priv; | |||||
if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) | |||||
return (-1); | |||||
p = buf; | |||||
lba = off / DEV_BSIZE; | |||||
lba += zdsk->dsk.start; | |||||
/* | |||||
* Align reads to 4k else 4k sector GELIs will not decrypt. | |||||
* Round LBA down to nearest multiple of DEV_GELIBOOT_BSIZE bytes. | |||||
*/ | |||||
alignlba = rounddown2(off, DEV_GELIBOOT_BSIZE) / DEV_BSIZE; | |||||
/* | |||||
* The read must be aligned to DEV_GELIBOOT_BSIZE bytes relative to the | |||||
* start of the GELI partition, not the start of the actual disk. | |||||
*/ | |||||
alignlba += zdsk->dsk.start; | |||||
diff = (lba - alignlba) * DEV_BSIZE; | |||||
while (bytes > 0) { | |||||
nb = bytes / DEV_BSIZE; | |||||
/* | |||||
* Ensure that the read size plus the leading offset does not | |||||
* exceed the size of the read buffer. | |||||
*/ | |||||
if (nb > (READ_BUF_SIZE - diff) / DEV_BSIZE) | |||||
nb = (READ_BUF_SIZE - diff) / DEV_BSIZE; | |||||
/* | |||||
* Round the number of blocks to read up to the nearest multiple | |||||
* of DEV_GELIBOOT_BSIZE. | |||||
*/ | |||||
alignnb = roundup2(nb * DEV_BSIZE + diff, DEV_GELIBOOT_BSIZE) | |||||
/ DEV_BSIZE; | |||||
if (zdsk->dsk.size > 0 && alignlba + alignnb > | |||||
zdsk->dsk.size + zdsk->dsk.start) { | |||||
printf("Shortening read at %lld from %d to %lld\n", | |||||
alignlba, alignnb, | |||||
(zdsk->dsk.size + zdsk->dsk.start) - alignlba); | |||||
alignnb = (zdsk->dsk.size + zdsk->dsk.start) - alignlba; | |||||
} | |||||
if (drvread(&zdsk->dsk, dmadat->rdbuf, alignlba, alignnb)) | |||||
return (-1); | |||||
#ifdef LOADER_GELI_SUPPORT | |||||
/* decrypt */ | |||||
if (zdsk->gdev != NULL) { | |||||
if (geli_read(zdsk->gdev, | |||||
((alignlba - zdsk->dsk.start) * DEV_BSIZE), | |||||
dmadat->rdbuf, alignnb * DEV_BSIZE)) | |||||
return (-1); | |||||
} | |||||
#endif | #endif | ||||
memcpy(p, dmadat->rdbuf + diff, nb * DEV_BSIZE); | &ufs_fsops, | ||||
p += nb * DEV_BSIZE; | NULL | ||||
lba += nb; | }; | ||||
alignlba += alignnb; | |||||
bytes -= nb * DEV_BSIZE; | |||||
/* Don't need the leading offset after the first block. */ | |||||
diff = 0; | |||||
} | |||||
return (0); | caddr_t | ||||
} | ptov(uintptr_t x) | ||||
/* Match the signature exactly due to signature madness */ | |||||
static int | |||||
vdev_read2(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) | |||||
{ | { | ||||
return (vdev_read(vdev, priv, off, buf, bytes)); | return (PTOV(x)); | ||||
} | } | ||||
static int | |||||
vdev_write(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) | |||||
{ | |||||
char *p; | |||||
daddr_t lba; | |||||
unsigned int nb; | |||||
struct zfsdsk *zdsk = priv; | |||||
if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) | |||||
return (-1); | |||||
p = buf; | |||||
lba = off / DEV_BSIZE; | |||||
lba += zdsk->dsk.start; | |||||
while (bytes > 0) { | |||||
nb = bytes / DEV_BSIZE; | |||||
if (nb > READ_BUF_SIZE / DEV_BSIZE) | |||||
nb = READ_BUF_SIZE / DEV_BSIZE; | |||||
memcpy(dmadat->rdbuf, p, nb * DEV_BSIZE); | |||||
if (drvwrite(&zdsk->dsk, dmadat->rdbuf, lba, nb)) | |||||
return (-1); | |||||
p += nb * DEV_BSIZE; | |||||
lba += nb; | |||||
bytes -= nb * DEV_BSIZE; | |||||
} | |||||
return (0); | |||||
} | |||||
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 format\n"); | |||||
return (-1); | |||||
} | |||||
return (0); | |||||
} | |||||
/* | |||||
* Read Pad2 (formerly "Boot Block Header") area of the first | |||||
* vdev label of the given vdev. | |||||
*/ | |||||
static int | |||||
vdev_read_pad2(vdev_t *vdev, char *buf, size_t size) | |||||
{ | |||||
blkptr_t bp; | |||||
char *tmp; | |||||
off_t off = offsetof(vdev_label_t, vl_pad2); | |||||
int rc; | |||||
if (size > VDEV_PAD_SIZE) | |||||
size = VDEV_PAD_SIZE; | |||||
tmp = malloc(VDEV_PAD_SIZE); | |||||
if (tmp == NULL) | |||||
return (ENOMEM); | |||||
BP_ZERO(&bp); | |||||
BP_SET_LSIZE(&bp, VDEV_PAD_SIZE); | |||||
BP_SET_PSIZE(&bp, VDEV_PAD_SIZE); | |||||
BP_SET_CHECKSUM(&bp, ZIO_CHECKSUM_LABEL); | |||||
BP_SET_COMPRESS(&bp, ZIO_COMPRESS_OFF); | |||||
DVA_SET_OFFSET(BP_IDENTITY(&bp), off); | |||||
rc = vdev_read_phys(vdev, &bp, tmp, off, 0); | |||||
if (rc == 0) | |||||
memcpy(buf, tmp, size); | |||||
free(tmp); | |||||
return (rc); | |||||
} | |||||
static int | |||||
vdev_clear_pad2(vdev_t *vdev) | |||||
{ | |||||
char *zeroes; | |||||
uint64_t *end; | |||||
off_t off = offsetof(vdev_label_t, vl_pad2); | |||||
int rc; | |||||
zeroes = malloc(VDEV_PAD_SIZE); | |||||
if (zeroes == NULL) | |||||
return (ENOMEM); | |||||
memset(zeroes, 0, VDEV_PAD_SIZE); | |||||
end = (uint64_t *)(zeroes + VDEV_PAD_SIZE); | |||||
/* ZIO_CHECKSUM_LABEL magic and pre-calcualted checksum for all zeros */ | |||||
end[-5] = 0x0210da7ab10c7a11; | |||||
end[-4] = 0x97f48f807f6e2a3f; | |||||
end[-3] = 0xaf909f1658aacefc; | |||||
end[-2] = 0xcbd1ea57ff6db48b; | |||||
end[-1] = 0x6ec692db0d465fab; | |||||
rc = vdev_write(vdev, vdev->v_read_priv, off, zeroes, VDEV_PAD_SIZE); | |||||
free(zeroes); | |||||
return (rc); | |||||
} | |||||
static void | |||||
bios_getmem(void) | |||||
{ | |||||
uint64_t size; | |||||
/* Parse system memory map */ | |||||
v86.ebx = 0; | |||||
do { | |||||
v86.ctl = V86_FLAGS; | |||||
v86.addr = 0x15; /* int 0x15 function 0xe820 */ | |||||
v86.eax = 0xe820; | |||||
v86.ecx = sizeof(struct bios_smap); | |||||
v86.edx = SMAP_SIG; | |||||
v86.es = VTOPSEG(&smap); | |||||
v86.edi = VTOPOFF(&smap); | |||||
v86int(); | |||||
if (V86_CY(v86.efl) || (v86.eax != SMAP_SIG)) | |||||
break; | |||||
/* look for a low-memory segment that's large enough */ | |||||
if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && | |||||
(smap.length >= (512 * 1024))) | |||||
bios_basemem = smap.length; | |||||
/* look for the first segment in 'extended' memory */ | |||||
if ((smap.type == SMAP_TYPE_MEMORY) && | |||||
(smap.base == 0x100000)) { | |||||
bios_extmem = smap.length; | |||||
} | |||||
/* | |||||
* Look for the largest segment in 'extended' memory beyond | |||||
* 1MB but below 4GB. | |||||
*/ | |||||
if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && | |||||
(smap.base < 0x100000000ull)) { | |||||
size = smap.length; | |||||
/* | |||||
* If this segment crosses the 4GB boundary, | |||||
* truncate it. | |||||
*/ | |||||
if (smap.base + size > 0x100000000ull) | |||||
size = 0x100000000ull - smap.base; | |||||
if (size > high_heap_size) { | |||||
high_heap_size = size; | |||||
high_heap_base = smap.base; | |||||
} | |||||
} | |||||
} while (v86.ebx != 0); | |||||
/* Fall back to the old compatibility function for base memory */ | |||||
if (bios_basemem == 0) { | |||||
v86.ctl = 0; | |||||
v86.addr = 0x12; /* int 0x12 */ | |||||
v86int(); | |||||
bios_basemem = (v86.eax & 0xffff) * 1024; | |||||
} | |||||
/* | |||||
* Fall back through several compatibility functions for extended | |||||
* memory. | |||||
*/ | |||||
if (bios_extmem == 0) { | |||||
v86.ctl = V86_FLAGS; | |||||
v86.addr = 0x15; /* int 0x15 function 0xe801 */ | |||||
v86.eax = 0xe801; | |||||
v86int(); | |||||
if (!V86_CY(v86.efl)) { | |||||
bios_extmem = ((v86.ecx & 0xffff) + | |||||
((v86.edx & 0xffff) * 64)) * 1024; | |||||
} | |||||
} | |||||
if (bios_extmem == 0) { | |||||
v86.ctl = 0; | |||||
v86.addr = 0x15; /* int 0x15 function 0x88 */ | |||||
v86.eax = 0x8800; | |||||
v86int(); | |||||
bios_extmem = (v86.eax & 0xffff) * 1024; | |||||
} | |||||
/* | |||||
* If we have extended memory and did not find a suitable heap | |||||
* region in the SMAP, use the last 3MB of 'extended' memory as a | |||||
* high heap candidate. | |||||
*/ | |||||
if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { | |||||
high_heap_size = HEAP_MIN; | |||||
high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; | |||||
} | |||||
} | |||||
/* | |||||
* Try to detect a device supported by the legacy int13 BIOS | |||||
*/ | |||||
static int | |||||
int13probe(int drive) | |||||
{ | |||||
v86.ctl = V86_FLAGS; | |||||
v86.addr = 0x13; | |||||
v86.eax = 0x800; | |||||
v86.edx = drive; | |||||
v86int(); | |||||
if (!V86_CY(v86.efl) && /* carry clear */ | |||||
((v86.edx & 0xff) != (drive & DRV_MASK))) { /* unit # OK */ | |||||
if ((v86.ecx & 0x3f) == 0) { /* absurd sector size */ | |||||
return (0); /* skip device */ | |||||
} | |||||
return (1); | |||||
} | |||||
return (0); | |||||
} | |||||
/* | |||||
* We call this when we find a ZFS vdev - ZFS consumes the dsk | |||||
* structure so we must make a new one. | |||||
*/ | |||||
static struct zfsdsk * | |||||
copy_dsk(struct zfsdsk *zdsk) | |||||
{ | |||||
struct zfsdsk *newdsk; | |||||
newdsk = malloc(sizeof(struct zfsdsk)); | |||||
*newdsk = *zdsk; | |||||
return (newdsk); | |||||
} | |||||
/* | |||||
* Get disk size from GPT. | |||||
*/ | |||||
static uint64_t | |||||
drvsize_gpt(struct dsk *dskp) | |||||
{ | |||||
#ifdef GPT | |||||
struct gpt_hdr hdr; | |||||
char *sec; | |||||
sec = dmadat->secbuf; | |||||
if (drvread(dskp, sec, 1, 1)) | |||||
return (0); | |||||
memcpy(&hdr, sec, sizeof(hdr)); | |||||
if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 || | |||||
hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 || | |||||
hdr.hdr_entsz < sizeof(struct gpt_ent) || | |||||
DEV_BSIZE % hdr.hdr_entsz != 0) { | |||||
return (0); | |||||
} | |||||
return (hdr.hdr_lba_alt + 1); | |||||
#else | |||||
return (0); | |||||
#endif | |||||
} | |||||
/* | |||||
* Get disk size from eax=0x800 and 0x4800. We need to probe both | |||||
* because 0x4800 may not be available and we would like to get more | |||||
* or less correct disk size - if it is possible at all. | |||||
* Note we do not really want to touch drv.c because that code is shared | |||||
* with boot2 and we can not afford to grow that code. | |||||
*/ | |||||
static uint64_t | |||||
drvsize_ext(struct zfsdsk *zdsk) | |||||
{ | |||||
struct dsk *dskp; | |||||
uint64_t size, tmp; | |||||
int cyl, hds, sec; | |||||
dskp = &zdsk->dsk; | |||||
/* Try to read disk size from GPT */ | |||||
size = drvsize_gpt(dskp); | |||||
if (size != 0) | |||||
return (size); | |||||
v86.ctl = V86_FLAGS; | |||||
v86.addr = 0x13; | |||||
v86.eax = 0x800; | |||||
v86.edx = dskp->drive; | |||||
v86int(); | |||||
/* Don't error out if we get bad sector number, try EDD as well */ | |||||
if (V86_CY(v86.efl) || /* carry set */ | |||||
(v86.edx & 0xff) <= (unsigned)(dskp->drive & 0x7f)) /* unit # bad */ | |||||
return (0); | |||||
cyl = ((v86.ecx & 0xc0) << 2) + ((v86.ecx & 0xff00) >> 8) + 1; | |||||
/* Convert max head # -> # of heads */ | |||||
hds = ((v86.edx & 0xff00) >> 8) + 1; | |||||
sec = v86.ecx & 0x3f; | |||||
size = (uint64_t)cyl * hds * sec; | |||||
/* Determine if we can use EDD with this device. */ | |||||
v86.ctl = V86_FLAGS; | |||||
v86.addr = 0x13; | |||||
v86.eax = 0x4100; | |||||
v86.edx = dskp->drive; | |||||
v86.ebx = 0x55aa; | |||||
v86int(); | |||||
if (V86_CY(v86.efl) || /* carry set */ | |||||
(v86.ebx & 0xffff) != 0xaa55 || /* signature */ | |||||
(v86.ecx & EDD_INTERFACE_FIXED_DISK) == 0) | |||||
return (size); | |||||
tmp = drvsize(dskp); | |||||
if (tmp > size) | |||||
size = tmp; | |||||
return (size); | |||||
} | |||||
/* | |||||
* The "layered" ioctl to read disk/partition size. Unfortunately | |||||
* the zfsboot case is hardest, because we do not have full software | |||||
* stack available, so we need to do some manual work here. | |||||
*/ | |||||
uint64_t | |||||
ldi_get_size(void *priv) | |||||
{ | |||||
struct zfsdsk *zdsk = priv; | |||||
uint64_t size = zdsk->dsk.size; | |||||
if (zdsk->dsk.start == 0) | |||||
size = drvsize_ext(zdsk); | |||||
return (size * DEV_BSIZE); | |||||
} | |||||
static void | |||||
probe_drive(struct zfsdsk *zdsk) | |||||
{ | |||||
#ifdef GPT | |||||
struct gpt_hdr hdr; | |||||
struct gpt_ent *ent; | |||||
unsigned part, entries_per_sec; | |||||
daddr_t slba; | |||||
#endif | |||||
#if defined(GPT) || defined(LOADER_GELI_SUPPORT) | |||||
daddr_t elba; | |||||
#endif | |||||
struct dos_partition *dp; | |||||
char *sec; | |||||
unsigned i; | |||||
#ifdef LOADER_GELI_SUPPORT | |||||
/* | |||||
* Taste the disk, if it is GELI encrypted, decrypt it then dig out the | |||||
* partition table and probe each slice/partition in turn for a vdev or | |||||
* GELI encrypted vdev. | |||||
*/ | |||||
elba = drvsize_ext(zdsk); | |||||
if (elba > 0) { | |||||
elba--; | |||||
} | |||||
zdsk->gdev = geli_taste(vdev_read, zdsk, elba, "disk%u:0:"); | |||||
if ((zdsk->gdev != NULL) && (geli_havekey(zdsk->gdev) == 0)) | |||||
geli_passphrase(zdsk->gdev, gelipw); | |||||
#endif /* LOADER_GELI_SUPPORT */ | |||||
sec = dmadat->secbuf; | |||||
zdsk->dsk.start = 0; | |||||
#ifdef GPT | |||||
/* | |||||
* First check for GPT. | |||||
*/ | |||||
if (drvread(&zdsk->dsk, sec, 1, 1)) { | |||||
return; | |||||
} | |||||
memcpy(&hdr, sec, sizeof(hdr)); | |||||
if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0 || | |||||
hdr.hdr_lba_self != 1 || hdr.hdr_revision < 0x00010000 || | |||||
hdr.hdr_entsz < sizeof(*ent) || DEV_BSIZE % hdr.hdr_entsz != 0) { | |||||
goto trymbr; | |||||
} | |||||
/* | |||||
* Probe all GPT partitions for the presence of ZFS pools. We | |||||
* return the spa_t for the first we find (if requested). This | |||||
* will have the effect of booting from the first pool on the | |||||
* disk. | |||||
* | |||||
* If no vdev is found, GELI decrypting the device and try again | |||||
*/ | |||||
entries_per_sec = DEV_BSIZE / hdr.hdr_entsz; | |||||
slba = hdr.hdr_lba_table; | |||||
elba = slba + hdr.hdr_entries / entries_per_sec; | |||||
while (slba < elba) { | |||||
zdsk->dsk.start = 0; | |||||
if (drvread(&zdsk->dsk, sec, slba, 1)) | |||||
return; | |||||
for (part = 0; part < entries_per_sec; part++) { | |||||
ent = (struct gpt_ent *)(sec + part * hdr.hdr_entsz); | |||||
if (memcmp(&ent->ent_type, &freebsd_zfs_uuid, | |||||
sizeof(uuid_t)) == 0) { | |||||
zdsk->dsk.start = ent->ent_lba_start; | |||||
zdsk->dsk.size = | |||||
ent->ent_lba_end - ent->ent_lba_start + 1; | |||||
zdsk->dsk.slice = part + 1; | |||||
zdsk->dsk.part = 255; | |||||
if (vdev_probe(vdev_read2, zdsk, NULL) == 0) { | |||||
/* | |||||
* This slice had a vdev. We need a new | |||||
* dsk structure now since the vdev now | |||||
* owns this one. | |||||
*/ | |||||
zdsk = copy_dsk(zdsk); | |||||
} | |||||
#ifdef LOADER_GELI_SUPPORT | |||||
else if ((zdsk->gdev = geli_taste(vdev_read, | |||||
zdsk, ent->ent_lba_end - ent->ent_lba_start, | |||||
"disk%up%u:", zdsk->dsk.unit, | |||||
zdsk->dsk.slice)) != NULL) { | |||||
if (geli_havekey(zdsk->gdev) == 0 || | |||||
geli_passphrase(zdsk->gdev, gelipw) | |||||
== 0) { | |||||
/* | |||||
* This slice has GELI, | |||||
* check it for ZFS. | |||||
*/ | |||||
if (vdev_probe(vdev_read2, | |||||
zdsk, NULL) == 0) { | |||||
/* | |||||
* This slice had a | |||||
* vdev. We need a new | |||||
* dsk structure now | |||||
* since the vdev now | |||||
* owns this one. | |||||
*/ | |||||
zdsk = copy_dsk(zdsk); | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
#endif /* LOADER_GELI_SUPPORT */ | |||||
} | |||||
} | |||||
slba++; | |||||
} | |||||
return; | |||||
trymbr: | |||||
#endif /* GPT */ | |||||
if (drvread(&zdsk->dsk, sec, DOSBBSECTOR, 1)) | |||||
return; | |||||
dp = (void *)(sec + DOSPARTOFF); | |||||
for (i = 0; i < NDOSPART; i++) { | |||||
if (!dp[i].dp_typ) | |||||
continue; | |||||
zdsk->dsk.start = dp[i].dp_start; | |||||
zdsk->dsk.size = dp[i].dp_size; | |||||
zdsk->dsk.slice = i + 1; | |||||
if (vdev_probe(vdev_read2, zdsk, NULL) == 0) { | |||||
zdsk = copy_dsk(zdsk); | |||||
} | |||||
#ifdef LOADER_GELI_SUPPORT | |||||
else if ((zdsk->gdev = geli_taste(vdev_read, zdsk, | |||||
dp[i].dp_size - dp[i].dp_start, "disk%us%u:")) != NULL) { | |||||
if (geli_havekey(zdsk->gdev) == 0 || | |||||
geli_passphrase(zdsk->gdev, gelipw) == 0) { | |||||
/* | |||||
* This slice has GELI, check it for ZFS. | |||||
*/ | |||||
if (vdev_probe(vdev_read2, zdsk, NULL) == 0) { | |||||
/* | |||||
* This slice had a vdev. We need a new | |||||
* dsk structure now since the vdev now | |||||
* owns this one. | |||||
*/ | |||||
zdsk = copy_dsk(zdsk); | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
#endif /* LOADER_GELI_SUPPORT */ | |||||
} | |||||
} | |||||
int | int | ||||
main(void) | main(void) | ||||
{ | { | ||||
dnode_phys_t dn; | unsigned i; | ||||
off_t off; | int auto_boot, fd, nextboot = 0; | ||||
struct zfsdsk *zdsk; | struct disk_devdesc devdesc; | ||||
int autoboot, i; | |||||
int nextboot; | |||||
int rc; | |||||
dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); | |||||
bios_getmem(); | bios_getmem(); | ||||
if (high_heap_size > 0) { | if (high_heap_size > 0) { | ||||
heap_end = PTOV(high_heap_base + high_heap_size); | heap_top = PTOV(high_heap_base + high_heap_size); | ||||
heap_next = PTOV(high_heap_base); | heap_bottom = PTOV(high_heap_base); | ||||
} else { | } else { | ||||
heap_next = (char *)dmadat + sizeof(*dmadat); | heap_bottom = (char *) | ||||
heap_end = (char *)PTOV(bios_basemem); | (roundup2(__base + (int32_t)&_end, 0x10000) - __base); | ||||
heap_top = (char *)PTOV(bios_basemem); | |||||
} | } | ||||
setheap(heap_next, heap_end); | setheap(heap_bottom, heap_top); | ||||
zdsk = calloc(1, sizeof(struct zfsdsk)); | /* | ||||
zdsk->dsk.drive = *(uint8_t *)PTOV(ARGS); | * Initialise the block cache. Set the upper limit. | ||||
zdsk->dsk.type = zdsk->dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; | */ | ||||
zdsk->dsk.unit = zdsk->dsk.drive & DRV_MASK; | bcache_init(32768, 512); | ||||
zdsk->dsk.slice = *(uint8_t *)PTOV(ARGS + 1) + 1; | |||||
zdsk->dsk.part = 0; | |||||
zdsk->dsk.start = 0; | |||||
zdsk->dsk.size = drvsize_ext(zdsk); | |||||
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_version = BOOTINFO_VERSION; | ||||
bootinfo.bi_size = sizeof(bootinfo); | bootinfo.bi_size = sizeof(bootinfo); | ||||
bootinfo.bi_basemem = bios_basemem / 1024; | bootinfo.bi_basemem = bios_basemem / 1024; | ||||
bootinfo.bi_extmem = bios_extmem / 1024; | bootinfo.bi_extmem = bios_extmem / 1024; | ||||
bootinfo.bi_memsizes_valid++; | bootinfo.bi_memsizes_valid++; | ||||
bootinfo.bi_bios_dev = zdsk->dsk.drive; | bootinfo.bi_bios_dev = *(uint8_t *)PTOV(ARGS); | ||||
bootdev = MAKEBOOTDEV(dev_maj[zdsk->dsk.type], | /* Set up fall back device name. */ | ||||
zdsk->dsk.slice, zdsk->dsk.unit, zdsk->dsk.part); | snprintf(boot_devname, sizeof (boot_devname), "disk%d:", | ||||
bd_bios2unit(bootinfo.bi_bios_dev)); | |||||
/* Process configuration file */ | for (i = 0; devsw[i] != NULL; i++) | ||||
if (devsw[i]->dv_init != NULL) | |||||
(devsw[i]->dv_init)(); | |||||
autoboot = 1; | disk_parsedev(&devdesc, boot_devname + 4, NULL); | ||||
zfs_init(); | bootdev = MAKEBOOTDEV(dev_maj[DEVT_DISK], devdesc.d_slice + 1, | ||||
devdesc.dd.d_unit, | |||||
devdesc.d_partition >= 0 ? devdesc.d_partition : 0xff); | |||||
/* | /* | ||||
* Probe the boot drive first - we will try to boot from whatever | * zfs_fmtdev() can be called only after dv_init | ||||
* pool we find on that drive. | |||||
*/ | */ | ||||
probe_drive(zdsk); | 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)); | ||||
* Probe the rest of the drives that the bios knows about. This | if (zfs_nextboot(bdev, cmd, sizeof(cmd)) == 0) { | ||||
* will find any other available pools and it may fill in missing | |||||
* vdevs for the boot pool. | |||||
*/ | |||||
#ifndef VIRTUALBOX | |||||
for (i = 0; i < *(unsigned char *)PTOV(BIOS_NUMDRIVES); i++) | |||||
#else | |||||
for (i = 0; i < MAXBDDEV; i++) | |||||
#endif | |||||
{ | |||||
if ((i | DRV_HARD) == *(uint8_t *)PTOV(ARGS)) | |||||
continue; | |||||
if (!int13probe(i | DRV_HARD)) | |||||
break; | |||||
zdsk = calloc(1, sizeof(struct zfsdsk)); | |||||
zdsk->dsk.drive = i | DRV_HARD; | |||||
zdsk->dsk.type = zdsk->dsk.drive & TYPE_AD; | |||||
zdsk->dsk.unit = i; | |||||
zdsk->dsk.slice = 0; | |||||
zdsk->dsk.part = 0; | |||||
zdsk->dsk.start = 0; | |||||
zdsk->dsk.size = drvsize_ext(zdsk); | |||||
probe_drive(zdsk); | |||||
} | |||||
/* | |||||
* The first discovered pool, if any, is the pool. | |||||
*/ | |||||
spa = spa_get_primary(); | |||||
if (!spa) { | |||||
printf("%s: No ZFS pools located, can't boot\n", BOOTPROG); | |||||
for (;;) | |||||
; | |||||
} | |||||
primary_spa = spa; | |||||
primary_vdev = spa_get_primary_vdev(spa); | |||||
nextboot = 0; | |||||
rc = vdev_read_pad2(primary_vdev, cmd, sizeof(cmd)); | |||||
if (vdev_clear_pad2(primary_vdev)) | |||||
printf("failed to clear pad2 area of primary vdev\n"); | |||||
if (rc == 0) { | |||||
if (*cmd) { | |||||
/* | |||||
* We could find an old-style ZFS Boot Block header | |||||
* here. Simply ignore it. | |||||
*/ | |||||
if (*(uint64_t *)cmd != 0x2f5b007b10c) { | |||||
/* | |||||
* Note that parse() is destructive to cmd[] | |||||
* and we also want to honor RBX_QUIET option | |||||
* that could be present in cmd[]. | |||||
*/ | |||||
nextboot = 1; | nextboot = 1; | ||||
memcpy(cmddup, cmd, sizeof(cmd)); | memcpy(cmddup, cmd, sizeof(cmd)); | ||||
if (parse_cmd()) { | if (parse_cmd()) { | ||||
printf("failed to parse pad2 area of " | if (!OPT_CHECK(RBX_QUIET)) | ||||
"primary vdev\n"); | printf("failed to parse pad2 area\n"); | ||||
reboot(); | exit(0); | ||||
} | } | ||||
if (!OPT_CHECK(RBX_QUIET)) | if (!OPT_CHECK(RBX_QUIET)) | ||||
printf("zfs nextboot: %s\n", cmddup); | printf("zfs nextboot: %s\n", cmddup); | ||||
} | |||||
/* Do not process this command twice */ | /* Do not process this command twice */ | ||||
*cmd = 0; | *cmd = 0; | ||||
} | } | ||||
} else | } | ||||
printf("failed to read pad2 area of primary vdev\n"); | |||||
/* Mount ZFS only if it's not already mounted via nextboot parsing. */ | /* now make sure we have bdev on all cases */ | ||||
if (zfsmount.spa == NULL && | free(bdev); | ||||
(zfs_spa_init(spa) != 0 || zfs_mount(spa, 0, &zfsmount) != 0)) { | i386_getdev((void **)&bdev, boot_devname, NULL); | ||||
printf("%s: failed to mount default pool %s\n", | |||||
BOOTPROG, spa->spa_name); | env_setenv("currdev", EV_VOLATILE, boot_devname, i386_setcurrdev, | ||||
autoboot = 0; | env_nounset); | ||||
} else if (zfs_lookup(&zfsmount, PATH_CONFIG, &dn) == 0 || | |||||
zfs_lookup(&zfsmount, PATH_DOTCONFIG, &dn) == 0) { | /* Process configuration file */ | ||||
off = 0; | auto_boot = 1; | ||||
zfs_read(spa, &dn, &off, cmd, sizeof(cmd)); | |||||
fd = open(PATH_CONFIG, O_RDONLY); | |||||
if (fd == -1) | |||||
fd = open(PATH_DOTCONFIG, O_RDONLY); | |||||
if (fd != -1) { | |||||
read(fd, cmd, sizeof (cmd)); | |||||
close(fd); | |||||
} | } | ||||
if (*cmd) { | if (*cmd) { | ||||
/* | /* | ||||
* Note that parse_cmd() is destructive to cmd[] and we also | * Note that parse_cmd() is destructive to cmd[] and we also | ||||
* want to honor RBX_QUIET option that could be present in | * want to honor RBX_QUIET option that could be present in | ||||
* cmd[]. | * cmd[]. | ||||
*/ | */ | ||||
memcpy(cmddup, cmd, sizeof(cmd)); | memcpy(cmddup, cmd, sizeof(cmd)); | ||||
if (parse_cmd()) | if (parse_cmd()) | ||||
autoboot = 0; | auto_boot = 0; | ||||
if (!OPT_CHECK(RBX_QUIET)) | if (!OPT_CHECK(RBX_QUIET)) | ||||
printf("%s: %s\n", PATH_CONFIG, cmddup); | printf("%s: %s\n", PATH_CONFIG, cmddup); | ||||
/* Do not process this command twice */ | /* Do not process this command twice */ | ||||
*cmd = 0; | *cmd = 0; | ||||
} | } | ||||
/* Do not risk waiting at the prompt forever. */ | /* Do not risk waiting at the prompt forever. */ | ||||
if (nextboot && !autoboot) | if (nextboot && !auto_boot) | ||||
reboot(); | exit(0); | ||||
if (autoboot && !*kname) { | if (auto_boot && !*kname) { | ||||
/* | /* | ||||
* Iterate through the list of loader and kernel paths, | * Iterate through the list of loader and kernel paths, | ||||
* trying to load. If interrupted by a keypress, or in case of | * trying to load. If interrupted by a keypress, or in case of | ||||
* failure, drop the user to the boot2 prompt. | * failure, drop the user to the boot2 prompt. | ||||
*/ | */ | ||||
for (i = 0; i < nitems(loadpath); i++) { | for (i = 0; i < nitems(loadpath); i++) { | ||||
memcpy(kname, loadpath[i].p, loadpath[i].len); | memcpy(kname, loadpath[i].p, loadpath[i].len); | ||||
if (keyhit(3)) | if (keyhit(3)) | ||||
break; | break; | ||||
load(); | load(); | ||||
} | } | ||||
} | } | ||||
/* Present the user with the boot2 prompt. */ | /* Present the user with the boot2 prompt. */ | ||||
for (;;) { | for (;;) { | ||||
if (!autoboot || !OPT_CHECK(RBX_QUIET)) { | if (!auto_boot || !OPT_CHECK(RBX_QUIET)) { | ||||
printf("\nFreeBSD/x86 boot\n"); | printf("\nFreeBSD/x86 boot\n"); | ||||
if (zfs_rlookup(spa, zfsmount.rootobj, rootname) != 0) | printf("Default: %s%s\nboot: ", boot_devname, kname); | ||||
printf("Default: %s/<0x%llx>:%s\n" | |||||
"boot: ", | |||||
spa->spa_name, zfsmount.rootobj, kname); | |||||
else if (rootname[0] != '\0') | |||||
printf("Default: %s/%s:%s\n" | |||||
"boot: ", | |||||
spa->spa_name, rootname, kname); | |||||
else | |||||
printf("Default: %s:%s\n" | |||||
"boot: ", | |||||
spa->spa_name, kname); | |||||
} | } | ||||
if (ioctrl & IO_SERIAL) | if (ioctrl & IO_SERIAL) | ||||
sio_flush(); | sio_flush(); | ||||
if (!autoboot || keyhit(5)) | if (!auto_boot || keyhit(5)) | ||||
getstr(cmd, sizeof(cmd)); | getstr(cmd, sizeof(cmd)); | ||||
else if (!autoboot || !OPT_CHECK(RBX_QUIET)) | else if (!auto_boot || !OPT_CHECK(RBX_QUIET)) | ||||
putchar('\n'); | putchar('\n'); | ||||
autoboot = 0; | auto_boot = 0; | ||||
if (parse_cmd()) | if (parse_cmd()) | ||||
putchar('\a'); | putchar('\a'); | ||||
else | else | ||||
load(); | load(); | ||||
} | } | ||||
} | } | ||||
/* XXX - Needed for btxld to link the boot2 binary; do not remove. */ | /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ | ||||
void | void | ||||
exit(int x) | exit(int x) | ||||
{ | { | ||||
__exit(x); | __exit(x); | ||||
} | } | ||||
void | |||||
reboot(void) | |||||
{ | |||||
__exit(0); | |||||
} | |||||
static void | static void | ||||
load(void) | load(void) | ||||
{ | { | ||||
union { | union { | ||||
struct exec ex; | struct exec ex; | ||||
Elf32_Ehdr eh; | Elf32_Ehdr eh; | ||||
} hdr; | } hdr; | ||||
static Elf32_Phdr ep[2]; | static Elf32_Phdr ep[2]; | ||||
static Elf32_Shdr es[2]; | static Elf32_Shdr es[2]; | ||||
caddr_t p; | caddr_t p; | ||||
dnode_phys_t dn; | |||||
off_t off; | |||||
uint32_t addr, x; | uint32_t addr, x; | ||||
int fmt, i, j; | int fd, fmt, i, j; | ||||
ssize_t size; | |||||
if (zfs_lookup(&zfsmount, kname, &dn)) { | if ((fd = open(kname, O_RDONLY)) == -1) { | ||||
printf("\nCan't find %s\n", kname); | printf("\nCan't find %s\n", kname); | ||||
return; | return; | ||||
} | } | ||||
off = 0; | |||||
if (xfsread(&dn, &off, &hdr, sizeof(hdr))) | size = sizeof(hdr); | ||||
if (read(fd, &hdr, sizeof (hdr)) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
if (N_GETMAGIC(hdr.ex) == ZMAGIC) | } | ||||
if (N_GETMAGIC(hdr.ex) == ZMAGIC) { | |||||
fmt = 0; | fmt = 0; | ||||
else if (IS_ELF(hdr.eh)) | } else if (IS_ELF(hdr.eh)) { | ||||
fmt = 1; | fmt = 1; | ||||
else { | } else { | ||||
printf("Invalid %s\n", "format"); | printf("Invalid %s\n", "format"); | ||||
close(fd); | |||||
return; | return; | ||||
} | } | ||||
if (fmt == 0) { | if (fmt == 0) { | ||||
addr = hdr.ex.a_entry & 0xffffff; | addr = hdr.ex.a_entry & 0xffffff; | ||||
p = PTOV(addr); | p = PTOV(addr); | ||||
off = PAGE_SIZE; | lseek(fd, PAGE_SIZE, SEEK_SET); | ||||
if (xfsread(&dn, &off, p, hdr.ex.a_text)) | size = hdr.ex.a_text; | ||||
if (read(fd, p, hdr.ex.a_text) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
p += roundup2(hdr.ex.a_text, PAGE_SIZE); | p += roundup2(hdr.ex.a_text, PAGE_SIZE); | ||||
if (xfsread(&dn, &off, p, hdr.ex.a_data)) | size = hdr.ex.a_data; | ||||
if (read(fd, p, hdr.ex.a_data) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); | p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); | ||||
bootinfo.bi_symtab = VTOP(p); | bootinfo.bi_symtab = VTOP(p); | ||||
memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); | memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); | ||||
p += sizeof(hdr.ex.a_syms); | p += sizeof(hdr.ex.a_syms); | ||||
if (hdr.ex.a_syms) { | if (hdr.ex.a_syms) { | ||||
if (xfsread(&dn, &off, p, hdr.ex.a_syms)) | size = hdr.ex.a_syms; | ||||
if (read(fd, p, hdr.ex.a_syms) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
p += hdr.ex.a_syms; | p += hdr.ex.a_syms; | ||||
if (xfsread(&dn, &off, p, sizeof(int))) | size = sizeof (int); | ||||
if (read(fd, p, sizeof (int)) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
x = *(uint32_t *)p; | x = *(uint32_t *)p; | ||||
p += sizeof(int); | p += sizeof(int); | ||||
x -= sizeof(int); | x -= sizeof(int); | ||||
if (xfsread(&dn, &off, p, x)) | size = x; | ||||
if (read(fd, p, x) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
p += x; | p += x; | ||||
} | } | ||||
} else { | } else { | ||||
off = hdr.eh.e_phoff; | lseek(fd, hdr.eh.e_phoff, SEEK_SET); | ||||
for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { | for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { | ||||
if (xfsread(&dn, &off, ep + j, sizeof(ep[0]))) | size = sizeof (ep[0]); | ||||
if (read(fd, ep + j, sizeof (ep[0])) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
if (ep[j].p_type == PT_LOAD) | if (ep[j].p_type == PT_LOAD) | ||||
j++; | j++; | ||||
} | } | ||||
for (i = 0; i < 2; i++) { | for (i = 0; i < 2; i++) { | ||||
p = PTOV(ep[i].p_paddr & 0xffffff); | p = PTOV(ep[i].p_paddr & 0xffffff); | ||||
off = ep[i].p_offset; | lseek(fd, ep[i].p_offset, SEEK_SET); | ||||
if (xfsread(&dn, &off, p, ep[i].p_filesz)) | size = ep[i].p_filesz; | ||||
if (read(fd, p, ep[i].p_filesz) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | } | ||||
} | |||||
p += roundup2(ep[1].p_memsz, PAGE_SIZE); | p += roundup2(ep[1].p_memsz, PAGE_SIZE); | ||||
bootinfo.bi_symtab = VTOP(p); | bootinfo.bi_symtab = VTOP(p); | ||||
if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { | if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { | ||||
off = hdr.eh.e_shoff + sizeof(es[0]) * | lseek(fd, hdr.eh.e_shoff + | ||||
(hdr.eh.e_shstrndx + 1); | sizeof (es[0]) * (hdr.eh.e_shstrndx + 1), | ||||
if (xfsread(&dn, &off, &es, sizeof(es))) | SEEK_SET); | ||||
size = sizeof(es); | |||||
if (read(fd, &es, sizeof (es)) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
for (i = 0; i < 2; i++) { | for (i = 0; i < 2; i++) { | ||||
memcpy(p, &es[i].sh_size, | memcpy(p, &es[i].sh_size, | ||||
sizeof(es[i].sh_size)); | sizeof(es[i].sh_size)); | ||||
p += sizeof(es[i].sh_size); | p += sizeof(es[i].sh_size); | ||||
off = es[i].sh_offset; | lseek(fd, es[i].sh_offset, SEEK_SET); | ||||
if (xfsread(&dn, &off, p, es[i].sh_size)) | size = es[i].sh_size; | ||||
if (read(fd, p, es[i].sh_size) != size) { | |||||
close(fd); | |||||
return; | return; | ||||
} | |||||
p += es[i].sh_size; | p += es[i].sh_size; | ||||
} | } | ||||
} | } | ||||
addr = hdr.eh.e_entry & 0xffffff; | addr = hdr.eh.e_entry & 0xffffff; | ||||
} | } | ||||
close(fd); | |||||
bootinfo.bi_esymtab = VTOP(p); | bootinfo.bi_esymtab = VTOP(p); | ||||
bootinfo.bi_kernelname = VTOP(kname); | bootinfo.bi_kernelname = VTOP(kname); | ||||
zfsargs.size = sizeof(zfsargs); | |||||
zfsargs.pool = zfsmount.spa->spa_guid; | |||||
zfsargs.root = zfsmount.rootobj; | |||||
zfsargs.primary_pool = primary_spa->spa_guid; | |||||
#ifdef LOADER_GELI_SUPPORT | #ifdef LOADER_GELI_SUPPORT | ||||
explicit_bzero(gelipw, sizeof(gelipw)); | 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); | export_geli_boot_data(&zfsargs.gelidata); | ||||
#endif | #endif | ||||
if (primary_vdev != NULL) | |||||
zfsargs.primary_vdev = primary_vdev->v_guid; | |||||
else | |||||
printf("failed to detect primary vdev\n"); | |||||
/* | /* | ||||
* Note that the zfsargs struct is passed by value, not by pointer. | * Note that the zfsargs struct is passed by value, not by | ||||
* Code in btxldr.S copies the values from the entry stack to a fixed | * pointer. Code in btxldr.S copies the values from the entry | ||||
* location within loader(8) at startup due to the presence of | * stack to a fixed location within loader(8) at startup due | ||||
* KARGS_FLAGS_EXTARG. | * to the presence of KARGS_FLAGS_EXTARG. | ||||
*/ | */ | ||||
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), | __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), | ||||
bootdev, | bootdev, | ||||
KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG, | KARGS_FLAGS_ZFS | KARGS_FLAGS_EXTARG, | ||||
(uint32_t)spa->spa_guid, | (uint32_t)bdev->d_kind.zfs.pool_guid, | ||||
(uint32_t)(spa->spa_guid >> 32), | (uint32_t)(bdev->d_kind.zfs.pool_guid >> 32), | ||||
VTOP(&bootinfo), | VTOP(&bootinfo), | ||||
zfsargs); | 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 | static int | ||||
zfs_mount_ds(char *dsname) | mount_root(char *arg) | ||||
{ | { | ||||
uint64_t newroot; | char *root; | ||||
spa_t *newspa; | struct i386_devdesc *ddesc; | ||||
char *q; | uint8_t part; | ||||
q = strchr(dsname, '/'); | if (asprintf(&root, "%s:", arg) < 0) | ||||
if (q) | return (1); | ||||
*q++ = '\0'; | |||||
newspa = spa_find_by_name(dsname); | if (i386_getdev((void **)&ddesc, root, NULL)) { | ||||
if (newspa == NULL) { | free(root); | ||||
printf("\nCan't find ZFS pool %s\n", dsname); | return (1); | ||||
return (-1); | |||||
} | } | ||||
if (zfs_spa_init(newspa)) | /* we should have new device descriptor, free old and replace it. */ | ||||
return (-1); | free(bdev); | ||||
bdev = ddesc; | |||||
newroot = 0; | if (bdev->dd.d_dev->dv_type == DEVT_DISK) { | ||||
if (q) { | if (bdev->d_kind.biosdisk.partition == -1) | ||||
if (zfs_lookup_dataset(newspa, q, &newroot)) { | part = 0xff; | ||||
printf("\nCan't find dataset %s in ZFS pool %s\n", | else | ||||
q, newspa->spa_name); | part = bdev->d_kind.biosdisk.partition; | ||||
return (-1); | 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); | |||||
} | } | ||||
if (zfs_mount(newspa, newroot, &zfsmount)) { | |||||
printf("\nCan't mount ZFS dataset\n"); | static void | ||||
return (-1); | 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; | |||||
} | } | ||||
spa = newspa; | pager_close(); | ||||
return (0); | close(fd); | ||||
} | } | ||||
static int | static int | ||||
parse_cmd(void) | parse_cmd(void) | ||||
{ | { | ||||
char *arg = cmd; | char *arg = cmd; | ||||
char *ep, *p, *q; | char *ep, *p, *q; | ||||
const char *cp; | const char *cp; | ||||
char line[80]; | |||||
int c, i, j; | int c, i, j; | ||||
while ((c = *arg++)) { | while ((c = *arg++)) { | ||||
if (c == ' ' || c == '\t' || c == '\n') | if (c == ' ' || c == '\t' || c == '\n') | ||||
continue; | continue; | ||||
for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++) | for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++) | ||||
; | ; | ||||
ep = p; | ep = p; | ||||
Show All 32 Lines | if (c == '-') { | ||||
} | } | ||||
ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : | ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : | ||||
OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; | OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; | ||||
if (ioctrl & IO_SERIAL) { | if (ioctrl & IO_SERIAL) { | ||||
if (sio_init(115200 / comspeed) != 0) | if (sio_init(115200 / comspeed) != 0) | ||||
ioctrl &= ~IO_SERIAL; | ioctrl &= ~IO_SERIAL; | ||||
} | } | ||||
} if (c == '?') { | } if (c == '?') { | ||||
dnode_phys_t dn; | printf("\n"); | ||||
if (*arg == '\0') | |||||
if (zfs_lookup(&zfsmount, arg, &dn) == 0) { | arg = (char *)"/"; | ||||
zap_list(spa, &dn); | fs_list(arg); | ||||
} | zfs_list(arg); | ||||
return (-1); | return (-1); | ||||
} else { | } else { | ||||
char *ptr; | |||||
printf("\n"); | |||||
arg--; | arg--; | ||||
/* | /* | ||||
* Report pool status if the comment is 'status'. Lets | * Report pool status if the comment is 'status'. Lets | ||||
* hope no-one wants to load /status as a kernel. | * hope no-one wants to load /status as a kernel. | ||||
*/ | */ | ||||
if (strcmp(arg, "status") == 0) { | if (strcmp(arg, "status") == 0) { | ||||
spa_all_status(); | 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); | return (-1); | ||||
} | } | ||||
/* | /* | ||||
* If there is "zfs:" prefix simply ignore it. | * If there is "zfs:" prefix simply ignore it. | ||||
*/ | */ | ||||
if (strncmp(arg, "zfs:", 4) == 0) | ptr = arg; | ||||
arg += 4; | if (strncmp(ptr, "zfs:", 4) == 0) | ||||
ptr += 4; | |||||
/* | /* | ||||
* If there is a colon, switch pools. | * If there is a colon, switch pools. | ||||
*/ | */ | ||||
q = strchr(arg, ':'); | q = strchr(ptr, ':'); | ||||
if (q) { | if (q) { | ||||
*q++ = '\0'; | *q++ = '\0'; | ||||
if (zfs_mount_ds(arg) != 0) | if (mount_root(arg) != 0) { | ||||
return (-1); | return (-1); | ||||
} | |||||
arg = q; | arg = q; | ||||
} | } | ||||
if ((i = ep - arg)) { | if ((i = ep - arg)) { | ||||
if ((size_t)i >= sizeof(kname)) | if ((size_t)i >= sizeof(kname)) | ||||
return (-1); | return (-1); | ||||
memcpy(kname, arg, i + 1); | memcpy(kname, arg, i + 1); | ||||
} | } | ||||
} | } | ||||
arg = p; | arg = p; | ||||
} | } | ||||
return (0); | 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; | |||||
} | |||||
} | |||||
} | } |