1 /* $NetBSD: disksubr.c,v 1.10 2002/03/05 09:40:40 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/buf.h> 41 #include <sys/device.h> 42 #include <sys/disk.h> 43 #include <sys/disklabel.h> 44 #include <sys/syslog.h> 45 #include <ufs/ufs/dinode.h> /* XXX for fs.h */ 46 #include <ufs/ffs/fs.h> /* XXX for BBSIZE & SBSIZE */ 47 48 #define b_cylin b_resid 49 50 static char *disklabel_mips_to_bsd __P((struct mips_volheader *, 51 struct disklabel *)); 52 static int disklabel_bsd_to_mips __P((struct disklabel *, 53 struct mips_volheader *)); 54 static int mipsvh_cksum __P((struct mips_volheader *)); 55 56 #define LABELSIZE(lp) ((char *)&lp->d_partitions[lp->d_npartitions] - \ 57 (char *)lp) 58 59 /* 60 * Attempt to read a disk label from a device 61 * using the indicated strategy routine. 62 * The label must be partly set up before this: 63 * secpercyl and anything required in the strategy routine 64 * (e.g., sector size) must be filled in before calling us. 65 * Returns null on success and an error string on failure. 66 */ 67 char * 68 readdisklabel(dev, strat, lp, clp) 69 dev_t dev; 70 void (*strat) __P((struct buf *bp)); 71 register struct disklabel *lp; 72 struct cpu_disklabel *clp; 73 { 74 register struct buf *bp; 75 struct disklabel *dlp; 76 struct mips_volheader *mvp; 77 int i, err; 78 79 /* minimum requirements for disk label */ 80 if (lp->d_secperunit == 0) 81 lp->d_secperunit = 0x1fffffff; 82 if (lp->d_npartitions == 0) { 83 lp->d_npartitions = RAW_PART + 1; 84 if (lp->d_partitions[RAW_PART].p_size == 0) 85 lp->d_partitions[RAW_PART].p_size = 0x1fffffff; 86 lp->d_partitions[RAW_PART].p_offset = 0; 87 } 88 89 bp = geteblk((int)lp->d_secsize); 90 91 bp->b_dev = dev; 92 bp->b_blkno = LABELSECTOR; 93 bp->b_bcount = lp->d_secsize; 94 bp->b_flags |= B_READ; 95 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 96 (*strat)(bp); 97 err = biowait(bp); 98 brelse(bp); 99 100 if (err) 101 return "error reading disklabel"; 102 103 /* Check for NetBSD label in second sector */ 104 dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET); 105 if (dlp->d_magic == DISKMAGIC) 106 if (!dkcksum(dlp)) { 107 memcpy(lp, dlp, LABELSIZE(dlp)); 108 return NULL; /* NetBSD label found */ 109 } 110 111 bp = geteblk((int)lp->d_secsize); 112 bp->b_dev = dev; 113 bp->b_blkno = MIPS_VHSECTOR; 114 bp->b_bcount = lp->d_secsize; 115 bp->b_flags |= B_READ; 116 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 117 (*strat)(bp); 118 err = biowait(bp); 119 brelse(bp); 120 121 if (err) 122 return "error reading volume header"; 123 124 mvp = (struct mips_volheader *)bp->b_un.b_addr; 125 /* Check for MIPS RISC/os volume header */ 126 if (mvp->vh_magic == MIPS_VHMAGIC) 127 return disklabel_mips_to_bsd(mvp, lp); 128 129 /* Search for NetBSD label in first sector */ 130 for (i=0; i <= lp->d_secsize - sizeof(*dlp); i += sizeof(long)) { 131 dlp = (struct disklabel *) ((char *)mvp + i); 132 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) { 133 if (dlp->d_npartitions > MAXPARTITIONS || 134 dkcksum(dlp) != 0) 135 return "disk label corrupted"; 136 else { 137 memcpy(lp, dlp, sizeof *lp); 138 return NULL; /* Found */ 139 } 140 } 141 } 142 return "no disk label"; 143 } 144 145 /* 146 * Check new disk label for sensibility 147 * before setting it. 148 */ 149 int 150 setdisklabel(olp, nlp, openmask, clp) 151 register struct disklabel *olp, *nlp; 152 u_long openmask; 153 struct cpu_disklabel *clp; 154 { 155 register int i; 156 register struct partition *opp, *npp; 157 158 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 159 dkcksum(nlp) != 0) 160 return (EINVAL); 161 while ((i = ffs(openmask)) != 0) { 162 i--; 163 openmask &= ~(1 << i); 164 if (nlp->d_npartitions <= i) 165 return (EBUSY); 166 opp = &olp->d_partitions[i]; 167 npp = &nlp->d_partitions[i]; 168 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 169 return (EBUSY); 170 /* 171 * Copy internally-set partition information 172 * if new label doesn't include it. XXX 173 */ 174 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 175 npp->p_fstype = opp->p_fstype; 176 npp->p_fsize = opp->p_fsize; 177 npp->p_frag = opp->p_frag; 178 npp->p_cpg = opp->p_cpg; 179 } 180 } 181 nlp->d_checksum = 0; 182 nlp->d_checksum = dkcksum(nlp); 183 *olp = *nlp; 184 return (0); 185 } 186 187 /* encoding of disk minor numbers, should be elsewhere... */ 188 #define dkunit(dev) (minor(dev) >> 3) 189 #define dkpart(dev) (minor(dev) & 07) 190 #define dkminor(unit, part) (((unit) << 3) | (part)) 191 192 /* 193 * Write disk label back to device after modification. 194 */ 195 int 196 writedisklabel(dev, strat, lp, clp) 197 dev_t dev; 198 void (*strat) __P((struct buf *bp)); 199 register struct disklabel *lp; 200 struct cpu_disklabel *clp; 201 { 202 struct buf *bp; 203 int labelpart; 204 int error; 205 206 labelpart = dkpart(dev); 207 if (lp->d_partitions[labelpart].p_offset != 0) { 208 if (lp->d_partitions[0].p_offset != 0) 209 return (EXDEV); /* not quite right */ 210 labelpart = 0; 211 } 212 213 /* Read RISC/os volume header before merging NetBSD partition info*/ 214 bp = geteblk((int)lp->d_secsize); 215 216 bp->b_dev = dev; 217 bp->b_blkno = MIPS_VHSECTOR; 218 bp->b_bcount = lp->d_secsize; 219 bp->b_flags |= B_READ; 220 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 221 (*strat)(bp); 222 223 if((error = biowait(bp)) != 0) 224 goto ioerror; 225 226 if ((error = disklabel_bsd_to_mips(lp, (void *)bp->b_data)) != 0) 227 goto ioerror; 228 229 /* Write MIPS RISC/os label to first sector */ 230 bp->b_flags &= ~(B_READ|B_DONE); 231 bp->b_flags |= B_WRITE; 232 (*strat)(bp); 233 if ((error = biowait(bp)) != 0) 234 goto ioerror; 235 236 /* Write NetBSD disk label to second sector */ 237 memset(bp->b_data, 0, lp->d_secsize); 238 memcpy(bp->b_data, lp, sizeof(*lp)); 239 bp->b_blkno = LABELSECTOR; 240 bp->b_bcount = lp->d_secsize; 241 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 242 bp->b_flags &= ~(B_READ | B_DONE); 243 bp->b_flags |= B_WRITE; 244 (*strat)(bp); 245 error = biowait(bp); 246 247 ioerror: 248 brelse(bp); 249 return error; 250 } 251 252 /* 253 * Determine the size of the transfer, and make sure it is 254 * within the boundaries of the partition. Adjust transfer 255 * if needed, and signal errors or early completion. 256 */ 257 int 258 bounds_check_with_label(bp, lp, wlabel) 259 struct buf *bp; 260 struct disklabel *lp; 261 int wlabel; 262 { 263 264 struct partition *p = lp->d_partitions + dkpart(bp->b_dev); 265 int labelsect = lp->d_partitions[0].p_offset; 266 int maxsz = p->p_size; 267 int sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 268 269 /* overwriting disk label ? */ 270 /* XXX should also protect bootstrap in first 8K */ 271 if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect && 272 (bp->b_flags & B_READ) == 0 && wlabel == 0) { 273 bp->b_error = EROFS; 274 goto bad; 275 } 276 277 /* beyond partition? */ 278 if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 279 /* if exactly at end of disk, return an EOF */ 280 if (bp->b_blkno == maxsz) { 281 bp->b_resid = bp->b_bcount; 282 return(0); 283 } 284 /* or truncate if part of it fits */ 285 sz = maxsz - bp->b_blkno; 286 if (sz <= 0) { 287 bp->b_error = EINVAL; 288 goto bad; 289 } 290 bp->b_bcount = sz << DEV_BSHIFT; 291 } 292 293 /* calculate cylinder for disksort to order transfers with */ 294 bp->b_resid = (bp->b_blkno + p->p_offset) / lp->d_secpercyl; 295 return(1); 296 bad: 297 bp->b_flags |= B_ERROR; 298 return(-1); 299 } 300 301 /* 302 * Convertion table for mapping partition numbers and types between 303 * a MIPS volume header and a BSD partition table. 304 * 305 * Mips volume header compatibility is required in order to boot 306 * NetBSD from the Mips stand alone shell, but due to the differences 307 * in the partition numbers used along with different methods for 308 * determining partition types we must use a table for mapping the 309 * differences. 310 */ 311 312 struct partitionmap { 313 int mips_part; /* Mips partition number */ 314 int mips_type; /* Mips partition type */ 315 int bsd_part; /* BSD partition number */ 316 int bsd_type; /* BSD partition type */ 317 }; 318 319 struct partitionmap partition_map[] = { 320 /* Mips Mips Type BSD BSD Type */ 321 {0, MIPS_FS_BSD42, 0, FS_BSDFFS}, 322 {1, MIPS_FS_BSD42, 1, FS_SWAP}, 323 {10, MIPS_FS_VOLUME, RAW_PART, FS_OTHER}, 324 {3, MIPS_FS_BSD42, 3, FS_BSDFFS}, 325 {4, MIPS_FS_BSD42, 4, FS_BSDFFS}, 326 {5, MIPS_FS_BSD42, 5, FS_BSDFFS}, 327 {6, MIPS_FS_BSD42, 6, FS_BSDFFS}, 328 {7, MIPS_FS_BSD42, 7, FS_BSDFFS} 329 }; 330 #define NPARTMAP (sizeof(partition_map)/sizeof(struct partitionmap)) 331 332 /* 333 * Convert a RISC/os disk label into a NetBSD disk label. 334 * 335 * Returns NULL on success, otherwise an error string 336 */ 337 static char * 338 disklabel_mips_to_bsd(vh, lp) 339 struct mips_volheader *vh; 340 struct disklabel *lp; 341 { 342 int i, bp, mp; 343 struct partition *lpp; 344 if (mipsvh_cksum(vh)) 345 return ("MIPS disk label corrupted"); 346 347 lp->d_secsize = vh->vh_dp.dp_secbytes; 348 lp->d_nsectors = vh->vh_dp.dp_secs; 349 lp->d_ntracks = vh->vh_dp.dp_trks0; 350 lp->d_ncylinders = vh->vh_dp.dp_cyls; 351 lp->d_interleave = vh->vh_dp.dp_interleave; 352 353 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 354 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 355 356 lp->d_bbsize = BBSIZE; 357 lp->d_sbsize = SBSIZE; 358 lp->d_npartitions = MAXPARTITIONS; 359 360 for (i = 0; i < NPARTMAP; i++) { 361 mp = partition_map[i].mips_part; 362 bp = partition_map[i].bsd_part; 363 364 lpp = &lp->d_partitions[bp]; 365 lpp->p_offset = vh->vh_part[mp].pt_offset; 366 lpp->p_size = vh->vh_part[mp].pt_size; 367 lpp->p_fstype = partition_map[i].bsd_type; 368 if (lpp->p_fstype == FS_BSDFFS) { 369 lpp->p_fsize = 1024; 370 lpp->p_frag = 8; 371 lpp->p_cpg = 16; 372 } 373 } 374 #if DIAGNOSTIC 375 printf("Warning: using MIPS disk label\n"); 376 #endif 377 return NULL; 378 } 379 380 /* 381 * Convert a NetBSD disk label into a RISC/os disk label. 382 * 383 * Returns NULL on success, otherwise an error string 384 */ 385 static int 386 disklabel_bsd_to_mips(lp, vh) 387 struct disklabel *lp; 388 struct mips_volheader *vh; 389 { 390 int i, bp, mp; 391 struct partition *lpp; 392 393 if (vh->vh_magic != MIPS_VHMAGIC || mipsvh_cksum(vh) != 0) { 394 #if DIAGNOSTIC 395 printf("Warning: writing MIPS compatible label\n"); 396 #endif 397 memset((void *)vh, 0, sizeof *vh); 398 vh->vh_magic = MIPS_VHMAGIC; 399 vh->vh_root = 0; /* a*/ 400 vh->vh_swap = 1; /* b*/ 401 } 402 strcpy(vh->bootfile, "/netbsd"); 403 vh->vh_dp.dp_skew = lp->d_trackskew; 404 vh->vh_dp.dp_gap1 = 1; /* XXX */ 405 vh->vh_dp.dp_gap2 = 1; /* XXX */ 406 vh->vh_dp.dp_cyls = lp->d_ncylinders; 407 vh->vh_dp.dp_shd0 = 0; 408 vh->vh_dp.dp_trks0 = lp->d_ntracks; 409 vh->vh_dp.dp_secs = lp->d_nsectors; 410 vh->vh_dp.dp_secbytes = lp->d_secsize; 411 vh->vh_dp.dp_interleave = lp->d_interleave; 412 vh->vh_dp.dp_nretries = 22; 413 414 for (i = 0; i < NPARTMAP; i++) { 415 mp = partition_map[i].mips_part; 416 bp = partition_map[i].bsd_part; 417 418 lpp = &lp->d_partitions[bp]; 419 vh->vh_part[mp].pt_offset = lpp->p_offset; 420 vh->vh_part[mp].pt_size = lpp->p_size; 421 vh->vh_part[mp].pt_fstype = partition_map[i].mips_type; 422 } 423 /* 424 * Create a fake partition for bootstrap code (or SASH) 425 */ 426 vh->vh_part[8].pt_offset = 0; 427 vh->vh_part[8].pt_size = vh->vh_part[vh->vh_root].pt_offset + 428 BBSIZE / vh->vh_dp.dp_secbytes; 429 vh->vh_part[8].pt_fstype = MIPS_FS_VOLHDR; 430 431 vh->vh_cksum = 0; 432 vh->vh_cksum = -mipsvh_cksum(vh); 433 return 0; 434 } 435 436 /* 437 * Compute checksum for MIPS disk volume header 438 * 439 * Mips volume header checksum is the 32bit 2's complement sum 440 * of the entire volume header structure 441 */ 442 int 443 mipsvh_cksum(vhp) 444 struct mips_volheader *vhp; 445 { 446 int i, *ptr; 447 int cksum = 0; 448 449 ptr = (int *)vhp; 450 i = sizeof(*vhp) / sizeof(*ptr); 451 while (i--) 452 cksum += *ptr++; 453 return cksum; 454 } 455