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
73char	*bootldr;
74
75char	*blkstore;
76size_t	blksize;
77
78struct 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
90static void	devread(int, void *, daddr_t, size_t, char *);
91static u_int	findopenbsd(int, struct disklabel *);
92static int	getbootparams(char *, int, struct disklabel *);
93static char	*loadproto(char *, long *);
94static int	gpt_chk_mbr(struct dos_partition *, u_int64_t);
95static int	sbchk(struct fs *, daddr_t);
96static void	sbread(int, daddr_t, struct fs **, char *);
97
98static 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
106void
107md_init(void)
108{
109	stages = 2;
110	stage1 = "/usr/mdec/biosboot";
111	stage2 = "/usr/mdec/boot";
112
113	bootldr = "/boot";
114}
115
116void
117md_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
131void
132md_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
154void
155md_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
194void
195write_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
244int
245create_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
288void
289write_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
420umount:
421	dst[mntlen] = '\0';
422	if (unmount(dst, MNT_FORCE) == -1)
423		err(1, "unmount('%s') failed", dst);
424
425rmdir:
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 */
443u_int
444findopenbsd(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
454again:
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 */
526static int
527gpt_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
552int
553findgptefisys(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 */
654static char *
655loadproto(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
714static void
715devread(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 */
725static int
726getbootparams(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
877void
878sym_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 */
898void
899pbr_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
959static int
960sbchk(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
1012static void
1013sbread(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