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