1/* $NetBSD: mkubootimage.c,v 1.33 2024/05/21 04:01:26 gutteridge 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.33 2024/05/21 04:01:26 gutteridge 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#include "crc32.h" 55 56#ifndef __arraycount 57#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) 58#endif 59 60enum image_format { 61 FMT_UNKNOWN, 62 FMT_UIMG, /* Legacy U-Boot image */ 63 FMT_ARM64, /* Linux ARM64 image (booti) */ 64}; 65 66static enum uboot_image_os image_os = IH_OS_NETBSD; 67static enum uboot_image_arch image_arch = IH_ARCH_UNKNOWN; 68static enum uboot_image_type image_type = IH_TYPE_UNKNOWN; 69static enum uboot_image_comp image_comp = IH_COMP_NONE; 70static uint32_t image_loadaddr = 0; 71static uint32_t image_entrypoint = 0; 72static char *image_name; 73static uint32_t image_magic = IH_MAGIC; 74static enum image_format image_format = FMT_UIMG; 75static int update_image = 0; 76 77static const struct uboot_image_format { 78 enum image_format format; 79 const char *name; 80} uboot_image_format[] = { 81 { FMT_UIMG, "uimg" }, 82 { FMT_ARM64, "arm64" }, 83}; 84 85static enum image_format 86get_image_format(const char *name) 87{ 88 unsigned int i; 89 90 for (i = 0; i < __arraycount(uboot_image_format); i++) { 91 if (strcmp(uboot_image_format[i].name, name) == 0) 92 return uboot_image_format[i].format; 93 } 94 95 return FMT_UNKNOWN; 96} 97 98static const char * 99get_image_format_name(enum image_format format) 100{ 101 unsigned int i; 102 103 for (i = 0; i < __arraycount(uboot_image_format); i++) { 104 if (uboot_image_format[i].format == format) 105 return uboot_image_format[i].name; 106 } 107 108 return "Unknown"; 109} 110 111static const struct uboot_os { 112 enum uboot_image_os os; 113 const char *name; 114} uboot_os[] = { 115 { IH_OS_OPENBSD, "openbsd" }, 116 { IH_OS_NETBSD, "netbsd" }, 117 { IH_OS_FREEBSD, "freebsd" }, 118 { IH_OS_LINUX, "linux" }, 119}; 120 121static enum uboot_image_os 122get_os(const char *name) 123{ 124 unsigned int i; 125 126 for (i = 0; i < __arraycount(uboot_os); i++) { 127 if (strcmp(uboot_os[i].name, name) == 0) 128 return uboot_os[i].os; 129 } 130 131 return IH_OS_UNKNOWN; 132} 133 134static const char * 135get_os_name(enum uboot_image_os os) 136{ 137 unsigned int i; 138 139 for (i = 0; i < __arraycount(uboot_os); i++) { 140 if (uboot_os[i].os == os) 141 return uboot_os[i].name; 142 } 143 144 return "Unknown"; 145} 146 147static const struct uboot_arch { 148 enum uboot_image_arch arch; 149 const char *name; 150} uboot_arch[] = { 151 { IH_ARCH_ARM, "arm" }, 152 { IH_ARCH_ARM64, "arm64" }, 153 { IH_ARCH_I386, "i386" }, 154 { IH_ARCH_MIPS, "mips" }, 155 { IH_ARCH_MIPS64, "mips64" }, 156 { IH_ARCH_PPC, "powerpc" }, 157 { IH_ARCH_OPENRISC, "or1k" }, 158 { IH_ARCH_RISCV, "riscv" }, 159 { IH_ARCH_SH, "sh" }, 160}; 161 162static enum uboot_image_arch 163get_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 175static const char * 176get_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 188static 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 200static enum uboot_image_type 201get_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 213static const char * 214get_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 226static 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 237static enum uboot_image_comp 238get_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 250static const char * 251get_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 264usage(void) 265{ 266 fprintf(stderr, 267"Usage: %s [-hu] -A <arm|arm64|i386|mips|mips64|or1k|powerpc|riscv|sh>\n" 268"\t-a address [-C <bz2|gz|lzma|lzo|none>] [-E address] [-e address] \n" 269"\t[-f <arm64|uimg>] [-m magic] -n image [-O <freebsd|linux|netbsd|openbsd>]\n" 270"\t-T <fs|kernel|kernel_noload|ramdisk|script|standalone> [-t timestamp]\n" 271"\tsource destination\n", getprogname()); 272 273 exit(EXIT_FAILURE); 274} 275 276static void 277dump_header_uimg(struct uboot_image_header *hdr) 278{ 279 time_t tm = ntohl(hdr->ih_time); 280 281 printf(" magic: 0x%08x\n", ntohl(hdr->ih_magic)); 282 printf(" time: %s", ctime(&tm)); 283 printf(" size: %u\n", ntohl(hdr->ih_size)); 284 printf(" load addr: 0x%08x\n", ntohl(hdr->ih_load)); 285 printf(" entry point: 0x%08x\n", ntohl(hdr->ih_ep)); 286 printf(" data crc: 0x%08x\n", ntohl(hdr->ih_dcrc)); 287 printf(" os: %d (%s)\n", hdr->ih_os, 288 get_os_name(hdr->ih_os)); 289 printf(" arch: %d (%s)\n", hdr->ih_arch, 290 get_arch_name(hdr->ih_arch)); 291 printf(" type: %d (%s)\n", hdr->ih_type, 292 get_type_name(hdr->ih_type)); 293 printf(" comp: %d (%s)\n", hdr->ih_comp, 294 get_comp_name(hdr->ih_comp)); 295 printf(" name: %s\n", hdr->ih_name); 296 printf(" header crc: 0x%08x\n", hdr->ih_hcrc); 297} 298 299static int 300generate_header_uimg(struct uboot_image_header *hdr, time_t repro_time, 301 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 = (uint32_t)(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 ? (uint32_t)(st.st_size - sizeof(*hdr)) : 338 (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(repro_time ? repro_time : 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 363static void 364dump_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 372static int 373generate_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 406static int 407write_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 455int 456main(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 time_t repro_time = 0; 466 467 while ((ch = getopt(argc, argv, "A:C:E:O:T:a:e:f:hm:n:t:u")) != -1) { 468 switch (ch) { 469 case 'A': /* arch */ 470 image_arch = get_arch(optarg); 471 break; 472 case 'C': /* comp */ 473 image_comp = get_comp(optarg); 474 break; 475 case 'O': /* os */ 476 image_os = get_os(optarg); 477 break; 478 case 'T': /* type */ 479 image_type = get_type(optarg); 480 break; 481 case 'a': /* addr */ 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_loadaddr = (uint32_t)num; 490 break; 491 case 'E': /* ep (byte swapped) */ 492 case 'e': /* ep */ 493 errno = 0; 494 num = strtoull(optarg, &ep, 0); 495 if (*ep != '\0' || (errno == ERANGE && 496 (num == ULLONG_MAX || num == 0)) || 497 ((signed long long)num != (int32_t)num && 498 num != (uint32_t)num)) 499 errx(1, "illegal number -- %s", optarg); 500 image_entrypoint = (uint32_t)num; 501 if (ch == 'E') 502 image_entrypoint = bswap32(image_entrypoint); 503 break; 504 case 'f': /* image format */ 505 image_format = get_image_format(optarg); 506 break; 507 case 'm': /* magic */ 508 errno = 0; 509 num = strtoul(optarg, &ep, 0); 510 if (*ep != '\0' || (errno == ERANGE && 511 (num == ULONG_MAX || num == 0))) 512 errx(1, "illegal number -- %s", optarg); 513 image_magic = (uint32_t)num; 514 break; 515 case 'n': /* name */ 516 image_name = strdup(optarg); 517 break; 518 case 't': /* FS timestamp */ 519 repro_time = atoll(optarg); 520 break; 521 case 'u': /* update image */ 522 update_image = 1; 523 break; 524 case 'h': 525 default: 526 usage(); 527 /* NOTREACHED */ 528 } 529 } 530 argc -= optind; 531 argv += optind; 532 533 if (argc != 2) 534 usage(); 535 536 if (image_entrypoint == 0) 537 image_entrypoint = image_loadaddr; 538 539 switch (image_format) { 540 case FMT_UIMG: 541 if (image_arch == IH_ARCH_UNKNOWN || 542 image_type == IH_TYPE_UNKNOWN || 543 image_name == NULL) 544 usage(); 545 /* NOTREACHED */ 546 547 switch (image_type) { 548 case IH_TYPE_SCRIPT: 549 case IH_TYPE_RAMDISK: 550 case IH_TYPE_KERNEL_NOLOAD: 551 break; 552 default: 553 if (image_loadaddr == 0) 554 usage(); 555 /* NOTREACHED */ 556 break; 557 } 558 break; 559 560 case FMT_ARM64: 561 if (image_arch != IH_ARCH_UNKNOWN && 562 image_arch != IH_ARCH_ARM64) 563 usage(); 564 /* NOTREACHED */ 565 566 break; 567 568 default: 569 usage(); 570 /* NOTREACHED */ 571 } 572 573 src = argv[0]; 574 dest = argv[1]; 575 576 kernel_fd = open(src, O_RDONLY); 577 if (kernel_fd == -1) { 578 perror("open kernel"); 579 return EXIT_FAILURE; 580 } 581 image_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0666); 582 if (image_fd == -1) { 583 perror("open image"); 584 return EXIT_FAILURE; 585 } 586 587 printf(" image type: %s\n", get_image_format_name(image_format)); 588 589 switch (image_format) { 590 case FMT_UIMG: 591 if (generate_header_uimg(&hdr_uimg, repro_time, kernel_fd) != 0) 592 return EXIT_FAILURE; 593 594 if (write_image(&hdr_uimg, sizeof(hdr_uimg), 595 kernel_fd, image_fd) != 0) 596 return EXIT_FAILURE; 597 598 break; 599 case FMT_ARM64: 600 if (generate_header_arm64(&hdr_arm64, kernel_fd) != 0) 601 return EXIT_FAILURE; 602 603 if (write_image(&hdr_arm64, sizeof(hdr_arm64), 604 kernel_fd, image_fd) != 0) 605 return EXIT_FAILURE; 606 607 break; 608 default: 609 break; 610 } 611 612 close(image_fd); 613 close(kernel_fd); 614 615 return EXIT_SUCCESS; 616} 617