diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2013-09-28 19:25:26 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2013-09-28 19:25:26 +0000 |
commit | 56dfcffca0f05672d031f33b28e6c3a3fa199aa9 (patch) | |
tree | 41c3453f30fea19a6d0f2bee5db6224293b06077 /sys/arch | |
parent | 5db3fefa533b53cb804221b6cb737e3a69a13b74 (diff) |
The first step in being able to share disks with DG/UX: recognize VDM/VDIT
disk layout, and if a vdmpart instance named "OpenBSD" is found (but not a
vdmaggr!!!), assume this is the OpenBSD part of the disk and the native label
is found within it.
Native DG/UX partitions (vdmpart or vdmaggr) will NOT be reported in the
spoofed label of a shared disk.
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/aviion/aviion/disksubr.c | 283 | ||||
-rw-r--r-- | sys/arch/aviion/include/disklabel.h | 40 |
2 files changed, 290 insertions, 33 deletions
diff --git a/sys/arch/aviion/aviion/disksubr.c b/sys/arch/aviion/aviion/disksubr.c index c7d9c63ae21..f31f24dbc8c 100644 --- a/sys/arch/aviion/aviion/disksubr.c +++ b/sys/arch/aviion/aviion/disksubr.c @@ -1,7 +1,21 @@ -/* $OpenBSD: disksubr.c,v 1.51 2011/07/08 00:08:00 krw Exp $ */ -/* $NetBSD: disksubr.c,v 1.21 1996/05/03 19:42:03 christos Exp $ */ +/* $OpenBSD: disksubr.c,v 1.52 2013/09/28 19:25:24 miod Exp $ */ /* + * Copyright (c) 2013 Miodrag Vallat. + * + * 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. + */ +/* * Copyright (c) 1996 Theo de Raadt * Copyright (c) 1982, 1986, 1988 Regents of the University of California. * All rights reserved. @@ -36,6 +50,11 @@ #include <sys/buf.h> #include <sys/disklabel.h> #include <sys/disk.h> +#include <sys/malloc.h> + +char *extract_vdit_portion(char *, const char *, unsigned int, int); +int readvditlabel(struct buf *, void (*)(struct buf *), struct disklabel *, + int *, int); /* * Attempt to read a disk label from a device @@ -44,17 +63,10 @@ * secpercyl, secsize and anything required for a block i/o read * operation in the driver's strategy/start routines * must be filled in before calling us. - * - * If dos partition table requested, attempt to load it and - * find disklabel inside a DOS partition. - * - * We would like to check if each MBR has a valid DOSMBR_SIGNATURE, but - * we cannot because it doesn't always exist. So.. we assume the - * MBR is valid. */ int -readdisklabel(dev_t dev, void (*strat)(struct buf *), - struct disklabel *lp, int spoofonly) +readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, + int spoofonly) { struct buf *bp = NULL; int error; @@ -66,6 +78,10 @@ readdisklabel(dev_t dev, void (*strat)(struct buf *), bp = geteblk((int)lp->d_secsize); bp->b_dev = dev; + error = readvditlabel(bp, strat, lp, NULL, spoofonly); + if (error == 0) + goto done; + error = readdoslabel(bp, strat, lp, NULL, spoofonly); if (error == 0) goto done; @@ -105,13 +121,17 @@ writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp) bp = geteblk((int)lp->d_secsize); bp->b_dev = dev; - if (readdoslabel(bp, strat, lp, &partoff, 1) != 0) + if (readvditlabel(bp, strat, lp, &partoff, 1) == 0) { + bp->b_blkno = partoff + LABELSECTOR; + offset = LABELOFFSET; + } else if (readdoslabel(bp, strat, lp, &partoff, 1) == 0) { + bp->b_blkno = DL_BLKTOSEC(lp, partoff + DOS_LABELSECTOR) * + DL_BLKSPERSEC(lp); + offset = DL_BLKOFFSET(lp, partoff + DOS_LABELSECTOR); + } else goto done; /* Read it in, slap the new label in, and write it back out */ - bp->b_blkno = DL_BLKTOSEC(lp, partoff + DOS_LABELSECTOR) * - DL_BLKSPERSEC(lp); - offset = DL_BLKOFFSET(lp, partoff + DOS_LABELSECTOR); bp->b_bcount = lp->d_secsize; CLR(bp->b_flags, B_READ | B_WRITE | B_DONE); SET(bp->b_flags, B_BUSY | B_READ | B_RAW); @@ -134,3 +154,236 @@ done: disk_change = 1; return (error); } + +/* + * Search for a VDIT volume information. If one is found, search for a + * vdmpart instance of name "OpenBSD". If one is found, set the disklabel + * bounds to the area it spans, and attempt to read a native label within + * it. + */ +int +readvditlabel(struct buf *bp, void (*strat)(struct buf *), struct disklabel *lp, + int *partoffp, int spoofonly) +{ + struct buf *sbp = NULL; + struct vdm_label *vdl; + struct vdit_block_header *vbh; + struct vdit_entry_header *veh; + char *vdit_storage = NULL, *vdit_end; + size_t vdit_size, largest_chunk; + int expected_kind; + daddr_t blkno; + int error = 0; + vdit_id_t *vdmpart_id; + struct vdit_vdmpart_instance *bsd_vdmpart; + + /* + * Read first sector and check for a VDM label. + * Note that a VDM label is theoretically only required for bootable + * disks; but do disks with a VDIT but no VDM label really exist? + */ + + bp->b_blkno = VDM_LABEL_SECTOR; + bp->b_bcount = lp->d_secsize; + CLR(bp->b_flags, B_READ | B_WRITE | B_DONE); + SET(bp->b_flags, B_BUSY | B_READ | B_RAW); + (*strat)(bp); + if ((error = biowait(bp)) != 0) + return error; + + vdl = (struct vdm_label *)(bp->b_data + VDM_LABEL_OFFSET); + if (vdl->signature != VDM_LABEL_SIGNATURE) + vdl = (struct vdm_label *)(bp->b_data + VDM_LABEL_OFFSET_ALT); + if (vdl->signature != VDM_LABEL_SIGNATURE) + return EINVAL; + + /* + * Figure out the size of the first VDIT. + */ + + vdit_size = largest_chunk = 0; + expected_kind = VDIT_BLOCK_HEAD_BE; + blkno = VDIT_SECTOR; + do { + bp->b_blkno = blkno; + bp->b_bcount = lp->d_secsize; + CLR(bp->b_flags, B_READ | B_WRITE | B_DONE); + SET(bp->b_flags, B_BUSY | B_READ | B_RAW); + (*strat)(bp); + if ((error = biowait(bp)) != 0) + return error; + + vbh = (struct vdit_block_header *)bp->b_data; + if (VDM_ID_KIND(&vbh->id) != expected_kind) + return EINVAL; + + if (vbh->chunksz > largest_chunk) + largest_chunk = vbh->chunksz; + vdit_size += vbh->chunksz; + blkno = vbh->nextblk; + expected_kind = VDIT_PORTION_HEADER_BLOCK; + } while (vbh->nextblk != VDM_NO_BLK_NUMBER); + + /* + * Now read the first VDIT. + */ + + vdit_size *= dbtob(1) - sizeof(struct vdit_block_header); + vdit_storage = malloc(vdit_size, M_DEVBUF, M_WAITOK); + largest_chunk = dbtob(largest_chunk); + sbp = geteblk(largest_chunk); + sbp->b_dev = bp->b_dev; + + vdit_end = vdit_storage; + expected_kind = VDIT_BLOCK_HEAD_BE; + blkno = VDIT_SECTOR; + do { + sbp->b_blkno = blkno; + sbp->b_bcount = largest_chunk; + CLR(sbp->b_flags, B_READ | B_WRITE | B_DONE); + SET(sbp->b_flags, B_BUSY | B_READ | B_RAW); + (*strat)(sbp); + if ((error = biowait(sbp)) != 0) + goto done; + + vbh = (struct vdit_block_header *)sbp->b_data; + if (VDM_ID_KIND(&vbh->id) != expected_kind) { + error = EINVAL; + goto done; + } + + vdit_end = extract_vdit_portion(vdit_end, sbp->b_data, + vbh->chunksz, expected_kind); + if (vdit_end == NULL) { + error = EINVAL; + goto done; + } + blkno = vbh->nextblk; + expected_kind = VDIT_PORTION_HEADER_BLOCK; + } while (vbh->nextblk != VDM_NO_BLK_NUMBER); + + /* + * Now walk the VDIT entries. + * + * If we find an OpenBSD vdmpart, we'll set our disk area bounds to + * its area, and will read a label from there. + */ + + vdmpart_id = NULL; + bsd_vdmpart = NULL; + + veh = (struct vdit_entry_header *)vdit_storage; + while ((caddr_t)veh < vdit_end) { + switch (veh->type) { + case VDIT_ENTRY_SUBDRIVER_INFO: + { + struct vdit_subdriver_entry *vse; + + vse = (struct vdit_subdriver_entry *)(veh + 1); + if (strcmp(vse->name, VDM_SUBDRIVER_VDMPART) == 0) + vdmpart_id = &vse->subdriver_id; + } + break; + case VDIT_ENTRY_INSTANCE: + { + struct vdit_instance_entry *vie; + + vie = (struct vdit_instance_entry *)(veh + 1); + if (strcmp(vie->name, VDM_INSTANCE_OPENBSD) == 0) { + if (vdmpart_id != NULL && + memcmp(vdmpart_id, &vie->subdriver_id, + sizeof(vdit_id_t)) == 0) { + /* found it! */ + if (bsd_vdmpart != NULL) { + bsd_vdmpart = NULL; + veh->type = VDIT_ENTRY_SENTINEL; + } else + bsd_vdmpart = (struct + vdit_vdmpart_instance *)vie; + } + } + } + break; + } + if (veh->type == VDIT_ENTRY_SENTINEL) + break; + veh = (struct vdit_entry_header *)((char *)veh + veh->size); + } + + if (bsd_vdmpart != NULL) { + uint32_t start, size; + + memcpy(&start, &bsd_vdmpart->start_blkno, sizeof(uint32_t)); + memcpy(&size, &bsd_vdmpart->size, sizeof(uint32_t)); + + if (start >= DL_GETDSIZE(lp) || + start + size > DL_GETDSIZE(lp)) { + error = EINVAL; + goto done; + } + + if (partoffp != NULL) { + *partoffp = start; + goto done; + } + DL_SETBSTART(lp, start); + DL_SETBEND(lp, start + size); + + /* + * Now read the native label. + */ + + if (spoofonly == 0) { + bp->b_blkno = start + LABELSECTOR; + bp->b_bcount = lp->d_secsize; + CLR(bp->b_flags, B_READ | B_WRITE | B_DONE); + SET(bp->b_flags, B_BUSY | B_READ | B_RAW); + (*strat)(bp); + if ((error = biowait(bp)) != 0) + goto done; + + checkdisklabel(bp->b_data + LABELOFFSET, lp, + start, start + size); + } + } else { + /* + * VDM label, but no OpenBSD vdmpart partition found. + * XXX is it worth registering the whole disk as a + * XXX `don't touch' vendor partition in that case? + */ + error = EINVAL; + goto done; + } + +done: + free(vdit_storage, M_DEVBUF); + if (sbp != NULL) { + sbp->b_flags |= B_INVAL; + brelse(sbp); + } + + return error; +} + +/* + * Process a contiguous chunk of VDIT, verifying and removing each block header + * as we go. + */ +char * +extract_vdit_portion(char *dst, const char *src, unsigned int nsec, int kind) +{ + struct vdit_block_header *vbh; + + for (; nsec != 0; nsec--) { + vbh = (struct vdit_block_header *)src; + if (VDM_ID_KIND(&vbh->id) != kind) + return NULL; + kind = VDIT_BLOCK; + + memcpy(dst, src + sizeof *vbh, dbtob(1) - sizeof *vbh); + dst += dbtob(1) - sizeof *vbh; + src += dbtob(1); + } + + return dst; +} diff --git a/sys/arch/aviion/include/disklabel.h b/sys/arch/aviion/include/disklabel.h index 611bfe9b85e..06a2609bd69 100644 --- a/sys/arch/aviion/include/disklabel.h +++ b/sys/arch/aviion/include/disklabel.h @@ -1,4 +1,4 @@ -/* $OpenBSD: disklabel.h,v 1.11 2011/03/23 16:54:34 pirofti Exp $ */ +/* $OpenBSD: disklabel.h,v 1.12 2013/09/28 19:25:25 miod Exp $ */ /* * Copyright (c) 2010 Miodrag Vallat. @@ -27,19 +27,19 @@ * AViiON native disk identification */ -#define VDM_SIGNATURE 0x1234abcd +#define VDM_LABEL_SIGNATURE 0x1234abcd -#define VDM_DISK_VERIFICATION_SECTOR 0 -#define VDM_DISK_VERIFICATION_OFFSET 0x1c8 -#define VDM_DISK_VERIFICATION_OFFSET_ALT 0x1c0 +#define VDM_LABEL_SECTOR 0 +#define VDM_LABEL_OFFSET 0x1c8 +#define VDM_LABEL_OFFSET_ALT 0x1c0 -struct vdm_disk_verification { +struct vdm_label { uint32_t signature; uint32_t version; uint32_t unused[2]; }; -#define VDM_DISK_VERSION 0 +#define VDM_LABEL_VERSION 0 struct vdm_boot_info { uint32_t padding[6]; @@ -49,21 +49,21 @@ struct vdm_boot_info { uint32_t version; }; -#define VDM_BOOT_INFO_VERSION 1 -#define VDM_BOOT_DEFAULT_SIZE 500 +#define VDM_BOOT_INFO_VERSION 1 +#define VDM_BOOT_DEFAULT_SIZE 500 /* * MBR identification information is in <sys/disklabel.h> */ -/* DG/UX VDM partition type */ -#define DOSPTYP_DGUX_VDM 0xdf +/* DG/UX VDM partition type (apparently not used on m88k AViiON) */ +#define DOSPTYP_DGUX_VDM 0xdf /* * DG/UX VDM structures */ -#define VDIT_SECTOR 1 +#define VDIT_SECTOR 1 struct vdm_self_id { union { @@ -77,13 +77,14 @@ struct vdm_self_id { #define VDM_BLKNO_MASK 0x00ffffff /* low 24 bits */ #define VDM_ID_BLKNO(id) ((id)->u._blkno) & VDM_BLKNO_MASK) #define VDM_NO_NODE_NUMBER 012345670123 +#define VDM_NO_BLK_NUMBER 0xffffffff #define VDIT_BLOCK 0x12 #define VDIT_PORTION_HEADER_BLOCK 0x13 #define VDIT_BLOCK_HEAD_BE 0x14 #define VDIT_BLOCK_HEAD_LE 0x18 -struct vdit_block_header { +struct vdit_block_header { struct vdm_self_id id; uint32_t nextblk; uint32_t timestamp; @@ -92,10 +93,13 @@ struct vdit_block_header { uint16_t padding; } __packed; +typedef uint32_t vdit_timestamp_t; +typedef uint32_t vdit_id_t; + struct vdit_entry_header { uint16_t type; uint16_t size; - uint32_t timestamp; + vdit_timestamp_t timestamp; } __packed; #define VDIT_ENTRY_SENTINEL 0x00 @@ -107,8 +111,8 @@ struct vdit_entry_header { #define VDIT_NAME_MAX 0x20 struct vdit_instance_id { - uint32_t generation_timestamp; - uint32_t system_id; + vdit_timestamp_t generation_timestamp; + vdit_id_t system_id; } __packed; struct vdit_boot_info_entry { @@ -119,7 +123,7 @@ struct vdit_boot_info_entry { struct vdit_subdriver_entry { uint16_t version; - uint32_t subdriver_id; + vdit_id_t subdriver_id; char name[VDIT_NAME_MAX]; } __packed; @@ -131,7 +135,7 @@ struct vdit_subdriver_entry { struct vdit_instance_entry { uint16_t version; char name[VDIT_NAME_MAX]; - uint32_t subdriver_id; + vdit_id_t subdriver_id; struct vdit_instance_id instance_id; uint8_t exported; } __packed; |