mkubootimage.c revision 1.25 1 /* $NetBSD: mkubootimage.c,v 1.25 2019/12/04 11:21:34 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Jared D. McNeill <jmcneill (at) invisible.ca>
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. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #if HAVE_NBTOOL_CONFIG_H
29 #include "nbtool_config.h"
30 #endif
31
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: mkubootimage.c,v 1.25 2019/12/04 11:21:34 jmcneill Exp $");
34
35 #include <sys/mman.h>
36 #include <sys/stat.h>
37 #include <sys/endian.h>
38 #include <sys/param.h>
39 #include <sys/uio.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <inttypes.h>
44 #include <limits.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <unistd.h>
51
52 #include "uboot.h"
53 #include "arm64.h"
54
55 #ifndef __arraycount
56 #define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
57 #endif
58
59 enum image_format {
60 FMT_UNKNOWN,
61 FMT_UIMG, /* Legacy U-Boot image */
62 FMT_ARM64, /* Linux ARM64 image (booti) */
63 };
64
65 extern uint32_t crc32(const void *, size_t);
66 extern uint32_t crc32v(const struct iovec *, int);
67
68 static enum uboot_image_os image_os = IH_OS_NETBSD;
69 static enum uboot_image_arch image_arch = IH_ARCH_UNKNOWN;
70 static enum uboot_image_type image_type = IH_TYPE_UNKNOWN;
71 static enum uboot_image_comp image_comp = IH_COMP_NONE;
72 static uint32_t image_loadaddr = 0;
73 static uint32_t image_entrypoint = 0;
74 static char *image_name;
75 static uint32_t image_magic = IH_MAGIC;
76 static enum image_format image_format = FMT_UIMG;
77 static int update_image = 0;
78
79 static const struct uboot_image_format {
80 enum image_format format;
81 const char *name;
82 } uboot_image_format[] = {
83 { FMT_UIMG, "uimg" },
84 { FMT_ARM64, "arm64" },
85 };
86
87 static enum image_format
88 get_image_format(const char *name)
89 {
90 unsigned int i;
91
92 for (i = 0; i < __arraycount(uboot_image_format); i++) {
93 if (strcmp(uboot_image_format[i].name, name) == 0)
94 return uboot_image_format[i].format;
95 }
96
97 return FMT_UNKNOWN;
98 }
99
100 static const char *
101 get_image_format_name(enum image_format format)
102 {
103 unsigned int i;
104
105 for (i = 0; i < __arraycount(uboot_image_format); i++) {
106 if (uboot_image_format[i].format == format)
107 return uboot_image_format[i].name;
108 }
109
110 return "Unknown";
111 }
112
113 static const struct uboot_os {
114 enum uboot_image_os os;
115 const char *name;
116 } uboot_os[] = {
117 { IH_OS_OPENBSD, "openbsd" },
118 { IH_OS_NETBSD, "netbsd" },
119 { IH_OS_FREEBSD, "freebsd" },
120 { IH_OS_LINUX, "linux" },
121 };
122
123 static enum uboot_image_os
124 get_os(const char *name)
125 {
126 unsigned int i;
127
128 for (i = 0; i < __arraycount(uboot_os); i++) {
129 if (strcmp(uboot_os[i].name, name) == 0)
130 return uboot_os[i].os;
131 }
132
133 return IH_OS_UNKNOWN;
134 }
135
136 static const char *
137 get_os_name(enum uboot_image_os os)
138 {
139 unsigned int i;
140
141 for (i = 0; i < __arraycount(uboot_os); i++) {
142 if (uboot_os[i].os == os)
143 return uboot_os[i].name;
144 }
145
146 return "Unknown";
147 }
148
149 static const struct uboot_arch {
150 enum uboot_image_arch arch;
151 const char *name;
152 } uboot_arch[] = {
153 { IH_ARCH_ARM, "arm" },
154 { IH_ARCH_ARM64, "arm64" },
155 { IH_ARCH_I386, "i386" },
156 { IH_ARCH_MIPS, "mips" },
157 { IH_ARCH_MIPS64, "mips64" },
158 { IH_ARCH_PPC, "powerpc" },
159 { IH_ARCH_OPENRISC, "or1k" },
160 { IH_ARCH_SH, "sh" },
161 };
162
163 static enum uboot_image_arch
164 get_arch(const char *name)
165 {
166 unsigned int i;
167
168 for (i = 0; i < __arraycount(uboot_arch); i++) {
169 if (strcmp(uboot_arch[i].name, name) == 0)
170 return uboot_arch[i].arch;
171 }
172
173 return IH_ARCH_UNKNOWN;
174 }
175
176 static const char *
177 get_arch_name(enum uboot_image_arch arch)
178 {
179 unsigned int i;
180
181 for (i = 0; i < __arraycount(uboot_arch); i++) {
182 if (uboot_arch[i].arch == arch)
183 return uboot_arch[i].name;
184 }
185
186 return "Unknown";
187 }
188
189 static const struct uboot_type {
190 enum uboot_image_type type;
191 const char *name;
192 } uboot_type[] = {
193 { IH_TYPE_STANDALONE, "standalone" },
194 { IH_TYPE_KERNEL, "kernel" },
195 { IH_TYPE_KERNEL_NOLOAD, "kernel_noload" },
196 { IH_TYPE_RAMDISK, "ramdisk" },
197 { IH_TYPE_FILESYSTEM, "fs" },
198 { IH_TYPE_SCRIPT, "script" },
199 };
200
201 static enum uboot_image_type
202 get_type(const char *name)
203 {
204 unsigned int i;
205
206 for (i = 0; i < __arraycount(uboot_type); i++) {
207 if (strcmp(uboot_type[i].name, name) == 0)
208 return uboot_type[i].type;
209 }
210
211 return IH_TYPE_UNKNOWN;
212 }
213
214 static const char *
215 get_type_name(enum uboot_image_type type)
216 {
217 unsigned int i;
218
219 for (i = 0; i < __arraycount(uboot_type); i++) {
220 if (uboot_type[i].type == type)
221 return uboot_type[i].name;
222 }
223
224 return "Unknown";
225 }
226
227 static const struct uboot_comp {
228 enum uboot_image_comp comp;
229 const char *name;
230 } uboot_comp[] = {
231 { IH_COMP_NONE, "none" },
232 { IH_COMP_GZIP, "gz" },
233 { IH_COMP_BZIP2, "bz2" },
234 { IH_COMP_LZMA, "lzma" },
235 { IH_COMP_LZO, "lzo" },
236 };
237
238 static enum uboot_image_comp
239 get_comp(const char *name)
240 {
241 unsigned int i;
242
243 for (i = 0; i < __arraycount(uboot_comp); i++) {
244 if (strcmp(uboot_comp[i].name, name) == 0)
245 return uboot_comp[i].comp;
246 }
247
248 return IH_COMP_NONE;
249 }
250
251 static const char *
252 get_comp_name(enum uboot_image_comp comp)
253 {
254 unsigned int i;
255
256 for (i = 0; i < __arraycount(uboot_comp); i++) {
257 if (uboot_comp[i].comp == comp)
258 return uboot_comp[i].name;
259 }
260
261 return "Unknown";
262 }
263
264 __dead static void
265 usage(void)
266 {
267 fprintf(stderr, "usage: mkubootimage -A "
268 "<arm|arm64|i386|mips|mips64|or1k|powerpc|sh>");
269 fprintf(stderr, " -C <none|bz2|gz|lzma|lzo>");
270 fprintf(stderr, " -O <openbsd|netbsd|freebsd|linux>");
271 fprintf(stderr, " -T <standalone|kernel|kernel_noload|ramdisk|fs|script>");
272 fprintf(stderr, " -a <addr> [-e <ep>] [-m <magic>] -n <name>");
273 fprintf(stderr, " [-f <uimg|arm64>] [-u]");
274 fprintf(stderr, " <srcfile> <dstfile>\n");
275
276 exit(EXIT_FAILURE);
277 }
278
279 static void
280 dump_header_uimg(struct uboot_image_header *hdr)
281 {
282 time_t tm = ntohl(hdr->ih_time);
283
284 printf(" magic: 0x%08x\n", ntohl(hdr->ih_magic));
285 printf(" time: %s", ctime(&tm));
286 printf(" size: %u\n", ntohl(hdr->ih_size));
287 printf(" load addr: 0x%08x\n", ntohl(hdr->ih_load));
288 printf(" entry point: 0x%08x\n", ntohl(hdr->ih_ep));
289 printf(" data crc: 0x%08x\n", ntohl(hdr->ih_dcrc));
290 printf(" os: %d (%s)\n", hdr->ih_os,
291 get_os_name(hdr->ih_os));
292 printf(" arch: %d (%s)\n", hdr->ih_arch,
293 get_arch_name(hdr->ih_arch));
294 printf(" type: %d (%s)\n", hdr->ih_type,
295 get_type_name(hdr->ih_type));
296 printf(" comp: %d (%s)\n", hdr->ih_comp,
297 get_comp_name(hdr->ih_comp));
298 printf(" name: %s\n", hdr->ih_name);
299 printf(" header crc: 0x%08x\n", hdr->ih_hcrc);
300 }
301
302 static int
303 generate_header_uimg(struct uboot_image_header *hdr, int kernel_fd)
304 {
305 uint8_t *p;
306 struct stat st;
307 uint32_t crc, dsize, size_buf[2];
308 int error;
309
310 error = fstat(kernel_fd, &st);
311 if (error == -1) {
312 perror("stat");
313 return errno;
314 }
315
316 if (st.st_size + sizeof(*hdr) > UINT32_MAX) {
317 fprintf(stderr, "fatal: kernel too big\n");
318 return EINVAL;
319 }
320
321 p = mmap(0, st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, kernel_fd, 0);
322 if (p == MAP_FAILED) {
323 perror("mmap kernel");
324 return EINVAL;
325 }
326 if (image_type == IH_TYPE_SCRIPT) {
327 struct iovec iov[3];
328 dsize = st.st_size + (sizeof(uint32_t) * 2);
329 size_buf[0] = htonl(st.st_size);
330 size_buf[1] = htonl(0);
331 iov[0].iov_base = &size_buf[0];
332 iov[0].iov_len = sizeof(size_buf[0]);
333 iov[1].iov_base = &size_buf[1];
334 iov[1].iov_len = sizeof(size_buf[1]);
335 iov[2].iov_base = p;
336 iov[2].iov_len = st.st_size;
337 crc = crc32v(iov, 3);
338 } else {
339 dsize = st.st_size;
340 crc = crc32(p, st.st_size);
341 }
342 munmap(p, st.st_size);
343
344 memset(hdr, 0, sizeof(*hdr));
345 hdr->ih_magic = htonl(image_magic);
346 hdr->ih_time = htonl(st.st_mtime);
347 hdr->ih_size = htonl(dsize);
348 hdr->ih_load = htonl(image_loadaddr);
349 hdr->ih_ep = htonl(image_entrypoint);
350 hdr->ih_dcrc = htonl(crc);
351 hdr->ih_os = image_os;
352 hdr->ih_arch = image_arch;
353 hdr->ih_type = image_type;
354 hdr->ih_comp = image_comp;
355 strlcpy((char *)hdr->ih_name, image_name, sizeof(hdr->ih_name));
356 crc = crc32((void *)hdr, sizeof(*hdr));
357 hdr->ih_hcrc = htonl(crc);
358
359 dump_header_uimg(hdr);
360
361 return 0;
362 }
363
364 static void
365 dump_header_arm64(struct arm64_image_header *hdr)
366 {
367 printf(" magic: 0x%" PRIx32 "\n", le32toh(hdr->magic));
368 printf(" text offset: 0x%" PRIx64 "\n", le64toh(hdr->text_offset));
369 printf(" image size: %" PRIu64 "\n", le64toh(hdr->image_size));
370 printf(" flags: 0x%" PRIx64 "\n", le64toh(hdr->flags));
371 }
372
373 static int
374 generate_header_arm64(struct arm64_image_header *hdr, int kernel_fd)
375 {
376 struct stat st;
377 uint32_t flags;
378 int error;
379
380 error = fstat(kernel_fd, &st);
381 if (error == -1) {
382 perror("stat");
383 return errno;
384 }
385
386 flags = 0;
387 flags |= ARM64_FLAGS_PAGE_SIZE_4K;
388 #if 0
389 flags |= ARM64_FLAGS_PHYS_PLACEMENT_ANY;
390 #endif
391
392 memset(hdr, 0, sizeof(*hdr));
393 hdr->code0 = htole32(ARM64_CODE0);
394 hdr->text_offset = htole64(image_entrypoint);
395 hdr->image_size = htole64(st.st_size + sizeof(*hdr));
396 hdr->flags = htole32(flags);
397 hdr->magic = htole32(ARM64_MAGIC);
398
399 dump_header_arm64(hdr);
400
401 return 0;
402 }
403
404 static int
405 write_image(void *hdr, size_t hdrlen, int kernel_fd, int image_fd)
406 {
407 uint8_t buf[4096];
408 ssize_t rlen, wlen;
409 struct stat st;
410 uint32_t size_buf[2];
411 int error;
412
413 error = fstat(kernel_fd, &st);
414 if (error == -1) {
415 perror("stat");
416 return errno;
417 }
418
419 wlen = write(image_fd, hdr, hdrlen);
420 if (wlen != (ssize_t)hdrlen) {
421 perror("short write");
422 return errno;
423 }
424
425 if (image_type == IH_TYPE_SCRIPT) {
426 size_buf[0] = htonl(st.st_size);
427 size_buf[1] = htonl(0);
428 wlen = write(image_fd, &size_buf, sizeof(size_buf));
429 if (wlen != sizeof(size_buf)) {
430 perror("short write");
431 return errno;
432 }
433 }
434
435 if (update_image) {
436 if (lseek(kernel_fd, hdrlen, SEEK_SET) != hdrlen) {
437 perror("seek failed");
438 return errno;
439 }
440 }
441
442 while ((rlen = read(kernel_fd, buf, sizeof(buf))) > 0) {
443 wlen = write(image_fd, buf, rlen);
444 if (wlen != rlen) {
445 perror("short write");
446 return errno;
447 }
448 }
449
450 return 0;
451 }
452
453 int
454 main(int argc, char *argv[])
455 {
456 struct uboot_image_header hdr_uimg;
457 struct arm64_image_header hdr_arm64;
458 const char *src, *dest;
459 char *ep;
460 int kernel_fd, image_fd;
461 int ch;
462 unsigned long long num;
463
464 while ((ch = getopt(argc, argv, "A:C:E:O:T:a:e:f:hm:n:u")) != -1) {
465 switch (ch) {
466 case 'A': /* arch */
467 image_arch = get_arch(optarg);
468 break;
469 case 'C': /* comp */
470 image_comp = get_comp(optarg);
471 break;
472 case 'O': /* os */
473 image_os = get_os(optarg);
474 break;
475 case 'T': /* type */
476 image_type = get_type(optarg);
477 break;
478 case 'a': /* addr */
479 errno = 0;
480 num = strtoull(optarg, &ep, 0);
481 if (*ep != '\0' || (errno == ERANGE &&
482 (num == ULLONG_MAX || num == 0)) ||
483 ((signed long long)num != (int32_t)num &&
484 num != (uint32_t)num))
485 errx(1, "illegal number -- %s", optarg);
486 image_loadaddr = (uint32_t)num;
487 break;
488 case 'E': /* ep (byte swapped) */
489 case 'e': /* ep */
490 errno = 0;
491 num = strtoull(optarg, &ep, 0);
492 if (*ep != '\0' || (errno == ERANGE &&
493 (num == ULLONG_MAX || num == 0)) ||
494 ((signed long long)num != (int32_t)num &&
495 num != (uint32_t)num))
496 errx(1, "illegal number -- %s", optarg);
497 image_entrypoint = (uint32_t)num;
498 if (ch == 'E')
499 image_entrypoint = bswap32(image_entrypoint);
500 break;
501 case 'f': /* image format */
502 image_format = get_image_format(optarg);
503 break;
504 case 'm': /* magic */
505 errno = 0;
506 num = strtoul(optarg, &ep, 0);
507 if (*ep != '\0' || (errno == ERANGE &&
508 (num == ULONG_MAX || num == 0)))
509 errx(1, "illegal number -- %s", optarg);
510 image_magic = (uint32_t)num;
511 break;
512 case 'n': /* name */
513 image_name = strdup(optarg);
514 break;
515 case 'u': /* update image */
516 update_image = 1;
517 break;
518 case 'h':
519 default:
520 usage();
521 /* NOTREACHED */
522 }
523 }
524 argc -= optind;
525 argv += optind;
526
527 if (argc != 2)
528 usage();
529
530 if (image_entrypoint == 0)
531 image_entrypoint = image_loadaddr;
532
533 switch (image_format) {
534 case FMT_UIMG:
535 if (image_arch == IH_ARCH_UNKNOWN ||
536 image_type == IH_TYPE_UNKNOWN ||
537 image_name == NULL)
538 usage();
539 /* NOTREACHED */
540
541 switch (image_type) {
542 case IH_TYPE_SCRIPT:
543 case IH_TYPE_RAMDISK:
544 case IH_TYPE_KERNEL_NOLOAD:
545 break;
546 default:
547 if (image_loadaddr == 0)
548 usage();
549 /* NOTREACHED */
550 break;
551 }
552 break;
553
554 case FMT_ARM64:
555 if (image_arch != IH_ARCH_UNKNOWN &&
556 image_arch != IH_ARCH_ARM64)
557 usage();
558 /* NOTREACHED */
559
560 break;
561
562 default:
563 usage();
564 /* NOTREACHED */
565 }
566
567 src = argv[0];
568 dest = argv[1];
569
570 kernel_fd = open(src, O_RDONLY);
571 if (kernel_fd == -1) {
572 perror("open kernel");
573 return EXIT_FAILURE;
574 }
575 image_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0666);
576 if (image_fd == -1) {
577 perror("open image");
578 return EXIT_FAILURE;
579 }
580
581 printf(" image type: %s\n", get_image_format_name(image_format));
582
583 switch (image_format) {
584 case FMT_UIMG:
585 if (generate_header_uimg(&hdr_uimg, kernel_fd) != 0)
586 return EXIT_FAILURE;
587
588 if (write_image(&hdr_uimg, sizeof(hdr_uimg),
589 kernel_fd, image_fd) != 0)
590 return EXIT_FAILURE;
591
592 break;
593 case FMT_ARM64:
594 if (generate_header_arm64(&hdr_arm64, kernel_fd) != 0)
595 return EXIT_FAILURE;
596
597 if (write_image(&hdr_arm64, sizeof(hdr_arm64),
598 kernel_fd, image_fd) != 0)
599 return EXIT_FAILURE;
600
601 break;
602 default:
603 break;
604 }
605
606 close(image_fd);
607 close(kernel_fd);
608
609 return EXIT_SUCCESS;
610 }
611