OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright (C) 2007 Ubiquiti Networks, Inc. |
||
3 | * Copyright (C) 2008 Lukas Kuna <ValXdater@seznam.cz> |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU General Public License as |
||
7 | * published by the Free Software Foundation; either version 2 of the |
||
8 | * License, or (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, but |
||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License |
||
16 | * along with this program; if not, write to the Free Software |
||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
18 | */ |
||
19 | |||
20 | #include <sys/types.h> |
||
21 | #include <sys/stat.h> |
||
22 | #include <fcntl.h> |
||
23 | #include <unistd.h> |
||
24 | #include <string.h> |
||
25 | #include <errno.h> |
||
26 | #include <zlib.h> |
||
27 | #include <sys/mman.h> |
||
28 | #include <netinet/in.h> |
||
29 | #include <stdio.h> |
||
30 | #include <stdlib.h> |
||
31 | #include <limits.h> |
||
32 | #include <stdbool.h> |
||
33 | #include "fw.h" |
||
34 | |||
35 | typedef struct fw_layout_data { |
||
36 | u_int32_t kern_start; |
||
37 | u_int32_t kern_entry; |
||
38 | u_int32_t firmware_max_length; |
||
39 | } fw_layout_t; |
||
40 | |||
41 | struct fw_info { |
||
42 | char name[PATH_MAX]; |
||
43 | struct fw_layout_data fw_layout; |
||
44 | bool sign; |
||
45 | }; |
||
46 | |||
47 | struct fw_info fw_info[] = { |
||
48 | { |
||
49 | .name = "XS2", |
||
50 | .fw_layout = { |
||
51 | .kern_start = 0xbfc30000, |
||
52 | .kern_entry = 0x80041000, |
||
53 | .firmware_max_length= 0x00390000, |
||
54 | }, |
||
55 | .sign = false, |
||
56 | }, |
||
57 | { |
||
58 | .name = "XS5", |
||
59 | .fw_layout = { |
||
60 | .kern_start = 0xbe030000, |
||
61 | .kern_entry = 0x80041000, |
||
62 | .firmware_max_length= 0x00390000, |
||
63 | }, |
||
64 | .sign = false, |
||
65 | }, |
||
66 | { |
||
67 | .name = "RS", |
||
68 | .fw_layout = { |
||
69 | .kern_start = 0xbf030000, |
||
70 | .kern_entry = 0x80060000, |
||
71 | .firmware_max_length= 0x00B00000, |
||
72 | }, |
||
73 | .sign = false, |
||
74 | }, |
||
75 | { |
||
76 | .name = "RSPRO", |
||
77 | .fw_layout = { |
||
78 | .kern_start = 0xbf030000, |
||
79 | .kern_entry = 0x80060000, |
||
80 | .firmware_max_length= 0x00F00000, |
||
81 | }, |
||
82 | .sign = false, |
||
83 | }, |
||
84 | { |
||
85 | .name = "LS-SR71", |
||
86 | .fw_layout = { |
||
87 | .kern_start = 0xbf030000, |
||
88 | .kern_entry = 0x80060000, |
||
89 | .firmware_max_length= 0x00640000, |
||
90 | }, |
||
91 | .sign = false, |
||
92 | }, |
||
93 | { |
||
94 | .name = "XS2-8", |
||
95 | .fw_layout = { |
||
96 | .kern_start = 0xa8030000, |
||
97 | .kern_entry = 0x80041000, |
||
98 | .firmware_max_length= 0x006C0000, |
||
99 | }, |
||
100 | .sign = false, |
||
101 | |||
102 | }, |
||
103 | { |
||
104 | .name = "XM", |
||
105 | .fw_layout = { |
||
106 | .kern_start = 0x9f050000, |
||
107 | .kern_entry = 0x80002000, |
||
108 | .firmware_max_length= 0x00760000, |
||
109 | }, |
||
110 | .sign = false, |
||
111 | }, |
||
112 | { |
||
113 | .name = "UBDEV01", |
||
114 | .fw_layout = { |
||
115 | .kern_start = 0x9f050000, |
||
116 | .kern_entry = 0x80002000, |
||
117 | .firmware_max_length= 0x006A0000, |
||
118 | }, |
||
119 | .sign = false, |
||
120 | }, |
||
121 | { |
||
122 | .name = "WA", |
||
123 | .fw_layout = { |
||
124 | .kern_start = 0x9f050000, |
||
125 | .kern_entry = 0x80002000, |
||
126 | .firmware_max_length= 0x00F60000, |
||
127 | }, |
||
128 | .sign = true, |
||
129 | }, |
||
130 | { |
||
131 | .name = "", |
||
132 | }, |
||
133 | }; |
||
134 | |||
135 | typedef struct part_data { |
||
136 | char partition_name[64]; |
||
137 | int partition_index; |
||
138 | u_int32_t partition_baseaddr; |
||
139 | u_int32_t partition_startaddr; |
||
140 | u_int32_t partition_memaddr; |
||
141 | u_int32_t partition_entryaddr; |
||
142 | u_int32_t partition_length; |
||
143 | |||
144 | char filename[PATH_MAX]; |
||
145 | struct stat stats; |
||
146 | } part_data_t; |
||
147 | |||
148 | #define MAX_SECTIONS 8 |
||
149 | #define DEFAULT_OUTPUT_FILE "firmware-image.bin" |
||
150 | #define DEFAULT_VERSION "UNKNOWN" |
||
151 | |||
152 | #define OPTIONS "B:hv:m:o:r:k:" |
||
153 | |||
154 | typedef struct image_info { |
||
155 | char magic[16]; |
||
156 | char version[256]; |
||
157 | char outputfile[PATH_MAX]; |
||
158 | u_int32_t part_count; |
||
159 | part_data_t parts[MAX_SECTIONS]; |
||
160 | struct fw_info* fwinfo; |
||
161 | } image_info_t; |
||
162 | |||
163 | static struct fw_info* get_fwinfo(char* board_name) { |
||
164 | struct fw_info *fwinfo = fw_info; |
||
165 | while(strlen(fwinfo->name)) { |
||
166 | if(strcmp(fwinfo->name, board_name) == 0) { |
||
167 | return fwinfo; |
||
168 | } |
||
169 | fwinfo++; |
||
170 | } |
||
171 | return NULL; |
||
172 | } |
||
173 | |||
174 | static void write_header(void* mem, const char *magic, const char* version) |
||
175 | { |
||
176 | header_t* header = mem; |
||
177 | memset(header, 0, sizeof(header_t)); |
||
178 | |||
179 | memcpy(header->magic, magic, MAGIC_LENGTH); |
||
180 | strncpy(header->version, version, sizeof(header->version)); |
||
181 | header->crc = htonl(crc32(0L, (unsigned char *)header, |
||
182 | sizeof(header_t) - 2 * sizeof(u_int32_t))); |
||
183 | header->pad = 0L; |
||
184 | } |
||
185 | |||
186 | |||
187 | static void write_signature(void* mem, u_int32_t sig_offset) |
||
188 | { |
||
189 | /* write signature */ |
||
190 | signature_t* sign = (signature_t*)(mem + sig_offset); |
||
191 | memset(sign, 0, sizeof(signature_t)); |
||
192 | |||
193 | memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH); |
||
194 | sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); |
||
195 | sign->pad = 0L; |
||
196 | } |
||
197 | |||
198 | static void write_signature_rsa(void* mem, u_int32_t sig_offset) |
||
199 | { |
||
200 | /* write signature */ |
||
201 | signature_rsa_t* sign = (signature_rsa_t*)(mem + sig_offset); |
||
202 | memset(sign, 0, sizeof(signature_rsa_t)); |
||
203 | |||
204 | memcpy(sign->magic, MAGIC_ENDS, MAGIC_LENGTH); |
||
205 | // sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); |
||
206 | sign->pad = 0L; |
||
207 | } |
||
208 | |||
209 | static int write_part(void* mem, part_data_t* d) |
||
210 | { |
||
211 | char* addr; |
||
212 | int fd; |
||
213 | part_t* p = mem; |
||
214 | part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size; |
||
215 | |||
216 | fd = open(d->filename, O_RDONLY); |
||
217 | if (fd < 0) |
||
218 | { |
||
219 | ERROR("Failed opening file '%s'\n", d->filename); |
||
220 | return -1; |
||
221 | } |
||
222 | |||
223 | if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) |
||
224 | { |
||
225 | ERROR("Failed mmaping memory for file '%s'\n", d->filename); |
||
226 | close(fd); |
||
227 | return -2; |
||
228 | } |
||
229 | |||
230 | memcpy(mem + sizeof(part_t), addr, d->stats.st_size); |
||
231 | munmap(addr, d->stats.st_size); |
||
232 | |||
233 | memset(p->name, 0, sizeof(p->name)); |
||
234 | strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH); |
||
235 | strncpy(p->name, d->partition_name, sizeof(p->name)); |
||
236 | p->index = htonl(d->partition_index); |
||
237 | p->data_size = htonl(d->stats.st_size); |
||
238 | p->part_size = htonl(d->partition_length); |
||
239 | p->baseaddr = htonl(d->partition_baseaddr); |
||
240 | p->memaddr = htonl(d->partition_memaddr); |
||
241 | p->entryaddr = htonl(d->partition_entryaddr); |
||
242 | |||
243 | crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t))); |
||
244 | crc->pad = 0L; |
||
245 | |||
246 | return 0; |
||
247 | } |
||
248 | |||
249 | static void usage(const char* progname) |
||
250 | { |
||
251 | INFO("Version %s\n" |
||
252 | "Usage: %s [options]\n" |
||
253 | "\t-v <version string>\t - firmware version information, default: %s\n" |
||
254 | "\t-o <output file>\t - firmware output file, default: %s\n" |
||
255 | "\t-m <magic>\t - firmware magic, default: %s\n" |
||
256 | "\t-k <kernel file>\t\t - kernel file\n" |
||
257 | "\t-r <rootfs file>\t\t - rootfs file\n" |
||
258 | "\t-B <board name>\t\t - choose firmware layout for specified board (XS2, XS5, RS, XM)\n" |
||
259 | "\t-h\t\t\t - this help\n", VERSION, |
||
260 | progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE, MAGIC_HEADER); |
||
261 | } |
||
262 | |||
263 | static void print_image_info(const image_info_t* im) |
||
264 | { |
||
265 | int i = 0; |
||
266 | INFO("Firmware version: '%s'\n" |
||
267 | "Output file: '%s'\n" |
||
268 | "Part count: %u\n", |
||
269 | im->version, im->outputfile, |
||
270 | im->part_count); |
||
271 | |||
272 | for (i = 0; i < im->part_count; ++i) |
||
273 | { |
||
274 | const part_data_t* d = &im->parts[i]; |
||
275 | INFO(" %10s: %8ld bytes (free: %8ld)\n", |
||
276 | d->partition_name, |
||
277 | d->stats.st_size, |
||
278 | d->partition_length - d->stats.st_size); |
||
279 | } |
||
280 | } |
||
281 | |||
282 | |||
283 | |||
284 | static u_int32_t filelength(const char* file) |
||
285 | { |
||
286 | FILE *p; |
||
287 | int ret = -1; |
||
288 | |||
289 | if ( (p = fopen(file, "rb") ) == NULL) return (-1); |
||
290 | |||
291 | fseek(p, 0, SEEK_END); |
||
292 | ret = ftell(p); |
||
293 | |||
294 | fclose (p); |
||
295 | |||
296 | return (ret); |
||
297 | } |
||
298 | |||
299 | static int create_image_layout(const char* kernelfile, const char* rootfsfile, char* board_name, image_info_t* im) |
||
300 | { |
||
301 | part_data_t* kernel = &im->parts[0]; |
||
302 | part_data_t* rootfs = &im->parts[1]; |
||
303 | |||
304 | fw_layout_t* p = &im->fwinfo->fw_layout; |
||
305 | |||
306 | printf("board = %s\n", im->fwinfo->name); |
||
307 | strcpy(kernel->partition_name, "kernel"); |
||
308 | kernel->partition_index = 1; |
||
309 | kernel->partition_baseaddr = p->kern_start; |
||
310 | if ( (kernel->partition_length = filelength(kernelfile)) == (u_int32_t)-1) return (-1); |
||
311 | kernel->partition_memaddr = p->kern_entry; |
||
312 | kernel->partition_entryaddr = p->kern_entry; |
||
313 | strncpy(kernel->filename, kernelfile, sizeof(kernel->filename)); |
||
314 | |||
315 | if (filelength(rootfsfile) + kernel->partition_length > p->firmware_max_length) |
||
316 | return (-2); |
||
317 | |||
318 | strcpy(rootfs->partition_name, "rootfs"); |
||
319 | rootfs->partition_index = 2; |
||
320 | rootfs->partition_baseaddr = kernel->partition_baseaddr + kernel->partition_length; |
||
321 | rootfs->partition_length = p->firmware_max_length - kernel->partition_length; |
||
322 | rootfs->partition_memaddr = 0x00000000; |
||
323 | rootfs->partition_entryaddr = 0x00000000; |
||
324 | strncpy(rootfs->filename, rootfsfile, sizeof(rootfs->filename)); |
||
325 | |||
326 | printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr); |
||
327 | printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr); |
||
328 | im->part_count = 2; |
||
329 | |||
330 | return 0; |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * Checks the availability and validity of all image components. |
||
335 | * Fills in stats member of the part_data structure. |
||
336 | */ |
||
337 | static int validate_image_layout(image_info_t* im) |
||
338 | { |
||
339 | int i; |
||
340 | |||
341 | if (im->part_count == 0 || im->part_count > MAX_SECTIONS) |
||
342 | { |
||
343 | ERROR("Invalid part count '%d'\n", im->part_count); |
||
344 | return -1; |
||
345 | } |
||
346 | |||
347 | for (i = 0; i < im->part_count; ++i) |
||
348 | { |
||
349 | part_data_t* d = &im->parts[i]; |
||
350 | int len = strlen(d->partition_name); |
||
351 | if (len == 0 || len > 16) |
||
352 | { |
||
353 | ERROR("Invalid partition name '%s' of the part %d\n", |
||
354 | d->partition_name, i); |
||
355 | return -1; |
||
356 | } |
||
357 | if (stat(d->filename, &d->stats) < 0) |
||
358 | { |
||
359 | ERROR("Couldn't stat file '%s' from part '%s'\n", |
||
360 | d->filename, d->partition_name); |
||
361 | return -2; |
||
362 | } |
||
363 | if (d->stats.st_size == 0) |
||
364 | { |
||
365 | ERROR("File '%s' from part '%s' is empty!\n", |
||
366 | d->filename, d->partition_name); |
||
367 | return -3; |
||
368 | } |
||
369 | if (d->stats.st_size > d->partition_length) { |
||
370 | ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %lu bytes)\n", |
||
371 | d->filename, i, d->partition_length, |
||
372 | d->stats.st_size - d->partition_length); |
||
373 | return -4; |
||
374 | } |
||
375 | } |
||
376 | |||
377 | return 0; |
||
378 | } |
||
379 | |||
380 | static int build_image(image_info_t* im) |
||
381 | { |
||
382 | char* mem; |
||
383 | char* ptr; |
||
384 | u_int32_t mem_size; |
||
385 | FILE* f; |
||
386 | int i; |
||
387 | |||
388 | // build in-memory buffer |
||
389 | mem_size = sizeof(header_t); |
||
390 | if(im->fwinfo->sign) { |
||
391 | mem_size += sizeof(signature_rsa_t); |
||
392 | } else { |
||
393 | mem_size += sizeof(signature_t); |
||
394 | } |
||
395 | for (i = 0; i < im->part_count; ++i) |
||
396 | { |
||
397 | part_data_t* d = &im->parts[i]; |
||
398 | mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); |
||
399 | } |
||
400 | |||
401 | mem = (char*)calloc(mem_size, 1); |
||
402 | if (mem == NULL) |
||
403 | { |
||
404 | ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size); |
||
405 | return -1; |
||
406 | } |
||
407 | |||
408 | // write header |
||
409 | write_header(mem, im->magic, im->version); |
||
410 | ptr = mem + sizeof(header_t); |
||
411 | // write all parts |
||
412 | for (i = 0; i < im->part_count; ++i) |
||
413 | { |
||
414 | part_data_t* d = &im->parts[i]; |
||
415 | int rc; |
||
416 | if ((rc = write_part(ptr, d)) != 0) |
||
417 | { |
||
418 | ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name); |
||
419 | } |
||
420 | ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); |
||
421 | } |
||
422 | // write signature |
||
423 | if(im->fwinfo->sign) { |
||
424 | write_signature_rsa(mem, mem_size - sizeof(signature_rsa_t)); |
||
425 | } else { |
||
426 | write_signature(mem, mem_size - sizeof(signature_t)); |
||
427 | } |
||
428 | |||
429 | // write in-memory buffer into file |
||
430 | if ((f = fopen(im->outputfile, "w")) == NULL) |
||
431 | { |
||
432 | ERROR("Can not create output file: '%s'\n", im->outputfile); |
||
433 | return -10; |
||
434 | } |
||
435 | |||
436 | if (fwrite(mem, mem_size, 1, f) != 1) |
||
437 | { |
||
438 | ERROR("Could not write %d bytes into file: '%s'\n", |
||
439 | mem_size, im->outputfile); |
||
440 | return -11; |
||
441 | } |
||
442 | |||
443 | free(mem); |
||
444 | fclose(f); |
||
445 | return 0; |
||
446 | } |
||
447 | |||
448 | |||
449 | int main(int argc, char* argv[]) |
||
450 | { |
||
451 | char kernelfile[PATH_MAX]; |
||
452 | char rootfsfile[PATH_MAX]; |
||
453 | char board_name[PATH_MAX]; |
||
454 | int o, rc; |
||
455 | image_info_t im; |
||
456 | struct fw_info *fwinfo; |
||
457 | |||
458 | memset(&im, 0, sizeof(im)); |
||
459 | memset(kernelfile, 0, sizeof(kernelfile)); |
||
460 | memset(rootfsfile, 0, sizeof(rootfsfile)); |
||
461 | memset(board_name, 0, sizeof(board_name)); |
||
462 | |||
463 | strcpy(im.outputfile, DEFAULT_OUTPUT_FILE); |
||
464 | strcpy(im.version, DEFAULT_VERSION); |
||
465 | strncpy(im.magic, MAGIC_HEADER, sizeof(im.magic)); |
||
466 | |||
467 | while ((o = getopt(argc, argv, OPTIONS)) != -1) |
||
468 | { |
||
469 | switch (o) { |
||
470 | case 'v': |
||
471 | if (optarg) |
||
472 | strncpy(im.version, optarg, sizeof(im.version)); |
||
473 | break; |
||
474 | case 'o': |
||
475 | if (optarg) |
||
476 | strncpy(im.outputfile, optarg, sizeof(im.outputfile)); |
||
477 | break; |
||
478 | case 'm': |
||
479 | if (optarg) |
||
480 | strncpy(im.magic, optarg, sizeof(im.magic)); |
||
481 | break; |
||
482 | case 'h': |
||
483 | usage(argv[0]); |
||
484 | return -1; |
||
485 | case 'k': |
||
486 | if (optarg) |
||
487 | strncpy(kernelfile, optarg, sizeof(kernelfile)); |
||
488 | break; |
||
489 | case 'r': |
||
490 | if (optarg) |
||
491 | strncpy(rootfsfile, optarg, sizeof(rootfsfile)); |
||
492 | break; |
||
493 | case 'B': |
||
494 | if (optarg) |
||
495 | strncpy(board_name, optarg, sizeof(board_name)); |
||
496 | break; |
||
497 | } |
||
498 | } |
||
499 | if (strlen(board_name) == 0) |
||
500 | strcpy(board_name, "XS2"); /* default to XS2 */ |
||
501 | |||
502 | if (strlen(kernelfile) == 0) |
||
503 | { |
||
504 | ERROR("Kernel file is not specified, cannot continue\n"); |
||
505 | usage(argv[0]); |
||
506 | return -2; |
||
507 | } |
||
508 | |||
509 | if (strlen(rootfsfile) == 0) |
||
510 | { |
||
511 | ERROR("Root FS file is not specified, cannot continue\n"); |
||
512 | usage(argv[0]); |
||
513 | return -2; |
||
514 | } |
||
515 | |||
516 | if ((fwinfo = get_fwinfo(board_name)) == NULL) { |
||
517 | ERROR("Invalid baord name '%s'\n", board_name); |
||
518 | usage(argv[0]); |
||
519 | return -2; |
||
520 | } |
||
521 | |||
522 | im.fwinfo = fwinfo; |
||
523 | |||
524 | if ((rc = create_image_layout(kernelfile, rootfsfile, board_name, &im)) != 0) |
||
525 | { |
||
526 | ERROR("Failed creating firmware layout description - error code: %d\n", rc); |
||
527 | return -3; |
||
528 | } |
||
529 | |||
530 | if ((rc = validate_image_layout(&im)) != 0) |
||
531 | { |
||
532 | ERROR("Failed validating firmware layout - error code: %d\n", rc); |
||
533 | return -4; |
||
534 | } |
||
535 | |||
536 | print_image_info(&im); |
||
537 | |||
538 | if ((rc = build_image(&im)) != 0) |
||
539 | { |
||
540 | ERROR("Failed building image file '%s' - error code: %d\n", im.outputfile, rc); |
||
541 | return -5; |
||
542 | } |
||
543 | |||
544 | return 0; |
||
545 | } |