freebsd-skq/sys/dev/slice/mbr.c
Julian Elischer 9294f850e8 Don't return an error if the magic number is not present.
Failing to probe is not an error, just a negative result.
1998-07-22 19:37:02 +00:00

726 lines
19 KiB
C

/*-
* Copyright (C) 1997,1998 Julian Elischer. All rights reserved.
* julian@freebsd.org
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: mbr.c,v 1.8 1998/07/20 04:12:39 julian Exp $
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/fcntl.h>
#include <sys/disklabel.h>
#include <sys/dkstat.h>
#include <sys/malloc.h>
#include <sys/sliceio.h>
#include <dev/slice/slice.h>
struct private_data {
u_int32_t flags;
struct slice *slice_down;
int savedoflags;
/* struct buf *bp; */
u_int32_t table_offset;
struct dos_partition dos_table[NDOSPART];
struct subdev {
int part;
struct slice *slice;
struct slicelimits limit;
struct private_data *pd;
u_int32_t offset; /* Fdisk only has 32 bits */
} subdevs[NDOSPART];
};
/*
* Bits in the mbr private data flag word
*/
#define MBRF_OPEN_RBIT 0x01
#define MBRF_S1_OPEN_RD 0x01
#define MBRF_S2_OPEN_RD 0x02
#define MBRF_S3_OPEN_RD 0x04
#define MBRF_S4_OPEN_RD 0x08
#define MBRF_MSK_RD 0x0F
#define MBRF_OPEN_WBIT 0x10
#define MBRF_S1_OPEN_WR 0x10
#define MBRF_S2_OPEN_WR 0x20
#define MBRF_S3_OPEN_WR 0x40
#define MBRF_S4_OPEN_WR 0x80
#define MBRF_MSK_WR 0xF0
#define MBRF_MSK_OPEN 0xFF
#define DOSPTYP_ONTRACK 84
static sl_h_IO_req_t mbr_IOreq; /* IO req downward (to device) */
static sl_h_ioctl_t mbr_ioctl; /* ioctl req downward (to device) */
static sl_h_open_t mbr_open; /* downwards travelling open */
/*static sl_h_close_t mbr_close; */ /* downwards travelling close */
static sl_h_claim_t mbr_claim; /* upwards travelling claim */
static sl_h_revoke_t mbr_revoke;/* upwards travelling revokation */
static sl_h_verify_t mbr_verify;/* things changed, are we stil valid? */
static sl_h_upconfig_t mbr_upconfig;/* config request from below */
static sl_h_dump_t mbr_dump; /* core dump req downward */
static sl_h_done_t mbr_done; /* callback after async request */
static struct slice_handler slicetype = {
"MBR",
0,
NULL,
0,
&mbr_done,
&mbr_IOreq,
&mbr_ioctl,
&mbr_open,
/*&mbr_close*/NULL,
&mbr_revoke, /* revoke */
&mbr_claim, /* claim */
&mbr_verify, /* verify */
&mbr_upconfig, /* config from below */
&mbr_dump
};
static void
sd_drvinit(void *unused)
{
sl_newtype(&slicetype);
}
SYSINIT(sddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sd_drvinit, NULL);
/*
* Allocate and the private data.
*/
static int
mbrallocprivate(sl_p slice)
{
register struct private_data *pd;
pd = malloc(sizeof(*pd), M_DEVBUF, M_NOWAIT);
if (pd == NULL) {
printf("mbr: failed malloc\n");
return (ENOMEM);
}
bzero(pd, sizeof(*pd));
pd->slice_down = slice;
slice->refs++;
slice->handler_up = &slicetype;
slice->private_up = pd;
slicetype.refs++;
return (0);
}
static int
mbr_claim(sl_p slice)
{
int error = 0;
/*
* Don't even BOTHER if it's not 512 byte sectors
*/
if (slice->limits.blksize != 512)
return (EINVAL);
if (slice->private_up == NULL) {
if ((error = mbrallocprivate(slice))) {
return (error);
}
}
if ((error = slice_request_block(slice, 0))) {
mbr_revoke(slice->private_up);
}
return (error);
}
static int
mbr_verify(sl_p slice)
{
int error = 0;
/*
* Don't even BOTHER if it's not 512 byte sectors
*/
if (slice->limits.blksize != 512)
return (EINVAL);
if ((error = slice_request_block(slice, 0))) {
mbr_revoke(slice->private_up);
}
return (error);
}
/*
* called with an argument of a bp when it is completed
*/
static int
mbr_done(sl_p slice, struct buf *bp)
{
struct private_data *pd;
struct dos_partition table[NDOSPART];
struct dos_partition *dp0, *dp, *dp2;
u_int8_t *cp;
int part;
int numactive = 0;
int i;
char name[64];
int error = 0;
RR;
/*
* Discover whether the IO was successful.
*/
pd = slice->private_up;
if ( bp->b_flags & B_ERROR ) {
error = bp->b_error;
bp->b_flags |= B_INVAL | B_AGE;
brelse(bp);
if (bootverbose)
printf ("failed.. IO error ");
goto nope;
}
cp = bp->b_data;
if (cp[0x1FE] != 0x55 || cp[0x1FF] != 0xAA) {
bp->b_flags |= B_INVAL | B_AGE;
brelse(bp);
if (bootverbose)
printf ("rejected.. bad magic ");
goto nope;
}
dp0 = (struct dos_partition *) (cp + DOSPARTOFF);
/* copy the table out of the buf and release it. */
bcopy(dp0, table, sizeof(table));
bp->b_flags |= B_INVAL | B_AGE;
brelse(bp);
/*
* Check for "Ontrack Diskmanager". Note that if the geometry is
* still needed then we probably won't be able to read a DiskManager
* MBR because we will fail to read sector 63. The very act of
* finding a Disk Manager might however have given us the info we
* need if the disk manager set's its partition up correctly.
* XXX not true with interrupt driven probes.
*/
if (pd->table_offset == 0) {
for (part = 0, dp = table;
part < NDOSPART; part++, dp++) {
if (dp->dp_typ == DOSPTYP_ONTRACK) {
#ifdef MAYBE
/*
* It's not known if this should always 63 or
* if this is just the start of the 2nd
* track.
*/
pd->table_offset = dp->dp_start;
#else
pd->table_offset = 63;
#endif
if (bootverbose)
printf("Found \"Ontrack Disk Manager\"\n");
slice_request_block(slice, pd->table_offset);
return (0);
}
}
}
/*
* The first block of the dos code is marked like a valid MBR.
* Try to distinguish this case by doing a sanity check on the table.
* Check:
* Flag byte can only be 0 or 0x80.
* At most one active partition.
* -Other tests to be added here-
*/
for (part = 0, dp = table; part < NDOSPART; part++, dp++) {
if (dp->dp_flag & 0x7f) {
if (bootverbose)
printf ("rejected.. bad flag ");
goto nope;
}
if ((dp->dp_typ) && (dp->dp_size) && (dp->dp_start == 0)) {
if (bootverbose)
printf("rejected.. Slice includes MBR ");
goto nope;
}
if (dp->dp_flag == 0x80)
numactive++;
}
if (numactive > 1) {
if (bootverbose)
printf ("rejected.. multiple active ");
goto nope;
}
/*-
* Handle the case when we are being asked to reevaluate
* an already loaded mbr table.
* We've already handled the case when it's completely vanished.
*
* Look at a slice that USED to be ours.
* Decide if any sub-slices need to be revoked.
* For each existing subslice, check that the basic size
* and position has not changed. Also check the TYPE.
* If not then at least ask them to verify themselves.
* It is possible we should allow a slice to grow.
*/
dp = pd->dos_table;
dp0 = pd->dos_table;
dp2 = table;
for (part = 0; part < NDOSPART; part++, dp++, dp2++) {
if (pd->subdevs[part].slice) {
if ((dp2->dp_start != dp->dp_start)
|| (dp2->dp_size != dp->dp_size)
|| (dp2->dp_typ != dp->dp_typ) ) {
sl_rmslice(pd->subdevs[part].slice);
pd->subdevs[part].slice = NULL;
} else if (pd->subdevs[part].slice->handler_up) {
(*pd->subdevs[part].slice->handler_up->verify)
(pd->subdevs[part].slice);
}
}
}
/*
* Having got rid of changing slices, replace
* the old table with the new one.
*/
bcopy( table, pd->dos_table, sizeof(table));
/*
* Handle each of the partitions.
* We should check that each makes sense and is legal.
* 1/ it should not already have a slice.
* 2/ should not be 0 length.
* 3/ should not go past end of our slice.
* 4/ should not include sector 0.
* 5/ should not overlap other slices.
*
* Be aware that this may queue up one (or more) IO requests
* for each subslice created.
*/
if (bootverbose)
printf("yep \n");
dp = dp0;
for (part = 0; part < NDOSPART; part++, dp++) {
int i;
if (pd->subdevs[part].slice != NULL)
breakout: continue;
if (dp->dp_size == 0)
continue;
if (dp->dp_start < 1)
continue;
printf(" part %d, start=%d, size=%d\n", part + 1, dp->dp_start, dp->dp_size);
if ((dp->dp_start + dp->dp_size) >
(slice->limits.slicesize/slice->limits.blksize)) {
printf("mbr: slice %d too big ", part);
printf("(%x > %x:%x )\n",
(dp->dp_start + dp->dp_size),
(slice->limits.slicesize / slice->limits.blksize) );
continue;
}
/* check for overlaps with existing slices */
for (i = 0; i < NDOSPART; i++) {
/* skip empty slots (including this one) */
if (pd->subdevs[i].slice == NULL )
continue;
if ((dp0[i].dp_start < (dp->dp_start + dp->dp_size))
&& ((dp0[i].dp_start + dp0[i].dp_size) > dp->dp_start))
{
printf("mbr: new slice %d overlaps slice %d\n",
part, i);
goto breakout;
}
}
/*
* the slice seems to make sense. Use it.
*/
pd->subdevs[part].part = part;
pd->subdevs[part].pd = pd;
pd->subdevs[part].offset = dp->dp_start;
pd->subdevs[part].limit.blksize
= slice->limits.blksize;
pd->subdevs[part].limit.slicesize
= (slice->limits.blksize * (u_int64_t)dp->dp_size);
sprintf(name, "%ss%d", slice->name, part + 1);
sl_make_slice(&slicetype,
&pd->subdevs[part],
&pd->subdevs[part].limit,
&pd->subdevs[part].slice,
name);
pd->subdevs[part].slice->probeinfo.typespecific = &dp->dp_typ;
switch (dp->dp_typ) { /* list stolen from fdisk */
case 0x00: /* "unused" */
case 0x01: /* "Primary DOS with 12 bit FAT" */
case 0x02: /* "XENIX / filesystem" */
case 0x03: /* "XENIX /usr filesystem" */
case 0x04: /* "Primary DOS with 16 bit FAT" */
case 0x05: /* "Extended DOS" */
case 0x06: /* "Primary 'big' DOS (> 32MB)" */
case 0x07: /* "OS/2 HPFS, QNX or Advanced UNIX" */
case 0x08: /* "AIX filesystem" */
case 0x09: /* "AIX boot partition or Coherent" */
case 0x0A: /* "OS/2 Boot Manager or OPUS" */
case 0x10: /* "OPUS" */
case 0x40: /* "VENIX 286" */
case 0x50: /* "DM" */
case 0x51: /* "DM" */
case 0x52: /* "CP/M or Microport SysV/AT" */
case 0x56: /* "GB" */
case 0x61: /* "Speed" */
case 0x63: /* "ISC UNIX, System V/386, GNU HURD or Mach" */
case 0x64: /* "Novell Netware 2.xx" */
case 0x65: /* "Novell Netware 3.xx" */
case 0x75: /* "PCIX" */
case 0x80: /* "Minix 1.1 ... 1.4a" */
case 0x81: /* "Minix 1.4b ... 1.5.10" */
case 0x82: /* "Linux swap" */
case 0x83: /* "Linux filesystem" */
case 0x93: /* "Amoeba filesystem" */
case 0x94: /* "Amoeba bad block table" */
case 0xA6: /* "OpenBSD" */
case 0xA7: /* "NEXTSTEP" */
case 0xB7: /* "BSDI BSD/386 filesystem" */
case 0xB8: /* "BSDI BSD/386 swap" */
case 0xDB: /* "Concurrent CPM or C.DOS or CTOS" */
case 0xE1: /* "Speed" */
case 0xE3: /* "Speed" */
case 0xE4: /* "Speed" */
case 0xF1: /* "Speed" */
case 0xF2: /* "DOS 3.3+ Secondary" */
case 0xF4: /* "Speed" */
case 0xFF: /* "BBT (Bad Blocks Table)" */
printf("%s: type %d. Leaving\n",
pd->subdevs[part].slice->name,
(u_int)dp->dp_typ);
pd->subdevs[part].slice->probeinfo.type = NO_SUBPART;
break;
case DOSPTYP_386BSD: /* 0xA5 "FreeBSD/NetBSD/386BSD" */
pd->subdevs[part].slice->probeinfo.type = "disklabel";
break;
default:
pd->subdevs[part].slice->probeinfo.type = NULL;
}
slice_start_probe(pd->subdevs[part].slice);
}
return (0);
nope:
printf("nope\n");
mbr_revoke(pd);
return (error);
}
/*
* This routine tries to guess the geometry for
* old disk drivers that need the MBR code to set it. Bits taken from
* diskslice_machdep.c which itself evolved from earlier code.
* This is not part of the SLICE code per-se, but just a convenient place to
* put this HACK because everything is in scope. Only called by the IDE driver.
* At the moment I don't know when it could be called from wd.c
* Possibly it might call the claim function itself so it may be called instead of
* the claim. It would have to inhibit the claim from calling the disklabel
* claim till after the correct values were assigned. possibbly this
* might be broken into two parts, the first of which hangs a struct off the
* private data, and the second of which fills it in after the mbr has been loaded.
*/
int
mbr_geom_hack(struct slice * slice, struct ide_geom *geom)
{
struct dos_partition table[NDOSPART];
struct dos_partition *dp, *dp0;
struct private_data *pd;
int part;
int error;
int max_ncyls;
int max_nsectors;
int max_ntracks;
u_int32_t secpercyl;
RR;
if (slice->handler_up != &slicetype)
return (ENXIO);
pd = slice->private_up; /* XXX */
if (pd == NULL)
return (ENXIO);
dp0 = pd->dos_table;
/*
* Guess the geometry. For some old drives (ESDI, st506) the
* driver below us may not yet know the geometry, but needs
* to before it can access blocks out of the first track.
* This hack is to use information in the MBR to "deduce"
* this information and pass it back.
*/
max_ncyls = 0;
max_nsectors = 0;
max_ntracks = 0;
for (part = 0, dp = dp0; part < NDOSPART; part++, dp++) {
int ncyls;
int nsectors;
int ntracks;
if (dp->dp_size == 0)
continue;
ncyls = DPCYL(dp->dp_ecyl, dp->dp_esect) + 1;
if (max_ncyls < ncyls)
max_ncyls = ncyls;
nsectors = DPSECT(dp->dp_esect);
if (max_nsectors < nsectors)
max_nsectors = nsectors;
ntracks = dp->dp_ehd + 1;
if (max_ntracks < ntracks)
max_ntracks = ntracks;
}
if ((max_ncyls == 0)
&& (max_nsectors == 0)
&& (max_ntracks == 0)) {
/* we've gained nought, so just return */
return (EINVAL);
}
secpercyl = (u_long) max_nsectors *max_ntracks;
printf("s=%d, h=%d, c=%d\n", max_nsectors, max_ntracks, max_ncyls);
/*
* Check that we have guessed the geometry right by checking
* the partition entries.
*/
error = 0;
for (part = 0, dp = dp0; part < NDOSPART; part++, dp++) {
int cyl;
int sector;
int track;
int secpercyl;
if (dp->dp_size == 0)
continue;
cyl = DPCYL(dp->dp_scyl, dp->dp_ssect);
track = dp->dp_shd;
sector = DPSECT(dp->dp_ssect) - 1;
secpercyl = max_nsectors * max_ntracks;
/*
* If the geometry doesn't work for any partition
* start then don't accept it.
*/
if (((((dp->dp_start / secpercyl) % 1024) != cyl)
&& (cyl != 1023))
|| (((dp->dp_start % secpercyl)
/ max_nsectors) != track)
|| (((dp->dp_start % secpercyl)
% max_nsectors) != sector)) {
printf("Can't get disk geometry from MBR\n");
return (EINVAL);
}
if ((dp->dp_start / secpercyl) > 1023) {
printf("part %d above BIOS reach\n", part);
}
}
/*
* Set our newely hypothesised numbers into the geometry
* slots in the supplied SLICE.
*/
geom->secpertrack = max_nsectors;
geom->trackpercyl = max_ntracks;
geom->cyls = max_ncyls;
return (0);
}
/*
* Invalidate all subslices, and free resources for this handler instance.
*/
static int
mbr_revoke(void *private)
{
register struct private_data *pd;
register struct slice *slice;
int part;
RR;
pd = private;
slice = pd->slice_down;
for (part = 0; part < NDOSPART; part++) {
if (pd->subdevs[part].slice) {
sl_rmslice(pd->subdevs[part].slice);
}
}
/*
* remove ourself as a handler
*/
slice->handler_up = NULL;
slice->private_up = NULL;
slicetype.refs--;
free(pd,M_DEVBUF);
sl_unref(slice);
return (0);
}
/*
* shift the appropriate IO by the offset for that slice.
*/
static void
mbr_IOreq(void *private, struct buf * bp)
{
register struct private_data *pd;
struct subdev *sdp;
register struct slice *slice;
RR;
sdp = private;
pd = sdp->pd;
slice = pd->slice_down;
bp->b_pblkno += sdp->offset; /* add the offset for that slice */
sliceio(slice, bp, SLW_ABOVE);
}
static int
mbr_open(void *private, int flags, int mode, struct proc * p)
{
register struct private_data *pd;
struct subdev *sdp;
register struct slice *slice;
int part;
int error;
int newflags = 0;
int oldoflags = 0;
int newoflags = 0;
RR;
sdp = private;
part = sdp->part;
pd = sdp->pd;
slice = pd->slice_down;
/*
* Calculate the change to to over-all picture here.
* Notice that this might result in LESS open bits
* if that was what was passed from above.
* (Prelude to 'mode-change' instead of open/close.)
*/
/* work out what our stored flags will be if this succeeds */
newflags = pd->flags & ~((MBRF_OPEN_WBIT|MBRF_OPEN_RBIT) << part);
newflags |= (flags & FWRITE) ? (MBRF_OPEN_WBIT << part) : 0;
newflags |= (flags & FREAD) ? (MBRF_OPEN_RBIT << part) : 0;
/* work out what we want to pass down this time */
newoflags = (newflags & MBRF_MSK_WR) ? FWRITE : 0;
newoflags |= (newflags & MBRF_MSK_RD) ? FREAD : 0;
/*
* If the agregate flags we used last time are the same as
* the agregate flags we would use this time, then don't
* bother re-doing the command.
*/
if (newoflags != pd->savedoflags) {
if (error = sliceopen(slice, newoflags, mode, p, SLW_ABOVE)) {
return (error);
}
}
/*
* Now that we know it succeeded, commit, by replacing the old
* flags with the new ones.
*/
pd->flags &= ~MBRF_MSK_OPEN;
pd->flags |= newflags;
pd->savedoflags = newoflags;
return (0);
}
static int
mbr_ioctl(void *private, u_long cmd, caddr_t addr, int flag, struct proc * p)
{
register struct private_data *pd;
struct subdev *sdp;
register struct slice *slice;
int error;
RR;
sdp = private;
pd = sdp->pd;
slice = pd->slice_down;
return ((*slice->handler_down->ioctl) (slice->private_down,
cmd, addr, flag, p));
}
static int
mbr_upconfig(struct slice *slice, int cmd, caddr_t addr,
int flag, struct proc * p)
{
int error;
RR;
switch (cmd) {
case SLCIOCRESET:
return (0);
case SLCIOCTRANSBAD:
{
struct private_data *pd;
struct subdev *sdp;
daddr_t blkno;
int part;
if (!slice->handler_up)
return (0);
blkno = *(daddr_t *)addr;
pd = slice->private_up;
sdp = pd->subdevs;
for (part = 0; part < NDOSPART; part++, sdp++) {
if (!sdp->slice)
continue;
if (blkno < sdp->offset)
continue;
if (blkno >= sdp->offset +
sdp->limit.slicesize / sdp->limit.blksize)
continue;
blkno -= sdp->offset;
slice = sdp->slice;
error = (*slice->handler_up->upconf)(slice, cmd,
(caddr_t)&blkno, flag, p);
if (!error)
*(daddr_t *)addr = blkno + sdp->offset;
return (error);
}
return (0);
}
/* These don't really make sense. keep the headers for a reminder */
default:
return (ENOIOCTL);
}
return (0);
}
static int
mbr_dump(void *private, int32_t blkoff, int32_t blkcnt)
{
struct private_data *pd;
struct subdev *sdp;
register struct slice *slice;
RR;
sdp = private;
pd = sdp->pd;
slice = pd->slice_down;
blkoff += sdp->offset;
if (slice->handler_down->dump) {
return (*slice->handler_down->dump)(slice->private_down,
blkoff, blkcnt);
}
return(ENXIO);
}