1/*	$OpenBSD: mkuboot.c,v 1.11 2021/06/22 14:52:33 jmc Exp $	*/
2
3/*
4 * Copyright (c) 2008 Mark Kettenis
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/stat.h>
21
22#include <elf.h>
23#include <err.h>
24#include <fcntl.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <time.h>
30#include <unistd.h>
31#include <zlib.h>
32
33#define IH_OS_OPENBSD		1 /* OpenBSD */
34#define IH_OS_LINUX		5 /* Linux */
35
36#define IH_ARCH_ALPHA           1       /* Alpha        */
37#define IH_ARCH_ARM             2       /* ARM          */
38#define IH_ARCH_I386            3       /* Intel x86    */
39#define IH_ARCH_MIPS            5       /* MIPS         */
40#define IH_ARCH_MIPS64          6       /* MIPS  64 Bit */
41#define IH_ARCH_PPC             7       /* PowerPC      */
42#define IH_ARCH_SH              9       /* SuperH       */
43#define IH_ARCH_SPARC           10      /* Sparc        */
44#define IH_ARCH_SPARC64         11      /* Sparc 64 Bit */
45#define IH_ARCH_M68K            12      /* M68K         */
46#define IH_ARCH_ARM64           22      /* AARCH64      */
47#define IH_ARCH_X86_64          24      /* AMD64        */
48
49#define IH_TYPE_STANDALONE	1 /* Standalone */
50#define IH_TYPE_KERNEL		2 /* OS Kernel Image */
51#define IH_TYPE_SCRIPT		6 /* Script file */
52
53#define IH_COMP_NONE		0 /* No compression */
54
55#define IH_MAGIC		0x27051956	/* Image Magic Number */
56#define IH_NMLEN		32 		/* Image Name Length */
57
58struct image_header {
59	uint32_t	ih_magic;
60	uint32_t	ih_hcrc;
61	uint32_t	ih_time;
62	uint32_t	ih_size;
63	uint32_t	ih_load;
64	uint32_t	ih_ep;
65	uint32_t	ih_dcrc;
66	uint8_t		ih_os;
67	uint8_t		ih_arch;
68	uint8_t		ih_type;
69	uint8_t		ih_comp;
70	uint8_t		ih_name[IH_NMLEN];
71};
72
73extern char *__progname;
74
75extern u_long	elf32_copy_elf(int, const char *, int, const char *, u_long,
76	    struct image_header *);
77extern u_long	elf64_copy_elf(int, const char *, int, const char *, u_long,
78	    struct image_header *);
79
80u_long	copy_data(int, const char *, int, const char *, u_long,
81	    struct image_header *, Elf_Word);
82u_long	(*copy_elf)(int, const char *, int, const char *, u_long,
83	    struct image_header *);
84
85u_long	copy_mem(void *, int, const char *, u_long, struct image_header *,
86	    Elf_Word);
87u_long	copy_raw(int, const char *, int, const char *, u_long,
88	    struct image_header *);
89u_long	fill_zeroes(int, const char *, u_long, struct image_header *, Elf_Word);
90int	is_elf(int, const char *);
91void	usage(void);
92
93struct arch_map {
94	int id;
95	const char *arch;
96};
97
98static const struct arch_map archmap[] = {
99    { IH_ARCH_ARM64,	"aarch64" },
100    { IH_ARCH_ALPHA,	"alpha" },
101    { IH_ARCH_X86_64,	"amd64" },
102    { IH_ARCH_ARM,	"arm" },
103    { IH_ARCH_I386,	"i386" },
104    { IH_ARCH_M68K,	"m68k" },
105    { IH_ARCH_MIPS,	"mips" },
106    { IH_ARCH_MIPS64,	"mips64" },
107    { IH_ARCH_PPC,	"powerpc" },
108    { IH_ARCH_SPARC,	"sparc" },
109    { IH_ARCH_SPARC64,	"sparc64" },
110    { IH_ARCH_SH,	"superh" },
111    { 0, NULL }
112};
113
114struct type_map {
115	int id;
116	const char *type;
117};
118static const struct type_map typemap[] = {
119    { IH_TYPE_STANDALONE,	"standalone" },
120    { IH_TYPE_KERNEL,		"kernel" },
121    { IH_TYPE_SCRIPT,		"script" },
122    { 0, NULL }
123};
124
125struct os_map {
126	int id;
127	const char *arch;
128};
129
130static const struct os_map osmap[] = {
131    { IH_OS_OPENBSD,	"OpenBSD" },
132    { IH_OS_LINUX,	"Linux" },
133    { 0, NULL }
134};
135
136
137int
138main(int argc, char *argv[])
139{
140	struct image_header ih;
141	struct stat sb;
142	const struct arch_map *mapptr;
143	const struct os_map *osmapptr;
144	const struct type_map *typemapptr;
145	const char *iname, *oname;
146	const char *arch = MACHINE_ARCH;
147	const char *os = "OpenBSD";
148	const char *type = "kernel";
149	const char *imgname = "boot";
150	int ifd, ofd;
151	uint32_t fsize;
152	u_long crc;
153	int c, ep, load;
154
155	ep = load = 0;
156	while ((c = getopt(argc, argv, "a:e:l:n:o:t:")) != -1) {
157		switch (c) {
158		case 'a':
159			arch = optarg;
160			break;
161		case 'e':
162			sscanf(optarg, "0x%x", &ep);
163			break;
164		case 'l':
165			sscanf(optarg, "0x%x", &load);
166			break;
167		case 'n':
168			imgname = optarg;
169			break;
170		case 'o':
171			os = optarg;
172			break;
173		case 't':
174			type = optarg;
175			break;
176		default:
177			usage();
178		}
179	}
180
181	for (mapptr = archmap; mapptr->arch; mapptr++)
182		if (strcasecmp(arch, mapptr->arch) == 0)
183			break;
184
185	if (mapptr->arch == NULL) {
186		printf("unknown arch '%s'\n", arch);
187		usage();
188	}
189
190	for (osmapptr = osmap; osmapptr->arch; osmapptr++)
191		if (strcasecmp(os, osmapptr->arch) == 0)
192			break;
193
194	if (osmapptr->arch == NULL) {
195		printf("unknown OS '%s'\n", os);
196		usage();
197	}
198
199	for (typemapptr = typemap; typemapptr->type; typemapptr++)
200		if (strcasecmp(type, typemapptr->type) == 0)
201			break;
202
203	if (typemapptr->type == NULL) {
204		printf("unknown type '%s'\n", os);
205		usage();
206	}
207
208	if (argc - optind != 2)
209		usage();
210
211	iname = argv[optind++];
212	oname = argv[optind++];
213
214	/* Initialize U-Boot header. */
215	bzero(&ih, sizeof ih);
216	ih.ih_magic = htobe32(IH_MAGIC);
217	ih.ih_time = htobe32(time(NULL));
218	ih.ih_load = htobe32(load);
219	ih.ih_ep = htobe32(ep);
220	ih.ih_os = osmapptr->id;
221	ih.ih_arch = mapptr->id;
222	ih.ih_type = typemapptr->id;
223	ih.ih_comp = IH_COMP_NONE;
224	strlcpy((char *)ih.ih_name, imgname, sizeof ih.ih_name);
225
226	ifd = open(iname, O_RDONLY);
227	if (ifd == -1)
228		err(1, "%s", iname);
229	if (fstat(ifd, &sb) == -1)
230		err(1, "%s", iname);
231
232	ofd = open(oname, O_RDWR | O_TRUNC | O_CREAT, 0644);
233	if (ofd == -1)
234		err(1, "%s", oname);
235
236	if (pledge("stdio", NULL) == -1)
237		err(1, "pledge");
238
239	/* Write initial header. */
240	if (write(ofd, &ih, sizeof ih) != sizeof ih)
241		err(1, "%s", oname);
242
243	/* Write data, calculating the data CRC as we go. */
244	crc = crc32(0L, Z_NULL, 0);
245
246	if (ih.ih_type == IH_TYPE_SCRIPT) {
247		/* scripts have two extra words of size/pad */
248		fsize = htobe32(sb.st_size);
249		crc = crc32(crc, (Bytef *)&fsize, sizeof(fsize));
250		if (write(ofd, &fsize, sizeof fsize) != sizeof fsize)
251			err(1, "%s", oname);
252		fsize = 0;
253		crc = crc32(crc, (Bytef *)&fsize, sizeof(fsize));
254		if (write(ofd, &fsize, sizeof fsize) != sizeof fsize)
255			err(1, "%s", oname);
256	}
257
258	if (is_elf(ifd, iname))
259		crc = copy_elf(ifd, iname, ofd, oname, crc, &ih);
260	else
261		crc = copy_raw(ifd, iname, ofd, oname, crc, &ih);
262	ih.ih_dcrc = htobe32(crc);
263
264	if (ih.ih_type == IH_TYPE_SCRIPT) {
265		ih.ih_size += 8; /* two extra pad words */
266	}
267
268	ih.ih_size = htobe32(ih.ih_size);
269
270	/* Calculate header CRC. */
271	crc = crc32(0, (Bytef *)&ih, sizeof ih);
272	ih.ih_hcrc = htobe32(crc);
273
274	/* Write finalized header. */
275	if (lseek(ofd, 0, SEEK_SET) != 0)
276		err(1, "%s", oname);
277	if (write(ofd, &ih, sizeof ih) != sizeof ih)
278		err(1, "%s", oname);
279
280	return(0);
281}
282
283int
284is_elf(int ifd, const char *iname)
285{
286	ssize_t nbytes;
287	Elf_Ehdr ehdr;
288
289	nbytes = read(ifd, &ehdr, sizeof ehdr);
290	if (nbytes == -1)
291		err(1, "%s", iname);
292	if (lseek(ifd, 0, SEEK_SET) != 0)
293		err(1, "%s", iname);
294
295	if (nbytes != sizeof ehdr || !IS_ELF(ehdr))
296		return 0;
297
298	if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
299		copy_elf = elf32_copy_elf;
300	else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
301		copy_elf = elf64_copy_elf;
302	else
303		err(1, "%s: invalid elf, not 32 or 64 bit", iname);
304	return 1;
305}
306
307u_long
308copy_data(int ifd, const char *iname, int ofd, const char *oname, u_long crc,
309    struct image_header *ih, Elf_Word size)
310{
311	ssize_t nbytes, chunk;
312	char buf[BUFSIZ];
313
314	while (size != 0) {
315		chunk = size > BUFSIZ ? BUFSIZ : size;
316		nbytes = read(ifd, buf, chunk);
317		if (nbytes != chunk)
318			err(1, "%s", iname);
319		if (write(ofd, buf, nbytes) != nbytes)
320			err(1, "%s", oname);
321		crc = crc32(crc, (Bytef *)buf, nbytes);
322		ih->ih_size += nbytes;
323		size -= nbytes;
324	}
325
326	return crc;
327}
328
329u_long
330copy_mem(void *mem, int ofd, const char *oname, u_long crc,
331    struct image_header *ih, Elf_Word size)
332{
333	ssize_t nbytes;
334	char *memp = (char *)mem;
335
336	while (size != 0) {
337		nbytes = size > BUFSIZ ? BUFSIZ : size;
338		if (write(ofd, memp, nbytes) != nbytes)
339			err(1, "%s", oname);
340		crc = crc32(crc, (Bytef *)memp, nbytes);
341		memp += nbytes;
342		ih->ih_size += nbytes;
343		size -= nbytes;
344	}
345
346	return crc;
347}
348
349u_long
350fill_zeroes(int ofd, const char *oname, u_long crc, struct image_header *ih,
351    Elf_Word size)
352{
353	ssize_t nbytes, chunk;
354	char buf[BUFSIZ];
355
356	memset(buf, 0, BUFSIZ);
357	while (size != 0) {
358		chunk = size > BUFSIZ ? BUFSIZ : size;
359		nbytes = write(ofd, buf, chunk);
360		if (nbytes != chunk)
361			err(1, "%s", oname);
362		crc = crc32(crc, (Bytef *)buf, nbytes);
363		ih->ih_size += nbytes;
364		size -= nbytes;
365	}
366
367	return crc;
368}
369
370u_long
371copy_raw(int ifd, const char *iname, int ofd, const char *oname, u_long crc,
372    struct image_header *ih)
373{
374	ssize_t nbytes;
375	char buf[BUFSIZ];
376
377	while ((nbytes = read(ifd, buf, sizeof buf)) != 0) {
378		if (nbytes == -1)
379			err(1, "%s", iname);
380		if (write(ofd, buf, nbytes) != nbytes)
381			err(1, "%s", oname);
382		crc = crc32(crc, (Bytef *)buf, nbytes);
383		ih->ih_size += nbytes;
384	}
385
386	return crc;
387}
388
389void
390usage(void)
391{
392	const struct arch_map *mapptr;
393	const struct os_map *osmapptr;
394
395	(void)fprintf(stderr,
396	    "usage: %s [-a arch] [-e entry] [-l loadaddr] [-n name] [-o os] "
397	    "[-t type] infile outfile\n", __progname);
398
399	exit(1);
400}
401