summaryrefslogtreecommitdiff
path: root/sys/arch/i386/stand
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2012-10-31 13:55:59 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2012-10-31 13:55:59 +0000
commit19f122c291f7971f82fbc9d676741c76b36da567 (patch)
tree694280e958b2c243b347adc5f73a1fac1ca47453 /sys/arch/i386/stand
parent789ec68e0cb697f025719ba41ee5869d977aa85a (diff)
Bring softraid support to i386/stand/libsa.
Diffstat (limited to 'sys/arch/i386/stand')
-rw-r--r--sys/arch/i386/stand/libsa/biosdev.c136
-rw-r--r--sys/arch/i386/stand/libsa/dev_i386.c46
-rw-r--r--sys/arch/i386/stand/libsa/disk.h14
-rw-r--r--sys/arch/i386/stand/libsa/diskprobe.c13
-rw-r--r--sys/arch/i386/stand/libsa/exec_i386.c31
-rw-r--r--sys/arch/i386/stand/libsa/gidt.S5
-rw-r--r--sys/arch/i386/stand/libsa/softraid.c567
-rw-r--r--sys/arch/i386/stand/libsa/softraid.h34
8 files changed, 784 insertions, 62 deletions
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 <sys/param.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
-#include <machine/tss.h>
-#include <machine/biosvar.h>
-#include <lib/libsa/saerrno.h>
#include <isofs/cd9660/iso.h>
-#include "disk.h"
+#include <lib/libsa/saerrno.h>
+#include <machine/biosvar.h>
+#include <machine/tss.h>
+
+#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 <sys/param.h>
#include <dev/cons.h>
+#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 <sys/queue.h>
@@ -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 <sys/queue.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
-#include <stand/boot/bootarg.h>
-#include <machine/biosvar.h>
#include <lib/libz/zlib.h>
-#include "disk.h"
+#include <machine/biosvar.h>
+#include <stand/boot/bootarg.h>
+
#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 <sys/param.h>
+#include <sys/disklabel.h>
#include <dev/cons.h>
-#include <stand/boot/bootarg.h>
+#include <lib/libsa/loadfile.h>
#include <machine/biosvar.h>
-#include <sys/disklabel.h>
+#include <stand/boot/bootarg.h>
+
#include "disk.h"
#include "libsa.h"
-#include <lib/libsa/loadfile.h>
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 <jsing@openbsd.org>
+ *
+ * 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 <sys/param.h>
+#include <sys/queue.h>
+#include <sys/disklabel.h>
+#include <sys/reboot.h>
+
+#include <dev/biovar.h>
+#include <dev/softraidvar.h>
+
+#include <lib/libsa/aes_xts.h>
+#include <lib/libsa/hmac_sha1.h>
+#include <lib/libsa/pbkdf2.h>
+#include <lib/libsa/rijndael.h>
+
+#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 <jsing@openbsd.org>
+ *
+ * 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 */