1 /* $OpenBSD: i386_installboot.c,v 1.6 2014/10/08 04:26:54 deraadt 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> 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_abi.h> 58 #include <err.h> 59 #include <fcntl.h> 60 #include <nlist.h> 61 #include <stdlib.h> 62 #include <stdio.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include <util.h> 66 67 #include "installboot.h" 68 #include "i386_installboot.h" 69 70 char *bootldr; 71 72 char *blkstore; 73 size_t blksize; 74 75 struct sym_data pbr_symbols[] = { 76 {"_fs_bsize_p", 2}, 77 {"_fs_bsize_s", 2}, 78 {"_fsbtodb", 1}, 79 {"_p_offset", 4}, 80 {"_inodeblk", 4}, 81 {"_inodedbl", 4}, 82 {"_nblocks", 2}, 83 {NULL} 84 }; 85 86 static void devread(int, void *, daddr_t, size_t, char *); 87 static u_int findopenbsd(int, struct disklabel *); 88 static int getbootparams(char *, int, struct disklabel *); 89 static char *loadproto(char *, long *); 90 91 /* 92 * Read information about /boot's inode and filesystem parameters, then 93 * put biosboot (partition boot record) on the target drive with these 94 * parameters patched in. 95 */ 96 97 void 98 md_init(void) 99 { 100 stages = 2; 101 stage1 = "/usr/mdec/biosboot"; 102 stage2 = "/usr/mdec/boot"; 103 104 bootldr = "/boot"; 105 } 106 107 void 108 md_loadboot(void) 109 { 110 /* Load prototype boot blocks. */ 111 if ((blkstore = loadproto(stage1, &blksize)) == NULL) 112 exit(1); 113 114 /* XXX - Paranoia: Make sure size is aligned! */ 115 if (blksize & (DEV_BSIZE - 1)) 116 errx(1, "proto %s bad size=%ld", stage1, blksize); 117 118 if (blksize > SBSIZE - DEV_BSIZE) 119 errx(1, "proto bootblocks too big"); 120 } 121 122 void 123 md_installboot(int devfd, char *dev) 124 { 125 struct disklabel dl; 126 127 /* Get and check disklabel. */ 128 if (ioctl(devfd, DIOCGDINFO, &dl) != 0) 129 err(1, "disklabel: %s", dev); 130 if (dl.d_magic != DISKMAGIC) 131 errx(1, "bad disklabel magic=0x%08x", dl.d_magic); 132 133 /* Warn on unknown disklabel types. */ 134 if (dl.d_type == 0) 135 warnx("disklabel type unknown"); 136 137 bootldr = fileprefix(root, bootldr); 138 if (verbose) 139 fprintf(stderr, "%s %s to %s\n", 140 (nowrite ? "would copy" : "copying"), stage2, bootldr); 141 if (!nowrite) 142 filecopy(stage2, bootldr); 143 144 /* Get bootstrap parameters to patch into proto. */ 145 if (getbootparams(bootldr, devfd, &dl) != 0) 146 exit(1); 147 148 /* Write boot blocks to device. */ 149 write_bootblocks(devfd, dev, &dl); 150 } 151 152 void 153 write_bootblocks(int devfd, char *dev, struct disklabel *dl) 154 { 155 struct stat sb; 156 u_int8_t *secbuf; 157 u_int start = 0; 158 159 /* Write patched proto bootblock(s) into the superblock. */ 160 if (fstat(devfd, &sb) < 0) 161 err(1, "fstat: %s", dev); 162 163 if (!S_ISCHR(sb.st_mode)) 164 errx(1, "%s: not a character device", dev); 165 166 /* Patch the parameters into the proto bootstrap sector. */ 167 pbr_set_symbols(stage1, blkstore, pbr_symbols); 168 169 if (!nowrite) { 170 /* Sync filesystems (to clean in-memory superblock?). */ 171 sync(); sleep(1); 172 } 173 174 /* 175 * Find OpenBSD partition. Floppies are special, getting an 176 * everything-in-one /boot starting at sector 0. 177 */ 178 if (dl->d_type != DTYPE_FLOPPY) { 179 start = findopenbsd(devfd, dl); 180 if (start == (u_int)-1) 181 errx(1, "no OpenBSD partition"); 182 } 183 184 if (verbose) 185 fprintf(stderr, "%s will be written at sector %u\n", 186 stage1, start); 187 188 if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC) 189 warnx("%s extends beyond sector %u. OpenBSD might not boot.", 190 stage1, BOOTBIOS_MAXSEC); 191 192 if (!nowrite) { 193 if (lseek(devfd, (off_t)start * dl->d_secsize, SEEK_SET) < 0) 194 err(1, "seek boot sector"); 195 secbuf = calloc(1, dl->d_secsize); 196 if (read(devfd, secbuf, dl->d_secsize) != dl->d_secsize) 197 err(1, "read boot sector"); 198 bcopy(blkstore, secbuf, blksize); 199 if (lseek(devfd, (off_t)start * dl->d_secsize, SEEK_SET) < 0) 200 err(1, "seek bootstrap"); 201 if (write(devfd, secbuf, dl->d_secsize) != dl->d_secsize) 202 err(1, "write bootstrap"); 203 free(secbuf); 204 } 205 } 206 207 u_int 208 findopenbsd(int devfd, struct disklabel *dl) 209 { 210 struct dos_mbr mbr; 211 u_int mbroff = DOSBBSECTOR; 212 u_int mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */ 213 struct dos_partition *dp; 214 u_int8_t *secbuf; 215 u_int maxebr = DOS_MAXEBR, nextebr; 216 int i; 217 218 again: 219 if (!maxebr--) { 220 if (verbose) 221 fprintf(stderr, "Traversed more than %d Extended Boot " 222 "Records (EBRs)\n", DOS_MAXEBR); 223 return ((u_int)-1); 224 } 225 226 if (verbose) 227 fprintf(stderr, "%s boot record (%cBR) at sector %u\n", 228 (mbroff == DOSBBSECTOR) ? "master" : "extended", 229 (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff); 230 231 if ((secbuf = malloc(dl->d_secsize)) == NULL) 232 err(1, NULL); 233 if (lseek(devfd, (off_t)mbroff * dl->d_secsize, SEEK_SET) < 0 || 234 read(devfd, secbuf, dl->d_secsize) < (ssize_t)sizeof(mbr)) 235 err(4, "can't read boot record"); 236 bcopy(secbuf, &mbr, sizeof(mbr)); 237 free(secbuf); 238 239 if (mbr.dmbr_sign != DOSMBR_SIGNATURE) 240 errx(1, "invalid boot record signature (0x%04X) @ sector %u", 241 mbr.dmbr_sign, mbroff); 242 243 nextebr = 0; 244 for (i = 0; i < NDOSPART; i++) { 245 dp = &mbr.dmbr_parts[i]; 246 if (!dp->dp_size) 247 continue; 248 249 if (verbose) 250 fprintf(stderr, 251 "\tpartition %d: type 0x%02X offset %u size %u\n", 252 i, dp->dp_typ, dp->dp_start, dp->dp_size); 253 254 if (dp->dp_typ == DOSPTYP_OPENBSD) { 255 if (dp->dp_start > (dp->dp_start + mbroff)) 256 continue; 257 return (dp->dp_start + mbroff); 258 } 259 260 if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || 261 dp->dp_typ == DOSPTYP_EXTENDL)) { 262 nextebr = dp->dp_start + mbr_eoff; 263 if (nextebr < dp->dp_start) 264 nextebr = (u_int)-1; 265 if (mbr_eoff == DOSBBSECTOR) 266 mbr_eoff = dp->dp_start; 267 } 268 } 269 270 if (nextebr && nextebr != (u_int)-1) { 271 mbroff = nextebr; 272 goto again; 273 } 274 275 return ((u_int)-1); 276 } 277 278 /* 279 * Load the prototype boot sector (biosboot) into memory. 280 */ 281 static char * 282 loadproto(char *fname, long *size) 283 { 284 int fd; 285 size_t tdsize; /* text+data size */ 286 char *bp; 287 Elf_Ehdr eh; 288 Elf_Word phsize; 289 Elf_Phdr *ph; 290 291 if ((fd = open(fname, O_RDONLY)) < 0) 292 err(1, "%s", fname); 293 294 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 295 errx(1, "%s: read failed", fname); 296 297 if (!IS_ELF(eh)) 298 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname, 299 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 300 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 301 302 /* 303 * We have to include the exec header in the beginning of 304 * the buffer, and leave extra space at the end in case 305 * the actual write to disk wants to skip the header. 306 */ 307 308 /* Program load header. */ 309 if (eh.e_phnum != 1) 310 errx(1, "%s: %u ELF load sections (only support 1)", 311 fname, eh.e_phnum); 312 313 ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr)); 314 if (ph == NULL) 315 err(1, NULL); 316 phsize = eh.e_phnum * sizeof(Elf_Phdr); 317 lseek(fd, eh.e_phoff, SEEK_SET); 318 319 if (read(fd, ph, phsize) != phsize) 320 errx(1, "%s: can't read header", fname); 321 322 tdsize = ph->p_filesz; 323 324 /* 325 * Allocate extra space here because the caller may copy 326 * the boot block starting at the end of the exec header. 327 * This prevents reading beyond the end of the buffer. 328 */ 329 if ((bp = calloc(tdsize, 1)) == NULL) 330 err(1, NULL); 331 332 /* Read the rest of the file. */ 333 lseek(fd, ph->p_offset, SEEK_SET); 334 if (read(fd, bp, tdsize) != (ssize_t)tdsize) 335 errx(1, "%s: read failed", fname); 336 337 *size = tdsize; /* not aligned to DEV_BSIZE */ 338 339 close(fd); 340 return bp; 341 } 342 343 static void 344 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg) 345 { 346 if (lseek(fd, dbtob((off_t)blk), SEEK_SET) != dbtob((off_t)blk)) 347 err(1, "%s: devread: lseek", msg); 348 349 if (read(fd, buf, size) != (ssize_t)size) 350 err(1, "%s: devread: read", msg); 351 } 352 353 static char sblock[SBSIZE]; 354 355 /* 356 * Read information about /boot's inode, then put this and filesystem 357 * parameters from the superblock into pbr_symbols. 358 */ 359 static int 360 getbootparams(char *boot, int devfd, struct disklabel *dl) 361 { 362 int fd; 363 struct stat dsb, fsb; 364 struct statfs fssb; 365 struct partition *pp; 366 struct fs *fs; 367 char *buf; 368 u_int blk, *ap; 369 struct ufs1_dinode *ip; 370 int ndb; 371 int mib[3]; 372 size_t size; 373 dev_t dev; 374 375 /* 376 * Open 2nd-level boot program and record enough details about 377 * where it is on the filesystem represented by `devfd' 378 * (inode block, offset within that block, and various filesystem 379 * parameters essentially taken from the superblock) for biosboot 380 * to be able to load it later. 381 */ 382 383 /* Make sure the (probably new) boot file is on disk. */ 384 sync(); sleep(1); 385 386 if ((fd = open(boot, O_RDONLY)) < 0) 387 err(1, "open: %s", boot); 388 389 if (fstatfs(fd, &fssb) != 0) 390 err(1, "statfs: %s", boot); 391 392 if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) && 393 strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) ) 394 errx(1, "%s: not on an FFS filesystem", boot); 395 396 #if 0 397 if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) 398 errx(1, "read: %s", boot); 399 400 if (!IS_ELF(eh)) { 401 errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", 402 boot, 403 eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1], 404 eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]); 405 } 406 #endif 407 408 if (fsync(fd) != 0) 409 err(1, "fsync: %s", boot); 410 411 if (fstat(fd, &fsb) != 0) 412 err(1, "fstat: %s", boot); 413 414 if (fstat(devfd, &dsb) != 0) 415 err(1, "fstat: %d", devfd); 416 417 /* Check devices. */ 418 mib[0] = CTL_MACHDEP; 419 mib[1] = CPU_CHR2BLK; 420 mib[2] = dsb.st_rdev; 421 size = sizeof(dev); 422 if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) { 423 if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS) 424 errx(1, "cross-device install"); 425 } 426 427 pp = &dl->d_partitions[DISKPART(fsb.st_dev)]; 428 close(fd); 429 430 /* Read superblock. */ 431 devread(devfd, sblock, DL_SECTOBLK(dl, pp->p_offset) + SBLOCK, 432 SBSIZE, "superblock"); 433 fs = (struct fs *)sblock; 434 435 /* Sanity-check super-block. */ 436 if (fs->fs_magic != FS_MAGIC) 437 errx(1, "Bad magic number in superblock"); 438 if (fs->fs_inopb <= 0) 439 err(1, "Bad inopb=%d in superblock", fs->fs_inopb); 440 441 /* Read inode. */ 442 if ((buf = malloc(fs->fs_bsize)) == NULL) 443 err(1, NULL); 444 445 blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino)); 446 447 devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk, 448 fs->fs_bsize, "inode"); 449 ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, fsb.st_ino); 450 451 /* 452 * Have the inode. Figure out how many filesystem blocks (not disk 453 * sectors) there are for biosboot to load. 454 */ 455 ndb = howmany(ip->di_size, fs->fs_bsize); 456 if (ndb <= 0) 457 errx(1, "No blocks to load"); 458 459 /* 460 * Now set the values that will need to go into biosboot 461 * (the partition boot record, a.k.a. the PBR). 462 */ 463 sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16)); 464 sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize / 465 dl->d_secsize)); 466 467 /* 468 * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The 469 * ino_to_fsba() return value is the number of fs_fsize units. 470 * Calculate the shift to convert fs_fsize into physical sectors, 471 * which are added to p_offset to get the sector address BIOS 472 * will use. 473 * 474 * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize. 475 */ 476 sym_set_value(pbr_symbols, "_fsbtodb", 477 ffs(fs->fs_fsize / dl->d_secsize) - 1); 478 479 sym_set_value(pbr_symbols, "_p_offset", pp->p_offset); 480 sym_set_value(pbr_symbols, "_inodeblk", 481 ino_to_fsba(fs, fsb.st_ino)); 482 ap = ip->di_db; 483 sym_set_value(pbr_symbols, "_inodedbl", 484 ((((char *)ap) - buf) + INODEOFF)); 485 sym_set_value(pbr_symbols, "_nblocks", ndb); 486 487 if (verbose) { 488 fprintf(stderr, "%s is %d blocks x %d bytes\n", 489 boot, ndb, fs->fs_bsize); 490 fprintf(stderr, "fs block shift %u; part offset %u; " 491 "inode block %lld, offset %u\n", 492 ffs(fs->fs_fsize / dl->d_secsize) - 1, 493 pp->p_offset, 494 ino_to_fsba(fs, fsb.st_ino), 495 (unsigned int)((((char *)ap) - buf) + INODEOFF)); 496 } 497 498 return 0; 499 } 500 501 void 502 sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value) 503 { 504 struct sym_data *p; 505 506 for (p = sym_list; p->sym_name != NULL; p++) { 507 if (strcmp(p->sym_name, sym) == 0) 508 break; 509 } 510 511 if (p->sym_name == NULL) 512 errx(1, "%s: no such symbol", sym); 513 514 p->sym_value = value; 515 p->sym_set = 1; 516 } 517 518 /* 519 * Write the parameters stored in sym_list into the in-memory copy of 520 * the prototype biosboot (proto), ready for it to be written to disk. 521 */ 522 void 523 pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list) 524 { 525 struct sym_data *sym; 526 struct nlist *nl; 527 char *vp; 528 u_int32_t *lp; 529 u_int16_t *wp; 530 u_int8_t *bp; 531 532 for (sym = sym_list; sym->sym_name != NULL; sym++) { 533 if (!sym->sym_set) 534 errx(1, "%s not set", sym->sym_name); 535 536 /* Allocate space for 2; second is null-terminator for list. */ 537 nl = calloc(2, sizeof(struct nlist)); 538 if (nl == NULL) 539 err(1, NULL); 540 541 nl->n_name = sym->sym_name; 542 543 if (nlist_elf32(fname, nl) != 0) 544 errx(1, "%s: symbol %s not found", 545 fname, sym->sym_name); 546 547 if (nl->n_type != (N_TEXT)) 548 errx(1, "%s: %s: wrong type (%x)", 549 fname, sym->sym_name, nl->n_type); 550 551 /* Get a pointer to where the symbol's value needs to go. */ 552 vp = proto + nl->n_value; 553 554 switch (sym->sym_size) { 555 case 4: /* u_int32_t */ 556 lp = (u_int32_t *) vp; 557 *lp = sym->sym_value; 558 break; 559 case 2: /* u_int16_t */ 560 if (sym->sym_value >= 0x10000) /* out of range */ 561 errx(1, "%s: symbol out of range (%u)", 562 sym->sym_name, sym->sym_value); 563 wp = (u_int16_t *) vp; 564 *wp = (u_int16_t) sym->sym_value; 565 break; 566 case 1: /* u_int16_t */ 567 if (sym->sym_value >= 0x100) /* out of range */ 568 errx(1, "%s: symbol out of range (%u)", 569 sym->sym_name, sym->sym_value); 570 bp = (u_int8_t *) vp; 571 *bp = (u_int8_t) sym->sym_value; 572 break; 573 default: 574 errx(1, "%s: bad symbol size %d", 575 sym->sym_name, sym->sym_size); 576 /* NOTREACHED */ 577 } 578 579 free(nl); 580 } 581 } 582