1 /* $OpenBSD: i386_installboot.c,v 1.48 2024/11/08 10:43:07 kettenis Exp $ */ 2 /* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ 3 4 /* 5 * Copyright (c) 2013 Pedro Martelletto 6 * Copyright (c) 2011 Joel Sing <jsing@openbsd.org> 7 * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com> 8 * Copyright (c) 1997 Michael Shalayeff 9 * Copyright (c) 1994 Paul Kranenburg 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by Paul Kranenburg. 23 * 4. The name of the author may not be used to endorse or promote products 24 * derived from this software without specific prior written permission 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #define ELFSIZE 32 39 40 #include <sys/param.h> /* DEV_BSIZE */ 41 #include <sys/disklabel.h> 42 #include <sys/dkio.h> 43 #include <sys/ioctl.h> 44 #include <sys/mount.h> 45 #include <sys/reboot.h> 46 #include <sys/stat.h> 47 #include <sys/sysctl.h> 48 #include <sys/time.h> 49 50 #include <ufs/ufs/dinode.h> 51 #include <ufs/ufs/dir.h> 52 #include <ufs/ffs/fs.h> 53 54 #include <machine/cpu.h> 55 #include <machine/biosvar.h> 56 57 #include <elf.h> 58 #include <err.h> 59 #include <errno.h> 60 #include <fcntl.h> 61 #include <nlist.h> 62 #include <stdlib.h> 63 #include <stdio.h> 64 #include <stdint.h> 65 #include <string.h> 66 #include <unistd.h> 67 #include <util.h> 68 #include <uuid.h> 69 70 #include "installboot.h" 71 #include "i386_installboot.h" 72 73 char *bootldr; 74 75 char *blkstore; 76 size_t blksize; 77 78 struct sym_data pbr_symbols[] = { 79 {"_fs_bsize_p", 2}, 80 {"_fs_bsize_s", 2}, 81 {"_fsbtodb", 1}, 82 {"_p_offset", 4}, 83 {"_inodeblk", 4}, 84 {"_inodedbl", 4}, 85 {"_nblocks", 2}, 86 {"_blkincr", 1}, 87 {NULL} 88 }; 89 90 static void devread(int, void *, daddr_t, size_t, char *); 91 static u_int findopenbsd(int, struct disklabel *); 92 static int getbootparams(char *, int, struct disklabel *); 93 static char *loadproto(char *, long *); 94 static int gpt_chk_mbr(struct dos_partition *, u_int64_t); 95 static int sbchk(struct fs *, daddr_t); 96 static void sbread(int, daddr_t, struct fs **, char *); 97 98 static const daddr_t sbtry[] = SBLOCKSEARCH; 99 100 /* 101 * Read information about /boot's inode and filesystem parameters, then 102 * put biosboot (partition boot record) on the target drive with these 103 * parameters patched in. 104 */ 105 106 void 107 md_init(void) 108 { 109 stages = 2; 110 stage1 = "/usr/mdec/biosboot"; 111 stage2 = "/usr/mdec/boot"; 112 113 bootldr = "/boot"; 114 } 115 116 void 117 md_loadboot(void) 118 { 119 /* Load prototype boot blocks. */ 120 if ((blkstore = loadproto(stage1, &blksize)) == NULL) 121 exit(1); 122 123 /* XXX - Paranoia: Make sure size is aligned! */ 124 if (blksize & (DEV_BSIZE - 1)) 125 errx(1, "proto %s bad size=%ld", stage1, blksize); 126 127 if (blksize > SBSIZE - DEV_BSIZE) 128 errx(1, "proto bootblocks too big"); 129 } 130 131 void 132 md_prepareboot(int devfd, char *dev) 133 { 134 struct disklabel dl; 135 int part; 136 137 /* Get and check disklabel. */ 138 if (ioctl(devfd, DIOCGDINFO, &dl) == -1) 139 err(1, "disklabel: %s", dev); 140 if (dl.d_magic != DISKMAGIC) 141 errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 142 143 /* Warn on unknown disklabel types. */ 144 if (dl.d_type == 0) 145 warnx("disklabel type unknown"); 146 147 part = findgptefisys(devfd, &dl); 148 if (part != -1) { 149 create_filesystem(&dl, (char)part); 150 return; 151 } 152 } 153 154 void 155 md_installboot(int devfd, char *dev) 156 { 157 struct disklabel dl; 158 int part; 159 160 /* Get and check disklabel. */ 161 if (ioctl(devfd, DIOCGDINFO, &dl) == -1) 162 err(1, "disklabel: %s", dev); 163 if (dl.d_magic != DISKMAGIC) 164 errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 165 166 /* Warn on unknown disklabel types. */ 167 if (dl.d_type == 0) 168 warnx("disklabel type unknown"); 169 170 part = findgptefisys(devfd, &dl); 171 if (part != -1) { 172 write_filesystem(&dl, (char)part); 173 return; 174 } 175 176 bootldr = fileprefix(root, bootldr); 177 if (bootldr == NULL) 178 exit(1); 179 if (verbose) 180 fprintf(stderr, "%s %s to %s\n", 181 (nowrite ? "would copy" : "copying"), stage2, bootldr); 182 if (!nowrite) 183 if (filecopy(stage2, bootldr) == -1) 184 exit(1); 185 186 /* Get bootstrap parameters to patch into proto. */ 187 if (getbootparams(bootldr, devfd, &dl) != 0) 188 exit(1); 189 190 /* Write boot blocks to device. */ 191 write_bootblocks(devfd, dev, &dl); 192 } 193 194 void 195 write_bootblocks(int devfd, char *dev, struct disklabel *dl) 196 { 197 struct stat sb; 198 u_int8_t *secbuf; 199 u_int start; 200 201 /* Write patched proto bootblock(s) into the superblock. */ 202 if (fstat(devfd, &sb) == -1) 203 err(1, "fstat: %s", dev); 204 205 if (!S_ISCHR(sb.st_mode)) 206 errx(1, "%s: not a character device", dev); 207 208 /* Patch the parameters into the proto bootstrap sector. */ 209 pbr_set_symbols(stage1, blkstore, pbr_symbols); 210 211 if (!nowrite) { 212 /* Sync filesystems (to clean in-memory superblock?). */ 213 sync(); sleep(1); 214 } 215 216 /* 217 * Find bootstrap sector. 218 */ 219 start = findopenbsd(devfd, dl); 220 if (verbose) { 221 if (start == 0) 222 fprintf(stderr, "no MBR, "); 223 fprintf(stderr, "%s will be written at sector %u\n", 224 stage1, start); 225 } 226 227 if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC) 228 warnx("%s extends beyond sector %u. OpenBSD might not boot.", 229 stage1, BOOTBIOS_MAXSEC); 230 231 if (!nowrite) { 232 secbuf = calloc(1, dl->d_secsize); 233 if (pread(devfd, secbuf, dl->d_secsize, (off_t)start * 234 dl->d_secsize) != dl->d_secsize) 235 err(1, "pread boot sector"); 236 bcopy(blkstore, secbuf, blksize); 237 if (pwrite(devfd, secbuf, dl->d_secsize, (off_t)start * 238 dl->d_secsize) != dl->d_secsize) 239 err(1, "pwrite bootstrap"); 240 free(secbuf); 241 } 242 } 243 244 int 245 create_filesystem(struct disklabel *dl, char part) 246 { 247 static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null"; 248 struct msdosfs_args args; 249 char cmd[60]; 250 int rslt; 251 252 /* Newfs <duid>.<part> as msdos filesystem. */ 253 memset(&args, 0, sizeof(args)); 254 rslt = asprintf(&args.fspec, 255 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 256 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], 257 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], 258 part); 259 if (rslt == -1) { 260 warn("bad special device"); 261 return rslt; 262 } 263 264 rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec); 265 if (rslt >= sizeof(cmd)) { 266 warnx("can't build newfs command"); 267 free(args.fspec); 268 rslt = -1; 269 return rslt; 270 } 271 272 if (verbose) 273 fprintf(stderr, "%s %s\n", 274 (nowrite ? "would newfs" : "newfsing"), args.fspec); 275 if (!nowrite) { 276 rslt = system(cmd); 277 if (rslt == -1) { 278 warn("system('%s') failed", cmd); 279 free(args.fspec); 280 return rslt; 281 } 282 } 283 284 free(args.fspec); 285 return 0; 286 } 287 288 void 289 write_filesystem(struct disklabel *dl, char part) 290 { 291 static const char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null"; 292 struct msdosfs_args args; 293 #ifdef __amd64__ 294 struct statfs sf; 295 #endif 296 char cmd[60]; 297 char dst[PATH_MAX]; 298 char *src; 299 size_t mntlen, pathlen, srclen; 300 int rslt; 301 302 src = NULL; 303 304 /* Create directory for temporary mount point. */ 305 strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst)); 306 if (mkdtemp(dst) == NULL) 307 err(1, "mkdtemp('%s') failed", dst); 308 mntlen = strlen(dst); 309 310 /* Mount <duid>.<part> as msdos filesystem. */ 311 memset(&args, 0, sizeof(args)); 312 rslt = asprintf(&args.fspec, 313 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 314 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], 315 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], 316 part); 317 if (rslt == -1) { 318 warn("bad special device"); 319 goto rmdir; 320 } 321 322 args.export_info.ex_root = -2; /* unchecked anyway on DOS fs */ 323 args.export_info.ex_flags = 0; 324 args.flags = MSDOSFSMNT_LONGNAME; 325 326 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 327 /* Try fsck'ing it. */ 328 rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec); 329 if (rslt >= sizeof(cmd)) { 330 warnx("can't build fsck command"); 331 rslt = -1; 332 goto rmdir; 333 } 334 rslt = system(cmd); 335 if (rslt == -1) { 336 warn("system('%s') failed", cmd); 337 goto rmdir; 338 } 339 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 340 /* Try newfs'ing it. */ 341 rslt = create_filesystem(dl, part); 342 if (rslt == -1) 343 goto rmdir; 344 rslt = mount(MOUNT_MSDOS, dst, 0, &args); 345 if (rslt == -1) { 346 warn("unable to mount EFI System partition"); 347 goto rmdir; 348 } 349 } 350 } 351 352 /* Create "/efi/BOOT" directory in <duid>.<part>. */ 353 if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) { 354 rslt = -1; 355 warn("unable to build /efi directory"); 356 goto umount; 357 } 358 rslt = mkdir(dst, 0); 359 if (rslt == -1 && errno != EEXIST) { 360 warn("mkdir('%s') failed", dst); 361 goto umount; 362 } 363 if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) { 364 rslt = -1; 365 warn("unable to build /BOOT directory"); 366 goto umount; 367 } 368 rslt = mkdir(dst, 0); 369 if (rslt == -1 && errno != EEXIST) { 370 warn("mkdir('%s') failed", dst); 371 goto umount; 372 } 373 374 /* 375 * Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/BOOT/. 376 * 377 * N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused! 378 */ 379 pathlen = strlen(dst); 380 if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) { 381 rslt = -1; 382 warn("unable to build /BOOTIA32.EFI path"); 383 goto umount; 384 } 385 src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI"); 386 if (src == NULL) { 387 rslt = -1; 388 goto umount; 389 } 390 srclen = strlen(src); 391 if (verbose) 392 fprintf(stderr, "%s %s to %s\n", 393 (nowrite ? "would copy" : "copying"), src, dst); 394 if (!nowrite) { 395 rslt = filecopy(src, dst); 396 if (rslt == -1) 397 goto umount; 398 } 399 src[srclen - strlen("/BOOTIA32.EFI")] = '\0'; 400 401 dst[pathlen] = '\0'; 402 if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) { 403 rslt = -1; 404 warn("unable to build /BOOTX64.EFI dst path"); 405 goto umount; 406 } 407 if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) { 408 rslt = -1; 409 warn("unable to build /BOOTX64.EFI src path"); 410 goto umount; 411 } 412 if (verbose) 413 fprintf(stderr, "%s %s to %s\n", 414 (nowrite ? "would copy" : "copying"), src, dst); 415 if (!nowrite) { 416 rslt = filecopy(src, dst); 417 if (rslt == -1) 418 goto umount; 419 } 420 421 #ifdef __amd64__ 422 /* Skip installing a 2nd copy if we have a small filesystem. */ 423 if (statfs(dst, &sf) || sf.f_blocks < 2048) { 424 rslt = 0; 425 goto umount; 426 } 427 428 /* Create "/efi/openbsd" directory in <duid>.<part>. */ 429 dst[mntlen] = '\0'; 430 if (strlcat(dst, "/efi/openbsd", sizeof(dst)) >= sizeof(dst)) { 431 rslt = -1; 432 warn("unable to build /efi/openbsd directory"); 433 goto umount; 434 } 435 rslt = mkdir(dst, 0755); 436 if (rslt == -1 && errno != EEXIST) { 437 warn("mkdir('%s') failed", dst); 438 goto umount; 439 } 440 441 /* Copy BOOTX64.EFI to /efi/openbsd/. */ 442 if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) { 443 rslt = -1; 444 warn("unable to build /BOOTX64.EFI path"); 445 goto umount; 446 } 447 src = fileprefix(root, "/usr/mdec/BOOTX64.EFI"); 448 if (src == NULL) { 449 rslt = -1; 450 goto umount; 451 } 452 srclen = strlen(src); 453 if (verbose) 454 fprintf(stderr, "%s %s to %s\n", 455 (nowrite ? "would copy" : "copying"), src, dst); 456 if (!nowrite) { 457 rslt = filecopy(src, dst); 458 if (rslt == -1) 459 goto umount; 460 } 461 #endif 462 463 rslt = 0; 464 465 umount: 466 dst[mntlen] = '\0'; 467 if (unmount(dst, MNT_FORCE) == -1) 468 err(1, "unmount('%s') failed", dst); 469 470 rmdir: 471 free(args.fspec); 472 dst[mntlen] = '\0'; 473 if (rmdir(dst) == -1) 474 err(1, "rmdir('%s') failed", dst); 475 476 free(src); 477 478 if (rslt == -1) 479 exit(1); 480 } 481 482 /* 483 * a) For media w/o an MBR use sector 0. 484 * b) For media with an MBR and an OpenBSD (A6) partition use the first 485 * sector of the OpenBSD partition. 486 * c) For media with an MBR and no OpenBSD partition error out. 487 */ 488 u_int 489 findopenbsd(int devfd, struct disklabel *dl) 490 { 491 struct dos_mbr mbr; 492 u_int mbroff = DOSBBSECTOR; 493 u_int mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */ 494 struct dos_partition *dp; 495 u_int8_t *secbuf; 496 u_int maxebr = DOS_MAXEBR, nextebr; 497 int i; 498 499 again: 500 if (!maxebr--) { 501 if (verbose) 502 fprintf(stderr, "Traversed more than %d Extended Boot " 503 "Records (EBRs)\n", DOS_MAXEBR); 504 goto done; 505 } 506 507 if (verbose) 508 fprintf(stderr, "%s boot record (%cBR) at sector %u\n", 509 (mbroff == DOSBBSECTOR) ? "master" : "extended", 510 (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff); 511 512 if ((secbuf = malloc(dl->d_secsize)) == NULL) 513 err(1, NULL); 514 if (pread(devfd, secbuf, dl->d_secsize, (off_t)mbroff * dl->d_secsize) 515 < (ssize_t)sizeof(mbr)) 516 err(4, "can't pread boot record"); 517 bcopy(secbuf, &mbr, sizeof(mbr)); 518 free(secbuf); 519 520 if (mbr.dmbr_sign != DOSMBR_SIGNATURE) { 521 if (mbroff == DOSBBSECTOR) 522 return 0; 523 errx(1, "invalid boot record signature (0x%04X) @ sector %u", 524 mbr.dmbr_sign, mbroff); 525 } 526 527 nextebr = 0; 528 for (i = 0; i < NDOSPART; i++) { 529 dp = &mbr.dmbr_parts[i]; 530 if (!dp->dp_size) 531 continue; 532 533 if (verbose) 534 fprintf(stderr, 535 "\tpartition %d: type 0x%02X offset %u size %u\n", 536 i, dp->dp_typ, dp->dp_start, dp->dp_size); 537 538 if (dp->dp_typ == DOSPTYP_OPENBSD) { 539 if (dp->dp_start > (dp->dp_start + mbroff)) 540 continue; 541 return (dp->dp_start + mbroff); 542 } 543 544 if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || 545 dp->dp_typ == DOSPTYP_EXTENDL)) { 546 nextebr = dp->dp_start + mbr_eoff; 547 if (nextebr < dp->dp_start) 548 nextebr = (u_int)-1; 549 if (mbr_eoff == DOSBBSECTOR) 550 mbr_eoff = dp->dp_start; 551 } 552 } 553 554 if (nextebr && nextebr != (u_int)-1) { 555 mbroff = nextebr; 556 goto again; 557 } 558 559 done: 560 errx(1, "no OpenBSD partition"); 561 } 562 563 /* 564 * Returns 0 if the MBR with the provided partition array is a GPT protective 565 * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only 566 * one MBR partition, an EFI partition that either covers the whole disk or as 567 * much of it as is possible with a 32bit size field. 568 * 569 * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** 570 */ 571 static int 572 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 573 { 574 struct dos_partition *dp2; 575 int efi, found, i; 576 u_int32_t psize; 577 578 found = efi = 0; 579 for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { 580 if (dp2->dp_typ == DOSPTYP_UNUSED) 581 continue; 582 found++; 583 if (dp2->dp_typ != DOSPTYP_EFI) 584 continue; 585 if (letoh32(dp2->dp_start) != GPTSECTOR) 586 continue; 587 psize = letoh32(dp2->dp_size); 588 if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) 589 efi++; 590 } 591 if (found == 1 && efi == 1) 592 return (0); 593 594 return (1); 595 } 596 597 int 598 findgptefisys(int devfd, struct disklabel *dl) 599 { 600 struct gpt_partition gp[NGPTPARTITIONS]; 601 struct gpt_header gh; 602 struct dos_partition dp[NDOSPART]; 603 struct uuid efisys_uuid; 604 const char efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM; 605 off_t off; 606 ssize_t len; 607 u_int64_t start; 608 int i; 609 uint32_t orig_csum, new_csum; 610 uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec; 611 u_int8_t *secbuf; 612 613 /* Prepare EFI System UUID */ 614 uuid_dec_be(efisys_uuid_code, &efisys_uuid); 615 616 if ((secbuf = malloc(dl->d_secsize)) == NULL) 617 err(1, NULL); 618 619 /* Check that there is a protective MBR. */ 620 len = pread(devfd, secbuf, dl->d_secsize, 0); 621 if (len != dl->d_secsize) 622 err(4, "can't read mbr"); 623 memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp)); 624 if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) { 625 free(secbuf); 626 return (-1); 627 } 628 629 /* Check GPT Header. */ 630 off = dl->d_secsize; /* Read header from sector 1. */ 631 len = pread(devfd, secbuf, dl->d_secsize, off); 632 if (len != dl->d_secsize) 633 err(4, "can't pread gpt header"); 634 635 memcpy(&gh, secbuf, sizeof(gh)); 636 free(secbuf); 637 638 /* Check signature */ 639 if (letoh64(gh.gh_sig) != GPTSIGNATURE) 640 return (-1); 641 642 if (letoh32(gh.gh_rev) != GPTREVISION) 643 return (-1); 644 645 ghsize = letoh32(gh.gh_size); 646 if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) 647 return (-1); 648 649 /* Check checksum */ 650 orig_csum = gh.gh_csum; 651 gh.gh_csum = 0; 652 new_csum = crc32((unsigned char *)&gh, ghsize); 653 gh.gh_csum = orig_csum; 654 if (letoh32(orig_csum) != new_csum) 655 return (-1); 656 657 off = letoh64(gh.gh_part_lba) * dl->d_secsize; 658 ghpartsize = letoh32(gh.gh_part_size); 659 ghpartspersec = dl->d_secsize / ghpartsize; 660 ghpartnum = letoh32(gh.gh_part_num); 661 if ((secbuf = malloc(dl->d_secsize)) == NULL) 662 err(1, NULL); 663 for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) { 664 len = pread(devfd, secbuf, dl->d_secsize, off); 665 if (len != dl->d_secsize) { 666 free(secbuf); 667 return (-1); 668 } 669 memcpy(gp + i * ghpartspersec, secbuf, 670 ghpartspersec * sizeof(struct gpt_partition)); 671 off += dl->d_secsize; 672 } 673 free(secbuf); 674 new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize); 675 if (new_csum != letoh32(gh.gh_part_csum)) 676 return (-1); 677 678 start = 0; 679 for (i = 0; i < ghpartnum && start == 0; i++) { 680 if (memcmp(&gp[i].gp_type, &efisys_uuid, 681 sizeof(struct uuid)) == 0) 682 start = letoh64(gp[i].gp_lba_start); 683 } 684 685 if (start) { 686 for (i = 0; i < MAXPARTITIONS; i++) { 687 if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 && 688 DL_GETPOFFSET(&dl->d_partitions[i]) == start) 689 return ('a' + i); 690 } 691 } 692 693 return (-1); 694 } 695 696 /* 697 * Load the prototype boot sector (biosboot) into memory. 698 */ 699 static char * 700 loadproto(char *fname, long *size) 701 { 702 int fd; 703 size_t tdsize; /* text+data size */ 704 char *bp; 705 Elf_Ehdr eh; 706 Elf_Word phsize; 707 Elf_Phdr *ph; 708 709 if ((fd = open(fname, O_RDONLY)) == -1) 710 err(1, "%s", fname); 711 712 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 713 errx(1, "%s: read failed", fname); 714 715 if (!IS_ELF(eh)) 716 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname, 717 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 718 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 719 720 /* 721 * We have to include the exec header in the beginning of 722 * the buffer, and leave extra space at the end in case 723 * the actual write to disk wants to skip the header. 724 */ 725 726 /* Program load header. */ 727 if (eh.e_phnum != 1) 728 errx(1, "%s: %u ELF load sections (only support 1)", 729 fname, eh.e_phnum); 730 731 ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr)); 732 if (ph == NULL) 733 err(1, NULL); 734 phsize = eh.e_phnum * sizeof(Elf_Phdr); 735 736 if (pread(fd, ph, phsize, eh.e_phoff) != phsize) 737 errx(1, "%s: can't pread header", fname); 738 739 tdsize = ph->p_filesz; 740 741 /* 742 * Allocate extra space here because the caller may copy 743 * the boot block starting at the end of the exec header. 744 * This prevents reading beyond the end of the buffer. 745 */ 746 if ((bp = calloc(tdsize, 1)) == NULL) 747 err(1, NULL); 748 749 /* Read the rest of the file. */ 750 if (pread(fd, bp, tdsize, ph->p_offset) != (ssize_t)tdsize) 751 errx(1, "%s: pread failed", fname); 752 753 *size = tdsize; /* not aligned to DEV_BSIZE */ 754 755 close(fd); 756 return bp; 757 } 758 759 static void 760 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) 761 { 762 if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size) 763 err(1, "%s: devread: pread", msg); 764 } 765 766 /* 767 * Read information about /boot's inode, then put this and filesystem 768 * parameters from the superblock into pbr_symbols. 769 */ 770 static int 771 getbootparams(char *boot, int devfd, struct disklabel *dl) 772 { 773 int fd; 774 struct stat dsb, fsb; 775 struct statfs fssb; 776 struct partition *pp; 777 struct fs *fs; 778 char *sblock, *buf; 779 u_int blk, *ap; 780 int ndb; 781 int mib[3]; 782 size_t size; 783 dev_t dev; 784 int incr; 785 786 /* 787 * Open 2nd-level boot program and record enough details about 788 * where it is on the filesystem represented by `devfd' 789 * (inode block, offset within that block, and various filesystem 790 * parameters essentially taken from the superblock) for biosboot 791 * to be able to load it later. 792 */ 793 794 /* Make sure the (probably new) boot file is on disk. */ 795 sync(); sleep(1); 796 797 if ((fd = open(boot, O_RDONLY)) == -1) 798 err(1, "open: %s", boot); 799 800 if (fstatfs(fd, &fssb) == -1) 801 err(1, "statfs: %s", boot); 802 803 if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) && 804 strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) ) 805 errx(1, "%s: not on an FFS filesystem", boot); 806 807 #if 0 808 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 809 errx(1, "read: %s", boot); 810 811 if (!IS_ELF(eh)) { 812 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", 813 boot, 814 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 815 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 816 } 817 #endif 818 819 if (fsync(fd) != 0) 820 err(1, "fsync: %s", boot); 821 822 if (fstat(fd, &fsb) != 0) 823 err(1, "fstat: %s", boot); 824 825 if (fstat(devfd, &dsb) != 0) 826 err(1, "fstat: %d", devfd); 827 828 /* Check devices. */ 829 mib[0] = CTL_MACHDEP; 830 mib[1] = CPU_CHR2BLK; 831 mib[2] = dsb.st_rdev; 832 size = sizeof(dev); 833 if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) { 834 if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS) 835 errx(1, "cross-device install"); 836 } 837 838 pp = &dl->d_partitions[DISKPART(fsb.st_dev)]; 839 close(fd); 840 841 if ((sblock = malloc(SBSIZE)) == NULL) 842 err(1, NULL); 843 844 sbread(devfd, DL_SECTOBLK(dl, pp->p_offset), &fs, sblock); 845 846 /* Read inode. */ 847 if ((buf = malloc(fs->fs_bsize)) == NULL) 848 err(1, NULL); 849 850 blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino)); 851 852 /* 853 * Have the inode. Figure out how many filesystem blocks (not disk 854 * sectors) there are for biosboot to load. 855 */ 856 devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk, 857 fs->fs_bsize, "inode"); 858 if (fs->fs_magic == FS_UFS2_MAGIC) { 859 struct ufs2_dinode *ip2 = (struct ufs2_dinode *)(buf) + 860 ino_to_fsbo(fs, fsb.st_ino); 861 ndb = howmany(ip2->di_size, fs->fs_bsize); 862 ap = (u_int *)ip2->di_db; 863 incr = sizeof(u_int32_t); 864 } else { 865 struct ufs1_dinode *ip1 = (struct ufs1_dinode *)(buf) + 866 ino_to_fsbo(fs, fsb.st_ino); 867 ndb = howmany(ip1->di_size, fs->fs_bsize); 868 ap = (u_int *)ip1->di_db; 869 incr = 0; 870 } 871 872 if (ndb <= 0) 873 errx(1, "No blocks to load"); 874 875 /* 876 * Now set the values that will need to go into biosboot 877 * (the partition boot record, a.k.a. the PBR). 878 */ 879 sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16)); 880 sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 881 dl->d_secsize)); 882 883 /* 884 * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The 885 * ino_to_fsba() return value is the number of fs_fsize units. 886 * Calculate the shift to convert fs_fsize into physical sectors, 887 * which are added to p_offset to get the sector address BIOS 888 * will use. 889 * 890 * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize. 891 */ 892 sym_set_value(pbr_symbols, "_fsbtodb", 893 ffs(fs->fs_fsize / dl->d_secsize) - 1); 894 895 sym_set_value(pbr_symbols, "_p_offset", pp->p_offset); 896 sym_set_value(pbr_symbols, "_inodeblk", 897 ino_to_fsba(fs, fsb.st_ino)); 898 sym_set_value(pbr_symbols, "_inodedbl", 899 ((((char *)ap) - buf) + INODEOFF)); 900 sym_set_value(pbr_symbols, "_nblocks", ndb); 901 sym_set_value(pbr_symbols, "_blkincr", incr); 902 903 if (verbose) { 904 fprintf(stderr, "%s is %d blocks x %d bytes\n", 905 boot, ndb, fs->fs_bsize); 906 fprintf(stderr, "fs block shift %u; part offset %u; " 907 "inode block %lld, offset %u\n", 908 ffs(fs->fs_fsize / dl->d_secsize) - 1, 909 pp->p_offset, 910 ino_to_fsba(fs, fsb.st_ino), 911 (unsigned int)((((char *)ap) - buf) + INODEOFF)); 912 fprintf(stderr, "expecting %d-bit fs blocks (incr %d)\n", 913 incr ? 64 : 32, incr); 914 } 915 916 free (sblock); 917 free (buf); 918 919 return 0; 920 } 921 922 void 923 sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value) 924 { 925 struct sym_data *p; 926 927 for (p = sym_list; p->sym_name != NULL; p++) { 928 if (strcmp(p->sym_name, sym) == 0) 929 break; 930 } 931 932 if (p->sym_name == NULL) 933 errx(1, "%s: no such symbol", sym); 934 935 p->sym_value = value; 936 p->sym_set = 1; 937 } 938 939 /* 940 * Write the parameters stored in sym_list into the in-memory copy of 941 * the prototype biosboot (proto), ready for it to be written to disk. 942 */ 943 void 944 pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list) 945 { 946 struct sym_data *sym; 947 struct nlist *nl; 948 char *vp; 949 u_int32_t *lp; 950 u_int16_t *wp; 951 u_int8_t *bp; 952 953 for (sym = sym_list; sym->sym_name != NULL; sym++) { 954 if (!sym->sym_set) 955 errx(1, "%s not set", sym->sym_name); 956 957 /* Allocate space for 2; second is null-terminator for list. */ 958 nl = calloc(2, sizeof(struct nlist)); 959 if (nl == NULL) 960 err(1, NULL); 961 962 nl->n_name = sym->sym_name; 963 964 if (nlist_elf32(fname, nl) != 0) 965 errx(1, "%s: symbol %s not found", 966 fname, sym->sym_name); 967 968 if (nl->n_type != (N_TEXT)) 969 errx(1, "%s: %s: wrong type (%x)", 970 fname, sym->sym_name, nl->n_type); 971 972 /* Get a pointer to where the symbol's value needs to go. */ 973 vp = proto + nl->n_value; 974 975 switch (sym->sym_size) { 976 case 4: /* u_int32_t */ 977 lp = (u_int32_t *) vp; 978 *lp = sym->sym_value; 979 break; 980 case 2: /* u_int16_t */ 981 if (sym->sym_value >= 0x10000) /* out of range */ 982 errx(1, "%s: symbol out of range (%u)", 983 sym->sym_name, sym->sym_value); 984 wp = (u_int16_t *) vp; 985 *wp = (u_int16_t) sym->sym_value; 986 break; 987 case 1: /* u_int16_t */ 988 if (sym->sym_value >= 0x100) /* out of range */ 989 errx(1, "%s: symbol out of range (%u)", 990 sym->sym_name, sym->sym_value); 991 bp = (u_int8_t *) vp; 992 *bp = (u_int8_t) sym->sym_value; 993 break; 994 default: 995 errx(1, "%s: bad symbol size %d", 996 sym->sym_name, sym->sym_size); 997 /* NOTREACHED */ 998 } 999 1000 free(nl); 1001 } 1002 } 1003 1004 static int 1005 sbchk(struct fs *fs, daddr_t sbloc) 1006 { 1007 if (verbose) 1008 fprintf(stderr, "looking for superblock at %lld\n", sbloc); 1009 1010 if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) { 1011 if (verbose) 1012 fprintf(stderr, "bad superblock magic 0x%x\n", 1013 fs->fs_magic); 1014 return (0); 1015 } 1016 1017 /* 1018 * Looking for an FFS1 file system at SBLOCK_UFS2 will find the 1019 * wrong superblock for file systems with 64k block size. 1020 */ 1021 if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) { 1022 if (verbose) 1023 fprintf(stderr, "skipping ffs1 superblock at %lld\n", 1024 sbloc); 1025 return (0); 1026 } 1027 1028 if (fs->fs_bsize <= 0 || fs->fs_bsize < sizeof(struct fs) || 1029 fs->fs_bsize > MAXBSIZE) { 1030 if (verbose) 1031 fprintf(stderr, "invalid superblock block size %d\n", 1032 fs->fs_bsize); 1033 return (0); 1034 } 1035 1036 if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) { 1037 if (verbose) 1038 fprintf(stderr, "invalid superblock size %d\n", 1039 fs->fs_sbsize); 1040 return (0); 1041 } 1042 1043 if (fs->fs_inopb <= 0) { 1044 if (verbose) 1045 fprintf(stderr, "invalid superblock inodes/block %d\n", 1046 fs->fs_inopb); 1047 return (0); 1048 } 1049 1050 if (verbose) 1051 fprintf(stderr, "found valid %s superblock\n", 1052 fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1"); 1053 1054 return (1); 1055 } 1056 1057 static void 1058 sbread(int fd, daddr_t poffset, struct fs **fs, char *sblock) 1059 { 1060 int i; 1061 daddr_t sboff; 1062 1063 for (i = 0; sbtry[i] != -1; i++) { 1064 sboff = sbtry[i] / DEV_BSIZE; 1065 devread(fd, sblock, poffset + sboff, SBSIZE, "superblock"); 1066 *fs = (struct fs *)sblock; 1067 if (sbchk(*fs, sbtry[i])) 1068 break; 1069 } 1070 1071 if (sbtry[i] == -1) 1072 errx(1, "couldn't find ffs superblock"); 1073 } 1074