1 /* $NetBSD: mkubootimage.c,v 1.24 2018/02/04 17:33:34 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 Jared D. McNeill <jmcneill@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.24 2018/02/04 17:33: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 78 static const struct uboot_image_format { 79 enum image_format format; 80 const char *name; 81 } uboot_image_format[] = { 82 { FMT_UIMG, "uimg" }, 83 { FMT_ARM64, "arm64" }, 84 }; 85 86 static enum image_format 87 get_image_format(const char *name) 88 { 89 unsigned int i; 90 91 for (i = 0; i < __arraycount(uboot_image_format); i++) { 92 if (strcmp(uboot_image_format[i].name, name) == 0) 93 return uboot_image_format[i].format; 94 } 95 96 return FMT_UNKNOWN; 97 } 98 99 static const char * 100 get_image_format_name(enum image_format format) 101 { 102 unsigned int i; 103 104 for (i = 0; i < __arraycount(uboot_image_format); i++) { 105 if (uboot_image_format[i].format == format) 106 return uboot_image_format[i].name; 107 } 108 109 return "Unknown"; 110 } 111 112 static const struct uboot_os { 113 enum uboot_image_os os; 114 const char *name; 115 } uboot_os[] = { 116 { IH_OS_OPENBSD, "openbsd" }, 117 { IH_OS_NETBSD, "netbsd" }, 118 { IH_OS_FREEBSD, "freebsd" }, 119 { IH_OS_LINUX, "linux" }, 120 }; 121 122 static enum uboot_image_os 123 get_os(const char *name) 124 { 125 unsigned int i; 126 127 for (i = 0; i < __arraycount(uboot_os); i++) { 128 if (strcmp(uboot_os[i].name, name) == 0) 129 return uboot_os[i].os; 130 } 131 132 return IH_OS_UNKNOWN; 133 } 134 135 static const char * 136 get_os_name(enum uboot_image_os os) 137 { 138 unsigned int i; 139 140 for (i = 0; i < __arraycount(uboot_os); i++) { 141 if (uboot_os[i].os == os) 142 return uboot_os[i].name; 143 } 144 145 return "Unknown"; 146 } 147 148 static const struct uboot_arch { 149 enum uboot_image_arch arch; 150 const char *name; 151 } uboot_arch[] = { 152 { IH_ARCH_ARM, "arm" }, 153 { IH_ARCH_ARM64, "arm64" }, 154 { IH_ARCH_I386, "i386" }, 155 { IH_ARCH_MIPS, "mips" }, 156 { IH_ARCH_MIPS64, "mips64" }, 157 { IH_ARCH_PPC, "powerpc" }, 158 { IH_ARCH_OPENRISC, "or1k" }, 159 { IH_ARCH_SH, "sh" }, 160 }; 161 162 static enum uboot_image_arch 163 get_arch(const char *name) 164 { 165 unsigned int i; 166 167 for (i = 0; i < __arraycount(uboot_arch); i++) { 168 if (strcmp(uboot_arch[i].name, name) == 0) 169 return uboot_arch[i].arch; 170 } 171 172 return IH_ARCH_UNKNOWN; 173 } 174 175 static const char * 176 get_arch_name(enum uboot_image_arch arch) 177 { 178 unsigned int i; 179 180 for (i = 0; i < __arraycount(uboot_arch); i++) { 181 if (uboot_arch[i].arch == arch) 182 return uboot_arch[i].name; 183 } 184 185 return "Unknown"; 186 } 187 188 static const struct uboot_type { 189 enum uboot_image_type type; 190 const char *name; 191 } uboot_type[] = { 192 { IH_TYPE_STANDALONE, "standalone" }, 193 { IH_TYPE_KERNEL, "kernel" }, 194 { IH_TYPE_KERNEL_NOLOAD, "kernel_noload" }, 195 { IH_TYPE_RAMDISK, "ramdisk" }, 196 { IH_TYPE_FILESYSTEM, "fs" }, 197 { IH_TYPE_SCRIPT, "script" }, 198 }; 199 200 static enum uboot_image_type 201 get_type(const char *name) 202 { 203 unsigned int i; 204 205 for (i = 0; i < __arraycount(uboot_type); i++) { 206 if (strcmp(uboot_type[i].name, name) == 0) 207 return uboot_type[i].type; 208 } 209 210 return IH_TYPE_UNKNOWN; 211 } 212 213 static const char * 214 get_type_name(enum uboot_image_type type) 215 { 216 unsigned int i; 217 218 for (i = 0; i < __arraycount(uboot_type); i++) { 219 if (uboot_type[i].type == type) 220 return uboot_type[i].name; 221 } 222 223 return "Unknown"; 224 } 225 226 static const struct uboot_comp { 227 enum uboot_image_comp comp; 228 const char *name; 229 } uboot_comp[] = { 230 { IH_COMP_NONE, "none" }, 231 { IH_COMP_GZIP, "gz" }, 232 { IH_COMP_BZIP2, "bz2" }, 233 { IH_COMP_LZMA, "lzma" }, 234 { IH_COMP_LZO, "lzo" }, 235 }; 236 237 static enum uboot_image_comp 238 get_comp(const char *name) 239 { 240 unsigned int i; 241 242 for (i = 0; i < __arraycount(uboot_comp); i++) { 243 if (strcmp(uboot_comp[i].name, name) == 0) 244 return uboot_comp[i].comp; 245 } 246 247 return IH_COMP_NONE; 248 } 249 250 static const char * 251 get_comp_name(enum uboot_image_comp comp) 252 { 253 unsigned int i; 254 255 for (i = 0; i < __arraycount(uboot_comp); i++) { 256 if (uboot_comp[i].comp == comp) 257 return uboot_comp[i].name; 258 } 259 260 return "Unknown"; 261 } 262 263 __dead static void 264 usage(void) 265 { 266 fprintf(stderr, "usage: mkubootimage -A " 267 "<arm|arm64|i386|mips|mips64|or1k|powerpc|sh>"); 268 fprintf(stderr, " -C <none|bz2|gz|lzma|lzo>"); 269 fprintf(stderr, " -O <openbsd|netbsd|freebsd|linux>"); 270 fprintf(stderr, " -T <standalone|kernel|kernel_noload|ramdisk|fs|script>"); 271 fprintf(stderr, " -a <addr> [-e <ep>] [-m <magic>] -n <name>"); 272 fprintf(stderr, " [-f <uimg|arm64>]"); 273 fprintf(stderr, " <srcfile> <dstfile>\n"); 274 275 exit(EXIT_FAILURE); 276 } 277 278 static void 279 dump_header_uimg(struct uboot_image_header *hdr) 280 { 281 time_t tm = ntohl(hdr->ih_time); 282 283 printf(" magic: 0x%08x\n", ntohl(hdr->ih_magic)); 284 printf(" time: %s", ctime(&tm)); 285 printf(" size: %u\n", ntohl(hdr->ih_size)); 286 printf(" load addr: 0x%08x\n", ntohl(hdr->ih_load)); 287 printf(" entry point: 0x%08x\n", ntohl(hdr->ih_ep)); 288 printf(" data crc: 0x%08x\n", ntohl(hdr->ih_dcrc)); 289 printf(" os: %d (%s)\n", hdr->ih_os, 290 get_os_name(hdr->ih_os)); 291 printf(" arch: %d (%s)\n", hdr->ih_arch, 292 get_arch_name(hdr->ih_arch)); 293 printf(" type: %d (%s)\n", hdr->ih_type, 294 get_type_name(hdr->ih_type)); 295 printf(" comp: %d (%s)\n", hdr->ih_comp, 296 get_comp_name(hdr->ih_comp)); 297 printf(" name: %s\n", hdr->ih_name); 298 printf(" header crc: 0x%08x\n", hdr->ih_hcrc); 299 } 300 301 static int 302 generate_header_uimg(struct uboot_image_header *hdr, int kernel_fd) 303 { 304 uint8_t *p; 305 struct stat st; 306 uint32_t crc, dsize, size_buf[2]; 307 int error; 308 309 error = fstat(kernel_fd, &st); 310 if (error == -1) { 311 perror("stat"); 312 return errno; 313 } 314 315 if (st.st_size + sizeof(*hdr) > UINT32_MAX) { 316 fprintf(stderr, "fatal: kernel too big\n"); 317 return EINVAL; 318 } 319 320 p = mmap(0, st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, kernel_fd, 0); 321 if (p == MAP_FAILED) { 322 perror("mmap kernel"); 323 return EINVAL; 324 } 325 if (image_type == IH_TYPE_SCRIPT) { 326 struct iovec iov[3]; 327 dsize = st.st_size + (sizeof(uint32_t) * 2); 328 size_buf[0] = htonl(st.st_size); 329 size_buf[1] = htonl(0); 330 iov[0].iov_base = &size_buf[0]; 331 iov[0].iov_len = sizeof(size_buf[0]); 332 iov[1].iov_base = &size_buf[1]; 333 iov[1].iov_len = sizeof(size_buf[1]); 334 iov[2].iov_base = p; 335 iov[2].iov_len = st.st_size; 336 crc = crc32v(iov, 3); 337 } else { 338 dsize = st.st_size; 339 crc = crc32(p, st.st_size); 340 } 341 munmap(p, st.st_size); 342 343 memset(hdr, 0, sizeof(*hdr)); 344 hdr->ih_magic = htonl(image_magic); 345 hdr->ih_time = htonl(st.st_mtime); 346 hdr->ih_size = htonl(dsize); 347 hdr->ih_load = htonl(image_loadaddr); 348 hdr->ih_ep = htonl(image_entrypoint); 349 hdr->ih_dcrc = htonl(crc); 350 hdr->ih_os = image_os; 351 hdr->ih_arch = image_arch; 352 hdr->ih_type = image_type; 353 hdr->ih_comp = image_comp; 354 strlcpy((char *)hdr->ih_name, image_name, sizeof(hdr->ih_name)); 355 crc = crc32((void *)hdr, sizeof(*hdr)); 356 hdr->ih_hcrc = htonl(crc); 357 358 dump_header_uimg(hdr); 359 360 return 0; 361 } 362 363 static void 364 dump_header_arm64(struct arm64_image_header *hdr) 365 { 366 printf(" magic: 0x%" PRIx32 "\n", le32toh(hdr->magic)); 367 printf(" text offset: 0x%" PRIx64 "\n", le64toh(hdr->text_offset)); 368 printf(" image size: %" PRIu64 "\n", le64toh(hdr->image_size)); 369 printf(" flags: 0x%" PRIx64 "\n", le64toh(hdr->flags)); 370 } 371 372 static int 373 generate_header_arm64(struct arm64_image_header *hdr, int kernel_fd) 374 { 375 struct stat st; 376 uint32_t flags; 377 int error; 378 379 error = fstat(kernel_fd, &st); 380 if (error == -1) { 381 perror("stat"); 382 return errno; 383 } 384 385 flags = 0; 386 flags |= ARM64_FLAGS_PAGE_SIZE_4K; 387 #if 0 388 flags |= ARM64_FLAGS_PHYS_PLACEMENT_ANY; 389 #endif 390 391 memset(hdr, 0, sizeof(*hdr)); 392 hdr->code0 = htole32(ARM64_CODE0); 393 hdr->text_offset = htole64(image_entrypoint); 394 hdr->image_size = htole64(st.st_size + sizeof(*hdr)); 395 hdr->flags = htole32(flags); 396 hdr->magic = htole32(ARM64_MAGIC); 397 398 dump_header_arm64(hdr); 399 400 return 0; 401 } 402 403 static int 404 write_image(void *hdr, size_t hdrlen, int kernel_fd, int image_fd) 405 { 406 uint8_t buf[4096]; 407 ssize_t rlen, wlen; 408 struct stat st; 409 uint32_t size_buf[2]; 410 int error; 411 412 error = fstat(kernel_fd, &st); 413 if (error == -1) { 414 perror("stat"); 415 return errno; 416 } 417 418 wlen = write(image_fd, hdr, hdrlen); 419 if (wlen != (ssize_t)hdrlen) { 420 perror("short write"); 421 return errno; 422 } 423 424 if (image_type == IH_TYPE_SCRIPT) { 425 size_buf[0] = htonl(st.st_size); 426 size_buf[1] = htonl(0); 427 wlen = write(image_fd, &size_buf, sizeof(size_buf)); 428 if (wlen != sizeof(size_buf)) { 429 perror("short write"); 430 return errno; 431 } 432 } 433 434 while ((rlen = read(kernel_fd, buf, sizeof(buf))) > 0) { 435 wlen = write(image_fd, buf, rlen); 436 if (wlen != rlen) { 437 perror("short write"); 438 return errno; 439 } 440 } 441 442 return 0; 443 } 444 445 int 446 main(int argc, char *argv[]) 447 { 448 struct uboot_image_header hdr_uimg; 449 struct arm64_image_header hdr_arm64; 450 const char *src, *dest; 451 char *ep; 452 int kernel_fd, image_fd; 453 int ch; 454 unsigned long long num; 455 456 while ((ch = getopt(argc, argv, "A:C:E:O:T:a:e:f:hm:n:")) != -1) { 457 switch (ch) { 458 case 'A': /* arch */ 459 image_arch = get_arch(optarg); 460 break; 461 case 'C': /* comp */ 462 image_comp = get_comp(optarg); 463 break; 464 case 'O': /* os */ 465 image_os = get_os(optarg); 466 break; 467 case 'T': /* type */ 468 image_type = get_type(optarg); 469 break; 470 case 'a': /* addr */ 471 errno = 0; 472 num = strtoull(optarg, &ep, 0); 473 if (*ep != '\0' || (errno == ERANGE && 474 (num == ULLONG_MAX || num == 0)) || 475 ((signed long long)num != (int32_t)num && 476 num != (uint32_t)num)) 477 errx(1, "illegal number -- %s", optarg); 478 image_loadaddr = (uint32_t)num; 479 break; 480 case 'E': /* ep (byte swapped) */ 481 case 'e': /* ep */ 482 errno = 0; 483 num = strtoull(optarg, &ep, 0); 484 if (*ep != '\0' || (errno == ERANGE && 485 (num == ULLONG_MAX || num == 0)) || 486 ((signed long long)num != (int32_t)num && 487 num != (uint32_t)num)) 488 errx(1, "illegal number -- %s", optarg); 489 image_entrypoint = (uint32_t)num; 490 if (ch == 'E') 491 image_entrypoint = bswap32(image_entrypoint); 492 break; 493 case 'f': /* image format */ 494 image_format = get_image_format(optarg); 495 break; 496 case 'm': /* magic */ 497 errno = 0; 498 num = strtoul(optarg, &ep, 0); 499 if (*ep != '\0' || (errno == ERANGE && 500 (num == ULONG_MAX || num == 0))) 501 errx(1, "illegal number -- %s", optarg); 502 image_magic = (uint32_t)num; 503 break; 504 case 'n': /* name */ 505 image_name = strdup(optarg); 506 break; 507 case 'h': 508 default: 509 usage(); 510 /* NOTREACHED */ 511 } 512 } 513 argc -= optind; 514 argv += optind; 515 516 if (argc != 2) 517 usage(); 518 519 if (image_entrypoint == 0) 520 image_entrypoint = image_loadaddr; 521 522 switch (image_format) { 523 case FMT_UIMG: 524 if (image_arch == IH_ARCH_UNKNOWN || 525 image_type == IH_TYPE_UNKNOWN || 526 image_name == NULL) 527 usage(); 528 /* NOTREACHED */ 529 530 switch (image_type) { 531 case IH_TYPE_SCRIPT: 532 case IH_TYPE_RAMDISK: 533 case IH_TYPE_KERNEL_NOLOAD: 534 break; 535 default: 536 if (image_loadaddr == 0) 537 usage(); 538 /* NOTREACHED */ 539 break; 540 } 541 break; 542 543 case FMT_ARM64: 544 if (image_arch != IH_ARCH_UNKNOWN && 545 image_arch != IH_ARCH_ARM64) 546 usage(); 547 /* NOTREACHED */ 548 549 break; 550 551 default: 552 usage(); 553 /* NOTREACHED */ 554 } 555 556 src = argv[0]; 557 dest = argv[1]; 558 559 kernel_fd = open(src, O_RDONLY); 560 if (kernel_fd == -1) { 561 perror("open kernel"); 562 return EXIT_FAILURE; 563 } 564 image_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0666); 565 if (image_fd == -1) { 566 perror("open image"); 567 return EXIT_FAILURE; 568 } 569 570 printf(" image type: %s\n", get_image_format_name(image_format)); 571 572 switch (image_format) { 573 case FMT_UIMG: 574 if (generate_header_uimg(&hdr_uimg, kernel_fd) != 0) 575 return EXIT_FAILURE; 576 577 if (write_image(&hdr_uimg, sizeof(hdr_uimg), 578 kernel_fd, image_fd) != 0) 579 return EXIT_FAILURE; 580 581 break; 582 case FMT_ARM64: 583 if (generate_header_arm64(&hdr_arm64, kernel_fd) != 0) 584 return EXIT_FAILURE; 585 586 if (write_image(&hdr_arm64, sizeof(hdr_arm64), 587 kernel_fd, image_fd) != 0) 588 return EXIT_FAILURE; 589 590 break; 591 default: 592 break; 593 } 594 595 close(image_fd); 596 close(kernel_fd); 597 598 return EXIT_SUCCESS; 599 } 600