OpenWrt – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Builder/viewer/extractor utility for Poray firmware image files |
||
3 | * |
||
4 | * Copyright (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr> |
||
5 | * Copyright (C) 2013 Felix Kaechele <felix@fetzig.org> |
||
6 | * Copyright (C) 2013 <admin@openschemes.com> |
||
7 | * |
||
8 | * This tool is based on: |
||
9 | * TP-Link firmware upgrade tool. |
||
10 | * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> |
||
11 | * |
||
12 | * Itself based on: |
||
13 | * TP-Link WR941 V2 firmware checksum fixing tool. |
||
14 | * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> |
||
15 | * |
||
16 | * This program is free software; you can redistribute it and/or modify it |
||
17 | * under the terms of the GNU General Public License version 2 as published |
||
18 | * by the Free Software Foundation. |
||
19 | * |
||
20 | */ |
||
21 | |||
22 | #include <stdio.h> |
||
23 | #include <stdlib.h> |
||
24 | #include <stdint.h> |
||
25 | #include <string.h> |
||
26 | #include <unistd.h> |
||
27 | #include <libgen.h> |
||
28 | #include <getopt.h> |
||
29 | #include <stdarg.h> |
||
30 | #include <errno.h> |
||
31 | #include <sys/stat.h> |
||
32 | #include <arpa/inet.h> |
||
33 | #include <netinet/in.h> |
||
34 | |||
35 | #if (__BYTE_ORDER == __BIG_ENDIAN) |
||
36 | # define HOST_TO_BE32(x) (x) |
||
37 | # define BE32_TO_HOST(x) (x) |
||
38 | # define HOST_TO_LE32(x) bswap_32(x) |
||
39 | # define LE32_TO_HOST(x) bswap_32(x) |
||
40 | #else |
||
41 | # define HOST_TO_BE32(x) bswap_32(x) |
||
42 | # define BE32_TO_HOST(x) bswap_32(x) |
||
43 | # define HOST_TO_LE32(x) (x) |
||
44 | # define LE32_TO_HOST(x) (x) |
||
45 | #endif |
||
46 | |||
47 | /* Fixed header flags */ |
||
48 | #define HEADER_FLAGS 0x020e0000 |
||
49 | |||
50 | /* Recognized Hardware ID magic */ |
||
51 | #define HWID_HAME_MPR_A1_L8 0x32473352 |
||
52 | #define HWID_PORAY_R50B 0x31353033 |
||
53 | #define HWID_PORAY_R50D 0x33353033 |
||
54 | #define HWID_PORAY_R50E 0x34353033 |
||
55 | #define HWID_PORAY_M3 0x31353335 |
||
56 | #define HWID_PORAY_M4 0x32353335 |
||
57 | #define HWID_PORAY_Q3 0x33353335 |
||
58 | #define HWID_PORAY_X5_X6 0x35353335 |
||
59 | #define HWID_PORAY_X8 0x36353335 |
||
60 | #define HWID_PORAY_X1 0x38353335 |
||
61 | #define HWID_NEXX_WT1520 0x30353332 |
||
62 | #define HWID_NEXX_WT3020 0x30323033 |
||
63 | #define HWID_A5_V11 0x32473352 |
||
64 | |||
65 | /* Recognized XOR obfuscation keys */ |
||
66 | #define KEY_HAME 0 |
||
67 | #define KEY_PORAY_1 1 |
||
68 | #define KEY_PORAY_2 2 |
||
69 | #define KEY_PORAY_3 3 |
||
70 | #define KEY_PORAY_4 4 |
||
71 | #define KEY_NEXX_1 5 |
||
72 | #define KEY_NEXX_2 6 |
||
73 | #define KEY_A5_V11 7 |
||
74 | |||
75 | /* XOR key length */ |
||
76 | #define KEY_LEN 15 |
||
77 | |||
78 | struct file_info { |
||
79 | char *file_name; /* Name of the file */ |
||
80 | uint32_t file_size; /* Length of the file */ |
||
81 | }; |
||
82 | |||
83 | struct fw_header { |
||
84 | uint32_t hw_id; /* Hardware id */ |
||
85 | uint32_t firmware_len; /* Firmware data length */ |
||
86 | uint32_t flags; /* Header flags */ |
||
87 | uint8_t pad[16]; |
||
88 | } __attribute__ ((packed)); |
||
89 | |||
90 | struct flash_layout { |
||
91 | char *id; |
||
92 | uint32_t fw_max_len; |
||
93 | }; |
||
94 | |||
95 | struct board_info { |
||
96 | char *id; |
||
97 | uint32_t hw_id; |
||
98 | char *layout_id; |
||
99 | uint32_t key; |
||
100 | }; |
||
101 | |||
102 | /* |
||
103 | * Globals |
||
104 | */ |
||
105 | static char *ofname; |
||
106 | static char *progname; |
||
107 | |||
108 | static char *board_id; |
||
109 | static struct board_info *board; |
||
110 | static char *layout_id; |
||
111 | static struct flash_layout *layout; |
||
112 | static char *opt_hw_id; |
||
113 | static uint32_t hw_id; |
||
114 | static struct file_info firmware_info; |
||
115 | static uint32_t firmware_len = 0; |
||
116 | |||
117 | static int inspect = 0; |
||
118 | static int extract = 0; |
||
119 | |||
120 | static uint8_t key[][KEY_LEN] = { |
||
121 | {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x12, 0x03, 0x08}, |
||
122 | {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, |
||
123 | {0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, |
||
124 | {0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, |
||
125 | {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, |
||
126 | {0x19, 0x1C, 0x4A, 0x93, 0x96, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0x16, 0xC6}, |
||
127 | {0x39, 0x1C, 0x4A, 0x93, 0x96, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0x16, 0xC6}, |
||
128 | {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x20, 0x11, 0x08}, |
||
129 | }; |
||
130 | |||
131 | static struct flash_layout layouts[] = { |
||
132 | { |
||
133 | .id = "4M", |
||
134 | .fw_max_len = 0x3c0000, |
||
135 | }, { |
||
136 | .id = "8M", |
||
137 | .fw_max_len = 0x7c0000, |
||
138 | }, { |
||
139 | /* terminating entry */ |
||
140 | } |
||
141 | }; |
||
142 | |||
143 | static struct board_info boards[] = { |
||
144 | { |
||
145 | .id = "A5-V11", |
||
146 | .hw_id = HWID_A5_V11, |
||
147 | .layout_id = "4M", |
||
148 | .key = KEY_A5_V11, |
||
149 | }, { |
||
150 | .id = "MPR-A1", |
||
151 | .hw_id = HWID_HAME_MPR_A1_L8, |
||
152 | .layout_id = "4M", |
||
153 | .key = KEY_HAME, |
||
154 | }, { |
||
155 | .id = "MPR-L8", |
||
156 | .hw_id = HWID_HAME_MPR_A1_L8, |
||
157 | .layout_id = "4M", |
||
158 | .key = KEY_HAME, |
||
159 | }, { |
||
160 | .id = "R50B", |
||
161 | .hw_id = HWID_PORAY_R50B, |
||
162 | .layout_id = "4M", |
||
163 | .key = KEY_PORAY_2, |
||
164 | }, { |
||
165 | .id = "R50D", |
||
166 | .hw_id = HWID_PORAY_R50D, |
||
167 | .layout_id = "4M", |
||
168 | .key = KEY_PORAY_3, |
||
169 | }, { |
||
170 | .id = "R50E", |
||
171 | .hw_id = HWID_PORAY_R50E, |
||
172 | .layout_id = "4M", |
||
173 | .key = KEY_PORAY_4, |
||
174 | }, { |
||
175 | .id = "M3", |
||
176 | .hw_id = HWID_PORAY_M3, |
||
177 | .layout_id = "4M", |
||
178 | .key = KEY_PORAY_1, |
||
179 | }, { |
||
180 | .id = "M4", |
||
181 | .hw_id = HWID_PORAY_M4, |
||
182 | .layout_id = "4M", |
||
183 | .key = KEY_PORAY_1, |
||
184 | }, { |
||
185 | .id = "Q3", |
||
186 | .hw_id = HWID_PORAY_Q3, |
||
187 | .layout_id = "4M", |
||
188 | .key = KEY_PORAY_1, |
||
189 | }, { |
||
190 | .id = "X5 or X6", |
||
191 | .hw_id = HWID_PORAY_X5_X6, |
||
192 | .layout_id = "8M", |
||
193 | .key = KEY_PORAY_1, |
||
194 | }, { |
||
195 | .id = "X5", |
||
196 | .hw_id = HWID_PORAY_X5_X6, |
||
197 | .layout_id = "8M", |
||
198 | .key = KEY_PORAY_1, |
||
199 | }, { |
||
200 | .id = "X6", |
||
201 | .hw_id = HWID_PORAY_X5_X6, |
||
202 | .layout_id = "8M", |
||
203 | .key = KEY_PORAY_1, |
||
204 | }, { |
||
205 | .id = "X8", |
||
206 | .hw_id = HWID_PORAY_X8, |
||
207 | .layout_id = "8M", |
||
208 | .key = KEY_PORAY_1, |
||
209 | }, { |
||
210 | .id = "X1", |
||
211 | .hw_id = HWID_PORAY_X1, |
||
212 | .layout_id = "8M", |
||
213 | .key = KEY_PORAY_1, |
||
214 | }, { |
||
215 | .id = "WT1520", |
||
216 | .hw_id = HWID_NEXX_WT1520, |
||
217 | .layout_id = "4M", |
||
218 | .key = KEY_NEXX_1, |
||
219 | }, { |
||
220 | .id = "WT1520", |
||
221 | .hw_id = HWID_NEXX_WT1520, |
||
222 | .layout_id = "8M", |
||
223 | .key = KEY_NEXX_1, |
||
224 | }, { |
||
225 | .id = "WT3020", |
||
226 | .hw_id = HWID_NEXX_WT3020, |
||
227 | .layout_id = "4M", |
||
228 | .key = KEY_NEXX_2, |
||
229 | }, { |
||
230 | .id = "WT3020", |
||
231 | .hw_id = HWID_NEXX_WT3020, |
||
232 | .layout_id = "8M", |
||
233 | .key = KEY_NEXX_2, |
||
234 | }, { |
||
235 | |||
236 | |||
237 | |||
238 | |||
239 | /* terminating entry */ |
||
240 | } |
||
241 | }; |
||
242 | |||
243 | /* |
||
244 | * Message macros |
||
245 | */ |
||
246 | #define ERR(fmt, ...) do { \ |
||
247 | fflush(0); \ |
||
248 | fprintf(stderr, "[%s] *** error: " fmt "\n", \ |
||
249 | progname, ## __VA_ARGS__ ); \ |
||
250 | } while (0) |
||
251 | |||
252 | #define ERRS(fmt, ...) do { \ |
||
253 | int save = errno; \ |
||
254 | fflush(0); \ |
||
255 | fprintf(stderr, "[%s] *** error: " fmt ":%s\n", \ |
||
256 | progname, ## __VA_ARGS__, strerror(save)); \ |
||
257 | } while (0) |
||
258 | |||
259 | #define DBG(fmt, ...) do { \ |
||
260 | fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ |
||
261 | } while (0) |
||
262 | |||
263 | /* |
||
264 | * Find a board by its name |
||
265 | */ |
||
266 | static struct board_info *find_board(char *id) |
||
267 | { |
||
268 | struct board_info *ret; |
||
269 | struct board_info *board; |
||
270 | |||
271 | ret = NULL; |
||
272 | for (board = boards; board->id != NULL; board++){ |
||
273 | if (strcasecmp(id, board->id) == 0) { |
||
274 | ret = board; |
||
275 | break; |
||
276 | } |
||
277 | }; |
||
278 | |||
279 | return ret; |
||
280 | } |
||
281 | |||
282 | /* |
||
283 | * Find a board by its hardware ID |
||
284 | */ |
||
285 | static struct board_info *find_board_by_hwid(uint32_t hw_id) |
||
286 | { |
||
287 | struct board_info *board; |
||
288 | |||
289 | for (board = boards; board->id != NULL; board++) { |
||
290 | if (hw_id == board->hw_id) |
||
291 | return board; |
||
292 | }; |
||
293 | |||
294 | return NULL; |
||
295 | } |
||
296 | |||
297 | /* |
||
298 | * Find a Flash memory layout by its name |
||
299 | */ |
||
300 | static struct flash_layout *find_layout(char *id) |
||
301 | { |
||
302 | struct flash_layout *ret; |
||
303 | struct flash_layout *l; |
||
304 | |||
305 | ret = NULL; |
||
306 | for (l = layouts; l->id != NULL; l++){ |
||
307 | if (strcasecmp(id, l->id) == 0) { |
||
308 | ret = l; |
||
309 | break; |
||
310 | } |
||
311 | }; |
||
312 | |||
313 | return ret; |
||
314 | } |
||
315 | |||
316 | /* |
||
317 | * Display usage |
||
318 | */ |
||
319 | static void usage(int status) |
||
320 | { |
||
321 | FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; |
||
322 | |||
323 | fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); |
||
324 | fprintf(stream, |
||
325 | "\n" |
||
326 | "Options:\n" |
||
327 | " -B <board> create image for the board specified with <board>\n" |
||
328 | " -H <hwid> use hardware id specified with <hwid>\n" |
||
329 | " -F <id> use flash layout specified with <id>\n" |
||
330 | " -f <file> read firmware image from the file <file>\n" |
||
331 | " -o <file> write output to the file <file>\n" |
||
332 | " -i inspect given firmware file (requires -f)\n" |
||
333 | " -x extract combined kernel and rootfs while inspecting (implies -i)\n" |
||
334 | " -h show this screen\n" |
||
335 | ); |
||
336 | |||
337 | exit(status); |
||
338 | } |
||
339 | |||
340 | /* |
||
341 | * Get file statistics |
||
342 | */ |
||
343 | static int get_file_stat(struct file_info *fdata) |
||
344 | { |
||
345 | struct stat st; |
||
346 | int res; |
||
347 | |||
348 | if (fdata->file_name == NULL) { |
||
349 | return 0; |
||
350 | } |
||
351 | res = stat(fdata->file_name, &st); |
||
352 | if (res){ |
||
353 | ERRS("stat failed on %s", fdata->file_name); |
||
354 | return res; |
||
355 | } |
||
356 | |||
357 | fdata->file_size = st.st_size; |
||
358 | return 0; |
||
359 | } |
||
360 | |||
361 | /* |
||
362 | * Read file into buffer |
||
363 | */ |
||
364 | static int read_to_buf(struct file_info *fdata, uint8_t *buf) |
||
365 | { |
||
366 | FILE *f; |
||
367 | int ret = EXIT_FAILURE; |
||
368 | |||
369 | f = fopen(fdata->file_name, "rb"); |
||
370 | if (f == NULL) { |
||
371 | ERRS("could not open \"%s\" for reading", fdata->file_name); |
||
372 | goto out; |
||
373 | } |
||
374 | |||
375 | errno = 0; |
||
376 | fread(buf, fdata->file_size, 1, f); |
||
377 | if (errno != 0) { |
||
378 | ERRS("unable to read from file \"%s\"", fdata->file_name); |
||
379 | goto out_close; |
||
380 | } |
||
381 | |||
382 | ret = EXIT_SUCCESS; |
||
383 | |||
384 | out_close: |
||
385 | fclose(f); |
||
386 | out: |
||
387 | return ret; |
||
388 | } |
||
389 | |||
390 | /* |
||
391 | * Check command line options |
||
392 | */ |
||
393 | static int check_options(void) |
||
394 | { |
||
395 | int ret; |
||
396 | |||
397 | if (firmware_info.file_name == NULL) { |
||
398 | ERR("no firmware image specified"); |
||
399 | return -1; |
||
400 | } |
||
401 | |||
402 | ret = get_file_stat(&firmware_info); |
||
403 | if (ret) |
||
404 | return ret; |
||
405 | |||
406 | if (inspect) |
||
407 | return 0; |
||
408 | |||
409 | if (board_id == NULL && opt_hw_id == NULL) { |
||
410 | ERR("either board or hardware id must be specified"); |
||
411 | return -1; |
||
412 | } |
||
413 | |||
414 | if (board_id) { |
||
415 | board = find_board(board_id); |
||
416 | if (board == NULL) { |
||
417 | ERR("unknown/unsupported board id \"%s\"", board_id); |
||
418 | return -1; |
||
419 | } |
||
420 | if (layout_id == NULL) { |
||
421 | layout_id = board->layout_id; |
||
422 | } |
||
423 | hw_id = board->hw_id; |
||
424 | } else { |
||
425 | hw_id = strtoul(opt_hw_id, NULL, 0); |
||
426 | board = find_board_by_hwid(hw_id); |
||
427 | if (layout_id == NULL) { |
||
428 | layout_id = board->layout_id; |
||
429 | } |
||
430 | } |
||
431 | |||
432 | layout = find_layout(layout_id); |
||
433 | if (layout == NULL) { |
||
434 | ERR("unknown flash layout \"%s\"", layout_id); |
||
435 | return -1; |
||
436 | } |
||
437 | |||
438 | firmware_len = firmware_info.file_size; |
||
439 | |||
440 | if (firmware_info.file_size > |
||
441 | layout->fw_max_len - sizeof (struct fw_header)) { |
||
442 | ERR("firmware image is too big"); |
||
443 | return -1; |
||
444 | } |
||
445 | |||
446 | if (ofname == NULL) { |
||
447 | ERR("no output file specified"); |
||
448 | return -1; |
||
449 | } |
||
450 | return 0; |
||
451 | } |
||
452 | |||
453 | /* |
||
454 | * Fill in firmware header |
||
455 | */ |
||
456 | static void fill_header(uint8_t *buf) |
||
457 | { |
||
458 | struct fw_header *hdr = (struct fw_header *) buf; |
||
459 | |||
460 | memset(hdr, 0, sizeof (struct fw_header)); |
||
461 | hdr->hw_id = HOST_TO_LE32(hw_id); |
||
462 | hdr->firmware_len = HOST_TO_LE32(firmware_len); |
||
463 | hdr->flags = HOST_TO_LE32(HEADER_FLAGS); |
||
464 | } |
||
465 | |||
466 | /* |
||
467 | * Compute firmware checksum |
||
468 | */ |
||
469 | static uint16_t checksum_fw(uint8_t *data, int len) |
||
470 | { |
||
471 | int i; |
||
472 | int32_t checksum = 0; |
||
473 | |||
474 | for (i = 0; i < len - 1; i += 2) { |
||
475 | checksum += (data[i + 1] << 8) | data[i]; |
||
476 | } |
||
477 | if (i < len) { |
||
478 | checksum += data[i]; |
||
479 | } |
||
480 | checksum = checksum + (checksum >> 16) + 0xffff; |
||
481 | checksum = ~(checksum + (checksum >> 16)) & 0xffff; |
||
482 | return (uint16_t) checksum; |
||
483 | } |
||
484 | |||
485 | /* |
||
486 | * (De)obfuscate firmware using an XOR operation with a fixed length key |
||
487 | */ |
||
488 | static void xor_fw(uint8_t *data, int len) |
||
489 | { |
||
490 | int i; |
||
491 | |||
492 | for (i = 0; i <= len; i++) { |
||
493 | data[i] ^= key[board->key][i % KEY_LEN]; |
||
494 | } |
||
495 | } |
||
496 | |||
497 | /* |
||
498 | * Write firmware to file |
||
499 | */ |
||
500 | static int write_fw(uint8_t *data, int len) |
||
501 | { |
||
502 | FILE *f; |
||
503 | int ret = EXIT_FAILURE; |
||
504 | |||
505 | f = fopen(ofname, "wb"); |
||
506 | if (f == NULL) { |
||
507 | ERRS("could not open \"%s\" for writing", ofname); |
||
508 | goto out; |
||
509 | } |
||
510 | |||
511 | errno = 0; |
||
512 | fwrite(data, len, 1, f); |
||
513 | if (errno) { |
||
514 | ERRS("unable to write output file"); |
||
515 | goto out_flush; |
||
516 | } |
||
517 | |||
518 | DBG("firmware file \"%s\" completed", ofname); |
||
519 | |||
520 | ret = EXIT_SUCCESS; |
||
521 | |||
522 | out_flush: |
||
523 | fflush(f); |
||
524 | fclose(f); |
||
525 | if (ret != EXIT_SUCCESS) { |
||
526 | unlink(ofname); |
||
527 | } |
||
528 | out: |
||
529 | return ret; |
||
530 | } |
||
531 | |||
532 | /* |
||
533 | * Build firmware file |
||
534 | */ |
||
535 | static int build_fw(void) |
||
536 | { |
||
537 | int buflen; |
||
538 | uint8_t *buf, *p; |
||
539 | int ret = EXIT_FAILURE; |
||
540 | int writelen = 0; |
||
541 | uint16_t checksum; |
||
542 | |||
543 | buflen = layout->fw_max_len; |
||
544 | |||
545 | buf = (uint8_t *) malloc(buflen); |
||
546 | if (!buf) { |
||
547 | ERR("no memory for buffer\n"); |
||
548 | goto out; |
||
549 | } |
||
550 | |||
551 | memset(buf, 0xff, buflen); |
||
552 | p = buf + sizeof (struct fw_header); |
||
553 | ret = read_to_buf(&firmware_info, p); |
||
554 | if (ret) { |
||
555 | goto out_free_buf; |
||
556 | } |
||
557 | writelen = sizeof (struct fw_header) + firmware_len + 2; |
||
558 | |||
559 | /* Fill in header */ |
||
560 | fill_header(buf); |
||
561 | |||
562 | /* Compute firmware checksum */ |
||
563 | checksum = checksum_fw(buf + sizeof (struct fw_header), firmware_len); |
||
564 | |||
565 | /* Cannot use network order function because checksum is not word-aligned */ |
||
566 | buf[writelen - 1] = checksum >> 8; |
||
567 | buf[writelen - 2] = checksum & 0xff; |
||
568 | |||
569 | /* XOR obfuscate firmware */ |
||
570 | xor_fw(buf + sizeof (struct fw_header), firmware_len + 2); |
||
571 | |||
572 | /* Write firmware file */ |
||
573 | ret = write_fw(buf, writelen); |
||
574 | if (ret) { |
||
575 | goto out_free_buf; |
||
576 | } |
||
577 | ret = EXIT_SUCCESS; |
||
578 | |||
579 | out_free_buf: |
||
580 | free(buf); |
||
581 | out: |
||
582 | return ret; |
||
583 | } |
||
584 | |||
585 | /* Helper functions to inspect_fw() representing different output formats */ |
||
586 | static inline void inspect_fw_pstr(char *label, char *str) |
||
587 | { |
||
588 | printf("%-23s: %s\n", label, str); |
||
589 | } |
||
590 | |||
591 | static inline void inspect_fw_phex(char *label, uint32_t val) |
||
592 | { |
||
593 | printf("%-23s: 0x%08x\n", label, val); |
||
594 | } |
||
595 | |||
596 | static inline void inspect_fw_phexpost(char *label, |
||
597 | uint32_t val, char *post) |
||
598 | { |
||
599 | printf("%-23s: 0x%08x (%s)\n", label, val, post); |
||
600 | } |
||
601 | |||
602 | static inline void inspect_fw_phexdef(char *label, |
||
603 | uint32_t val, uint32_t defval) |
||
604 | { |
||
605 | printf("%-23s: 0x%08x ", label, val); |
||
606 | |||
607 | if (val == defval) { |
||
608 | printf("(== OpenWrt default)\n"); |
||
609 | } else { |
||
610 | printf("(OpenWrt default: 0x%08x)\n", defval); |
||
611 | } |
||
612 | } |
||
613 | |||
614 | static inline void inspect_fw_phexexp(char *label, |
||
615 | uint32_t val, uint32_t expval) |
||
616 | { |
||
617 | printf("%-23s: 0x%08x ", label, val); |
||
618 | |||
619 | if (val == expval) { |
||
620 | printf("(ok)\n"); |
||
621 | } else { |
||
622 | printf("(expected: 0x%08x)\n", expval); |
||
623 | } |
||
624 | } |
||
625 | |||
626 | static inline void inspect_fw_phexdec(char *label, uint32_t val) |
||
627 | { |
||
628 | printf("%-23s: 0x%08x / %8u bytes\n", label, val, val); |
||
629 | } |
||
630 | |||
631 | static inline void inspect_fw_pchecksum(char *label, |
||
632 | uint16_t val, uint16_t expval) |
||
633 | { |
||
634 | printf("%-23s: 0x%04x ", label, val); |
||
635 | if (val == expval) { |
||
636 | printf("(ok)\n"); |
||
637 | } else { |
||
638 | printf("(expected: 0x%04x)\n", expval); |
||
639 | } |
||
640 | } |
||
641 | |||
642 | static int inspect_fw(void) |
||
643 | { |
||
644 | uint8_t *buf; |
||
645 | struct fw_header *hdr; |
||
646 | int ret = EXIT_FAILURE; |
||
647 | uint16_t computed_checksum, file_checksum; |
||
648 | |||
649 | buf = (uint8_t *) malloc(firmware_info.file_size); |
||
650 | if (!buf) { |
||
651 | ERR("no memory for buffer!\n"); |
||
652 | goto out; |
||
653 | } |
||
654 | |||
655 | ret = read_to_buf(&firmware_info, buf); |
||
656 | if (ret) { |
||
657 | goto out_free_buf; |
||
658 | } |
||
659 | hdr = (struct fw_header *)buf; |
||
660 | |||
661 | inspect_fw_pstr("File name", firmware_info.file_name); |
||
662 | inspect_fw_phexdec("File size", firmware_info.file_size); |
||
663 | |||
664 | printf("\n"); |
||
665 | |||
666 | inspect_fw_phexdec("Header size", sizeof (struct fw_header)); |
||
667 | board = find_board_by_hwid(LE32_TO_HOST(hdr->hw_id)); |
||
668 | if (board) { |
||
669 | layout = find_layout(board->layout_id); |
||
670 | inspect_fw_phexpost("Hardware ID", |
||
671 | LE32_TO_HOST( hdr->hw_id), board->id); |
||
672 | } else { |
||
673 | inspect_fw_phexpost("Hardware ID", |
||
674 | LE32_TO_HOST(hdr->hw_id), "unknown"); |
||
675 | } |
||
676 | inspect_fw_phexdec("Firmware data length", |
||
677 | LE32_TO_HOST(hdr->firmware_len)); |
||
678 | |||
679 | inspect_fw_phexexp("Flags", |
||
680 | LE32_TO_HOST(hdr->flags), HEADER_FLAGS); |
||
681 | printf("\n"); |
||
682 | |||
683 | /* XOR unobfuscate firmware */ |
||
684 | xor_fw(buf + sizeof (struct fw_header), LE32_TO_HOST(hdr->firmware_len) + 2); |
||
685 | |||
686 | /* Compute firmware checksum */ |
||
687 | computed_checksum = checksum_fw(buf + sizeof (struct fw_header), LE32_TO_HOST(hdr->firmware_len)); |
||
688 | |||
689 | /* Cannot use network order function because checksum is not word-aligned */ |
||
690 | file_checksum = (buf[firmware_info.file_size - 1] << 8) | buf[firmware_info.file_size - 2]; |
||
691 | inspect_fw_pchecksum("Firmware checksum", computed_checksum, file_checksum); |
||
692 | |||
693 | /* Verify checksum */ |
||
694 | if (computed_checksum != file_checksum) { |
||
695 | ret = -1; |
||
696 | ERR("checksums do not match"); |
||
697 | goto out_free_buf; |
||
698 | } |
||
699 | |||
700 | printf("\n"); |
||
701 | |||
702 | if (extract) { |
||
703 | FILE *fp; |
||
704 | char *filename; |
||
705 | |||
706 | if (ofname == NULL) { |
||
707 | filename = malloc(strlen(firmware_info.file_name) + 10); |
||
708 | sprintf(filename, "%s-firmware", firmware_info.file_name); |
||
709 | } else { |
||
710 | filename = ofname; |
||
711 | } |
||
712 | printf("Extracting firmware to \"%s\"...\n", filename); |
||
713 | fp = fopen(filename, "wb"); |
||
714 | if (fp) { |
||
715 | if (!fwrite(buf + sizeof (struct fw_header), |
||
716 | LE32_TO_HOST(hdr->firmware_len), 1, fp)) { |
||
717 | ERRS("error in fwrite(): %s", strerror(errno)); |
||
718 | } |
||
719 | fclose(fp); |
||
720 | } else { |
||
721 | ERRS("error in fopen(): %s", strerror(errno)); |
||
722 | } |
||
723 | if (ofname == NULL) { |
||
724 | free(filename); |
||
725 | } |
||
726 | printf("\n"); |
||
727 | } |
||
728 | |||
729 | out_free_buf: |
||
730 | free(buf); |
||
731 | out: |
||
732 | return ret; |
||
733 | } |
||
734 | |||
735 | /* |
||
736 | * Main entry point |
||
737 | */ |
||
738 | int main(int argc, char *argv[]) |
||
739 | { |
||
740 | int ret = EXIT_FAILURE; |
||
741 | |||
742 | progname = basename(argv[0]); |
||
743 | |||
744 | int c; |
||
745 | |||
746 | while ((c = getopt(argc, argv, "B:H:F:f:o:ixh")) != -1) { |
||
747 | switch (c) { |
||
748 | case 'B': |
||
749 | board_id = optarg; |
||
750 | break; |
||
751 | case 'H': |
||
752 | opt_hw_id = optarg; |
||
753 | break; |
||
754 | case 'F': |
||
755 | layout_id = optarg; |
||
756 | break; |
||
757 | case 'f': |
||
758 | firmware_info.file_name = optarg; |
||
759 | break; |
||
760 | case 'o': |
||
761 | ofname = optarg; |
||
762 | break; |
||
763 | case 'i': |
||
764 | inspect = 1; |
||
765 | break; |
||
766 | case 'x': |
||
767 | inspect = 1; |
||
768 | extract = 1; |
||
769 | break; |
||
770 | case 'h': |
||
771 | usage(EXIT_SUCCESS); |
||
772 | break; |
||
773 | default: |
||
774 | usage(EXIT_FAILURE); |
||
775 | break; |
||
776 | } |
||
777 | } |
||
778 | |||
779 | ret = check_options(); |
||
780 | if (ret) { |
||
781 | goto out; |
||
782 | } |
||
783 | if (!inspect) { |
||
784 | ret = build_fw(); |
||
785 | } else { |
||
786 | ret = inspect_fw(); |
||
787 | } |
||
788 | |||
789 | out: |
||
790 | return ret; |
||
791 | } |