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