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