1 /* $NetBSD: disksubr.c,v 1.35 2008/01/02 11:48:23 ad Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Leo Weppelman. 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 Leo Weppelman. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.35 2008/01/02 11:48:23 ad Exp $"); 35 36 #ifndef DISKLABEL_NBDA 37 #define DISKLABEL_NBDA /* required */ 38 #endif 39 40 #include "opt_compat_netbsd.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/buf.h> 45 #include <ufs/ufs/dinode.h> 46 #include <ufs/ffs/fs.h> 47 #include <sys/disk.h> 48 #include <sys/disklabel.h> 49 #include <machine/ahdilabel.h> 50 51 /* 52 * BBSIZE in <ufs/ffs/fs.h> must be greater than 53 * or equal to BBMINSIZE in <machine/disklabel.h> 54 */ 55 #if BBSIZE < BBMINSIZE 56 #error BBSIZE smaller than BBMINSIZE 57 #endif 58 59 static void ck_label __P((struct disklabel *, struct cpu_disklabel *)); 60 static int bsd_label __P((dev_t, void (*)(struct buf *), 61 struct disklabel *, u_int, u_int *)); 62 static int ahdi_label __P((dev_t, void (*)(struct buf *), 63 struct disklabel *, struct cpu_disklabel *)); 64 static void ahdi_to_bsd __P((struct disklabel *, struct ahdi_ptbl *)); 65 static u_int ahdi_getparts __P((dev_t, void (*)(struct buf *), u_int, 66 u_int, u_int, struct ahdi_ptbl *)); 67 68 /* 69 * Attempt to read a disk label from a device using the 70 * indicated strategy routine. The label must be partly 71 * set up before this: 72 * secpercyl and anything required in the strategy routine 73 * (e.g. sector size) must be filled in before calling us. 74 * Returns NULL on success and an error string on failure. 75 */ 76 const char * 77 readdisklabel(dev, strat, lp, clp) 78 dev_t dev; 79 void (*strat)(struct buf *); 80 struct disklabel *lp; 81 struct cpu_disklabel *clp; 82 { 83 int e; 84 85 if (clp != NULL) 86 bzero(clp, sizeof *clp); 87 else printf("Warning: clp == NULL\n"); 88 89 /* 90 * Give some guaranteed validity to the disk label. 91 */ 92 if (lp->d_secsize == 0) 93 lp->d_secsize = DEV_BSIZE; 94 if (lp->d_secperunit == 0) 95 lp->d_secperunit = 0x1fffffff; 96 if (lp->d_secpercyl == 0) 97 return("Zero secpercyl"); 98 99 /* 100 * Some parts of the kernel (see scsipi/cd.c for an example) 101 * assume that stuff they already had setup in d_partitions 102 * is still there after reading the disklabel. Hence the 103 * 'if 0' 104 */ 105 #if 0 106 bzero(lp->d_partitions, sizeof lp->d_partitions); 107 #endif 108 109 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 110 lp->d_npartitions = RAW_PART + 1; 111 lp->d_bbsize = BBSIZE; 112 lp->d_sbsize = SBLOCKSIZE; 113 114 #ifdef DISKLABEL_NBDA 115 /* Try the native NetBSD/Atari format first. */ 116 e = bsd_label(dev, strat, lp, 0, clp != NULL ? &clp->cd_label : NULL); 117 #endif 118 #if 0 119 /* Other label formats go here. */ 120 if (e > 0) 121 e = foo_label(dev, strat, lp, ...); 122 #endif 123 #ifdef DISKLABEL_AHDI 124 /* The unprotected AHDI format comes last. */ 125 if (e > 0 && (clp != NULL)) 126 e = ahdi_label(dev, strat, lp, clp); 127 #endif 128 if (e < 0) 129 return("I/O error"); 130 131 /* Unknown format or uninitialized volume? */ 132 if (e > 0) 133 uprintf("Warning: unknown disklabel format" 134 "- assuming empty disk\n"); 135 136 /* Calulate new checksum. */ 137 lp->d_magic = lp->d_magic2 = DISKMAGIC; 138 lp->d_checksum = 0; 139 lp->d_checksum = dkcksum(lp); 140 141 return(NULL); 142 } 143 144 /* 145 * Check new disk label for sensibility before setting it. 146 */ 147 int 148 setdisklabel(olp, nlp, openmask, clp) 149 struct disklabel *olp, *nlp; 150 u_long openmask; 151 struct cpu_disklabel *clp; 152 { 153 /* special case to allow disklabel to be invalidated */ 154 if (nlp->d_magic == 0xffffffff) { 155 *olp = *nlp; 156 return(0); 157 } 158 159 /* sanity clause */ 160 if (nlp->d_secpercyl == 0 || nlp->d_npartitions > MAXPARTITIONS 161 || nlp->d_secsize == 0 || (nlp->d_secsize % DEV_BSIZE) != 0 162 || nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC 163 || dkcksum(nlp) != 0) 164 return(EINVAL); 165 166 #ifdef DISKLABEL_AHDI 167 if (clp && clp->cd_bblock) 168 ck_label(nlp, clp); 169 #endif 170 while (openmask) { 171 struct partition *op, *np; 172 int i = ffs(openmask) - 1; 173 openmask &= ~(1 << i); 174 if (i >= nlp->d_npartitions) 175 return(EBUSY); 176 op = &olp->d_partitions[i]; 177 np = &nlp->d_partitions[i]; 178 if (np->p_offset != op->p_offset || np->p_size < op->p_size) 179 return(EBUSY); 180 /* 181 * Copy internally-set partition information 182 * if new label doesn't include it. XXX 183 */ 184 if (np->p_fstype == FS_UNUSED && op->p_fstype != FS_UNUSED) { 185 np->p_fstype = op->p_fstype; 186 np->p_fsize = op->p_fsize; 187 np->p_frag = op->p_frag; 188 np->p_cpg = op->p_cpg; 189 } 190 } 191 nlp->d_checksum = 0; 192 nlp->d_checksum = dkcksum(nlp); 193 *olp = *nlp; 194 return(0); 195 } 196 197 /* 198 * Write disk label back to device after modification. 199 */ 200 int 201 writedisklabel(dev, strat, lp, clp) 202 dev_t dev; 203 void (*strat)(struct buf *); 204 struct disklabel *lp; 205 struct cpu_disklabel *clp; 206 { 207 struct buf *bp; 208 u_int blk; 209 int rv; 210 211 blk = clp->cd_bblock; 212 if (blk == NO_BOOT_BLOCK) 213 return(ENXIO); 214 215 bp = geteblk(BBMINSIZE); 216 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART); 217 bp->b_flags |= B_READ; 218 bp->b_bcount = BBMINSIZE; 219 bp->b_blkno = blk; 220 bp->b_cylinder = blk / lp->d_secpercyl; 221 (*strat)(bp); 222 rv = biowait(bp); 223 if (!rv) { 224 struct bootblock *bb = (struct bootblock *)bp->b_data; 225 /* 226 * Allthough the disk pack label may appear anywhere 227 * in the boot block while reading, it is always 228 * written at a fixed location. 229 */ 230 if (clp->cd_label != LABELOFFSET) { 231 clp->cd_label = LABELOFFSET; 232 bzero(bb, sizeof(*bb)); 233 } 234 bb->bb_magic = (blk == 0) ? NBDAMAGIC : AHDIMAGIC; 235 BBSETLABEL(bb, lp); 236 237 bp->b_oflags &= ~(BO_DONE); 238 bp->b_flags &= ~(B_READ); 239 bp->b_flags |= B_WRITE; 240 bp->b_bcount = BBMINSIZE; 241 bp->b_blkno = blk; 242 bp->b_cylinder = blk / lp->d_secpercyl; 243 (*strat)(bp); 244 rv = biowait(bp); 245 } 246 brelse(bp, 0); 247 return(rv); 248 } 249 250 /* 251 * Read bootblock at block `blkno' and check 252 * if it contains a valid NetBSD disk label. 253 * 254 * Returns: 0 if successful, 255 * -1 if an I/O error occurred, 256 * +1 if no valid label was found. 257 */ 258 static int 259 bsd_label(dev, strat, label, blkno, offsetp) 260 dev_t dev; 261 void (*strat)(struct buf *); 262 struct disklabel *label; 263 u_int blkno, 264 *offsetp; 265 { 266 struct buf *bp; 267 int rv; 268 269 bp = geteblk(BBMINSIZE); 270 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART); 271 bp->b_flags |= B_READ; 272 bp->b_bcount = BBMINSIZE; 273 bp->b_blkno = blkno; 274 bp->b_cylinder = blkno / label->d_secpercyl; 275 (*strat)(bp); 276 277 rv = -1; 278 if (!biowait(bp)) { 279 struct bootblock *bb; 280 u_int32_t *p, *end; 281 282 rv = 1; 283 bb = (struct bootblock *)bp->b_data; 284 end = (u_int32_t *)((char *)&bb[1] - sizeof(struct disklabel)); 285 for (p = (u_int32_t *)bb; p < end; ++p) { 286 struct disklabel *dl = (struct disklabel *)&p[1]; 287 /* 288 * Compatibility kludge: the boot block magic number is 289 * new in 1.1A, in previous versions the disklabel was 290 * stored at the end of the boot block (offset 7168). 291 */ 292 if ( ( (p[0] == NBDAMAGIC && blkno == 0) 293 || (p[0] == AHDIMAGIC && blkno != 0) 294 #ifdef COMPAT_11 295 || (char *)dl - (char *)bb == 7168 296 #endif 297 ) 298 && dl->d_npartitions <= MAXPARTITIONS 299 && dl->d_magic2 == DISKMAGIC 300 && dl->d_magic == DISKMAGIC 301 && dkcksum(dl) == 0 302 ) { 303 if (offsetp != NULL) 304 *offsetp = (char *)dl - (char *)bb; 305 *label = *dl; 306 rv = 0; 307 break; 308 } 309 } 310 } 311 brelse(bp, 0); 312 return(rv); 313 } 314 315 #ifdef DISKLABEL_AHDI 316 /* 317 * Check for consistency between the NetBSD partition table 318 * and the AHDI auxiliary root sectors. There's no good reason 319 * to force such consistency, but issuing a warning may help 320 * an inexperienced sysadmin to prevent corruption of AHDI 321 * partitions. 322 */ 323 static void 324 ck_label(dl, cdl) 325 struct disklabel *dl; 326 struct cpu_disklabel *cdl; 327 { 328 u_int *rp, i; 329 330 for (i = 0; i < dl->d_npartitions; ++i) { 331 struct partition *p = &dl->d_partitions[i]; 332 if (i == RAW_PART || p->p_size == 0) 333 continue; 334 if ( (p->p_offset >= cdl->cd_bslst 335 && p->p_offset <= cdl->cd_bslend) 336 || (cdl->cd_bslst >= p->p_offset 337 && cdl->cd_bslst < p->p_offset + p->p_size)) { 338 uprintf("Warning: NetBSD partition %c includes" 339 " AHDI bad sector list\n", 'a'+i); 340 } 341 for (rp = &cdl->cd_roots[0]; *rp; ++rp) { 342 if (*rp >= p->p_offset 343 && *rp < p->p_offset + p->p_size) { 344 uprintf("Warning: NetBSD partition %c" 345 " includes AHDI auxiliary root\n", 'a'+i); 346 } 347 } 348 } 349 } 350 351 /* 352 * Check volume for the existence of an AHDI label. Fetch 353 * NetBSD label from NBD or RAW partition, or otherwise 354 * create a fake NetBSD label based on the AHDI label. 355 * 356 * Returns: 0 if successful, 357 * -1 if an I/O error occurred, 358 * +1 if no valid AHDI label was found. 359 */ 360 int 361 ahdi_label(dev, strat, dl, cdl) 362 dev_t dev; 363 void (*strat)(struct buf *); 364 struct disklabel *dl; 365 struct cpu_disklabel *cdl; 366 { 367 struct ahdi_ptbl apt; 368 u_int i; 369 int j; 370 371 /* 372 * The AHDI format requires a specific block size. 373 */ 374 if (dl->d_secsize != AHDI_BSIZE) 375 return(1); 376 377 /* 378 * Fetch the AHDI partition descriptors. 379 */ 380 apt.at_cdl = cdl; 381 apt.at_nroots = apt.at_nparts = 0; 382 i = ahdi_getparts(dev, strat, dl->d_secpercyl, 383 AHDI_BBLOCK, AHDI_BBLOCK, &apt); 384 if (i) { 385 if (i < dl->d_secperunit) 386 return(-1); /* disk read error */ 387 else return(1); /* reading past end of medium */ 388 } 389 390 /* 391 * Perform sanity checks. 392 */ 393 if (apt.at_bslst == 0 || apt.at_bslend == 0) { 394 /* 395 * Illegal according to Atari, however some hd-utils 396 * use it - notably ICD *sigh* 397 * Work around it..... 398 */ 399 apt.at_bslst = apt.at_bslend = 0; 400 uprintf("Warning: Illegal 'bad sector list' format" 401 "- assuming non exists\n"); 402 } 403 if (apt.at_hdsize == 0 || apt.at_nparts == 0) /* unlikely */ 404 return(1); 405 if (apt.at_nparts > AHDI_MAXPARTS) /* XXX kludge */ 406 return(-1); 407 for (i = 0; i < apt.at_nparts; ++i) { 408 struct ahdi_part *p1 = &apt.at_parts[i]; 409 410 for (j = 0; j < apt.at_nroots; ++j) { 411 u_int aux = apt.at_roots[j]; 412 if (aux >= p1->ap_st && aux <= p1->ap_end) 413 return(1); 414 } 415 for (j = i + 1; j < apt.at_nparts; ++j) { 416 struct ahdi_part *p2 = &apt.at_parts[j]; 417 if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end) 418 return(1); 419 if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end) 420 return(1); 421 } 422 if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend) 423 return(1); 424 if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end) 425 return(1); 426 } 427 428 /* 429 * Search for a NetBSD disk label 430 */ 431 apt.at_bblock = NO_BOOT_BLOCK; 432 for (i = 0; i < apt.at_nparts; ++i) { 433 struct ahdi_part *pd = &apt.at_parts[i]; 434 u_int id = *((u_int32_t *)&pd->ap_flg); 435 if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) { 436 u_int blkno = pd->ap_st; 437 j = bsd_label(dev, strat, dl, blkno, &apt.at_label); 438 if (j < 0) { 439 return(j); /* I/O error */ 440 } 441 if (!j) { 442 apt.at_bblock = blkno; /* got it */ 443 ck_label(dl, cdl); 444 return(0); 445 } 446 /* 447 * Not yet, but if this is the first NBD partition 448 * on this volume, we'll mark it anyway as a possible 449 * destination for future writedisklabel() calls, just 450 * in case there is no valid disk label on any of the 451 * other AHDI partitions. 452 */ 453 if (id == AHDI_PID_NBD 454 && apt.at_bblock == NO_BOOT_BLOCK) 455 apt.at_bblock = blkno; 456 } 457 } 458 459 /* 460 * No NetBSD disk label on this volume, use the AHDI 461 * label to create a fake BSD label. If there is no 462 * NBD partition on this volume either, subsequent 463 * writedisklabel() calls will fail. 464 */ 465 ahdi_to_bsd(dl, &apt); 466 return(0); 467 } 468 469 /* 470 * Map the AHDI partition table to the NetBSD table. 471 * 472 * This means: 473 * Part 0 : Root 474 * Part 1 : Swap 475 * Part 2 : Whole disk 476 * Part 3.. : User partitions 477 * 478 * When more than one root partition is found, only the first one will 479 * be recognized as such. The others are mapped as user partitions. 480 */ 481 static void 482 ahdi_to_bsd(dl, apt) 483 struct disklabel *dl; 484 struct ahdi_ptbl *apt; 485 { 486 int i, have_root, user_part; 487 488 user_part = RAW_PART; 489 have_root = (apt->at_bblock != NO_BOOT_BLOCK); 490 491 for (i = 0; i < apt->at_nparts; ++i) { 492 struct ahdi_part *pd = &apt->at_parts[i]; 493 int fst, pno = -1; 494 495 switch (*((u_int32_t *)&pd->ap_flg)) { 496 case AHDI_PID_NBD: 497 /* 498 * If this partition has been marked as the 499 * first NBD partition, it will be the root 500 * partition. 501 */ 502 if (pd->ap_st == apt->at_bblock) 503 pno = 0; 504 /* FALL THROUGH */ 505 case AHDI_PID_NBR: 506 /* 507 * If there is no NBD partition and this is 508 * the first NBR partition, it will be the 509 * root partition. 510 */ 511 if (!have_root) { 512 have_root = 1; 513 pno = 0; 514 } 515 /* FALL THROUGH */ 516 case AHDI_PID_NBU: 517 fst = FS_BSDFFS; 518 break; 519 case AHDI_PID_NBS: 520 case AHDI_PID_SWP: 521 if (dl->d_partitions[1].p_size == 0) 522 pno = 1; 523 fst = FS_SWAP; 524 break; 525 case AHDI_PID_BGM: 526 case AHDI_PID_GEM: 527 fst = FS_MSDOS; 528 break; 529 default: 530 fst = FS_OTHER; 531 break; 532 } 533 if (pno < 0) { 534 if((pno = user_part + 1) >= MAXPARTITIONS) 535 continue; 536 user_part = pno; 537 } 538 dl->d_partitions[pno].p_size = pd->ap_end - pd->ap_st + 1; 539 dl->d_partitions[pno].p_offset = pd->ap_st; 540 dl->d_partitions[pno].p_fstype = fst; 541 } 542 dl->d_npartitions = user_part + 1; 543 } 544 545 /* 546 * Fetch the AHDI partitions and auxiliary roots. 547 * 548 * Returns: 0 if successful, 549 * otherwise an I/O error occurred, and the 550 * number of the offending block is returned. 551 */ 552 static u_int 553 ahdi_getparts(dev, strat, secpercyl, rsec, esec, apt) 554 dev_t dev; 555 void (*strat)(struct buf *); 556 u_int secpercyl, 557 rsec, esec; 558 struct ahdi_ptbl *apt; 559 { 560 struct ahdi_part *part, *end; 561 struct ahdi_root *root; 562 struct buf *bp; 563 u_int rv; 564 565 bp = geteblk(AHDI_BSIZE); 566 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART); 567 bp->b_flags |= B_READ; 568 bp->b_bcount = AHDI_BSIZE; 569 bp->b_blkno = rsec; 570 bp->b_cylinder = rsec / secpercyl; 571 (*strat)(bp); 572 if (biowait(bp)) { 573 rv = rsec + (rsec == 0); 574 goto done; 575 } 576 root = (struct ahdi_root *)bp->b_data; 577 578 if (rsec == AHDI_BBLOCK) 579 end = &root->ar_parts[AHDI_MAXRPD]; 580 else end = &root->ar_parts[AHDI_MAXARPD]; 581 for (part = root->ar_parts; part < end; ++part) { 582 u_int id = *((u_int32_t *)&part->ap_flg); 583 if (!(id & 0x01000000)) 584 continue; 585 if ((id &= 0x00ffffff) == AHDI_PID_XGM) { 586 u_int offs = part->ap_st + esec; 587 if (apt->at_nroots < AHDI_MAXROOTS) 588 apt->at_roots[apt->at_nroots] = offs; 589 apt->at_nroots += 1; 590 rv = ahdi_getparts(dev, strat, secpercyl, offs, 591 (esec == AHDI_BBLOCK) ? offs : esec, apt); 592 if (rv) 593 goto done; 594 continue; 595 } 596 else if (apt->at_nparts < AHDI_MAXPARTS) { 597 struct ahdi_part *p = &apt->at_parts[apt->at_nparts]; 598 *((u_int32_t *)&p->ap_flg) = id; 599 p->ap_st = part->ap_st + rsec; 600 p->ap_end = p->ap_st + part->ap_size - 1; 601 } 602 apt->at_nparts += 1; 603 } 604 apt->at_hdsize = root->ar_hdsize; 605 apt->at_bslst = root->ar_bslst; 606 apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1; 607 rv = 0; 608 done: 609 brelse(bp, 0); 610 return(rv); 611 } 612 #endif /* DISKLABEL_AHDI */ 613