xref: /openbsd-src/usr.sbin/installboot/i386_installboot.c (revision db15c72a1929803b548b8f444dbcdc667e3d1ad2)
1*db15c72aSkettenis /*	$OpenBSD: i386_installboot.c,v 1.48 2024/11/08 10:43:07 kettenis Exp $	*/
2b4544c7cSjsing /*	$NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
3b4544c7cSjsing 
4b4544c7cSjsing /*
5fe786928Sotto  * Copyright (c) 2013 Pedro Martelletto
6b4544c7cSjsing  * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
7b4544c7cSjsing  * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
8b4544c7cSjsing  * Copyright (c) 1997 Michael Shalayeff
9b4544c7cSjsing  * Copyright (c) 1994 Paul Kranenburg
10b4544c7cSjsing  * All rights reserved.
11b4544c7cSjsing  *
12b4544c7cSjsing  * Redistribution and use in source and binary forms, with or without
13b4544c7cSjsing  * modification, are permitted provided that the following conditions
14b4544c7cSjsing  * are met:
15b4544c7cSjsing  * 1. Redistributions of source code must retain the above copyright
16b4544c7cSjsing  *    notice, this list of conditions and the following disclaimer.
17b4544c7cSjsing  * 2. Redistributions in binary form must reproduce the above copyright
18b4544c7cSjsing  *    notice, this list of conditions and the following disclaimer in the
19b4544c7cSjsing  *    documentation and/or other materials provided with the distribution.
20b4544c7cSjsing  * 3. All advertising materials mentioning features or use of this software
21b4544c7cSjsing  *    must display the following acknowledgement:
22b4544c7cSjsing  *      This product includes software developed by Paul Kranenburg.
23b4544c7cSjsing  * 4. The name of the author may not be used to endorse or promote products
24b4544c7cSjsing  *    derived from this software without specific prior written permission
25b4544c7cSjsing  *
26b4544c7cSjsing  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27b4544c7cSjsing  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28b4544c7cSjsing  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29b4544c7cSjsing  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30b4544c7cSjsing  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31b4544c7cSjsing  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32b4544c7cSjsing  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33b4544c7cSjsing  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34b4544c7cSjsing  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35b4544c7cSjsing  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36b4544c7cSjsing  */
37b4544c7cSjsing 
38b4544c7cSjsing #define ELFSIZE 32
39b4544c7cSjsing 
4078eb0b7eSderaadt #include <sys/param.h>	/* DEV_BSIZE */
41b4544c7cSjsing #include <sys/disklabel.h>
42b4544c7cSjsing #include <sys/dkio.h>
43b4544c7cSjsing #include <sys/ioctl.h>
44b4544c7cSjsing #include <sys/mount.h>
45b4544c7cSjsing #include <sys/reboot.h>
46b4544c7cSjsing #include <sys/stat.h>
47b4544c7cSjsing #include <sys/sysctl.h>
48b4544c7cSjsing #include <sys/time.h>
49b4544c7cSjsing 
50b4544c7cSjsing #include <ufs/ufs/dinode.h>
51b4544c7cSjsing #include <ufs/ufs/dir.h>
52b4544c7cSjsing #include <ufs/ffs/fs.h>
53b4544c7cSjsing 
54b4544c7cSjsing #include <machine/cpu.h>
55b4544c7cSjsing #include <machine/biosvar.h>
56b4544c7cSjsing 
57e9d517b1Smpi #include <elf.h>
58b4544c7cSjsing #include <err.h>
59f66515a4Skrw #include <errno.h>
60b4544c7cSjsing #include <fcntl.h>
61b4544c7cSjsing #include <nlist.h>
62b4544c7cSjsing #include <stdlib.h>
63b4544c7cSjsing #include <stdio.h>
640461d12bSkrw #include <stdint.h>
65b4544c7cSjsing #include <string.h>
66b4544c7cSjsing #include <unistd.h>
67b4544c7cSjsing #include <util.h>
68c60e821eSjsg #include <uuid.h>
69b4544c7cSjsing 
70b4544c7cSjsing #include "installboot.h"
71b4544c7cSjsing #include "i386_installboot.h"
72b4544c7cSjsing 
73b4544c7cSjsing char	*bootldr;
74b4544c7cSjsing 
75b4544c7cSjsing char	*blkstore;
76b4544c7cSjsing size_t	blksize;
77b4544c7cSjsing 
78b4544c7cSjsing struct sym_data pbr_symbols[] = {
79b4544c7cSjsing 	{"_fs_bsize_p",	2},
80b4544c7cSjsing 	{"_fs_bsize_s",	2},
81b4544c7cSjsing 	{"_fsbtodb",	1},
82b4544c7cSjsing 	{"_p_offset",	4},
83b4544c7cSjsing 	{"_inodeblk",	4},
84b4544c7cSjsing 	{"_inodedbl",	4},
85b4544c7cSjsing 	{"_nblocks",	2},
86fe786928Sotto 	{"_blkincr",	1},
87b4544c7cSjsing 	{NULL}
88b4544c7cSjsing };
89b4544c7cSjsing 
90b4544c7cSjsing static void	devread(int, void *, daddr_t, size_t, char *);
91b4544c7cSjsing static u_int	findopenbsd(int, struct disklabel *);
92b4544c7cSjsing static int	getbootparams(char *, int, struct disklabel *);
93b4544c7cSjsing static char	*loadproto(char *, long *);
9412b4e828Skrw static int	gpt_chk_mbr(struct dos_partition *, u_int64_t);
95fe786928Sotto static int	sbchk(struct fs *, daddr_t);
96fe786928Sotto static void	sbread(int, daddr_t, struct fs **, char *);
97fe786928Sotto 
98fe786928Sotto static const daddr_t sbtry[] = SBLOCKSEARCH;
99b4544c7cSjsing 
100b4544c7cSjsing /*
101b4544c7cSjsing  * Read information about /boot's inode and filesystem parameters, then
102b4544c7cSjsing  * put biosboot (partition boot record) on the target drive with these
103b4544c7cSjsing  * parameters patched in.
104b4544c7cSjsing  */
105b4544c7cSjsing 
106b4544c7cSjsing void
107b4544c7cSjsing md_init(void)
108b4544c7cSjsing {
109b4544c7cSjsing 	stages = 2;
110b4544c7cSjsing 	stage1 = "/usr/mdec/biosboot";
111b4544c7cSjsing 	stage2 = "/usr/mdec/boot";
112b4544c7cSjsing 
113b4544c7cSjsing 	bootldr = "/boot";
114b4544c7cSjsing }
115b4544c7cSjsing 
116b4544c7cSjsing void
117b4544c7cSjsing md_loadboot(void)
118b4544c7cSjsing {
119b4544c7cSjsing 	/* Load prototype boot blocks. */
120b4544c7cSjsing 	if ((blkstore = loadproto(stage1, &blksize)) == NULL)
121b4544c7cSjsing 		exit(1);
122b4544c7cSjsing 
123b4544c7cSjsing 	/* XXX - Paranoia: Make sure size is aligned! */
124b4544c7cSjsing 	if (blksize & (DEV_BSIZE - 1))
125b4544c7cSjsing 		errx(1, "proto %s bad size=%ld", stage1, blksize);
126b4544c7cSjsing 
127b4544c7cSjsing 	if (blksize > SBSIZE - DEV_BSIZE)
128b4544c7cSjsing 		errx(1, "proto bootblocks too big");
129b4544c7cSjsing }
130b4544c7cSjsing 
131b4544c7cSjsing void
132c3e1bf61Skettenis md_prepareboot(int devfd, char *dev)
133c3e1bf61Skettenis {
134c3e1bf61Skettenis 	struct disklabel dl;
135c3e1bf61Skettenis 	int part;
136c3e1bf61Skettenis 
137c3e1bf61Skettenis 	/* Get and check disklabel. */
138c3e1bf61Skettenis 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
139c3e1bf61Skettenis 		err(1, "disklabel: %s", dev);
140c3e1bf61Skettenis 	if (dl.d_magic != DISKMAGIC)
141c3e1bf61Skettenis 		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
142c3e1bf61Skettenis 
143c3e1bf61Skettenis 	/* Warn on unknown disklabel types. */
144c3e1bf61Skettenis 	if (dl.d_type == 0)
145c3e1bf61Skettenis 		warnx("disklabel type unknown");
146c3e1bf61Skettenis 
147c3e1bf61Skettenis 	part = findgptefisys(devfd, &dl);
148c3e1bf61Skettenis 	if (part != -1) {
14914cbb8d0Skn 		create_filesystem(&dl, (char)part);
15014cbb8d0Skn 		return;
151c3e1bf61Skettenis 	}
152c3e1bf61Skettenis }
153c3e1bf61Skettenis 
154c3e1bf61Skettenis void
155b4544c7cSjsing md_installboot(int devfd, char *dev)
156b4544c7cSjsing {
157b4544c7cSjsing 	struct disklabel dl;
158f66515a4Skrw 	int part;
159b4544c7cSjsing 
160b4544c7cSjsing 	/* Get and check disklabel. */
161df69c215Sderaadt 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
162b4544c7cSjsing 		err(1, "disklabel: %s", dev);
163b4544c7cSjsing 	if (dl.d_magic != DISKMAGIC)
164b4544c7cSjsing 		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
165b4544c7cSjsing 
166b4544c7cSjsing 	/* Warn on unknown disklabel types. */
167b4544c7cSjsing 	if (dl.d_type == 0)
168b4544c7cSjsing 		warnx("disklabel type unknown");
169b4544c7cSjsing 
170f66515a4Skrw 	part = findgptefisys(devfd, &dl);
171f66515a4Skrw 	if (part != -1) {
172a284d5afSderaadt 		write_filesystem(&dl, (char)part);
173f66515a4Skrw 		return;
174f66515a4Skrw 	}
175f66515a4Skrw 
176b4544c7cSjsing 	bootldr = fileprefix(root, bootldr);
177597864b7Skrw 	if (bootldr == NULL)
178597864b7Skrw 		exit(1);
1796a1403f6Sjsing 	if (verbose)
1806a1403f6Sjsing 		fprintf(stderr, "%s %s to %s\n",
1816a1403f6Sjsing 		    (nowrite ? "would copy" : "copying"), stage2, bootldr);
182b4544c7cSjsing 	if (!nowrite)
183597864b7Skrw 		if (filecopy(stage2, bootldr) == -1)
184597864b7Skrw 			exit(1);
185b4544c7cSjsing 
186b4544c7cSjsing 	/* Get bootstrap parameters to patch into proto. */
187b4544c7cSjsing 	if (getbootparams(bootldr, devfd, &dl) != 0)
188b4544c7cSjsing 		exit(1);
189b4544c7cSjsing 
190b4544c7cSjsing 	/* Write boot blocks to device. */
191b4544c7cSjsing 	write_bootblocks(devfd, dev, &dl);
192b4544c7cSjsing }
193b4544c7cSjsing 
194b4544c7cSjsing void
195b4544c7cSjsing write_bootblocks(int devfd, char *dev, struct disklabel *dl)
196b4544c7cSjsing {
197b4544c7cSjsing 	struct stat	sb;
198b4544c7cSjsing 	u_int8_t	*secbuf;
1991a05cb57Skrw 	u_int		start;
200b4544c7cSjsing 
201b4544c7cSjsing 	/* Write patched proto bootblock(s) into the superblock. */
202df69c215Sderaadt 	if (fstat(devfd, &sb) == -1)
203b4544c7cSjsing 		err(1, "fstat: %s", dev);
204b4544c7cSjsing 
205b4544c7cSjsing 	if (!S_ISCHR(sb.st_mode))
206b4544c7cSjsing 		errx(1, "%s: not a character device", dev);
207b4544c7cSjsing 
208b4544c7cSjsing 	/* Patch the parameters into the proto bootstrap sector. */
209b4544c7cSjsing 	pbr_set_symbols(stage1, blkstore, pbr_symbols);
210b4544c7cSjsing 
211b4544c7cSjsing 	if (!nowrite) {
21268507edaSjsing 		/* Sync filesystems (to clean in-memory superblock?). */
21368507edaSjsing 		sync(); sleep(1);
214b4544c7cSjsing 	}
215b4544c7cSjsing 
216b4544c7cSjsing 	/*
2171a05cb57Skrw 	 * Find bootstrap sector.
218b4544c7cSjsing 	 */
219b4544c7cSjsing 	start = findopenbsd(devfd, dl);
2201a05cb57Skrw 	if (verbose) {
2211a05cb57Skrw 		if (start == 0)
2221a05cb57Skrw 			fprintf(stderr, "no MBR, ");
223b4544c7cSjsing 		fprintf(stderr, "%s will be written at sector %u\n",
224b4544c7cSjsing 		    stage1, start);
2251a05cb57Skrw 	}
226b4544c7cSjsing 
227b4544c7cSjsing 	if (start + (blksize / dl->d_secsize) > BOOTBIOS_MAXSEC)
228b4544c7cSjsing 		warnx("%s extends beyond sector %u. OpenBSD might not boot.",
229b4544c7cSjsing 		    stage1, BOOTBIOS_MAXSEC);
230b4544c7cSjsing 
231b4544c7cSjsing 	if (!nowrite) {
232b4544c7cSjsing 		secbuf = calloc(1, dl->d_secsize);
233c462e017Skrw 		if (pread(devfd, secbuf, dl->d_secsize, (off_t)start *
234c462e017Skrw 		    dl->d_secsize) != dl->d_secsize)
235c462e017Skrw 			err(1, "pread boot sector");
236b4544c7cSjsing 		bcopy(blkstore, secbuf, blksize);
237c462e017Skrw 		if (pwrite(devfd, secbuf, dl->d_secsize, (off_t)start *
238c462e017Skrw 		    dl->d_secsize) != dl->d_secsize)
239c462e017Skrw 			err(1, "pwrite bootstrap");
240b4544c7cSjsing 		free(secbuf);
241b4544c7cSjsing 	}
242b4544c7cSjsing }
243b4544c7cSjsing 
244c3e1bf61Skettenis int
245c3e1bf61Skettenis create_filesystem(struct disklabel *dl, char part)
246c3e1bf61Skettenis {
2477a17f38cSkrw 	static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null";
248c3e1bf61Skettenis 	struct msdosfs_args args;
249c3e1bf61Skettenis 	char cmd[60];
250c3e1bf61Skettenis 	int rslt;
251c3e1bf61Skettenis 
252acd6e620Skn 	/* Newfs <duid>.<part> as msdos filesystem. */
253c3e1bf61Skettenis 	memset(&args, 0, sizeof(args));
254c3e1bf61Skettenis 	rslt = asprintf(&args.fspec,
255c3e1bf61Skettenis 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
256c3e1bf61Skettenis             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
257c3e1bf61Skettenis             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
258c3e1bf61Skettenis 	    part);
259c3e1bf61Skettenis 	if (rslt == -1) {
260c3e1bf61Skettenis 		warn("bad special device");
261c3e1bf61Skettenis 		return rslt;
262c3e1bf61Skettenis 	}
263c3e1bf61Skettenis 
264c3e1bf61Skettenis 	rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
265c3e1bf61Skettenis 	if (rslt >= sizeof(cmd)) {
266c3e1bf61Skettenis 		warnx("can't build newfs command");
267993e4236Skrw 		free(args.fspec);
268c3e1bf61Skettenis 		rslt = -1;
269c3e1bf61Skettenis 		return rslt;
270c3e1bf61Skettenis 	}
271c3e1bf61Skettenis 
272c3e1bf61Skettenis 	if (verbose)
273c3e1bf61Skettenis 		fprintf(stderr, "%s %s\n",
274c3e1bf61Skettenis 		    (nowrite ? "would newfs" : "newfsing"), args.fspec);
275c3e1bf61Skettenis 	if (!nowrite) {
276c3e1bf61Skettenis 		rslt = system(cmd);
277c3e1bf61Skettenis 		if (rslt == -1) {
278c3e1bf61Skettenis 			warn("system('%s') failed", cmd);
279993e4236Skrw 			free(args.fspec);
280c3e1bf61Skettenis 			return rslt;
281c3e1bf61Skettenis 		}
282c3e1bf61Skettenis 	}
283c3e1bf61Skettenis 
284993e4236Skrw 	free(args.fspec);
285c3e1bf61Skettenis 	return 0;
286c3e1bf61Skettenis }
287c3e1bf61Skettenis 
288f66515a4Skrw void
289a284d5afSderaadt write_filesystem(struct disklabel *dl, char part)
290f66515a4Skrw {
2917a17f38cSkrw 	static const char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null";
292f66515a4Skrw 	struct msdosfs_args args;
293*db15c72aSkettenis #ifdef __amd64__
294*db15c72aSkettenis 	struct statfs sf;
295*db15c72aSkettenis #endif
296f66515a4Skrw 	char cmd[60];
297cdbd10f3Skettenis 	char dst[PATH_MAX];
298f66515a4Skrw 	char *src;
299f66515a4Skrw 	size_t mntlen, pathlen, srclen;
300f66515a4Skrw 	int rslt;
301f66515a4Skrw 
302f66515a4Skrw 	src = NULL;
303f66515a4Skrw 
304f66515a4Skrw 	/* Create directory for temporary mount point. */
305f66515a4Skrw 	strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
306f66515a4Skrw 	if (mkdtemp(dst) == NULL)
307f66515a4Skrw 		err(1, "mkdtemp('%s') failed", dst);
308f66515a4Skrw 	mntlen = strlen(dst);
309f66515a4Skrw 
310f66515a4Skrw 	/* Mount <duid>.<part> as msdos filesystem. */
311f66515a4Skrw 	memset(&args, 0, sizeof(args));
312f66515a4Skrw 	rslt = asprintf(&args.fspec,
313f66515a4Skrw 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
314f66515a4Skrw             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
315f66515a4Skrw             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
316f66515a4Skrw 	    part);
317f66515a4Skrw 	if (rslt == -1) {
318f66515a4Skrw 		warn("bad special device");
319f66515a4Skrw 		goto rmdir;
320f66515a4Skrw 	}
321f66515a4Skrw 
322f66515a4Skrw 	args.export_info.ex_root = -2;	/* unchecked anyway on DOS fs */
323f66515a4Skrw 	args.export_info.ex_flags = 0;
324d160f728Sderaadt 	args.flags = MSDOSFSMNT_LONGNAME;
325f66515a4Skrw 
326f66515a4Skrw 	if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
327f66515a4Skrw 		/* Try fsck'ing it. */
328f66515a4Skrw 		rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
329f66515a4Skrw 		if (rslt >= sizeof(cmd)) {
3304163633cSkettenis 			warnx("can't build fsck command");
331f66515a4Skrw 			rslt = -1;
332f66515a4Skrw 			goto rmdir;
333f66515a4Skrw 		}
334f66515a4Skrw 		rslt = system(cmd);
335f66515a4Skrw 		if (rslt == -1) {
336f66515a4Skrw 			warn("system('%s') failed", cmd);
337f66515a4Skrw 			goto rmdir;
338f66515a4Skrw 		}
339f66515a4Skrw 		if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
340f66515a4Skrw 			/* Try newfs'ing it. */
341c3e1bf61Skettenis 			rslt = create_filesystem(dl, part);
342c3e1bf61Skettenis 			if (rslt == -1)
343f66515a4Skrw 				goto rmdir;
344f66515a4Skrw 			rslt = mount(MOUNT_MSDOS, dst, 0, &args);
345f66515a4Skrw 			if (rslt == -1) {
346f66515a4Skrw 				warn("unable to mount EFI System partition");
347f66515a4Skrw 				goto rmdir;
348f66515a4Skrw 			}
349f66515a4Skrw 		}
350f66515a4Skrw 	}
351f66515a4Skrw 
35263a3f190Srpe 	/* Create "/efi/BOOT" directory in <duid>.<part>. */
353f66515a4Skrw 	if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) {
354f66515a4Skrw 		rslt = -1;
355f66515a4Skrw 		warn("unable to build /efi directory");
356f66515a4Skrw 		goto umount;
357f66515a4Skrw 	}
358f66515a4Skrw 	rslt = mkdir(dst, 0);
359f66515a4Skrw 	if (rslt == -1 && errno != EEXIST) {
360f66515a4Skrw 		warn("mkdir('%s') failed", dst);
361f66515a4Skrw 		goto umount;
362f66515a4Skrw 	}
363f66515a4Skrw 	if (strlcat(dst, "/BOOT", sizeof(dst)) >= sizeof(dst)) {
364f66515a4Skrw 		rslt = -1;
365f66515a4Skrw 		warn("unable to build /BOOT directory");
366f66515a4Skrw 		goto umount;
367f66515a4Skrw 	}
368f66515a4Skrw 	rslt = mkdir(dst, 0);
369f66515a4Skrw 	if (rslt == -1 && errno != EEXIST) {
370f66515a4Skrw 		warn("mkdir('%s') failed", dst);
371f66515a4Skrw 		goto umount;
372f66515a4Skrw 	}
373f66515a4Skrw 
374f66515a4Skrw 	/*
37563a3f190Srpe 	 * Copy BOOTIA32.EFI and BOOTX64.EFI to /efi/BOOT/.
376f66515a4Skrw 	 *
377f66515a4Skrw 	 * N.B.: BOOTIA32.EFI is longer than BOOTX64.EFI, so src can be reused!
378f66515a4Skrw 	 */
379f66515a4Skrw 	pathlen = strlen(dst);
380f66515a4Skrw 	if (strlcat(dst, "/BOOTIA32.EFI", sizeof(dst)) >= sizeof(dst)) {
381f66515a4Skrw 		rslt = -1;
382f66515a4Skrw 		warn("unable to build /BOOTIA32.EFI path");
383f66515a4Skrw 		goto umount;
384f66515a4Skrw 	}
385f66515a4Skrw 	src = fileprefix(root, "/usr/mdec/BOOTIA32.EFI");
386597864b7Skrw 	if (src == NULL) {
387597864b7Skrw 		rslt = -1;
388597864b7Skrw 		goto umount;
389597864b7Skrw 	}
390f66515a4Skrw 	srclen = strlen(src);
391f66515a4Skrw 	if (verbose)
392f66515a4Skrw 		fprintf(stderr, "%s %s to %s\n",
393f66515a4Skrw 		    (nowrite ? "would copy" : "copying"), src, dst);
394597864b7Skrw 	if (!nowrite) {
395597864b7Skrw 		rslt = filecopy(src, dst);
396597864b7Skrw 		if (rslt == -1)
397597864b7Skrw 			goto umount;
398597864b7Skrw 	}
399f66515a4Skrw 	src[srclen - strlen("/BOOTIA32.EFI")] = '\0';
400f66515a4Skrw 
401f66515a4Skrw 	dst[pathlen] = '\0';
402f66515a4Skrw 	if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) {
403f66515a4Skrw 		rslt = -1;
404f66515a4Skrw 		warn("unable to build /BOOTX64.EFI dst path");
405f66515a4Skrw 		goto umount;
406f66515a4Skrw 	}
407f66515a4Skrw 	if (strlcat(src, "/BOOTX64.EFI", srclen+1) >= srclen+1) {
408f66515a4Skrw 		rslt = -1;
409f66515a4Skrw 		warn("unable to build /BOOTX64.EFI src path");
410f66515a4Skrw 		goto umount;
411f66515a4Skrw 	}
412f66515a4Skrw 	if (verbose)
413f66515a4Skrw 		fprintf(stderr, "%s %s to %s\n",
414f66515a4Skrw 		    (nowrite ? "would copy" : "copying"), src, dst);
415597864b7Skrw 	if (!nowrite) {
416597864b7Skrw 		rslt = filecopy(src, dst);
417597864b7Skrw 		if (rslt == -1)
418597864b7Skrw 			goto umount;
419597864b7Skrw 	}
420f66515a4Skrw 
421fbfcabeaSkettenis #ifdef __amd64__
422*db15c72aSkettenis 	/* Skip installing a 2nd copy if we have a small filesystem. */
423*db15c72aSkettenis 	if (statfs(dst, &sf) || sf.f_blocks < 2048) {
424*db15c72aSkettenis 		rslt = 0;
425*db15c72aSkettenis 		goto umount;
426*db15c72aSkettenis 	}
427*db15c72aSkettenis 
428fbfcabeaSkettenis 	/* Create "/efi/openbsd" directory in <duid>.<part>. */
429fbfcabeaSkettenis 	dst[mntlen] = '\0';
430fbfcabeaSkettenis 	if (strlcat(dst, "/efi/openbsd", sizeof(dst)) >= sizeof(dst)) {
431fbfcabeaSkettenis 		rslt = -1;
432fbfcabeaSkettenis 		warn("unable to build /efi/openbsd directory");
433fbfcabeaSkettenis 		goto umount;
434fbfcabeaSkettenis 	}
435fbfcabeaSkettenis 	rslt = mkdir(dst, 0755);
436fbfcabeaSkettenis 	if (rslt == -1 && errno != EEXIST) {
437fbfcabeaSkettenis 		warn("mkdir('%s') failed", dst);
438fbfcabeaSkettenis 		goto umount;
439fbfcabeaSkettenis 	}
440fbfcabeaSkettenis 
441fbfcabeaSkettenis 	/* Copy BOOTX64.EFI to /efi/openbsd/. */
442fbfcabeaSkettenis 	if (strlcat(dst, "/BOOTX64.EFI", sizeof(dst)) >= sizeof(dst)) {
443fbfcabeaSkettenis 		rslt = -1;
444fbfcabeaSkettenis 		warn("unable to build /BOOTX64.EFI path");
445fbfcabeaSkettenis 		goto umount;
446fbfcabeaSkettenis 	}
447fbfcabeaSkettenis 	src = fileprefix(root, "/usr/mdec/BOOTX64.EFI");
448fbfcabeaSkettenis 	if (src == NULL) {
449fbfcabeaSkettenis 		rslt = -1;
450fbfcabeaSkettenis 		goto umount;
451fbfcabeaSkettenis 	}
452fbfcabeaSkettenis 	srclen = strlen(src);
453fbfcabeaSkettenis 	if (verbose)
454fbfcabeaSkettenis 		fprintf(stderr, "%s %s to %s\n",
455fbfcabeaSkettenis 		    (nowrite ? "would copy" : "copying"), src, dst);
456fbfcabeaSkettenis 	if (!nowrite) {
457fbfcabeaSkettenis 		rslt = filecopy(src, dst);
458fbfcabeaSkettenis 		if (rslt == -1)
459fbfcabeaSkettenis 			goto umount;
460fbfcabeaSkettenis 	}
461fbfcabeaSkettenis #endif
462fbfcabeaSkettenis 
463f66515a4Skrw 	rslt = 0;
464f66515a4Skrw 
465f66515a4Skrw umount:
466f66515a4Skrw 	dst[mntlen] = '\0';
467f66515a4Skrw 	if (unmount(dst, MNT_FORCE) == -1)
468f66515a4Skrw 		err(1, "unmount('%s') failed", dst);
469f66515a4Skrw 
470f66515a4Skrw rmdir:
471f66515a4Skrw 	free(args.fspec);
472f66515a4Skrw 	dst[mntlen] = '\0';
473f66515a4Skrw 	if (rmdir(dst) == -1)
474f66515a4Skrw 		err(1, "rmdir('%s') failed", dst);
475f66515a4Skrw 
476f66515a4Skrw 	free(src);
477f66515a4Skrw 
478f66515a4Skrw 	if (rslt == -1)
479f66515a4Skrw 		exit(1);
480f66515a4Skrw }
481f66515a4Skrw 
4821a05cb57Skrw /*
4831a05cb57Skrw  * a) For media w/o an MBR use sector 0.
4841a05cb57Skrw  * b) For media with an MBR and an OpenBSD (A6) partition use the first
4851a05cb57Skrw  *    sector of the OpenBSD partition.
4861a05cb57Skrw  * c) For media with an MBR and no OpenBSD partition error out.
4871a05cb57Skrw  */
488b4544c7cSjsing u_int
489b4544c7cSjsing findopenbsd(int devfd, struct disklabel *dl)
490b4544c7cSjsing {
491b4544c7cSjsing 	struct		dos_mbr mbr;
492b4544c7cSjsing 	u_int		mbroff = DOSBBSECTOR;
493b4544c7cSjsing 	u_int		mbr_eoff = DOSBBSECTOR; /* Offset of extended part. */
494b4544c7cSjsing 	struct		dos_partition *dp;
495b4544c7cSjsing 	u_int8_t	*secbuf;
496b4544c7cSjsing 	u_int		maxebr = DOS_MAXEBR, nextebr;
497b4544c7cSjsing 	int		i;
498b4544c7cSjsing 
499b4544c7cSjsing again:
500b4544c7cSjsing 	if (!maxebr--) {
501b4544c7cSjsing 		if (verbose)
502b4544c7cSjsing 			fprintf(stderr, "Traversed more than %d Extended Boot "
503b4544c7cSjsing 			    "Records (EBRs)\n", DOS_MAXEBR);
5041a05cb57Skrw 		goto done;
505b4544c7cSjsing 	}
506b4544c7cSjsing 
507b4544c7cSjsing 	if (verbose)
508b4544c7cSjsing 		fprintf(stderr, "%s boot record (%cBR) at sector %u\n",
509b4544c7cSjsing 		    (mbroff == DOSBBSECTOR) ? "master" : "extended",
510b4544c7cSjsing 		    (mbroff == DOSBBSECTOR) ? 'M' : 'E', mbroff);
511b4544c7cSjsing 
5126ea8b240Stobias 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
5136ea8b240Stobias 		err(1, NULL);
514c462e017Skrw 	if (pread(devfd, secbuf, dl->d_secsize, (off_t)mbroff * dl->d_secsize)
515c462e017Skrw 	    < (ssize_t)sizeof(mbr))
516c462e017Skrw 		err(4, "can't pread boot record");
517b4544c7cSjsing 	bcopy(secbuf, &mbr, sizeof(mbr));
518b4544c7cSjsing 	free(secbuf);
519b4544c7cSjsing 
5201a05cb57Skrw 	if (mbr.dmbr_sign != DOSMBR_SIGNATURE) {
5211a05cb57Skrw 		if (mbroff == DOSBBSECTOR)
5221a05cb57Skrw 			return 0;
523b4544c7cSjsing 		errx(1, "invalid boot record signature (0x%04X) @ sector %u",
524b4544c7cSjsing 		    mbr.dmbr_sign, mbroff);
5251a05cb57Skrw 	}
526b4544c7cSjsing 
527b4544c7cSjsing 	nextebr = 0;
528b4544c7cSjsing 	for (i = 0; i < NDOSPART; i++) {
529b4544c7cSjsing 		dp = &mbr.dmbr_parts[i];
530b4544c7cSjsing 		if (!dp->dp_size)
531b4544c7cSjsing 			continue;
532b4544c7cSjsing 
533b4544c7cSjsing 		if (verbose)
534b4544c7cSjsing 			fprintf(stderr,
535b4544c7cSjsing 			    "\tpartition %d: type 0x%02X offset %u size %u\n",
536b4544c7cSjsing 			    i, dp->dp_typ, dp->dp_start, dp->dp_size);
537b4544c7cSjsing 
538b4544c7cSjsing 		if (dp->dp_typ == DOSPTYP_OPENBSD) {
539b4544c7cSjsing 			if (dp->dp_start > (dp->dp_start + mbroff))
540b4544c7cSjsing 				continue;
541b4544c7cSjsing 			return (dp->dp_start + mbroff);
542b4544c7cSjsing 		}
543b4544c7cSjsing 
544b4544c7cSjsing 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
545b4544c7cSjsing 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
546b4544c7cSjsing 			nextebr = dp->dp_start + mbr_eoff;
547b4544c7cSjsing 			if (nextebr < dp->dp_start)
548b4544c7cSjsing 				nextebr = (u_int)-1;
549b4544c7cSjsing 			if (mbr_eoff == DOSBBSECTOR)
550b4544c7cSjsing 				mbr_eoff = dp->dp_start;
551b4544c7cSjsing 		}
552b4544c7cSjsing 	}
553b4544c7cSjsing 
554b4544c7cSjsing 	if (nextebr && nextebr != (u_int)-1) {
555b4544c7cSjsing 		mbroff = nextebr;
556b4544c7cSjsing 		goto again;
557b4544c7cSjsing 	}
558b4544c7cSjsing 
5591a05cb57Skrw  done:
5601a05cb57Skrw 	errx(1, "no OpenBSD partition");
561b4544c7cSjsing }
562b4544c7cSjsing 
5630461d12bSkrw /*
5640461d12bSkrw  * Returns 0 if the MBR with the provided partition array is a GPT protective
5650461d12bSkrw  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
5660461d12bSkrw  * one MBR partition, an EFI partition that either covers the whole disk or as
5670461d12bSkrw  * much of it as is possible with a 32bit size field.
5680461d12bSkrw  *
5690461d12bSkrw  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
5700461d12bSkrw  */
571b3eeebd8Skrw static int
57212b4e828Skrw gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
5730461d12bSkrw {
5740461d12bSkrw 	struct dos_partition *dp2;
5750461d12bSkrw 	int efi, found, i;
5760461d12bSkrw 	u_int32_t psize;
5770461d12bSkrw 
5780461d12bSkrw 	found = efi = 0;
5790461d12bSkrw 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
5800461d12bSkrw 		if (dp2->dp_typ == DOSPTYP_UNUSED)
5810461d12bSkrw 			continue;
5820461d12bSkrw 		found++;
5830461d12bSkrw 		if (dp2->dp_typ != DOSPTYP_EFI)
5840461d12bSkrw 			continue;
58571c0bb0bSkrw 		if (letoh32(dp2->dp_start) != GPTSECTOR)
58671c0bb0bSkrw 			continue;
5870461d12bSkrw 		psize = letoh32(dp2->dp_size);
58871c0bb0bSkrw 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX)
5890461d12bSkrw 			efi++;
5900461d12bSkrw 	}
5910461d12bSkrw 	if (found == 1 && efi == 1)
5920461d12bSkrw 		return (0);
5930461d12bSkrw 
5940343e5afSkrw 	return (1);
5950461d12bSkrw }
5960461d12bSkrw 
5977528d680Sstsp int
598f66515a4Skrw findgptefisys(int devfd, struct disklabel *dl)
599f66515a4Skrw {
600f66515a4Skrw 	struct gpt_partition	 gp[NGPTPARTITIONS];
601f66515a4Skrw 	struct gpt_header	 gh;
6020461d12bSkrw 	struct dos_partition	 dp[NDOSPART];
603f66515a4Skrw 	struct uuid		 efisys_uuid;
604f66515a4Skrw 	const char		 efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM;
605f66515a4Skrw 	off_t			 off;
606f66515a4Skrw 	ssize_t			 len;
607f66515a4Skrw 	u_int64_t		 start;
608f66515a4Skrw 	int			 i;
609f66515a4Skrw 	uint32_t		 orig_csum, new_csum;
610f66515a4Skrw 	uint32_t		 ghsize, ghpartsize, ghpartnum, ghpartspersec;
611f66515a4Skrw 	u_int8_t		*secbuf;
612f66515a4Skrw 
613f66515a4Skrw 	/* Prepare EFI System UUID */
614f66515a4Skrw 	uuid_dec_be(efisys_uuid_code, &efisys_uuid);
615f66515a4Skrw 
616f66515a4Skrw 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
617f66515a4Skrw 		err(1, NULL);
6180461d12bSkrw 
6190461d12bSkrw 	/* Check that there is a protective MBR. */
6200461d12bSkrw 	len = pread(devfd, secbuf, dl->d_secsize, 0);
6210461d12bSkrw 	if (len != dl->d_secsize)
6220461d12bSkrw 		err(4, "can't read mbr");
6230461d12bSkrw 	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
62412b4e828Skrw 	if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) {
6250461d12bSkrw 		free(secbuf);
6260461d12bSkrw 		return (-1);
6270461d12bSkrw 	}
6280461d12bSkrw 
6290461d12bSkrw 	/* Check GPT Header. */
630f66515a4Skrw 	off = dl->d_secsize;	/* Read header from sector 1. */
631f66515a4Skrw 	len = pread(devfd, secbuf, dl->d_secsize, off);
632f66515a4Skrw 	if (len != dl->d_secsize)
633c462e017Skrw 		err(4, "can't pread gpt header");
634f66515a4Skrw 
635f66515a4Skrw 	memcpy(&gh, secbuf, sizeof(gh));
636f66515a4Skrw 	free(secbuf);
637f66515a4Skrw 
638f66515a4Skrw 	/* Check signature */
639f66515a4Skrw 	if (letoh64(gh.gh_sig) != GPTSIGNATURE)
640f66515a4Skrw 		return (-1);
641f66515a4Skrw 
642f66515a4Skrw 	if (letoh32(gh.gh_rev) != GPTREVISION)
643f66515a4Skrw 		return (-1);
644f66515a4Skrw 
645f66515a4Skrw 	ghsize = letoh32(gh.gh_size);
646f66515a4Skrw 	if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header))
647f66515a4Skrw 		return (-1);
648f66515a4Skrw 
649f66515a4Skrw 	/* Check checksum */
650f66515a4Skrw 	orig_csum = gh.gh_csum;
651f66515a4Skrw 	gh.gh_csum = 0;
652f66515a4Skrw 	new_csum = crc32((unsigned char *)&gh, ghsize);
653f66515a4Skrw 	gh.gh_csum = orig_csum;
654f66515a4Skrw 	if (letoh32(orig_csum) != new_csum)
655f66515a4Skrw 		return (-1);
656f66515a4Skrw 
657f66515a4Skrw 	off = letoh64(gh.gh_part_lba) * dl->d_secsize;
658f66515a4Skrw 	ghpartsize = letoh32(gh.gh_part_size);
659f66515a4Skrw 	ghpartspersec = dl->d_secsize / ghpartsize;
660f66515a4Skrw 	ghpartnum = letoh32(gh.gh_part_num);
661f66515a4Skrw 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
662f66515a4Skrw 		err(1, NULL);
663f66515a4Skrw 	for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) {
664f66515a4Skrw 		len = pread(devfd, secbuf, dl->d_secsize, off);
665c4643c3cSjsg 		if (len != dl->d_secsize) {
666c4643c3cSjsg 			free(secbuf);
667f66515a4Skrw 			return (-1);
668c4643c3cSjsg 		}
669f66515a4Skrw 		memcpy(gp + i * ghpartspersec, secbuf,
670f66515a4Skrw 		    ghpartspersec * sizeof(struct gpt_partition));
671f66515a4Skrw 		off += dl->d_secsize;
672f66515a4Skrw 	}
673f66515a4Skrw 	free(secbuf);
674f66515a4Skrw 	new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize);
675f66515a4Skrw 	if (new_csum != letoh32(gh.gh_part_csum))
676f66515a4Skrw 		return (-1);
677f66515a4Skrw 
678f66515a4Skrw 	start = 0;
679f66515a4Skrw 	for (i = 0; i < ghpartnum && start == 0; i++) {
680f66515a4Skrw 		if (memcmp(&gp[i].gp_type, &efisys_uuid,
681f66515a4Skrw 		    sizeof(struct uuid)) == 0)
682f66515a4Skrw 			start = letoh64(gp[i].gp_lba_start);
683f66515a4Skrw 	}
684f66515a4Skrw 
685f66515a4Skrw 	if (start) {
686f66515a4Skrw 		for (i = 0; i < MAXPARTITIONS; i++) {
687f66515a4Skrw 			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
688f66515a4Skrw 			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
689f66515a4Skrw 				return ('a' + i);
690f66515a4Skrw 		}
691f66515a4Skrw 	}
692f66515a4Skrw 
693f66515a4Skrw 	return (-1);
694f66515a4Skrw }
695f66515a4Skrw 
696b4544c7cSjsing /*
697b4544c7cSjsing  * Load the prototype boot sector (biosboot) into memory.
698b4544c7cSjsing  */
699b4544c7cSjsing static char *
700b4544c7cSjsing loadproto(char *fname, long *size)
701b4544c7cSjsing {
702b4544c7cSjsing 	int	fd;
703b4544c7cSjsing 	size_t	tdsize;		/* text+data size */
704b4544c7cSjsing 	char	*bp;
705b4544c7cSjsing 	Elf_Ehdr eh;
706b4544c7cSjsing 	Elf_Word phsize;
707b4544c7cSjsing 	Elf_Phdr *ph;
708b4544c7cSjsing 
709df69c215Sderaadt 	if ((fd = open(fname, O_RDONLY)) == -1)
710b4544c7cSjsing 		err(1, "%s", fname);
711b4544c7cSjsing 
712b4544c7cSjsing 	if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
713b4544c7cSjsing 		errx(1, "%s: read failed", fname);
714b4544c7cSjsing 
715b4544c7cSjsing 	if (!IS_ELF(eh))
716b4544c7cSjsing 		errx(1, "%s: bad magic: 0x%02x%02x%02x%02x", fname,
717b4544c7cSjsing 		    eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
718b4544c7cSjsing 		    eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
719b4544c7cSjsing 
720b4544c7cSjsing 	/*
721b4544c7cSjsing 	 * We have to include the exec header in the beginning of
722b4544c7cSjsing 	 * the buffer, and leave extra space at the end in case
723b4544c7cSjsing 	 * the actual write to disk wants to skip the header.
724b4544c7cSjsing 	 */
725b4544c7cSjsing 
726b4544c7cSjsing 	/* Program load header. */
727b4544c7cSjsing 	if (eh.e_phnum != 1)
728b4544c7cSjsing 		errx(1, "%s: %u ELF load sections (only support 1)",
729b4544c7cSjsing 		    fname, eh.e_phnum);
730b4544c7cSjsing 
73186c5fefbSderaadt 	ph = reallocarray(NULL, eh.e_phnum, sizeof(Elf_Phdr));
732b4544c7cSjsing 	if (ph == NULL)
733b4544c7cSjsing 		err(1, NULL);
73486c5fefbSderaadt 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
735b4544c7cSjsing 
736c462e017Skrw 	if (pread(fd, ph, phsize, eh.e_phoff) != phsize)
737c462e017Skrw 		errx(1, "%s: can't pread header", fname);
738b4544c7cSjsing 
739b4544c7cSjsing 	tdsize = ph->p_filesz;
740b4544c7cSjsing 
741b4544c7cSjsing 	/*
742b4544c7cSjsing 	 * Allocate extra space here because the caller may copy
743b4544c7cSjsing 	 * the boot block starting at the end of the exec header.
744b4544c7cSjsing 	 * This prevents reading beyond the end of the buffer.
745b4544c7cSjsing 	 */
746b4544c7cSjsing 	if ((bp = calloc(tdsize, 1)) == NULL)
747b4544c7cSjsing 		err(1, NULL);
748b4544c7cSjsing 
749b4544c7cSjsing 	/* Read the rest of the file. */
750c462e017Skrw 	if (pread(fd, bp, tdsize, ph->p_offset) != (ssize_t)tdsize)
751c462e017Skrw 		errx(1, "%s: pread failed", fname);
752b4544c7cSjsing 
753b4544c7cSjsing 	*size = tdsize;	/* not aligned to DEV_BSIZE */
754b4544c7cSjsing 
755b4544c7cSjsing 	close(fd);
756b4544c7cSjsing 	return bp;
757b4544c7cSjsing }
758b4544c7cSjsing 
759b4544c7cSjsing static void
760b4544c7cSjsing devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
761b4544c7cSjsing {
762c462e017Skrw 	if (pread(fd, buf, size, dbtob((off_t)blk)) != (ssize_t)size)
763c462e017Skrw 		err(1, "%s: devread: pread", msg);
764b4544c7cSjsing }
765b4544c7cSjsing 
766b4544c7cSjsing /*
767b4544c7cSjsing  * Read information about /boot's inode, then put this and filesystem
768b4544c7cSjsing  * parameters from the superblock into pbr_symbols.
769b4544c7cSjsing  */
770b4544c7cSjsing static int
771b4544c7cSjsing getbootparams(char *boot, int devfd, struct disklabel *dl)
772b4544c7cSjsing {
773b4544c7cSjsing 	int		fd;
774b4544c7cSjsing 	struct stat	dsb, fsb;
775b4544c7cSjsing 	struct statfs	fssb;
776b4544c7cSjsing 	struct partition *pp;
777b4544c7cSjsing 	struct fs	*fs;
77875cc614cSnaddy 	char		*sblock, *buf;
779b4544c7cSjsing 	u_int		blk, *ap;
780b4544c7cSjsing 	int		ndb;
781b4544c7cSjsing 	int		mib[3];
782b4544c7cSjsing 	size_t		size;
783b4544c7cSjsing 	dev_t		dev;
784fe786928Sotto 	int		incr;
785b4544c7cSjsing 
786b4544c7cSjsing 	/*
787b4544c7cSjsing 	 * Open 2nd-level boot program and record enough details about
788b4544c7cSjsing 	 * where it is on the filesystem represented by `devfd'
789b4544c7cSjsing 	 * (inode block, offset within that block, and various filesystem
790b4544c7cSjsing 	 * parameters essentially taken from the superblock) for biosboot
791b4544c7cSjsing 	 * to be able to load it later.
792b4544c7cSjsing 	 */
793b4544c7cSjsing 
79468507edaSjsing 	/* Make sure the (probably new) boot file is on disk. */
79568507edaSjsing 	sync(); sleep(1);
796b4544c7cSjsing 
797df69c215Sderaadt 	if ((fd = open(boot, O_RDONLY)) == -1)
798b4544c7cSjsing 		err(1, "open: %s", boot);
799b4544c7cSjsing 
800df69c215Sderaadt 	if (fstatfs(fd, &fssb) == -1)
801b4544c7cSjsing 		err(1, "statfs: %s", boot);
802b4544c7cSjsing 
803b4544c7cSjsing 	if (strncmp(fssb.f_fstypename, "ffs", MFSNAMELEN) &&
804b4544c7cSjsing 	    strncmp(fssb.f_fstypename, "ufs", MFSNAMELEN) )
805b4544c7cSjsing 		errx(1, "%s: not on an FFS filesystem", boot);
806b4544c7cSjsing 
807b4544c7cSjsing #if 0
808b4544c7cSjsing 	if (read(fd, &eh, sizeof(eh)) != sizeof(eh))
809b4544c7cSjsing 		errx(1, "read: %s", boot);
810b4544c7cSjsing 
811b4544c7cSjsing 	if (!IS_ELF(eh)) {
812b4544c7cSjsing 		errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
813b4544c7cSjsing 		    boot,
814b4544c7cSjsing 		    eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
815b4544c7cSjsing 		    eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
816b4544c7cSjsing 	}
817b4544c7cSjsing #endif
818b4544c7cSjsing 
819b4544c7cSjsing 	if (fsync(fd) != 0)
820b4544c7cSjsing 		err(1, "fsync: %s", boot);
821b4544c7cSjsing 
822b4544c7cSjsing 	if (fstat(fd, &fsb) != 0)
823b4544c7cSjsing 		err(1, "fstat: %s", boot);
824b4544c7cSjsing 
825b4544c7cSjsing 	if (fstat(devfd, &dsb) != 0)
826b4544c7cSjsing 		err(1, "fstat: %d", devfd);
827b4544c7cSjsing 
828b4544c7cSjsing 	/* Check devices. */
829b4544c7cSjsing 	mib[0] = CTL_MACHDEP;
830b4544c7cSjsing 	mib[1] = CPU_CHR2BLK;
831b4544c7cSjsing 	mib[2] = dsb.st_rdev;
832b4544c7cSjsing 	size = sizeof(dev);
833b4544c7cSjsing 	if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0) {
834b4544c7cSjsing 		if (fsb.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS)
835b4544c7cSjsing 			errx(1, "cross-device install");
836b4544c7cSjsing 	}
837b4544c7cSjsing 
838b4544c7cSjsing 	pp = &dl->d_partitions[DISKPART(fsb.st_dev)];
839b4544c7cSjsing 	close(fd);
840b4544c7cSjsing 
84175cc614cSnaddy 	if ((sblock = malloc(SBSIZE)) == NULL)
84275cc614cSnaddy 		err(1, NULL);
84375cc614cSnaddy 
844fe786928Sotto 	sbread(devfd, DL_SECTOBLK(dl, pp->p_offset), &fs, sblock);
845b4544c7cSjsing 
846b4544c7cSjsing 	/* Read inode. */
847b4544c7cSjsing 	if ((buf = malloc(fs->fs_bsize)) == NULL)
848b4544c7cSjsing 		err(1, NULL);
849b4544c7cSjsing 
850b4544c7cSjsing 	blk = fsbtodb(fs, ino_to_fsba(fs, fsb.st_ino));
851b4544c7cSjsing 
852b4544c7cSjsing 	/*
853b4544c7cSjsing 	 * Have the inode.  Figure out how many filesystem blocks (not disk
854b4544c7cSjsing 	 * sectors) there are for biosboot to load.
855b4544c7cSjsing 	 */
856fe786928Sotto 	devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk,
857fe786928Sotto 	    fs->fs_bsize, "inode");
858fe786928Sotto 	if (fs->fs_magic == FS_UFS2_MAGIC) {
859fe786928Sotto 		struct ufs2_dinode *ip2 = (struct ufs2_dinode *)(buf) +
860fe786928Sotto 		    ino_to_fsbo(fs, fsb.st_ino);
861fe786928Sotto 		ndb = howmany(ip2->di_size, fs->fs_bsize);
862fe786928Sotto 		ap = (u_int *)ip2->di_db;
863fe786928Sotto 		incr = sizeof(u_int32_t);
864fe786928Sotto 	} else {
865fe786928Sotto 		struct ufs1_dinode *ip1 = (struct ufs1_dinode *)(buf) +
866fe786928Sotto 		    ino_to_fsbo(fs, fsb.st_ino);
867fe786928Sotto 		ndb = howmany(ip1->di_size, fs->fs_bsize);
868fe786928Sotto 		ap = (u_int *)ip1->di_db;
869fe786928Sotto 		incr = 0;
870fe786928Sotto 	}
871fe786928Sotto 
872b4544c7cSjsing 	if (ndb <= 0)
873b4544c7cSjsing 		errx(1, "No blocks to load");
874b4544c7cSjsing 
875b4544c7cSjsing 	/*
876b4544c7cSjsing 	 * Now set the values that will need to go into biosboot
877b4544c7cSjsing 	 * (the partition boot record, a.k.a. the PBR).
878b4544c7cSjsing 	 */
879b4544c7cSjsing 	sym_set_value(pbr_symbols, "_fs_bsize_p", (fs->fs_bsize / 16));
880b4544c7cSjsing 	sym_set_value(pbr_symbols, "_fs_bsize_s", (fs->fs_bsize /
881b4544c7cSjsing 	    dl->d_secsize));
882b4544c7cSjsing 
883b4544c7cSjsing 	/*
884b4544c7cSjsing 	 * fs_fsbtodb is the shift to convert fs_fsize to DEV_BSIZE. The
885b4544c7cSjsing 	 * ino_to_fsba() return value is the number of fs_fsize units.
886b4544c7cSjsing 	 * Calculate the shift to convert fs_fsize into physical sectors,
887b4544c7cSjsing 	 * which are added to p_offset to get the sector address BIOS
888b4544c7cSjsing 	 * will use.
889b4544c7cSjsing 	 *
890b4544c7cSjsing 	 * N.B.: ASSUMES fs_fsize is a power of 2 of d_secsize.
891b4544c7cSjsing 	 */
892b4544c7cSjsing 	sym_set_value(pbr_symbols, "_fsbtodb",
893b4544c7cSjsing 	    ffs(fs->fs_fsize / dl->d_secsize) - 1);
894b4544c7cSjsing 
895b4544c7cSjsing 	sym_set_value(pbr_symbols, "_p_offset", pp->p_offset);
896b4544c7cSjsing 	sym_set_value(pbr_symbols, "_inodeblk",
897b4544c7cSjsing 	    ino_to_fsba(fs, fsb.st_ino));
898b4544c7cSjsing 	sym_set_value(pbr_symbols, "_inodedbl",
899b4544c7cSjsing 	    ((((char *)ap) - buf) + INODEOFF));
900b4544c7cSjsing 	sym_set_value(pbr_symbols, "_nblocks", ndb);
901fe786928Sotto 	sym_set_value(pbr_symbols, "_blkincr", incr);
902b4544c7cSjsing 
903b4544c7cSjsing 	if (verbose) {
904b4544c7cSjsing 		fprintf(stderr, "%s is %d blocks x %d bytes\n",
905b4544c7cSjsing 		    boot, ndb, fs->fs_bsize);
906b4544c7cSjsing 		fprintf(stderr, "fs block shift %u; part offset %u; "
907b4544c7cSjsing 		    "inode block %lld, offset %u\n",
908b4544c7cSjsing 		    ffs(fs->fs_fsize / dl->d_secsize) - 1,
909b4544c7cSjsing 		    pp->p_offset,
910b4544c7cSjsing 		    ino_to_fsba(fs, fsb.st_ino),
911b4544c7cSjsing 		    (unsigned int)((((char *)ap) - buf) + INODEOFF));
912fe786928Sotto 		fprintf(stderr, "expecting %d-bit fs blocks (incr %d)\n",
913fe786928Sotto 		    incr ? 64 : 32, incr);
914b4544c7cSjsing 	}
915b4544c7cSjsing 
91675cc614cSnaddy 	free (sblock);
91742554ba7Skrw 	free (buf);
91842554ba7Skrw 
919b4544c7cSjsing 	return 0;
920b4544c7cSjsing }
921b4544c7cSjsing 
922b4544c7cSjsing void
923b4544c7cSjsing sym_set_value(struct sym_data *sym_list, char *sym, u_int32_t value)
924b4544c7cSjsing {
925b4544c7cSjsing 	struct sym_data *p;
926b4544c7cSjsing 
927b4544c7cSjsing 	for (p = sym_list; p->sym_name != NULL; p++) {
928b4544c7cSjsing 		if (strcmp(p->sym_name, sym) == 0)
929b4544c7cSjsing 			break;
930b4544c7cSjsing 	}
931b4544c7cSjsing 
932b4544c7cSjsing 	if (p->sym_name == NULL)
933b4544c7cSjsing 		errx(1, "%s: no such symbol", sym);
934b4544c7cSjsing 
935b4544c7cSjsing 	p->sym_value = value;
936b4544c7cSjsing 	p->sym_set = 1;
937b4544c7cSjsing }
938b4544c7cSjsing 
939b4544c7cSjsing /*
940b4544c7cSjsing  * Write the parameters stored in sym_list into the in-memory copy of
941b4544c7cSjsing  * the prototype biosboot (proto), ready for it to be written to disk.
942b4544c7cSjsing  */
943b4544c7cSjsing void
944b4544c7cSjsing pbr_set_symbols(char *fname, char *proto, struct sym_data *sym_list)
945b4544c7cSjsing {
946b4544c7cSjsing 	struct sym_data *sym;
947b4544c7cSjsing 	struct nlist	*nl;
948b4544c7cSjsing 	char		*vp;
949b4544c7cSjsing 	u_int32_t	*lp;
950b4544c7cSjsing 	u_int16_t	*wp;
951b4544c7cSjsing 	u_int8_t	*bp;
952b4544c7cSjsing 
953b4544c7cSjsing 	for (sym = sym_list; sym->sym_name != NULL; sym++) {
954b4544c7cSjsing 		if (!sym->sym_set)
955b4544c7cSjsing 			errx(1, "%s not set", sym->sym_name);
956b4544c7cSjsing 
957b4544c7cSjsing 		/* Allocate space for 2; second is null-terminator for list. */
958b4544c7cSjsing 		nl = calloc(2, sizeof(struct nlist));
959b4544c7cSjsing 		if (nl == NULL)
960b4544c7cSjsing 			err(1, NULL);
961b4544c7cSjsing 
962b4544c7cSjsing 		nl->n_name = sym->sym_name;
963b4544c7cSjsing 
964b4544c7cSjsing 		if (nlist_elf32(fname, nl) != 0)
965b4544c7cSjsing 			errx(1, "%s: symbol %s not found",
966b4544c7cSjsing 			    fname, sym->sym_name);
967b4544c7cSjsing 
968b4544c7cSjsing 		if (nl->n_type != (N_TEXT))
969b4544c7cSjsing 			errx(1, "%s: %s: wrong type (%x)",
970b4544c7cSjsing 			    fname, sym->sym_name, nl->n_type);
971b4544c7cSjsing 
972b4544c7cSjsing 		/* Get a pointer to where the symbol's value needs to go. */
973b4544c7cSjsing 		vp = proto + nl->n_value;
974b4544c7cSjsing 
975b4544c7cSjsing 		switch (sym->sym_size) {
976b4544c7cSjsing 		case 4:					/* u_int32_t */
977b4544c7cSjsing 			lp = (u_int32_t *) vp;
978b4544c7cSjsing 			*lp = sym->sym_value;
979b4544c7cSjsing 			break;
980b4544c7cSjsing 		case 2:					/* u_int16_t */
981b4544c7cSjsing 			if (sym->sym_value >= 0x10000)	/* out of range */
982b4544c7cSjsing 				errx(1, "%s: symbol out of range (%u)",
983b4544c7cSjsing 				    sym->sym_name, sym->sym_value);
984b4544c7cSjsing 			wp = (u_int16_t *) vp;
985b4544c7cSjsing 			*wp = (u_int16_t) sym->sym_value;
986b4544c7cSjsing 			break;
987b4544c7cSjsing 		case 1:					/* u_int16_t */
988b4544c7cSjsing 			if (sym->sym_value >= 0x100)	/* out of range */
989b4544c7cSjsing 				errx(1, "%s: symbol out of range (%u)",
990b4544c7cSjsing 				    sym->sym_name, sym->sym_value);
991b4544c7cSjsing 			bp = (u_int8_t *) vp;
992b4544c7cSjsing 			*bp = (u_int8_t) sym->sym_value;
993b4544c7cSjsing 			break;
994b4544c7cSjsing 		default:
995b4544c7cSjsing 			errx(1, "%s: bad symbol size %d",
996b4544c7cSjsing 			    sym->sym_name, sym->sym_size);
997b4544c7cSjsing 			/* NOTREACHED */
998b4544c7cSjsing 		}
999b4544c7cSjsing 
1000b4544c7cSjsing 		free(nl);
1001b4544c7cSjsing 	}
1002b4544c7cSjsing }
1003fe786928Sotto 
1004fe786928Sotto static int
1005fe786928Sotto sbchk(struct fs *fs, daddr_t sbloc)
1006fe786928Sotto {
1007fe786928Sotto 	if (verbose)
1008fe786928Sotto 		fprintf(stderr, "looking for superblock at %lld\n", sbloc);
1009fe786928Sotto 
1010fe786928Sotto 	if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) {
1011fe786928Sotto 		if (verbose)
1012fe786928Sotto 			fprintf(stderr, "bad superblock magic 0x%x\n",
1013fe786928Sotto 			    fs->fs_magic);
1014fe786928Sotto 		return (0);
1015fe786928Sotto 	}
1016fe786928Sotto 
1017fe786928Sotto 	/*
1018fe786928Sotto 	 * Looking for an FFS1 file system at SBLOCK_UFS2 will find the
1019fe786928Sotto 	 * wrong superblock for file systems with 64k block size.
1020fe786928Sotto 	 */
1021fe786928Sotto 	if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) {
1022fe786928Sotto 		if (verbose)
1023fe786928Sotto 			fprintf(stderr, "skipping ffs1 superblock at %lld\n",
1024fe786928Sotto 			    sbloc);
1025fe786928Sotto 		return (0);
1026fe786928Sotto 	}
1027fe786928Sotto 
1028fe786928Sotto 	if (fs->fs_bsize <= 0 || fs->fs_bsize < sizeof(struct fs) ||
1029fe786928Sotto 	    fs->fs_bsize > MAXBSIZE) {
1030fe786928Sotto 		if (verbose)
1031fe786928Sotto 			fprintf(stderr, "invalid superblock block size %d\n",
1032fe786928Sotto 			    fs->fs_bsize);
1033fe786928Sotto 		return (0);
1034fe786928Sotto 	}
1035fe786928Sotto 
1036fe786928Sotto 	if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) {
1037fe786928Sotto 		if (verbose)
1038fe786928Sotto 			fprintf(stderr, "invalid superblock size %d\n",
1039fe786928Sotto 			    fs->fs_sbsize);
1040fe786928Sotto 		return (0);
1041fe786928Sotto 	}
1042fe786928Sotto 
1043fe786928Sotto 	if (fs->fs_inopb <= 0) {
1044fe786928Sotto 		if (verbose)
1045fe786928Sotto 			fprintf(stderr, "invalid superblock inodes/block %d\n",
1046fe786928Sotto 			    fs->fs_inopb);
1047fe786928Sotto 		return (0);
1048fe786928Sotto 	}
1049fe786928Sotto 
1050fe786928Sotto 	if (verbose)
1051fe786928Sotto 		fprintf(stderr, "found valid %s superblock\n",
1052fe786928Sotto 		    fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1");
1053fe786928Sotto 
1054fe786928Sotto 	return (1);
1055fe786928Sotto }
1056fe786928Sotto 
1057fe786928Sotto static void
1058fe786928Sotto sbread(int fd, daddr_t poffset, struct fs **fs, char *sblock)
1059fe786928Sotto {
1060fe786928Sotto 	int i;
1061fe786928Sotto 	daddr_t sboff;
1062fe786928Sotto 
1063fe786928Sotto 	for (i = 0; sbtry[i] != -1; i++) {
1064fe786928Sotto 		sboff = sbtry[i] / DEV_BSIZE;
1065fe786928Sotto 		devread(fd, sblock, poffset + sboff, SBSIZE, "superblock");
1066fe786928Sotto 		*fs = (struct fs *)sblock;
1067fe786928Sotto 		if (sbchk(*fs, sbtry[i]))
1068fe786928Sotto 			break;
1069fe786928Sotto 	}
1070fe786928Sotto 
1071fe786928Sotto 	if (sbtry[i] == -1)
1072fe786928Sotto 		errx(1, "couldn't find ffs superblock");
1073fe786928Sotto }
1074