1 /* $NetBSD: mkubootimage.c,v 1.17 2012/12/29 15:11:56 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.17 2012/12/29 15:11:56 jmcneill Exp $"); 34 35 #include <sys/mman.h> 36 #include <sys/stat.h> 37 #include <sys/endian.h> 38 #include <sys/uio.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <limits.h> 43 #include <stdint.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <unistd.h> 49 50 #include "uboot.h" 51 52 #ifndef __arraycount 53 #define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) 54 #endif 55 56 extern uint32_t crc32(const void *, size_t); 57 extern uint32_t crc32v(const struct iovec *, int); 58 59 static enum uboot_image_os image_os = IH_OS_NETBSD; 60 static enum uboot_image_arch image_arch = IH_ARCH_UNKNOWN; 61 static enum uboot_image_type image_type = IH_TYPE_UNKNOWN; 62 static enum uboot_image_comp image_comp = IH_COMP_NONE; 63 static uint32_t image_loadaddr = 0; 64 static uint32_t image_entrypoint = 0; 65 static char *image_name; 66 static uint32_t image_magic = IH_MAGIC; 67 68 static const struct uboot_os { 69 enum uboot_image_os os; 70 const char *name; 71 } uboot_os[] = { 72 { IH_OS_OPENBSD, "openbsd" }, 73 { IH_OS_NETBSD, "netbsd" }, 74 { IH_OS_FREEBSD, "freebsd" }, 75 { IH_OS_LINUX, "linux" }, 76 }; 77 78 static enum uboot_image_os 79 get_os(const char *name) 80 { 81 unsigned int i; 82 83 for (i = 0; i < __arraycount(uboot_os); i++) { 84 if (strcmp(uboot_os[i].name, name) == 0) 85 return uboot_os[i].os; 86 } 87 88 return IH_OS_UNKNOWN; 89 } 90 91 static const char * 92 get_os_name(enum uboot_image_os os) 93 { 94 unsigned int i; 95 96 for (i = 0; i < __arraycount(uboot_os); i++) { 97 if (uboot_os[i].os == os) 98 return uboot_os[i].name; 99 } 100 101 return "Unknown"; 102 } 103 104 static const struct uboot_arch { 105 enum uboot_image_arch arch; 106 const char *name; 107 } uboot_arch[] = { 108 { IH_ARCH_ARM, "arm" }, 109 { IH_ARCH_MIPS, "mips" }, 110 { IH_ARCH_MIPS64, "mips64" }, 111 { IH_ARCH_PPC, "powerpc" }, 112 }; 113 114 static enum uboot_image_arch 115 get_arch(const char *name) 116 { 117 unsigned int i; 118 119 for (i = 0; i < __arraycount(uboot_arch); i++) { 120 if (strcmp(uboot_arch[i].name, name) == 0) 121 return uboot_arch[i].arch; 122 } 123 124 return IH_ARCH_UNKNOWN; 125 } 126 127 static const char * 128 get_arch_name(enum uboot_image_arch arch) 129 { 130 unsigned int i; 131 132 for (i = 0; i < __arraycount(uboot_arch); i++) { 133 if (uboot_arch[i].arch == arch) 134 return uboot_arch[i].name; 135 } 136 137 return "Unknown"; 138 } 139 140 static const struct uboot_type { 141 enum uboot_image_type type; 142 const char *name; 143 } uboot_type[] = { 144 { IH_TYPE_STANDALONE, "standalone" }, 145 { IH_TYPE_KERNEL, "kernel" }, 146 { IH_TYPE_RAMDISK, "ramdisk" }, 147 { IH_TYPE_FILESYSTEM, "fs" }, 148 { IH_TYPE_SCRIPT, "script" }, 149 }; 150 151 static enum uboot_image_type 152 get_type(const char *name) 153 { 154 unsigned int i; 155 156 for (i = 0; i < __arraycount(uboot_type); i++) { 157 if (strcmp(uboot_type[i].name, name) == 0) 158 return uboot_type[i].type; 159 } 160 161 return IH_TYPE_UNKNOWN; 162 } 163 164 static const char * 165 get_type_name(enum uboot_image_type type) 166 { 167 unsigned int i; 168 169 for (i = 0; i < __arraycount(uboot_type); i++) { 170 if (uboot_type[i].type == type) 171 return uboot_type[i].name; 172 } 173 174 return "Unknown"; 175 } 176 177 static const struct uboot_comp { 178 enum uboot_image_comp comp; 179 const char *name; 180 } uboot_comp[] = { 181 { IH_COMP_NONE, "none" }, 182 { IH_COMP_GZIP, "gz" }, 183 { IH_COMP_BZIP2, "bz2" }, 184 { IH_COMP_LZMA, "lzma" }, 185 { IH_COMP_LZO, "lzo" }, 186 }; 187 188 static enum uboot_image_comp 189 get_comp(const char *name) 190 { 191 unsigned int i; 192 193 for (i = 0; i < __arraycount(uboot_comp); i++) { 194 if (strcmp(uboot_comp[i].name, name) == 0) 195 return uboot_comp[i].comp; 196 } 197 198 return IH_TYPE_UNKNOWN; 199 } 200 201 static const char * 202 get_comp_name(enum uboot_image_comp comp) 203 { 204 unsigned int i; 205 206 for (i = 0; i < __arraycount(uboot_comp); i++) { 207 if (uboot_comp[i].comp == comp) 208 return uboot_comp[i].name; 209 } 210 211 return "Unknown"; 212 } 213 214 __dead static void 215 usage(void) 216 { 217 fprintf(stderr, "usage: mkubootimage -A <arm|mips|mips64|powerpc>"); 218 fprintf(stderr, " -C <none|bz2|gz|lzma|lzo>"); 219 fprintf(stderr, " -O <openbsd|netbsd|freebsd|linux>"); 220 fprintf(stderr, " -T <standalone|kernel|ramdisk|fs|script>"); 221 fprintf(stderr, " -a <addr> [-e <ep>] [-m <magic>] -n <name>"); 222 fprintf(stderr, " <srcfile> <dstfile>\n"); 223 224 exit(EXIT_FAILURE); 225 } 226 227 static void 228 dump_header(struct uboot_image_header *hdr) 229 { 230 time_t tm = ntohl(hdr->ih_time); 231 232 printf(" magic: 0x%08x\n", ntohl(hdr->ih_magic)); 233 printf(" time: %s", ctime(&tm)); 234 printf(" size: %u\n", ntohl(hdr->ih_size)); 235 printf(" load addr: 0x%08x\n", ntohl(hdr->ih_load)); 236 printf(" entry point: 0x%08x\n", ntohl(hdr->ih_ep)); 237 printf(" data crc: 0x%08x\n", ntohl(hdr->ih_dcrc)); 238 printf(" os: %d (%s)\n", hdr->ih_os, 239 get_os_name(hdr->ih_os)); 240 printf(" arch: %d (%s)\n", hdr->ih_arch, 241 get_arch_name(hdr->ih_arch)); 242 printf(" type: %d (%s)\n", hdr->ih_type, 243 get_type_name(hdr->ih_type)); 244 printf(" comp: %d (%s)\n", hdr->ih_comp, 245 get_comp_name(hdr->ih_comp)); 246 printf(" name: %s\n", hdr->ih_name); 247 printf(" header crc: 0x%08x\n", hdr->ih_hcrc); 248 } 249 250 static int 251 generate_header(struct uboot_image_header *hdr, int kernel_fd) 252 { 253 uint8_t *p; 254 struct stat st; 255 uint32_t crc, dsize, size_buf[2]; 256 int error; 257 258 error = fstat(kernel_fd, &st); 259 if (error == -1) { 260 perror("stat"); 261 return errno; 262 } 263 264 if (st.st_size + sizeof(*hdr) > UINT32_MAX) { 265 fprintf(stderr, "fatal: kernel too big\n"); 266 return EINVAL; 267 } 268 269 p = mmap(0, st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, kernel_fd, 0); 270 if (p == MAP_FAILED) { 271 perror("mmap kernel"); 272 return EINVAL; 273 } 274 if (image_type == IH_TYPE_SCRIPT) { 275 struct iovec iov[3]; 276 dsize = st.st_size + (sizeof(uint32_t) * 2); 277 size_buf[0] = htonl(st.st_size); 278 size_buf[1] = htonl(0); 279 iov[0].iov_base = &size_buf[0]; 280 iov[0].iov_len = sizeof(size_buf[0]); 281 iov[1].iov_base = &size_buf[1]; 282 iov[1].iov_len = sizeof(size_buf[1]); 283 iov[2].iov_base = p; 284 iov[2].iov_len = st.st_size; 285 crc = crc32v(iov, 3); 286 } else { 287 dsize = st.st_size; 288 crc = crc32(p, st.st_size); 289 } 290 munmap(p, st.st_size); 291 292 memset(hdr, 0, sizeof(*hdr)); 293 hdr->ih_magic = htonl(image_magic); 294 hdr->ih_time = htonl(st.st_mtime); 295 hdr->ih_size = htonl(dsize); 296 hdr->ih_load = htonl(image_loadaddr); 297 hdr->ih_ep = htonl(image_entrypoint); 298 hdr->ih_dcrc = htonl(crc); 299 hdr->ih_os = image_os; 300 hdr->ih_arch = image_arch; 301 hdr->ih_type = image_type; 302 hdr->ih_comp = image_comp; 303 strlcpy((char *)hdr->ih_name, image_name, sizeof(hdr->ih_name)); 304 crc = crc32((void *)hdr, sizeof(*hdr)); 305 hdr->ih_hcrc = htonl(crc); 306 307 dump_header(hdr); 308 309 return 0; 310 } 311 312 static int 313 write_image(struct uboot_image_header *hdr, int kernel_fd, int image_fd) 314 { 315 uint8_t buf[4096]; 316 ssize_t rlen, wlen; 317 struct stat st; 318 uint32_t size_buf[2]; 319 int error; 320 321 error = fstat(kernel_fd, &st); 322 if (error == -1) { 323 perror("stat"); 324 return errno; 325 } 326 327 wlen = write(image_fd, hdr, sizeof(*hdr)); 328 if (wlen != sizeof(*hdr)) { 329 perror("short write"); 330 return errno; 331 } 332 333 if (image_type == IH_TYPE_SCRIPT) { 334 size_buf[0] = htonl(st.st_size); 335 size_buf[1] = htonl(0); 336 wlen = write(image_fd, &size_buf, sizeof(size_buf)); 337 if (wlen != sizeof(size_buf)) { 338 perror("short write"); 339 return errno; 340 } 341 } 342 343 while ((rlen = read(kernel_fd, buf, sizeof(buf))) > 0) { 344 wlen = write(image_fd, buf, rlen); 345 if (wlen != rlen) { 346 perror("short write"); 347 return errno; 348 } 349 } 350 351 return 0; 352 } 353 354 int 355 main(int argc, char *argv[]) 356 { 357 struct uboot_image_header hdr; 358 const char *src, *dest; 359 char *ep; 360 int kernel_fd, image_fd; 361 int ch; 362 unsigned long long num; 363 364 while ((ch = getopt(argc, argv, "A:C:E:O:T:a:e:hm:n:")) != -1) { 365 switch (ch) { 366 case 'A': /* arch */ 367 image_arch = get_arch(optarg); 368 break; 369 case 'C': /* comp */ 370 image_comp = get_comp(optarg); 371 break; 372 case 'O': /* os */ 373 image_os = get_os(optarg); 374 break; 375 case 'T': /* type */ 376 image_type = get_type(optarg); 377 break; 378 case 'a': /* addr */ 379 errno = 0; 380 num = strtoull(optarg, &ep, 0); 381 if (*ep != '\0' || (errno == ERANGE && 382 (num == ULLONG_MAX || num == 0)) || 383 ((signed long long)num != (int32_t)num && 384 num != (uint32_t)num)) 385 errx(1, "illegal number -- %s", optarg); 386 image_loadaddr = (uint32_t)num; 387 break; 388 case 'E': /* ep (byte swapped) */ 389 case 'e': /* ep */ 390 errno = 0; 391 num = strtoull(optarg, &ep, 0); 392 if (*ep != '\0' || (errno == ERANGE && 393 (num == ULLONG_MAX || num == 0)) || 394 ((signed long long)num != (int32_t)num && 395 num != (uint32_t)num)) 396 errx(1, "illegal number -- %s", optarg); 397 image_entrypoint = (uint32_t)num; 398 if (ch == 'E') 399 image_entrypoint = bswap32(image_entrypoint); 400 break; 401 case 'm': /* magic */ 402 errno = 0; 403 num = strtoul(optarg, &ep, 0); 404 if (*ep != '\0' || (errno == ERANGE && 405 (num == ULONG_MAX || num == 0))) 406 errx(1, "illegal number -- %s", optarg); 407 image_magic = (uint32_t)num; 408 case 'n': /* name */ 409 image_name = strdup(optarg); 410 break; 411 case 'h': 412 default: 413 usage(); 414 /* NOTREACHED */ 415 } 416 } 417 argc -= optind; 418 argv += optind; 419 420 if (argc != 2) 421 usage(); 422 423 if (image_entrypoint == 0) 424 image_entrypoint = image_loadaddr; 425 426 if (image_arch == IH_ARCH_UNKNOWN || 427 image_type == IH_TYPE_UNKNOWN || 428 (image_type != IH_TYPE_SCRIPT && image_loadaddr == 0) || 429 image_name == NULL) 430 usage(); 431 432 src = argv[0]; 433 dest = argv[1]; 434 435 kernel_fd = open(src, O_RDONLY); 436 if (kernel_fd == -1) { 437 perror("open kernel"); 438 return EXIT_FAILURE; 439 } 440 image_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0666); 441 if (image_fd == -1) { 442 perror("open image"); 443 return EXIT_FAILURE; 444 } 445 446 if (generate_header(&hdr, kernel_fd) != 0) 447 return EXIT_FAILURE; 448 449 if (write_image(&hdr, kernel_fd, image_fd) != 0) 450 return EXIT_FAILURE; 451 452 close(image_fd); 453 close(kernel_fd); 454 455 return EXIT_SUCCESS; 456 } 457