summaryrefslogtreecommitdiff
path: root/sys/arch/amd64
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2012-01-11 14:47:03 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2012-01-11 14:47:03 +0000
commit11c62b32113e88e6569071d0c1e18ac8418336d0 (patch)
tree7dff992b099305b43b91b484ed8c67f7f66d4f10 /sys/arch/amd64
parent8be10ea4f64de84f03dcc10b3e013d821b737a2d (diff)
Teach OpenBSD/amd64 boot(8) how to access softraid(8) volumes. This
allows a kernel to be loaded from a softraid RAID 1 volume. Furthermore, if you boot from a disk that is a member of a bootable softraid volume then it will default to booting from the softraid volume (sr[0-9]a:/bsd). Discussed with deraadt@
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r--sys/arch/amd64/stand/libsa/biosdev.c211
-rw-r--r--sys/arch/amd64/stand/libsa/dev_i386.c35
-rw-r--r--sys/arch/amd64/stand/libsa/disk.h9
-rw-r--r--sys/arch/amd64/stand/libsa/diskprobe.c190
4 files changed, 397 insertions, 48 deletions
diff --git a/sys/arch/amd64/stand/libsa/biosdev.c b/sys/arch/amd64/stand/libsa/biosdev.c
index a8950c2375e..1b913f3b5eb 100644
--- a/sys/arch/amd64/stand/libsa/biosdev.c
+++ b/sys/arch/amd64/stand/libsa/biosdev.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: biosdev.c,v 1.16 2011/07/19 01:08:35 krw Exp $ */
+/* $OpenBSD: biosdev.c,v 1.17 2012/01/11 14:47:02 jsing Exp $ */
/*
* Copyright (c) 1996 Michael Shalayeff
@@ -31,10 +31,12 @@
#include <sys/param.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
+#include <dev/biovar.h>
+#include <dev/softraidvar.h>
+#include <isofs/cd9660/iso.h>
+#include <lib/libsa/saerrno.h>
#include <machine/tss.h>
#include <machine/biosvar.h>
-#include <lib/libsa/saerrno.h>
-#include <isofs/cd9660/iso.h>
#include "disk.h"
#include "libsa.h"
#include "biosdev.h"
@@ -47,18 +49,14 @@ static int EDD_rw (int, int, u_int32_t, u_int32_t, void *);
static u_int findopenbsd(bios_diskinfo_t *, const char **);
+static const char *sr_getdisklabel(struct sr_boot_volume *, struct disklabel *);
+static int sr_strategy(struct sr_boot_volume *, int, daddr32_t, size_t,
+ void *, size_t *);
+
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 */
@@ -341,7 +339,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)
@@ -457,11 +455,14 @@ bios_getdisklabel(bios_diskinfo_t *bd, struct disklabel *label)
int
biosopen(struct open_file *f, ...)
{
- va_list ap;
- register char *cp, **file;
+ struct sr_boot_volume *bv;
+ 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 **));
@@ -473,46 +474,87 @@ 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;
+ /* 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_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;
+ }
+
+ 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 */
@@ -556,8 +598,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);
@@ -662,10 +703,12 @@ biosstrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
u_int8_t error = 0;
size_t nsect;
+ /* Intercept strategy for softraid volumes. */
+ if (dip->sr_vol)
+ return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
+
nsect = (size + DEV_BSIZE-1) / DEV_BSIZE;
- if (rsize != NULL)
- blk += dip->disklabel.
- d_partitions[B_PARTITION(dip->bsddev)].p_offset;
+ blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;
/* Read all, sub-functions handle track boundaries */
if (blk < 0)
@@ -700,3 +743,91 @@ biosioctl(struct open_file *f, u_long cmd, void *data)
{
return 0;
}
+
+static 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;
+
+ /* 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') {
+ printf("mmmm... crypto!\n");
+ return ENOTSUP;
+ } else
+ return ENOTSUP;
+}
+
+static 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(struct dos_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));
+}
diff --git a/sys/arch/amd64/stand/libsa/dev_i386.c b/sys/arch/amd64/stand/libsa/dev_i386.c
index b5c5f98a1de..4b762562a79 100644
--- a/sys/arch/amd64/stand/libsa/dev_i386.c
+++ b/sys/arch/amd64/stand/libsa/dev_i386.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dev_i386.c,v 1.8 2011/06/26 23:19:11 tedu Exp $ */
+/* $OpenBSD: dev_i386.c,v 1.9 2012/01/11 14:47:02 jsing Exp $ */
/*
* Copyright (c) 1996-1999 Michael Shalayeff
@@ -26,10 +26,15 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "libsa.h"
-#include "biosdev.h"
#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/disklabel.h>
#include <dev/cons.h>
+#include <dev/biovar.h>
+#include <dev/softraidvar.h>
+#include "libsa.h"
+#include "biosdev.h"
+#include "disk.h"
extern int debug;
@@ -89,6 +94,10 @@ devopen(struct open_file *f, const char *fname, char **file)
void
devboot(dev_t bootdev, char *p)
{
+ struct sr_boot_volume *bv;
+ struct sr_boot_chunk *bc;
+ int sr_boot_vol = -1;
+
#ifdef _TEST
*p++ = '/';
*p++ = 'd';
@@ -97,7 +106,25 @@ devboot(dev_t bootdev, char *p)
*p++ = '/';
*p++ = 'r';
#endif
- if (bootdev & 0x100) {
+
+ /*
+ * 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;
+ }
+
+ if (sr_boot_vol != -1) {
+ *p++ = 's';
+ *p++ = 'r';
+ *p++ = '0' + sr_boot_vol;
+ } else if (bootdev & 0x100) {
*p++ = 'c';
*p++ = 'd';
*p++ = '0';
diff --git a/sys/arch/amd64/stand/libsa/disk.h b/sys/arch/amd64/stand/libsa/disk.h
index e95a3642924..efb84c603d4 100644
--- a/sys/arch/amd64/stand/libsa/disk.h
+++ b/sys/arch/amd64/stand/libsa/disk.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: disk.h,v 1.1 2004/02/03 12:09:47 mickey Exp $ */
+/* $OpenBSD: disk.h,v 1.2 2012/01/11 14:47:02 jsing Exp $ */
/*
* Copyright (c) 1997 Tobias Weingartner
@@ -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,10 +44,12 @@ struct diskinfo {
};
TAILQ_HEAD(disklist_lh, diskinfo);
-/* Head of this list */
+/* Head of this list. */
extern struct diskinfo *bootdev_dip;
+/* List of softraid volumes. */
+extern struct sr_boot_volume_head sr_volumes;
+
void dump_diskinfo(void);
#endif /* _DISKPROBE_H */
-
diff --git a/sys/arch/amd64/stand/libsa/diskprobe.c b/sys/arch/amd64/stand/libsa/diskprobe.c
index e8cdf8e7e7b..4a9f80597ed 100644
--- a/sys/arch/amd64/stand/libsa/diskprobe.c
+++ b/sys/arch/amd64/stand/libsa/diskprobe.c
@@ -1,7 +1,8 @@
-/* $OpenBSD: diskprobe.c,v 1.8 2010/04/23 15:25:20 jsing Exp $ */
+/* $OpenBSD: diskprobe.c,v 1.9 2012/01/11 14:47:02 jsing Exp $ */
/*
* Copyright (c) 1997 Tobias Weingartner
+ * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,6 +35,8 @@
#include <sys/queue.h>
#include <sys/reboot.h>
#include <sys/disklabel.h>
+#include <dev/biovar.h>
+#include <dev/softraidvar.h>
#include <stand/boot/bootarg.h>
#include <machine/biosvar.h>
#include <lib/libz/zlib.h>
@@ -49,6 +52,9 @@ static int disksum(int);
/* List of disk devices we found/probed */
struct disklist_lh disklist;
+/* List of softraid volumes. */
+struct sr_boot_volume_head sr_volumes;
+
/* Pointer to boot device */
struct diskinfo *bootdev_dip;
@@ -160,6 +166,186 @@ hardprobe(void)
}
+static 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;
+
+ /* XXX - extract necessary optional metadata. */
+
+ 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_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);
+
+ /* 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);
+}
+
+
/* Probe for all BIOS supported disks */
u_int32_t bios_cksumlen;
void
@@ -182,6 +368,8 @@ diskprobe(void)
#endif
hardprobe();
+ srprobe();
+
/* Checksumming of hard disks */
for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
;