1 /* $OpenBSD: i386_installboot.c,v 1.46 2023/06/11 14:00:04 krw Exp $ */
2 /* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
3
4 /*
5 * Copyright (c) 2013 Pedro Martelletto
6 * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
7 * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
8 * Copyright (c) 1997 Michael Shalayeff
9 * Copyright (c) 1994 Paul Kranenburg
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Paul Kranenburg.
23 * 4. The name of the author may not be used to endorse or promote products
24 * derived from this software without specific prior written permission
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #define ELFSIZE 32
39
40 #include <sys/param.h> /* DEV_BSIZE */
41 #include <sys/disklabel.h>
42 #include <sys/dkio.h>
43 #include <sys/ioctl.h>
44 #include <sys/mount.h>
45 #include <sys/reboot.h>
46 #include <sys/stat.h>
47 #include <sys/sysctl.h>
48 #include <sys/time.h>
49
50 #include <ufs/ufs/dinode.h>
51 #include <ufs/ufs/dir.h>
52 #include <ufs/ffs/fs.h>
53
54 #include <machine/cpu.h>
55 #include <machine/biosvar.h>
56
57 #include <elf.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <nlist.h>
62 #include <stdlib.h>
63 #include <stdio.h>
64 #include <stdint.h>
65 #include <string.h>
66 #include <unistd.h>
67 #include <util.h>
68 #include <uuid.h>
69
70 #include "installboot.h"
71 #include "i386_installboot.h"
72
73 char *bootldr;
74
75 char *blkstore;
76 size_t blksize;
77
78 struct sym_data pbr_symbols[] = {
79 {"_fs_bsize_p", 2},
80 {"_fs_bsize_s", 2},
81 {"_fsbtodb", 1},
82 {"_p_offset", 4},
83 {"_inodeblk", 4},
84 {"_inodedbl", 4},
85 {"_nblocks", 2},
86 {"_blkincr", 1},
87 {NULL}
88 };
89
90 static void devread(int, void *, daddr_t, size_t, char *);
91 static u_int findopenbsd(int, struct disklabel *);
92 static int getbootparams(char *, int, struct disklabel *);
93 static char *loadproto(char *, long *);
94 static int gpt_chk_mbr(struct dos_partition *, u_int64_t);
95 static int sbchk(struct fs *, daddr_t);
96 static void sbread(int, daddr_t, struct fs **, char *);
97
98 static const daddr_t sbtry[] = SBLOCKSEARCH;
99
100 /*
101 * Read information about /boot's inode and filesystem parameters, then
102 * put biosboot (partition boot record) on the target drive with these
103 * parameters patched in.
104 */
105
106 void
md_init(void)107 md_init(void)
108 {
109 stages = 2;
110 stage1 = "/usr/mdec/biosboot";
111 stage2 = "/usr/mdec/boot";
112
113 bootldr = "/boot";
114 }
115
116 void
md_loadboot(void)117 md_loadboot(void)
118 {
119 /* Load prototype boot blocks. */
120 if ((blkstore = loadproto(stage1, &blksize)) == NULL)
121 exit(1);
122
123 /* XXX - Paranoia: Make sure size is aligned! */
124 if (blksize & (DEV_BSIZE - 1))
125 errx(1, "proto %s bad size=%ld", stage1, blksize);
126
127 if (blksize > SBSIZE - DEV_BSIZE)
128 errx(1, "proto bootblocks too big");
129 }
130
131 void
md_prepareboot(int devfd,char * dev)132 md_prepareboot(int devfd, char *dev)
133 {
134 struct disklabel dl;
135 int part;
136
137 /* Get and check disklabel. */
138 if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
139 err(1, "disklabel: %s", dev);
140 if (dl.d_magic != DISKMAGIC)
141 errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
142
143 /* Warn on unknown disklabel types. */
144 if (dl.d_type == 0)
145 warnx("disklabel type unknown");
146
147 part = findgptefisys(devfd, &dl);
148 if (part != -1) {
149 create_filesystem(&dl, (char)part);
150 return;
151 }
152 }
153
154 void
md_installboot(int devfd,char * dev)155 md_installboot(int devfd, char *dev)
156 {
157 struct disklabel dl;
158 int part;
159
160 /* Get and check disklabel. */
161 if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
162 err(1, "disklabel: %s", dev);
163 if (dl.d_magic != DISKMAGIC)
164 errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
165
166 /* Warn on unknown disklabel types. */
167 if (dl.d_type == 0)
168 warnx("disklabel type unknown");
169
170 part = findgptefisys(devfd, &dl);
171 if (part != -1) {
172 write_filesystem(&dl, (char)part);
173 return;
174 }
175
176 bootldr = fileprefix(root, bootldr);
177 if (bootldr == NULL)
178 exit(1);
179 if (verbose)
180 fprintf(stderr, "%s %s to %s\n",
181 (nowrite ? "would copy" : "copying"), stage2, bootldr);
182 if (!nowrite)
183 if (filecopy(stage2, bootldr) == -1)
184 exit(1);
185
186 /* Get bootstrap parameters to patch into proto. */
187 if (getbootparams(bootldr, devfd, &dl) != 0)
188 exit(1);
189
190 /* Write boot blocks to device. */
191 write_bootblocks(devfd, dev, &dl);
192 }
193
194 void
write_bootblocks(int devfd,char * dev,struct disklabel * dl)195 write_bootblocks(int devfd, char *dev, struct disklabel *dl)
196 {
197 struct stat sb;
198 u_int8_t *secbuf;
199 u_int start;
200
201 /* Write patched proto bootblock(s) into the superblock. */
202 if (fstat(devfd, &sb) == -1)
203 err(1, "fstat: %s", dev);
204
205 if (!S_ISCHR(sb.st_mode))
206 errx(1, "%s: not a character device", dev);
207
208 /* Patch the parameters into the proto bootstrap sector. */
209 pbr_set_symbols(stage1, blkstore, pbr_symbols);
210
211 if (!nowrite) {
212 /* Sync filesystems (to clean in-memory superblock?). */
213 sync(); sleep(1);
214 }
215
216 /*
217 * Find bootstrap sector.
218 */
219 start = findopenbsd(devfd, dl);
220 if (verbose) {
221 if (start == 0)
222 fprintf(stderr, "no MBR, ");
223 fprintf(stderr, "%s will be written at sector %u\n",
224 stage1, start);
225 }
226
227 if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC)
228 warnx("%s extends beyond sector %u. OpenBSD might not boot.",
229 stage1, BOOTBIOS_MAXSEC);
230
231 if (!nowrite) {
232 secbuf = calloc(1, dl->d_secsize);
233 if (pread(devfd, secbuf, dl->d_secsize, (off_t)start *
234 dl->d_secsize) != dl->d_secsize)
235 err(1, "pread boot sector");
236 bcopy(blkstore, secbuf, blksize);
237 if (pwrite(devfd, secbuf, dl->d_secsize, (off_t)start *
238 dl->d_secsize) != dl->d_secsize)
239 err(1, "pwrite bootstrap");
240 free(secbuf);
241 }
242 }
243
244 int
create_filesystem(struct disklabel * dl,char part)245 create_filesystem(struct disklabel *dl, char part)
246 {
247 static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null";
248 struct msdosfs_args args;
249 char cmd[60];
250 int rslt;
251
252 /* Newfs <duid>.<part> as msdos filesystem. */
253 memset(&args, 0, sizeof(args));
254 rslt = asprintf(&args.fspec,
255 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
256 dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
257 dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
258 part);
259 if (rslt == -1) {
260 warn("bad special device");
261 return rslt;
262 }
263
264 rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
265 if (rslt >= sizeof(cmd)) {
266 warnx("can't build newfs command");
267 free(args.fspec);
268 rslt = -1;
269 return rslt;
270 }
271
272 if (verbose)
273 fprintf(stderr, "%s %s\n",
274 (nowrite ? "would newfs" : "newfsing"), args.fspec);
275 if (!nowrite) {
276 rslt = system(cmd);
277 if (rslt == -1) {
278 warn("system('%s') failed", cmd);
279 free(args.fspec);
280 return rslt;
281 }
282 }
283
284 free(args.fspec);
285 return 0;
286 }
287
288 void
write_filesystem(struct disklabel * dl,char part)289 write_filesystem(struct disklabel *dl, char part)
290 {
291 static const char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null";
292 struct msdosfs_args args;
293 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
420 umount:
421 dst[mntlen] = '\0';
422 if (unmount(dst, MNT_FORCE) == -1)
423 err(1, "unmount('%s') failed", dst);
424
425 rmdir:
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 */
443 u_int
findopenbsd(int devfd,struct disklabel * dl)444 findopenbsd(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
454 again:
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 */
526 static int
gpt_chk_mbr(struct dos_partition * dp,u_int64_t dsize)527 gpt_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
552 int
findgptefisys(int devfd,struct disklabel * dl)553 findgptefisys(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 */
654 static char *
loadproto(char * fname,long * size)655 loadproto(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
714 static void
devread(int fd,void * buf,daddr_t blk,size_t size,char * msg)715 devread(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 */
725 static int
getbootparams(char * boot,int devfd,struct disklabel * dl)726 getbootparams(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
877 void
sym_set_value(struct sym_data * sym_list,char * sym,u_int32_t value)878 sym_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 */
898 void
pbr_set_symbols(char * fname,char * proto,struct sym_data * sym_list)899 pbr_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
959 static int
sbchk(struct fs * fs,daddr_t sbloc)960 sbchk(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
1012 static void
sbread(int fd,daddr_t poffset,struct fs ** fs,char * sblock)1013 sbread(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