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