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; 64 65/* 66 * Message macros 67 */ 68#define ERR(fmt, ...) do { \ 69 fflush(0); \ 70 fprintf(stderr, "[%s] *** error: " fmt "\n", \ 71 progname, ## __VA_ARGS__ ); \ 72} while (0) 73 74#define ERRS(fmt, ...) do { \ 75 int save = errno; \ 76 fflush(0); \ 77 fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \ 78 progname, ## __VA_ARGS__, strerror(save)); \ 79} while (0) 80 81#define DBG(fmt, ...) do { \ 82 fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ 83} while (0) 84 85static void usage(int status) 86{ 87 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; 88 89 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); 90 fprintf(stream, 91"\n" 92"Options:\n" 93" -k <file> read kernel image from the file <file>\n" 94" -M <model> set model to <model>\n" 95" -o <file> write output to the file <file>\n" 96" -r <file> read rootfs image from the file <file>\n" 97" -S <signature> set image signature to <signature>\n" 98" -R <region> set image region to <region>\n" 99" -V <version> set image version to <version>\n" 100" -I <size> set image size to <size>\n" 101" -K <size> set kernel size to <size>\n" 102" -h show this screen\n" 103 ); 104 105 exit(status); 106} 107 108int 109str2u32(char *arg, uint32_t *val) 110{ 111 char *err = NULL; 112 uint32_t t; 113 114 errno=0; 115 t = strtoul(arg, &err, 0); 116 if (errno || (err==arg) || ((err != NULL) && *err)) { 117 return -1; 118 } 119 120 *val = t; 121 return 0; 122} 123 124static int get_file_stat(struct file_info *fdata) 125{ 126 struct stat st; 127 int res; 128 129 if (fdata->file_name == NULL) 130 return 0; 131 132 res = stat(fdata->file_name, &st); 133 if (res){ 134 ERRS("stat failed on %s", fdata->file_name); 135 return res; 136 } 137 138 fdata->file_size = st.st_size; 139 fdata->write_size = fdata->file_size; 140 return 0; 141} 142 143static int read_to_buf(struct file_info *fdata, char *buf) 144{ 145 FILE *f; 146 int ret = EXIT_FAILURE; 147 148 f = fopen(fdata->file_name, "r"); 149 if (f == NULL) { 150 ERRS("could not open \"%s\" for reading", fdata->file_name); 151 goto out; 152 } 153 154 errno = 0; 155 fread(buf, fdata->file_size, 1, f); 156 if (errno != 0) { 157 ERRS("unable to read from file \"%s\"", fdata->file_name); 158 goto out_close; 159 } 160 161 ret = EXIT_SUCCESS; 162 163out_close: 164 fclose(f); 165out: 166 return ret; 167} 168 169static int check_options(void) 170{ 171 int ret; 172 173#define CHKSTR(_name, _msg) \ 174 do { \ 175 if (_name == NULL) { \ 176 ERR("no %s specified", _msg); \ 177 return -1; \ 178 } \ 179 } while (0) 180 181#define CHKSTRLEN(_name, _msg) \ 182 do { \ 183 int field_len; \ 184 CHKSTR(_name, _msg); \ 185 field_len = FIELD_SIZEOF(struct img_header, _name) - 1; \ 186 if (strlen(_name) > field_len) { \ 187 ERR("%s is too long, max length is %d", \ 188 _msg, field_len); \ 189 return -1; \ 190 } \ 191 } while (0) 192 193 CHKSTRLEN(model, "model"); 194 CHKSTRLEN(signature, "signature"); 195 CHKSTRLEN(region, "region"); 196 CHKSTRLEN(version, "version"); 197 CHKSTR(ofname, "output file"); 198 CHKSTR(kernel_info.file_name, "kernel image"); 199 CHKSTR(rootfs_info.file_name, "rootfs image"); 200 201 ret = get_file_stat(&kernel_info); 202 if (ret) 203 return ret; 204 205 ret = get_file_stat(&rootfs_info); 206 if (ret) 207 return ret; 208 209 if (kernel_size) { 210 /* override kernel size */ 211 kernel_info.write_size = kernel_size; 212 } 213 214 if (image_size) { 215 if (image_size < kernel_info.write_size) 216 kernel_info.write_size = image_size; 217 218 /* override rootfs size */ 219 rootfs_info.write_size = image_size - kernel_info.write_size; 220 } 221 222 if (kernel_info.file_size > kernel_info.write_size) { 223 ERR("kernel image is too big"); 224 return -1; 225 } 226 227 if (rootfs_info.file_size > rootfs_info.write_size) { 228 ERR("rootfs image is too big"); 229 return -1; 230 } 231 232 return 0; 233} 234 235static int write_fw(char *data, int len) 236{ 237 FILE *f; 238 int ret = EXIT_FAILURE; 239 240 f = fopen(ofname, "w"); 241 if (f == NULL) { 242 ERRS("could not open \"%s\" for writing", ofname); 243 goto out; 244 } 245 246 errno = 0; 247 fwrite(data, len, 1, f); 248 if (errno) { 249 ERRS("unable to write output file"); 250 goto out_flush; 251 } 252 253 DBG("firmware file \"%s\" completed", ofname); 254 255 ret = EXIT_SUCCESS; 256 257out_flush: 258 fflush(f); 259 fclose(f); 260 if (ret != EXIT_SUCCESS) { 261 unlink(ofname); 262 } 263out: 264 return ret; 265} 266 267static uint32_t get_csum(unsigned char *p, uint32_t len) 268{ 269 uint32_t csum = 0; 270 271 while (len--) 272 csum += *p++; 273 274 return csum; 275} 276 277static int build_fw(void) 278{ 279 int buflen; 280 char *buf; 281 char *p; 282 uint32_t csum; 283 struct img_header *hdr; 284 int ret = EXIT_FAILURE; 285 286 buflen = sizeof(struct img_header) + 287 kernel_info.write_size + rootfs_info.write_size; 288 289 buf = malloc(buflen); 290 if (!buf) { 291 ERR("no memory for buffer\n"); 292 goto out; 293 } 294 295 memset(buf, 0, buflen); 296 297 p = buf + sizeof(struct img_header); 298 299 /* read kernel data */ 300 ret = read_to_buf(&kernel_info, p); 301 if (ret) 302 goto out_free_buf; 303 304 p += kernel_info.write_size; 305 306 /* read rootfs data */ 307 ret = read_to_buf(&rootfs_info, p); 308 if (ret) 309 goto out_free_buf; 310 311 csum = get_csum((unsigned char *)(buf + sizeof(struct img_header)), 312 buflen - sizeof(struct img_header)); 313 314 /* fill firmware header */ 315 hdr = (struct img_header *) buf; 316 317 hdr->checksum = htonl(csum); 318 hdr->image_size = htonl(buflen - sizeof(struct img_header)); 319 hdr->kernel_size = htonl(kernel_info.write_size); 320 hdr->header_len = sizeof(struct img_header); 321 strncpy(hdr->model, model, sizeof(hdr->model)); 322 strncpy(hdr->signature, signature, sizeof(hdr->signature)); 323 strncpy(hdr->version, version, sizeof(hdr->version)); 324 strncpy(hdr->region, region, sizeof(hdr->region)); 325 326 ret = write_fw(buf, buflen); 327 if (ret) 328 goto out_free_buf; 329 330 ret = EXIT_SUCCESS; 331 332out_free_buf: 333 free(buf); 334out: 335 return ret; 336} 337 338int main(int argc, char *argv[]) 339{ 340 int ret = EXIT_FAILURE; 341 342 progname = basename(argv[0]); 343 344 while (1) { 345 int c; 346 347 c = getopt(argc, argv, "M:S:V:R:k:K:I:r:o:h"); 348 if (c == -1) 349 break; 350 351 switch (c) { 352 case 'M': 353 model = optarg; 354 break; 355 case 'S': 356 signature = optarg; 357 break; 358 case 'V': 359 version = optarg; 360 break; 361 case 'R': 362 region = optarg; 363 break; 364 case 'k': 365 kernel_info.file_name = optarg; 366 break; 367 case 'K': 368 if (str2u32(optarg, &kernel_size)) { 369 ERR("%s is invalid '%s'", 370 "kernel size", optarg); 371 goto out; 372 } 373 break; 374 case 'I': 375 if (str2u32(optarg, &image_size)) { 376 ERR("%s is invalid '%s'", 377 "image size", optarg); 378 goto out; 379 } 380 break; 381 case 'r': 382 rootfs_info.file_name = optarg; 383 break; 384 case 'o': 385 ofname = optarg; 386 break; 387 case 'h': 388 usage(EXIT_SUCCESS); 389 break; 390 default: 391 usage(EXIT_FAILURE); 392 break; 393 } 394 } 395 396 ret = check_options(); 397 if (ret) 398 goto out; 399 400 ret = build_fw(); 401 402out: 403 return ret; 404} 405 406