1 /* $NetBSD: dkwedge_bsdlabel.c,v 1.26 2026/01/05 11:00:01 nia 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.26 2026/01/05 11:00:01 nia Exp $"); 83 84 #include <sys/param.h> 85 #include <sys/endian.h> 86 #ifdef _KERNEL 87 #include <sys/systm.h> 88 #endif 89 #include <sys/proc.h> 90 #include <sys/errno.h> 91 #include <sys/disk.h> 92 #include <sys/vnode.h> 93 #include <sys/buf.h> 94 95 #include <sys/bootblock.h> 96 #include <sys/disklabel.h> 97 98 #define BSD44_MBR_LABELSECTOR 1 99 100 #define DISKLABEL_SIZE(x) \ 101 (offsetof(struct disklabel, d_partitions) + \ 102 (sizeof(struct partition) * (x))) 103 104 /* 105 * Note the smallest MAXPARTITIONS was 8, so we allow a disklabel 106 * that size to be locted at the end of the sector. 107 */ 108 #define DISKLABEL_MINSIZE DISKLABEL_SIZE(8) 109 110 /* 111 * Table of known platform-specific disklabel locations. 112 */ 113 static const struct disklabel_location { 114 daddr_t label_sector; /* sector containing label */ 115 size_t label_offset; /* byte offset of label in sector */ 116 } disklabel_locations[] = { 117 { 0, 0 }, /* mvme68k, next68k */ 118 { 0, 64 }, /* algor, alpha, amiga, amigappc, evbmips, evbppc, 119 luna68k, mac68k, macppc, news68k, newsmips, 120 pc532, pdp11, pmax, vax, x68k */ 121 { 0, 128 }, /* sparc, sun68k */ 122 { 1, 0 }, /* amd64, arc, arm, bebox, cobalt, evbppc, hppa, 123 hpcarm, hpcmips, i386, ibmnws, mipsco, mvmeppc, 124 ofppc, playstation2, pmppc, prep, sandpoint, 125 sbmips, sgimips, sh3 */ 126 /* XXX atari is weird */ 127 { 2, 0 }, /* cesfic, hp300 */ 128 129 { -1, 0 }, 130 }; 131 132 #define SCAN_CONTINUE 0 133 #define SCAN_FOUND 1 134 #define SCAN_ERROR 2 135 136 typedef struct mbr_args { 137 struct disk *pdk; 138 struct vnode *vp; 139 struct buf *bp; 140 int error; 141 uint32_t secsize; 142 } mbr_args_t; 143 144 static const char * 145 bsdlabel_fstype_to_str(uint8_t fstype) 146 { 147 const char *str; 148 149 /* 150 * For each type known to FSTYPE_DEFN (from <sys/disklabel.h>), 151 * a suitable case branch will convert the type number to a string. 152 */ 153 switch (fstype) { 154 #define FSTYPE_TO_STR_CASE(tag, number, name, fsck, mount) \ 155 case __CONCAT(FS_,tag): str = __CONCAT(DKW_PTYPE_,tag); break; 156 FSTYPE_DEFN(FSTYPE_TO_STR_CASE) 157 #undef FSTYPE_TO_STR_CASE 158 default: str = NULL; break; 159 } 160 161 return (str); 162 } 163 164 static void 165 swap_disklabel(struct disklabel *lp) 166 { 167 int i; 168 169 #define SWAP16(x) lp->x = bswap16(lp->x) 170 #define SWAP32(x) lp->x = bswap32(lp->x) 171 172 SWAP32(d_magic); 173 SWAP16(d_type); 174 SWAP16(d_subtype); 175 SWAP32(d_secsize); 176 SWAP32(d_nsectors); 177 SWAP32(d_ntracks); 178 SWAP32(d_ncylinders); 179 SWAP32(d_secpercyl); 180 SWAP32(d_secperunit); 181 SWAP16(d_sparespertrack); 182 SWAP16(d_sparespercyl); 183 SWAP32(d_acylinders); 184 SWAP16(d_rpm); 185 SWAP16(d_interleave); 186 SWAP16(d_trackskew); 187 SWAP16(d_cylskew); 188 SWAP32(d_headswitch); 189 SWAP32(d_trkseek); 190 SWAP32(d_flags); 191 192 for (i = 0; i < NDDATA; i++) 193 SWAP32(d_drivedata[i]); 194 for (i = 0; i < NSPARE; i++) 195 SWAP32(d_spare[i]); 196 197 SWAP32(d_magic2); 198 SWAP16(d_checksum); 199 SWAP16(d_npartitions); 200 SWAP32(d_bbsize); 201 SWAP32(d_sbsize); 202 203 for (i = 0; i < lp->d_npartitions; i++) { 204 SWAP32(d_partitions[i].p_size); 205 SWAP32(d_partitions[i].p_offset); 206 SWAP32(d_partitions[i].p_fsize); 207 SWAP16(d_partitions[i].p_cpg); 208 } 209 210 #undef SWAP16 211 #undef SWAP32 212 } 213 214 /* 215 * Add wedges for a valid NetBSD disklabel. 216 */ 217 static void 218 addwedges(const mbr_args_t *a, const struct disklabel *lp) 219 { 220 int error, i; 221 222 for (i = 0; i < lp->d_npartitions; i++) { 223 struct dkwedge_info dkw; 224 const struct partition *p; 225 const char *ptype; 226 227 p = &lp->d_partitions[i]; 228 229 if (p->p_fstype == FS_UNUSED) 230 continue; 231 232 memset(&dkw, 0, sizeof(dkw)); 233 234 ptype = bsdlabel_fstype_to_str(p->p_fstype); 235 if (ptype == NULL) 236 snprintf(dkw.dkw_ptype, sizeof(dkw.dkw_ptype), 237 "unknown#%u", p->p_fstype); 238 else 239 strlcpy(dkw.dkw_ptype, ptype, sizeof(dkw.dkw_ptype)); 240 241 strlcpy(dkw.dkw_parent, a->pdk->dk_name, 242 sizeof(dkw.dkw_parent)); 243 dkw.dkw_offset = p->p_offset; 244 dkw.dkw_size = p->p_size; 245 246 /* 247 * If the label defines a name, append the partition 248 * letter and use it as the wedge name. 249 * Otherwise use historical disk naming style 250 * wedge names. 251 */ 252 if (lp->d_packname[0] && 253 strcmp(lp->d_packname,"fictitious") != 0) { 254 snprintf((char *)&dkw.dkw_wname, sizeof(dkw.dkw_wname), 255 "%.*s/%c", (int)sizeof(dkw.dkw_wname)-3, 256 lp->d_packname, 'a' + i); 257 } else { 258 snprintf((char *)&dkw.dkw_wname, sizeof(dkw.dkw_wname), 259 "%s%c", a->pdk->dk_name, 'a' + i); 260 } 261 262 error = dkwedge_add(&dkw); 263 if (error == EEXIST) 264 aprint_error("%s: wedge named '%s' already " 265 "exists, manual intervention required\n", 266 a->pdk->dk_name, dkw.dkw_wname); 267 else if (error) 268 aprint_error("%s: error %d adding partition " 269 "%d type %d\n", a->pdk->dk_name, error, 270 i, p->p_fstype); 271 } 272 } 273 274 static int 275 validate_label(mbr_args_t *a, daddr_t label_sector, size_t label_offset) 276 { 277 struct disklabel *lp; 278 void *lp_lim; 279 int error, swapped; 280 uint16_t npartitions; 281 282 error = dkwedge_read(a->pdk, a->vp, label_sector, a->bp->b_data, 283 a->secsize); 284 if (error) { 285 aprint_error("%s: unable to read BSD disklabel @ %" PRId64 286 ", error = %d\n", a->pdk->dk_name, label_sector, error); 287 a->error = error; 288 return (SCAN_ERROR); 289 } 290 291 /* 292 * We ignore label_offset; this seems to have not been used 293 * consistently in the old code, requiring us to do the search 294 * in the sector. 295 */ 296 lp = a->bp->b_data; 297 lp_lim = (char *)a->bp->b_data + a->secsize - DISKLABEL_MINSIZE; 298 for (;; lp = (void *)((char *)lp + sizeof(uint32_t))) { 299 if ((char *)lp > (char *)lp_lim) 300 return (SCAN_CONTINUE); 301 label_offset = (size_t)((char *)lp - (char *)a->bp->b_data); 302 if (lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC) { 303 if (lp->d_magic != bswap32(DISKMAGIC) || 304 lp->d_magic2 != bswap32(DISKMAGIC)) 305 continue; 306 /* Label is in the other byte order. */ 307 swapped = 1; 308 } else 309 swapped = 0; 310 311 npartitions = (swapped) ? bswap16(lp->d_npartitions) 312 : lp->d_npartitions; 313 314 /* Validate label length. */ 315 if ((char *)lp + DISKLABEL_SIZE(npartitions) > 316 (char *)a->bp->b_data + a->secsize) { 317 aprint_error("%s: BSD disklabel @ " 318 "%" PRId64 "+%zd has bogus partition count (%u)\n", 319 a->pdk->dk_name, label_sector, label_offset, 320 npartitions); 321 continue; 322 } 323 324 /* 325 * We have validated the partition count. Checksum it. 326 * Note that we purposefully checksum before swapping 327 * the byte order. 328 */ 329 if (dkcksum_sized(lp, npartitions) != 0) { 330 aprint_error("%s: BSD disklabel @ %" PRId64 331 "+%zd has bad checksum\n", a->pdk->dk_name, 332 label_sector, label_offset); 333 continue; 334 } 335 /* Put the disklabel in the right order. */ 336 if (swapped) 337 swap_disklabel(lp); 338 addwedges(a, lp); 339 return (SCAN_FOUND); 340 } 341 } 342 343 static int 344 scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, struct mbr_partition *, 345 int, u_int)) 346 { 347 struct mbr_partition ptns[MBR_PART_COUNT]; 348 struct mbr_partition *dp; 349 struct mbr_sector *mbr; 350 u_int ext_base, this_ext, next_ext; 351 int i, rval; 352 #ifdef COMPAT_386BSD_MBRPART 353 int dp_386bsd = -1; 354 #endif 355 356 ext_base = 0; 357 this_ext = 0; 358 for (;;) { 359 a->error = dkwedge_read(a->pdk, a->vp, this_ext, a->bp->b_data, 360 a->secsize); 361 if (a->error) { 362 aprint_error("%s: unable to read MBR @ %u, " 363 "error = %d\n", a->pdk->dk_name, this_ext, 364 a->error); 365 return (SCAN_ERROR); 366 } 367 368 mbr = a->bp->b_data; 369 if (mbr->mbr_magic != htole16(MBR_MAGIC)) 370 return (SCAN_CONTINUE); 371 372 /* Copy data out of buffer so action can use the buffer. */ 373 memcpy(ptns, &mbr->mbr_parts, sizeof(ptns)); 374 375 /* Looks for NetBSD partition. */ 376 next_ext = 0; 377 dp = ptns; 378 for (i = 0; i < MBR_PART_COUNT; i++, dp++) { 379 if (dp->mbrp_type == 0) 380 continue; 381 if (MBR_IS_EXTENDED(dp->mbrp_type)) { 382 next_ext = le32toh(dp->mbrp_start); 383 continue; 384 } 385 #ifdef COMPAT_386BSD_MBRPART 386 if (dp->mbrp_type == MBR_PTYPE_386BSD) { 387 /* 388 * If more than one matches, take last, 389 * as NetBSD install tool does. 390 */ 391 if (this_ext == 0) 392 dp_386bsd = i; 393 continue; 394 } 395 #endif 396 rval = (*actn)(a, dp, i, this_ext); 397 if (rval != SCAN_CONTINUE) 398 return (rval); 399 } 400 if (next_ext == 0) 401 break; 402 if (ext_base == 0) { 403 ext_base = next_ext; 404 next_ext = 0; 405 } 406 next_ext += ext_base; 407 if (next_ext <= this_ext) 408 break; 409 this_ext = next_ext; 410 } 411 #ifdef COMPAT_386BSD_MBRPART 412 if (this_ext == 0 && dp_386bsd != -1) 413 return ((*actn)(a, &ptns[dp_386bsd], dp_386bsd, 0)); 414 #endif 415 return (SCAN_CONTINUE); 416 } 417 418 static int 419 look_netbsd_part(mbr_args_t *a, struct mbr_partition *dp, int slot, 420 u_int ext_base) 421 { 422 int ptn_base = ext_base + le32toh(dp->mbrp_start); 423 int rval; 424 425 if ( 426 #ifdef COMPAT_386BSD_MBRPART 427 dp->mbrp_type == MBR_PTYPE_386BSD || 428 #endif 429 dp->mbrp_type == MBR_PTYPE_NETBSD) { 430 rval = validate_label(a, ptn_base + BSD44_MBR_LABELSECTOR, 0); 431 432 /* If we got a NetBSD label, look no further. */ 433 if (rval == SCAN_FOUND) 434 return (rval); 435 } 436 437 return (SCAN_CONTINUE); 438 } 439 440 static int 441 dkwedge_discover_bsdlabel(struct disk *pdk, struct vnode *vp) 442 { 443 mbr_args_t a; 444 const struct disklabel_location *dl; 445 int rval; 446 447 a.pdk = pdk; 448 a.secsize = DEV_BSIZE << pdk->dk_blkshift; 449 a.vp = vp; 450 a.bp = geteblk(a.secsize); 451 a.error = 0; 452 453 /* MBR search. */ 454 rval = scan_mbr(&a, look_netbsd_part); 455 if (rval != SCAN_CONTINUE) { 456 if (rval == SCAN_FOUND) 457 a.error = 0; /* found it, wedges installed */ 458 goto out; 459 } 460 461 /* Known location search. */ 462 for (dl = disklabel_locations; dl->label_sector != -1; dl++) { 463 rval = validate_label(&a, dl->label_sector, dl->label_offset); 464 if (rval != SCAN_CONTINUE) { 465 if (rval == SCAN_FOUND) 466 a.error = 0; /* found it, wedges installed */ 467 goto out; 468 } 469 } 470 471 /* No NetBSD disklabel found. */ 472 a.error = ESRCH; 473 out: 474 brelse(a.bp, 0); 475 return (a.error); 476 } 477 478 #ifdef _KERNEL 479 DKWEDGE_DISCOVERY_METHOD_DECL(BSD44, 5, dkwedge_discover_bsdlabel); 480 #endif 481