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