1 /* $NetBSD: mkubootimage.c,v 1.30 2020/02/08 13:27:00 ryo 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.30 2020/02/08 13:27:00 ryo 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 [-hu] -A " 268 "<arm|arm64|i386|mips|mips64|or1k|powerpc|sh> -a address\n"); 269 fprintf(stderr, "\t-C <bz2|gz|lzma|lzo|none> [-E address] [-e address]\n"); 270 fprintf(stderr, "\t[-f <arm64|uimg>] [-m magic] -n image -O <freebsd|linux|netbsd|openbsd>\n"); 271 fprintf(stderr, "\t-T <fs|kernel|kernel_noload|ramdisk|script|standalone>\n"); 272 fprintf(stderr, "\tsource destination\n"); 273 274 exit(EXIT_FAILURE); 275 } 276 277 static void 278 dump_header_uimg(struct uboot_image_header *hdr) 279 { 280 time_t tm = ntohl(hdr->ih_time); 281 282 printf(" magic: 0x%08x\n", ntohl(hdr->ih_magic)); 283 printf(" time: %s", ctime(&tm)); 284 printf(" size: %u\n", ntohl(hdr->ih_size)); 285 printf(" load addr: 0x%08x\n", ntohl(hdr->ih_load)); 286 printf(" entry point: 0x%08x\n", ntohl(hdr->ih_ep)); 287 printf(" data crc: 0x%08x\n", ntohl(hdr->ih_dcrc)); 288 printf(" os: %d (%s)\n", hdr->ih_os, 289 get_os_name(hdr->ih_os)); 290 printf(" arch: %d (%s)\n", hdr->ih_arch, 291 get_arch_name(hdr->ih_arch)); 292 printf(" type: %d (%s)\n", hdr->ih_type, 293 get_type_name(hdr->ih_type)); 294 printf(" comp: %d (%s)\n", hdr->ih_comp, 295 get_comp_name(hdr->ih_comp)); 296 printf(" name: %s\n", hdr->ih_name); 297 printf(" header crc: 0x%08x\n", hdr->ih_hcrc); 298 } 299 300 static int 301 generate_header_uimg(struct uboot_image_header *hdr, int kernel_fd) 302 { 303 uint8_t *p; 304 struct stat st; 305 uint32_t crc, dsize, size_buf[2]; 306 int error; 307 308 error = fstat(kernel_fd, &st); 309 if (error == -1) { 310 perror("stat"); 311 return errno; 312 } 313 314 if (st.st_size + sizeof(*hdr) > UINT32_MAX) { 315 fprintf(stderr, "fatal: kernel too big\n"); 316 return EINVAL; 317 } 318 319 p = mmap(0, st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, kernel_fd, 0); 320 if (p == MAP_FAILED) { 321 perror("mmap kernel"); 322 return EINVAL; 323 } 324 if (image_type == IH_TYPE_SCRIPT) { 325 struct iovec iov[3]; 326 dsize = st.st_size + (sizeof(uint32_t) * 2); 327 size_buf[0] = htonl(st.st_size); 328 size_buf[1] = htonl(0); 329 iov[0].iov_base = &size_buf[0]; 330 iov[0].iov_len = sizeof(size_buf[0]); 331 iov[1].iov_base = &size_buf[1]; 332 iov[1].iov_len = sizeof(size_buf[1]); 333 iov[2].iov_base = p; 334 iov[2].iov_len = st.st_size; 335 crc = crc32v(iov, 3); 336 } else { 337 dsize = update_image ? 338 (uint32_t)st.st_size - sizeof(*hdr) : (uint32_t)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 const uint64_t dsize = update_image ? 392 (uint64_t)st.st_size : (uint64_t)st.st_size + sizeof(*hdr); 393 394 memset(hdr, 0, sizeof(*hdr)); 395 hdr->code0 = htole32(ARM64_CODE0); 396 hdr->text_offset = htole64(image_entrypoint); 397 hdr->image_size = htole64(dsize); 398 hdr->flags = htole32(flags); 399 hdr->magic = htole32(ARM64_MAGIC); 400 401 dump_header_arm64(hdr); 402 403 return 0; 404 } 405 406 static int 407 write_image(void *hdr, size_t hdrlen, int kernel_fd, int image_fd) 408 { 409 uint8_t buf[4096]; 410 ssize_t rlen, wlen; 411 struct stat st; 412 uint32_t size_buf[2]; 413 int error; 414 415 error = fstat(kernel_fd, &st); 416 if (error == -1) { 417 perror("stat"); 418 return errno; 419 } 420 421 wlen = write(image_fd, hdr, hdrlen); 422 if (wlen != (ssize_t)hdrlen) { 423 perror("short write"); 424 return errno; 425 } 426 427 if (image_type == IH_TYPE_SCRIPT) { 428 size_buf[0] = htonl(st.st_size); 429 size_buf[1] = htonl(0); 430 wlen = write(image_fd, &size_buf, sizeof(size_buf)); 431 if (wlen != sizeof(size_buf)) { 432 perror("short write"); 433 return errno; 434 } 435 } 436 437 if (update_image) { 438 if (lseek(kernel_fd, hdrlen, SEEK_SET) != (off_t)hdrlen) { 439 perror("seek failed"); 440 return errno; 441 } 442 } 443 444 while ((rlen = read(kernel_fd, buf, sizeof(buf))) > 0) { 445 wlen = write(image_fd, buf, rlen); 446 if (wlen != rlen) { 447 perror("short write"); 448 return errno; 449 } 450 } 451 452 return 0; 453 } 454 455 int 456 main(int argc, char *argv[]) 457 { 458 struct uboot_image_header hdr_uimg; 459 struct arm64_image_header hdr_arm64; 460 const char *src, *dest; 461 char *ep; 462 int kernel_fd, image_fd; 463 int ch; 464 unsigned long long num; 465 466 while ((ch = getopt(argc, argv, "A:C:E:O:T:a:e:f:hm:n:u")) != -1) { 467 switch (ch) { 468 case 'A': /* arch */ 469 image_arch = get_arch(optarg); 470 break; 471 case 'C': /* comp */ 472 image_comp = get_comp(optarg); 473 break; 474 case 'O': /* os */ 475 image_os = get_os(optarg); 476 break; 477 case 'T': /* type */ 478 image_type = get_type(optarg); 479 break; 480 case 'a': /* addr */ 481 errno = 0; 482 num = strtoull(optarg, &ep, 0); 483 if (*ep != '\0' || (errno == ERANGE && 484 (num == ULLONG_MAX || num == 0)) || 485 ((signed long long)num != (int32_t)num && 486 num != (uint32_t)num)) 487 errx(1, "illegal number -- %s", optarg); 488 image_loadaddr = (uint32_t)num; 489 break; 490 case 'E': /* ep (byte swapped) */ 491 case 'e': /* ep */ 492 errno = 0; 493 num = strtoull(optarg, &ep, 0); 494 if (*ep != '\0' || (errno == ERANGE && 495 (num == ULLONG_MAX || num == 0)) || 496 ((signed long long)num != (int32_t)num && 497 num != (uint32_t)num)) 498 errx(1, "illegal number -- %s", optarg); 499 image_entrypoint = (uint32_t)num; 500 if (ch == 'E') 501 image_entrypoint = bswap32(image_entrypoint); 502 break; 503 case 'f': /* image format */ 504 image_format = get_image_format(optarg); 505 break; 506 case 'm': /* magic */ 507 errno = 0; 508 num = strtoul(optarg, &ep, 0); 509 if (*ep != '\0' || (errno == ERANGE && 510 (num == ULONG_MAX || num == 0))) 511 errx(1, "illegal number -- %s", optarg); 512 image_magic = (uint32_t)num; 513 break; 514 case 'n': /* name */ 515 image_name = strdup(optarg); 516 break; 517 case 'u': /* update image */ 518 update_image = 1; 519 break; 520 case 'h': 521 default: 522 usage(); 523 /* NOTREACHED */ 524 } 525 } 526 argc -= optind; 527 argv += optind; 528 529 if (argc != 2) 530 usage(); 531 532 if (image_entrypoint == 0) 533 image_entrypoint = image_loadaddr; 534 535 switch (image_format) { 536 case FMT_UIMG: 537 if (image_arch == IH_ARCH_UNKNOWN || 538 image_type == IH_TYPE_UNKNOWN || 539 image_name == NULL) 540 usage(); 541 /* NOTREACHED */ 542 543 switch (image_type) { 544 case IH_TYPE_SCRIPT: 545 case IH_TYPE_RAMDISK: 546 case IH_TYPE_KERNEL_NOLOAD: 547 break; 548 default: 549 if (image_loadaddr == 0) 550 usage(); 551 /* NOTREACHED */ 552 break; 553 } 554 break; 555 556 case FMT_ARM64: 557 if (image_arch != IH_ARCH_UNKNOWN && 558 image_arch != IH_ARCH_ARM64) 559 usage(); 560 /* NOTREACHED */ 561 562 break; 563 564 default: 565 usage(); 566 /* NOTREACHED */ 567 } 568 569 src = argv[0]; 570 dest = argv[1]; 571 572 kernel_fd = open(src, O_RDONLY); 573 if (kernel_fd == -1) { 574 perror("open kernel"); 575 return EXIT_FAILURE; 576 } 577 image_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0666); 578 if (image_fd == -1) { 579 perror("open image"); 580 return EXIT_FAILURE; 581 } 582 583 printf(" image type: %s\n", get_image_format_name(image_format)); 584 585 switch (image_format) { 586 case FMT_UIMG: 587 if (generate_header_uimg(&hdr_uimg, kernel_fd) != 0) 588 return EXIT_FAILURE; 589 590 if (write_image(&hdr_uimg, sizeof(hdr_uimg), 591 kernel_fd, image_fd) != 0) 592 return EXIT_FAILURE; 593 594 break; 595 case FMT_ARM64: 596 if (generate_header_arm64(&hdr_arm64, kernel_fd) != 0) 597 return EXIT_FAILURE; 598 599 if (write_image(&hdr_arm64, sizeof(hdr_arm64), 600 kernel_fd, image_fd) != 0) 601 return EXIT_FAILURE; 602 603 break; 604 default: 605 break; 606 } 607 608 close(image_fd); 609 close(kernel_fd); 610 611 return EXIT_SUCCESS; 612 } 613