1 /* $NetBSD: disksubr.c,v 1.14 2010/03/23 20:01:09 martin 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.14 2010/03/23 20:01:09 martin 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_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 92 { 93 struct buf *bp; 94 struct disklabel *dlp; 95 struct sun_disklabel *slp; 96 int error; 97 98 /* minimal requirements for archtypal disk label */ 99 if (lp->d_secperunit == 0) 100 lp->d_secperunit = 0x1fffffff; 101 if (lp->d_npartitions == 0) { 102 lp->d_npartitions = RAW_PART + 1; 103 if (lp->d_partitions[RAW_PART].p_size == 0) 104 lp->d_partitions[RAW_PART].p_size = 0x1fffffff; 105 lp->d_partitions[RAW_PART].p_offset = 0; 106 } 107 108 /* obtain buffer to probe drive with */ 109 bp = geteblk((int)lp->d_secsize); 110 111 /* next, dig out disk label */ 112 bp->b_dev = dev; 113 bp->b_blkno = LABELSECTOR; 114 bp->b_cylinder = 0; 115 bp->b_bcount = lp->d_secsize; 116 bp->b_flags |= B_READ; 117 (*strat)(bp); 118 119 /* if successful, locate disk label within block and validate */ 120 error = biowait(bp); 121 if (error == 0) { 122 /* Save the whole block in case it has info we need. */ 123 memcpy(clp->cd_block, bp->b_data, sizeof(clp->cd_block)); 124 } 125 brelse(bp, 0); 126 if (error) 127 return ("disk label read error"); 128 129 /* Check for a NetBSD disk label at LABELOFFSET */ 130 dlp = (struct disklabel *) (clp->cd_block + LABELOFFSET); 131 if (dlp->d_magic == DISKMAGIC) { 132 if (dkcksum(dlp)) 133 return ("NetBSD disk label corrupted"); 134 *lp = *dlp; 135 return (NULL); 136 } 137 138 /* Check for a Sun disk label (for PROM compatibility). */ 139 slp = (struct sun_disklabel *) clp->cd_block; 140 if (slp->sl_magic == SUN_DKMAGIC) 141 return (disklabel_sun_to_bsd(clp->cd_block, lp)); 142 143 /* 144 * Check for a NetBSD disk label somewhere in LABELSECTOR 145 * (compat with others big-endian boxes) 146 */ 147 for (dlp = (struct disklabel *)clp->cd_block; 148 dlp <= (struct disklabel *)((char *)clp->cd_block + 149 DEV_BSIZE - sizeof(*dlp)); 150 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) { 151 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { 152 continue; 153 } 154 if (dlp->d_npartitions > MAXPARTITIONS || dkcksum(dlp) != 0) 155 return("NetBSD disk label corrupted"); 156 else { 157 *lp = *dlp; 158 return(NULL); 159 } 160 } 161 162 memset(clp->cd_block, 0, sizeof(clp->cd_block)); 163 return ("no disk label"); 164 } 165 166 /* 167 * Check new disk label for sensibility 168 * before setting it. 169 */ 170 int 171 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, struct cpu_disklabel *clp) 172 { 173 int i; 174 struct partition *opp, *npp; 175 176 /* sanity clause */ 177 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 || 178 (nlp->d_secsize % DEV_BSIZE) != 0) 179 return(EINVAL); 180 181 /* special case to allow disklabel to be invalidated */ 182 if (nlp->d_magic == 0xffffffff) { 183 *olp = *nlp; 184 return (0); 185 } 186 187 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 188 dkcksum(nlp) != 0) 189 return (EINVAL); 190 191 while ((i = ffs(openmask)) != 0) { 192 i--; 193 openmask &= ~(1 << i); 194 if (nlp->d_npartitions <= i) 195 return (EBUSY); 196 opp = &olp->d_partitions[i]; 197 npp = &nlp->d_partitions[i]; 198 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 199 return (EBUSY); 200 } 201 202 *olp = *nlp; 203 return (0); 204 } 205 206 /* 207 * Write disk label back to device after modification. 208 * Current label is already in clp->cd_block[] 209 */ 210 int 211 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 212 { 213 struct buf *bp; 214 int error; 215 struct disklabel *dlp; 216 struct sun_disklabel *slp; 217 218 /* 219 * Embed native label in a piece of wasteland. 220 */ 221 if (sizeof(struct disklabel) > sizeof slp->sl_bsdlabel) 222 return EFBIG; 223 224 slp = (struct sun_disklabel *)clp->cd_block; 225 memset(slp->sl_bsdlabel, 0, sizeof(slp->sl_bsdlabel)); 226 dlp = (struct disklabel *)slp->sl_bsdlabel; 227 *dlp = *lp; 228 229 /* Build a SunOS compatible label around the native label */ 230 error = disklabel_bsd_to_sun(lp, clp->cd_block); 231 if (error) 232 return (error); 233 234 /* Get a buffer and copy the new label into it. */ 235 bp = geteblk((int)lp->d_secsize); 236 memcpy(bp->b_data, clp->cd_block, sizeof(clp->cd_block)); 237 238 /* Write out the updated label. */ 239 bp->b_dev = dev; 240 bp->b_blkno = LABELSECTOR; 241 bp->b_cylinder = 0; 242 bp->b_bcount = lp->d_secsize; 243 bp->b_flags |= B_WRITE; 244 (*strat)(bp); 245 error = biowait(bp); 246 brelse(bp, 0); 247 248 return (error); 249 } 250 251 /************************************************************************ 252 * 253 * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c 254 * and then substantially rewritten by Gordon W. Ross 255 * 256 ************************************************************************/ 257 258 /* What partition types to assume for Sun disklabels: */ 259 static u_char 260 sun_fstypes[8] = { 261 FS_BSDFFS, /* a */ 262 FS_SWAP, /* b */ 263 FS_OTHER, /* c - whole disk */ 264 FS_BSDFFS, /* d */ 265 FS_BSDFFS, /* e */ 266 FS_BSDFFS, /* f */ 267 FS_BSDFFS, /* g */ 268 FS_BSDFFS, /* h */ 269 }; 270 271 /* 272 * Given a SunOS disk label, set lp to a BSD disk label. 273 * Returns NULL on success, else an error string. 274 * 275 * The BSD label is cleared out before this is called. 276 */ 277 static const char * 278 disklabel_sun_to_bsd(char *cp, struct disklabel *lp) 279 { 280 struct sun_disklabel *sl; 281 struct partition *npp; 282 struct sun_dkpart *spp; 283 int i, secpercyl; 284 unsigned int secpblck; 285 u_short cksum, *sp1, *sp2; 286 287 sl = (struct sun_disklabel *)cp; 288 289 /* Verify the XOR check. */ 290 sp1 = (u_short *)sl; 291 sp2 = (u_short *)(sl + 1); 292 cksum = 0; 293 while (sp1 < sp2) 294 cksum ^= *sp1++; 295 if (cksum != 0) 296 return("SunOS disk label, bad checksum"); 297 298 /* Format conversion. */ 299 lp->d_magic = DISKMAGIC; 300 lp->d_magic2 = DISKMAGIC; 301 memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname)); 302 303 secpblck = lp->d_secsize / 512; 304 if (secpblck == 0) secpblck = 1; /* can't happen */ 305 lp->d_secsize = 512; 306 lp->d_nsectors = sl->sl_nsectors; 307 lp->d_ntracks = sl->sl_ntracks; 308 lp->d_ncylinders = sl->sl_ncylinders; 309 310 secpercyl = sl->sl_nsectors * sl->sl_ntracks; 311 lp->d_secpercyl = secpercyl; 312 lp->d_secperunit = secpercyl * sl->sl_ncylinders; 313 314 lp->d_sparespercyl = sl->sl_sparespercyl; 315 lp->d_acylinders = sl->sl_acylinders; 316 lp->d_rpm = sl->sl_rpm; 317 lp->d_interleave = sl->sl_interleave; 318 319 lp->d_npartitions = 8; 320 /* These are as defined in <ufs/ffs/fs.h> */ 321 lp->d_bbsize = 8192; /* XXX */ 322 lp->d_sbsize = 8192; /* XXX */ 323 324 for (i = 0; i < 8; i++) { 325 spp = &sl->sl_part[i]; 326 npp = &lp->d_partitions[i]; 327 328 if (npp->p_fstype == FS_ISO9660 329 && spp->sdkp_cyloffset * secpercyl == npp->p_offset*secpblck 330 && spp->sdkp_nsectors <= npp->p_size*secpblck 331 && npp->p_size > 0 && spp->sdkp_nsectors > 0) { 332 /* 333 * This happens for example on sunlabel'd hybrid 334 * (ffs + ISO9660) CDs, like our install CDs. 335 * The cd driver has initialized a valid ISO9660 336 * partition (including session parameters), so 337 * we better not overwrite it. 338 */ 339 npp->p_offset *= secpblck; 340 npp->p_size = spp->sdkp_nsectors; 341 npp->p_cdsession *= secpblck; 342 continue; 343 } 344 npp->p_offset = spp->sdkp_cyloffset * secpercyl; 345 npp->p_size = spp->sdkp_nsectors; 346 if (npp->p_size == 0) { 347 npp->p_fstype = FS_UNUSED; 348 } else { 349 npp->p_fstype = sun_fstypes[i]; 350 if (npp->p_fstype == FS_BSDFFS) { 351 /* 352 * The sun label does not store the FFS fields, 353 * so just set them with default values here. 354 */ 355 npp->p_fsize = 1024; 356 npp->p_frag = 8; 357 npp->p_cpg = 16; 358 } 359 } 360 } 361 362 lp->d_checksum = 0; 363 lp->d_checksum = dkcksum(lp); 364 return (NULL); 365 } 366 367 /* 368 * Given a BSD disk label, update the Sun disklabel 369 * pointed to by cp with the new info. Note that the 370 * Sun disklabel may have other info we need to keep. 371 * Returns zero or error code. 372 */ 373 static int 374 disklabel_bsd_to_sun(struct disklabel *lp, char *cp) 375 { 376 struct sun_disklabel *sl; 377 struct partition *npp; 378 struct sun_dkpart *spp; 379 int i, secpercyl; 380 u_short cksum, *sp1, *sp2; 381 382 if (lp->d_secsize != 512) 383 return (EINVAL); 384 385 sl = (struct sun_disklabel *)cp; 386 387 /* 388 * Format conversion. 389 */ 390 memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname)); 391 sl->sl_rpm = lp->d_rpm; 392 sl->sl_pcylinders = lp->d_ncylinders + lp->d_acylinders; /* XXX */ 393 sl->sl_sparespercyl = lp->d_sparespercyl; 394 sl->sl_interleave = lp->d_interleave; 395 sl->sl_ncylinders = lp->d_ncylinders; 396 sl->sl_acylinders = lp->d_acylinders; 397 sl->sl_ntracks = lp->d_ntracks; 398 sl->sl_nsectors = lp->d_nsectors; 399 400 secpercyl = sl->sl_nsectors * sl->sl_ntracks; 401 for (i = 0; i < 8; i++) { 402 spp = &sl->sl_part[i]; 403 npp = &lp->d_partitions[i]; 404 405 /* 406 * SunOS partitions must start on a cylinder boundary. 407 * Note this restriction is forced upon NetBSD/sparc 408 * labels too, since we want to keep both labels 409 * synchronised. 410 */ 411 if (npp->p_offset % secpercyl) 412 return (EINVAL); 413 spp->sdkp_cyloffset = npp->p_offset / secpercyl; 414 spp->sdkp_nsectors = npp->p_size; 415 } 416 sl->sl_magic = SUN_DKMAGIC; 417 418 /* Compute the XOR check. */ 419 sp1 = (u_short *)sl; 420 sp2 = (u_short *)(sl + 1); 421 sl->sl_cksum = cksum = 0; 422 while (sp1 < sp2) 423 cksum ^= *sp1++; 424 sl->sl_cksum = cksum; 425 426 return (0); 427 } 428 429 /* 430 * Search the bad sector table looking for the specified sector. 431 * Return index if found. 432 * Return -1 if not found. 433 */ 434 int 435 isbad(struct dkbad *bt, int cyl, int trk, int sec) 436 { 437 int i; 438 long blk, bblk; 439 440 blk = ((long)cyl << 16) + (trk << 8) + sec; 441 for (i = 0; i < 126; i++) { 442 bblk = ((long)bt->bt_bad[i].bt_cyl << 16) + 443 bt->bt_bad[i].bt_trksec; 444 if (blk == bblk) 445 return (i); 446 if (blk < bblk || bblk < 0) 447 break; 448 } 449 return (-1); 450 } 451