1 /*	$OpenBSD: powerpc64_installboot.c,v 1.3 2021/07/20 14:51:56 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
5  * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
6  * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
7  * Copyright (c) 1997 Michael Shalayeff
8  * Copyright (c) 1994 Paul Kranenburg
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by Paul Kranenburg.
22  * 4. The name of the author may not be used to endorse or promote products
23  *    derived from this software without specific prior written permission
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 #include <sys/param.h>	/* DEV_BSIZE */
38 #include <sys/disklabel.h>
39 #include <sys/dkio.h>
40 #include <sys/ioctl.h>
41 #include <sys/mount.h>
42 #include <sys/stat.h>
43 
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <util.h>
52 #include <endian.h>
53 
54 #include "installboot.h"
55 
56 static int	create_filesystem(struct disklabel *, char);
57 static void	write_filesystem(struct disklabel *, char);
58 static int	findmbrfat(int, struct disklabel *);
59 
60 char	duid[20];
61 
62 void
63 md_init(void)
64 {
65 }
66 
67 void
68 md_loadboot(void)
69 {
70 }
71 
72 void
73 md_prepareboot(int devfd, char *dev)
74 {
75 	struct disklabel dl;
76 	int part;
77 
78 	/* Get and check disklabel. */
79 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
80 		err(1, "disklabel: %s", dev);
81 	if (dl.d_magic != DISKMAGIC)
82 		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
83 
84 	/* Warn on unknown disklabel types. */
85 	if (dl.d_type == 0)
86 		warnx("disklabel type unknown");
87 
88 	part = findmbrfat(devfd, &dl);
89 	if (part != -1) {
90 		create_filesystem(&dl, (char)part);
91 		return;
92 	}
93 }
94 
95 void
96 md_installboot(int devfd, char *dev)
97 {
98 	struct disklabel dl;
99 	int part;
100 
101 	/* Get and check disklabel. */
102 	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
103 		err(1, "disklabel: %s", dev);
104 	if (dl.d_magic != DISKMAGIC)
105 		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
106 
107 	snprintf(duid, sizeof duid,
108 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
109             dl.d_uid[0], dl.d_uid[1], dl.d_uid[2], dl.d_uid[3],
110             dl.d_uid[4], dl.d_uid[5], dl.d_uid[6], dl.d_uid[7]);
111 
112 	/* Warn on unknown disklabel types. */
113 	if (dl.d_type == 0)
114 		warnx("disklabel type unknown");
115 
116 	part = findmbrfat(devfd, &dl);
117 	if (part != -1) {
118 		write_filesystem(&dl, (char)part);
119 		return;
120 	}
121 }
122 
123 static int
124 create_filesystem(struct disklabel *dl, char part)
125 {
126 	static char *newfsfmt ="/sbin/newfs_msdos %s >/dev/null";
127 	struct msdosfs_args args;
128 	char cmd[60];
129 	int rslt;
130 
131 	/* Mount <duid>.<part> as msdos filesystem. */
132 	memset(&args, 0, sizeof(args));
133 	rslt = asprintf(&args.fspec,
134 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
135             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
136             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
137 	    part);
138 	if (rslt == -1) {
139 		warn("bad special device");
140 		return rslt;
141 	}
142 
143 	rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
144 	if (rslt >= sizeof(cmd)) {
145 		warnx("can't build newfs command");
146 		rslt = -1;
147 		return rslt;
148 	}
149 
150 	if (verbose)
151 		fprintf(stderr, "%s %s\n",
152 		    (nowrite ? "would newfs" : "newfsing"), args.fspec);
153 	if (!nowrite) {
154 		rslt = system(cmd);
155 		if (rslt == -1) {
156 			warn("system('%s') failed", cmd);
157 			return rslt;
158 		}
159 	}
160 
161 	return 0;
162 }
163 
164 static void
165 write_filesystem(struct disklabel *dl, char part)
166 {
167 	static char *fsckfmt = "/sbin/fsck_msdos %s >/dev/null";
168 	struct msdosfs_args args;
169 	char cmd[60];
170 	char dir[PATH_MAX];
171 	char dst[PATH_MAX];
172 	char *src;
173 	size_t mntlen, srclen;
174 	int rslt;
175 
176 	src = NULL;
177 
178 	/* Create directory for temporary mount point. */
179 	strlcpy(dir, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
180 	if (mkdtemp(dir) == NULL)
181 		err(1, "mkdtemp('%s') failed", dst);
182 	mntlen = strlen(dir);
183 
184 	/* Mount <duid>.<part> as msdos filesystem. */
185 	memset(&args, 0, sizeof(args));
186 	rslt = asprintf(&args.fspec,
187 	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
188             dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
189             dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
190 	    part);
191 	if (rslt == -1) {
192 		warn("bad special device");
193 		goto rmdir;
194 	}
195 
196 	args.export_info.ex_root = -2;
197 	args.export_info.ex_flags = 0;
198 	args.flags = MSDOSFSMNT_LONGNAME;
199 
200 	if (mount(MOUNT_MSDOS, dir, 0, &args) == -1) {
201 		/* Try fsck'ing it. */
202 		rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
203 		if (rslt >= sizeof(cmd)) {
204 			warnx("can't build fsck command");
205 			rslt = -1;
206 			goto rmdir;
207 		}
208 		rslt = system(cmd);
209 		if (rslt == -1) {
210 			warn("system('%s') failed", cmd);
211 			goto rmdir;
212 		}
213 		if (mount(MOUNT_MSDOS, dir, 0, &args) == -1) {
214 			/* Try newfs'ing it. */
215 			rslt = create_filesystem(dl, part);
216 			if (rslt == -1)
217 				goto rmdir;
218 			rslt = mount(MOUNT_MSDOS, dir, 0, &args);
219 			if (rslt == -1) {
220 				warn("unable to mount MSDOS partition");
221 				goto rmdir;
222 			}
223 		}
224 	}
225 
226 	/*
227 	 * Copy /usr/mdec/boot to /mnt/boot.
228 	 */
229 	strlcpy(dst, dir, sizeof dst);
230 	if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) {
231 		rslt = -1;
232 		warn("unable to build /boot path");
233 		goto umount;
234 	}
235 	src = fileprefix(root, "/usr/mdec/boot");
236 	if (src == NULL) {
237 		rslt = -1;
238 		goto umount;
239 	}
240 	srclen = strlen(src);
241 	if (verbose)
242 		fprintf(stderr, "%s %s to %s\n",
243 		    (nowrite ? "would copy" : "copying"), src, dst);
244 	if (!nowrite) {
245 		rslt = filecopy(src, dst);
246 		if (rslt == -1)
247 			goto umount;
248 	}
249 
250 	/*
251 	 * Create grub.cfg
252 	 */
253 	strlcpy(dst, dir, sizeof dst);
254 	if (strlcat(dst, "/grub.cfg", sizeof(dst)) >= sizeof(dst)) {
255 		rslt = -1;
256 		warn("unable to build /grub.cfg path");
257 		goto umount;
258 	}
259 	if (verbose)
260 		fprintf(stderr, "%s %s\n",
261 		    (nowrite ? "would create" : "creating"), dst);
262 	if (!nowrite) {
263 		FILE *f;
264 
265 		f = fopen(dst, "w+");
266 		if (f == NULL)
267 			goto umount;
268 		fprintf(f,
269 		    "menuentry \"OpenBSD\" {\n"
270 		    "\tlinux /boot bootduid=%s\n"
271 		    "\tinitrd /boot\n"
272 		    "}\n", duid);
273 		fclose(f);
274 	}
275 
276 	rslt = 0;
277 
278 umount:
279 	dir[mntlen] = '\0';
280 	if (unmount(dir, MNT_FORCE) == -1)
281 		err(1, "unmount('%s') failed", dir);
282 
283 rmdir:
284 	free(args.fspec);
285 	dst[mntlen] = '\0';
286 	if (rmdir(dir) == -1)
287 		err(1, "rmdir('%s') failed", dir);
288 
289 	free(src);
290 
291 	if (rslt == -1)
292 		exit(1);
293 }
294 
295 int
296 findmbrfat(int devfd, struct disklabel *dl)
297 {
298 	struct dos_partition	 dp[NDOSPART];
299 	ssize_t			 len;
300 	u_int64_t		 start = 0;
301 	int			 i;
302 	u_int8_t		*secbuf;
303 
304 	if ((secbuf = malloc(dl->d_secsize)) == NULL)
305 		err(1, NULL);
306 
307 	/* Read MBR. */
308 	len = pread(devfd, secbuf, dl->d_secsize, 0);
309 	if (len != dl->d_secsize)
310 		err(4, "can't read mbr");
311 	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
312 
313 	for (i = 0; i < NDOSPART; i++) {
314 		if (dp[i].dp_typ == DOSPTYP_UNUSED)
315 			continue;
316 		if (dp[i].dp_typ == DOSPTYP_FAT16L ||
317 		    dp[i].dp_typ == DOSPTYP_FAT32L ||
318 		    dp[i].dp_typ == DOSPTYP_FAT16B)
319 			start = letoh32(dp[i].dp_start);
320 	}
321 
322 	free(secbuf);
323 
324 	if (start) {
325 		for (i = 0; i < MAXPARTITIONS; i++) {
326 			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
327 			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
328 				return ('a' + i);
329 		}
330 	}
331 
332 	return (-1);
333 }
334