1 /* $NetBSD: dkwedge_bsdlabel.c,v 1.19 2014/03/31 11:25:49 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Adapted from kern/subr_disk_mbr.c: 34 * 35 * Copyright (c) 1982, 1986, 1988 Regents of the University of California. 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 1. Redistributions of source code must retain the above copyright 42 * notice, this list of conditions and the following disclaimer. 43 * 2. Redistributions in binary form must reproduce the above copyright 44 * notice, this list of conditions and the following disclaimer in the 45 * documentation and/or other materials provided with the distribution. 46 * 3. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 * 62 * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 63 */ 64 65 /* 66 * 4.4BSD disklabel support for disk wedges 67 * 68 * Here is the basic search algorithm in use here: 69 * 70 * For historical reasons, we scan for x86-style MBR partitions looking 71 * for a MBR_PTYPE_NETBSD (or MBR_PTYPE_386BSD) partition. The first 72 * 4.4BSD disklabel found in the 2nd sector of such a partition is used. 73 * We assume that the 4.4BSD disklabel describes all partitions on the 74 * disk; we do not use any partition information from the MBR partition 75 * table. 76 * 77 * If that fails, then we fall back on a table of known locations for 78 * various platforms. 79 */ 80 81 #include <sys/cdefs.h> 82 __KERNEL_RCSID(0, "$NetBSD: dkwedge_bsdlabel.c,v 1.19 2014/03/31 11:25:49 martin Exp $"); 83 84 #include <sys/param.h> 85 #ifdef _KERNEL 86 #include <sys/systm.h> 87 #endif 88 #include <sys/proc.h> 89 #include <sys/errno.h> 90 #include <sys/disk.h> 91 #include <sys/vnode.h> 92 #include <sys/malloc.h> 93 94 #include <sys/bootblock.h> 95 #include <sys/disklabel.h> 96 97 #define BSD44_MBR_LABELSECTOR 1 98 99 #define DISKLABEL_SIZE(x) \ 100 (offsetof(struct disklabel, d_partitions) + \ 101 (sizeof(struct partition) * (x))) 102 103 /* 104 * Note the smallest MAXPARTITIONS was 8, so we allow a disklabel 105 * that size to be locted at the end of the sector. 106 */ 107 #define DISKLABEL_MINSIZE DISKLABEL_SIZE(8) 108 109 /* 110 * Table of known platform-specific disklabel locations. 111 */ 112 static const struct disklabel_location { 113 daddr_t label_sector; /* sector containing label */ 114 size_t label_offset; /* byte offset of label in sector */ 115 } disklabel_locations[] = { 116 { 0, 0 }, /* mvme68k, next68k */ 117 { 0, 64 }, /* algor, alpha, amiga, amigappc, evbmips, evbppc, 118 luna68k, mac68k, macppc, news68k, newsmips, 119 pc532, pdp11, pmax, vax, x68k */ 120 { 0, 128 }, /* sparc, sun68k */ 121 { 1, 0 }, /* amd64, arc, arm, bebox, cobalt, evbppc, hppa, 122 hpcarm, hpcmips, i386, ibmnws, mipsco, mvmeppc, 123 ofppc, playstation2, pmppc, prep, sandpoint, 124 sbmips, sgimips, sh3 */ 125 /* XXX atari is weird */ 126 { 2, 0 }, /* cesfic, hp300 */ 127 128 { -1, 0 }, 129 }; 130 131 #define SCAN_CONTINUE 0 132 #define SCAN_FOUND 1 133 #define SCAN_ERROR 2 134 135 typedef struct mbr_args { 136 struct disk *pdk; 137 struct vnode *vp; 138 void *buf; 139 int error; 140 } mbr_args_t; 141 142 static const char * 143 bsdlabel_fstype_to_str(uint8_t fstype) 144 { 145 const char *str; 146 147 switch (fstype) { 148 case FS_UNUSED: str = DKW_PTYPE_UNUSED; break; 149 case FS_SWAP: str = DKW_PTYPE_SWAP; break; 150 case FS_BSDFFS: str = DKW_PTYPE_FFS; break; 151 case FS_MSDOS: str = DKW_PTYPE_FAT; break; 152 case FS_BSDLFS: str = DKW_PTYPE_LFS; break; 153 case FS_ISO9660: str = DKW_PTYPE_ISO9660; break; 154 case FS_ADOS: str = DKW_PTYPE_AMIGADOS; break; 155 case FS_HFS: str = DKW_PTYPE_APPLEHFS; break; 156 case FS_FILECORE: str = DKW_PTYPE_FILECORE; break; 157 case FS_EX2FS: str = DKW_PTYPE_EXT2FS; break; 158 case FS_NTFS: str = DKW_PTYPE_NTFS; break; 159 case FS_RAID: str = DKW_PTYPE_RAIDFRAME; break; 160 case FS_CCD: str = DKW_PTYPE_CCD; break; 161 case FS_APPLEUFS: str = DKW_PTYPE_APPLEUFS; break; 162 default: str = NULL; break; 163 } 164 165 return (str); 166 } 167 168 static void 169 swap_disklabel(struct disklabel *lp) 170 { 171 int i; 172 173 #define SWAP16(x) lp->x = bswap16(lp->x) 174 #define SWAP32(x) lp->x = bswap32(lp->x) 175 176 SWAP32(d_magic); 177 SWAP16(d_type); 178 SWAP16(d_subtype); 179 SWAP32(d_secsize); 180 SWAP32(d_nsectors); 181 SWAP32(d_ntracks); 182 SWAP32(d_ncylinders); 183 SWAP32(d_secpercyl); 184 SWAP32(d_secperunit); 185 SWAP16(d_sparespertrack); 186 SWAP16(d_sparespercyl); 187 SWAP32(d_acylinders); 188 SWAP16(d_rpm); 189 SWAP16(d_interleave); 190 SWAP16(d_trackskew); 191 SWAP16(d_cylskew); 192 SWAP32(d_headswitch); 193 SWAP32(d_trkseek); 194 SWAP32(d_flags); 195 196 for (i = 0; i < NDDATA; i++) 197 SWAP32(d_drivedata[i]); 198 for (i = 0; i < NSPARE; i++) 199 SWAP32(d_spare[i]); 200 201 SWAP32(d_magic2); 202 SWAP16(d_checksum); 203 SWAP16(d_npartitions); 204 SWAP32(d_bbsize); 205 SWAP32(d_sbsize); 206 207 for (i = 0; i < lp->d_npartitions; i++) { 208 SWAP32(d_partitions[i].p_size); 209 SWAP32(d_partitions[i].p_offset); 210 SWAP32(d_partitions[i].p_fsize); 211 SWAP16(d_partitions[i].p_cpg); 212 } 213 214 #undef SWAP16 215 #undef SWAP32 216 } 217 218 /* 219 * Add wedges for a valid NetBSD disklabel. 220 */ 221 static void 222 addwedges(const mbr_args_t *a, const struct disklabel *lp) 223 { 224 int error, i; 225 226 for (i = 0; i < lp->d_npartitions; i++) { 227 struct dkwedge_info dkw; 228 const struct partition *p; 229 const char *ptype; 230 231 p = &lp->d_partitions[i]; 232 233 if (p->p_fstype == FS_UNUSED) 234 continue; 235 if ((ptype = bsdlabel_fstype_to_str(p->p_fstype)) == NULL) { 236 /* 237 * XXX Should probably just add these... 238 * XXX maybe just have an empty ptype? 239 */ 240 aprint_verbose("%s: skipping partition %d, type %d\n", 241 a->pdk->dk_name, i, p->p_fstype); 242 continue; 243 } 244 strcpy(dkw.dkw_ptype, ptype); 245 246 strcpy(dkw.dkw_parent, a->pdk->dk_name); 247 dkw.dkw_offset = p->p_offset; 248 dkw.dkw_size = p->p_size; 249 250 /* 251 * If the label defines a name, append the partition 252 * letter and use it as the wedge name. 253 * Otherwise use historical disk naming style 254 * wedge names. 255 */ 256 if (lp->d_packname[0] && 257 strcmp(lp->d_packname,"fictitious") != 0) { 258 snprintf((char *)&dkw.dkw_wname, sizeof(dkw.dkw_wname), 259 "%.*s/%c", (int)sizeof(dkw.dkw_wname)-3, 260 lp->d_packname, 'a' + i); 261 } else { 262 snprintf((char *)&dkw.dkw_wname, sizeof(dkw.dkw_wname), 263 "%s%c", a->pdk->dk_name, 'a' + i); 264 } 265 266 error = dkwedge_add(&dkw); 267 if (error == EEXIST) 268 aprint_error("%s: wedge named '%s' already " 269 "exists, manual intervention required\n", 270 a->pdk->dk_name, dkw.dkw_wname); 271 else if (error) 272 aprint_error("%s: error %d adding partition " 273 "%d type %d\n", a->pdk->dk_name, error, 274 i, p->p_fstype); 275 } 276 } 277 278 static int 279 validate_label(mbr_args_t *a, daddr_t label_sector, size_t label_offset) 280 { 281 struct disklabel *lp; 282 void *lp_lim; 283 int error, swapped; 284 uint16_t npartitions; 285 286 error = dkwedge_read(a->pdk, a->vp, label_sector, a->buf, DEV_BSIZE); 287 if (error) { 288 aprint_error("%s: unable to read BSD disklabel @ %" PRId64 289 ", error = %d\n", a->pdk->dk_name, label_sector, error); 290 a->error = error; 291 return (SCAN_ERROR); 292 } 293 294 /* 295 * We ignore label_offset; this seems to have not been used 296 * consistently in the old code, requiring us to do the search 297 * in the sector. 298 */ 299 lp = a->buf; 300 lp_lim = (char *)a->buf + DEV_BSIZE - DISKLABEL_MINSIZE; 301 for (;; lp = (void *)((char *)lp + sizeof(uint32_t))) { 302 if ((char *)lp > (char *)lp_lim) 303 return (SCAN_CONTINUE); 304 label_offset = (size_t)((char *)lp - (char *)a->buf); 305 if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC) { 306 if (lp->d_magic != bswap32(DISKMAGIC) || 307 lp->d_magic2 != bswap32(DISKMAGIC)) 308 continue; 309 /* Label is in the other byte order. */ 310 swapped = 1; 311 } else 312 swapped = 0; 313 314 npartitions = (swapped) ? bswap16(lp->d_npartitions) 315 : lp->d_npartitions; 316 317 /* Validate label length. */ 318 if ((char *)lp + DISKLABEL_SIZE(npartitions) > 319 (char *)a->buf + DEV_BSIZE) { 320 aprint_error("%s: BSD disklabel @ " 321 "%" PRId64 "+%zd has bogus partition count (%u)\n", 322 a->pdk->dk_name, label_sector, label_offset, 323 npartitions); 324 continue; 325 } 326 327 /* 328 * We have validated the partition count. Checksum it. 329 * Note that we purposefully checksum before swapping 330 * the byte order. 331 */ 332 if (dkcksum_sized(lp, npartitions) != 0) { 333 aprint_error("%s: BSD disklabel @ %" PRId64 334 "+%zd has bad checksum\n", a->pdk->dk_name, 335 label_sector, label_offset); 336 continue; 337 } 338 /* Put the disklabel in the right order. */ 339 if (swapped) 340 swap_disklabel(lp); 341 addwedges(a, lp); 342 return (SCAN_FOUND); 343 } 344 } 345 346 static int 347 scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, struct mbr_partition *, 348 int, u_int)) 349 { 350 struct mbr_partition ptns[MBR_PART_COUNT]; 351 struct mbr_partition *dp; 352 struct mbr_sector *mbr; 353 u_int ext_base, this_ext, next_ext; 354 int i, rval; 355 #ifdef COMPAT_386BSD_MBRPART 356 int dp_386bsd = -1; 357 #endif 358 359 ext_base = 0; 360 this_ext = 0; 361 for (;;) { 362 a->error = dkwedge_read(a->pdk, a->vp, this_ext, a->buf, 363 DEV_BSIZE); 364 if (a->error) { 365 aprint_error("%s: unable to read MBR @ %u, " 366 "error = %d\n", a->pdk->dk_name, this_ext, 367 a->error); 368 return (SCAN_ERROR); 369 } 370 371 mbr = a->buf; 372 if (mbr->mbr_magic != htole16(MBR_MAGIC)) 373 return (SCAN_CONTINUE); 374 375 /* Copy data out of buffer so action can use the buffer. */ 376 memcpy(ptns, &mbr->mbr_parts, sizeof(ptns)); 377 378 /* Looks for NetBSD partition. */ 379 next_ext = 0; 380 dp = ptns; 381 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 382 if (dp->mbrp_type == 0) 383 continue; 384 if (MBR_IS_EXTENDED(dp->mbrp_type)) { 385 next_ext = le32toh(dp->mbrp_start); 386 continue; 387 } 388 #ifdef COMPAT_386BSD_MBRPART 389 if (dp->mbrp_type == MBR_PTYPE_386BSD) { 390 /* 391 * If more than one matches, take last, 392 * as NetBSD install tool does. 393 */ 394 if (this_ext == 0) 395 dp_386bsd = i; 396 continue; 397 } 398 #endif 399 rval = (*actn)(a, dp, i, this_ext); 400 if (rval != SCAN_CONTINUE) 401 return (rval); 402 } 403 if (next_ext == 0) 404 break; 405 if (ext_base == 0) { 406 ext_base = next_ext; 407 next_ext = 0; 408 } 409 next_ext += ext_base; 410 if (next_ext <= this_ext) 411 break; 412 this_ext = next_ext; 413 } 414 #ifdef COMPAT_386BSD_MBRPART 415 if (this_ext == 0 && dp_386bsd != -1) 416 return ((*actn)(a, &ptns[dp_386bsd], dp_386bsd, 0)); 417 #endif 418 return (SCAN_CONTINUE); 419 } 420 421 static int 422 look_netbsd_part(mbr_args_t *a, struct mbr_partition *dp, int slot, 423 u_int ext_base) 424 { 425 int ptn_base = ext_base + le32toh(dp->mbrp_start); 426 int rval; 427 428 if ( 429 #ifdef COMPAT_386BSD_MBRPART 430 dp->mbrp_type == MBR_PTYPE_386BSD || 431 #endif 432 dp->mbrp_type == MBR_PTYPE_NETBSD) { 433 rval = validate_label(a, ptn_base + BSD44_MBR_LABELSECTOR, 0); 434 435 /* If we got a NetBSD label, look no further. */ 436 if (rval == SCAN_FOUND) 437 return (rval); 438 } 439 440 return (SCAN_CONTINUE); 441 } 442 443 #ifdef _KERNEL 444 #define DKW_MALLOC(SZ) malloc((SZ), M_DEVBUF, M_WAITOK) 445 #define DKW_FREE(PTR) free((PTR), M_DEVBUF) 446 #else 447 #define DKW_MALLOC(SZ) malloc((SZ)) 448 #define DKW_FREE(PTR) free((PTR)) 449 #endif 450 451 static int 452 dkwedge_discover_bsdlabel(struct disk *pdk, struct vnode *vp) 453 { 454 mbr_args_t a; 455 const struct disklabel_location *dl; 456 int rval; 457 458 a.pdk = pdk; 459 a.vp = vp; 460 a.buf = DKW_MALLOC(DEV_BSIZE); 461 a.error = 0; 462 463 /* MBR search. */ 464 rval = scan_mbr(&a, look_netbsd_part); 465 if (rval != SCAN_CONTINUE) { 466 if (rval == SCAN_FOUND) 467 a.error = 0; /* found it, wedges installed */ 468 goto out; 469 } 470 471 /* Known location search. */ 472 for (dl = disklabel_locations; dl->label_sector != -1; dl++) { 473 rval = validate_label(&a, dl->label_sector, dl->label_offset); 474 if (rval != SCAN_CONTINUE) { 475 if (rval == SCAN_FOUND) 476 a.error = 0; /* found it, wedges installed */ 477 goto out; 478 } 479 } 480 481 /* No NetBSD disklabel found. */ 482 a.error = ESRCH; 483 out: 484 DKW_FREE(a.buf); 485 return (a.error); 486 } 487 488 #ifdef _KERNEL 489 DKWEDGE_DISCOVERY_METHOD_DECL(BSD44, 5, dkwedge_discover_bsdlabel); 490 #endif 491