OpenWrt – Blame information for rev 2
?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 | * Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> |
||
5 | * |
||
6 | * This program is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU General Public License as |
||
8 | * published by the Free Software Foundation; either version 2 of the |
||
9 | * License, or (at your option) any later version. |
||
10 | * |
||
11 | * This program is distributed in the hope that it will be useful, but |
||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
14 | * General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU General Public License |
||
17 | * along with this program; if not, write to the Free Software |
||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
19 | */ |
||
20 | |||
21 | #include <sys/types.h> |
||
22 | #include <sys/stat.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 "fw.h" |
||
34 | |||
35 | #undef VERSION |
||
36 | #define VERSION "1.2-OpenWrt.1" |
||
37 | |||
38 | #define MAX_SECTIONS 8 |
||
39 | #define DEFAULT_OUTPUT_FILE "firmware-image.bin" |
||
40 | #define DEFAULT_VERSION "UNKNOWN" |
||
41 | #define DEFAULT_FLASH_BASE (0xbfc00000) |
||
42 | |||
43 | #define FIRMWARE_MAX_LENGTH (0x390000) |
||
44 | |||
45 | typedef struct part_data { |
||
46 | char partition_name[64]; |
||
47 | int partition_index; |
||
48 | u_int32_t partition_baseaddr; |
||
49 | u_int32_t partition_offset; |
||
50 | u_int32_t partition_memaddr; |
||
51 | u_int32_t partition_entryaddr; |
||
52 | u_int32_t partition_length; |
||
53 | |||
54 | char filename[PATH_MAX]; |
||
55 | struct stat stats; |
||
56 | } part_data_t; |
||
57 | |||
58 | typedef struct image_info { |
||
59 | char version[256]; |
||
60 | char outputfile[PATH_MAX]; |
||
61 | char magic[MAGIC_LENGTH]; |
||
62 | u_int32_t flash_baseaddr; |
||
63 | u_int32_t part_count; |
||
64 | part_data_t parts[MAX_SECTIONS]; |
||
65 | } image_info_t; |
||
66 | |||
67 | static image_info_t im; |
||
68 | static int debug = 0; |
||
69 | static int zero_part_baseaddr = 0; |
||
70 | |||
71 | static void write_header(void* mem, const char* version) |
||
72 | { |
||
73 | header_t* header = mem; |
||
74 | memset(header, 0, sizeof(header_t)); |
||
75 | |||
76 | memcpy(header->magic, im.magic, MAGIC_LENGTH); |
||
77 | strncpy(header->version, version, sizeof(header->version)); |
||
78 | header->crc = htonl(crc32(0L, (unsigned char *)header, |
||
79 | sizeof(header_t) - 2 * sizeof(u_int32_t))); |
||
80 | header->pad = 0L; |
||
81 | } |
||
82 | |||
83 | static void write_signature(void* mem, u_int32_t sig_offset) |
||
84 | { |
||
85 | /* write signature */ |
||
86 | signature_t* sign = (signature_t*)(mem + sig_offset); |
||
87 | memset(sign, 0, sizeof(signature_t)); |
||
88 | |||
89 | memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH); |
||
90 | sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); |
||
91 | sign->pad = 0L; |
||
92 | } |
||
93 | |||
94 | static int write_part(void* mem, part_data_t* d) |
||
95 | { |
||
96 | char* addr; |
||
97 | int fd; |
||
98 | part_t* p = mem; |
||
99 | part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size; |
||
100 | |||
101 | fd = open(d->filename, O_RDONLY); |
||
102 | if (fd < 0) { |
||
103 | ERROR("Failed opening file '%s'\n", d->filename); |
||
104 | return -1; |
||
105 | } |
||
106 | |||
107 | if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { |
||
108 | ERROR("Failed mmaping memory for file '%s'\n", d->filename); |
||
109 | close(fd); |
||
110 | return -2; |
||
111 | } |
||
112 | |||
113 | memcpy(mem + sizeof(part_t), addr, d->stats.st_size); |
||
114 | munmap(addr, d->stats.st_size); |
||
115 | |||
116 | memset(p->name, 0, sizeof(p->name)); |
||
117 | strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH); |
||
118 | strncpy(p->name, d->partition_name, sizeof(p->name)); |
||
119 | p->index = htonl(d->partition_index); |
||
120 | p->data_size = htonl(d->stats.st_size); |
||
121 | p->part_size = htonl(d->partition_length); |
||
122 | p->baseaddr = htonl(d->partition_baseaddr); |
||
123 | p->memaddr = htonl(d->partition_memaddr); |
||
124 | p->entryaddr = htonl(d->partition_entryaddr); |
||
125 | |||
126 | crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t))); |
||
127 | crc->pad = 0L; |
||
128 | |||
129 | return 0; |
||
130 | } |
||
131 | |||
132 | static void usage(const char* progname) |
||
133 | { |
||
134 | INFO("Version %s\n" |
||
135 | "Usage: %s [options]\n" |
||
136 | "\t-v <version string>\t - firmware version information, default: %s\n" |
||
137 | "\t-m <magic>\t\t - firmware magic, default: %s\n" |
||
138 | "\t-f <flash base>\t\t - flash base address, default: 0x%08x\n" |
||
139 | "\t-o <output file>\t - firmware output file, default: %s\n" |
||
140 | "\t-p <name>:<offset>:<len>:<memaddr>:<entry>:<file>\n " |
||
141 | "\t\t\t\t - create a partition from <file>\n" |
||
142 | "\t-z\t\t\t - set partition offsets to zero\n" |
||
143 | "\t-h\t\t\t - this help\n", |
||
144 | VERSION, progname, DEFAULT_VERSION, MAGIC_HEADER, |
||
145 | DEFAULT_FLASH_BASE, DEFAULT_OUTPUT_FILE); |
||
146 | } |
||
147 | |||
148 | static void print_image_info(void) |
||
149 | { |
||
150 | int i; |
||
151 | |||
152 | INFO("Firmware version : '%s'\n" |
||
153 | "Output file : '%s'\n" |
||
154 | "Part count : %u\n", |
||
155 | im.version, im.outputfile, im.part_count); |
||
156 | |||
157 | for (i = 0; i < im.part_count; ++i) { |
||
158 | const part_data_t* d = &im.parts[i]; |
||
159 | INFO(" %10s: %08x %08x %08x %08x %8ld bytes (free: %8ld)\n", |
||
160 | d->partition_name, |
||
161 | d->partition_baseaddr, |
||
162 | d->partition_length, |
||
163 | d->partition_entryaddr, |
||
164 | d->partition_memaddr, |
||
165 | d->stats.st_size, |
||
166 | d->partition_length - d->stats.st_size); |
||
167 | } |
||
168 | } |
||
169 | |||
170 | static int filelength(const char* file) |
||
171 | { |
||
172 | FILE *p; |
||
173 | int ret = -1; |
||
174 | |||
175 | if ( (p = fopen(file, "rb") ) == NULL) return (-1); |
||
176 | |||
177 | fseek(p, 0, SEEK_END); |
||
178 | ret = ftell(p); |
||
179 | |||
180 | fclose (p); |
||
181 | |||
182 | return (ret); |
||
183 | } |
||
184 | |||
185 | int str2u32(char *arg, u_int32_t *val) |
||
186 | { |
||
187 | char *err = NULL; |
||
188 | uint32_t t; |
||
189 | |||
190 | errno = 0; |
||
191 | t = strtoul(arg, &err, 0); |
||
192 | if (errno || (err == arg) || ((err != NULL) && *err)) { |
||
193 | return -1; |
||
194 | } |
||
195 | |||
196 | *val = t; |
||
197 | return 0; |
||
198 | } |
||
199 | |||
200 | #ifndef STRINGIFY |
||
201 | #define STRINGIFY2(X) #X |
||
202 | #define STRINGIFY(X) STRINGIFY2(X) |
||
203 | #endif |
||
204 | static int image_layout_add_partition(const char *part_desc) |
||
205 | { |
||
206 | part_data_t *d; |
||
207 | char memaddr[16]; |
||
208 | char entryaddr[16]; |
||
209 | char offset[16]; |
||
210 | char length[16]; |
||
211 | int t; |
||
212 | |||
213 | if (im.part_count >= MAX_SECTIONS) { |
||
214 | ERROR("Too many partitions specified\n"); |
||
215 | return (-1); |
||
216 | } |
||
217 | |||
218 | d = &im.parts[im.part_count]; |
||
219 | t = sscanf(part_desc, "%15[-0-9a-zA-Z]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%"STRINGIFY(PATH_MAX)"s", |
||
220 | d->partition_name, |
||
221 | offset, |
||
222 | length, |
||
223 | memaddr, |
||
224 | entryaddr, |
||
225 | d->filename); |
||
226 | |||
227 | if (t != 6) { |
||
228 | ERROR("Bad partition parameter %d, '%s'\n", t, part_desc); |
||
229 | return (-1); |
||
230 | } |
||
231 | |||
232 | if (strlen(d->partition_name) == 0) { |
||
233 | ERROR("No partition name specified in '%s'\n", part_desc); |
||
234 | return (-1); |
||
235 | } |
||
236 | |||
237 | if (str2u32(offset, &d->partition_offset)) { |
||
238 | ERROR("Bad offset value '%s'\n", offset); |
||
239 | return (-1); |
||
240 | } |
||
241 | |||
242 | if (str2u32(length, &d->partition_length)) { |
||
243 | ERROR("Bad length value '%s'\n", length); |
||
244 | return (-1); |
||
245 | } |
||
246 | |||
247 | if (d->partition_length == 0) { |
||
248 | int flen; |
||
249 | flen = filelength(d->filename); |
||
250 | if (flen < 0) { |
||
251 | ERROR("Unable to determine size of '%s'\n", |
||
252 | d->filename); |
||
253 | return (-1); |
||
254 | } |
||
255 | d->partition_length = flen; |
||
256 | } |
||
257 | |||
258 | if (str2u32(memaddr, &d->partition_memaddr)) { |
||
259 | ERROR("Bad memaddr vaule '%s'\n", memaddr); |
||
260 | return (-1); |
||
261 | } |
||
262 | |||
263 | if (str2u32(entryaddr, &d->partition_entryaddr)) { |
||
264 | ERROR("Bad entry address value '%s'\n", entryaddr); |
||
265 | return (-1); |
||
266 | } |
||
267 | |||
268 | im.part_count++; |
||
269 | d->partition_index = im.part_count; |
||
270 | |||
271 | return 0; |
||
272 | } |
||
273 | |||
274 | static int image_layout_verify(void) |
||
275 | { |
||
276 | u_int32_t offset; |
||
277 | int i; |
||
278 | |||
279 | if (im.part_count == 0) { |
||
280 | ERROR("No partitions specified\n"); |
||
281 | return -1; |
||
282 | } |
||
283 | |||
284 | offset = im.parts[0].partition_offset; |
||
285 | for (i = 0; i < im.part_count; i++) |
||
286 | { |
||
287 | part_data_t* d = &im.parts[i]; |
||
288 | |||
289 | if (stat(d->filename, &d->stats) < 0) { |
||
290 | ERROR("Couldn't stat file '%s' from part '%s'\n", |
||
291 | d->filename, d->partition_name); |
||
292 | return -2; |
||
293 | } |
||
294 | |||
295 | if (d->stats.st_size == 0) { |
||
296 | ERROR("File '%s' from part '%s' is empty!\n", |
||
297 | d->filename, d->partition_name); |
||
298 | return -3; |
||
299 | } |
||
300 | |||
301 | if (d->stats.st_size > d->partition_length) { |
||
302 | ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %lu bytes)\n", |
||
303 | d->filename, i, d->partition_length, |
||
304 | d->stats.st_size - d->partition_length); |
||
305 | return -4; |
||
306 | } |
||
307 | |||
308 | if (d->partition_offset < offset) |
||
309 | d->partition_offset = offset; |
||
310 | |||
311 | if (zero_part_baseaddr) { |
||
312 | d->partition_baseaddr = 0; |
||
313 | } else { |
||
314 | d->partition_baseaddr = |
||
315 | im.flash_baseaddr + d->partition_offset; |
||
316 | } |
||
317 | offset += d->partition_length; |
||
318 | } |
||
319 | |||
320 | return 0; |
||
321 | } |
||
322 | |||
323 | static int build_image(void) |
||
324 | { |
||
325 | char* mem; |
||
326 | char* ptr; |
||
327 | u_int32_t mem_size; |
||
328 | FILE* f; |
||
329 | int i; |
||
330 | |||
331 | /* build in-memory buffer */ |
||
332 | mem_size = sizeof(header_t) + sizeof(signature_t); |
||
333 | for (i = 0; i < im.part_count; ++i) { |
||
334 | part_data_t* d = &im.parts[i]; |
||
335 | mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); |
||
336 | } |
||
337 | |||
338 | mem = (char*)calloc(mem_size, 1); |
||
339 | if (mem == NULL) { |
||
340 | ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size); |
||
341 | return -1; |
||
342 | } |
||
343 | |||
344 | /* write header */ |
||
345 | write_header(mem, im.version); |
||
346 | ptr = mem + sizeof(header_t); |
||
347 | |||
348 | /* write all parts */ |
||
349 | for (i = 0; i < im.part_count; ++i) { |
||
350 | part_data_t* d = &im.parts[i]; |
||
351 | int rc; |
||
352 | if ((rc = write_part(ptr, d)) != 0) { |
||
353 | ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name); |
||
354 | return -1; |
||
355 | } |
||
356 | ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); |
||
357 | } |
||
358 | |||
359 | |||
360 | /* write signature */ |
||
361 | write_signature(mem, mem_size - sizeof(signature_t)); |
||
362 | |||
363 | /* write in-memory buffer into file */ |
||
364 | if ((f = fopen(im.outputfile, "w")) == NULL) { |
||
365 | ERROR("Can not create output file: '%s'\n", im.outputfile); |
||
366 | return -10; |
||
367 | } |
||
368 | |||
369 | if (fwrite(mem, mem_size, 1, f) != 1) { |
||
370 | ERROR("Could not write %d bytes into file: '%s'\n", |
||
371 | mem_size, im.outputfile); |
||
372 | return -11; |
||
373 | } |
||
374 | |||
375 | free(mem); |
||
376 | fclose(f); |
||
377 | return 0; |
||
378 | } |
||
379 | |||
380 | int main(int argc, char* argv[]) |
||
381 | { |
||
382 | int o, rc; |
||
383 | |||
384 | memset(&im, 0, sizeof(im)); |
||
385 | |||
386 | strcpy(im.outputfile, DEFAULT_OUTPUT_FILE); |
||
387 | strcpy(im.version, DEFAULT_VERSION); |
||
388 | memcpy(im.magic, MAGIC_HEADER, MAGIC_LENGTH); |
||
389 | im.flash_baseaddr = DEFAULT_FLASH_BASE; |
||
390 | |||
391 | while ((o = getopt(argc, argv, "f:hm:o:p:v:z")) != -1) |
||
392 | { |
||
393 | switch (o) { |
||
394 | case 'f': |
||
395 | if (optarg) |
||
396 | if (str2u32(optarg, &im.flash_baseaddr)) { |
||
397 | ERROR("Invalid flash start address %s\n", optarg); |
||
398 | return -1; |
||
399 | } |
||
400 | break; |
||
401 | case 'h': |
||
402 | usage(argv[0]); |
||
403 | return -1; |
||
404 | case 'm': |
||
405 | if (optarg) { |
||
406 | if (strlen(optarg) != MAGIC_LENGTH) { |
||
407 | ERROR("Invalid magic %s\n", optarg); |
||
408 | return -1; |
||
409 | } |
||
410 | |||
411 | memcpy(im.magic, optarg, MAGIC_LENGTH); |
||
412 | } |
||
413 | break; |
||
414 | case 'o': |
||
415 | if (optarg) |
||
416 | strncpy(im.outputfile, optarg, sizeof(im.outputfile)); |
||
417 | break; |
||
418 | case 'p': |
||
419 | if (optarg) { |
||
420 | if (image_layout_add_partition(optarg)) |
||
421 | return -1; |
||
422 | } |
||
423 | break; |
||
424 | case 'v': |
||
425 | if (optarg) |
||
426 | strncpy(im.version, optarg, sizeof(im.version)); |
||
427 | break; |
||
428 | case 'z': |
||
429 | zero_part_baseaddr = 1; |
||
430 | break; |
||
431 | } |
||
432 | } |
||
433 | |||
434 | rc = image_layout_verify(); |
||
435 | if (rc) { |
||
436 | ERROR("Failed validating firmware layout - error code: %d\n", |
||
437 | rc); |
||
438 | return -4; |
||
439 | } |
||
440 | |||
441 | print_image_info(); |
||
442 | |||
443 | rc = build_image(); |
||
444 | if (rc) { |
||
445 | ERROR("Failed building image file '%s' - error code: %d\n", |
||
446 | im.outputfile, rc); |
||
447 | return -5; |
||
448 | } |
||
449 | |||
450 | return 0; |
||
451 | } |