/*- * 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: disklabel.c,v 1.7 1998/07/13 08:22:54 julian Exp $ */ #define BAD144 #undef BAD144 #include #include #include #include #include #include #include #include #ifdef BAD144 #include #endif #include #include #include #include #include struct private_data { u_int32_t flags; u_int8_t rflags; u_int8_t wflags; int savedoflags; struct slice *slice_down; struct disklabel disklabel; struct subdev { int part; struct slice *slice; struct slicelimits limit; struct private_data *pd; u_int32_t offset; /* all disklabel supports */ } subdevs[MAXPARTITIONS]; #ifdef BAD144 struct dkbad_intern *bad; #endif }; static sl_h_IO_req_t dkl_IOreq; /* IO req downward (to device) */ static sl_h_ioctl_t dkl_ioctl; /* ioctl req downward (to device) */ static sl_h_open_t dkl_open; /* downwards travelling open */ /*static sl_h_close_t dkl_close; */ /* downwards travelling close */ static sl_h_claim_t dkl_claim; /* upwards travelling claim */ static sl_h_revoke_t dkl_revoke;/* upwards travelling revokation */ static sl_h_verify_t dkl_verify;/* things changed, are we stil valid? */ static sl_h_upconfig_t dkl_upconfig;/* config requests from below */ static sl_h_dump_t dkl_dump; /* core dump req downward */ static sl_h_done_t dkl_done; /* callback after async request */ static struct slice_handler slicetype = { "disklabel", 0, NULL, 0, &dkl_done, &dkl_IOreq, &dkl_ioctl, &dkl_open, /*&dkl_close*/NULL, &dkl_revoke, /* revoke */ &dkl_claim, /* claim */ &dkl_verify, /* verify */ &dkl_upconfig, /* subslice manipulation */ &dkl_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 dklallocprivate(sl_p slice) { register struct private_data *pd; pd = malloc(sizeof(*pd), M_DEVBUF, M_NOWAIT); if (pd == NULL) { printf("dkl: 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 dkl_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 = dklallocprivate(slice))) { return (error); } } if ((error = slice_request_block(slice, LABELSECTOR))) { dkl_revoke(slice->private_up); } return (error); } static int dkl_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, LABELSECTOR))) { dkl_revoke(slice->private_up); } return (error); } /* * called with an argument of a bp when it is completed */ static int dkl_done(sl_p slice, struct buf *bp) { register struct private_data *pd; struct disklabel label; struct disklabel *lp, *dlp, *dl; struct partition *dp0, *dp, *dp2; int part; int found = 0; int i; char name[64]; int slice_offset; int error = 0; RR; /* * Discover whether the IO was successful. */ pd = slice->private_up; if ( bp->b_flags & B_ERROR ) { error = bp->b_error; goto nope; } /* * Step through the block looking for the label. * It may not be at the front (Though I have never seen this). * When found, copy it to the destination supplied. */ for (dlp = (struct disklabel *) bp->b_data; dlp <= (struct disklabel *) ((char *) bp->b_data + slice->limits.blksize - sizeof(*dlp)); dlp = (struct disklabel *) (((char *) dlp) + sizeof(long))) { if ((dlp->d_magic == DISKMAGIC) && (dlp->d_magic2 == DISKMAGIC) && (dlp->d_npartitions <= MAXPARTITIONS) && (dkcksum(dlp) == 0)) { found = 1; break; } } if (! found) { goto nope; } /* copy the table out of the buf and release it. */ bcopy(dlp, &label, sizeof(label)); bp->b_flags |= B_INVAL | B_AGE; brelse(bp); /* * Disklabels are done relative to the base of the disk, * rather than the local partition, (DUH!) * so use partition 2 (c) to get the base, * and subtract it from all non-0 offsets. */ dp = label.d_partitions; slice_offset = dp[2].p_offset; for (part = 0; part < MAXPARTITIONS; part++, dp++) { /* * We could be reloading, in which case skip * entries already set up. */ if (dp->p_size == 0) continue; if ( dp->p_offset < slice_offset ) { printf("slice before 'c'\n"); dp->p_size = 0; continue; } dp->p_offset -= slice_offset; } /*- * Handle the case when we are being asked to reevaluate * an already loaded disklabel. * 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. */ dl = &(pd->disklabel); dp = dl->d_partitions; dp2 = label.d_partitions; for (part = 0; part < MAXPARTITIONS; part++, dp++, dp2++) { if (pd->subdevs[part].slice) { if ((dp2->p_offset != dp->p_offset) || (dp2->p_size != dp->p_size)) { 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, and * handle any new slices by calling the constructor. */ bcopy(&label, dl, sizeof(label)); #ifdef BAD144 #if 0 /* place holder: remember to add some state machine to handle bad144 loading */ if (pd->disklabel.d_flags & D_BADSECT) { if ((error = dkl_readbad144(pd))) { free(pd, M_DEVBUF); return (error); } } #endif #endif dp0 = dl->d_partitions; /*- * Handle each of the partitions. * We should check that each makes sence 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 overlap other slices. * It can include sector 0 (unfortunatly) */ dp = dp0; for (part = 0; part < MAXPARTITIONS; part++, dp++) { int i; if ( part == 2 ) continue; /* XXX skip the 'c' partition */ /* * We could be reloading, in which case skip * entries already set up. */ if (pd->subdevs[part].slice != NULL) breakout: continue; /* * also skip partitions not present */ if (dp->p_size == 0) continue; printf(" part %c, start=%d, size=%d\n", part + 'a', dp->p_offset, dp->p_size); if ((dp->p_offset + dp->p_size) > (slice->limits.slicesize / slice->limits.blksize)) { printf("dkl: slice %d too big ", part); printf("(%x > %x:%x )\n", (dp->p_offset + dp->p_size), (slice->limits.slicesize / slice->limits.blksize) ); continue; } /* check for overlaps with existing slices */ for (i = 0; i < MAXPARTITIONS; i++) { /* skip empty slots (including this one) */ if (pd->subdevs[i].slice == NULL) continue; if ((dp0[i].p_offset < (dp->p_offset + dp->p_size)) && ((dp0[i].p_offset + dp0[i].p_size) > dp->p_offset)) { printf("dkl: 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->p_offset; pd->subdevs[part].limit.blksize = slice->limits.blksize; pd->subdevs[part].limit.slicesize = (slice->limits.blksize * (u_int64_t)dp->p_size); sprintf(name, "%s%c", slice->name, (char )('a' + part)); sl_make_slice(&slicetype, &pd->subdevs[part], &pd->subdevs[part].limit, &pd->subdevs[part].slice, name); pd->subdevs[part].slice->probeinfo.typespecific = &dp->p_fstype; switch (dp->p_fstype) { case FS_UNUSED: /* allow unuseed to be further split */ pd->subdevs[part].slice->probeinfo.type = NULL; break; case FS_V6: case FS_V7: case FS_SYSV: case FS_V71K: case FS_V8: case FS_MSDOS: case FS_BSDLFS: case FS_OTHER: case FS_HPFS: case FS_ISO9660: case FS_BOOT : #if 0 printf("%s: type %d. Leaving\n", pd->subdevs[part].slice->name, (u_int)dp->p_fstype); #endif case FS_SWAP: case FS_BSDFFS: pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; break; default: pd->subdevs[part].slice->probeinfo.type = NULL; } /* * Dont allow further breakup of slices that * cover our disklabel (that would recurse forever) */ if (dp->p_offset < 16) { #if 0 printf("%s: covers disklabel. Leaving\n", pd->subdevs[part].slice->name); #endif pd->subdevs[part].slice->probeinfo.type = NO_SUBPART; } slice_start_probe(pd->subdevs[part].slice); } return (0); nope: printf(" .. nope\n"); dkl_revoke(pd); return (error); } #if 0 /*- * This is a special HACK function for the IDE driver. * It is here because everything it need is in scope here, * but it is not really part of the SLICE code. * Because old ESDI drives could not tell their geometry, They need * to get it from the MBR or the disklabel. This is the disklabel bit. */ int dkl_geom_hack(struct slice * slice, struct ide_geom *geom) { struct disklabel disklabel; struct disklabel *dl, *dl0; int error; RR; /* first check it's a disklabel*/ if ((error = dkl_claim (slice))) return (error); /*- * Try load a valid disklabel table. * This is wasteful but never called on new (< 5 YO ) drives. */ if ((error = dkl_extract_table(slice, &disklabel)) != 0) { return (error); } geom->secpertrack = disklabel. d_nsectors; geom->trackpercyl = disklabel.d_ntracks; geom->cyls = disklabel.d_ncylinders; return (0); } #endif /*- * Invalidate all subslices, and free resources for this handler instance. */ static int dkl_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 < MAXPARTITIONS; 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--; #ifdef BAD144 if (pd->bad) free(pd->bad, M_DEVBUF); #endif free(pd, M_DEVBUF); sl_unref(slice); return (0); } #ifdef BAD144 #if 0 bucket= blknum >> 4; /* set 16 blocks to the same bucket */ bucket ^= (bucket>>16); /* combine bytes 1+3, 2+4 */ bucket ^= (bucket>>8); /* combine bytes 1+3+2+4 */ bucket &= 0x7F; /* AND 128 entries */ #endif /* * Given a bad144 table, load the values into ram. * eventually we should hash them so we can do forwards lookups. * Probably should hash on (blknum >> 4) to minimise * lookups for a clustered IO. (see above) */ static int dkl_internbad144(struct private_data *pd, struct dkbad *btp, int flag) { struct disklabel *lp = &pd->disklabel; struct dkbad_intern *bip = pd->bad; int i; if (bip == NULL) { bip = malloc(sizeof *bip, M_DEVBUF, flag); if (bip == NULL) return (ENOMEM); pd->bad = bip; } /* * Spare sectors are allocated beginning with the last sector of * the second last track of the disk (the last track is used for * the bad sector list). */ bip->bi_maxspare = lp->d_secperunit - lp->d_nsectors - 1; bip->bi_nbad = DKBAD_MAXBAD; for (i=0; i < DKBAD_MAXBAD && btp->bt_bad[i].bt_cyl != DKBAD_NOCYL; i++) bip->bi_bad[i] = btp->bt_bad[i].bt_cyl * lp->d_secpercyl + (btp->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + (btp->bt_bad[i].bt_trksec & 0x00ff); bip->bi_bad[i] = -1; #if 1 for (i = 0; i < DKBAD_MAXBAD && bip->bi_bad[i] != -1; i++) printf(" %8d => %8d\n", bip->bi_bad[i], bip->bi_maxspare - i); #endif return (0); } /* * Hunt in the last cylinder for the bad144 table * this needs to be turned around to be made into a state operation * driven by IO completion of the read. */ static int dkl_readbad144(struct private_data *pd) { sl_p slice = pd->slice_down; struct disklabel *lp = &pd->disklabel; struct dkbad *db; struct buf *bp; int blkno, i, error; for (i = 0; i < min(10, lp->d_nsectors); i += 2) { blkno = lp->d_secperunit - lp->d_nsectors + i; if (lp->d_secsize > slice->limits.blksize) blkno *= lp->d_secsize / slice->limits.blksize; else blkno /= slice->limits.blksize / lp->d_secsize; error = slice_readblock(slice, blkno, &bp); if (error) return (error); bp->b_flags |= B_INVAL | B_AGE; db = (struct dkbad *)bp->b_data; if (db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC) { printf(" bad144 table found at block %d\n", blkno); error = dkl_internbad144(pd, db, M_NOWAIT); brelse(bp); return (error); } brelse(bp); } return (EINVAL); } static __inline daddr_t dkl_transbad144(struct private_data *pd, daddr_t blkno) { return transbad144(pd->bad, blkno); } #endif /*- * shift the appropriate IO by the offset for that slice. */ static void dkl_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 dkl_open(void *private, int flags, int mode, struct proc * p) { register struct private_data *pd; struct subdev *sdp; register struct slice *slice; int error; u_int8_t newrflags = 0; u_int8_t newwflags = 0; int newoflags; int part; u_int8_t partbit; RR; sdp = private; part = sdp->part; partbit = (1 << 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 */ newwflags &= ~ (partbit); newrflags &= ~ (partbit); newwflags |= (flags & FWRITE) ? (partbit) : 0; newrflags |= (flags & FREAD) ? (partbit) : 0; /* work out what we want to pass down this time */ newoflags = newwflags ? FWRITE : 0; newoflags |= newrflags ? 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->rflags = newrflags; pd->wflags = newwflags; pd->savedoflags = newoflags; return (0); } static int dkl_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; struct disklabel *lp; int error; RR; sdp = private; pd = sdp->pd; slice = pd->slice_down; lp = &pd->disklabel; switch (cmd) { case DIOCGDINFO: *(struct disklabel *)addr = *lp; return (0); case DIOCGPART: if (lp == NULL) return (EINVAL); ((struct partinfo *)addr)->disklab = lp; ((struct partinfo *)addr)->part = lp->d_partitions + sdp->part; return (0); #ifdef BAD144 case DIOCSBAD: if (!(flag & FWRITE)) return (EBADF); return (dkl_internbad144(pd, (struct dkbad *)addr, M_WAITOK)); #endif /* These don't really make sense. keep the headers for a reminder */ case DIOCSDINFO: case DIOCSYNCSLICEINFO: case DIOCWDINFO: case DIOCWLABEL: return (ENOIOCTL); } return ((*slice->handler_down->ioctl) (slice->private_down, cmd, addr, flag, p)); } static int dkl_upconfig(struct slice *slice, int cmd, caddr_t addr, int flag, struct proc * p) { RR; switch (cmd) { case SLCIOCRESET: return (0); #ifdef BAD144 case SLCIOCTRANSBAD: { struct private_data *pd; daddr_t blkno; pd = slice->private_up; if (pd->bad) *(daddr_t*)addr = dkl_transbad144(pd, *(daddr_t*)addr); return (0); } #endif /* These don't really make sense. keep the headers for a reminder */ default: return (ENOIOCTL); } return (0); } static struct disklabel static_label; /* * This is a hack routine called from the slice generic code to produce a dummy * disklabel when given a slice descriptor. It's in here because this code * knows about disklabels. */ int dkl_dummy_ioctl(struct slice *slice, u_long cmd, caddr_t addr, int flag, struct proc * p) { struct disklabel *lp = &static_label; switch (cmd) { case DIOCGDINFO: case DIOCGPART: bzero(lp, sizeof(static_label)); lp->d_magic = DISKMAGIC; lp->d_magic2 = DISKMAGIC; lp->d_secsize = slice->limits.blksize; lp->d_nsectors = 1; lp->d_ntracks = 1; lp->d_secpercyl = 1; lp->d_ncylinders = lp->d_secperunit = slice->limits.slicesize / slice->limits.blksize; lp->d_npartitions = RAW_PART + 1; lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; lp->d_partitions[RAW_PART].p_offset = 0; break; default: return (ENOIOCTL); } lp->d_checksum = dkcksum(lp); switch (cmd) { case DIOCGDINFO: *(struct disklabel *)addr = *lp; break; case DIOCGPART: /* XXX hack alert. * This is a hack as this information is consumed immediatly * otherwise the use of a static buffer would be dangerous. */ ((struct partinfo *)addr)->disklab = lp; ((struct partinfo *)addr)->part = lp->d_partitions + RAW_PART; } return (0); } #if 0 /* use the existing one for now */ /*- * Compute checksum for disk label. */ u_int dkcksum(lp) register struct disklabel *lp; { register u_short *start, *end; register u_short sum = 0; start = (u_short *) lp; end = (u_short *) & lp->d_partitions[lp->d_npartitions]; while (start < end) sum ^= *start++; return (sum); } #endif /* 0 */ /* * pass down a dump request. * make sure it's offset by the right amount. */ static int dkl_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); }