1116743Ssam/* $OpenBSD: i386_installboot.c,v 1.46 2023/06/11 14:00:04 krw Exp $ */ 2186904Ssam/* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ 3116743Ssam 4116743Ssam/* 5116743Ssam * Copyright (c) 2013 Pedro Martelletto 6116743Ssam * Copyright (c) 2011 Joel Sing <jsing@openbsd.org> 7116743Ssam * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com> 8116743Ssam * Copyright (c) 1997 Michael Shalayeff 9116743Ssam * Copyright (c) 1994 Paul Kranenburg 10116743Ssam * All rights reserved. 11116743Ssam * 12116743Ssam * Redistribution and use in source and binary forms, with or without 13116743Ssam * modification, are permitted provided that the following conditions 14116743Ssam * are met: 15116743Ssam * 1. Redistributions of source code must retain the above copyright 16116743Ssam * notice, this list of conditions and the following disclaimer. 17116743Ssam * 2. Redistributions in binary form must reproduce the above copyright 18116743Ssam * notice, this list of conditions and the following disclaimer in the 19116743Ssam * documentation and/or other materials provided with the distribution. 20116743Ssam * 3. All advertising materials mentioning features or use of this software 21116743Ssam * must display the following acknowledgement: 22116743Ssam * This product includes software developed by Paul Kranenburg. 23116743Ssam * 4. The name of the author may not be used to endorse or promote products 24116743Ssam * derived from this software without specific prior written permission 25116743Ssam * 26116743Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27116743Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28116743Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29116743Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30116743Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31116743Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32116743Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33116743Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34116743Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35116743Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36116743Ssam */ 37116743Ssam 38116743Ssam#define ELFSIZE 32 39116743Ssam 40116743Ssam#include <sys/param.h> /* DEV_BSIZE */ 41116743Ssam#include <sys/disklabel.h> 42155492Ssam#include <sys/dkio.h> 43138570Ssam#include <sys/ioctl.h> 44116743Ssam#include <sys/mount.h> 45116743Ssam#include <sys/reboot.h> 46116743Ssam#include <sys/stat.h> 47138570Ssam#include <sys/sysctl.h> 48116743Ssam#include <sys/time.h> 49138570Ssam 50116743Ssam#include <ufs/ufs/dinode.h> 51116743Ssam#include <ufs/ufs/dir.h> 52116743Ssam#include <ufs/ffs/fs.h> 53116743Ssam 54116743Ssam#include <machine/cpu.h> 55116743Ssam#include <machine/biosvar.h> 56116743Ssam 57116743Ssam#include <elf.h> 58116743Ssam#include <err.h> 59116743Ssam#include <errno.h> 60116743Ssam#include <fcntl.h> 61116743Ssam#include <nlist.h> 62116743Ssam#include <stdlib.h> 63116743Ssam#include <stdio.h> 64116743Ssam#include <stdint.h> 65116743Ssam#include <string.h> 66116743Ssam#include <unistd.h> 67116743Ssam#include <util.h> 68116743Ssam#include <uuid.h> 69116743Ssam 70127779Ssam#include "installboot.h" 71127779Ssam#include "i386_installboot.h" 72170530Ssam 73170530Ssamchar *bootldr; 74116743Ssam 75116743Ssamchar *blkstore; 76116743Ssamsize_t blksize; 77116743Ssam 78116743Ssamstruct sym_data pbr_symbols[] = { 79116743Ssam {"_fs_bsize_p", 2}, 80138570Ssam {"_fs_bsize_s", 2}, 81116743Ssam {"_fsbtodb", 1}, 82218689Sadrian {"_p_offset", 4}, 83119147Ssam {"_inodeblk", 4}, 84127779Ssam {"_inodedbl", 4}, 85138570Ssam {"_nblocks", 2}, 86138570Ssam {"_blkincr", 1}, 87119147Ssam {NULL} 88138570Ssam}; 89138570Ssam 90161187Ssamstatic void devread(int, void *, daddr_t, size_t, char *); 91138570Ssamstatic u_int findopenbsd(int, struct disklabel *); 92116743Ssamstatic int getbootparams(char *, int, struct disklabel *); 93116743Ssamstatic char *loadproto(char *, long *); 94116743Ssamstatic int gpt_chk_mbr(struct dos_partition *, u_int64_t); 95116743Ssamstatic int sbchk(struct fs *, daddr_t); 96116743Ssamstatic void sbread(int, daddr_t, struct fs **, char *); 97116743Ssam 98116743Ssamstatic const daddr_t sbtry[] = SBLOCKSEARCH; 99138570Ssam 100138570Ssam/* 101138570Ssam * Read information about /boot's inode and filesystem parameters, then 102138570Ssam * put biosboot (partition boot record) on the target drive with these 103159894Ssam * parameters patched in. 104159894Ssam */ 105160992Ssam 106170530Ssamvoid 107170530Ssammd_init(void) 108170530Ssam{ 109170530Ssam stages = 2; 110170530Ssam stage1 = "/usr/mdec/biosboot"; 111170530Ssam stage2 = "/usr/mdec/boot"; 112186904Ssam 113186904Ssam bootldr = "/boot"; 114186904Ssam} 115186904Ssam 116186904Ssamvoid 117186904Ssammd_loadboot(void) 118188195Ssam{ 119188195Ssam /* Load prototype boot blocks. */ 120188555Ssam if ((blkstore = loadproto(stage1, &blksize)) == NULL) 121211299Sadrian exit(1); 122217684Sadrian 123218378Sadrian /* XXX - Paranoia: Make sure size is aligned! */ 124221965Sadrian if (blksize & (DEV_BSIZE - 1)) 125221965Sadrian errx(1, "proto %s bad size=%ld", stage1, blksize); 126221965Sadrian 127221965Sadrian if (blksize > SBSIZE - DEV_BSIZE) 128221965Sadrian errx(1, "proto bootblocks too big"); 129218689Sadrian} 130218924Sadrian 131221965Sadrianvoid 132220772Sadrianmd_prepareboot(int devfd, char *dev) 133220782Sadrian{ 134221965Sadrian struct disklabel dl; 135221965Sadrian int part; 136221965Sadrian 137221965Sadrian /* Get and check disklabel. */ 138116743Ssam if (ioctl(devfd, DIOCGDINFO, &dl) == -1) 139116743Ssam err(1, "disklabel: %s", dev); 140116743Ssam if (dl.d_magic != DISKMAGIC) 141188557Ssam errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 142116743Ssam 143123044Ssam /* Warn on unknown disklabel types. */ 144138570Ssam if (dl.d_type == 0) 145138570Ssam warnx("disklabel type unknown"); 146138570Ssam 147138570Ssam part = findgptefisys(devfd, &dl); 148138570Ssam if (part != -1) { 149138570Ssam create_filesystem(&dl, (char)part); 150138570Ssam return; 151138570Ssam } 152138570Ssam} 153138570Ssam 154123044Ssamvoid 155123044Ssammd_installboot(int devfd, char *dev) 156123044Ssam{ 157224245Sadrian struct disklabel dl; 158123044Ssam int part; 159119783Ssam 160119783Ssam /* Get and check disklabel. */ 161119783Ssam if (ioctl(devfd, DIOCGDINFO, &dl) == -1) 162119783Ssam err(1, "disklabel: %s", dev); 163154140Ssam if (dl.d_magic != DISKMAGIC) 164119783Ssam errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 165119783Ssam 166123928Ssam /* Warn on unknown disklabel types. */ 167154140Ssam if (dl.d_type == 0) 168154140Ssam warnx("disklabel type unknown"); 169170530Ssam 170119783Ssam part = findgptefisys(devfd, &dl); 171119783Ssam if (part != -1) { 172119783Ssam write_filesystem(&dl, (char)part); 173119783Ssam return; 174154140Ssam } 175154140Ssam 176119783Ssam bootldr = fileprefix(root, bootldr); 177170530Ssam if (bootldr == NULL) 178170530Ssam exit(1); 179170530Ssam if (verbose) 180170530Ssam fprintf(stderr, "%s %s to %s\n", 181170530Ssam (nowrite ? "would copy" : "copying"), stage2, bootldr); 182119783Ssam if (!nowrite) 183170530Ssam if (filecopy(stage2, bootldr) == -1) 184170530Ssam exit(1); 185170530Ssam 186119783Ssam /* Get bootstrap parameters to patch into proto. */ 187119783Ssam if (getbootparams(bootldr, devfd, &dl) != 0) 188154140Ssam exit(1); 189119783Ssam 190119783Ssam /* Write boot blocks to device. */ 191123928Ssam write_bootblocks(devfd, dev, &dl); 192123928Ssam} 193170530Ssam 194119783Ssamvoid 195119783Ssamwrite_bootblocks(int devfd, char *dev, struct disklabel *dl) 196119783Ssam{ 197119783Ssam struct stat sb; 198154140Ssam u_int8_t *secbuf; 199154140Ssam u_int start; 200119783Ssam 201123928Ssam /* Write patched proto bootblock(s) into the superblock. */ 202123928Ssam if (fstat(devfd, &sb) == -1) 203170530Ssam err(1, "fstat: %s", dev); 204170530Ssam 205170530Ssam if (!S_ISCHR(sb.st_mode)) 206170530Ssam errx(1, "%s: not a character device", dev); 207170530Ssam 208119783Ssam /* Patch the parameters into the proto bootstrap sector. */ 209224245Sadrian pbr_set_symbols(stage1, blkstore, pbr_symbols); 210224245Sadrian 211224245Sadrian if (!nowrite) { 212224245Sadrian /* Sync filesystems (to clean in-memory superblock?). */ 213224245Sadrian sync(); sleep(1); 214224245Sadrian } 215224245Sadrian 216224245Sadrian /* 217224245Sadrian * Find bootstrap sector. 218224245Sadrian */ 219224245Sadrian start = findopenbsd(devfd, dl); 220224245Sadrian if (verbose) { 221224245Sadrian if (start == 0) 222224245Sadrian fprintf(stderr, "no MBR, "); 223224245Sadrian fprintf(stderr, "%s will be written at sector %u\n", 224224245Sadrian stage1, start); 225224245Sadrian } 226224245Sadrian 227224245Sadrian if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC) 228224245Sadrian warnx("%s extends beyond sector %u. OpenBSD might not boot.", 229224245Sadrian stage1, BOOTBIOS_MAXSEC); 230224245Sadrian 231224245Sadrian if (!nowrite) { 232224245Sadrian secbuf = calloc(1, dl->d_secsize); 233224245Sadrian if (pread(devfd, secbuf, dl->d_secsize, (off_t)start * 234224245Sadrian dl->d_secsize) != dl->d_secsize) 235224245Sadrian err(1, "pread boot sector"); 236224245Sadrian bcopy(blkstore, secbuf, blksize); 237224245Sadrian if (pwrite(devfd, secbuf, dl->d_secsize, (off_t)start * 238224245Sadrian dl->d_secsize) != dl->d_secsize) 239116743Ssam err(1, "pwrite bootstrap"); 240 free(secbuf); 241 } 242} 243 244int 245create_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 288void 289write_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 char cmd[60]; 294 char dst[PATH_MAX]; 295 char *src; 296 size_t mntlen, pathlen, srclen; 297 int rslt; 298 299 src = NULL; 300 301 /* Create directory for temporary mount point. */ 302 strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst)); 303 if (mkdtemp(dst) == NULL) 304 err(1, "mkdtemp('%s') failed", dst); 305 mntlen = strlen(dst); 306 307 /* Mount <duid>.<part> as msdos filesystem. */ 308 memset(&args, 0, sizeof(args)); 309 rslt = asprintf(&args.fspec, 310 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 311 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], 312 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], 313 part); 314 if (rslt == -1) { 315 warn("bad special device"); 316 goto rmdir; 317 } 318 319 args.export_info.ex_root = -2; /* unchecked anyway on DOS fs */ 320 args.export_info.ex_flags = 0; 321 args.flags = MSDOSFSMNT_LONGNAME; 322 323 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 324 /* Try fsck'ing it. */ 325 rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec); 326 if (rslt >= sizeof(cmd)) { 327 warnx("can't build fsck command"); 328 rslt = -1; 329 goto rmdir; 330 } 331 rslt = system(cmd); 332 if (rslt == -1) { 333 warn("system('%s') failed", cmd); 334 goto rmdir; 335 } 336 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 337 /* Try newfs'ing it. */ 338 rslt = create_filesystem(dl, part); 339 if (rslt == -1) 340 goto rmdir; 341 rslt = mount(MOUNT_MSDOS, dst, 0, &args); 342 if (rslt == -1) { 343 warn("unable to mount EFI System partition"); 344 goto rmdir; 345 } 346 } 347 } 348 349 /* Create "/efi/BOOT" directory in <duid>.<part>. */ 350 if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) { 351 rslt = -1; 352 warn("unable to build /efi directory"); 353 goto umount; 354 } 355 rslt = mkdir(dst, 0); 356 if (rslt == -1 && errno != EEXIST) { 357 warn("mkdir('%s') failed", dst); 358 goto umount; 359 } 360 if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) { 361 rslt = -1; 362 warn("unable to build /BOOT directory"); 363 goto umount; 364 } 365 rslt = mkdir(dst, 0); 366 if (rslt == -1 && errno != EEXIST) { 367 warn("mkdir('%s') failed", dst); 368 goto umount; 369 } 370 371 /* 372 * Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/BOOT/. 373 * 374 * N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused! 375 */ 376 pathlen = strlen(dst); 377 if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) { 378 rslt = -1; 379 warn("unable to build /BOOTIA32.EFI path"); 380 goto umount; 381 } 382 src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI"); 383 if (src == NULL) { 384 rslt = -1; 385 goto umount; 386 } 387 srclen = strlen(src); 388 if (verbose) 389 fprintf(stderr, "%s %s to %s\n", 390 (nowrite ? "would copy" : "copying"), src, dst); 391 if (!nowrite) { 392 rslt = filecopy(src, dst); 393 if (rslt == -1) 394 goto umount; 395 } 396 src[srclen - strlen("/BOOTIA32.EFI")] = '\0'; 397 398 dst[pathlen] = '\0'; 399 if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) { 400 rslt = -1; 401 warn("unable to build /BOOTX64.EFI dst path"); 402 goto umount; 403 } 404 if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) { 405 rslt = -1; 406 warn("unable to build /BOOTX64.EFI src path"); 407 goto umount; 408 } 409 if (verbose) 410 fprintf(stderr, "%s %s to %s\n", 411 (nowrite ? "would copy" : "copying"), src, dst); 412 if (!nowrite) { 413 rslt = filecopy(src, dst); 414 if (rslt == -1) 415 goto umount; 416 } 417 418 rslt = 0; 419 420umount: 421 dst[mntlen] = '\0'; 422 if (unmount(dst, MNT_FORCE) == -1) 423 err(1, "unmount('%s') failed", dst); 424 425rmdir: 426 free(args.fspec); 427 dst[mntlen] = '\0'; 428 if (rmdir(dst) == -1) 429 err(1, "rmdir('%s') failed", dst); 430 431 free(src); 432 433 if (rslt == -1) 434 exit(1); 435} 436 437/* 438 * a) For media w/o an MBR use sector 0. 439 * b) For media with an MBR and an OpenBSD (A6) partition use the first 440 * sector of the OpenBSD partition. 441 * c) For media with an MBR and no OpenBSD partition error out. 442 */ 443u_int 444findopenbsd(int devfd, struct disklabel *dl) 445{ 446 struct dos_mbr mbr; 447 u_int mbroff = DOSBBSECTOR; 448 u_int mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */ 449 struct dos_partition *dp; 450 u_int8_t *secbuf; 451 u_int maxebr = DOS_MAXEBR, nextebr; 452 int i; 453 454again: 455 if (!maxebr--) { 456 if (verbose) 457 fprintf(stderr, "Traversed more than %d Extended Boot " 458 "Records (EBRs)\n", DOS_MAXEBR); 459 goto done; 460 } 461 462 if (verbose) 463 fprintf(stderr, "%s boot record (%cBR) at sector %u\n", 464 (mbroff == DOSBBSECTOR) ? "master" : "extended", 465 (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff); 466 467 if ((secbuf = malloc(dl->d_secsize)) == NULL) 468 err(1, NULL); 469 if (pread(devfd, secbuf, dl->d_secsize, (off_t)mbroff * dl->d_secsize) 470 < (ssize_t)sizeof(mbr)) 471 err(4, "can't pread boot record"); 472 bcopy(secbuf, &mbr, sizeof(mbr)); 473 free(secbuf); 474 475 if (mbr.dmbr_sign != DOSMBR_SIGNATURE) { 476 if (mbroff == DOSBBSECTOR) 477 return 0; 478 errx(1, "invalid boot record signature (0x%04X) @ sector %u", 479 mbr.dmbr_sign, mbroff); 480 } 481 482 nextebr = 0; 483 for (i = 0; i < NDOSPART; i++) { 484 dp = &mbr.dmbr_parts[i]; 485 if (!dp->dp_size) 486 continue; 487 488 if (verbose) 489 fprintf(stderr, 490 "\tpartition %d: type 0x%02X offset %u size %u\n", 491 i, dp->dp_typ, dp->dp_start, dp->dp_size); 492 493 if (dp->dp_typ == DOSPTYP_OPENBSD) { 494 if (dp->dp_start > (dp->dp_start + mbroff)) 495 continue; 496 return (dp->dp_start + mbroff); 497 } 498 499 if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || 500 dp->dp_typ == DOSPTYP_EXTENDL)) { 501 nextebr = dp->dp_start + mbr_eoff; 502 if (nextebr < dp->dp_start) 503 nextebr = (u_int)-1; 504 if (mbr_eoff == DOSBBSECTOR) 505 mbr_eoff = dp->dp_start; 506 } 507 } 508 509 if (nextebr && nextebr != (u_int)-1) { 510 mbroff = nextebr; 511 goto again; 512 } 513 514 done: 515 errx(1, "no OpenBSD partition"); 516} 517 518/* 519 * Returns 0 if the MBR with the provided partition array is a GPT protective 520 * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only 521 * one MBR partition, an EFI partition that either covers the whole disk or as 522 * much of it as is possible with a 32bit size field. 523 * 524 * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** 525 */ 526static int 527gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 528{ 529 struct dos_partition *dp2; 530 int efi, found, i; 531 u_int32_t psize; 532 533 found = efi = 0; 534 for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { 535 if (dp2->dp_typ == DOSPTYP_UNUSED) 536 continue; 537 found++; 538 if (dp2->dp_typ != DOSPTYP_EFI) 539 continue; 540 if (letoh32(dp2->dp_start) != GPTSECTOR) 541 continue; 542 psize = letoh32(dp2->dp_size); 543 if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) 544 efi++; 545 } 546 if (found == 1 && efi == 1) 547 return (0); 548 549 return (1); 550} 551 552int 553findgptefisys(int devfd, struct disklabel *dl) 554{ 555 struct gpt_partition gp[NGPTPARTITIONS]; 556 struct gpt_header gh; 557 struct dos_partition dp[NDOSPART]; 558 struct uuid efisys_uuid; 559 const char efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM; 560 off_t off; 561 ssize_t len; 562 u_int64_t start; 563 int i; 564 uint32_t orig_csum, new_csum; 565 uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec; 566 u_int8_t *secbuf; 567 568 /* Prepare EFI System UUID */ 569 uuid_dec_be(efisys_uuid_code, &efisys_uuid); 570 571 if ((secbuf = malloc(dl->d_secsize)) == NULL) 572 err(1, NULL); 573 574 /* Check that there is a protective MBR. */ 575 len = pread(devfd, secbuf, dl->d_secsize, 0); 576 if (len != dl->d_secsize) 577 err(4, "can't read mbr"); 578 memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp)); 579 if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) { 580 free(secbuf); 581 return (-1); 582 } 583 584 /* Check GPT Header. */ 585 off = dl->d_secsize; /* Read header from sector 1. */ 586 len = pread(devfd, secbuf, dl->d_secsize, off); 587 if (len != dl->d_secsize) 588 err(4, "can't pread gpt header"); 589 590 memcpy(&gh, secbuf, sizeof(gh)); 591 free(secbuf); 592 593 /* Check signature */ 594 if (letoh64(gh.gh_sig) != GPTSIGNATURE) 595 return (-1); 596 597 if (letoh32(gh.gh_rev) != GPTREVISION) 598 return (-1); 599 600 ghsize = letoh32(gh.gh_size); 601 if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) 602 return (-1); 603 604 /* Check checksum */ 605 orig_csum = gh.gh_csum; 606 gh.gh_csum = 0; 607 new_csum = crc32((unsigned char *)&gh, ghsize); 608 gh.gh_csum = orig_csum; 609 if (letoh32(orig_csum) != new_csum) 610 return (-1); 611 612 off = letoh64(gh.gh_part_lba) * dl->d_secsize; 613 ghpartsize = letoh32(gh.gh_part_size); 614 ghpartspersec = dl->d_secsize / ghpartsize; 615 ghpartnum = letoh32(gh.gh_part_num); 616 if ((secbuf = malloc(dl->d_secsize)) == NULL) 617 err(1, NULL); 618 for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) { 619 len = pread(devfd, secbuf, dl->d_secsize, off); 620 if (len != dl->d_secsize) { 621 free(secbuf); 622 return (-1); 623 } 624 memcpy(gp + i * ghpartspersec, secbuf, 625 ghpartspersec * sizeof(struct gpt_partition)); 626 off += dl->d_secsize; 627 } 628 free(secbuf); 629 new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize); 630 if (new_csum != letoh32(gh.gh_part_csum)) 631 return (-1); 632 633 start = 0; 634 for (i = 0; i < ghpartnum && start == 0; i++) { 635 if (memcmp(&gp[i].gp_type, &efisys_uuid, 636 sizeof(struct uuid)) == 0) 637 start = letoh64(gp[i].gp_lba_start); 638 } 639 640 if (start) { 641 for (i = 0; i < MAXPARTITIONS; i++) { 642 if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 && 643 DL_GETPOFFSET(&dl->d_partitions[i]) == start) 644 return ('a' + i); 645 } 646 } 647 648 return (-1); 649} 650 651/* 652 * Load the prototype boot sector (biosboot) into memory. 653 */ 654static char * 655loadproto(char *fname, long *size) 656{ 657 int fd; 658 size_t tdsize; /* text+data size */ 659 char *bp; 660 Elf_Ehdr eh; 661 Elf_Word phsize; 662 Elf_Phdr *ph; 663 664 if ((fd = open(fname, O_RDONLY)) == -1) 665 err(1, "%s", fname); 666 667 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 668 errx(1, "%s: read failed", fname); 669 670 if (!IS_ELF(eh)) 671 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname, 672 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 673 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 674 675 /* 676 * We have to include the exec header in the beginning of 677 * the buffer, and leave extra space at the end in case 678 * the actual write to disk wants to skip the header. 679 */ 680 681 /* Program load header. */ 682 if (eh.e_phnum != 1) 683 errx(1, "%s: %u ELF load sections (only support 1)", 684 fname, eh.e_phnum); 685 686 ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr)); 687 if (ph == NULL) 688 err(1, NULL); 689 phsize = eh.e_phnum * sizeof(Elf_Phdr); 690 691 if (pread(fd, ph, phsize, eh.e_phoff) != phsize) 692 errx(1, "%s: can't pread header", fname); 693 694 tdsize = ph->p_filesz; 695 696 /* 697 * Allocate extra space here because the caller may copy 698 * the boot block starting at the end of the exec header. 699 * This prevents reading beyond the end of the buffer. 700 */ 701 if ((bp = calloc(tdsize, 1)) == NULL) 702 err(1, NULL); 703 704 /* Read the rest of the file. */ 705 if (pread(fd, bp, tdsize, ph->p_offset) != (ssize_t)tdsize) 706 errx(1, "%s: pread failed", fname); 707 708 *size = tdsize; /* not aligned to DEV_BSIZE */ 709 710 close(fd); 711 return bp; 712} 713 714static void 715devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) 716{ 717 if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size) 718 err(1, "%s: devread: pread", msg); 719} 720 721/* 722 * Read information about /boot's inode, then put this and filesystem 723 * parameters from the superblock into pbr_symbols. 724 */ 725static int 726getbootparams(char *boot, int devfd, struct disklabel *dl) 727{ 728 int fd; 729 struct stat dsb, fsb; 730 struct statfs fssb; 731 struct partition *pp; 732 struct fs *fs; 733 char *sblock, *buf; 734 u_int blk, *ap; 735 int ndb; 736 int mib[3]; 737 size_t size; 738 dev_t dev; 739 int incr; 740 741 /* 742 * Open 2nd-level boot program and record enough details about 743 * where it is on the filesystem represented by `devfd' 744 * (inode block, offset within that block, and various filesystem 745 * parameters essentially taken from the superblock) for biosboot 746 * to be able to load it later. 747 */ 748 749 /* Make sure the (probably new) boot file is on disk. */ 750 sync(); sleep(1); 751 752 if ((fd = open(boot, O_RDONLY)) == -1) 753 err(1, "open: %s", boot); 754 755 if (fstatfs(fd, &fssb) == -1) 756 err(1, "statfs: %s", boot); 757 758 if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) && 759 strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) ) 760 errx(1, "%s: not on an FFS filesystem", boot); 761 762#if 0 763 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 764 errx(1, "read: %s", boot); 765 766 if (!IS_ELF(eh)) { 767 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", 768 boot, 769 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 770 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 771 } 772#endif 773 774 if (fsync(fd) != 0) 775 err(1, "fsync: %s", boot); 776 777 if (fstat(fd, &fsb) != 0) 778 err(1, "fstat: %s", boot); 779 780 if (fstat(devfd, &dsb) != 0) 781 err(1, "fstat: %d", devfd); 782 783 /* Check devices. */ 784 mib[0] = CTL_MACHDEP; 785 mib[1] = CPU_CHR2BLK; 786 mib[2] = dsb.st_rdev; 787 size = sizeof(dev); 788 if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) { 789 if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS) 790 errx(1, "cross-device install"); 791 } 792 793 pp = &dl->d_partitions[DISKPART(fsb.st_dev)]; 794 close(fd); 795 796 if ((sblock = malloc(SBSIZE)) == NULL) 797 err(1, NULL); 798 799 sbread(devfd, DL_SECTOBLK(dl, pp->p_offset), &fs, sblock); 800 801 /* Read inode. */ 802 if ((buf = malloc(fs->fs_bsize)) == NULL) 803 err(1, NULL); 804 805 blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino)); 806 807 /* 808 * Have the inode. Figure out how many filesystem blocks (not disk 809 * sectors) there are for biosboot to load. 810 */ 811 devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk, 812 fs->fs_bsize, "inode"); 813 if (fs->fs_magic == FS_UFS2_MAGIC) { 814 struct ufs2_dinode *ip2 = (struct ufs2_dinode *)(buf) + 815 ino_to_fsbo(fs, fsb.st_ino); 816 ndb = howmany(ip2->di_size, fs->fs_bsize); 817 ap = (u_int *)ip2->di_db; 818 incr = sizeof(u_int32_t); 819 } else { 820 struct ufs1_dinode *ip1 = (struct ufs1_dinode *)(buf) + 821 ino_to_fsbo(fs, fsb.st_ino); 822 ndb = howmany(ip1->di_size, fs->fs_bsize); 823 ap = (u_int *)ip1->di_db; 824 incr = 0; 825 } 826 827 if (ndb <= 0) 828 errx(1, "No blocks to load"); 829 830 /* 831 * Now set the values that will need to go into biosboot 832 * (the partition boot record, a.k.a. the PBR). 833 */ 834 sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16)); 835 sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 836 dl->d_secsize)); 837 838 /* 839 * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The 840 * ino_to_fsba() return value is the number of fs_fsize units. 841 * Calculate the shift to convert fs_fsize into physical sectors, 842 * which are added to p_offset to get the sector address BIOS 843 * will use. 844 * 845 * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize. 846 */ 847 sym_set_value(pbr_symbols, "_fsbtodb", 848 ffs(fs->fs_fsize / dl->d_secsize) - 1); 849 850 sym_set_value(pbr_symbols, "_p_offset", pp->p_offset); 851 sym_set_value(pbr_symbols, "_inodeblk", 852 ino_to_fsba(fs, fsb.st_ino)); 853 sym_set_value(pbr_symbols, "_inodedbl", 854 ((((char *)ap) - buf) + INODEOFF)); 855 sym_set_value(pbr_symbols, "_nblocks", ndb); 856 sym_set_value(pbr_symbols, "_blkincr", incr); 857 858 if (verbose) { 859 fprintf(stderr, "%s is %d blocks x %d bytes\n", 860 boot, ndb, fs->fs_bsize); 861 fprintf(stderr, "fs block shift %u; part offset %u; " 862 "inode block %lld, offset %u\n", 863 ffs(fs->fs_fsize / dl->d_secsize) - 1, 864 pp->p_offset, 865 ino_to_fsba(fs, fsb.st_ino), 866 (unsigned int)((((char *)ap) - buf) + INODEOFF)); 867 fprintf(stderr, "expecting %d-bit fs blocks (incr %d)\n", 868 incr ? 64 : 32, incr); 869 } 870 871 free (sblock); 872 free (buf); 873 874 return 0; 875} 876 877void 878sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value) 879{ 880 struct sym_data *p; 881 882 for (p = sym_list; p->sym_name != NULL; p++) { 883 if (strcmp(p->sym_name, sym) == 0) 884 break; 885 } 886 887 if (p->sym_name == NULL) 888 errx(1, "%s: no such symbol", sym); 889 890 p->sym_value = value; 891 p->sym_set = 1; 892} 893 894/* 895 * Write the parameters stored in sym_list into the in-memory copy of 896 * the prototype biosboot (proto), ready for it to be written to disk. 897 */ 898void 899pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list) 900{ 901 struct sym_data *sym; 902 struct nlist *nl; 903 char *vp; 904 u_int32_t *lp; 905 u_int16_t *wp; 906 u_int8_t *bp; 907 908 for (sym = sym_list; sym->sym_name != NULL; sym++) { 909 if (!sym->sym_set) 910 errx(1, "%s not set", sym->sym_name); 911 912 /* Allocate space for 2; second is null-terminator for list. */ 913 nl = calloc(2, sizeof(struct nlist)); 914 if (nl == NULL) 915 err(1, NULL); 916 917 nl->n_name = sym->sym_name; 918 919 if (nlist_elf32(fname, nl) != 0) 920 errx(1, "%s: symbol %s not found", 921 fname, sym->sym_name); 922 923 if (nl->n_type != (N_TEXT)) 924 errx(1, "%s: %s: wrong type (%x)", 925 fname, sym->sym_name, nl->n_type); 926 927 /* Get a pointer to where the symbol's value needs to go. */ 928 vp = proto + nl->n_value; 929 930 switch (sym->sym_size) { 931 case 4: /* u_int32_t */ 932 lp = (u_int32_t *) vp; 933 *lp = sym->sym_value; 934 break; 935 case 2: /* u_int16_t */ 936 if (sym->sym_value >= 0x10000) /* out of range */ 937 errx(1, "%s: symbol out of range (%u)", 938 sym->sym_name, sym->sym_value); 939 wp = (u_int16_t *) vp; 940 *wp = (u_int16_t) sym->sym_value; 941 break; 942 case 1: /* u_int16_t */ 943 if (sym->sym_value >= 0x100) /* out of range */ 944 errx(1, "%s: symbol out of range (%u)", 945 sym->sym_name, sym->sym_value); 946 bp = (u_int8_t *) vp; 947 *bp = (u_int8_t) sym->sym_value; 948 break; 949 default: 950 errx(1, "%s: bad symbol size %d", 951 sym->sym_name, sym->sym_size); 952 /* NOTREACHED */ 953 } 954 955 free(nl); 956 } 957} 958 959static int 960sbchk(struct fs *fs, daddr_t sbloc) 961{ 962 if (verbose) 963 fprintf(stderr, "looking for superblock at %lld\n", sbloc); 964 965 if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) { 966 if (verbose) 967 fprintf(stderr, "bad superblock magic 0x%x\n", 968 fs->fs_magic); 969 return (0); 970 } 971 972 /* 973 * Looking for an FFS1 file system at SBLOCK_UFS2 will find the 974 * wrong superblock for file systems with 64k block size. 975 */ 976 if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) { 977 if (verbose) 978 fprintf(stderr, "skipping ffs1 superblock at %lld\n", 979 sbloc); 980 return (0); 981 } 982 983 if (fs->fs_bsize <= 0 || fs->fs_bsize < sizeof(struct fs) || 984 fs->fs_bsize > MAXBSIZE) { 985 if (verbose) 986 fprintf(stderr, "invalid superblock block size %d\n", 987 fs->fs_bsize); 988 return (0); 989 } 990 991 if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) { 992 if (verbose) 993 fprintf(stderr, "invalid superblock size %d\n", 994 fs->fs_sbsize); 995 return (0); 996 } 997 998 if (fs->fs_inopb <= 0) { 999 if (verbose) 1000 fprintf(stderr, "invalid superblock inodes/block %d\n", 1001 fs->fs_inopb); 1002 return (0); 1003 } 1004 1005 if (verbose) 1006 fprintf(stderr, "found valid %s superblock\n", 1007 fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1"); 1008 1009 return (1); 1010} 1011 1012static void 1013sbread(int fd, daddr_t poffset, struct fs **fs, char *sblock) 1014{ 1015 int i; 1016 daddr_t sboff; 1017 1018 for (i = 0; sbtry[i] != -1; i++) { 1019 sboff = sbtry[i] / DEV_BSIZE; 1020 devread(fd, sblock, poffset + sboff, SBSIZE, "superblock"); 1021 *fs = (struct fs *)sblock; 1022 if (sbchk(*fs, sbtry[i])) 1023 break; 1024 } 1025 1026 if (sbtry[i] == -1) 1027 errx(1, "couldn't find ffs superblock"); 1028} 1029