xref: /openbsd-src/usr.sbin/installboot/efi_installboot.c (revision db15c72a1929803b548b8f444dbcdc667e3d1ad2)
1*db15c72aSkettenis /*	$OpenBSD: efi_installboot.c,v 1.12 2024/11/08 10:43:07 kettenis Exp $	*/
2db32265bSvisa /*	$NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
3db32265bSvisa 
4db32265bSvisa /*
5db32265bSvisa  * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
6db32265bSvisa  * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
7db32265bSvisa  * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
8db32265bSvisa  * Copyright (c) 1997 Michael Shalayeff
9db32265bSvisa  * Copyright (c) 1994 Paul Kranenburg
10db32265bSvisa  * All rights reserved.
11db32265bSvisa  *
12db32265bSvisa  * Redistribution and use in source and binary forms, with or without
13db32265bSvisa  * modification, are permitted provided that the following conditions
14db32265bSvisa  * are met:
15db32265bSvisa  * 1. Redistributions of source code must retain the above copyright
16db32265bSvisa  *    notice, this list of conditions and the following disclaimer.
17db32265bSvisa  * 2. Redistributions in binary form must reproduce the above copyright
18db32265bSvisa  *    notice, this list of conditions and the following disclaimer in the
19db32265bSvisa  *    documentation and/or other materials provided with the distribution.
20db32265bSvisa  * 3. All advertising materials mentioning features or use of this software
21db32265bSvisa  *    must display the following acknowledgement:
22db32265bSvisa  *      This product includes software developed by Paul Kranenburg.
23db32265bSvisa  * 4. The name of the author may not be used to endorse or promote products
24db32265bSvisa  *    derived from this software without specific prior written permission
25db32265bSvisa  *
26db32265bSvisa  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27db32265bSvisa  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28db32265bSvisa  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29db32265bSvisa  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30db32265bSvisa  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31db32265bSvisa  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32db32265bSvisa  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33db32265bSvisa  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34db32265bSvisa  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35db32265bSvisa  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36db32265bSvisa  */
37db32265bSvisa 
38db32265bSvisa #include <sys/param.h>	/* DEV_BSIZE */
39db32265bSvisa #include <sys/disklabel.h>
40db32265bSvisa #include <sys/dkio.h>
41db32265bSvisa #include <sys/ioctl.h>
42db32265bSvisa #include <sys/mount.h>
43db32265bSvisa #include <sys/stat.h>
44db32265bSvisa 
45db32265bSvisa #include <err.h>
46db32265bSvisa #include <errno.h>
47db32265bSvisa #include <fcntl.h>
48db32265bSvisa #include <stdlib.h>
49db32265bSvisa #include <stdio.h>
50db32265bSvisa #include <stdint.h>
51db32265bSvisa #include <string.h>
52db32265bSvisa #include <unistd.h>
53db32265bSvisa #include <util.h>
54db32265bSvisa #include <uuid.h>
55db32265bSvisa 
56db32265bSvisa #include "installboot.h"
57db32265bSvisa 
584a5d5189Svisa #if defined(__aarch64__)
594a5d5189Svisa #define BOOTEFI_SRC	"BOOTAA64.EFI"
604a5d5189Svisa #define BOOTEFI_DST	"bootaa64.efi"
614a5d5189Svisa #elif defined(__arm__)
624a5d5189Svisa #define BOOTEFI_SRC	"BOOTARM.EFI"
634a5d5189Svisa #define BOOTEFI_DST	"bootarm.efi"
644a5d5189Svisa #elif defined(__riscv)
654a5d5189Svisa #define BOOTEFI_SRC	"BOOTRISCV64.EFI"
664a5d5189Svisa #define BOOTEFI_DST	"bootriscv64.efi"
674a5d5189Svisa #else
684a5d5189Svisa #error "unhandled architecture"
694a5d5189Svisa #endif
704a5d5189Svisa 
71db32265bSvisa static int	create_filesystem(struct disklabel *, char);
72db32265bSvisa static void	write_filesystem(struct disklabel *, char);
738fb3e437Stobhe static int	write_firmware(const char *, const char *);
74db32265bSvisa static int	findgptefisys(int, struct disklabel *);
75db32265bSvisa static int	findmbrfat(int, struct disklabel *);
76db32265bSvisa 
77db32265bSvisa void
78db32265bSvisa md_init(void)
79db32265bSvisa {
8022942c2fSkn 	stages = 1;
8122942c2fSkn 	stage1 = "/usr/mdec/" BOOTEFI_SRC;
82db32265bSvisa }
83db32265bSvisa 
84db32265bSvisa void
85db32265bSvisa md_loadboot(void)
86db32265bSvisa {
87db32265bSvisa }
88db32265bSvisa 
89db32265bSvisa void
90db32265bSvisa md_prepareboot(int devfd, char *dev)
91db32265bSvisa {
92db32265bSvisa 	struct disklabel dl;
93db32265bSvisa 	int part;
94db32265bSvisa 
95db32265bSvisa 	/* Get and check disklabel. */
96db32265bSvisa 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
97db32265bSvisa 		err(1, "disklabel: %s", dev);
98db32265bSvisa 	if (dl.d_magic != DISKMAGIC)
99db32265bSvisa 		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
100db32265bSvisa 
101db32265bSvisa 	/* Warn on unknown disklabel types. */
102db32265bSvisa 	if (dl.d_type == 0)
103db32265bSvisa 		warnx("disklabel type unknown");
104db32265bSvisa 
105db32265bSvisa 	part = findgptefisys(devfd, &dl);
10614cbb8d0Skn 	if (part != -1) {
10714cbb8d0Skn 		create_filesystem(&dl, (char)part);
10814cbb8d0Skn 		return;
10914cbb8d0Skn 	}
11014cbb8d0Skn 
111db32265bSvisa 	part = findmbrfat(devfd, &dl);
112db32265bSvisa 	if (part != -1) {
11314cbb8d0Skn 		create_filesystem(&dl, (char)part);
11414cbb8d0Skn 		return;
115db32265bSvisa 	}
116db32265bSvisa }
117db32265bSvisa 
118db32265bSvisa void
119db32265bSvisa md_installboot(int devfd, char *dev)
120db32265bSvisa {
121db32265bSvisa 	struct disklabel dl;
122db32265bSvisa 	int part;
123db32265bSvisa 
124db32265bSvisa 	/* Get and check disklabel. */
125db32265bSvisa 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
126db32265bSvisa 		err(1, "disklabel: %s", dev);
127db32265bSvisa 	if (dl.d_magic != DISKMAGIC)
128db32265bSvisa 		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
129db32265bSvisa 
130db32265bSvisa 	/* Warn on unknown disklabel types. */
131db32265bSvisa 	if (dl.d_type == 0)
132db32265bSvisa 		warnx("disklabel type unknown");
133db32265bSvisa 
134db32265bSvisa 	part = findgptefisys(devfd, &dl);
135db32265bSvisa 	if (part != -1) {
136db32265bSvisa 		write_filesystem(&dl, (char)part);
137db32265bSvisa 		return;
138db32265bSvisa 	}
139db32265bSvisa 
140db32265bSvisa 	part = findmbrfat(devfd, &dl);
141db32265bSvisa 	if (part != -1) {
142db32265bSvisa 		write_filesystem(&dl, (char)part);
143db32265bSvisa 		return;
144db32265bSvisa 	}
145db32265bSvisa }
146db32265bSvisa 
147db32265bSvisa static int
148db32265bSvisa create_filesystem(struct disklabel *dl, char part)
149db32265bSvisa {
1507a17f38cSkrw 	static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null";
151db32265bSvisa 	struct msdosfs_args args;
152db32265bSvisa 	char cmd[60];
153db32265bSvisa 	int rslt;
154db32265bSvisa 
155acd6e620Skn 	/* Newfs <duid>.<part> as msdos filesystem. */
156db32265bSvisa 	memset(&args, 0, sizeof(args));
157db32265bSvisa 	rslt = asprintf(&args.fspec,
158db32265bSvisa 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
159db32265bSvisa             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
160db32265bSvisa             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
161db32265bSvisa 	    part);
162db32265bSvisa 	if (rslt == -1) {
163db32265bSvisa 		warn("bad special device");
164db32265bSvisa 		return rslt;
165db32265bSvisa 	}
166db32265bSvisa 
167db32265bSvisa 	rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
168db32265bSvisa 	if (rslt >= sizeof(cmd)) {
169db32265bSvisa 		warnx("can't build newfs command");
170993e4236Skrw 		free(args.fspec);
171db32265bSvisa 		rslt = -1;
172db32265bSvisa 		return rslt;
173db32265bSvisa 	}
174db32265bSvisa 
175db32265bSvisa 	if (verbose)
176db32265bSvisa 		fprintf(stderr, "%s %s\n",
177db32265bSvisa 		    (nowrite ? "would newfs" : "newfsing"), args.fspec);
178db32265bSvisa 	if (!nowrite) {
179db32265bSvisa 		rslt = system(cmd);
180db32265bSvisa 		if (rslt == -1) {
181db32265bSvisa 			warn("system('%s') failed", cmd);
182993e4236Skrw 			free(args.fspec);
183db32265bSvisa 			return rslt;
184db32265bSvisa 		}
185db32265bSvisa 	}
186db32265bSvisa 
187993e4236Skrw 	free(args.fspec);
188db32265bSvisa 	return 0;
189db32265bSvisa }
190db32265bSvisa 
191db32265bSvisa static void
192db32265bSvisa write_filesystem(struct disklabel *dl, char part)
193db32265bSvisa {
1947a17f38cSkrw 	static const char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null";
195db32265bSvisa 	struct msdosfs_args args;
196*db15c72aSkettenis 	struct statfs sf;
197db32265bSvisa 	char cmd[60];
198db32265bSvisa 	char dst[PATH_MAX];
199db32265bSvisa 	char *src;
200db32265bSvisa 	size_t mntlen, pathlen, srclen;
201db32265bSvisa 	int rslt;
202db32265bSvisa 
203db32265bSvisa 	src = NULL;
204db32265bSvisa 
205db32265bSvisa 	/* Create directory for temporary mount point. */
206db32265bSvisa 	strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
207db32265bSvisa 	if (mkdtemp(dst) == NULL)
208db32265bSvisa 		err(1, "mkdtemp('%s') failed", dst);
209db32265bSvisa 	mntlen = strlen(dst);
210db32265bSvisa 
211db32265bSvisa 	/* Mount <duid>.<part> as msdos filesystem. */
212db32265bSvisa 	memset(&args, 0, sizeof(args));
213db32265bSvisa 	rslt = asprintf(&args.fspec,
214db32265bSvisa 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
215db32265bSvisa             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
216db32265bSvisa             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
217db32265bSvisa 	    part);
218db32265bSvisa 	if (rslt == -1) {
219db32265bSvisa 		warn("bad special device");
220db32265bSvisa 		goto rmdir;
221db32265bSvisa 	}
222db32265bSvisa 
223db32265bSvisa 	args.export_info.ex_root = -2;
224db32265bSvisa 	args.export_info.ex_flags = 0;
225db32265bSvisa 	args.flags = MSDOSFSMNT_LONGNAME;
226db32265bSvisa 
227db32265bSvisa 	if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
228db32265bSvisa 		/* Try fsck'ing it. */
229db32265bSvisa 		rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
230db32265bSvisa 		if (rslt >= sizeof(cmd)) {
231db32265bSvisa 			warnx("can't build fsck command");
232db32265bSvisa 			rslt = -1;
233db32265bSvisa 			goto rmdir;
234db32265bSvisa 		}
235db32265bSvisa 		rslt = system(cmd);
236db32265bSvisa 		if (rslt == -1) {
237db32265bSvisa 			warn("system('%s') failed", cmd);
238db32265bSvisa 			goto rmdir;
239db32265bSvisa 		}
240db32265bSvisa 		if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
241db32265bSvisa 			/* Try newfs'ing it. */
242db32265bSvisa 			rslt = create_filesystem(dl, part);
243db32265bSvisa 			if (rslt == -1)
244db32265bSvisa 				goto rmdir;
245db32265bSvisa 			rslt = mount(MOUNT_MSDOS, dst, 0, &args);
246db32265bSvisa 			if (rslt == -1) {
247db32265bSvisa 				warn("unable to mount EFI System partition");
248db32265bSvisa 				goto rmdir;
249db32265bSvisa 			}
250db32265bSvisa 		}
251db32265bSvisa 	}
252db32265bSvisa 
253db32265bSvisa 	/* Create "/efi/boot" directory in <duid>.<part>. */
254db32265bSvisa 	if (strlcat(dst, "/efi", sizeof(dst)) >= sizeof(dst)) {
255db32265bSvisa 		rslt = -1;
256db32265bSvisa 		warn("unable to build /efi directory");
257db32265bSvisa 		goto umount;
258db32265bSvisa 	}
259db32265bSvisa 	rslt = mkdir(dst, 0755);
260db32265bSvisa 	if (rslt == -1 && errno != EEXIST) {
261db32265bSvisa 		warn("mkdir('%s') failed", dst);
262db32265bSvisa 		goto umount;
263db32265bSvisa 	}
264db32265bSvisa 	if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) {
265db32265bSvisa 		rslt = -1;
266db32265bSvisa 		warn("unable to build /boot directory");
267db32265bSvisa 		goto umount;
268db32265bSvisa 	}
269db32265bSvisa 	rslt = mkdir(dst, 0755);
270db32265bSvisa 	if (rslt == -1 && errno != EEXIST) {
271db32265bSvisa 		warn("mkdir('%s') failed", dst);
272db32265bSvisa 		goto umount;
273db32265bSvisa 	}
274db32265bSvisa 
2754a5d5189Svisa 	/* Copy EFI bootblocks to /efi/boot/. */
276db32265bSvisa 	pathlen = strlen(dst);
2774a5d5189Svisa 	if (strlcat(dst, "/" BOOTEFI_DST, sizeof(dst)) >= sizeof(dst)) {
278db32265bSvisa 		rslt = -1;
2794a5d5189Svisa 		warn("unable to build /%s path", BOOTEFI_DST);
280db32265bSvisa 		goto umount;
281db32265bSvisa 	}
2824a5d5189Svisa 	src = fileprefix(root, "/usr/mdec/" BOOTEFI_SRC);
283db32265bSvisa 	if (src == NULL) {
284db32265bSvisa 		rslt = -1;
285db32265bSvisa 		goto umount;
286db32265bSvisa 	}
287db32265bSvisa 	srclen = strlen(src);
288db32265bSvisa 	if (verbose)
289db32265bSvisa 		fprintf(stderr, "%s %s to %s\n",
290db32265bSvisa 		    (nowrite ? "would copy" : "copying"), src, dst);
291db32265bSvisa 	if (!nowrite) {
292db32265bSvisa 		rslt = filecopy(src, dst);
293db32265bSvisa 		if (rslt == -1)
294db32265bSvisa 			goto umount;
295db32265bSvisa 	}
296db32265bSvisa 
2974a5d5189Svisa 	/* Write /efi/boot/startup.nsh. */
2984a5d5189Svisa 	dst[pathlen] = '\0';
2994a5d5189Svisa 	if (strlcat(dst, "/startup.nsh", sizeof(dst)) >= sizeof(dst)) {
3004a5d5189Svisa 		rslt = -1;
3014a5d5189Svisa 		warn("unable to build /startup.nsh path");
3024a5d5189Svisa 		goto umount;
3034a5d5189Svisa 	}
3044a5d5189Svisa 	if (verbose)
3054a5d5189Svisa 		fprintf(stderr, "%s %s\n",
3064a5d5189Svisa 		    (nowrite ? "would write" : "writing"), dst);
3074a5d5189Svisa 	if (!nowrite) {
3084a5d5189Svisa 		rslt = fileprintf(dst, "%s\n", BOOTEFI_DST);
3094a5d5189Svisa 		if (rslt == -1)
3104a5d5189Svisa 			goto umount;
3114a5d5189Svisa 	}
3124a5d5189Svisa 
313*db15c72aSkettenis 	/* Skip installing a 2nd copy if we have a small filesystem. */
314*db15c72aSkettenis 	if (statfs(dst, &sf) || sf.f_blocks < 2048) {
315*db15c72aSkettenis 		rslt = 0;
316*db15c72aSkettenis 		goto firmware;
317*db15c72aSkettenis 	}
318*db15c72aSkettenis 
319fbfcabeaSkettenis 	/* Create "/efi/openbsd" directory in <duid>.<part>. */
320fbfcabeaSkettenis 	dst[mntlen] = '\0';
321fbfcabeaSkettenis 	if (strlcat(dst, "/efi/openbsd", sizeof(dst)) >= sizeof(dst)) {
322fbfcabeaSkettenis 		rslt = -1;
323fbfcabeaSkettenis 		warn("unable to build /efi/openbsd directory");
324fbfcabeaSkettenis 		goto umount;
325fbfcabeaSkettenis 	}
326fbfcabeaSkettenis 	rslt = mkdir(dst, 0755);
327fbfcabeaSkettenis 	if (rslt == -1 && errno != EEXIST) {
328fbfcabeaSkettenis 		warn("mkdir('%s') failed", dst);
329fbfcabeaSkettenis 		goto umount;
330fbfcabeaSkettenis 	}
331fbfcabeaSkettenis 
332fbfcabeaSkettenis 	/* Copy EFI bootblocks to /efi/openbsd/. */
333fbfcabeaSkettenis 	if (strlcat(dst, "/" BOOTEFI_DST, sizeof(dst)) >= sizeof(dst)) {
334fbfcabeaSkettenis 		rslt = -1;
335fbfcabeaSkettenis 		warn("unable to build /%s path", BOOTEFI_DST);
336fbfcabeaSkettenis 		goto umount;
337fbfcabeaSkettenis 	}
338fbfcabeaSkettenis 	src = fileprefix(root, "/usr/mdec/" BOOTEFI_SRC);
339fbfcabeaSkettenis 	if (src == NULL) {
340fbfcabeaSkettenis 		rslt = -1;
341fbfcabeaSkettenis 		goto umount;
342fbfcabeaSkettenis 	}
343fbfcabeaSkettenis 	srclen = strlen(src);
344fbfcabeaSkettenis 	if (verbose)
345fbfcabeaSkettenis 		fprintf(stderr, "%s %s to %s\n",
346fbfcabeaSkettenis 		    (nowrite ? "would copy" : "copying"), src, dst);
347fbfcabeaSkettenis 	if (!nowrite) {
348fbfcabeaSkettenis 		rslt = filecopy(src, dst);
349fbfcabeaSkettenis 		if (rslt == -1)
350fbfcabeaSkettenis 			goto umount;
351fbfcabeaSkettenis 	}
352fbfcabeaSkettenis 
353*db15c72aSkettenis firmware:
3548fb3e437Stobhe 	dst[mntlen] = '\0';
3558fb3e437Stobhe 	rslt = write_firmware(root, dst);
3568fb3e437Stobhe 	if (rslt == -1)
3578fb3e437Stobhe 		warnx("unable to write firmware");
358db32265bSvisa 
359db32265bSvisa umount:
360db32265bSvisa 	dst[mntlen] = '\0';
361db32265bSvisa 	if (unmount(dst, MNT_FORCE) == -1)
362db32265bSvisa 		err(1, "unmount('%s') failed", dst);
363db32265bSvisa 
364db32265bSvisa rmdir:
365db32265bSvisa 	free(args.fspec);
366db32265bSvisa 	dst[mntlen] = '\0';
367db32265bSvisa 	if (rmdir(dst) == -1)
368db32265bSvisa 		err(1, "rmdir('%s') failed", dst);
369db32265bSvisa 
370db32265bSvisa 	free(src);
371db32265bSvisa 
372db32265bSvisa 	if (rslt == -1)
373db32265bSvisa 		exit(1);
374db32265bSvisa }
375db32265bSvisa 
3768fb3e437Stobhe static int
3778fb3e437Stobhe write_firmware(const char *root, const char *mnt)
3788fb3e437Stobhe {
3798fb3e437Stobhe 	char dst[PATH_MAX];
3808fb3e437Stobhe 	char fw[PATH_MAX];
3818fb3e437Stobhe 	char *src;
3828fb3e437Stobhe 	struct stat st;
3838fb3e437Stobhe 	int rslt;
3848fb3e437Stobhe 
3858fb3e437Stobhe 	strlcpy(dst, mnt, sizeof(dst));
3868fb3e437Stobhe 
3878fb3e437Stobhe 	/* Skip if no /etc/firmware exists */
3888fb3e437Stobhe 	rslt = snprintf(fw, sizeof(fw), "%s/%s", root, "etc/firmware");
3898fb3e437Stobhe 	if (rslt < 0 || rslt >= PATH_MAX) {
3908fb3e437Stobhe 		warnx("unable to build /etc/firmware path");
3918fb3e437Stobhe 		return -1;
3928fb3e437Stobhe 	}
3938fb3e437Stobhe 	if ((stat(fw, &st) != 0) || !S_ISDIR(st.st_mode))
3948fb3e437Stobhe 		return 0;
3958fb3e437Stobhe 
3968fb3e437Stobhe 	/* Copy apple-boot firmware to /m1n1/boot.bin if available */
3978fb3e437Stobhe 	src = fileprefix(fw, "/apple-boot.bin");
3988fb3e437Stobhe 	if (src == NULL)
3998fb3e437Stobhe 		return -1;
4008fb3e437Stobhe 	if (access(src, R_OK) == 0) {
4018fb3e437Stobhe 		if (strlcat(dst, "/m1n1", sizeof(dst)) >= sizeof(dst)) {
4028fb3e437Stobhe 			rslt = -1;
4038fb3e437Stobhe 			warnx("unable to build /m1n1 path");
4048fb3e437Stobhe 			goto cleanup;
4058fb3e437Stobhe 		}
4068fb3e437Stobhe 		if ((stat(dst, &st) != 0) || !S_ISDIR(st.st_mode)) {
4078fb3e437Stobhe 			rslt = 0;
4088fb3e437Stobhe 			goto cleanup;
4098fb3e437Stobhe 		}
4108fb3e437Stobhe 		if (strlcat(dst, "/boot.bin", sizeof(dst)) >= sizeof(dst)) {
4118fb3e437Stobhe 			rslt = -1;
4128fb3e437Stobhe 			warnx("unable to build /m1n1/boot.bin path");
4138fb3e437Stobhe 			goto cleanup;
4148fb3e437Stobhe 		}
4158fb3e437Stobhe 		if (verbose)
4168fb3e437Stobhe 			fprintf(stderr, "%s %s to %s\n",
4178fb3e437Stobhe 			    (nowrite ? "would copy" : "copying"), src, dst);
418dcbdaee6Skn 		if (!nowrite) {
4198fb3e437Stobhe 			rslt = filecopy(src, dst);
4208fb3e437Stobhe 			if (rslt == -1)
4218fb3e437Stobhe 				goto cleanup;
4228fb3e437Stobhe 		}
423dcbdaee6Skn 	}
4248fb3e437Stobhe 	rslt = 0;
4258fb3e437Stobhe 
4268fb3e437Stobhe  cleanup:
4278fb3e437Stobhe 	free(src);
4288fb3e437Stobhe 	return rslt;
4298fb3e437Stobhe }
4308fb3e437Stobhe 
431db32265bSvisa /*
432db32265bSvisa  * Returns 0 if the MBR with the provided partition array is a GPT protective
433db32265bSvisa  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
434db32265bSvisa  * one MBR partition, an EFI partition that either covers the whole disk or as
435db32265bSvisa  * much of it as is possible with a 32bit size field.
436db32265bSvisa  *
437db32265bSvisa  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
438db32265bSvisa  */
439db32265bSvisa static int
440db32265bSvisa gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
441db32265bSvisa {
442db32265bSvisa 	struct dos_partition *dp2;
443db32265bSvisa 	int efi, found, i;
444db32265bSvisa 	u_int32_t psize;
445db32265bSvisa 
446db32265bSvisa 	found = efi = 0;
447db32265bSvisa 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
448db32265bSvisa 		if (dp2->dp_typ == DOSPTYP_UNUSED)
449db32265bSvisa 			continue;
450db32265bSvisa 		found++;
451db32265bSvisa 		if (dp2->dp_typ != DOSPTYP_EFI)
452db32265bSvisa 			continue;
453db32265bSvisa 		if (letoh32(dp2->dp_start) != GPTSECTOR)
454db32265bSvisa 			continue;
455db32265bSvisa 		psize = letoh32(dp2->dp_size);
456db32265bSvisa 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX)
457db32265bSvisa 			efi++;
458db32265bSvisa 	}
459db32265bSvisa 	if (found == 1 && efi == 1)
460db32265bSvisa 		return (0);
461db32265bSvisa 
462db32265bSvisa 	return (1);
463db32265bSvisa }
464db32265bSvisa 
465db32265bSvisa int
466db32265bSvisa findgptefisys(int devfd, struct disklabel *dl)
467db32265bSvisa {
468db32265bSvisa 	struct gpt_partition	 gp[NGPTPARTITIONS];
469db32265bSvisa 	struct gpt_header	 gh;
470db32265bSvisa 	struct dos_partition	 dp[NDOSPART];
471db32265bSvisa 	struct uuid		 efisys_uuid;
472db32265bSvisa 	const char		 efisys_uuid_code[] = GPT_UUID_EFI_SYSTEM;
473db32265bSvisa 	off_t			 off;
474db32265bSvisa 	ssize_t			 len;
475db32265bSvisa 	u_int64_t		 start;
476db32265bSvisa 	int			 i;
477db32265bSvisa 	uint32_t		 orig_csum, new_csum;
478db32265bSvisa 	uint32_t		 ghsize, ghpartsize, ghpartnum, ghpartspersec;
479db32265bSvisa 	u_int8_t		*secbuf;
480db32265bSvisa 
481db32265bSvisa 	/* Prepare EFI System UUID */
482db32265bSvisa 	uuid_dec_be(efisys_uuid_code, &efisys_uuid);
483db32265bSvisa 
484db32265bSvisa 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
485db32265bSvisa 		err(1, NULL);
486db32265bSvisa 
487db32265bSvisa 	/* Check that there is a protective MBR. */
488db32265bSvisa 	len = pread(devfd, secbuf, dl->d_secsize, 0);
489db32265bSvisa 	if (len != dl->d_secsize)
490db32265bSvisa 		err(4, "can't read mbr");
491db32265bSvisa 	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
492db32265bSvisa 	if (gpt_chk_mbr(dp, DL_GETDSIZE(dl))) {
493db32265bSvisa 		free(secbuf);
494db32265bSvisa 		return (-1);
495db32265bSvisa 	}
496db32265bSvisa 
497db32265bSvisa 	/* Check GPT Header. */
498db32265bSvisa 	off = dl->d_secsize;	/* Read header from sector 1. */
499db32265bSvisa 	len = pread(devfd, secbuf, dl->d_secsize, off);
500db32265bSvisa 	if (len != dl->d_secsize)
501db32265bSvisa 		err(4, "can't pread gpt header");
502db32265bSvisa 
503db32265bSvisa 	memcpy(&gh, secbuf, sizeof(gh));
504db32265bSvisa 	free(secbuf);
505db32265bSvisa 
506db32265bSvisa 	/* Check signature */
507db32265bSvisa 	if (letoh64(gh.gh_sig) != GPTSIGNATURE)
508db32265bSvisa 		return (-1);
509db32265bSvisa 
510db32265bSvisa 	if (letoh32(gh.gh_rev) != GPTREVISION)
511db32265bSvisa 		return (-1);
512db32265bSvisa 
513db32265bSvisa 	ghsize = letoh32(gh.gh_size);
514db32265bSvisa 	if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header))
515db32265bSvisa 		return (-1);
516db32265bSvisa 
517db32265bSvisa 	/* Check checksum */
518db32265bSvisa 	orig_csum = gh.gh_csum;
519db32265bSvisa 	gh.gh_csum = 0;
520db32265bSvisa 	new_csum = crc32((unsigned char *)&gh, ghsize);
521db32265bSvisa 	gh.gh_csum = orig_csum;
522db32265bSvisa 	if (letoh32(orig_csum) != new_csum)
523db32265bSvisa 		return (-1);
524db32265bSvisa 
525db32265bSvisa 	off = letoh64(gh.gh_part_lba) * dl->d_secsize;
526db32265bSvisa 	ghpartsize = letoh32(gh.gh_part_size);
527db32265bSvisa 	ghpartspersec = dl->d_secsize / ghpartsize;
528db32265bSvisa 	ghpartnum = letoh32(gh.gh_part_num);
529db32265bSvisa 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
530db32265bSvisa 		err(1, NULL);
531db32265bSvisa 	for (i = 0; i < (ghpartnum + ghpartspersec - 1) / ghpartspersec; i++) {
532db32265bSvisa 		len = pread(devfd, secbuf, dl->d_secsize, off);
533db32265bSvisa 		if (len != dl->d_secsize) {
534db32265bSvisa 			free(secbuf);
535db32265bSvisa 			return (-1);
536db32265bSvisa 		}
537db32265bSvisa 		memcpy(gp + i * ghpartspersec, secbuf,
538db32265bSvisa 		    ghpartspersec * sizeof(struct gpt_partition));
539db32265bSvisa 		off += dl->d_secsize;
540db32265bSvisa 	}
541db32265bSvisa 	free(secbuf);
542db32265bSvisa 	new_csum = crc32((unsigned char *)&gp, ghpartnum * ghpartsize);
543db32265bSvisa 	if (new_csum != letoh32(gh.gh_part_csum))
544db32265bSvisa 		return (-1);
545db32265bSvisa 
546db32265bSvisa 	start = 0;
547db32265bSvisa 	for (i = 0; i < ghpartnum && start == 0; i++) {
548db32265bSvisa 		if (memcmp(&gp[i].gp_type, &efisys_uuid,
549db32265bSvisa 		    sizeof(struct uuid)) == 0)
550db32265bSvisa 			start = letoh64(gp[i].gp_lba_start);
551db32265bSvisa 	}
552db32265bSvisa 
553db32265bSvisa 	if (start) {
554db32265bSvisa 		for (i = 0; i < MAXPARTITIONS; i++) {
555db32265bSvisa 			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
556db32265bSvisa 			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
557db32265bSvisa 				return ('a' + i);
558db32265bSvisa 		}
559db32265bSvisa 	}
560db32265bSvisa 
561db32265bSvisa 	return (-1);
562db32265bSvisa }
563db32265bSvisa 
564db32265bSvisa int
565db32265bSvisa findmbrfat(int devfd, struct disklabel *dl)
566db32265bSvisa {
567db32265bSvisa 	struct dos_partition	 dp[NDOSPART];
568db32265bSvisa 	ssize_t			 len;
569db32265bSvisa 	u_int64_t		 start = 0;
570db32265bSvisa 	int			 i;
571db32265bSvisa 	u_int8_t		*secbuf;
572db32265bSvisa 
573db32265bSvisa 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
574db32265bSvisa 		err(1, NULL);
575db32265bSvisa 
576db32265bSvisa 	/* Read MBR. */
577db32265bSvisa 	len = pread(devfd, secbuf, dl->d_secsize, 0);
578db32265bSvisa 	if (len != dl->d_secsize)
579db32265bSvisa 		err(4, "can't read mbr");
580db32265bSvisa 	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
581db32265bSvisa 
582db32265bSvisa 	for (i = 0; i < NDOSPART; i++) {
583db32265bSvisa 		if (dp[i].dp_typ == DOSPTYP_UNUSED)
584db32265bSvisa 			continue;
585db32265bSvisa 		if (dp[i].dp_typ == DOSPTYP_FAT16L ||
586db32265bSvisa 		    dp[i].dp_typ == DOSPTYP_FAT32L ||
587db32265bSvisa 		    dp[i].dp_typ == DOSPTYP_EFISYS)
588db32265bSvisa 			start = dp[i].dp_start;
589db32265bSvisa 	}
590db32265bSvisa 
591db32265bSvisa 	free(secbuf);
592db32265bSvisa 
593db32265bSvisa 	if (start) {
594db32265bSvisa 		for (i = 0; i < MAXPARTITIONS; i++) {
595db32265bSvisa 			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
596db32265bSvisa 			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
597db32265bSvisa 				return ('a' + i);
598db32265bSvisa 		}
599db32265bSvisa 	}
600db32265bSvisa 
601db32265bSvisa 	return (-1);
602db32265bSvisa }
603