1/*	$NetBSD: installboot.c,v 1.7 2005/12/11 12:18:51 christos Exp $	*/
2
3/*
4 * Copyright (c) 2000 NONAKA Kimihiro (nonaka@NetBSD.org).
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/param.h>
31#include <sys/exec_elf.h>
32#include <sys/bootblock.h>
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <err.h>
39#include <string.h>
40
41int nowrite, verbose;
42char *boot, *dev;
43
44void usage(void);
45int devread(int, void *, daddr_t, size_t, char *);
46char *load_boot(char *, size_t *);
47int load_prep_partition(int, struct mbr_partition *);
48int main(int, char **);
49
50void
51usage(void)
52{
53
54	fprintf(stderr, "usage: %s [-n] [-v] <boot> <device>\n",
55	    getprogname());
56	exit(1);
57}
58
59int
60devread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
61{
62
63	if (lseek(fd, (off_t)dbtob(blk), SEEK_SET) != dbtob(blk)) {
64		warn("%s: devread: lseek", msg);
65		return 1;
66	}
67	if (read(fd, buf, size) != size) {
68		warn("%s: devread: read", msg);
69		return 1;
70	}
71	return 0;
72}
73
74char *
75load_boot(char *boot, size_t *bootsize)
76{
77	Elf32_Ehdr eh;
78	Elf32_Phdr ph;
79	struct stat st;
80	int fd;
81	int i;
82	size_t imgsz = 0;
83	char *bp = NULL;
84
85	if ((fd = open(boot, O_RDONLY)) < 0) {
86		warn("open: %s", boot);
87		return NULL;
88	}
89
90	if (fstat(fd, &st) != 0) {
91		warn("fstat: %s", boot);
92		goto out;
93	}
94
95	/*
96	 * First, check ELF.
97	 */
98	if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) {
99		warn("read: eh: %s", boot);
100		goto out;
101	}
102	if (memcmp(eh.e_ident, ELFMAG, SELFMAG) != 0 ||
103	    eh.e_ident[EI_CLASS] != ELFCLASS32) {
104		lseek(fd, 0L, SEEK_SET);
105		goto notelf;
106	}
107	if (be16toh(eh.e_machine) != EM_PPC) {
108		warn("not PowerPC binary.");
109		goto out;
110	}
111
112	for (i = 0; i < be16toh(eh.e_phnum); i++) {
113		(void)lseek(fd, be32toh(eh.e_phoff) + sizeof(ph) * i, SEEK_SET);
114		if (read(fd, &ph, sizeof(ph)) != sizeof(ph)) {
115			warn("read: ph: %s", boot);
116			goto out;
117		}
118
119		if ((be32toh(ph.p_type) != PT_LOAD) ||
120		    !(be32toh(ph.p_flags) & PF_X))
121			continue;
122
123		imgsz = st.st_size - be32toh(ph.p_offset);
124		lseek(fd, be32toh(ph.p_offset), SEEK_SET);
125		break;
126	}
127
128notelf:
129	/*
130	 * Second, check PReP bootable image.
131	 */
132	if (imgsz == 0) {
133		char buf[DEV_BSIZE];
134
135		printf("Bootable image: ");
136		if (load_prep_partition(fd, 0)) {
137			warn("no PReP bootable image.");
138			goto out;
139		}
140
141		if (lseek(fd, (off_t)dbtob(1), SEEK_SET) != dbtob(1)) {
142			warn("bootable image lseek sector 1");
143			goto out;
144		}
145		if (read(fd, buf, DEV_BSIZE) != DEV_BSIZE) {
146			warn("read: start/size");
147			goto out;
148		}
149
150		imgsz = le32toh(*(u_int32_t *)(buf + sizeof(u_int32_t)))
151		    - dbtob(2);
152		lseek(fd, le32toh(*(u_int32_t *)buf), SEEK_SET);
153	}
154
155	if ((bp = (char *)calloc(roundup(imgsz, DEV_BSIZE), 1)) == NULL) {
156		warn("calloc: no memory for boot image.");
157		goto out;
158	}
159
160	if (read(fd, bp, imgsz) != imgsz) {
161		warn("read: boot image: %s", boot);
162		goto out;
163	}
164
165	if (verbose) {
166		printf("image size = %d\n", imgsz);
167	}
168
169	*bootsize = roundup(imgsz, DEV_BSIZE);
170
171	close(fd);
172	return bp;
173
174out:
175	if (bp != NULL)
176		free(bp);
177	if (fd >= 0)
178		close(fd);
179	return NULL;
180}
181
182int
183load_prep_partition(int devfd, struct mbr_partition *ppp)
184{
185	char mbr[512];
186	struct mbr_partition *mbrp;
187	int i;
188
189	if (devread(devfd, mbr, MBR_BBSECTOR, DEV_BSIZE, "MBR") != 0)
190		return 1;
191	if (*(u_int16_t *)&mbr[MBR_MAGIC_OFFSET] != htole16(MBR_MAGIC)) {
192		warn("no MBR_MAGIC");
193		return 1;
194	}
195
196	mbrp = (struct mbr_partition *)&mbr[MBR_PART_OFFSET];
197	for (i = 0; i < MBR_PART_COUNT; i++) {
198		if (mbrp[i].mbrp_type == MBR_PTYPE_PREP)
199			break;
200	}
201	if (i == MBR_PART_COUNT) {
202		warn("no PReP partition.");
203		return 1;
204	}
205
206	if (verbose) {
207		printf("PReP partition: start = %d, size = %d\n",
208		    le32toh(mbrp[i].mbrp_start), le32toh(mbrp[i].mbrp_size));
209	}
210
211	if (ppp) {
212		*ppp = mbrp[i];
213		ppp->mbrp_start = le32toh(ppp->mbrp_start);
214		ppp->mbrp_size = le32toh(ppp->mbrp_size);
215	}
216
217	return 0;
218}
219
220int
221main(int argc, char **argv)
222{
223	struct mbr_partition ppp;
224	size_t bootsize;
225	int c;
226	int boot00[512/sizeof(int)];
227	int devfd = -1;
228	char *bp;
229
230	while ((c = getopt(argc, argv, "vn")) != EOF) {
231		switch (c) {
232		case 'n':
233			nowrite = 1;
234			break;
235		case 'v':
236			verbose = 1;
237			break;
238		default:
239			usage();
240			break;
241		}
242	}
243
244	if (argc - optind < 2)
245		usage();
246
247	boot = argv[optind];
248	dev = argv[optind + 1];
249	if (verbose) {
250		printf("boot: %s\n", boot);
251		printf("dev: %s\n", dev);
252	}
253
254	if ((bp = load_boot(boot, &bootsize)) == NULL)
255		return 1;
256
257	if ((devfd = open(dev, O_RDONLY, 0)) < 0) {
258		warn("open: %s", dev);
259		goto out;
260	}
261
262	if (load_prep_partition(devfd, &ppp)) {
263		warn("load_prep_partition");
264		goto out;
265	}
266
267	if (bootsize + dbtob(2) > dbtob(ppp.mbrp_size)) {
268		warn("boot image is too big.");
269		goto out;
270	}
271
272	close(devfd);
273
274	if (nowrite) {
275		free(bp);
276		return 0;
277	}
278
279	if ((devfd = open(dev, O_RDWR, 0)) < 0) {
280		warn("open: %s", dev);
281		goto out;
282	}
283
284	/*
285	 * Write boot image.
286	 */
287	memset(boot00, 0, sizeof(boot00));
288	(void)lseek(devfd, (off_t)dbtob(ppp.mbrp_start), SEEK_SET);
289	if (write(devfd, boot00, sizeof(boot00)) != sizeof(boot00)) {
290		warn("write boot00(prep mbr)");
291		goto out;
292	}
293
294	(void)lseek(devfd, (off_t)dbtob(ppp.mbrp_start+1), SEEK_SET);
295	boot00[0] = htole32(dbtob(2));
296	boot00[1] = htole32(bootsize);
297	if (write(devfd, boot00, sizeof(boot00)) != sizeof(boot00)) {
298		warn("write boot00(prep start/size)");
299		goto out;
300	}
301
302	if (devread(devfd, boot00, 1, DEV_BSIZE, "start/size") != 0)
303		goto out;
304	boot00[0] = htole32(dbtob(ppp.mbrp_start));
305	boot00[1] = htole32(bootsize + dbtob(2));
306	(void)lseek(devfd, (off_t)dbtob(1), SEEK_SET);
307	if (write(devfd, boot00, sizeof(boot00)) != sizeof(boot00)) {
308		warn("write boot00(master start/size)");
309		goto out;
310	}
311
312	(void)lseek(devfd, (off_t)dbtob(ppp.mbrp_start+2), SEEK_SET);
313	if (write(devfd, bp, bootsize) != bootsize) {
314		warn("write boot loader");
315		goto out;
316	}
317
318	close(devfd);
319	free(bp);
320	return 0;
321
322out:
323	if (devfd >= 0)
324		close(devfd);
325	if (bp != NULL)
326		free(bp);
327	return 1;
328}
329