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