1 /* $NetBSD: disksubr.c,v 1.7 2005/12/11 12:23:56 christos 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * Copyright (c) 1994, 1995 Gordon W. Ross 33 * Copyright (c) 1994 Theo de Raadt 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 46 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 47 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 48 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 49 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 50 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 51 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 52 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 53 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 54 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 */ 56 57 #include <sys/cdefs.h> 58 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.7 2005/12/11 12:23:56 christos Exp $"); 59 60 #include <sys/param.h> 61 #include <sys/systm.h> 62 #include <sys/buf.h> 63 #include <sys/ioccom.h> 64 #include <sys/device.h> 65 #include <sys/disklabel.h> 66 #include <sys/disk.h> 67 #include <sys/dkbad.h> 68 69 #include <dev/sun/disklabel.h> 70 71 #if LABELSECTOR != 0 72 #error "Default value of LABELSECTOR no longer zero?" 73 #endif 74 75 static const char *disklabel_sun_to_bsd(char *, struct disklabel *); 76 static int disklabel_bsd_to_sun(struct disklabel *, char *); 77 78 /* 79 * Attempt to read a disk label from a device 80 * using the indicated strategy routine. 81 * The label must be partly set up before this: 82 * secpercyl, secsize and anything required for a block i/o read 83 * operation in the driver's strategy/start routines 84 * must be filled in before calling us. 85 * 86 * Return buffer for use in signalling errors if requested. 87 * 88 * Returns null on success and an error string on failure. 89 */ 90 const char * 91 readdisklabel(dev, strat, lp, clp) 92 dev_t dev; 93 void (*strat)(struct buf *); 94 struct disklabel *lp; 95 struct cpu_disklabel *clp; 96 { 97 struct buf *bp; 98 struct disklabel *dlp; 99 struct sun_disklabel *slp; 100 int error; 101 102 /* minimal requirements for archtypal disk label */ 103 if (lp->d_secperunit == 0) 104 lp->d_secperunit = 0x1fffffff; 105 if (lp->d_npartitions == 0) { 106 lp->d_npartitions = RAW_PART + 1; 107 if (lp->d_partitions[RAW_PART].p_size == 0) 108 lp->d_partitions[RAW_PART].p_size = 0x1fffffff; 109 lp->d_partitions[RAW_PART].p_offset = 0; 110 } 111 112 /* obtain buffer to probe drive with */ 113 bp = geteblk((int)lp->d_secsize); 114 115 /* next, dig out disk label */ 116 bp->b_dev = dev; 117 bp->b_blkno = LABELSECTOR; 118 bp->b_cylinder = 0; 119 bp->b_bcount = lp->d_secsize; 120 bp->b_flags |= B_READ; 121 (*strat)(bp); 122 123 /* if successful, locate disk label within block and validate */ 124 error = biowait(bp); 125 if (error == 0) { 126 /* Save the whole block in case it has info we need. */ 127 memcpy(clp->cd_block, bp->b_data, sizeof(clp->cd_block)); 128 } 129 brelse(bp); 130 if (error) 131 return ("disk label read error"); 132 133 /* Check for a NetBSD disk label at LABELOFFSET */ 134 dlp = (struct disklabel *) (clp->cd_block + LABELOFFSET); 135 if (dlp->d_magic == DISKMAGIC) { 136 if (dkcksum(dlp)) 137 return ("NetBSD disk label corrupted"); 138 *lp = *dlp; 139 return (NULL); 140 } 141 142 /* Check for a Sun disk label (for PROM compatibility). */ 143 slp = (struct sun_disklabel *) clp->cd_block; 144 if (slp->sl_magic == SUN_DKMAGIC) 145 return (disklabel_sun_to_bsd(clp->cd_block, lp)); 146 147 /* 148 * Check for a NetBSD disk label somewhere in LABELSECTOR 149 * (compat with others big-endian boxes) 150 */ 151 for (dlp = (struct disklabel *)clp->cd_block; 152 dlp <= (struct disklabel *)((char *)clp->cd_block + 153 DEV_BSIZE - sizeof(*dlp)); 154 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 155 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 156 continue; 157 } 158 if (dlp->d_npartitions > MAXPARTITIONS || dkcksum(dlp) != 0) 159 return("NetBSD disk label corrupted"); 160 else { 161 *lp = *dlp; 162 return(NULL); 163 } 164 } 165 166 memset(clp->cd_block, 0, sizeof(clp->cd_block)); 167 return ("no disk label"); 168 } 169 170 /* 171 * Check new disk label for sensibility 172 * before setting it. 173 */ 174 int 175 setdisklabel(olp, nlp, openmask, clp) 176 struct disklabel *olp, *nlp; 177 u_long openmask; 178 struct cpu_disklabel *clp; 179 { 180 int i; 181 struct partition *opp, *npp; 182 183 /* sanity clause */ 184 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 || 185 (nlp->d_secsize % DEV_BSIZE) != 0) 186 return(EINVAL); 187 188 /* special case to allow disklabel to be invalidated */ 189 if (nlp->d_magic == 0xffffffff) { 190 *olp = *nlp; 191 return (0); 192 } 193 194 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 195 dkcksum(nlp) != 0) 196 return (EINVAL); 197 198 while ((i = ffs(openmask)) != 0) { 199 i--; 200 openmask &= ~(1 << i); 201 if (nlp->d_npartitions <= i) 202 return (EBUSY); 203 opp = &olp->d_partitions[i]; 204 npp = &nlp->d_partitions[i]; 205 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 206 return (EBUSY); 207 } 208 209 *olp = *nlp; 210 return (0); 211 } 212 213 /* 214 * Write disk label back to device after modification. 215 * Current label is already in clp->cd_block[] 216 */ 217 int 218 writedisklabel(dev, strat, lp, clp) 219 dev_t dev; 220 void (*strat)(struct buf *); 221 struct disklabel *lp; 222 struct cpu_disklabel *clp; 223 { 224 struct buf *bp; 225 int error; 226 struct disklabel *dlp; 227 struct sun_disklabel *slp; 228 229 /* 230 * Embed native label in a piece of wasteland. 231 */ 232 if (sizeof(struct disklabel) > sizeof slp->sl_bsdlabel) 233 return EFBIG; 234 235 slp = (struct sun_disklabel *)clp->cd_block; 236 memset(slp->sl_bsdlabel, 0, sizeof(slp->sl_bsdlabel)); 237 dlp = (struct disklabel *)slp->sl_bsdlabel; 238 *dlp = *lp; 239 240 /* Build a SunOS compatible label around the native label */ 241 error = disklabel_bsd_to_sun(lp, clp->cd_block); 242 if (error) 243 return (error); 244 245 /* Get a buffer and copy the new label into it. */ 246 bp = geteblk((int)lp->d_secsize); 247 memcpy(bp->b_data, clp->cd_block, sizeof(clp->cd_block)); 248 249 /* Write out the updated label. */ 250 bp->b_dev = dev; 251 bp->b_blkno = LABELSECTOR; 252 bp->b_cylinder = 0; 253 bp->b_bcount = lp->d_secsize; 254 bp->b_flags |= B_WRITE; 255 (*strat)(bp); 256 error = biowait(bp); 257 brelse(bp); 258 259 return (error); 260 } 261 262 /* 263 * Determine the size of the transfer, and make sure it is 264 * within the boundaries of the partition. Adjust transfer 265 * if needed, and signal errors or early completion. 266 */ 267 int 268 bounds_check_with_label(dk, bp, wlabel) 269 struct disk *dk; 270 struct buf *bp; 271 int wlabel; 272 { 273 struct disklabel *lp = dk->dk_label; 274 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 275 int maxsz = p->p_size; 276 int sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 277 278 /* 279 * overwriting disk label ? 280 * The label is always in sector LABELSECTOR. 281 * XXX should also protect bootstrap in first 8K 282 */ 283 if (bp->b_blkno + p->p_offset <= LABELSECTOR && 284 (bp->b_flags & B_READ) == 0 && wlabel == 0) { 285 bp->b_error = EROFS; 286 goto bad; 287 } 288 289 /* beyond partition? */ 290 if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 291 /* if exactly at end of disk, return an EOF */ 292 if (bp->b_blkno == maxsz) { 293 bp->b_resid = bp->b_bcount; 294 return(0); 295 } 296 /* or truncate if part of it fits */ 297 sz = maxsz - bp->b_blkno; 298 if (sz <= 0) { 299 bp->b_error = EINVAL; 300 goto bad; 301 } 302 bp->b_bcount = sz << DEV_BSHIFT; 303 } 304 305 /* calculate cylinder for disksort to order transfers with */ 306 bp->b_resid = (bp->b_blkno + p->p_offset) / lp->d_secpercyl; 307 return(1); 308 bad: 309 bp->b_flags |= B_ERROR; 310 return(-1); 311 } 312 313 /************************************************************************ 314 * 315 * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c 316 * and then substantially rewritten by Gordon W. Ross 317 * 318 ************************************************************************/ 319 320 /* What partition types to assume for Sun disklabels: */ 321 static u_char 322 sun_fstypes[8] = { 323 FS_BSDFFS, /* a */ 324 FS_SWAP, /* b */ 325 FS_OTHER, /* c - whole disk */ 326 FS_BSDFFS, /* d */ 327 FS_BSDFFS, /* e */ 328 FS_BSDFFS, /* f */ 329 FS_BSDFFS, /* g */ 330 FS_BSDFFS, /* h */ 331 }; 332 333 /* 334 * Given a SunOS disk label, set lp to a BSD disk label. 335 * Returns NULL on success, else an error string. 336 * 337 * The BSD label is cleared out before this is called. 338 */ 339 static const char * 340 disklabel_sun_to_bsd(cp, lp) 341 char *cp; 342 struct disklabel *lp; 343 { 344 struct sun_disklabel *sl; 345 struct partition *npp; 346 struct sun_dkpart *spp; 347 int i, secpercyl; 348 u_short cksum, *sp1, *sp2; 349 350 sl = (struct sun_disklabel *)cp; 351 352 /* Verify the XOR check. */ 353 sp1 = (u_short *)sl; 354 sp2 = (u_short *)(sl + 1); 355 cksum = 0; 356 while (sp1 < sp2) 357 cksum ^= *sp1++; 358 if (cksum != 0) 359 return("SunOS disk label, bad checksum"); 360 361 /* Format conversion. */ 362 lp->d_magic = DISKMAGIC; 363 lp->d_magic2 = DISKMAGIC; 364 memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname)); 365 366 lp->d_secsize = 512; 367 lp->d_nsectors = sl->sl_nsectors; 368 lp->d_ntracks = sl->sl_ntracks; 369 lp->d_ncylinders = sl->sl_ncylinders; 370 371 secpercyl = sl->sl_nsectors * sl->sl_ntracks; 372 lp->d_secpercyl = secpercyl; 373 lp->d_secperunit = secpercyl * sl->sl_ncylinders; 374 375 lp->d_sparespercyl = sl->sl_sparespercyl; 376 lp->d_acylinders = sl->sl_acylinders; 377 lp->d_rpm = sl->sl_rpm; 378 lp->d_interleave = sl->sl_interleave; 379 380 lp->d_npartitions = 8; 381 /* These are as defined in <ufs/ffs/fs.h> */ 382 lp->d_bbsize = 8192; /* XXX */ 383 lp->d_sbsize = 8192; /* XXX */ 384 385 for (i = 0; i < 8; i++) { 386 spp = &sl->sl_part[i]; 387 npp = &lp->d_partitions[i]; 388 npp->p_offset = spp->sdkp_cyloffset * secpercyl; 389 npp->p_size = spp->sdkp_nsectors; 390 if (npp->p_size == 0) { 391 npp->p_fstype = FS_UNUSED; 392 } else { 393 npp->p_fstype = sun_fstypes[i]; 394 if (npp->p_fstype == FS_BSDFFS) { 395 /* 396 * The sun label does not store the FFS fields, 397 * so just set them with default values here. 398 */ 399 npp->p_fsize = 1024; 400 npp->p_frag = 8; 401 npp->p_cpg = 16; 402 } 403 } 404 } 405 406 lp->d_checksum = 0; 407 lp->d_checksum = dkcksum(lp); 408 return (NULL); 409 } 410 411 /* 412 * Given a BSD disk label, update the Sun disklabel 413 * pointed to by cp with the new info. Note that the 414 * Sun disklabel may have other info we need to keep. 415 * Returns zero or error code. 416 */ 417 static int 418 disklabel_bsd_to_sun(lp, cp) 419 struct disklabel *lp; 420 char *cp; 421 { 422 struct sun_disklabel *sl; 423 struct partition *npp; 424 struct sun_dkpart *spp; 425 int i, secpercyl; 426 u_short cksum, *sp1, *sp2; 427 428 if (lp->d_secsize != 512) 429 return (EINVAL); 430 431 sl = (struct sun_disklabel *)cp; 432 433 /* 434 * Format conversion. 435 */ 436 memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname)); 437 sl->sl_rpm = lp->d_rpm; 438 sl->sl_pcylinders = lp->d_ncylinders + lp->d_acylinders; /* XXX */ 439 sl->sl_sparespercyl = lp->d_sparespercyl; 440 sl->sl_interleave = lp->d_interleave; 441 sl->sl_ncylinders = lp->d_ncylinders; 442 sl->sl_acylinders = lp->d_acylinders; 443 sl->sl_ntracks = lp->d_ntracks; 444 sl->sl_nsectors = lp->d_nsectors; 445 446 secpercyl = sl->sl_nsectors * sl->sl_ntracks; 447 for (i = 0; i < 8; i++) { 448 spp = &sl->sl_part[i]; 449 npp = &lp->d_partitions[i]; 450 451 /* 452 * SunOS partitions must start on a cylinder boundary. 453 * Note this restriction is forced upon NetBSD/sparc 454 * labels too, since we want to keep both labels 455 * synchronised. 456 */ 457 if (npp->p_offset % secpercyl) 458 return (EINVAL); 459 spp->sdkp_cyloffset = npp->p_offset / secpercyl; 460 spp->sdkp_nsectors = npp->p_size; 461 } 462 sl->sl_magic = SUN_DKMAGIC; 463 464 /* Compute the XOR check. */ 465 sp1 = (u_short *)sl; 466 sp2 = (u_short *)(sl + 1); 467 sl->sl_cksum = cksum = 0; 468 while (sp1 < sp2) 469 cksum ^= *sp1++; 470 sl->sl_cksum = cksum; 471 472 return (0); 473 } 474 475 /* 476 * Search the bad sector table looking for the specified sector. 477 * Return index if found. 478 * Return -1 if not found. 479 */ 480 int 481 isbad(bt, cyl, trk, sec) 482 struct dkbad *bt; 483 int cyl, trk, sec; 484 { 485 int i; 486 long blk, bblk; 487 488 blk = ((long)cyl << 16) + (trk << 8) + sec; 489 for (i = 0; i < 126; i++) { 490 bblk = ((long)bt->bt_bad[i].bt_cyl << 16) + 491 bt->bt_bad[i].bt_trksec; 492 if (blk == bblk) 493 return (i); 494 if (blk < bblk || bblk < 0) 495 break; 496 } 497 return (-1); 498 } 499