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