rpcd: iwinfo plugin fixes
[openwrt.git] / tools / firmware-utils / src / fix-u-media-header.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 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include <unistd.h>     /* for unlink() */
14 #include <libgen.h>
15 #include <getopt.h>     /* for getopt() */
16 #include <stdarg.h>
17 #include <errno.h>
18 #include <sys/stat.h>
19
20 #include "cyg_crc.h"
21
22 #include <arpa/inet.h>
23 #include <netinet/in.h>
24
25 #define IH_MAGIC        0x27051956      /* Image Magic Number           */
26 #define IH_NMLEN        32              /* Image Name Length            */
27
28 #define UM_MAGIC        0x55525F46
29 #define UM_HEADER_LEN   12
30
31 /*
32  * all data in network byte order (aka natural aka bigendian)
33  */
34 struct u_media_header {
35         uint32_t        ih_magic;       /* Image Header Magic Number    */
36         uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
37         uint32_t        ih_time;        /* Image Creation Timestamp     */
38         uint32_t        ih_size;        /* Image Data Size              */
39         uint32_t        ih_load;        /* Data  Load  Address          */
40         uint32_t        ih_ep;          /* Entry Point Address          */
41         uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
42         uint8_t         ih_os;          /* Operating System             */
43         uint8_t         ih_arch;        /* CPU architecture             */
44         uint8_t         ih_type;        /* Image Type                   */
45         uint8_t         ih_comp;        /* Compression Type             */
46         uint8_t         ih_name[IH_NMLEN - UM_HEADER_LEN]; /* Image Name                */
47
48         uint32_t        ih_UMedia_magic;        /* U-Media magic number */
49         uint32_t        ih_UMedia_boardID;      /* U-Media board ID     */
50         uint8_t         ih_UMedia_imageType;    /* U-Media image type */
51         uint8_t         ih_UMedia_LoadDefault;  /* U-Media load to factory default setting */
52         uint8_t         ih_UMedia_temp1;        /* U-Media didn't use this tag */
53         uint8_t         ih_UMedia_temp2;        /* U-Media didn't use this tag */
54 } __attribute__ ((packed));
55
56 struct if_info {
57         char            *file_name;     /* name of the file */
58         uint32_t        file_size;      /* length of the file */
59 };
60
61 static char *progname;
62 static char *ofname;
63 static struct if_info if_info;
64 static int factory_defaults;
65 static uint32_t board_id;
66 static uint8_t image_type;
67
68 /*
69  * Message macros
70  */
71 #define ERR(fmt, ...) do { \
72         fflush(0); \
73         fprintf(stderr, "[%s] *** error: " fmt "\n", \
74                         progname, ## __VA_ARGS__ ); \
75 } while (0)
76
77 #define ERRS(fmt, ...) do { \
78         int save = errno; \
79         fflush(0); \
80         fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \
81                         progname, ## __VA_ARGS__, strerror(save)); \
82 } while (0)
83
84 #define DBG(fmt, ...) do { \
85         fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \
86 } while (0)
87
88 static void usage(int status)
89 {
90         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
91
92         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
93         fprintf(stream,
94 "\n"
95 "Options:\n"
96 "  -B <board_id>   set board ID to <board_id>\n"
97 "  -i <file>       read input from the file <file>\n"
98 "  -F              load factory defaults\n"
99 "  -o <file>       write output to the file <file>\n"
100 "  -T <type>       set image type to <type>\n"
101 "  -h              show this screen\n"
102         );
103
104         exit(status);
105 }
106
107 static int str2u32(char *arg, uint32_t *val)
108 {
109         char *err = NULL;
110         uint32_t t;
111
112         errno=0;
113         t = strtoul(arg, &err, 0);
114         if (errno || (err==arg) || ((err != NULL) && *err)) {
115                 return -1;
116         }
117
118         *val = t;
119         return 0;
120 }
121
122 static int str2u8(char *arg, uint8_t *val)
123 {
124         char *err = NULL;
125         uint32_t t;
126
127         errno=0;
128         t = strtoul(arg, &err, 0);
129         if (errno || (err==arg) || ((err != NULL) && *err)) {
130                 return -1;
131         }
132
133         if (t > 255)
134                 return -1;
135
136         *val = t;
137         return 0;
138 }
139
140 static int get_file_stat(struct if_info *fdata)
141 {
142         struct stat st;
143         int res;
144
145         if (fdata->file_name == NULL)
146                 return 0;
147
148         res = stat(fdata->file_name, &st);
149         if (res){
150                 ERRS("stat failed on %s", fdata->file_name);
151                 return res;
152         }
153
154         fdata->file_size = st.st_size;
155         return 0;
156 }
157
158 static int read_to_buf(struct if_info *fdata, char *buf)
159 {
160         FILE *f;
161         int ret = EXIT_FAILURE;
162
163         f = fopen(fdata->file_name, "r");
164         if (f == NULL) {
165                 ERRS("could not open \"%s\" for reading", fdata->file_name);
166                 goto out;
167         }
168
169         errno = 0;
170         fread(buf, fdata->file_size, 1, f);
171         if (errno != 0) {
172                 ERRS("unable to read from file \"%s\"", fdata->file_name);
173                 goto out_close;
174         }
175
176         ret = EXIT_SUCCESS;
177
178 out_close:
179         fclose(f);
180 out:
181         return ret;
182 }
183
184 static int check_options(void)
185 {
186         int ret;
187
188         if (ofname == NULL) {
189                 ERR("no %s specified", "output file");
190                 return -1;
191         }
192
193         if (if_info.file_name == NULL) {
194                 ERR("no %s specified", "input file");
195                 return -1;
196         }
197
198         ret = get_file_stat(&if_info);
199         if (ret)
200                 return ret;
201
202         return 0;
203 }
204
205 static int write_fw(char *data, int len)
206 {
207         FILE *f;
208         int ret = EXIT_FAILURE;
209
210         f = fopen(ofname, "w");
211         if (f == NULL) {
212                 ERRS("could not open \"%s\" for writing", ofname);
213                 goto out;
214         }
215
216         errno = 0;
217         fwrite(data, len, 1, f);
218         if (errno) {
219                 ERRS("unable to write output file");
220                 goto out_flush;
221         }
222
223         ret = EXIT_SUCCESS;
224
225 out_flush:
226         fflush(f);
227         fclose(f);
228         if (ret != EXIT_SUCCESS) {
229                 unlink(ofname);
230         }
231 out:
232         return ret;
233 }
234
235 static int fix_header(void)
236 {
237         int buflen;
238         char *buf;
239         uint32_t crc, crc_orig;
240         struct u_media_header *hdr;
241         int ret = EXIT_FAILURE;
242
243         buflen = if_info.file_size;
244         if (buflen < sizeof(*hdr)) {
245                 ERR("invalid input file\n");
246                 return ret;
247         }
248
249         buf = malloc(buflen);
250         if (!buf) {
251                 ERR("no memory for buffer\n");
252                 goto out;
253         }
254
255         ret = read_to_buf(&if_info, buf);
256         if (ret)
257                 goto out_free_buf;
258
259         hdr = (struct u_media_header *) buf;
260         if (ntohl(hdr->ih_magic) != IH_MAGIC) {
261                 ERR("invalid input file, bad magic\n");
262                 goto out_free_buf;
263         }
264
265         /* verify header CRC */
266         crc_orig = ntohl(hdr->ih_hcrc);
267         hdr->ih_hcrc = 0;
268         crc = cyg_ether_crc32((unsigned char *)hdr, sizeof(*hdr));
269         if (crc != crc_orig) {
270                 ERR("invalid input file, bad header CRC\n");
271                 goto out_free_buf;
272         }
273
274         hdr->ih_name[IH_NMLEN - UM_HEADER_LEN - 1] = '\0';
275
276         /* set U-Media specific fields */
277         hdr->ih_UMedia_magic = htonl(UM_MAGIC);
278         hdr->ih_UMedia_boardID = htonl(board_id);
279         hdr->ih_UMedia_imageType = image_type;
280         hdr->ih_UMedia_LoadDefault = (factory_defaults) ? 1 : 0;
281
282         /* update header CRC */
283         crc = cyg_ether_crc32((unsigned char *)hdr, sizeof(*hdr));
284         hdr->ih_hcrc = htonl(crc);
285
286         ret = write_fw(buf, buflen);
287         if (ret)
288                 goto out_free_buf;
289
290         DBG("U-Media header fixed in \"%s\"", ofname);
291
292         ret = EXIT_SUCCESS;
293
294 out_free_buf:
295         free(buf);
296 out:
297         return ret;
298 }
299
300 int main(int argc, char *argv[])
301 {
302         int ret = EXIT_FAILURE;
303
304         progname = basename(argv[0]);
305
306         while (1) {
307                 int c;
308
309                 c = getopt(argc, argv, "B:Fi:o:T:h");
310                 if (c == -1)
311                         break;
312
313                 switch (c) {
314                 case 'B':
315                         if (str2u32(optarg, &board_id)) {
316                                 ERR("%s is invalid '%s'",
317                                     "board ID", optarg);
318                                 goto out;
319                         }
320                         break;
321                 case 'T':
322                         if (str2u8(optarg, &image_type)) {
323                                 ERR("%s is invalid '%s'",
324                                     "image type", optarg);
325                                 goto out;
326                         }
327                         break;
328                 case 'F':
329                         factory_defaults = 1;
330                         break;
331                 case 'i':
332                         if_info.file_name = optarg;
333                         break;
334                 case 'o':
335                         ofname = optarg;
336                         break;
337                 case 'h':
338                         usage(EXIT_SUCCESS);
339                         break;
340                 default:
341                         usage(EXIT_FAILURE);
342                         break;
343                 }
344         }
345
346         ret = check_options();
347         if (ret)
348                 goto out;
349
350         ret = fix_header();
351
352 out:
353         return ret;
354 }