1/* 2 * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 as published 6 * by the Free Software Foundation. 7 * 8 */ 9 10#include <stdio.h> 11#include <stdlib.h> 12#include <stdint.h> 13#include <string.h> 14#include <unistd.h> /* for unlink() */ 15#include <libgen.h> 16#include <getopt.h> /* for getopt() */ 17#include <stdarg.h> 18#include <errno.h> 19#include <sys/stat.h> 20 21#include <arpa/inet.h> 22#include <netinet/in.h> 23 24#define MAX_MODEL_LEN 20 25#define MAX_SIGNATURE_LEN 30 26#define MAX_REGION_LEN 4 27#define MAX_VERSION_LEN 12 28 29#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) 30 31struct file_info { 32 char *file_name; /* name of the file */ 33 uint32_t file_size; /* length of the file */ 34 uint32_t write_size; 35}; 36 37struct img_header { 38 uint32_t checksum; 39 uint32_t image_size; 40 uint32_t kernel_size; 41 char model[MAX_MODEL_LEN]; 42 char signature[MAX_SIGNATURE_LEN]; 43 char region[MAX_REGION_LEN]; 44 char version[MAX_VERSION_LEN]; 45 unsigned char header_len; 46 unsigned char is_tgz; 47 unsigned char pad[4]; 48} __attribute__ ((packed)); 49 50/* 51 * Globals 52 */ 53static char *ofname; 54static char *progname; 55 56static char *model; 57static char *signature; 58static char *region = "DEF"; 59static char *version; 60static struct file_info kernel_info; 61static struct file_info rootfs_info; 62static uint32_t kernel_size; 63static uint32_t image_size; 64static int combined; 65 66/* 67 * Message macros 68 */ 69#define ERR(fmt, ...) do { \ 70 fflush(0); \ 71 fprintf(stderr, "[%s] *** error: " fmt "\n", \ 72 progname, ## __VA_ARGS__ ); \ 73} while (0) 74 75#define ERRS(fmt, ...) do { \ 76 int save = errno; \ 77 fflush(0); \ 78 fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \ 79 progname, ## __VA_ARGS__, strerror(save)); \ 80} while (0) 81 82#define DBG(fmt, ...) do { \ 83 fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ 84} while (0) 85 86static void usage(int status) 87{ 88 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; 89 90 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); 91 fprintf(stream, 92"\n" 93"Options:\n" 94" -k <file> read kernel image from the file <file>\n" 95" -c use the kernel image as a combined image\n" 96" -M <model> set model to <model>\n" 97" -o <file> write output to the file <file>\n" 98" -r <file> read rootfs image from the file <file>\n" 99" -S <signature> set image signature to <signature>\n" 100" -R <region> set image region to <region>\n" 101" -V <version> set image version to <version>\n" 102" -I <size> set image size to <size>\n" 103" -K <size> set kernel size to <size>\n" 104" -h show this screen\n" 105 ); 106 107 exit(status); 108} 109 110int 111str2u32(char *arg, uint32_t *val) 112{ 113 char *err = NULL; 114 uint32_t t; 115 116 errno=0; 117 t = strtoul(arg, &err, 0); 118 if (errno || (err==arg) || ((err != NULL) && *err)) { 119 return -1; 120 } 121 122 *val = t; 123 return 0; 124} 125 126static int get_file_stat(struct file_info *fdata) 127{ 128 struct stat st; 129 int res; 130 131 if (fdata->file_name == NULL) 132 return 0; 133 134 res = stat(fdata->file_name, &st); 135 if (res){ 136 ERRS("stat failed on %s", fdata->file_name); 137 return res; 138 } 139 140 fdata->file_size = st.st_size; 141 fdata->write_size = fdata->file_size; 142 return 0; 143} 144 145static int read_to_buf(struct file_info *fdata, char *buf) 146{ 147 FILE *f; 148 int ret = EXIT_FAILURE; 149 150 f = fopen(fdata->file_name, "r"); 151 if (f == NULL) { 152 ERRS("could not open \"%s\" for reading", fdata->file_name); 153 goto out; 154 } 155 156 errno = 0; 157 fread(buf, fdata->file_size, 1, f); 158 if (errno != 0) { 159 ERRS("unable to read from file \"%s\"", fdata->file_name); 160 goto out_close; 161 } 162 163 ret = EXIT_SUCCESS; 164 165out_close: 166 fclose(f); 167out: 168 return ret; 169} 170 171static int check_options(void) 172{ 173 int ret; 174 175#define CHKSTR(_name, _msg) \ 176 do { \ 177 if (_name == NULL) { \ 178 ERR("no %s specified", _msg); \ 179 return -1; \ 180 } \ 181 } while (0) 182 183#define CHKSTRLEN(_name, _msg) \ 184 do { \ 185 int field_len; \ 186 CHKSTR(_name, _msg); \ 187 field_len = FIELD_SIZEOF(struct img_header, _name) - 1; \ 188 if (strlen(_name) > field_len) { \ 189 ERR("%s is too long, max length is %d", \ 190 _msg, field_len); \ 191 return -1; \ 192 } \ 193 } while (0) 194 195 CHKSTRLEN(model, "model"); 196 CHKSTRLEN(signature, "signature"); 197 CHKSTRLEN(region, "region"); 198 CHKSTRLEN(version, "version"); 199 CHKSTR(ofname, "output file"); 200 CHKSTR(kernel_info.file_name, "kernel image"); 201 202 ret = get_file_stat(&kernel_info); 203 if (ret) 204 return ret; 205 206 if (combined) { 207 if (!kernel_size) { 208 ERR("kernel size must be specified for combined images"); 209 return -1; \ 210 } 211 212 if (!image_size) 213 image_size = kernel_info.file_size; 214 215 if (kernel_info.file_size > image_size) { 216 ERR("kernel image is too big"); 217 return -1; 218 } 219 220 kernel_info.write_size = image_size; 221 } else { 222 CHKSTR(rootfs_info.file_name, "rootfs image"); 223 224 ret = get_file_stat(&rootfs_info); 225 if (ret) 226 return ret; 227 228 if (kernel_size) { 229 /* override kernel size */ 230 kernel_info.write_size = kernel_size; 231 } 232 233 if (image_size) { 234 if (image_size < kernel_info.write_size) 235 kernel_info.write_size = image_size; 236 237 /* override rootfs size */ 238 rootfs_info.write_size = image_size - kernel_info.write_size; 239 } 240 241 if (kernel_info.file_size > kernel_info.write_size) { 242 ERR("kernel image is too big"); 243 return -1; 244 } 245 246 if (rootfs_info.file_size > rootfs_info.write_size) { 247 ERR("rootfs image is too big"); 248 return -1; 249 } 250 } 251 252 return 0; 253} 254 255static int write_fw(char *data, int len) 256{ 257 FILE *f; 258 int ret = EXIT_FAILURE; 259 260 f = fopen(ofname, "w"); 261 if (f == NULL) { 262 ERRS("could not open \"%s\" for writing", ofname); 263 goto out; 264 } 265 266 errno = 0; 267 fwrite(data, len, 1, f); 268 if (errno) { 269 ERRS("unable to write output file"); 270 goto out_flush; 271 } 272 273 DBG("firmware file \"%s\" completed", ofname); 274 275 ret = EXIT_SUCCESS; 276 277out_flush: 278 fflush(f); 279 fclose(f); 280 if (ret != EXIT_SUCCESS) { 281 unlink(ofname); 282 } 283out: 284 return ret; 285} 286 287static uint32_t get_csum(unsigned char *p, uint32_t len) 288{ 289 uint32_t csum = 0; 290 291 while (len--) 292 csum += *p++; 293 294 return csum; 295} 296 297static int build_fw(void) 298{ 299 int buflen; 300 char *buf; 301 char *p; 302 uint32_t csum; 303 struct img_header *hdr; 304 int ret = EXIT_FAILURE; 305 306 buflen = sizeof(struct img_header) + 307 kernel_info.write_size + rootfs_info.write_size; 308 309 buf = malloc(buflen); 310 if (!buf) { 311 ERR("no memory for buffer\n"); 312 goto out; 313 } 314 315 memset(buf, 0, buflen); 316 317 p = buf + sizeof(struct img_header); 318 319 /* read kernel data */ 320 ret = read_to_buf(&kernel_info, p); 321 if (ret) 322 goto out_free_buf; 323 324 if (!combined) { 325 p += kernel_info.write_size; 326 327 /* read rootfs data */ 328 ret = read_to_buf(&rootfs_info, p); 329 if (ret) 330 goto out_free_buf; 331 } 332 333 csum = get_csum((unsigned char *)(buf + sizeof(struct img_header)), 334 buflen - sizeof(struct img_header)); 335 336 /* fill firmware header */ 337 hdr = (struct img_header *) buf; 338 339 hdr->checksum = htonl(csum); 340 hdr->image_size = htonl(buflen - sizeof(struct img_header)); 341 if (!combined) 342 hdr->kernel_size = htonl(kernel_info.write_size); 343 else 344 hdr->kernel_size = htonl(kernel_size); 345 hdr->header_len = sizeof(struct img_header); 346 strncpy(hdr->model, model, sizeof(hdr->model)); 347 strncpy(hdr->signature, signature, sizeof(hdr->signature)); 348 strncpy(hdr->version, version, sizeof(hdr->version)); 349 strncpy(hdr->region, region, sizeof(hdr->region)); 350 351 ret = write_fw(buf, buflen); 352 if (ret) 353 goto out_free_buf; 354 355 ret = EXIT_SUCCESS; 356 357out_free_buf: 358 free(buf); 359out: 360 return ret; 361} 362 363int main(int argc, char *argv[]) 364{ 365 int ret = EXIT_FAILURE; 366 367 progname = basename(argv[0]); 368 369 while (1) { 370 int c; 371 372 c = getopt(argc, argv, "M:S:V:R:k:K:I:r:o:hc"); 373 if (c == -1) 374 break; 375 376 switch (c) { 377 case 'M': 378 model = optarg; 379 break; 380 case 'S': 381 signature = optarg; 382 break; 383 case 'V': 384 version = optarg; 385 break; 386 case 'R': 387 region = optarg; 388 break; 389 case 'k': 390 kernel_info.file_name = optarg; 391 break; 392 case 'K': 393 if (str2u32(optarg, &kernel_size)) { 394 ERR("%s is invalid '%s'", 395 "kernel size", optarg); 396 goto out; 397 } 398 break; 399 case 'I': 400 if (str2u32(optarg, &image_size)) { 401 ERR("%s is invalid '%s'", 402 "image size", optarg); 403 goto out; 404 } 405 break; 406 case 'r': 407 rootfs_info.file_name = optarg; 408 break; 409 case 'c': 410 combined = 1; 411 break; 412 case 'o': 413 ofname = optarg; 414 break; 415 case 'h': 416 usage(EXIT_SUCCESS); 417 break; 418 default: 419 usage(EXIT_FAILURE); 420 break; 421 } 422 } 423 424 ret = check_options(); 425 if (ret) 426 goto out; 427 428 ret = build_fw(); 429 430out: 431 return ret; 432} 433 434