summaryrefslogtreecommitdiffstats
path: root/sys/boot/i386
diff options
context:
space:
mode:
authorallanjude <allanjude@FreeBSD.org>2016-03-16 23:12:19 +0000
committerallanjude <allanjude@FreeBSD.org>2016-03-16 23:12:19 +0000
commit736b39250974facd8d5f4ab24f56636212c50a33 (patch)
treee3e47b4adfddec374c56af417380ab82142fa71a /sys/boot/i386
parentecffce941a074abbd944385dc1cc659f4df1b04d (diff)
downloadFreeBSD-src-736b39250974facd8d5f4ab24f56636212c50a33.zip
FreeBSD-src-736b39250974facd8d5f4ab24f56636212c50a33.tar.gz
Implement GELI (AES-XTS and AES-CBC only) in gptboot and gptzfsboot
Allows booting from a GELI encrypted root file system, via UFS or ZFS Reviewed by: gnn, smh (previous version), delphij (previous version) Relnotes: yes Sponsored by: ScaleEngine Inc. Differential Revision: https://reviews.freebsd.org/D4593
Diffstat (limited to 'sys/boot/i386')
-rw-r--r--sys/boot/i386/common/bootargs.h6
-rw-r--r--sys/boot/i386/common/cons.c7
-rw-r--r--sys/boot/i386/common/drv.c2
-rw-r--r--sys/boot/i386/common/drv.h2
-rw-r--r--sys/boot/i386/gptboot/Makefile16
-rw-r--r--sys/boot/i386/gptboot/gptboot.c221
-rw-r--r--sys/boot/i386/gptzfsboot/Makefile14
-rw-r--r--sys/boot/i386/libi386/Makefile6
-rw-r--r--sys/boot/i386/libi386/biosdisk.c178
-rw-r--r--sys/boot/i386/loader/Makefile11
-rw-r--r--sys/boot/i386/loader/main.c31
-rw-r--r--sys/boot/i386/zfsboot/zfsboot.c119
12 files changed, 571 insertions, 42 deletions
diff --git a/sys/boot/i386/common/bootargs.h b/sys/boot/i386/common/bootargs.h
index 4768d68..e6674e1 100644
--- a/sys/boot/i386/common/bootargs.h
+++ b/sys/boot/i386/common/bootargs.h
@@ -64,6 +64,12 @@ struct bootargs
*/
};
+struct geli_boot_args
+{
+ uint32_t size;
+ char gelipw[256];
+};
+
#endif /*__ASSEMBLER__*/
#endif /* !_BOOT_I386_ARGS_H_ */
diff --git a/sys/boot/i386/common/cons.c b/sys/boot/i386/common/cons.c
index 97019c6..b967d9b 100644
--- a/sys/boot/i386/common/cons.c
+++ b/sys/boot/i386/common/cons.c
@@ -97,6 +97,13 @@ xgetc(int fn)
}
int
+getchar(void)
+{
+
+ return (xgetc(0));
+}
+
+int
keyhit(unsigned int secs)
{
uint32_t t0, t1;
diff --git a/sys/boot/i386/common/drv.c b/sys/boot/i386/common/drv.c
index 52933d5..f5133de 100644
--- a/sys/boot/i386/common/drv.c
+++ b/sys/boot/i386/common/drv.c
@@ -29,7 +29,6 @@ __FBSDID("$FreeBSD$");
#include "xreadorg.h"
#endif
-#ifdef GPT
static struct edd_params params;
uint64_t
@@ -50,7 +49,6 @@ drvsize(struct dsk *dskp)
}
return (params.sectors);
}
-#endif /* GPT */
#ifndef USE_XREAD
static struct edd_packet packet;
diff --git a/sys/boot/i386/common/drv.h b/sys/boot/i386/common/drv.h
index 1ecfbc3..8ad3c9c 100644
--- a/sys/boot/i386/common/drv.h
+++ b/sys/boot/i386/common/drv.h
@@ -42,7 +42,7 @@ struct dsk {
int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
#ifdef GPT
int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk);
-uint64_t drvsize(struct dsk *dskp);
#endif /* GPT */
+uint64_t drvsize(struct dsk *dskp);
#endif /* !_DRV_H_ */
diff --git a/sys/boot/i386/gptboot/Makefile b/sys/boot/i386/gptboot/Makefile
index 1aafe0b..af9c9e0 100644
--- a/sys/boot/i386/gptboot/Makefile
+++ b/sys/boot/i386/gptboot/Makefile
@@ -39,6 +39,14 @@ CFLAGS= -DBOOTPROG=\"gptboot\" \
CFLAGS.gcc+= --param max-inline-insns-single=100
+.if !defined(LOADER_NO_GELI_SUPPORT)
+CFLAGS+= -DLOADER_GELI_SUPPORT
+CFLAGS+= -I${.CURDIR}/../../geli
+LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a
+.PATH: ${.CURDIR}/../../../opencrypto
+OPENCRYPTO_XTS= xform_aes_xts.o
+.endif
+
LD_FLAGS=-static -N --gc-sections
LIBSTAND= ${.OBJDIR}/../../libstand32/libstand.a
@@ -60,14 +68,14 @@ gptldr.bin: gptldr.out
gptldr.out: gptldr.o
${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
-CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o gpt.o crc32.o drv.o \
- cons.o util.o
+CLEANFILES+= gptboot.bin gptboot.out gptboot.o sio.o crc32.o drv.o \
+ cons.o util.o ${OPENCRYPTO_XTS}
gptboot.bin: gptboot.out
${OBJCOPY} -S -O binary gptboot.out ${.TARGET}
-gptboot.out: ${BTXCRT} gptboot.o sio.o gpt.o crc32.o drv.o cons.o util.o
- ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND}
+gptboot.out: ${BTXCRT} gptboot.o sio.o crc32.o drv.o cons.o util.o ${OPENCRYPTO_XTS}
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT}
gptboot.o: ${.CURDIR}/../../common/ufsread.c
diff --git a/sys/boot/i386/gptboot/gptboot.c b/sys/boot/i386/gptboot/gptboot.c
index a1f46eb..14438e6 100644
--- a/sys/boot/i386/gptboot/gptboot.c
+++ b/sys/boot/i386/gptboot/gptboot.c
@@ -23,6 +23,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bootinfo.h>
#include <machine/elf.h>
+#include <machine/pc/bios.h>
#include <machine/psl.h>
#include <stdarg.h>
@@ -31,6 +32,7 @@ __FBSDID("$FreeBSD$");
#include <btxv86.h>
+#include "bootargs.h"
#include "lib.h"
#include "rbx.h"
#include "drv.h"
@@ -82,14 +84,60 @@ static struct dsk dsk;
static char kname[1024];
static int comspeed = SIOSPD;
static struct bootinfo bootinfo;
+static struct geli_boot_args geliargs;
+
+static vm_offset_t high_heap_base;
+static uint32_t bios_basemem, bios_extmem, high_heap_size;
+
+static struct bios_smap smap;
+
+/*
+ * The minimum amount of memory to reserve in bios_extmem for the heap.
+ */
+#define HEAP_MIN (3 * 1024 * 1024)
+
+static char *heap_next;
+static char *heap_end;
void exit(int);
static void load(void);
static int parse(char *, int *);
static int dskread(void *, daddr_t, unsigned);
-static uint32_t memsize(void);
+void *malloc(size_t n);
+void free(void *ptr);
+#ifdef LOADER_GELI_SUPPORT
+static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf,
+ size_t bytes);
+#endif
+
+void *
+malloc(size_t n)
+{
+ char *p = heap_next;
+ if (p + n > heap_end) {
+ printf("malloc failure\n");
+ for (;;)
+ ;
+ /* NOTREACHED */
+ return (0);
+ }
+ heap_next += n;
+ return (p);
+}
+
+void
+free(void *ptr)
+{
+
+ return;
+}
#include "ufsread.c"
+#include "gpt.c"
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.c"
+static char gelipw[GELI_PW_MAXLEN];
+#endif
static inline int
xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
@@ -102,14 +150,90 @@ xfsread(ufs_ino_t inode, void *buf, size_t nbyte)
return (0);
}
-static inline uint32_t
-memsize(void)
+static void
+bios_getmem(void)
{
+ uint64_t size;
- v86.addr = MEM_EXT;
+ /* Parse system memory map */
+ v86.ebx = 0;
+ do {
+ v86.ctl = V86_FLAGS;
+ v86.addr = MEM_EXT; /* 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.efl & 1) || (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.efl & 1)) {
+ 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();
- return (v86.eax);
+ 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;
+ }
}
static int
@@ -124,6 +248,16 @@ gptinit(void)
printf("%s: no UFS partition was found\n", BOOTPROG);
return (-1);
}
+#ifdef LOADER_GELI_SUPPORT
+ if (geli_taste(vdev_read, &dsk, (gpttable[curent].ent_lba_end -
+ gpttable[curent].ent_lba_start)) == 0) {
+ if (geli_passphrase(&gelipw, dsk.unit, 'p', curent + 1, &dsk) != 0) {
+ printf("%s: unable to decrypt GELI key\n", BOOTPROG);
+ return (-1);
+ }
+ }
+#endif
+
dsk_meta = 0;
return (0);
}
@@ -137,6 +271,17 @@ main(void)
ufs_ino_t ino;
dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base);
+
+ bios_getmem();
+
+ if (high_heap_size > 0) {
+ heap_end = PTOV(high_heap_base + high_heap_size);
+ heap_next = PTOV(high_heap_base);
+ } else {
+ heap_next = (char *)dmadat + sizeof(*dmadat);
+ heap_end = (char *)PTOV(bios_basemem);
+ }
+
v86.ctl = V86_FLAGS;
v86.efl = PSL_RESERVED_DEFAULT | PSL_I;
dsk.drive = *(uint8_t *)PTOV(ARGS);
@@ -146,10 +291,14 @@ main(void)
dsk.start = 0;
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_size = sizeof(bootinfo);
- bootinfo.bi_basemem = 0; /* XXX will be filled by loader or kernel */
- bootinfo.bi_extmem = memsize();
+ bootinfo.bi_basemem = bios_basemem / 1024;
+ bootinfo.bi_extmem = bios_extmem / 1024;
bootinfo.bi_memsizes_valid++;
+ bootinfo.bi_bios_dev = dsk.drive;
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
/* Process configuration file */
if (gptinit() != 0)
@@ -327,9 +476,16 @@ load(void)
bootinfo.bi_esymtab = VTOP(p);
bootinfo.bi_kernelname = VTOP(kname);
bootinfo.bi_bios_dev = dsk.drive;
+ geliargs.size = sizeof(geliargs);
+#ifdef LOADER_GELI_SUPPORT
+ bcopy(gelipw, geliargs.gelipw, sizeof(geliargs.gelipw));
+ bzero(gelipw, sizeof(gelipw));
+#else
+ geliargs.gelipw[0] = '\0';
+#endif
__exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK),
MAKEBOOTDEV(dev_maj[dsk.type], dsk.part + 1, dsk.unit, 0xff),
- 0, 0, 0, VTOP(&bootinfo));
+ KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs);
}
static int
@@ -430,6 +586,53 @@ parse(char *cmdstr, int *dskupdated)
static int
dskread(void *buf, daddr_t lba, unsigned nblk)
{
+ int err;
+
+ err = drvread(&dsk, buf, lba + dsk.start, nblk);
- return drvread(&dsk, buf, lba + dsk.start, nblk);
+#ifdef LOADER_GELI_SUPPORT
+ if (err == 0 && is_geli(&dsk) == 0) {
+ /* Decrypt */
+ if (geli_read(&dsk, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE))
+ return (err);
+ }
+#endif
+
+ return (err);
+}
+
+#ifdef LOADER_GELI_SUPPORT
+/*
+ * Read function compartible with the ZFS callback, required to keep the GELI
+ * Implementation the same for both UFS and ZFS
+ */
+static int
+vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes)
+{
+ char *p;
+ daddr_t lba;
+ unsigned int nb;
+ struct dsk *dskp = (struct dsk *) priv;
+
+ if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1)))
+ return (-1);
+
+ p = buf;
+ lba = off / DEV_BSIZE;
+ lba += dskp->start;
+
+ while (bytes > 0) {
+ nb = bytes / DEV_BSIZE;
+ if (nb > VBLKSIZE / DEV_BSIZE)
+ nb = VBLKSIZE / DEV_BSIZE;
+ if (drvread(dskp, dmadat->blkbuf, lba, nb))
+ return (-1);
+ memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE);
+ p += nb * DEV_BSIZE;
+ lba += nb;
+ bytes -= nb * DEV_BSIZE;
+ }
+
+ return (0);
}
+#endif /* LOADER_GELI_SUPPORT */
diff --git a/sys/boot/i386/gptzfsboot/Makefile b/sys/boot/i386/gptzfsboot/Makefile
index 1406333..8f6d3bc 100644
--- a/sys/boot/i386/gptzfsboot/Makefile
+++ b/sys/boot/i386/gptzfsboot/Makefile
@@ -35,6 +35,14 @@ CFLAGS= -DBOOTPROG=\"gptzfsboot\" \
-Wpointer-arith -Wshadow -Wstrict-prototypes -Wwrite-strings \
-Winline
+.if !defined(LOADER_NO_GELI_SUPPORT)
+CFLAGS+= -DLOADER_GELI_SUPPORT
+CFLAGS+= -I${.CURDIR}/../../geli
+LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a
+.PATH: ${.CURDIR}/../../../opencrypto
+OPENCRYPTO_XTS= xform_aes_xts.o
+.endif
+
CFLAGS.gcc+= --param max-inline-insns-single=100
LD_FLAGS=-static -N --gc-sections
@@ -59,13 +67,13 @@ gptldr.out: gptldr.o
${LD} ${LD_FLAGS} -e start -Ttext ${ORG1} -o ${.TARGET} gptldr.o
CLEANFILES+= gptzfsboot.bin gptzfsboot.out zfsboot.o sio.o cons.o \
- drv.o gpt.o util.o
+ drv.o gpt.o util.o ${OPENCRYPTO_XTS}
gptzfsboot.bin: gptzfsboot.out
${OBJCOPY} -S -O binary gptzfsboot.out ${.TARGET}
-gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o
- ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND}
+gptzfsboot.out: ${BTXCRT} zfsboot.o sio.o gpt.o drv.o cons.o util.o ${OPENCRYPTO_XTS}
+ ${LD} ${LD_FLAGS} -Ttext ${ORG2} -o ${.TARGET} ${.ALLSRC} ${LIBSTAND} ${LIBGELIBOOT}
zfsboot.o: ${.CURDIR}/../../zfs/zfsimpl.c
diff --git a/sys/boot/i386/libi386/Makefile b/sys/boot/i386/libi386/Makefile
index f3c1d8d..d38f18c 100644
--- a/sys/boot/i386/libi386/Makefile
+++ b/sys/boot/i386/libi386/Makefile
@@ -30,6 +30,12 @@ CFLAGS+= -DCOMSPEED=${BOOT_COMCONSOLE_SPEED}
CFLAGS+= -DDISK_DEBUG
.endif
+.if !defined(LOADER_NO_GELI_SUPPORT)
+# Decrypt encrypted drives
+CFLAGS+= -DLOADER_GELI_SUPPORT
+CFLAGS+= -I${.CURDIR}/../../geli
+.endif
+
.if !defined(BOOT_HIDE_SERIAL_NUMBERS)
# Export serial numbers, UUID, and asset tag from loader.
CFLAGS+= -DSMBIOS_SERIAL_NUMBERS
diff --git a/sys/boot/i386/libi386/biosdisk.c b/sys/boot/i386/libi386/biosdisk.c
index 2a71d64..1c54769 100644
--- a/sys/boot/i386/libi386/biosdisk.c
+++ b/sys/boot/i386/libi386/biosdisk.c
@@ -49,6 +49,34 @@ __FBSDID("$FreeBSD$");
#include "disk.h"
#include "libi386.h"
+#ifdef LOADER_GELI_SUPPORT
+#include "cons.h"
+#include "drv.h"
+#include "gpt.h"
+#include "part.h"
+#include <uuid.h>
+struct pentry {
+ struct ptable_entry part;
+ uint64_t flags;
+ union {
+ uint8_t bsd;
+ uint8_t mbr;
+ uuid_t gpt;
+ uint16_t vtoc8;
+ } type;
+ STAILQ_ENTRY(pentry) entry;
+};
+struct ptable {
+ enum ptable_type type;
+ uint16_t sectorsize;
+ uint64_t sectors;
+
+ STAILQ_HEAD(, pentry) entries;
+};
+
+#include "geliboot.c"
+#endif /* LOADER_GELI_SUPPORT */
+
CTASSERT(sizeof(struct i386_devdesc) >= sizeof(struct disk_devdesc));
#define BIOS_NUMDRIVES 0x475
@@ -108,6 +136,18 @@ static int bd_ioctl(struct open_file *f, u_long cmd, void *data);
static void bd_print(int verbose);
static void bd_cleanup(void);
+#ifdef LOADER_GELI_SUPPORT
+static enum isgeli {
+ ISGELI_UNKNOWN,
+ ISGELI_NO,
+ ISGELI_YES
+};
+static enum isgeli geli_status[MAXBDDEV][MAXTBLENTS];
+
+int bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf,
+ size_t bytes);
+#endif /* LOADER_GELI_SUPPORT */
+
struct devsw biosdisk = {
"disk",
DEVT_DISK,
@@ -154,6 +194,9 @@ bd_init(void)
{
int base, unit, nfd = 0;
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
/* sequence 0, 0x80 */
for (base = 0; base <= 0x80; base += 0x80) {
for (unit = base; (nbdinfo < MAXBDDEV); unit++) {
@@ -299,7 +342,8 @@ bd_print(int verbose)
static int
bd_open(struct open_file *f, ...)
{
- struct disk_devdesc *dev;
+ struct disk_devdesc *dev, rdev;
+ int err, g_err;
va_list ap;
va_start(ap, f);
@@ -309,9 +353,83 @@ bd_open(struct open_file *f, ...)
if (dev->d_unit < 0 || dev->d_unit >= nbdinfo)
return (EIO);
- return (disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
+ err = disk_open(dev, BD(dev).bd_sectors * BD(dev).bd_sectorsize,
BD(dev).bd_sectorsize, (BD(dev).bd_flags & BD_FLOPPY) ?
- DISK_F_NOCACHE: 0));
+ DISK_F_NOCACHE: 0);
+
+#ifdef LOADER_GELI_SUPPORT
+ static char gelipw[GELI_PW_MAXLEN];
+ char *passphrase;
+
+ if (err)
+ return (err);
+
+ /* if we already know there is no GELI, skip the rest */
+ if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_UNKNOWN)
+ return (err);
+
+ struct dsk dskp;
+ struct ptable *table = NULL;
+ struct ptable_entry part;
+ struct pentry *entry;
+ int geli_part = 0;
+
+ dskp.drive = bd_unit2bios(dev->d_unit);
+ dskp.type = dev->d_type;
+ dskp.unit = dev->d_unit;
+ dskp.slice = dev->d_slice;
+ dskp.part = dev->d_partition;
+ dskp.start = dev->d_offset;
+
+ memcpy(&rdev, dev, sizeof(rdev));
+ /* to read the GPT table, we need to read the first sector */
+ rdev.d_offset = 0;
+ /* We need the LBA of the end of the partition */
+ table = ptable_open(&rdev, BD(dev).bd_sectors,
+ BD(dev).bd_sectorsize, ptblread);
+ if (table == NULL) {
+ DEBUG("Can't read partition table");
+ /* soft failure, return the exit status of disk_open */
+ return (err);
+ }
+
+ if (table->type == PTABLE_GPT)
+ dskp.part = 255;
+
+ STAILQ_FOREACH(entry, &table->entries, entry) {
+ dskp.slice = entry->part.index;
+ dskp.start = entry->part.start;
+ if (is_geli(&dskp) == 0) {
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ return (0);
+ }
+ if (geli_taste(bios_read, &dskp,
+ entry->part.end - entry->part.start) == 0) {
+ if ((passphrase = getenv("kern.geom.eli.passphrase"))
+ != NULL) {
+ /* Use the cached passphrase */
+ bcopy(passphrase, &gelipw, GELI_PW_MAXLEN);
+ }
+ if (geli_passphrase(&gelipw, dskp.unit, 'p',
+ (dskp.slice > 0 ? dskp.slice : dskp.part),
+ &dskp) == 0) {
+ setenv("kern.geom.eli.passphrase", &gelipw, 1);
+ bzero(gelipw, sizeof(gelipw));
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_YES;
+ geli_part++;
+ }
+ } else
+ geli_status[dev->d_unit][dskp.slice] = ISGELI_NO;
+ }
+
+ /* none of the partitions on this disk have GELI */
+ if (geli_part == 0) {
+ /* found no GELI */
+ geli_status[dev->d_unit][dev->d_slice] = ISGELI_NO;
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
+ return (err);
}
static int
@@ -586,6 +704,38 @@ bd_io(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest, int write)
static int
bd_read(struct disk_devdesc *dev, daddr_t dblk, int blks, caddr_t dest)
{
+#ifdef LOADER_GELI_SUPPORT
+ struct dsk dskp;
+ off_t p_off;
+ int err, n;
+
+ /* if we already know there is no GELI, skip the rest */
+ if (geli_status[dev->d_unit][dev->d_slice] != ISGELI_YES)
+ return (bd_io(dev, dblk, blks, dest, 0));
+
+ if (geli_status[dev->d_unit][dev->d_slice] == ISGELI_YES) {
+ err = bd_io(dev, dblk, blks, dest, 0);
+ if (err)
+ return (err);
+
+ dskp.drive = bd_unit2bios(dev->d_unit);
+ dskp.type = dev->d_type;
+ dskp.unit = dev->d_unit;
+ dskp.slice = dev->d_slice;
+ dskp.part = dev->d_partition;
+ dskp.start = dev->d_offset;
+
+ /* GELI needs the offset relative to the partition start */
+ p_off = dblk - dskp.start;
+
+ err = geli_read(&dskp, p_off * BIOSDISK_SECSIZE, dest,
+ blks * BIOSDISK_SECSIZE);
+ if (err)
+ return (err);
+
+ return (0);
+ }
+#endif /* LOADER_GELI_SUPPORT */
return (bd_io(dev, dblk, blks, dest, 0));
}
@@ -682,3 +832,25 @@ bd_getdev(struct i386_devdesc *d)
DEBUG("dev is 0x%x\n", rootdev);
return(rootdev);
}
+
+#ifdef LOADER_GELI_SUPPORT
+int
+bios_read(void *vdev __unused, struct dsk *priv, off_t off, char *buf, size_t bytes)
+{
+ struct disk_devdesc dev;
+
+ dev.d_dev = &biosdisk;
+ dev.d_type = priv->type;
+ dev.d_unit = priv->unit;
+ dev.d_slice = priv->slice;
+ dev.d_partition = priv->part;
+ dev.d_offset = priv->start;
+
+ off = off / BIOSDISK_SECSIZE;
+ /* GELI gives us the offset relative to the partition start */
+ off += dev.d_offset;
+ bytes = bytes / BIOSDISK_SECSIZE;
+
+ return (bd_io(&dev, off, bytes, buf, 0));
+}
+#endif /* LOADER_GELI_SUPPORT */
diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile
index 776ba92..233876a 100644
--- a/sys/boot/i386/loader/Makefile
+++ b/sys/boot/i386/loader/Makefile
@@ -58,6 +58,13 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT
.if defined(LOADER_NANDFS_SUPPORT)
CFLAGS+= -DLOADER_NANDFS_SUPPORT
.endif
+.if !defined(LOADER_NO_GELI_SUPPORT)
+CFLAGS+= -DLOADER_GELI_SUPPORT
+LIBGELIBOOT= ${.OBJDIR}/../../geli/libgeliboot.a
+.PATH: ${.CURDIR}/../../../opencrypto
+SRCS+= xform_aes_xts.c
+CFLAGS+= -I${.CURDIR}/../../.. -D_STAND
+.endif
# Always add MI sources
.PATH: ${.CURDIR}/../../common
@@ -116,8 +123,8 @@ FILES+= loader.rc menu.rc
# XXX crt0.o needs to be first for pxeboot(8) to work
OBJS= ${BTXCRT}
-DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND}
-LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND}
+DPADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT}
+LDADD= ${LIBFICL} ${LIBFIREWIRE} ${LIBZFSBOOT} ${LIBI386} ${LIBSTAND} ${LIBGELIBOOT}
.include <bsd.prog.mk>
diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c
index c0782fc..b3e7b41 100644
--- a/sys/boot/i386/loader/main.c
+++ b/sys/boot/i386/loader/main.c
@@ -68,7 +68,11 @@ 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_GELI_SUPPORT
+struct geli_boot_args *gargs;
+#endif
#ifdef LOADER_ZFS_SUPPORT
+struct zfs_boot_args *zargs;
static void i386_zfs_probe(void);
#endif
@@ -164,7 +168,31 @@ main(void)
archsw.arch_isaoutb = isa_outb;
#ifdef LOADER_ZFS_SUPPORT
archsw.arch_zfs_probe = i386_zfs_probe;
-#endif
+
+#ifdef LOADER_GELI_SUPPORT
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
+ zargs = (struct zfs_boot_args *)(kargs + 1);
+ if (zargs != NULL && zargs->size >= offsetof(struct zfs_boot_args, gelipw)) {
+ if (zargs->gelipw[0] != '\0') {
+ setenv("kern.geom.eli.passphrase", zargs->gelipw, 1);
+ bzero(zargs->gelipw, sizeof(zargs->gelipw));
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+#else /* !LOADER_ZFS_SUPPORT */
+#ifdef LOADER_GELI_SUPPORT
+ if ((kargs->bootflags & KARGS_FLAGS_EXTARG) != 0) {
+ gargs = (struct geli_boot_args *)(kargs + 1);
+ if (gargs != NULL && gargs->size >= offsetof(struct geli_boot_args, gelipw)) {
+ if (gargs->gelipw[0] != '\0') {
+ setenv("kern.geom.eli.passphrase", gargs->gelipw, 1);
+ bzero(gargs->gelipw, sizeof(gargs->gelipw));
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+#endif /* LOADER_ZFS_SUPPORT */
/*
* March through the device switch probing for things.
@@ -214,7 +242,6 @@ extract_currdev(void)
struct i386_devdesc new_currdev;
#ifdef LOADER_ZFS_SUPPORT
char buf[20];
- struct zfs_boot_args *zargs;
#endif
int biosdev = -1;
diff --git a/sys/boot/i386/zfsboot/zfsboot.c b/sys/boot/i386/zfsboot/zfsboot.c
index 6fbbc6f..4b371dc 100644
--- a/sys/boot/i386/zfsboot/zfsboot.c
+++ b/sys/boot/i386/zfsboot/zfsboot.c
@@ -121,8 +121,10 @@ void exit(int);
static void load(void);
static int parse(void);
static void bios_getmem(void);
+void *malloc(size_t n);
+void free(void *ptr);
-static void *
+void *
malloc(size_t n)
{
char *p = heap_next;
@@ -130,10 +132,18 @@ malloc(size_t n)
printf("malloc failure\n");
for (;;)
;
- return 0;
+ /* NOTREACHED */
+ return (0);
}
heap_next += n;
- return p;
+ return (p);
+}
+
+void
+free(void *ptr)
+{
+
+ return;
}
static char *
@@ -141,9 +151,14 @@ strdup(const char *s)
{
char *p = malloc(strlen(s) + 1);
strcpy(p, s);
- return p;
+ return (p);
}
+#ifdef LOADER_GELI_SUPPORT
+#include "geliboot.c"
+static char gelipw[GELI_PW_MAXLEN];
+#endif
+
#include "zfsimpl.c"
/*
@@ -199,6 +214,14 @@ vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes)
nb = READ_BUF_SIZE / DEV_BSIZE;
if (drvread(dsk, dmadat->rdbuf, lba, nb))
return -1;
+#ifdef LOADER_GELI_SUPPORT
+ /* decrypt */
+ if (is_geli(dsk) == 0) {
+ if (geli_read(dsk, ((lba - dsk->start) * DEV_BSIZE),
+ dmadat->rdbuf, nb * DEV_BSIZE))
+ return (-1);
+ }
+#endif
memcpy(p, dmadat->rdbuf, nb * DEV_BSIZE);
p += nb * DEV_BSIZE;
lba += nb;
@@ -302,7 +325,7 @@ bios_getmem(void)
high_heap_size = HEAP_MIN;
high_heap_base = bios_extmem + 0x100000 - HEAP_MIN;
}
-}
+}
/*
* Try to detect a device supported by the legacy int13 BIOS
@@ -346,21 +369,39 @@ probe_drive(struct dsk *dsk)
#ifdef GPT
struct gpt_hdr hdr;
struct gpt_ent *ent;
- daddr_t slba, elba;
unsigned part, entries_per_sec;
#endif
+ daddr_t slba, elba;
struct dos_partition *dp;
char *sec;
unsigned i;
/*
- * If we find a vdev on the whole disk, stop here. Otherwise dig
- * out the partition table and probe each slice/partition
- * in turn for a vdev.
+ * If we find a vdev on the whole disk, stop here.
*/
if (vdev_probe(vdev_read, dsk, NULL) == 0)
return;
+#ifdef LOADER_GELI_SUPPORT
+ /*
+ * Taste the disk, if it is GELI encrypted, decrypt it and check to see if
+ * it is a usable vdev then. Otherwise dig
+ * out the partition table and probe each slice/partition
+ * in turn for a vdev or GELI encrypted vdev.
+ */
+ elba = drvsize(dsk);
+ if (elba > 0) {
+ elba--;
+ }
+ if (geli_taste(vdev_read, dsk, elba) == 0) {
+ if (geli_passphrase(&gelipw, dsk->unit, ':', 0, dsk) == 0) {
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ return;
+ }
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
+
sec = dmadat->secbuf;
dsk->start = 0;
@@ -383,6 +424,8 @@ probe_drive(struct dsk *dsk)
* 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;
@@ -396,6 +439,8 @@ probe_drive(struct dsk *dsk)
if (memcmp(&ent->ent_type, &freebsd_zfs_uuid,
sizeof(uuid_t)) == 0) {
dsk->start = ent->ent_lba_start;
+ dsk->slice = part + 1;
+ dsk->part = 255;
if (vdev_probe(vdev_read, dsk, NULL) == 0) {
/*
* This slice had a vdev. We need a new dsk
@@ -403,13 +448,31 @@ probe_drive(struct dsk *dsk)
*/
dsk = copy_dsk(dsk);
}
+#ifdef LOADER_GELI_SUPPORT
+ else if (geli_taste(vdev_read, dsk, ent->ent_lba_end -
+ ent->ent_lba_start) == 0) {
+ if (geli_passphrase(&gelipw, dsk->unit, 'p', dsk->slice, dsk) == 0) {
+ /*
+ * This slice has GELI, check it for ZFS.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+ break;
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
}
}
slba++;
}
return;
trymbr:
-#endif
+#endif /* GPT */
if (drvread(dsk, sec, DOSBBSECTOR, 1))
return;
@@ -419,13 +482,28 @@ trymbr:
if (!dp[i].dp_typ)
continue;
dsk->start = dp[i].dp_start;
+ dsk->slice = i + 1;
if (vdev_probe(vdev_read, dsk, NULL) == 0) {
- /*
- * This slice had a vdev. We need a new dsk structure now
- * since the vdev now owns this one.
- */
dsk = copy_dsk(dsk);
}
+#ifdef LOADER_GELI_SUPPORT
+ else if (geli_taste(vdev_read, dsk, dp[i].dp_size -
+ dp[i].dp_start) == 0) {
+ if (geli_passphrase(&gelipw, dsk->unit, 's', i, dsk) == 0) {
+ /*
+ * This slice has GELI, check it for ZFS.
+ */
+ if (vdev_probe(vdev_read, dsk, NULL) == 0) {
+ /*
+ * This slice had a vdev. We need a new dsk
+ * structure now since the vdev now owns this one.
+ */
+ dsk = copy_dsk(dsk);
+ }
+ break;
+ }
+ }
+#endif /* LOADER_GELI_SUPPORT */
}
}
@@ -445,8 +523,8 @@ main(void)
heap_end = PTOV(high_heap_base + high_heap_size);
heap_next = PTOV(high_heap_base);
} else {
- heap_next = (char *) dmadat + sizeof(*dmadat);
- heap_end = (char *) PTOV(bios_basemem);
+ heap_next = (char *)dmadat + sizeof(*dmadat);
+ heap_end = (char *)PTOV(bios_basemem);
}
dsk = malloc(sizeof(struct dsk));
@@ -472,6 +550,9 @@ main(void)
autoboot = 1;
+#ifdef LOADER_GELI_SUPPORT
+ geli_init();
+#endif
zfs_init();
/*
@@ -690,6 +771,12 @@ load(void)
zfsargs.pool = zfsmount.spa->spa_guid;
zfsargs.root = zfsmount.rootobj;
zfsargs.primary_pool = primary_spa->spa_guid;
+#ifdef LOADER_GELI_SUPPORT
+ bcopy(gelipw, zfsargs.gelipw, sizeof(zfsargs.gelipw));
+ bzero(gelipw, sizeof(gelipw));
+#else
+ zfsargs.gelipw[0] = '\0';
+#endif
if (primary_vdev != NULL)
zfsargs.primary_vdev = primary_vdev->v_guid;
else
OpenPOWER on IntegriCloud