1 /* $NetBSD: subr_disk_mbr.c,v 1.13 2005/12/18 17:02:45 dsl 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 32 */ 33 34 /* 35 * Code to find a NetBSD label on a disk that contains an i386 style MBR. 36 * The first NetBSD label found in the 2nd sector of a NetBSD partition 37 * is used. 38 * If we don't find a label searching the MBR, we look at the start of the 39 * disk, if that fails then a label is faked up from the MBR. 40 * 41 * If there isn't a disklabel or anything in the MBR then partition a 42 * is set to cover the whole disk. 43 * Useful for files that contain single filesystems (etc). 44 * 45 * This code will read host endian netbsd labels from little endian MBR. 46 * 47 * Based on the i386 disksubr.c 48 * 49 * Since the mbr only has 32bit fields for sector addresses, we do the same. 50 * 51 * XXX There are potential problems writing labels to disks where there 52 * is only space for 8 netbsd partitions but this code has been compiled 53 * with MAXPARTITIONS=16. 54 */ 55 56 #include <sys/cdefs.h> 57 __KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.13 2005/12/18 17:02:45 dsl Exp $"); 58 59 #include <sys/param.h> 60 #include <sys/systm.h> 61 #include <sys/buf.h> 62 #include <sys/disklabel.h> 63 #include <sys/disk.h> 64 #include <sys/syslog.h> 65 66 #include "opt_mbr.h" 67 68 typedef struct mbr_partition mbr_partition_t; 69 70 #define MBR_LABELSECTOR 1 71 72 #define SCAN_CONTINUE 0 73 #define SCAN_FOUND 1 74 #define SCAN_ERROR 2 75 76 typedef struct mbr_args { 77 struct disklabel *lp; 78 void (*strat)(struct buf *); 79 struct buf *bp; 80 const char *msg; 81 int error; 82 int written; /* number of times we wrote label */ 83 uint label_sector; /* where we found the label */ 84 } mbr_args_t; 85 86 #define READ_LABEL 1 87 #define UPDATE_LABEL 2 88 #define WRITE_LABEL 3 89 static int validate_label(mbr_args_t *, uint, int); 90 static int look_netbsd_part(mbr_args_t *, mbr_partition_t *, int, uint); 91 static int write_netbsd_label(mbr_args_t *, mbr_partition_t *, int, uint); 92 93 static int 94 read_sector(mbr_args_t *a, uint sector) 95 { 96 struct buf *bp = a->bp; 97 int error; 98 99 bp->b_blkno = sector; 100 bp->b_bcount = a->lp->d_secsize; 101 bp->b_flags = (bp->b_flags & ~(B_WRITE | B_DONE)) | B_READ; 102 bp->b_cylinder = sector / a->lp->d_secpercyl; 103 (*a->strat)(bp); 104 error = biowait(bp); 105 if (error != 0) 106 a->error = error; 107 return error; 108 } 109 110 /* 111 * Scan MBR for partitions, call 'action' routine for each. 112 */ 113 114 static int 115 scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, mbr_partition_t *, int, uint)) 116 { 117 mbr_partition_t ptns[MBR_PART_COUNT]; 118 mbr_partition_t *dp; 119 struct mbr_sector *mbr; 120 uint ext_base, this_ext, next_ext; 121 int rval; 122 int i; 123 #ifdef COMPAT_386BSD_MBRPART 124 int dp_386bsd = -1; 125 #endif 126 127 ext_base = 0; 128 this_ext = 0; 129 for (;;) { 130 if (read_sector(a, this_ext)) { 131 a->msg = "dos partition I/O error"; 132 return SCAN_ERROR; 133 } 134 135 /* Note: Magic number is little-endian. */ 136 mbr = (void *)a->bp->b_data; 137 if (mbr->mbr_magic != htole16(MBR_MAGIC)) 138 return SCAN_CONTINUE; 139 140 /* Copy data out of buffer so action can use bp */ 141 memcpy(ptns, &mbr->mbr_parts, sizeof ptns); 142 143 /* look for NetBSD partition */ 144 next_ext = 0; 145 dp = ptns; 146 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 147 if (dp->mbrp_type == 0) 148 continue; 149 if (MBR_IS_EXTENDED(dp->mbrp_type)) { 150 next_ext = le32toh(dp->mbrp_start); 151 continue; 152 } 153 #ifdef COMPAT_386BSD_MBRPART 154 if (dp->mbrp_type == MBR_PTYPE_386BSD) { 155 /* 156 * If more than one matches, take last, 157 * as NetBSD install tool does. 158 */ 159 if (this_ext == 0) 160 dp_386bsd = i; 161 continue; 162 } 163 #endif 164 rval = (*actn)(a, dp, i, this_ext); 165 if (rval != SCAN_CONTINUE) 166 return rval; 167 } 168 if (next_ext == 0) 169 break; 170 if (ext_base == 0) { 171 ext_base = next_ext; 172 next_ext = 0; 173 } 174 next_ext += ext_base; 175 if (next_ext <= this_ext) 176 break; 177 this_ext = next_ext; 178 } 179 #ifdef COMPAT_386BSD_MBRPART 180 if (this_ext == 0 && dp_386bsd != -1) 181 return (*actn)(a, &ptns[dp_386bsd], dp_386bsd, 0); 182 #endif 183 return SCAN_CONTINUE; 184 } 185 186 /* 187 * Attempt to read a disk label from a device 188 * using the indicated strategy routine. 189 * The label must be partly set up before this: 190 * secpercyl, secsize and anything required for a block i/o read 191 * operation in the driver's strategy/start routines 192 * must be filled in before calling us. 193 * 194 * If dos partition table requested, attempt to load it and 195 * find disklabel inside a DOS partition. Also, if bad block 196 * table needed, attempt to extract it as well. Return buffer 197 * for use in signalling errors if requested. 198 * 199 * Returns null on success and an error string on failure. 200 */ 201 const char * 202 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 203 struct cpu_disklabel *osdep) 204 { 205 struct dkbad *bdp; 206 int rval; 207 int i; 208 mbr_args_t a; 209 210 memset(&a, 0, sizeof a); 211 a.lp = lp; 212 a.strat = strat; 213 214 /* minimal requirements for architypal disk label */ 215 if (lp->d_secsize == 0) 216 lp->d_secsize = DEV_BSIZE; 217 if (lp->d_secperunit == 0) 218 lp->d_secperunit = 0x1fffffff; 219 lp->d_npartitions = RAW_PART + 1; 220 for (i = 0; i < RAW_PART; i++) { 221 lp->d_partitions[i].p_size = 0; 222 lp->d_partitions[i].p_offset = 0; 223 } 224 if (lp->d_partitions[RAW_PART].p_size == 0) 225 lp->d_partitions[RAW_PART].p_size = 0x1fffffff; 226 lp->d_partitions[RAW_PART].p_offset = 0; 227 228 /* 229 * Set partition 'a' to be the whole disk. 230 * Cleared if we find an mbr or a netbsd label. 231 */ 232 lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size; 233 lp->d_partitions[0].p_fstype = FS_BSDFFS; 234 235 /* get a buffer and initialize it */ 236 a.bp = geteblk((int)lp->d_secsize); 237 a.bp->b_dev = dev; 238 239 if (osdep) 240 /* 241 * Scan mbr searching for netbsd partition and saving 242 * bios partition information to use if the netbsd one 243 * is absent. 244 */ 245 rval = scan_mbr(&a, look_netbsd_part); 246 else 247 rval = SCAN_CONTINUE; 248 249 if (rval == SCAN_CONTINUE) { 250 /* Look at start of disk */ 251 rval = validate_label(&a, LABELSECTOR, READ_LABEL); 252 if (LABELSECTOR != 0 && rval == SCAN_CONTINUE) 253 rval = validate_label(&a, 0, READ_LABEL); 254 } 255 256 #if 0 257 /* 258 * Save sector where we found the label for the 'don't overwrite 259 * the label' check in bounds_check_with_label. 260 */ 261 if (rval == SCAN_FOUND) 262 xxx->label_sector = a.label_sector; 263 #endif 264 265 /* Obtain bad sector table if requested and present */ 266 if (rval == SCAN_FOUND && osdep && (lp->d_flags & D_BADSECT)) { 267 struct dkbad *db; 268 int blkno; 269 270 bdp = &osdep->bad; 271 i = 0; 272 rval = SCAN_ERROR; 273 do { 274 /* read a bad sector table */ 275 blkno = lp->d_secperunit - lp->d_nsectors + i; 276 if (lp->d_secsize > DEV_BSIZE) 277 blkno *= lp->d_secsize / DEV_BSIZE; 278 else 279 blkno /= DEV_BSIZE / lp->d_secsize; 280 /* if successful, validate, otherwise try another */ 281 if (read_sector(&a, blkno)) { 282 a.msg = "bad sector table I/O error"; 283 continue; 284 } 285 db = (struct dkbad *)(a.bp->b_data); 286 #define DKBAD_MAGIC 0x4321 287 if (db->bt_mbz != 0 || db->bt_flag != DKBAD_MAGIC) { 288 a.msg = "bad sector table corrupted"; 289 continue; 290 } 291 rval = SCAN_FOUND; 292 *bdp = *db; 293 break; 294 } while ((a.bp->b_flags & B_ERROR) && (i += 2) < 10 && 295 i < lp->d_nsectors); 296 } 297 298 brelse(a.bp); 299 if (rval == SCAN_ERROR) 300 return a.msg; 301 return NULL; 302 } 303 304 static int 305 look_netbsd_part(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 306 { 307 struct partition *pp; 308 int ptn_base = ext_base + le32toh(dp->mbrp_start); 309 int rval; 310 311 if ( 312 #ifdef COMPAT_386BSD_MBRPART 313 dp->mbrp_type == MBR_PTYPE_386BSD || 314 #endif 315 dp->mbrp_type == MBR_PTYPE_NETBSD) { 316 rval = validate_label(a, ptn_base + MBR_LABELSECTOR, READ_LABEL); 317 318 #if RAW_PART == 3 319 /* Put actual location where we found the label into ptn 2 */ 320 if (rval == SCAN_FOUND || a->lp->d_partitions[2].p_size == 0) { 321 a->lp->d_partitions[2].p_size = le32toh(dp->mbrp_size); 322 a->lp->d_partitions[2].p_offset = ptn_base; 323 } 324 #endif 325 326 /* If we got a netbsd label look no further */ 327 if (rval == SCAN_FOUND) 328 return rval; 329 } 330 331 /* Install main partitions into e..h and extended into i+ */ 332 if (ext_base == 0) 333 slot += 4; 334 else { 335 slot = 4 + MBR_PART_COUNT; 336 pp = &a->lp->d_partitions[slot]; 337 for (; slot < MAXPARTITIONS; pp++, slot++) { 338 /* This gets called twice - avoid duplicates */ 339 if (pp->p_offset == ptn_base && 340 pp->p_size == le32toh(dp->mbrp_size)) 341 break; 342 if (pp->p_size == 0) 343 break; 344 } 345 } 346 347 if (slot < MAXPARTITIONS) { 348 /* Stop 'a' being the entire disk */ 349 a->lp->d_partitions[0].p_size = 0; 350 a->lp->d_partitions[0].p_fstype = 0; 351 352 /* save partition info */ 353 pp = &a->lp->d_partitions[slot]; 354 pp->p_offset = ptn_base; 355 pp->p_size = le32toh(dp->mbrp_size); 356 pp->p_fstype = xlat_mbr_fstype(dp->mbrp_type); 357 358 if (slot >= a->lp->d_npartitions) 359 a->lp->d_npartitions = slot + 1; 360 } 361 362 return SCAN_CONTINUE; 363 } 364 365 366 static int 367 validate_label(mbr_args_t *a, uint label_sector, int action) 368 { 369 struct disklabel *dlp; 370 char *dlp_lim; 371 int error; 372 373 /* Next, dig out disk label */ 374 if (read_sector(a, label_sector)) { 375 a->msg = "disk label read failed"; 376 return SCAN_ERROR; 377 } 378 379 /* Locate disk label within block and validate */ 380 /* 381 * XXX (dsl) This search may be a waste of time, a lot of other i386 382 * code assumes the label is at offset LABELOFFSET (=0) in the sector. 383 * 384 * If we want to support disks from other netbsd ports, then the 385 * code should also allow for a shorter label nearer the end of 386 * the disk sector, and (IIRC) labels within 8k of the disk start. 387 */ 388 dlp = (void *)a->bp->b_data; 389 if (action != WRITE_LABEL) { 390 dlp_lim = a->bp->b_data + a->lp->d_secsize - sizeof(*dlp); 391 for (;; dlp = (void *)((char *)dlp + sizeof(long))) { 392 if ((char *)dlp > dlp_lim) 393 return SCAN_CONTINUE; 394 if (dlp->d_magic != DISKMAGIC 395 || dlp->d_magic2 != DISKMAGIC) 396 continue; 397 if (dlp->d_npartitions > MAXPARTITIONS 398 || dkcksum(dlp) != 0) { 399 a->msg = "disk label corrupted"; 400 continue; 401 } 402 break; 403 } 404 } 405 406 switch (action) { 407 case READ_LABEL: 408 *a->lp = *dlp; 409 a->label_sector = label_sector; 410 return SCAN_FOUND; 411 case UPDATE_LABEL: 412 case WRITE_LABEL: 413 *dlp = *a->lp; 414 a->bp->b_flags &= ~(B_READ|B_DONE); 415 a->bp->b_flags |= B_WRITE; 416 (*a->strat)(a->bp); 417 error = biowait(a->bp); 418 if (error != 0) { 419 a->error = error; 420 a->msg = "disk label write failed"; 421 return SCAN_ERROR; 422 } 423 a->written++; 424 /* Write label to all mbr partitions */ 425 return SCAN_CONTINUE; 426 default: 427 return SCAN_ERROR; 428 } 429 } 430 431 /* 432 * Check new disk label for sensibility 433 * before setting it. 434 */ 435 int 436 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, 437 struct cpu_disklabel *osdep) 438 { 439 int i; 440 struct partition *opp, *npp; 441 442 /* sanity clause */ 443 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 444 || (nlp->d_secsize % DEV_BSIZE) != 0) 445 return (EINVAL); 446 447 /* special case to allow disklabel to be invalidated */ 448 if (nlp->d_magic == 0xffffffff) { 449 *olp = *nlp; 450 return (0); 451 } 452 453 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 454 dkcksum(nlp) != 0) 455 return (EINVAL); 456 457 /* XXX missing check if other dos partitions will be overwritten */ 458 459 while (openmask != 0) { 460 i = ffs(openmask) - 1; 461 openmask &= ~(1 << i); 462 if (i > nlp->d_npartitions) 463 return (EBUSY); 464 opp = &olp->d_partitions[i]; 465 npp = &nlp->d_partitions[i]; 466 /* 467 * Copy internally-set partition information 468 * if new label doesn't include it. XXX 469 */ 470 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 471 *npp = *opp; 472 continue; 473 } 474 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 475 return (EBUSY); 476 } 477 nlp->d_checksum = 0; 478 nlp->d_checksum = dkcksum(nlp); 479 *olp = *nlp; 480 return (0); 481 } 482 483 484 /* 485 * Write disk label back to device after modification. 486 */ 487 int 488 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, 489 struct cpu_disklabel *osdep) 490 { 491 mbr_args_t a; 492 493 memset(&a, 0, sizeof a); 494 a.lp = lp; 495 a.strat = strat; 496 497 /* get a buffer and initialize it */ 498 a.bp = geteblk((int)lp->d_secsize); 499 a.bp->b_dev = dev; 500 501 if (osdep) 502 /* Write the label to every netbsd mbr partition */ 503 scan_mbr(&a, write_netbsd_label); 504 505 /* and overwrite any label at the start of the volume */ 506 validate_label(&a, LABELSECTOR, UPDATE_LABEL); 507 if (LABELSECTOR != 0) 508 validate_label(&a, 0, UPDATE_LABEL); 509 510 if (a.written == 0 && a.error == 0) 511 a.error = ESRCH; 512 513 brelse(a.bp); 514 return a.error; 515 } 516 517 static int 518 write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base) 519 { 520 int ptn_base = ext_base + le32toh(dp->mbrp_start); 521 522 if (dp->mbrp_type != MBR_PTYPE_NETBSD) 523 return SCAN_CONTINUE; 524 525 return validate_label(a, ptn_base + MBR_LABELSECTOR, WRITE_LABEL); 526 } 527 528 529 /* 530 * Determine the size of the transfer, and make sure it is 531 * within the boundaries of the partition. Adjust transfer 532 * if needed, and signal errors or early completion. 533 */ 534 int 535 bounds_check_with_label(struct disk *dk, struct buf *bp, int wlabel) 536 { 537 struct disklabel *lp = dk->dk_label; 538 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 539 int labelsector = LABELSECTOR; 540 int64_t sz; 541 542 #if RAW_PART == 3 543 labelsector += lp->d_partitions[2].p_offset; 544 #endif 545 546 sz = howmany(bp->b_bcount, lp->d_secsize); 547 548 if (bp->b_blkno + sz > p->p_size) { 549 sz = p->p_size - bp->b_blkno; 550 if (sz == 0) { 551 /* If exactly at end of disk, return EOF. */ 552 bp->b_resid = bp->b_bcount; 553 return (0); 554 } 555 if (sz < 0) { 556 /* If past end of disk, return EINVAL. */ 557 bp->b_error = EINVAL; 558 goto bad; 559 } 560 /* Otherwise, truncate request. */ 561 bp->b_bcount = sz << DEV_BSHIFT; 562 } 563 564 /* Overwriting disk label? */ 565 if (bp->b_blkno + p->p_offset <= labelsector && 566 bp->b_blkno + p->p_offset + sz > labelsector && 567 (bp->b_flags & B_READ) == 0 && !wlabel) { 568 bp->b_error = EROFS; 569 goto bad; 570 } 571 572 /* calculate cylinder for disksort to order transfers with */ 573 bp->b_cylinder = (bp->b_blkno + p->p_offset) / 574 (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl; 575 return (1); 576 577 bad: 578 bp->b_flags |= B_ERROR; 579 return (-1); 580 } 581