add chaos_calmer branch
[15.05/openwrt.git] / tools / firmware-utils / src / mkcameofw.c
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
31 struct 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
37 struct 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  */
53 static char *ofname;
54 static char *progname;
55
56 static char *model;
57 static char *signature;
58 static char *region = "DEF";
59 static char *version;
60 static struct file_info kernel_info;
61 static struct file_info rootfs_info;
62 static uint32_t kernel_size;
63 static uint32_t image_size;
64 static 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
86 static 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
110 int
111 str2u32(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
126 static 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
145 static 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
165 out_close:
166         fclose(f);
167 out:
168         return ret;
169 }
170
171 static 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
255 static 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
277 out_flush:
278         fflush(f);
279         fclose(f);
280         if (ret != EXIT_SUCCESS) {
281                 unlink(ofname);
282         }
283 out:
284         return ret;
285 }
286
287 static 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
297 static 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
357 out_free_buf:
358         free(buf);
359 out:
360         return ret;
361 }
362
363 int 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
430 out:
431         return ret;
432 }
433