Home | History | Annotate | Line # | Download | only in ofppc
disksubr.c revision 1.4
      1 /*	$NetBSD: disksubr.c,v 1.3 1998/03/02 16:18:17 drochner Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 1996 Wolfgang Solfrank.
      5  * Copyright (C) 1996 TooLs GmbH.
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *	This product includes software developed by TooLs GmbH.
     19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 #include <sys/param.h>
     34 #include <sys/buf.h>
     35 #include <sys/conf.h>
     36 #include <sys/device.h>
     37 #include <sys/disk.h>
     38 #include <sys/disklabel.h>
     39 #include <sys/fcntl.h>
     40 #include <sys/ioctl.h>
     41 #include <sys/malloc.h>
     42 #include <sys/stat.h>
     43 #include <sys/systm.h>
     44 
     45 #include "opt_mbr.h"
     46 
     47 static inline unsigned short get_short __P((void *p));
     48 static inline unsigned long get_long __P((void *p));
     49 static int get_netbsd_label __P((dev_t dev, void (*strat)(struct buf *),
     50 				 struct disklabel *lp, daddr_t bno));
     51 static int mbr_to_label __P((dev_t dev, void (*strat)(struct buf *),
     52 			     daddr_t bno, struct disklabel *lp,
     53 			     unsigned short *pnpart,
     54 			     struct cpu_disklabel *osdep, daddr_t off));
     55 
     56 /*
     57  * Little endian access routines
     58  */
     59 static inline unsigned short
     60 get_short(p)
     61 	void *p;
     62 {
     63 	unsigned char *cp = p;
     64 
     65 	return cp[0] | (cp[1] << 8);
     66 }
     67 
     68 static inline unsigned long
     69 get_long(p)
     70 	void *p;
     71 {
     72 	unsigned char *cp = p;
     73 
     74 	return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
     75 }
     76 
     77 /*
     78  * Get real NetBSD disk label
     79  */
     80 static int
     81 get_netbsd_label(dev, strat, lp, bno)
     82 	dev_t dev;
     83 	void (*strat)();
     84 	struct disklabel *lp;
     85 	daddr_t bno;
     86 {
     87 	struct buf *bp;
     88 	struct disklabel *dlp;
     89 
     90 	/* get a buffer and initialize it */
     91 	bp = geteblk((int)lp->d_secsize);
     92 	bp->b_dev = dev;
     93 
     94 	/* Now get the label block */
     95 	bp->b_blkno = bno + LABELSECTOR;
     96 	bp->b_bcount = lp->d_secsize;
     97 	bp->b_flags = B_BUSY | B_READ;
     98 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
     99 	(*strat)(bp);
    100 
    101 	if (biowait(bp))
    102 		goto done;
    103 
    104 	for (dlp = (struct disklabel *)bp->b_data;
    105 	     dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof (*dlp));
    106 	     dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
    107 		if (dlp->d_magic == DISKMAGIC
    108 		    && dlp->d_magic2 == DISKMAGIC
    109 		    && dlp->d_npartitions <= MAXPARTITIONS
    110 		    && dkcksum(dlp) == 0) {
    111 			*lp = *dlp;
    112 			brelse(bp);
    113 			return 1;
    114 		}
    115 	}
    116 done:
    117 	bp->b_flags |= B_INVAL;
    118 	brelse(bp);
    119 	return 0;
    120 }
    121 
    122 /*
    123  * Construct disklabel entries from partition entries.
    124  */
    125 static int
    126 mbr_to_label(dev, strat, bno, lp, pnpart, osdep, off)
    127 	dev_t dev;
    128 	void (*strat)();
    129 	daddr_t bno;
    130 	struct disklabel *lp;
    131 	unsigned short *pnpart;
    132 	struct cpu_disklabel *osdep;
    133 	daddr_t off;
    134 {
    135 	static int recursion = 0;
    136 	struct mbr_partition *mp;
    137 	struct partition *pp;
    138 	struct buf *bp;
    139 	int i, found = 0;
    140 
    141 	/* Check for recursion overflow. */
    142 	if (recursion > MAXPARTITIONS)
    143 		return 0;
    144 
    145 	/*
    146 	 * Extended partitions seem to be relative to their first occurence?
    147 	 */
    148 	if (recursion++ == 1)
    149 		off = bno;
    150 
    151 	/* get a buffer and initialize it */
    152 	bp = geteblk((int)lp->d_secsize);
    153 	bp->b_dev = dev;
    154 
    155 	/* Now get the MBR */
    156 	bp->b_blkno = bno;
    157 	bp->b_bcount = lp->d_secsize;
    158 	bp->b_flags = B_BUSY | B_READ;
    159 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
    160 	(*strat)(bp);
    161 
    162 	if (biowait(bp))
    163 		goto done;
    164 
    165 	if (get_short(bp->b_data + MBRMAGICOFF) != MBRMAGIC)
    166 		goto done;
    167 
    168 	/* Extract info from MBR partition table */
    169 	mp = (struct mbr_partition *)(bp->b_data + MBRPARTOFF);
    170 	for (i = 0; i < NMBRPART; i++, mp++) {
    171 		if (get_long(&mp->mbr_size)) {
    172 			switch (mp->mbr_type) {
    173 			case MBR_EXTENDED:
    174 				if (*pnpart < MAXPARTITIONS) {
    175 					pp = lp->d_partitions + *pnpart;
    176 					bzero(pp, sizeof *pp);
    177 					pp->p_size = get_long(&mp->mbr_size);
    178 					pp->p_offset = off + get_long(&mp->mbr_start);
    179 					++*pnpart;
    180 				}
    181 				if (found = mbr_to_label(dev, strat,
    182 							 off + get_long(&mp->mbr_start),
    183 							 lp, pnpart, osdep, off))
    184 					goto done;
    185 				break;
    186 #ifdef COMPAT_386BSD_MBRPART
    187 			case MBR_386BSD:
    188 				printf("WARNING: old BSD partition ID!\n");
    189 				/* FALLTHROUGH */
    190 #endif
    191 			case MBR_NETBSD:
    192 				/* Found the real NetBSD partition, use it */
    193 				osdep->cd_start = off + get_long(&mp->mbr_start);
    194 				if (found = get_netbsd_label(dev, strat, lp, osdep->cd_start))
    195 					goto done;
    196 				/* FALLTHROUGH */
    197 			default:
    198 				if (*pnpart < MAXPARTITIONS) {
    199 					pp = lp->d_partitions + *pnpart;
    200 					bzero(pp, sizeof *pp);
    201 					pp->p_size = get_long(&mp->mbr_size);
    202 					pp->p_offset = off + get_long(&mp->mbr_start);
    203 					++*pnpart;
    204 				}
    205 				break;
    206 			}
    207 		}
    208 	}
    209 done:
    210 	recursion--;
    211 	bp->b_flags |= B_INVAL;
    212 	brelse(bp);
    213 	return found;
    214 }
    215 
    216 /*
    217  * Attempt to read a disk label from a device
    218  * using the indicated strategy routine.
    219  *
    220  * If we can't find a NetBSD label, we attempt to fake one
    221  * based on the MBR (and extended partition) information
    222  */
    223 char *
    224 readdisklabel(dev, strat, lp, osdep)
    225 	dev_t dev;
    226 	void (*strat)();
    227 	struct disklabel *lp;
    228 	struct cpu_disklabel *osdep;
    229 {
    230 	struct mbr_partition *mp;
    231 	struct buf *bp;
    232 	char *msg = 0;
    233 	int i;
    234 
    235 	/* Initialize disk label with some defaults */
    236 	if (lp->d_secsize == 0)
    237 		lp->d_secsize = DEV_BSIZE;
    238 	if (lp->d_secpercyl == 0)
    239 		lp->d_secpercyl = 1;
    240 	if (lp->d_secperunit == 0)
    241 		lp->d_secperunit = 0x7fffffff;
    242 	lp->d_npartitions = RAW_PART + 1;
    243 	for (i = 0; i < MAXPARTITIONS; i++) {
    244 		if (i != RAW_PART) {
    245 			lp->d_partitions[i].p_size = 0;
    246 			lp->d_partitions[i].p_offset = 0;
    247 		}
    248 	}
    249 	if (lp->d_partitions[RAW_PART].p_size == 0) {
    250 		lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
    251 		lp->d_partitions[RAW_PART].p_offset = 0;
    252 	}
    253 
    254 	osdep->cd_start = -1;
    255 
    256 	mbr_to_label(dev, strat, MBRSECTOR, lp, &lp->d_npartitions, osdep, 0);
    257 	return 0;
    258 }
    259 
    260 /*
    261  * Check new disk label for sensibility before setting it.
    262  */
    263 int
    264 setdisklabel(olp, nlp, openmask, osdep)
    265 	struct disklabel *olp, *nlp;
    266 	u_long openmask;
    267 	struct cpu_disklabel *osdep;
    268 {
    269 	/* sanity clause */
    270 	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
    271 	    || (nlp->d_secsize % DEV_BSIZE) != 0)
    272 		return EINVAL;
    273 
    274 	/* special case to allow disklabel to be invalidated */
    275 	if (nlp->d_magic == 0xffffffff) {
    276 		*olp = *nlp;
    277 		return 0;
    278 	}
    279 
    280 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC
    281 	    || dkcksum(nlp) != 0)
    282 		return EINVAL;
    283 
    284 	/* openmask parameter ignored */
    285 
    286 	*olp = *nlp;
    287 	return 0;
    288 }
    289 
    290 /*
    291  * Write disk label back to device after modification.
    292  */
    293 int
    294 writedisklabel(dev, strat, lp, osdep)
    295 	dev_t dev;
    296 	void (*strat)();
    297 	struct disklabel *lp;
    298 	struct cpu_disklabel *osdep;
    299 {
    300 	struct buf *bp;
    301 	int error;
    302 	struct disklabel label;
    303 
    304 	/*
    305 	 * Try to re-read a disklabel, in case he changed the MBR.
    306 	 */
    307 	label = *lp;
    308 	readdisklabel(dev, strat, &label, osdep);
    309 	if (osdep->cd_start < 0)
    310 		return EINVAL;
    311 
    312 	/* get a buffer and initialize it */
    313 	bp = geteblk(lp->d_secsize);
    314 	bp->b_dev = dev;
    315 
    316 	bp->b_blkno = osdep->cd_start + LABELSECTOR;
    317 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
    318 	bp->b_bcount = lp->d_secsize;
    319 	bp->b_flags = B_BUSY | B_WRITE;
    320 
    321 	bcopy((caddr_t)lp, (caddr_t)bp->b_data, sizeof *lp);
    322 
    323 	(*strat)(bp);
    324 	error = biowait(bp);
    325 
    326 	bp->b_flags |= B_INVAL;
    327 	brelse(bp);
    328 
    329 	return error;
    330 }
    331 
    332 /*
    333  * Determine the size of the transfer, and make sure it is
    334  * within the boundaris of the partition.  Adjust transfer
    335  * if needed, and signal errors or early completion.
    336  */
    337 int
    338 bounds_check_with_label(bp, lp, wlabel)
    339 	struct buf *bp;
    340 	struct disklabel *lp;
    341 	int wlabel;
    342 {
    343 	struct partition *p = lp->d_partitions + DISKPART(bp->b_dev);
    344 	int sz;
    345 
    346 	sz = howmany(bp->b_bcount, lp->d_secsize);
    347 
    348 	if (bp->b_blkno + sz > p->p_size) {
    349 		sz = p->p_size - bp->b_blkno;
    350 		if (sz == 0) {
    351 			/* If axactly at end of disk, return EOF. */
    352 			bp->b_resid = bp->b_bcount;
    353 			goto done;
    354 		}
    355 		if (sz < 0) {
    356 			/* If past end of disk, return EINVAL. */
    357 			bp->b_error = EINVAL;
    358 			goto bad;
    359 		}
    360 		/* Otherwise truncate request. */
    361 		bp->b_bcount = sz * lp->d_secsize;
    362 	}
    363 
    364 	/* calculate cylinder for disksort to order transfers with */
    365 	bp->b_cylinder = (bp->b_blkno + p->p_offset)
    366 			 / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
    367 
    368 	return 1;
    369 
    370 bad:
    371 	bp->b_flags |= B_ERROR;
    372 done:
    373 	return 0;
    374 }
    375 
    376 /*
    377  * This is called by main to set dumplo and dumpsize.
    378  */
    379 void
    380 cpu_dumpconf()
    381 {
    382 	int nblks;		/* size of dump device */
    383 	int skip;
    384 	int maj;
    385 
    386 	if (dumpdev == NODEV)
    387 		return;
    388 	maj = major(dumpdev);
    389 	if (maj < 0 || maj >= nblkdev)
    390 		panic("dumpconf: bad dumpdev=0x%x", dumpdev);
    391 	if (bdevsw[maj].d_psize == NULL)
    392 		return;
    393 	nblks = (*bdevsw[maj].d_psize)(dumpdev);
    394 	if (nblks <= ctod(1))
    395 		return;
    396 
    397 	dumpsize = physmem;
    398 
    399 	/* Skip enough blocks at start of disk to preserve an eventual disklabel. */
    400 	skip = LABELSECTOR + 1;
    401 	skip += ctod(1) - 1;
    402 	skip = ctod(dtoc(skip));
    403 	if (dumplo < skip)
    404 		dumplo = skip;
    405 
    406 	/* Put dump at end of partition */
    407 	if (dumpsize > dtoc(nblks - dumplo))
    408 		dumpsize = dtoc(nblks - dumplo);
    409 	if (dumplo < nblks - ctod(dumpsize))
    410 		dumplo = nblks - ctod(dumpsize);
    411 }
    412