From 19f122c291f7971f82fbc9d676741c76b36da567 Mon Sep 17 00:00:00 2001 From: Joel Sing Date: Wed, 31 Oct 2012 13:55:59 +0000 Subject: Bring softraid support to i386/stand/libsa. --- sys/arch/i386/stand/libsa/biosdev.c | 136 +++++--- sys/arch/i386/stand/libsa/dev_i386.c | 46 ++- sys/arch/i386/stand/libsa/disk.h | 14 +- sys/arch/i386/stand/libsa/diskprobe.c | 13 +- sys/arch/i386/stand/libsa/exec_i386.c | 31 +- sys/arch/i386/stand/libsa/gidt.S | 5 +- sys/arch/i386/stand/libsa/softraid.c | 567 ++++++++++++++++++++++++++++++++++ sys/arch/i386/stand/libsa/softraid.h | 34 ++ 8 files changed, 784 insertions(+), 62 deletions(-) create mode 100644 sys/arch/i386/stand/libsa/softraid.c create mode 100644 sys/arch/i386/stand/libsa/softraid.h (limited to 'sys/arch/i386') diff --git a/sys/arch/i386/stand/libsa/biosdev.c b/sys/arch/i386/stand/libsa/biosdev.c index 6a61ea47d78..73d4e78e8af 100644 --- a/sys/arch/i386/stand/libsa/biosdev.c +++ b/sys/arch/i386/stand/libsa/biosdev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: biosdev.c,v 1.84 2011/07/19 01:08:35 krw Exp $ */ +/* $OpenBSD: biosdev.c,v 1.85 2012/10/31 13:55:58 jsing Exp $ */ /* * Copyright (c) 1996 Michael Shalayeff @@ -31,14 +31,15 @@ #include #include #include -#include -#include -#include #include -#include "disk.h" +#include +#include +#include + +#include "biosdev.h" #include "debug.h" +#include "disk.h" #include "libsa.h" -#include "biosdev.h" static const char *biosdisk_err(u_int); static int biosdisk_errno(u_int); @@ -52,14 +53,6 @@ extern int debug; int bios_bootdev; int bios_cddev = -1; /* Set by srt0 if coming from CD */ -#if 0 -struct biosdisk { - bios_diskinfo_t *bios_info; - dev_t bsddev; - struct disklabel disklabel; -}; -#endif - struct EDD_CB { u_int8_t edd_len; /* size of packet */ u_int8_t edd_res1; /* reserved */ @@ -342,7 +335,7 @@ biosd_io(int rw, bios_diskinfo_t *bd, u_int off, int nsect, void *buf) } /* - * Try to read the bsd label on the given BIOS device + * Try to read the bsd label on the given BIOS device. */ static u_int findopenbsd(bios_diskinfo_t *bd, const char **err) @@ -458,11 +451,16 @@ bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label) int biosopen(struct open_file *f, ...) { - va_list ap; - register char *cp, **file; +#ifdef SOFTRAID + struct sr_boot_volume *bv; +#endif + register char *cp, **file; dev_t maj, unit, part; struct diskinfo *dip; - int biosdev; + int biosdev, devlen; + const char *st; + va_list ap; + char *dev; va_start(ap, f); cp = *(file = va_arg(ap, char **)); @@ -474,46 +472,93 @@ biosopen(struct open_file *f, ...) #endif f->f_devdata = NULL; - /* search for device specification */ - cp += 2; - if (cp[2] != ':') { - if (cp[3] != ':') - return ENOENT; - else - cp++; - } - for (maj = 0; maj < nbdevs && strncmp(*file, bdevs[maj], cp - *file); ) - maj++; - if (maj >= nbdevs) { - printf("Unknown device: "); - for (cp = *file; *cp != ':'; cp++) - putchar(*cp); - putchar('\n'); - return EADAPT; - } + /* Search for device specification. */ + dev = cp; + if (cp[4] == ':') + devlen = 2; + else if (cp[5] == ':') + devlen = 3; + else + return ENOENT; + cp += devlen; - /* get unit */ + /* Get unit. */ if ('0' <= *cp && *cp <= '9') unit = *cp++ - '0'; else { printf("Bad unit number\n"); return EUNIT; } - /* get partition */ + + /* Get partition. */ if ('a' <= *cp && *cp <= 'p') part = *cp++ - 'a'; else { - printf("Bad partition id\n"); + printf("Bad partition\n"); return EPART; } + /* Get filename. */ cp++; /* skip ':' */ if (*cp != 0) *file = cp; else f->f_flags |= F_RAW; +#ifdef SOFTRAID + /* Intercept softraid disks. */ + if (strncmp("sr", dev, 2) == 0) { + + /* Create a fake diskinfo for this softraid volume. */ + SLIST_FOREACH(bv, &sr_volumes, sbv_link) + if (bv->sbv_unit == unit) + break; + if (bv == NULL) { + printf("Unknown device: sr%d\n", unit); + return EADAPT; + } + + if (bv->sbv_level == 'C' && bv->sbv_keys == NULL) + sr_crypto_decrypt_keys(bv); + + if (bv->sbv_diskinfo == NULL) { + dip = alloc(sizeof(struct diskinfo)); + bzero(dip, sizeof(*dip)); + bv->sbv_diskinfo = dip; + dip->sr_vol = bv; + dip->bios_info.flags |= BDI_BADLABEL; + } + + dip = bv->sbv_diskinfo; + + if (dip->bios_info.flags & BDI_BADLABEL) { + /* Attempt to read disklabel. */ + bv->sbv_part = 'c'; + if (sr_getdisklabel(bv, &dip->disklabel)) + return ERDLAB; + dip->bios_info.flags &= ~BDI_BADLABEL; + } + + bv->sbv_part = part + 'a'; + + bootdev_dip = dip; + f->f_devdata = dip; + + return 0; + } +#endif + + for (maj = 0; maj < nbdevs && + strncmp(dev, bdevs[maj], devlen); maj++); + if (maj >= nbdevs) { + printf("Unknown device: "); + for (cp = *file; *cp != ':'; cp++) + putchar(*cp); + putchar('\n'); + return EADAPT; + } + biosdev = unit; switch (maj) { case 0: /* wd */ @@ -557,8 +602,7 @@ biosopen(struct open_file *f, ...) /* Try for disklabel again (might be removable media) */ if (dip->bios_info.flags & BDI_BADLABEL) { - const char *st = bios_getdisklabel(&dip->bios_info, - &dip->disklabel); + st = bios_getdisklabel(&dip->bios_info, &dip->disklabel); #ifdef BIOS_DEBUG if (debug && st) printf("%s\n", st); @@ -663,10 +707,14 @@ biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf, u_int8_t error = 0; size_t nsect; - nsect = (size + DEV_BSIZE-1) / DEV_BSIZE; - if (rsize != NULL) - blk += dip->disklabel. - d_partitions[B_PARTITION(dip->bsddev)].p_offset; +#ifdef SOFTRAID + /* Intercept strategy for softraid volumes. */ + if (dip->sr_vol) + return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize); +#endif + + nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; + blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset; /* Read all, sub-functions handle track boundaries */ if (blk < 0) diff --git a/sys/arch/i386/stand/libsa/dev_i386.c b/sys/arch/i386/stand/libsa/dev_i386.c index e855b579bbf..06d1e55a9b6 100644 --- a/sys/arch/i386/stand/libsa/dev_i386.c +++ b/sys/arch/i386/stand/libsa/dev_i386.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dev_i386.c,v 1.36 2012/10/30 14:06:29 jsing Exp $ */ +/* $OpenBSD: dev_i386.c,v 1.37 2012/10/31 13:55:58 jsing Exp $ */ /* * Copyright (c) 1996-1999 Michael Shalayeff @@ -26,11 +26,12 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#include "libsa.h" -#include "biosdev.h" #include #include +#include "libsa.h" +#include "biosdev.h" + extern int debug; /* XXX use slot for 'rd' for 'hd' pseudo-device */ @@ -89,6 +90,14 @@ devopen(struct open_file *f, const char *fname, char **file) void devboot(dev_t bootdev, char *p) { +#ifdef SOFTRAID + struct sr_boot_volume *bv; + struct sr_boot_chunk *bc; + struct diskinfo *dip = NULL; +#endif + int sr_boot_vol = -1; + int part_type = FS_UNUSED; + #ifdef _TEST *p++ = '/'; *p++ = 'd'; @@ -97,7 +106,36 @@ devboot(dev_t bootdev, char *p) *p++ = '/'; *p++ = 'r'; #endif - if (bootdev & 0x100) { + +#ifdef SOFTRAID + /* + * Determine the partition type for the 'a' partition of the + * boot device. + */ + TAILQ_FOREACH(dip, &disklist, list) + if (dip->bios_info.bios_number == bootdev && + (dip->bios_info.flags & BDI_BADLABEL) == 0) + part_type = dip->disklabel.d_partitions[0].p_fstype; + + /* + * See if we booted from a disk that is a member of a bootable + * softraid volume. + */ + SLIST_FOREACH(bv, &sr_volumes, sbv_link) { + if (bv->sbv_flags & BIOC_SCBOOTABLE) + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) + if (bc->sbc_disk == bootdev) + sr_boot_vol = bv->sbv_unit; + if (sr_boot_vol != -1) + break; + } +#endif + + if (sr_boot_vol != -1 && part_type != FS_BSDFFS) { + *p++ = 's'; + *p++ = 'r'; + *p++ = '0' + sr_boot_vol; + } else if (bootdev & 0x100) { *p++ = 'c'; *p++ = 'd'; *p++ = '0'; diff --git a/sys/arch/i386/stand/libsa/disk.h b/sys/arch/i386/stand/libsa/disk.h index a9633ca7294..e1ea7b262c6 100644 --- a/sys/arch/i386/stand/libsa/disk.h +++ b/sys/arch/i386/stand/libsa/disk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: disk.h,v 1.4 2004/03/09 19:12:13 tom Exp $ */ +/* $OpenBSD: disk.h,v 1.5 2012/10/31 13:55:58 jsing Exp $ */ /* * Copyright (c) 1997 Tobias Weingartner @@ -27,8 +27,8 @@ * */ -#ifndef _DISKPROBE_H -#define _DISKPROBE_H +#ifndef _DISK_H +#define _DISK_H #include @@ -36,6 +36,7 @@ struct diskinfo { bios_diskinfo_t bios_info; struct disklabel disklabel; + struct sr_boot_volume *sr_vol; dev_t bsddev, bootdev; @@ -43,9 +44,12 @@ struct diskinfo { }; TAILQ_HEAD(disklist_lh, diskinfo); -/* Head of this list */ +/* Disk info for boot device. */ extern struct diskinfo *bootdev_dip; +/* List of probed disks. */ +extern struct disklist_lh disklist; + void dump_diskinfo(void); -#endif /* _DISKPROBE_H */ +#endif /* _DISK_H */ diff --git a/sys/arch/i386/stand/libsa/diskprobe.c b/sys/arch/i386/stand/libsa/diskprobe.c index 4e0a121b9e1..16292b1f8bc 100644 --- a/sys/arch/i386/stand/libsa/diskprobe.c +++ b/sys/arch/i386/stand/libsa/diskprobe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: diskprobe.c,v 1.32 2010/04/23 15:25:20 jsing Exp $ */ +/* $OpenBSD: diskprobe.c,v 1.33 2012/10/31 13:55:58 jsing Exp $ */ /* * Copyright (c) 1997 Tobias Weingartner @@ -34,11 +34,12 @@ #include #include #include -#include -#include #include -#include "disk.h" +#include +#include + #include "biosdev.h" +#include "disk.h" #include "libsa.h" #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ @@ -183,6 +184,10 @@ diskprobe(void) #endif hardprobe(); +#ifdef SOFTRAID + srprobe(); +#endif + /* Checksumming of hard disks */ for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) ; diff --git a/sys/arch/i386/stand/libsa/exec_i386.c b/sys/arch/i386/stand/libsa/exec_i386.c index d587e4be4a1..49e644b562c 100644 --- a/sys/arch/i386/stand/libsa/exec_i386.c +++ b/sys/arch/i386/stand/libsa/exec_i386.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_i386.c,v 1.37 2012/10/30 14:06:29 jsing Exp $ */ +/* $OpenBSD: exec_i386.c,v 1.38 2012/10/31 13:55:58 jsing Exp $ */ /* * Copyright (c) 1997-1998 Michael Shalayeff @@ -29,13 +29,14 @@ */ #include +#include #include -#include +#include #include -#include +#include + #include "disk.h" #include "libsa.h" -#include typedef void (*startfuncp)(int, int, int, int, int, int, int, int) __attribute__ ((noreturn)); @@ -47,6 +48,9 @@ run_loadfile(u_long *marks, int howto) { u_long entry; #ifndef _TEST +#ifdef EXEC_DEBUG + extern int debug; +#endif dev_t bootdev = bootdev_dip->bootdev; size_t ac = BOOTARG_LEN; caddr_t av = (caddr_t)BOOTARG_OFF; @@ -56,6 +60,10 @@ run_loadfile(u_long *marks, int howto) bios_ddb_t ddb; extern int db_console; bios_bootduid_t bootduid; +#ifdef SOFTRAID + bios_bootsr_t bootsr; + struct sr_boot_volume *bv; +#endif if (sa_cleanup != NULL) (*sa_cleanup)(); @@ -77,6 +85,21 @@ run_loadfile(u_long *marks, int howto) bcopy(bootdev_dip->disklabel.d_uid, &bootduid.duid, sizeof(bootduid)); addbootarg(BOOTARG_BOOTDUID, sizeof(bootduid), &bootduid); +#ifdef SOFTRAID + if (bootdev_dip->sr_vol != NULL) { + bv = bootdev_dip->sr_vol; + bzero(&bootsr, sizeof(bootsr)); + bcopy(&bv->sbv_uuid, &bootsr.uuid, sizeof(bootsr.uuid)); + if (bv->sbv_maskkey != NULL) + bcopy(bv->sbv_maskkey, &bootsr.maskkey, + sizeof(bootsr.maskkey)); + addbootarg(BOOTARG_BOOTSR, sizeof(bios_bootsr_t), &bootsr); + explicit_bzero(&bootsr, sizeof(bootsr)); + } + + sr_clear_keys(); +#endif + /* Pass memory map to the kernel */ mem_pass(); diff --git a/sys/arch/i386/stand/libsa/gidt.S b/sys/arch/i386/stand/libsa/gidt.S index 06b35a8c084..830dfe4ff9b 100644 --- a/sys/arch/i386/stand/libsa/gidt.S +++ b/sys/arch/i386/stand/libsa/gidt.S @@ -1,4 +1,4 @@ -/* $OpenBSD: gidt.S,v 1.35 2012/10/09 12:39:12 jsing Exp $ */ +/* $OpenBSD: gidt.S,v 1.36 2012/10/31 13:55:58 jsing Exp $ */ /* * Copyright (c) 1997 Michael Shalayeff @@ -124,6 +124,9 @@ .globl _C_LABEL(_rtt) ENTRY(_rtt) +#ifdef SOFTRAID + call _C_LABEL(sr_clear_keys) +#endif #ifdef GIDT_DEBUG movl $0xb8000, %ebx movl $0x4f514f51, (%ebx) diff --git a/sys/arch/i386/stand/libsa/softraid.c b/sys/arch/i386/stand/libsa/softraid.c new file mode 100644 index 00000000000..a489db1ff94 --- /dev/null +++ b/sys/arch/i386/stand/libsa/softraid.c @@ -0,0 +1,567 @@ +/* $OpenBSD: softraid.c,v 1.1 2012/10/31 13:55:58 jsing Exp $ */ + +/* + * Copyright (c) 2012 Joel Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "libsa.h" +#include "biosdev.h" +#include "disk.h" +#include "softraid.h" + +/* List of softraid volumes. */ +struct sr_boot_volume_head sr_volumes; + +void +srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som) +{ + struct sr_meta_opt_hdr *omh; + struct sr_meta_opt_item *omi; +#if 0 + u_int8_t checksum[MD5_DIGEST_LENGTH]; +#endif + int i; + + /* Process optional metadata. */ + omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) + + sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no); + for (i = 0; i < sm->ssdi.ssd_opt_no; i++) { + +#ifdef BIOS_DEBUG + printf("Found optional metadata of type %u, length %u\n", + omh->som_type, omh->som_length); +#endif + + /* Unsupported old fixed length optional metadata. */ + if (omh->som_length == 0) { + omh = (struct sr_meta_opt_hdr *)((void *)omh + + omh->som_length); + continue; + } + + /* Load variable length optional metadata. */ + omi = alloc(sizeof(struct sr_meta_opt_item)); + bzero(omi, sizeof(struct sr_meta_opt_item)); + SLIST_INSERT_HEAD(som, omi, omi_link); + omi->omi_som = alloc(omh->som_length); + bzero(omi->omi_som, omh->som_length); + bcopy(omh, omi->omi_som, omh->som_length); + +#if 0 + /* XXX - Validate checksum. */ + bcopy(&omi->omi_som->som_checksum, &checksum, + MD5_DIGEST_LENGTH); + bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH); + sr_checksum(sc, omi->omi_som, + &omi->omi_som->som_checksum, omh->som_length); + if (bcmp(&checksum, &omi->omi_som->som_checksum, + sizeof(checksum))) + panic("%s: invalid optional metadata checksum", + DEVNAME(sc)); +#endif + + omh = (struct sr_meta_opt_hdr *)((void *)omh + + omh->som_length); + } +} + +void +srprobe(void) +{ + struct sr_boot_volume *bv, *bv1, *bv2; + struct sr_boot_chunk *bc, *bc1, *bc2; + struct sr_meta_chunk *mc; + struct sr_metadata *md; + struct diskinfo *dip; + struct partition *pp; + int i, error, volno; + dev_t bsd_dev; + daddr_t off; + + /* Probe for softraid volumes. */ + SLIST_INIT(&sr_volumes); + + md = alloc(SR_META_SIZE * 512); + + TAILQ_FOREACH(dip, &disklist, list) { + + /* Only check hard disks, skip those with I/O errors. */ + if ((dip->bios_info.bios_number & 0x80) == 0 || + (dip->bios_info.flags & BDI_INVALID)) + continue; + + /* Make sure disklabel has been read. */ + if ((dip->bios_info.flags & (BDI_BADLABEL|BDI_GOODLABEL)) == 0) + continue; + + for (i = 0; i < MAXPARTITIONS; i++) { + + pp = &dip->disklabel.d_partitions[i]; + if (pp->p_fstype != FS_RAID || pp->p_size == 0) + continue; + + /* Read softraid metadata. */ + bzero(md, SR_META_SIZE * 512); + off = DL_GETPOFFSET(pp) + SR_META_OFFSET; + error = biosd_io(F_READ, &dip->bios_info, off, + SR_META_SIZE, md); + if (error) + continue; + + /* Is this valid softraid metadata? */ + if (md->ssdi.ssd_magic != SR_MAGIC) + continue; + + /* XXX - validate checksum. */ + + /* Locate chunk-specific metadata for this chunk. */ + mc = (struct sr_meta_chunk *)(md + 1); + mc += md->ssdi.ssd_chunk_id; + + bc = alloc(sizeof(struct sr_boot_chunk)); + bc->sbc_diskinfo = dip; + bc->sbc_disk = dip->bios_info.bios_number; + bc->sbc_part = 'a' + i; + + bsd_dev = dip->bios_info.bsd_dev; + bc->sbc_mm = MAKEBOOTDEV(B_TYPE(bsd_dev), + B_ADAPTOR(bsd_dev), B_CONTROLLER(bsd_dev), + B_UNIT(bsd_dev), bc->sbc_part - 'a'); + + bc->sbc_chunk_id = md->ssdi.ssd_chunk_id; + bc->sbc_ondisk = md->ssd_ondisk; + bc->sbc_state = mc->scm_status; + + /* Handle key disks separately... later. */ + if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) + continue; + + SLIST_FOREACH(bv, &sr_volumes, sbv_link) { + if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid, + sizeof(md->ssdi.ssd_uuid)) == 0) + break; + } + + if (bv == NULL) { + bv = alloc(sizeof(struct sr_boot_volume)); + bv->sbv_diskinfo = NULL; + bv->sbv_keys = NULL; + bv->sbv_level = md->ssdi.ssd_level; + bv->sbv_volid = md->ssdi.ssd_volid; + bv->sbv_chunk_no = md->ssdi.ssd_chunk_no; + bv->sbv_flags = md->ssdi.ssd_vol_flags; + bv->sbv_size = md->ssdi.ssd_size; + bv->sbv_data_offset = md->ssd_data_offset; + bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid, + sizeof(md->ssdi.ssd_uuid)); + SLIST_INIT(&bv->sbv_chunks); + SLIST_INIT(&bv->sbv_meta_opt); + + /* Load optional metadata for this volume. */ + srprobe_meta_opt_load(md, &bv->sbv_meta_opt); + + /* Maintain volume order. */ + bv2 = NULL; + SLIST_FOREACH(bv1, &sr_volumes, sbv_link) { + if (bv1->sbv_volid > bv->sbv_volid) + break; + bv2 = bv1; + } + if (bv2 == NULL) + SLIST_INSERT_HEAD(&sr_volumes, bv, + sbv_link); + else + SLIST_INSERT_AFTER(bv2, bv, sbv_link); + } + + /* Maintain chunk order. */ + bc2 = NULL; + SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) { + if (bc1->sbc_chunk_id > bc->sbc_chunk_id) + break; + bc2 = bc1; + } + if (bc2 == NULL) + SLIST_INSERT_HEAD(&bv->sbv_chunks, + bc, sbc_link); + else + SLIST_INSERT_AFTER(bc2, bc, sbc_link); + + bv->sbv_chunks_found++; + } + } + + /* + * Assemble RAID volumes. + */ + volno = 0; + SLIST_FOREACH(bv, &sr_volumes, sbv_link) { + + /* Skip if this is a hotspare "volume". */ + if (bv->sbv_level == SR_HOTSPARE_LEVEL && + bv->sbv_chunk_no == 1) + continue; + + /* Determine current ondisk version. */ + bv->sbv_ondisk = 0; + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { + if (bc->sbc_ondisk > bv->sbv_ondisk) + bv->sbv_ondisk = bc->sbc_ondisk; + } + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { + if (bc->sbc_ondisk != bv->sbv_ondisk) + bc->sbc_state = BIOC_SDOFFLINE; + } + + /* XXX - Check for duplicate chunks. */ + + /* + * Validate that volume has sufficient chunks for + * read-only access. + * + * XXX - check chunk states. + */ + bv->sbv_state = BIOC_SVOFFLINE; + switch (bv->sbv_level) { + case 0: + case 'C': + case 'c': + if (bv->sbv_chunk_no == bv->sbv_chunks_found) + bv->sbv_state = BIOC_SVONLINE; + break; + + case 1: + if (bv->sbv_chunk_no == bv->sbv_chunks_found) + bv->sbv_state = BIOC_SVONLINE; + else if (bv->sbv_chunks_found > 0) + bv->sbv_state = BIOC_SVDEGRADED; + break; + } + + bv->sbv_unit = volno++; + if (bv->sbv_state != BIOC_SVOFFLINE) + printf(" sr%d%s", bv->sbv_unit, + bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : ""); + } + + if (md) + free(md, 0); +} + +int +sr_strategy(struct sr_boot_volume *bv, int rw, daddr32_t blk, size_t size, + void *buf, size_t *rsize) +{ + struct diskinfo *sr_dip, *dip; + struct sr_boot_chunk *bc; + struct aes_xts_ctx ctx; + size_t i, j, nsect; + daddr64_t blkno; + u_char iv[8]; + u_char *bp; + int err; + + /* We only support read-only softraid. */ + if (rw != F_READ) + return EPERM; + + /* Partition offset within softraid volume. */ + sr_dip = (struct diskinfo *)bv->sbv_diskinfo; + blk += sr_dip->disklabel.d_partitions[bv->sbv_part - 'a'].p_offset; + + if (bv->sbv_level == 0) { + return ENOTSUP; + } else if (bv->sbv_level == 1) { + + /* Select first online chunk. */ + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) + if (bc->sbc_state == BIOC_SDONLINE) + break; + if (bc == NULL) + return EIO; + + dip = (struct diskinfo *)bc->sbc_diskinfo; + dip->bsddev = bc->sbc_mm; + blk += bv->sbv_data_offset; + + /* XXX - If I/O failed we should try another chunk... */ + return biosstrategy(dip, rw, blk, size, buf, rsize); + + } else if (bv->sbv_level == 'C') { + + /* Select first online chunk. */ + SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) + if (bc->sbc_state == BIOC_SDONLINE) + break; + if (bc == NULL) + return EIO; + + dip = (struct diskinfo *)bc->sbc_diskinfo; + dip->bsddev = bc->sbc_mm; + + /* XXX - select correct key. */ + aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64); + + nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; + for (i = 0; i < nsect; i++) { + blkno = blk + i; + bp = ((u_char *)buf) + i * DEV_BSIZE; + err = biosstrategy(dip, rw, bv->sbv_data_offset + blkno, + DEV_BSIZE, bp, NULL); + if (err != 0) + return err; + + bcopy(&blkno, iv, sizeof(blkno)); + aes_xts_reinit(&ctx, iv); + for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE) + aes_xts_decrypt(&ctx, bp + j); + } + if (rsize != NULL) + *rsize = nsect * DEV_BSIZE; + + return err; + + } else + return ENOTSUP; +} + +const char * +sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label) +{ + struct dos_partition *dp; + struct dos_mbr mbr; + u_int start = 0; + char *buf; + int i; + + /* Check for MBR to determine partition offset. */ + bzero(&mbr, sizeof(mbr)); + sr_strategy(bv, F_READ, DOSBBSECTOR, sizeof(mbr), &mbr, NULL); + if (mbr.dmbr_sign == DOSMBR_SIGNATURE) { + + /* Search for OpenBSD partition */ + for (i = 0; i < NDOSPART; i++) { + dp = &mbr.dmbr_parts[i]; + if (!dp->dp_size) + continue; + if (dp->dp_typ == DOSPTYP_OPENBSD) { + if (dp->dp_start > (dp->dp_start + DOSBBSECTOR)) + continue; + start = dp->dp_start + DOSBBSECTOR; + } + } + } + + start += LABELSECTOR; + + /* Read the disklabel. */ + buf = alloca(DEV_BSIZE); + sr_strategy(bv, F_READ, start, sizeof(struct disklabel), buf, NULL); + +#if BIOS_DEBUG + printf("sr_getdisklabel: magic %lx\n", + ((struct disklabel *)buf)->d_magic); + for (i = 0; i < MAXPARTITIONS; i++) + printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i, + (int)((struct disklabel *)buf)->d_partitions[i].p_fstype, + (int)((struct disklabel *)buf)->d_partitions[i].p_size, + (int)((struct disklabel *)buf)->d_partitions[i].p_offset); +#endif + + /* Fill in disklabel */ + return (getdisklabel(buf, label)); +} + + +#define RIJNDAEL128_BLOCK_LEN 16 +#define PASSPHRASE_LENGTH 1024 + +#define SR_CRYPTO_KEYBLOCK_BYTES SR_CRYPTO_MAXKEYS * SR_CRYPTO_KEYBYTES + +#ifdef BIOS_DEBUG +void +printhex(const char *s, const u_int8_t *buf, size_t len) +{ + u_int8_t n1, n2; + size_t i; + + printf("%s: ", s); + for (i = 0; i < len; i++) { + n1 = buf[i] & 0x0f; + n2 = buf[i] >> 4; + printf("%c", n2 > 9 ? n2 + 'a' - 10 : n2 + '0'); + printf("%c", n1 > 9 ? n1 + 'a' - 10 : n1 + '0'); + } + printf("\n"); +} +#endif + +void +sr_clear_keys(void) +{ + struct sr_boot_volume *bv; + + SLIST_FOREACH(bv, &sr_volumes, sbv_link) { + if (bv->sbv_level != 'C') + continue; + if (bv->sbv_keys != NULL) { + explicit_bzero(bv->sbv_keys, SR_CRYPTO_KEYBLOCK_BYTES); + free(bv->sbv_keys, 0); + bv->sbv_keys = NULL; + } + if (bv->sbv_maskkey != NULL) { + explicit_bzero(bv->sbv_maskkey, SR_CRYPTO_MAXKEYBYTES); + free(bv->sbv_maskkey, 0); + bv->sbv_maskkey = NULL; + } + } +} + +void +sr_crypto_calculate_check_hmac_sha1(u_int8_t *maskkey, int maskkey_size, + u_int8_t *key, int key_size, u_char *check_digest) +{ + u_int8_t check_key[SHA1_DIGEST_LENGTH]; + SHA1_CTX shactx; + + explicit_bzero(check_key, sizeof(check_key)); + explicit_bzero(&shactx, sizeof(shactx)); + + /* k = SHA1(mask_key) */ + SHA1Init(&shactx); + SHA1Update(&shactx, maskkey, maskkey_size); + SHA1Final(check_key, &shactx); + + /* mac = HMAC_SHA1_k(unencrypted key) */ + hmac_sha1(key, key_size, check_key, sizeof(check_key), check_digest); + + explicit_bzero(check_key, sizeof(check_key)); + explicit_bzero(&shactx, sizeof(shactx)); +} + +int +sr_crypto_decrypt_keys(struct sr_boot_volume *bv) +{ + struct sr_meta_crypto *cm; + struct sr_meta_opt_item *omi; + struct sr_crypto_kdf_pbkdf2 *kdfhint; + struct sr_crypto_kdfinfo kdfinfo; + char passphrase[PASSPHRASE_LENGTH]; + u_int8_t digest[SHA1_DIGEST_LENGTH]; + u_int8_t *keys = NULL; + u_int8_t *kp, *cp; + rijndael_ctx ctx; + int rv = -1; + int c, i; + + SLIST_FOREACH(omi, &bv->sbv_meta_opt, omi_link) + if (omi->omi_som->som_type == SR_OPT_CRYPTO) + break; + + if (omi == NULL) { + printf("Crypto metadata not found!\n"); + goto done; + } + + cm = (struct sr_meta_crypto *)omi->omi_som; + kdfhint = (struct sr_crypto_kdf_pbkdf2 *)&cm->scm_kdfhint; + + switch (cm->scm_mask_alg) { + case SR_CRYPTOM_AES_ECB_256: + break; + default: + printf("unsupported encryption algorithm %u\n", + cm->scm_mask_alg); + goto done; + } + + printf("Passphrase: "); + i = 0; + for (i = 0; i < PASSPHRASE_LENGTH - 1; i++) { + c = cngetc(); + if (c == '\r' || c == '\n') + break; + passphrase[i] = (c & 0xff); + } + passphrase[i] = 0; + printf("\n"); + +#ifdef BIOS_DEBUG + printf("Got passphrase: %s with len %d\n", + passphrase, strlen(passphrase)); +#endif + + if (pkcs5_pbkdf2(passphrase, strlen(passphrase), kdfhint->salt, + sizeof(kdfhint->salt), kdfinfo.maskkey, sizeof(kdfinfo.maskkey), + kdfhint->rounds) != 0) { + printf("pbkdf2 failed\n"); + goto done; + } + + /* kdfinfo->maskkey now has key. */ + + /* Decrypt disk keys. */ + keys = alloc(SR_CRYPTO_KEYBLOCK_BYTES); + bzero(keys, SR_CRYPTO_KEYBLOCK_BYTES); + + if (rijndael_set_key(&ctx, kdfinfo.maskkey, 256) != 0) + goto done; + + cp = (u_int8_t *)cm->scm_key; + kp = keys; + for (i = 0; i < SR_CRYPTO_KEYBLOCK_BYTES; i += RIJNDAEL128_BLOCK_LEN) + rijndael_decrypt(&ctx, (u_char *)(cp + i), (u_char *)(kp + i)); + + /* Check that the key decrypted properly. */ + sr_crypto_calculate_check_hmac_sha1(kdfinfo.maskkey, + sizeof(kdfinfo.maskkey), keys, SR_CRYPTO_KEYBLOCK_BYTES, digest); + + if (bcmp(digest, cm->chk_hmac_sha1.sch_mac, sizeof(digest))) { + printf("incorrect passphrase\n"); + goto done; + } + + /* Keys will be cleared before boot and from _rtt. */ + bv->sbv_keys = keys; + bv->sbv_maskkey = alloc(sizeof(kdfinfo.maskkey)); + bcopy(&kdfinfo.maskkey, bv->sbv_maskkey, sizeof(kdfinfo.maskkey)); + + rv = 0; + +done: + explicit_bzero(passphrase, PASSPHRASE_LENGTH); + explicit_bzero(&kdfinfo, sizeof(kdfinfo)); + explicit_bzero(digest, sizeof(digest)); + + if (keys != NULL && rv != 0) { + explicit_bzero(keys, SR_CRYPTO_KEYBLOCK_BYTES); + free(keys, 0); + } + + return (rv); +} diff --git a/sys/arch/i386/stand/libsa/softraid.h b/sys/arch/i386/stand/libsa/softraid.h new file mode 100644 index 00000000000..58cd422c13c --- /dev/null +++ b/sys/arch/i386/stand/libsa/softraid.h @@ -0,0 +1,34 @@ +/* $OpenBSD: softraid.h,v 1.1 2012/10/31 13:55:58 jsing Exp $ */ + +/* + * Copyright (c) 2012 Joel Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SOFTRAID_H_ +#define _SOFTRAID_H_ + +void srprobe(void); + +const char *sr_getdisklabel(struct sr_boot_volume *, struct disklabel *); +int sr_strategy(struct sr_boot_volume *, int, daddr32_t, size_t, + void *, size_t *); + +int sr_crypto_decrypt_keys(struct sr_boot_volume *); +void sr_clear_keys(void); + +/* List of softraid volumes. */ +extern struct sr_boot_volume_head sr_volumes; + +#endif /* _SOFTRAID_H */ -- cgit v1.2.3