1 /* $OpenBSD: i386_installboot.c,v 1.31 2017/10/27 16:47:08 mpi Exp $ */ 2 /* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ 3 4 /* 5 * Copyright (c) 2011 Joel Sing <jsing@openbsd.org> 6 * Copyright (c) 2010 Otto Moerbeek <otto@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 {NULL} 87 }; 88 89 static void devread(int, void *, daddr_t, size_t, char *); 90 static u_int findopenbsd(int, struct disklabel *); 91 static int getbootparams(char *, int, struct disklabel *); 92 static char *loadproto(char *, long *); 93 static int gpt_chk_mbr(struct dos_partition *, u_int64_t); 94 95 /* 96 * Read information about /boot's inode and filesystem parameters, then 97 * put biosboot (partition boot record) on the target drive with these 98 * parameters patched in. 99 */ 100 101 void 102 md_init(void) 103 { 104 stages = 2; 105 stage1 = "/usr/mdec/biosboot"; 106 stage2 = "/usr/mdec/boot"; 107 108 bootldr = "/boot"; 109 } 110 111 void 112 md_loadboot(void) 113 { 114 /* Load prototype boot blocks. */ 115 if ((blkstore = loadproto(stage1, &blksize)) == NULL) 116 exit(1); 117 118 /* XXX - Paranoia: Make sure size is aligned! */ 119 if (blksize & (DEV_BSIZE - 1)) 120 errx(1, "proto %s bad size=%ld", stage1, blksize); 121 122 if (blksize > SBSIZE - DEV_BSIZE) 123 errx(1, "proto bootblocks too big"); 124 } 125 126 void 127 md_installboot(int devfd, char *dev) 128 { 129 struct disklabel dl; 130 int part; 131 132 /* Get and check disklabel. */ 133 if (ioctl(devfd, DIOCGDINFO, &dl) != 0) 134 err(1, "disklabel: %s", dev); 135 if (dl.d_magic != DISKMAGIC) 136 errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 137 138 /* Warn on unknown disklabel types. */ 139 if (dl.d_type == 0) 140 warnx("disklabel type unknown"); 141 142 part = findgptefisys(devfd, &dl); 143 if (part != -1) { 144 write_efisystem(&dl, (char)part); 145 return; 146 } 147 148 bootldr = fileprefix(root, bootldr); 149 if (bootldr == NULL) 150 exit(1); 151 if (verbose) 152 fprintf(stderr, "%s %s to %s\n", 153 (nowrite ? "would copy" : "copying"), stage2, bootldr); 154 if (!nowrite) 155 if (filecopy(stage2, bootldr) == -1) 156 exit(1); 157 158 /* Get bootstrap parameters to patch into proto. */ 159 if (getbootparams(bootldr, devfd, &dl) != 0) 160 exit(1); 161 162 /* Write boot blocks to device. */ 163 write_bootblocks(devfd, dev, &dl); 164 } 165 166 void 167 write_bootblocks(int devfd, char *dev, struct disklabel *dl) 168 { 169 struct stat sb; 170 u_int8_t *secbuf; 171 u_int start = 0; 172 173 /* Write patched proto bootblock(s) into the superblock. */ 174 if (fstat(devfd, &sb) < 0) 175 err(1, "fstat: %s", dev); 176 177 if (!S_ISCHR(sb.st_mode)) 178 errx(1, "%s: not a character device", dev); 179 180 /* Patch the parameters into the proto bootstrap sector. */ 181 pbr_set_symbols(stage1, blkstore, pbr_symbols); 182 183 if (!nowrite) { 184 /* Sync filesystems (to clean in-memory superblock?). */ 185 sync(); sleep(1); 186 } 187 188 /* 189 * Find OpenBSD partition. Floppies are special, getting an 190 * everything-in-one /boot starting at sector 0. 191 */ 192 if (dl->d_type != DTYPE_FLOPPY) { 193 start = findopenbsd(devfd, dl); 194 if (start == (u_int)-1) 195 errx(1, "no OpenBSD partition"); 196 } 197 198 if (verbose) 199 fprintf(stderr, "%s will be written at sector %u\n", 200 stage1, start); 201 202 if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC) 203 warnx("%s extends beyond sector %u. OpenBSD might not boot.", 204 stage1, BOOTBIOS_MAXSEC); 205 206 if (!nowrite) { 207 secbuf = calloc(1, dl->d_secsize); 208 if (pread(devfd, secbuf, dl->d_secsize, (off_t)start * 209 dl->d_secsize) != dl->d_secsize) 210 err(1, "pread boot sector"); 211 bcopy(blkstore, secbuf, blksize); 212 if (pwrite(devfd, secbuf, dl->d_secsize, (off_t)start * 213 dl->d_secsize) != dl->d_secsize) 214 err(1, "pwrite bootstrap"); 215 free(secbuf); 216 } 217 } 218 219 void 220 write_efisystem(struct disklabel *dl, char part) 221 { 222 static char *fsckfmt = "/sbin/fsck_msdos %s >/dev/null"; 223 static char *newfsfmt ="/sbin/newfs_msdos %s >/dev/null"; 224 struct msdosfs_args args; 225 char cmd[60]; 226 char dst[PATH_MAX]; 227 char *src; 228 size_t mntlen, pathlen, srclen; 229 int rslt; 230 231 src = NULL; 232 233 /* Create directory for temporary mount point. */ 234 strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst)); 235 if (mkdtemp(dst) == NULL) 236 err(1, "mkdtemp('%s') failed", dst); 237 mntlen = strlen(dst); 238 239 /* Mount <duid>.<part> as msdos filesystem. */ 240 memset(&args, 0, sizeof(args)); 241 rslt = asprintf(&args.fspec, 242 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c", 243 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3], 244 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7], 245 part); 246 if (rslt == -1) { 247 warn("bad special device"); 248 goto rmdir; 249 } 250 251 args.export_info.ex_root = -2; /* unchecked anyway on DOS fs */ 252 args.export_info.ex_flags = 0; 253 254 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 255 /* Try fsck'ing it. */ 256 rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec); 257 if (rslt >= sizeof(cmd)) { 258 warnx("can't build fsck command"); 259 rslt = -1; 260 goto rmdir; 261 } 262 rslt = system(cmd); 263 if (rslt == -1) { 264 warn("system('%s') failed", cmd); 265 goto rmdir; 266 } 267 if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) { 268 /* Try newfs'ing it. */ 269 rslt = snprintf(cmd, sizeof(cmd), newfsfmt, 270 args.fspec); 271 if (rslt >= sizeof(cmd)) { 272 warnx("can't build newfs command"); 273 rslt = -1; 274 goto rmdir; 275 } 276 rslt = system(cmd); 277 if (rslt == -1) { 278 warn("system('%s') failed", cmd); 279 goto rmdir; 280 } 281 rslt = mount(MOUNT_MSDOS, dst, 0, &args); 282 if (rslt == -1) { 283 warn("unable to mount EFI System partition"); 284 goto rmdir; 285 } 286 } 287 } 288 289 /* Create "/efi/BOOT" directory in <duid>.<part>. */ 290 if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) { 291 rslt = -1; 292 warn("unable to build /efi directory"); 293 goto umount; 294 } 295 rslt = mkdir(dst, 0); 296 if (rslt == -1 && errno != EEXIST) { 297 warn("mkdir('%s') failed", dst); 298 goto umount; 299 } 300 if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) { 301 rslt = -1; 302 warn("unable to build /BOOT directory"); 303 goto umount; 304 } 305 rslt = mkdir(dst, 0); 306 if (rslt == -1 && errno != EEXIST) { 307 warn("mkdir('%s') failed", dst); 308 goto umount; 309 } 310 311 /* 312 * Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/BOOT/. 313 * 314 * N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused! 315 */ 316 pathlen = strlen(dst); 317 if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) { 318 rslt = -1; 319 warn("unable to build /BOOTIA32.EFI path"); 320 goto umount; 321 } 322 src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI"); 323 if (src == NULL) { 324 rslt = -1; 325 goto umount; 326 } 327 srclen = strlen(src); 328 if (verbose) 329 fprintf(stderr, "%s %s to %s\n", 330 (nowrite ? "would copy" : "copying"), src, dst); 331 if (!nowrite) { 332 rslt = filecopy(src, dst); 333 if (rslt == -1) 334 goto umount; 335 } 336 src[srclen - strlen("/BOOTIA32.EFI")] = '\0'; 337 338 dst[pathlen] = '\0'; 339 if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) { 340 rslt = -1; 341 warn("unable to build /BOOTX64.EFI dst path"); 342 goto umount; 343 } 344 if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) { 345 rslt = -1; 346 warn("unable to build /BOOTX64.EFI src path"); 347 goto umount; 348 } 349 if (verbose) 350 fprintf(stderr, "%s %s to %s\n", 351 (nowrite ? "would copy" : "copying"), src, dst); 352 if (!nowrite) { 353 rslt = filecopy(src, dst); 354 if (rslt == -1) 355 goto umount; 356 } 357 358 rslt = 0; 359 360 umount: 361 dst[mntlen] = '\0'; 362 if (unmount(dst, MNT_FORCE) == -1) 363 err(1, "unmount('%s') failed", dst); 364 365 rmdir: 366 free(args.fspec); 367 dst[mntlen] = '\0'; 368 if (rmdir(dst) == -1) 369 err(1, "rmdir('%s') failed", dst); 370 371 free(src); 372 373 if (rslt == -1) 374 exit(1); 375 } 376 377 u_int 378 findopenbsd(int devfd, struct disklabel *dl) 379 { 380 struct dos_mbr mbr; 381 u_int mbroff = DOSBBSECTOR; 382 u_int mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */ 383 struct dos_partition *dp; 384 u_int8_t *secbuf; 385 u_int maxebr = DOS_MAXEBR, nextebr; 386 int i; 387 388 again: 389 if (!maxebr--) { 390 if (verbose) 391 fprintf(stderr, "Traversed more than %d Extended Boot " 392 "Records (EBRs)\n", DOS_MAXEBR); 393 return ((u_int)-1); 394 } 395 396 if (verbose) 397 fprintf(stderr, "%s boot record (%cBR) at sector %u\n", 398 (mbroff == DOSBBSECTOR) ? "master" : "extended", 399 (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff); 400 401 if ((secbuf = malloc(dl->d_secsize)) == NULL) 402 err(1, NULL); 403 if (pread(devfd, secbuf, dl->d_secsize, (off_t)mbroff * dl->d_secsize) 404 < (ssize_t)sizeof(mbr)) 405 err(4, "can't pread boot record"); 406 bcopy(secbuf, &mbr, sizeof(mbr)); 407 free(secbuf); 408 409 if (mbr.dmbr_sign != DOSMBR_SIGNATURE) 410 errx(1, "invalid boot record signature (0x%04X) @ sector %u", 411 mbr.dmbr_sign, mbroff); 412 413 nextebr = 0; 414 for (i = 0; i < NDOSPART; i++) { 415 dp = &mbr.dmbr_parts[i]; 416 if (!dp->dp_size) 417 continue; 418 419 if (verbose) 420 fprintf(stderr, 421 "\tpartition %d: type 0x%02X offset %u size %u\n", 422 i, dp->dp_typ, dp->dp_start, dp->dp_size); 423 424 if (dp->dp_typ == DOSPTYP_OPENBSD) { 425 if (dp->dp_start > (dp->dp_start + mbroff)) 426 continue; 427 return (dp->dp_start + mbroff); 428 } 429 430 if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || 431 dp->dp_typ == DOSPTYP_EXTENDL)) { 432 nextebr = dp->dp_start + mbr_eoff; 433 if (nextebr < dp->dp_start) 434 nextebr = (u_int)-1; 435 if (mbr_eoff == DOSBBSECTOR) 436 mbr_eoff = dp->dp_start; 437 } 438 } 439 440 if (nextebr && nextebr != (u_int)-1) { 441 mbroff = nextebr; 442 goto again; 443 } 444 445 return ((u_int)-1); 446 } 447 448 /* 449 * Returns 0 if the MBR with the provided partition array is a GPT protective 450 * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only 451 * one MBR partition, an EFI partition that either covers the whole disk or as 452 * much of it as is possible with a 32bit size field. 453 * 454 * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** 455 */ 456 static int 457 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 458 { 459 struct dos_partition *dp2; 460 int efi, found, i; 461 u_int32_t psize; 462 463 found = efi = 0; 464 for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { 465 if (dp2->dp_typ == DOSPTYP_UNUSED) 466 continue; 467 found++; 468 if (dp2->dp_typ != DOSPTYP_EFI) 469 continue; 470 psize = letoh32(dp2->dp_size); 471 if (psize == (dsize - 1) || 472 psize == UINT32_MAX) { 473 if (letoh32(dp2->dp_start) == 1) 474 efi++; 475 } 476 } 477 if (found == 1 && efi == 1) 478 return (0); 479 480 return (1); 481 } 482 483 int 484 findgptefisys(int devfd, struct disklabel *dl) 485 { 486 struct gpt_partition gp[NGPTPARTITIONS]; 487 struct gpt_header gh; 488 struct dos_partition dp[NDOSPART]; 489 struct uuid efisys_uuid; 490 const char efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM; 491 off_t off; 492 ssize_t len; 493 u_int64_t start; 494 int i; 495 uint32_t orig_csum, new_csum; 496 uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec; 497 u_int8_t *secbuf; 498 499 /* Prepare EFI System UUID */ 500 uuid_dec_be(efisys_uuid_code, &efisys_uuid); 501 502 if ((secbuf = malloc(dl->d_secsize)) == NULL) 503 err(1, NULL); 504 505 /* Check that there is a protective MBR. */ 506 len = pread(devfd, secbuf, dl->d_secsize, 0); 507 if (len != dl->d_secsize) 508 err(4, "can't read mbr"); 509 memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp)); 510 if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) { 511 free(secbuf); 512 return (-1); 513 } 514 515 /* Check GPT Header. */ 516 off = dl->d_secsize; /* Read header from sector 1. */ 517 len = pread(devfd, secbuf, dl->d_secsize, off); 518 if (len != dl->d_secsize) 519 err(4, "can't pread gpt header"); 520 521 memcpy(&gh, secbuf, sizeof(gh)); 522 free(secbuf); 523 524 /* Check signature */ 525 if (letoh64(gh.gh_sig) != GPTSIGNATURE) 526 return (-1); 527 528 if (letoh32(gh.gh_rev) != GPTREVISION) 529 return (-1); 530 531 ghsize = letoh32(gh.gh_size); 532 if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) 533 return (-1); 534 535 /* Check checksum */ 536 orig_csum = gh.gh_csum; 537 gh.gh_csum = 0; 538 new_csum = crc32((unsigned char *)&gh, ghsize); 539 gh.gh_csum = orig_csum; 540 if (letoh32(orig_csum) != new_csum) 541 return (-1); 542 543 off = letoh64(gh.gh_part_lba) * dl->d_secsize; 544 ghpartsize = letoh32(gh.gh_part_size); 545 ghpartspersec = dl->d_secsize / ghpartsize; 546 ghpartnum = letoh32(gh.gh_part_num); 547 if ((secbuf = malloc(dl->d_secsize)) == NULL) 548 err(1, NULL); 549 for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) { 550 len = pread(devfd, secbuf, dl->d_secsize, off); 551 if (len != dl->d_secsize) { 552 free(secbuf); 553 return (-1); 554 } 555 memcpy(gp + i * ghpartspersec, secbuf, 556 ghpartspersec * sizeof(struct gpt_partition)); 557 off += dl->d_secsize; 558 } 559 free(secbuf); 560 new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize); 561 if (new_csum != letoh32(gh.gh_part_csum)) 562 return (-1); 563 564 start = 0; 565 for (i = 0; i < ghpartnum && start == 0; i++) { 566 if (memcmp(&gp[i].gp_type, &efisys_uuid, 567 sizeof(struct uuid)) == 0) 568 start = letoh64(gp[i].gp_lba_start); 569 } 570 571 if (start) { 572 for (i = 0; i < MAXPARTITIONS; i++) { 573 if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 && 574 DL_GETPOFFSET(&dl->d_partitions[i]) == start) 575 return ('a' + i); 576 } 577 } 578 579 return (-1); 580 } 581 582 /* 583 * Load the prototype boot sector (biosboot) into memory. 584 */ 585 static char * 586 loadproto(char *fname, long *size) 587 { 588 int fd; 589 size_t tdsize; /* text+data size */ 590 char *bp; 591 Elf_Ehdr eh; 592 Elf_Word phsize; 593 Elf_Phdr *ph; 594 595 if ((fd = open(fname, O_RDONLY)) < 0) 596 err(1, "%s", fname); 597 598 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 599 errx(1, "%s: read failed", fname); 600 601 if (!IS_ELF(eh)) 602 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname, 603 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 604 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 605 606 /* 607 * We have to include the exec header in the beginning of 608 * the buffer, and leave extra space at the end in case 609 * the actual write to disk wants to skip the header. 610 */ 611 612 /* Program load header. */ 613 if (eh.e_phnum != 1) 614 errx(1, "%s: %u ELF load sections (only support 1)", 615 fname, eh.e_phnum); 616 617 ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr)); 618 if (ph == NULL) 619 err(1, NULL); 620 phsize = eh.e_phnum * sizeof(Elf_Phdr); 621 622 if (pread(fd, ph, phsize, eh.e_phoff) != phsize) 623 errx(1, "%s: can't pread header", fname); 624 625 tdsize = ph->p_filesz; 626 627 /* 628 * Allocate extra space here because the caller may copy 629 * the boot block starting at the end of the exec header. 630 * This prevents reading beyond the end of the buffer. 631 */ 632 if ((bp = calloc(tdsize, 1)) == NULL) 633 err(1, NULL); 634 635 /* Read the rest of the file. */ 636 if (pread(fd, bp, tdsize, ph->p_offset) != (ssize_t)tdsize) 637 errx(1, "%s: pread failed", fname); 638 639 *size = tdsize; /* not aligned to DEV_BSIZE */ 640 641 close(fd); 642 return bp; 643 } 644 645 static void 646 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) 647 { 648 if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size) 649 err(1, "%s: devread: pread", msg); 650 } 651 652 /* 653 * Read information about /boot's inode, then put this and filesystem 654 * parameters from the superblock into pbr_symbols. 655 */ 656 static int 657 getbootparams(char *boot, int devfd, struct disklabel *dl) 658 { 659 int fd; 660 struct stat dsb, fsb; 661 struct statfs fssb; 662 struct partition *pp; 663 struct fs *fs; 664 char *sblock, *buf; 665 u_int blk, *ap; 666 struct ufs1_dinode *ip; 667 int ndb; 668 int mib[3]; 669 size_t size; 670 dev_t dev; 671 672 /* 673 * Open 2nd-level boot program and record enough details about 674 * where it is on the filesystem represented by `devfd' 675 * (inode block, offset within that block, and various filesystem 676 * parameters essentially taken from the superblock) for biosboot 677 * to be able to load it later. 678 */ 679 680 /* Make sure the (probably new) boot file is on disk. */ 681 sync(); sleep(1); 682 683 if ((fd = open(boot, O_RDONLY)) < 0) 684 err(1, "open: %s", boot); 685 686 if (fstatfs(fd, &fssb) != 0) 687 err(1, "statfs: %s", boot); 688 689 if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) && 690 strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) ) 691 errx(1, "%s: not on an FFS filesystem", boot); 692 693 #if 0 694 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 695 errx(1, "read: %s", boot); 696 697 if (!IS_ELF(eh)) { 698 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", 699 boot, 700 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 701 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 702 } 703 #endif 704 705 if (fsync(fd) != 0) 706 err(1, "fsync: %s", boot); 707 708 if (fstat(fd, &fsb) != 0) 709 err(1, "fstat: %s", boot); 710 711 if (fstat(devfd, &dsb) != 0) 712 err(1, "fstat: %d", devfd); 713 714 /* Check devices. */ 715 mib[0] = CTL_MACHDEP; 716 mib[1] = CPU_CHR2BLK; 717 mib[2] = dsb.st_rdev; 718 size = sizeof(dev); 719 if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) { 720 if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS) 721 errx(1, "cross-device install"); 722 } 723 724 pp = &dl->d_partitions[DISKPART(fsb.st_dev)]; 725 close(fd); 726 727 /* Read superblock. */ 728 if ((sblock = malloc(SBSIZE)) == NULL) 729 err(1, NULL); 730 731 devread(devfd, sblock, DL_SECTOBLK(dl, pp->p_offset) + SBLOCK, 732 SBSIZE, "superblock"); 733 fs = (struct fs *)sblock; 734 735 /* Sanity-check super-block. */ 736 if (fs->fs_magic != FS_MAGIC) 737 errx(1, "Bad magic number in superblock"); 738 if (fs->fs_inopb <= 0) 739 err(1, "Bad inopb=%d in superblock", fs->fs_inopb); 740 741 /* Read inode. */ 742 if ((buf = malloc(fs->fs_bsize)) == NULL) 743 err(1, NULL); 744 745 blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino)); 746 747 devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk, 748 fs->fs_bsize, "inode"); 749 ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, fsb.st_ino); 750 751 /* 752 * Have the inode. Figure out how many filesystem blocks (not disk 753 * sectors) there are for biosboot to load. 754 */ 755 ndb = howmany(ip->di_size, fs->fs_bsize); 756 if (ndb <= 0) 757 errx(1, "No blocks to load"); 758 759 /* 760 * Now set the values that will need to go into biosboot 761 * (the partition boot record, a.k.a. the PBR). 762 */ 763 sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16)); 764 sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 765 dl->d_secsize)); 766 767 /* 768 * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The 769 * ino_to_fsba() return value is the number of fs_fsize units. 770 * Calculate the shift to convert fs_fsize into physical sectors, 771 * which are added to p_offset to get the sector address BIOS 772 * will use. 773 * 774 * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize. 775 */ 776 sym_set_value(pbr_symbols, "_fsbtodb", 777 ffs(fs->fs_fsize / dl->d_secsize) - 1); 778 779 sym_set_value(pbr_symbols, "_p_offset", pp->p_offset); 780 sym_set_value(pbr_symbols, "_inodeblk", 781 ino_to_fsba(fs, fsb.st_ino)); 782 ap = ip->di_db; 783 sym_set_value(pbr_symbols, "_inodedbl", 784 ((((char *)ap) - buf) + INODEOFF)); 785 sym_set_value(pbr_symbols, "_nblocks", ndb); 786 787 if (verbose) { 788 fprintf(stderr, "%s is %d blocks x %d bytes\n", 789 boot, ndb, fs->fs_bsize); 790 fprintf(stderr, "fs block shift %u; part offset %u; " 791 "inode block %lld, offset %u\n", 792 ffs(fs->fs_fsize / dl->d_secsize) - 1, 793 pp->p_offset, 794 ino_to_fsba(fs, fsb.st_ino), 795 (unsigned int)((((char *)ap) - buf) + INODEOFF)); 796 } 797 798 free (sblock); 799 free (buf); 800 801 return 0; 802 } 803 804 void 805 sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value) 806 { 807 struct sym_data *p; 808 809 for (p = sym_list; p->sym_name != NULL; p++) { 810 if (strcmp(p->sym_name, sym) == 0) 811 break; 812 } 813 814 if (p->sym_name == NULL) 815 errx(1, "%s: no such symbol", sym); 816 817 p->sym_value = value; 818 p->sym_set = 1; 819 } 820 821 /* 822 * Write the parameters stored in sym_list into the in-memory copy of 823 * the prototype biosboot (proto), ready for it to be written to disk. 824 */ 825 void 826 pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list) 827 { 828 struct sym_data *sym; 829 struct nlist *nl; 830 char *vp; 831 u_int32_t *lp; 832 u_int16_t *wp; 833 u_int8_t *bp; 834 835 for (sym = sym_list; sym->sym_name != NULL; sym++) { 836 if (!sym->sym_set) 837 errx(1, "%s not set", sym->sym_name); 838 839 /* Allocate space for 2; second is null-terminator for list. */ 840 nl = calloc(2, sizeof(struct nlist)); 841 if (nl == NULL) 842 err(1, NULL); 843 844 nl->n_name = sym->sym_name; 845 846 if (nlist_elf32(fname, nl) != 0) 847 errx(1, "%s: symbol %s not found", 848 fname, sym->sym_name); 849 850 if (nl->n_type != (N_TEXT)) 851 errx(1, "%s: %s: wrong type (%x)", 852 fname, sym->sym_name, nl->n_type); 853 854 /* Get a pointer to where the symbol's value needs to go. */ 855 vp = proto + nl->n_value; 856 857 switch (sym->sym_size) { 858 case 4: /* u_int32_t */ 859 lp = (u_int32_t *) vp; 860 *lp = sym->sym_value; 861 break; 862 case 2: /* u_int16_t */ 863 if (sym->sym_value >= 0x10000) /* out of range */ 864 errx(1, "%s: symbol out of range (%u)", 865 sym->sym_name, sym->sym_value); 866 wp = (u_int16_t *) vp; 867 *wp = (u_int16_t) sym->sym_value; 868 break; 869 case 1: /* u_int16_t */ 870 if (sym->sym_value >= 0x100) /* out of range */ 871 errx(1, "%s: symbol out of range (%u)", 872 sym->sym_name, sym->sym_value); 873 bp = (u_int8_t *) vp; 874 *bp = (u_int8_t) sym->sym_value; 875 break; 876 default: 877 errx(1, "%s: bad symbol size %d", 878 sym->sym_name, sym->sym_size); 879 /* NOTREACHED */ 880 } 881 882 free(nl); 883 } 884 } 885