1/*	$OpenBSD: powerpc64_installboot.c,v 1.9 2023/04/26 18:04:21 kn 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
56static int	create_filesystem(struct disklabel *, char);
57static void	write_filesystem(struct disklabel *, char);
58static int	findmbrfat(int, struct disklabel *);
59
60char	duid[20];
61
62void
63md_init(void)
64{
65	stages = 1;
66	stage1 = "/usr/mdec/boot";
67}
68
69void
70md_loadboot(void)
71{
72}
73
74void
75md_prepareboot(int devfd, char *dev)
76{
77	struct disklabel dl;
78	int part;
79
80	/* Get and check disklabel. */
81	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
82		err(1, "disklabel: %s", dev);
83	if (dl.d_magic != DISKMAGIC)
84		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
85
86	/* Warn on unknown disklabel types. */
87	if (dl.d_type == 0)
88		warnx("disklabel type unknown");
89
90	part = findmbrfat(devfd, &dl);
91	if (part != -1) {
92		create_filesystem(&dl, (char)part);
93		return;
94	}
95}
96
97void
98md_installboot(int devfd, char *dev)
99{
100	struct disklabel dl;
101	int part;
102
103	/* Get and check disklabel. */
104	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
105		err(1, "disklabel: %s", dev);
106	if (dl.d_magic != DISKMAGIC)
107		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
108
109	snprintf(duid, sizeof duid,
110	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
111            dl.d_uid[0], dl.d_uid[1], dl.d_uid[2], dl.d_uid[3],
112            dl.d_uid[4], dl.d_uid[5], dl.d_uid[6], dl.d_uid[7]);
113
114	/* Warn on unknown disklabel types. */
115	if (dl.d_type == 0)
116		warnx("disklabel type unknown");
117
118	part = findmbrfat(devfd, &dl);
119	if (part != -1) {
120		write_filesystem(&dl, (char)part);
121		return;
122	}
123}
124
125static int
126create_filesystem(struct disklabel *dl, char part)
127{
128	static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null";
129	struct msdosfs_args args;
130	char cmd[60];
131	int rslt;
132
133	/* Newfs <duid>.<part> as msdos filesystem. */
134	memset(&args, 0, sizeof(args));
135	rslt = asprintf(&args.fspec,
136	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
137            dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
138            dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
139	    part);
140	if (rslt == -1) {
141		warn("bad special device");
142		return rslt;
143	}
144
145	rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
146	if (rslt >= sizeof(cmd)) {
147		warnx("can't build newfs command");
148		free(args.fspec);
149		rslt = -1;
150		return rslt;
151	}
152
153	if (verbose)
154		fprintf(stderr, "%s %s\n",
155		    (nowrite ? "would newfs" : "newfsing"), args.fspec);
156	if (!nowrite) {
157		rslt = system(cmd);
158		if (rslt == -1) {
159			warn("system('%s') failed", cmd);
160			free(args.fspec);
161			return rslt;
162		}
163	}
164
165	free(args.fspec);
166	return 0;
167}
168
169static void
170write_filesystem(struct disklabel *dl, char part)
171{
172	static const char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null";
173	struct msdosfs_args args;
174	char cmd[60];
175	char dir[PATH_MAX];
176	char dst[PATH_MAX];
177	size_t mntlen;
178	int rslt;
179
180	/* Create directory for temporary mount point. */
181	strlcpy(dir, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
182	if (mkdtemp(dir) == NULL)
183		err(1, "mkdtemp('%s') failed", dst);
184	mntlen = strlen(dir);
185
186	/* Mount <duid>.<part> as msdos filesystem. */
187	memset(&args, 0, sizeof(args));
188	rslt = asprintf(&args.fspec,
189	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
190            dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
191            dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
192	    part);
193	if (rslt == -1) {
194		warn("bad special device");
195		goto rmdir;
196	}
197
198	args.export_info.ex_root = -2;
199	args.export_info.ex_flags = 0;
200	args.flags = MSDOSFSMNT_LONGNAME;
201
202	if (mount(MOUNT_MSDOS, dir, 0, &args) == -1) {
203		/* Try fsck'ing it. */
204		rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
205		if (rslt >= sizeof(cmd)) {
206			warnx("can't build fsck command");
207			rslt = -1;
208			goto rmdir;
209		}
210		rslt = system(cmd);
211		if (rslt == -1) {
212			warn("system('%s') failed", cmd);
213			goto rmdir;
214		}
215		if (mount(MOUNT_MSDOS, dir, 0, &args) == -1) {
216			/* Try newfs'ing it. */
217			rslt = create_filesystem(dl, part);
218			if (rslt == -1)
219				goto rmdir;
220			rslt = mount(MOUNT_MSDOS, dir, 0, &args);
221			if (rslt == -1) {
222				warn("unable to mount MSDOS partition");
223				goto rmdir;
224			}
225		}
226	}
227
228	/*
229	 * Copy /usr/mdec/boot to /mnt/boot.
230	 */
231	strlcpy(dst, dir, sizeof dst);
232	if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) {
233		rslt = -1;
234		warn("unable to build /boot path");
235		goto umount;
236	}
237	if (verbose)
238		fprintf(stderr, "%s %s to %s\n",
239		    (nowrite ? "would copy" : "copying"), stage1, dst);
240	if (!nowrite) {
241		rslt = filecopy(stage1, dst);
242		if (rslt == -1)
243			goto umount;
244	}
245
246	/*
247	 * Create grub.cfg
248	 */
249	strlcpy(dst, dir, sizeof dst);
250	if (strlcat(dst, "/grub.cfg", sizeof(dst)) >= sizeof(dst)) {
251		rslt = -1;
252		warn("unable to build /grub.cfg path");
253		goto umount;
254	}
255	if (verbose)
256		fprintf(stderr, "%s %s\n",
257		    (nowrite ? "would create" : "creating"), dst);
258	if (!nowrite) {
259		FILE *f;
260
261		f = fopen(dst, "w+");
262		if (f == NULL)
263			goto umount;
264		fprintf(f,
265		    "menuentry \"OpenBSD\" {\n"
266		    "\tlinux /boot bootduid=%s\n"
267		    "\tinitrd /boot\n"
268		    "}\n", duid);
269		fclose(f);
270	}
271
272	rslt = 0;
273
274umount:
275	dir[mntlen] = '\0';
276	if (unmount(dir, MNT_FORCE) == -1)
277		err(1, "unmount('%s') failed", dir);
278
279rmdir:
280	free(args.fspec);
281	dst[mntlen] = '\0';
282	if (rmdir(dir) == -1)
283		err(1, "rmdir('%s') failed", dir);
284
285	if (rslt == -1)
286		exit(1);
287}
288
289int
290findmbrfat(int devfd, struct disklabel *dl)
291{
292	struct dos_partition	 dp[NDOSPART];
293	ssize_t			 len;
294	u_int64_t		 start = 0;
295	int			 i;
296	u_int8_t		*secbuf;
297
298	if ((secbuf = malloc(dl->d_secsize)) == NULL)
299		err(1, NULL);
300
301	/* Read MBR. */
302	len = pread(devfd, secbuf, dl->d_secsize, 0);
303	if (len != dl->d_secsize)
304		err(4, "can't read mbr");
305	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
306
307	for (i = 0; i < NDOSPART; i++) {
308		if (dp[i].dp_typ == DOSPTYP_UNUSED)
309			continue;
310		if (dp[i].dp_typ == DOSPTYP_FAT16L ||
311		    dp[i].dp_typ == DOSPTYP_FAT32L ||
312		    dp[i].dp_typ == DOSPTYP_FAT16B)
313			start = letoh32(dp[i].dp_start);
314	}
315
316	free(secbuf);
317
318	if (start) {
319		for (i = 0; i < MAXPARTITIONS; i++) {
320			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
321			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
322				return ('a' + i);
323		}
324	}
325
326	return (-1);
327}
328