xref: /openbsd-src/usr.sbin/installboot/i386_installboot.c (revision db15c72a1929803b548b8f444dbcdc667e3d1ad2)
1 /*	$OpenBSD: i386_installboot.c,v 1.48 2024/11/08 10:43:07 kettenis 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
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
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
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
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
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
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
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 #ifdef __amd64__
294 	struct statfs sf;
295 #endif
296 	char cmd[60];
297 	char dst[PATH_MAX];
298 	char *src;
299 	size_t mntlen, pathlen, srclen;
300 	int rslt;
301 
302 	src = NULL;
303 
304 	/* Create directory for temporary mount point. */
305 	strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
306 	if (mkdtemp(dst) == NULL)
307 		err(1, "mkdtemp('%s') failed", dst);
308 	mntlen = strlen(dst);
309 
310 	/* Mount <duid>.<part> as msdos filesystem. */
311 	memset(&args, 0, sizeof(args));
312 	rslt = asprintf(&args.fspec,
313 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
314             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
315             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
316 	    part);
317 	if (rslt == -1) {
318 		warn("bad special device");
319 		goto rmdir;
320 	}
321 
322 	args.export_info.ex_root = -2;	/* unchecked anyway on DOS fs */
323 	args.export_info.ex_flags = 0;
324 	args.flags = MSDOSFSMNT_LONGNAME;
325 
326 	if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
327 		/* Try fsck'ing it. */
328 		rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
329 		if (rslt >= sizeof(cmd)) {
330 			warnx("can't build fsck command");
331 			rslt = -1;
332 			goto rmdir;
333 		}
334 		rslt = system(cmd);
335 		if (rslt == -1) {
336 			warn("system('%s') failed", cmd);
337 			goto rmdir;
338 		}
339 		if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
340 			/* Try newfs'ing it. */
341 			rslt = create_filesystem(dl, part);
342 			if (rslt == -1)
343 				goto rmdir;
344 			rslt = mount(MOUNT_MSDOS, dst, 0, &args);
345 			if (rslt == -1) {
346 				warn("unable to mount EFI System partition");
347 				goto rmdir;
348 			}
349 		}
350 	}
351 
352 	/* Create "/efi/BOOT" directory in <duid>.<part>. */
353 	if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) {
354 		rslt = -1;
355 		warn("unable to build /efi directory");
356 		goto umount;
357 	}
358 	rslt = mkdir(dst, 0);
359 	if (rslt == -1 && errno != EEXIST) {
360 		warn("mkdir('%s') failed", dst);
361 		goto umount;
362 	}
363 	if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) {
364 		rslt = -1;
365 		warn("unable to build /BOOT directory");
366 		goto umount;
367 	}
368 	rslt = mkdir(dst, 0);
369 	if (rslt == -1 && errno != EEXIST) {
370 		warn("mkdir('%s') failed", dst);
371 		goto umount;
372 	}
373 
374 	/*
375 	 * Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/BOOT/.
376 	 *
377 	 * N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused!
378 	 */
379 	pathlen = strlen(dst);
380 	if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) {
381 		rslt = -1;
382 		warn("unable to build /BOOTIA32.EFI path");
383 		goto umount;
384 	}
385 	src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI");
386 	if (src == NULL) {
387 		rslt = -1;
388 		goto umount;
389 	}
390 	srclen = strlen(src);
391 	if (verbose)
392 		fprintf(stderr, "%s %s to %s\n",
393 		    (nowrite ? "would copy" : "copying"), src, dst);
394 	if (!nowrite) {
395 		rslt = filecopy(src, dst);
396 		if (rslt == -1)
397 			goto umount;
398 	}
399 	src[srclen - strlen("/BOOTIA32.EFI")] = '\0';
400 
401 	dst[pathlen] = '\0';
402 	if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) {
403 		rslt = -1;
404 		warn("unable to build /BOOTX64.EFI dst path");
405 		goto umount;
406 	}
407 	if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) {
408 		rslt = -1;
409 		warn("unable to build /BOOTX64.EFI src path");
410 		goto umount;
411 	}
412 	if (verbose)
413 		fprintf(stderr, "%s %s to %s\n",
414 		    (nowrite ? "would copy" : "copying"), src, dst);
415 	if (!nowrite) {
416 		rslt = filecopy(src, dst);
417 		if (rslt == -1)
418 			goto umount;
419 	}
420 
421 #ifdef __amd64__
422 	/* Skip installing a 2nd copy if we have a small filesystem. */
423 	if (statfs(dst, &sf) || sf.f_blocks < 2048) {
424 		rslt = 0;
425 		goto umount;
426 	}
427 
428 	/* Create "/efi/openbsd" directory in <duid>.<part>. */
429 	dst[mntlen] = '\0';
430 	if (strlcat(dst, "/efi/openbsd", sizeof(dst)) >= sizeof(dst)) {
431 		rslt = -1;
432 		warn("unable to build /efi/openbsd directory");
433 		goto umount;
434 	}
435 	rslt = mkdir(dst, 0755);
436 	if (rslt == -1 && errno != EEXIST) {
437 		warn("mkdir('%s') failed", dst);
438 		goto umount;
439 	}
440 
441 	/* Copy BOOTX64.EFI to /efi/openbsd/. */
442 	if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) {
443 		rslt = -1;
444 		warn("unable to build /BOOTX64.EFI path");
445 		goto umount;
446 	}
447 	src = fileprefix(root, "/usr/mdec/BOOTX64.EFI");
448 	if (src == NULL) {
449 		rslt = -1;
450 		goto umount;
451 	}
452 	srclen = strlen(src);
453 	if (verbose)
454 		fprintf(stderr, "%s %s to %s\n",
455 		    (nowrite ? "would copy" : "copying"), src, dst);
456 	if (!nowrite) {
457 		rslt = filecopy(src, dst);
458 		if (rslt == -1)
459 			goto umount;
460 	}
461 #endif
462 
463 	rslt = 0;
464 
465 umount:
466 	dst[mntlen] = '\0';
467 	if (unmount(dst, MNT_FORCE) == -1)
468 		err(1, "unmount('%s') failed", dst);
469 
470 rmdir:
471 	free(args.fspec);
472 	dst[mntlen] = '\0';
473 	if (rmdir(dst) == -1)
474 		err(1, "rmdir('%s') failed", dst);
475 
476 	free(src);
477 
478 	if (rslt == -1)
479 		exit(1);
480 }
481 
482 /*
483  * a) For media w/o an MBR use sector 0.
484  * b) For media with an MBR and an OpenBSD (A6) partition use the first
485  *    sector of the OpenBSD partition.
486  * c) For media with an MBR and no OpenBSD partition error out.
487  */
488 u_int
489 findopenbsd(int devfd, struct disklabel *dl)
490 {
491 	struct		dos_mbr mbr;
492 	u_int		mbroff = DOSBBSECTOR;
493 	u_int		mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */
494 	struct		dos_partition *dp;
495 	u_int8_t	*secbuf;
496 	u_int		maxebr = DOS_MAXEBR, nextebr;
497 	int		i;
498 
499 again:
500 	if (!maxebr--) {
501 		if (verbose)
502 			fprintf(stderr, "Traversed more than %d Extended Boot "
503 			    "Records (EBRs)\n", DOS_MAXEBR);
504 		goto done;
505 	}
506 
507 	if (verbose)
508 		fprintf(stderr, "%s boot record (%cBR) at sector %u\n",
509 		    (mbroff == DOSBBSECTOR) ? "master" : "extended",
510 		    (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff);
511 
512 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
513 		err(1, NULL);
514 	if (pread(devfd, secbuf, dl->d_secsize, (off_t)mbroff * dl->d_secsize)
515 	    < (ssize_t)sizeof(mbr))
516 		err(4, "can't pread boot record");
517 	bcopy(secbuf, &mbr, sizeof(mbr));
518 	free(secbuf);
519 
520 	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
521 		if (mbroff == DOSBBSECTOR)
522 			return 0;
523 		errx(1, "invalid boot record signature (0x%04X) @ sector %u",
524 		    mbr.dmbr_sign, mbroff);
525 	}
526 
527 	nextebr = 0;
528 	for (i = 0; i < NDOSPART; i++) {
529 		dp = &mbr.dmbr_parts[i];
530 		if (!dp->dp_size)
531 			continue;
532 
533 		if (verbose)
534 			fprintf(stderr,
535 			    "\tpartition %d: type 0x%02X offset %u size %u\n",
536 			    i, dp->dp_typ, dp->dp_start, dp->dp_size);
537 
538 		if (dp->dp_typ == DOSPTYP_OPENBSD) {
539 			if (dp->dp_start > (dp->dp_start + mbroff))
540 				continue;
541 			return (dp->dp_start + mbroff);
542 		}
543 
544 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
545 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
546 			nextebr = dp->dp_start + mbr_eoff;
547 			if (nextebr < dp->dp_start)
548 				nextebr = (u_int)-1;
549 			if (mbr_eoff == DOSBBSECTOR)
550 				mbr_eoff = dp->dp_start;
551 		}
552 	}
553 
554 	if (nextebr && nextebr != (u_int)-1) {
555 		mbroff = nextebr;
556 		goto again;
557 	}
558 
559  done:
560 	errx(1, "no OpenBSD partition");
561 }
562 
563 /*
564  * Returns 0 if the MBR with the provided partition array is a GPT protective
565  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
566  * one MBR partition, an EFI partition that either covers the whole disk or as
567  * much of it as is possible with a 32bit size field.
568  *
569  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
570  */
571 static int
572 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
573 {
574 	struct dos_partition *dp2;
575 	int efi, found, i;
576 	u_int32_t psize;
577 
578 	found = efi = 0;
579 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
580 		if (dp2->dp_typ == DOSPTYP_UNUSED)
581 			continue;
582 		found++;
583 		if (dp2->dp_typ != DOSPTYP_EFI)
584 			continue;
585 		if (letoh32(dp2->dp_start) != GPTSECTOR)
586 			continue;
587 		psize = letoh32(dp2->dp_size);
588 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX)
589 			efi++;
590 	}
591 	if (found == 1 && efi == 1)
592 		return (0);
593 
594 	return (1);
595 }
596 
597 int
598 findgptefisys(int devfd, struct disklabel *dl)
599 {
600 	struct gpt_partition	 gp[NGPTPARTITIONS];
601 	struct gpt_header	 gh;
602 	struct dos_partition	 dp[NDOSPART];
603 	struct uuid		 efisys_uuid;
604 	const char		 efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM;
605 	off_t			 off;
606 	ssize_t			 len;
607 	u_int64_t		 start;
608 	int			 i;
609 	uint32_t		 orig_csum, new_csum;
610 	uint32_t		 ghsize, ghpartsize, ghpartnum, ghpartspersec;
611 	u_int8_t		*secbuf;
612 
613 	/* Prepare EFI System UUID */
614 	uuid_dec_be(efisys_uuid_code, &efisys_uuid);
615 
616 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
617 		err(1, NULL);
618 
619 	/* Check that there is a protective MBR. */
620 	len = pread(devfd, secbuf, dl->d_secsize, 0);
621 	if (len != dl->d_secsize)
622 		err(4, "can't read mbr");
623 	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
624 	if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) {
625 		free(secbuf);
626 		return (-1);
627 	}
628 
629 	/* Check GPT Header. */
630 	off = dl->d_secsize;	/* Read header from sector 1. */
631 	len = pread(devfd, secbuf, dl->d_secsize, off);
632 	if (len != dl->d_secsize)
633 		err(4, "can't pread gpt header");
634 
635 	memcpy(&gh, secbuf, sizeof(gh));
636 	free(secbuf);
637 
638 	/* Check signature */
639 	if (letoh64(gh.gh_sig) != GPTSIGNATURE)
640 		return (-1);
641 
642 	if (letoh32(gh.gh_rev) != GPTREVISION)
643 		return (-1);
644 
645 	ghsize = letoh32(gh.gh_size);
646 	if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header))
647 		return (-1);
648 
649 	/* Check checksum */
650 	orig_csum = gh.gh_csum;
651 	gh.gh_csum = 0;
652 	new_csum = crc32((unsigned char *)&gh, ghsize);
653 	gh.gh_csum = orig_csum;
654 	if (letoh32(orig_csum) != new_csum)
655 		return (-1);
656 
657 	off = letoh64(gh.gh_part_lba) * dl->d_secsize;
658 	ghpartsize = letoh32(gh.gh_part_size);
659 	ghpartspersec = dl->d_secsize / ghpartsize;
660 	ghpartnum = letoh32(gh.gh_part_num);
661 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
662 		err(1, NULL);
663 	for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) {
664 		len = pread(devfd, secbuf, dl->d_secsize, off);
665 		if (len != dl->d_secsize) {
666 			free(secbuf);
667 			return (-1);
668 		}
669 		memcpy(gp + i * ghpartspersec, secbuf,
670 		    ghpartspersec * sizeof(struct gpt_partition));
671 		off += dl->d_secsize;
672 	}
673 	free(secbuf);
674 	new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize);
675 	if (new_csum != letoh32(gh.gh_part_csum))
676 		return (-1);
677 
678 	start = 0;
679 	for (i = 0; i < ghpartnum && start == 0; i++) {
680 		if (memcmp(&gp[i].gp_type, &efisys_uuid,
681 		    sizeof(struct uuid)) == 0)
682 			start = letoh64(gp[i].gp_lba_start);
683 	}
684 
685 	if (start) {
686 		for (i = 0; i < MAXPARTITIONS; i++) {
687 			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
688 			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
689 				return ('a' + i);
690 		}
691 	}
692 
693 	return (-1);
694 }
695 
696 /*
697  * Load the prototype boot sector (biosboot) into memory.
698  */
699 static char *
700 loadproto(char *fname, long *size)
701 {
702 	int	fd;
703 	size_t	tdsize;		/* text+data size */
704 	char	*bp;
705 	Elf_Ehdr eh;
706 	Elf_Word phsize;
707 	Elf_Phdr *ph;
708 
709 	if ((fd = open(fname, O_RDONLY)) == -1)
710 		err(1, "%s", fname);
711 
712 	if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
713 		errx(1, "%s: read failed", fname);
714 
715 	if (!IS_ELF(eh))
716 		errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname,
717 		    eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
718 		    eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
719 
720 	/*
721 	 * We have to include the exec header in the beginning of
722 	 * the buffer, and leave extra space at the end in case
723 	 * the actual write to disk wants to skip the header.
724 	 */
725 
726 	/* Program load header. */
727 	if (eh.e_phnum != 1)
728 		errx(1, "%s: %u ELF load sections (only support 1)",
729 		    fname, eh.e_phnum);
730 
731 	ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr));
732 	if (ph == NULL)
733 		err(1, NULL);
734 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
735 
736 	if (pread(fd, ph, phsize, eh.e_phoff) != phsize)
737 		errx(1, "%s: can't pread header", fname);
738 
739 	tdsize = ph->p_filesz;
740 
741 	/*
742 	 * Allocate extra space here because the caller may copy
743 	 * the boot block starting at the end of the exec header.
744 	 * This prevents reading beyond the end of the buffer.
745 	 */
746 	if ((bp = calloc(tdsize, 1)) == NULL)
747 		err(1, NULL);
748 
749 	/* Read the rest of the file. */
750 	if (pread(fd, bp, tdsize, ph->p_offset) != (ssize_t)tdsize)
751 		errx(1, "%s: pread failed", fname);
752 
753 	*size = tdsize;	/* not aligned to DEV_BSIZE */
754 
755 	close(fd);
756 	return bp;
757 }
758 
759 static void
760 devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
761 {
762 	if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size)
763 		err(1, "%s: devread: pread", msg);
764 }
765 
766 /*
767  * Read information about /boot's inode, then put this and filesystem
768  * parameters from the superblock into pbr_symbols.
769  */
770 static int
771 getbootparams(char *boot, int devfd, struct disklabel *dl)
772 {
773 	int		fd;
774 	struct stat	dsb, fsb;
775 	struct statfs	fssb;
776 	struct partition *pp;
777 	struct fs	*fs;
778 	char		*sblock, *buf;
779 	u_int		blk, *ap;
780 	int		ndb;
781 	int		mib[3];
782 	size_t		size;
783 	dev_t		dev;
784 	int		incr;
785 
786 	/*
787 	 * Open 2nd-level boot program and record enough details about
788 	 * where it is on the filesystem represented by `devfd'
789 	 * (inode block, offset within that block, and various filesystem
790 	 * parameters essentially taken from the superblock) for biosboot
791 	 * to be able to load it later.
792 	 */
793 
794 	/* Make sure the (probably new) boot file is on disk. */
795 	sync(); sleep(1);
796 
797 	if ((fd = open(boot, O_RDONLY)) == -1)
798 		err(1, "open: %s", boot);
799 
800 	if (fstatfs(fd, &fssb) == -1)
801 		err(1, "statfs: %s", boot);
802 
803 	if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) &&
804 	    strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) )
805 		errx(1, "%s: not on an FFS filesystem", boot);
806 
807 #if 0
808 	if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
809 		errx(1, "read: %s", boot);
810 
811 	if (!IS_ELF(eh)) {
812 		errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
813 		    boot,
814 		    eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
815 		    eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
816 	}
817 #endif
818 
819 	if (fsync(fd) != 0)
820 		err(1, "fsync: %s", boot);
821 
822 	if (fstat(fd, &fsb) != 0)
823 		err(1, "fstat: %s", boot);
824 
825 	if (fstat(devfd, &dsb) != 0)
826 		err(1, "fstat: %d", devfd);
827 
828 	/* Check devices. */
829 	mib[0] = CTL_MACHDEP;
830 	mib[1] = CPU_CHR2BLK;
831 	mib[2] = dsb.st_rdev;
832 	size = sizeof(dev);
833 	if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) {
834 		if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS)
835 			errx(1, "cross-device install");
836 	}
837 
838 	pp = &dl->d_partitions[DISKPART(fsb.st_dev)];
839 	close(fd);
840 
841 	if ((sblock = malloc(SBSIZE)) == NULL)
842 		err(1, NULL);
843 
844 	sbread(devfd, DL_SECTOBLK(dl, pp->p_offset), &fs, sblock);
845 
846 	/* Read inode. */
847 	if ((buf = malloc(fs->fs_bsize)) == NULL)
848 		err(1, NULL);
849 
850 	blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino));
851 
852 	/*
853 	 * Have the inode.  Figure out how many filesystem blocks (not disk
854 	 * sectors) there are for biosboot to load.
855 	 */
856 	devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk,
857 	    fs->fs_bsize, "inode");
858 	if (fs->fs_magic == FS_UFS2_MAGIC) {
859 		struct ufs2_dinode *ip2 = (struct ufs2_dinode *)(buf) +
860 		    ino_to_fsbo(fs, fsb.st_ino);
861 		ndb = howmany(ip2->di_size, fs->fs_bsize);
862 		ap = (u_int *)ip2->di_db;
863 		incr = sizeof(u_int32_t);
864 	} else {
865 		struct ufs1_dinode *ip1 = (struct ufs1_dinode *)(buf) +
866 		    ino_to_fsbo(fs, fsb.st_ino);
867 		ndb = howmany(ip1->di_size, fs->fs_bsize);
868 		ap = (u_int *)ip1->di_db;
869 		incr = 0;
870 	}
871 
872 	if (ndb <= 0)
873 		errx(1, "No blocks to load");
874 
875 	/*
876 	 * Now set the values that will need to go into biosboot
877 	 * (the partition boot record, a.k.a. the PBR).
878 	 */
879 	sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16));
880 	sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize /
881 	    dl->d_secsize));
882 
883 	/*
884 	 * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The
885 	 * ino_to_fsba() return value is the number of fs_fsize units.
886 	 * Calculate the shift to convert fs_fsize into physical sectors,
887 	 * which are added to p_offset to get the sector address BIOS
888 	 * will use.
889 	 *
890 	 * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize.
891 	 */
892 	sym_set_value(pbr_symbols, "_fsbtodb",
893 	    ffs(fs->fs_fsize / dl->d_secsize) - 1);
894 
895 	sym_set_value(pbr_symbols, "_p_offset", pp->p_offset);
896 	sym_set_value(pbr_symbols, "_inodeblk",
897 	    ino_to_fsba(fs, fsb.st_ino));
898 	sym_set_value(pbr_symbols, "_inodedbl",
899 	    ((((char *)ap) - buf) + INODEOFF));
900 	sym_set_value(pbr_symbols, "_nblocks", ndb);
901 	sym_set_value(pbr_symbols, "_blkincr", incr);
902 
903 	if (verbose) {
904 		fprintf(stderr, "%s is %d blocks x %d bytes\n",
905 		    boot, ndb, fs->fs_bsize);
906 		fprintf(stderr, "fs block shift %u; part offset %u; "
907 		    "inode block %lld, offset %u\n",
908 		    ffs(fs->fs_fsize / dl->d_secsize) - 1,
909 		    pp->p_offset,
910 		    ino_to_fsba(fs, fsb.st_ino),
911 		    (unsigned int)((((char *)ap) - buf) + INODEOFF));
912 		fprintf(stderr, "expecting %d-bit fs blocks (incr %d)\n",
913 		    incr ? 64 : 32, incr);
914 	}
915 
916 	free (sblock);
917 	free (buf);
918 
919 	return 0;
920 }
921 
922 void
923 sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value)
924 {
925 	struct sym_data *p;
926 
927 	for (p = sym_list; p->sym_name != NULL; p++) {
928 		if (strcmp(p->sym_name, sym) == 0)
929 			break;
930 	}
931 
932 	if (p->sym_name == NULL)
933 		errx(1, "%s: no such symbol", sym);
934 
935 	p->sym_value = value;
936 	p->sym_set = 1;
937 }
938 
939 /*
940  * Write the parameters stored in sym_list into the in-memory copy of
941  * the prototype biosboot (proto), ready for it to be written to disk.
942  */
943 void
944 pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list)
945 {
946 	struct sym_data *sym;
947 	struct nlist	*nl;
948 	char		*vp;
949 	u_int32_t	*lp;
950 	u_int16_t	*wp;
951 	u_int8_t	*bp;
952 
953 	for (sym = sym_list; sym->sym_name != NULL; sym++) {
954 		if (!sym->sym_set)
955 			errx(1, "%s not set", sym->sym_name);
956 
957 		/* Allocate space for 2; second is null-terminator for list. */
958 		nl = calloc(2, sizeof(struct nlist));
959 		if (nl == NULL)
960 			err(1, NULL);
961 
962 		nl->n_name = sym->sym_name;
963 
964 		if (nlist_elf32(fname, nl) != 0)
965 			errx(1, "%s: symbol %s not found",
966 			    fname, sym->sym_name);
967 
968 		if (nl->n_type != (N_TEXT))
969 			errx(1, "%s: %s: wrong type (%x)",
970 			    fname, sym->sym_name, nl->n_type);
971 
972 		/* Get a pointer to where the symbol's value needs to go. */
973 		vp = proto + nl->n_value;
974 
975 		switch (sym->sym_size) {
976 		case 4:					/* u_int32_t */
977 			lp = (u_int32_t *) vp;
978 			*lp = sym->sym_value;
979 			break;
980 		case 2:					/* u_int16_t */
981 			if (sym->sym_value >= 0x10000)	/* out of range */
982 				errx(1, "%s: symbol out of range (%u)",
983 				    sym->sym_name, sym->sym_value);
984 			wp = (u_int16_t *) vp;
985 			*wp = (u_int16_t) sym->sym_value;
986 			break;
987 		case 1:					/* u_int16_t */
988 			if (sym->sym_value >= 0x100)	/* out of range */
989 				errx(1, "%s: symbol out of range (%u)",
990 				    sym->sym_name, sym->sym_value);
991 			bp = (u_int8_t *) vp;
992 			*bp = (u_int8_t) sym->sym_value;
993 			break;
994 		default:
995 			errx(1, "%s: bad symbol size %d",
996 			    sym->sym_name, sym->sym_size);
997 			/* NOTREACHED */
998 		}
999 
1000 		free(nl);
1001 	}
1002 }
1003 
1004 static int
1005 sbchk(struct fs *fs, daddr_t sbloc)
1006 {
1007 	if (verbose)
1008 		fprintf(stderr, "looking for superblock at %lld\n", sbloc);
1009 
1010 	if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) {
1011 		if (verbose)
1012 			fprintf(stderr, "bad superblock magic 0x%x\n",
1013 			    fs->fs_magic);
1014 		return (0);
1015 	}
1016 
1017 	/*
1018 	 * Looking for an FFS1 file system at SBLOCK_UFS2 will find the
1019 	 * wrong superblock for file systems with 64k block size.
1020 	 */
1021 	if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) {
1022 		if (verbose)
1023 			fprintf(stderr, "skipping ffs1 superblock at %lld\n",
1024 			    sbloc);
1025 		return (0);
1026 	}
1027 
1028 	if (fs->fs_bsize <= 0 || fs->fs_bsize < sizeof(struct fs) ||
1029 	    fs->fs_bsize > MAXBSIZE) {
1030 		if (verbose)
1031 			fprintf(stderr, "invalid superblock block size %d\n",
1032 			    fs->fs_bsize);
1033 		return (0);
1034 	}
1035 
1036 	if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) {
1037 		if (verbose)
1038 			fprintf(stderr, "invalid superblock size %d\n",
1039 			    fs->fs_sbsize);
1040 		return (0);
1041 	}
1042 
1043 	if (fs->fs_inopb <= 0) {
1044 		if (verbose)
1045 			fprintf(stderr, "invalid superblock inodes/block %d\n",
1046 			    fs->fs_inopb);
1047 		return (0);
1048 	}
1049 
1050 	if (verbose)
1051 		fprintf(stderr, "found valid %s superblock\n",
1052 		    fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1");
1053 
1054 	return (1);
1055 }
1056 
1057 static void
1058 sbread(int fd, daddr_t poffset, struct fs **fs, char *sblock)
1059 {
1060 	int i;
1061 	daddr_t sboff;
1062 
1063 	for (i = 0; sbtry[i] != -1; i++) {
1064 		sboff = sbtry[i] / DEV_BSIZE;
1065 		devread(fd, sblock, poffset + sboff, SBSIZE, "superblock");
1066 		*fs = (struct fs *)sblock;
1067 		if (sbchk(*fs, sbtry[i]))
1068 			break;
1069 	}
1070 
1071 	if (sbtry[i] == -1)
1072 		errx(1, "couldn't find ffs superblock");
1073 }
1074