diff options
Diffstat (limited to 'utils/hwpatcher/hwpatcher.c')
-rw-r--r-- | utils/hwpatcher/hwpatcher.c | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/utils/hwpatcher/hwpatcher.c b/utils/hwpatcher/hwpatcher.c new file mode 100644 index 0000000000..60e142bbc1 --- /dev/null +++ b/utils/hwpatcher/hwpatcher.c | |||
@@ -0,0 +1,1123 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2013 Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #define _ISOC99_SOURCE /* snprintf() */ | ||
23 | #define _POSIX_C_SOURCE 200809L /* for strdup */ | ||
24 | #include <stdio.h> | ||
25 | #include <errno.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <string.h> | ||
28 | #include <ctype.h> | ||
29 | #include <time.h> | ||
30 | #include <stdarg.h> | ||
31 | #include <strings.h> | ||
32 | #include <getopt.h> | ||
33 | #include <lua.h> | ||
34 | #include <lualib.h> | ||
35 | #include <lauxlib.h> | ||
36 | #include <readline/readline.h> | ||
37 | #include <readline/history.h> | ||
38 | |||
39 | #if LUA_VERSION_NUM < 502 | ||
40 | #warning You need at least lua 5.2 | ||
41 | #endif | ||
42 | |||
43 | #include "crypto.h" | ||
44 | #include "elf.h" | ||
45 | #include "sb.h" | ||
46 | #include "sb1.h" | ||
47 | #include "misc.h" | ||
48 | #include "md5.h" | ||
49 | |||
50 | lua_State *g_lua; | ||
51 | bool g_exit = false; | ||
52 | |||
53 | /** | ||
54 | * FW object library | ||
55 | */ | ||
56 | |||
57 | enum fw_type_t | ||
58 | { | ||
59 | FW_UNK, FW_ELF, FW_SB1, FW_SB2, FW_BIN, FW_EDOC | ||
60 | }; | ||
61 | |||
62 | struct bin_file_t | ||
63 | { | ||
64 | size_t size; | ||
65 | void *data; | ||
66 | }; | ||
67 | |||
68 | struct edoc_section_t | ||
69 | { | ||
70 | uint32_t addr; | ||
71 | size_t size; | ||
72 | void *data; | ||
73 | }; | ||
74 | |||
75 | struct edoc_file_t | ||
76 | { | ||
77 | int nr_sections; | ||
78 | struct edoc_section_t *sections; | ||
79 | }; | ||
80 | |||
81 | struct edoc_header_t | ||
82 | { | ||
83 | char magic[4]; | ||
84 | uint32_t total_size; | ||
85 | uint32_t zero; | ||
86 | } __attribute__((packed)); | ||
87 | |||
88 | struct edoc_section_header_t | ||
89 | { | ||
90 | uint32_t addr; | ||
91 | uint32_t size; | ||
92 | uint32_t checksum; | ||
93 | } __attribute__((packed)); | ||
94 | |||
95 | uint32_t edoc_checksum(void *buffer, size_t size) | ||
96 | { | ||
97 | uint32_t c = 0; | ||
98 | uint32_t *p = buffer; | ||
99 | while(size >= 4) | ||
100 | { | ||
101 | c += *p + (*p >> 16); | ||
102 | p++; | ||
103 | size -= 4; | ||
104 | } | ||
105 | if(size != 0) | ||
106 | printf("[Checksum section size is not a multiple of 4 bytes !]\n"); | ||
107 | return c & 0xffff; | ||
108 | } | ||
109 | |||
110 | #define FWOBJ_MAGIC ('F' | 'W' << 8 | 'O' << 16 | 'B' << 24) | ||
111 | |||
112 | struct fw_object_t | ||
113 | { | ||
114 | uint32_t magic; | ||
115 | enum fw_type_t type; | ||
116 | union | ||
117 | { | ||
118 | struct sb_file_t *sb; | ||
119 | struct sb1_file_t *sb1; | ||
120 | struct elf_params_t *elf; | ||
121 | struct bin_file_t *bin; | ||
122 | struct edoc_file_t *edoc; | ||
123 | }u; | ||
124 | }; | ||
125 | |||
126 | typedef struct fw_addr_t | ||
127 | { | ||
128 | uint32_t addr; | ||
129 | const char *section; | ||
130 | }fw_addr_t; | ||
131 | |||
132 | #define INVALID_FW_ADDR ((struct fw_addr_t){.addr = (uint32_t)-1, .section = ""}) | ||
133 | #define IS_VALID_FW_ADDR(x) !(x.addr == (uint32_t)-1 && x.section && strlen(x.section) == 0) | ||
134 | |||
135 | typedef struct fw_sym_addr_t | ||
136 | { | ||
137 | const char *name; | ||
138 | const char *section; | ||
139 | }fw_sym_addr_t; | ||
140 | |||
141 | struct fw_section_info_t | ||
142 | { | ||
143 | uint32_t addr; | ||
144 | uint32_t size; | ||
145 | }; | ||
146 | |||
147 | static inline struct fw_addr_t make_addr(uint32_t addr, const char *section) | ||
148 | { | ||
149 | return (struct fw_addr_t){.addr = addr, .section = section}; | ||
150 | } | ||
151 | |||
152 | static enum fw_type_t fw_guess(const char *filename) | ||
153 | { | ||
154 | enum sb_version_guess_t ver = guess_sb_version(filename); | ||
155 | if(ver == SB_VERSION_ERR) | ||
156 | { | ||
157 | printf("Cannot open/read SB file: %m\n"); | ||
158 | return FW_UNK; | ||
159 | } | ||
160 | if(ver == SB_VERSION_1) return FW_SB1; | ||
161 | if(ver == SB_VERSION_2) return FW_SB2; | ||
162 | FILE *fd = fopen(filename, "rb"); | ||
163 | if(fd == NULL) | ||
164 | { | ||
165 | printf("Cannot open '%s' for reading: %m\n", filename); | ||
166 | return FW_UNK; | ||
167 | } | ||
168 | uint8_t sig[4]; | ||
169 | if(fread(sig, 4, 1, fd) == 1) | ||
170 | { | ||
171 | if(sig[0] == 'E' && sig[1] == 'D' && sig[2] == 'O' && sig[3] == 'C') | ||
172 | return FW_EDOC; | ||
173 | } | ||
174 | bool is_elf = elf_guess(elf_std_read, fd); | ||
175 | fclose(fd); | ||
176 | return is_elf ? FW_ELF : FW_BIN; | ||
177 | } | ||
178 | |||
179 | static const char *fw_type_name(enum fw_type_t t) | ||
180 | { | ||
181 | switch(t) | ||
182 | { | ||
183 | case FW_SB1: return "SB1"; | ||
184 | case FW_SB2: return "SB2"; | ||
185 | case FW_ELF: return "ELF"; | ||
186 | case FW_BIN: return "binary"; | ||
187 | case FW_EDOC: return "EDOC"; | ||
188 | default: return "<unk>"; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static struct fw_object_t *fw_load(const char *filename, enum fw_type_t force) | ||
193 | { | ||
194 | if(force == FW_UNK) | ||
195 | force = fw_guess(filename); | ||
196 | if(force == FW_UNK) | ||
197 | { | ||
198 | printf("Cannot guess type of file: %m. This probably indicates the file cannot be read.\n"); | ||
199 | return NULL; | ||
200 | } | ||
201 | |||
202 | struct fw_object_t *obj = malloc(sizeof(struct fw_object_t)); | ||
203 | memset(obj, 0, sizeof(struct fw_object_t)); | ||
204 | obj->magic = FWOBJ_MAGIC; | ||
205 | |||
206 | printf("Loading '%s' as %s file...\n", filename, fw_type_name(force)); | ||
207 | color(OFF); | ||
208 | if(force == FW_SB2) | ||
209 | { | ||
210 | enum sb_error_t err; | ||
211 | obj->type = FW_SB2; | ||
212 | obj->u.sb = sb_read_file(filename, false, NULL, generic_std_printf, &err); | ||
213 | if(obj->u.sb == NULL) | ||
214 | { | ||
215 | printf("SB read failed: %d\n", err); | ||
216 | goto Lerr; | ||
217 | } | ||
218 | return obj; | ||
219 | } | ||
220 | else if(force == FW_SB1) | ||
221 | { | ||
222 | enum sb1_error_t err; | ||
223 | obj->type = FW_SB1; | ||
224 | obj->u.sb1 = sb1_read_file(filename, NULL, generic_std_printf, &err); | ||
225 | if(obj->u.sb1 == NULL) | ||
226 | { | ||
227 | printf("SB1 read failed: %d\n", err); | ||
228 | goto Lerr; | ||
229 | } | ||
230 | return obj; | ||
231 | } | ||
232 | else if(force == FW_ELF) | ||
233 | { | ||
234 | FILE *fd = fopen(filename, "rb"); | ||
235 | if(fd == NULL) | ||
236 | { | ||
237 | printf("cannot open '%s' for reading: %m\n", filename); | ||
238 | goto Lerr; | ||
239 | } | ||
240 | obj->type = FW_ELF; | ||
241 | obj->u.elf = malloc(sizeof(struct elf_params_t)); | ||
242 | elf_init(obj->u.elf); | ||
243 | bool loaded = elf_read_file(obj->u.elf, elf_std_read, generic_std_printf, fd); | ||
244 | fclose(fd); | ||
245 | if(!loaded) | ||
246 | { | ||
247 | printf("error loading elf file '%s'\n", filename); | ||
248 | free(obj->u.elf); | ||
249 | goto Lerr; | ||
250 | } | ||
251 | return obj; | ||
252 | } | ||
253 | else if(force == FW_EDOC) | ||
254 | { | ||
255 | FILE *fd = fopen(filename, "rb"); | ||
256 | if(fd == NULL) | ||
257 | { | ||
258 | printf("cannot open '%s' for reading: %m\n", filename); | ||
259 | goto Lerr; | ||
260 | } | ||
261 | struct edoc_header_t hdr; | ||
262 | if(fread(&hdr, sizeof(hdr), 1, fd) != 1) | ||
263 | { | ||
264 | printf("cannot read EDOC header: %m\n"); | ||
265 | goto Lerr; | ||
266 | } | ||
267 | if(strncmp(hdr.magic, "EDOC", 4) != 0) | ||
268 | { | ||
269 | printf("EDOC signature mismatch\n"); | ||
270 | goto Lerr; | ||
271 | } | ||
272 | struct edoc_file_t *file = xmalloc(sizeof(struct edoc_file_t)); | ||
273 | memset(file, 0, sizeof(struct edoc_file_t)); | ||
274 | for(size_t pos = sizeof(hdr); pos < hdr.total_size + 8;) | ||
275 | { | ||
276 | struct edoc_section_header_t shdr; | ||
277 | if(fread(&shdr, sizeof(shdr), 1, fd) != 1) | ||
278 | { | ||
279 | printf("cannot read EDOC section header: %m\n"); | ||
280 | goto Lerr; | ||
281 | } | ||
282 | file->sections = realloc(file->sections, (file->nr_sections + 1) * | ||
283 | sizeof(struct edoc_section_t)); | ||
284 | file->sections[file->nr_sections].addr = shdr.addr; | ||
285 | file->sections[file->nr_sections].size = shdr.size; | ||
286 | file->sections[file->nr_sections].data = xmalloc(shdr.size); | ||
287 | if(fread(file->sections[file->nr_sections].data, shdr.size, 1, fd) != 1) | ||
288 | { | ||
289 | printf("cannot read EDOC section: %m\n"); | ||
290 | goto Lerr; | ||
291 | } | ||
292 | if(edoc_checksum(file->sections[file->nr_sections].data, shdr.size) != shdr.checksum) | ||
293 | { | ||
294 | printf("EDOC section checksum mismatch\n"); | ||
295 | goto Lerr; | ||
296 | } | ||
297 | file->nr_sections++; | ||
298 | pos += sizeof(shdr) + shdr.size; | ||
299 | } | ||
300 | fclose(fd); | ||
301 | obj->type = FW_EDOC; | ||
302 | obj->u.edoc = file; | ||
303 | return obj; | ||
304 | } | ||
305 | else | ||
306 | { | ||
307 | FILE *fd = fopen(filename, "rb"); | ||
308 | if(fd == NULL) | ||
309 | { | ||
310 | printf("cannot open '%s' for reading: %m\n", filename); | ||
311 | goto Lerr; | ||
312 | } | ||
313 | obj->u.bin = malloc(sizeof(struct bin_file_t)); | ||
314 | obj->type = FW_BIN; | ||
315 | fseek(fd, 0, SEEK_END); | ||
316 | obj->u.bin->size = ftell(fd); | ||
317 | fseek(fd, 0, SEEK_SET); | ||
318 | obj->u.bin->data = xmalloc(obj->u.bin->size); | ||
319 | if(fread(obj->u.bin->data, obj->u.bin->size, 1, fd) != 1) | ||
320 | { | ||
321 | printf("cannot read '%s': %m\n", filename); | ||
322 | free(obj->u.bin->data); | ||
323 | free(obj->u.bin); | ||
324 | goto Lerr; | ||
325 | } | ||
326 | fclose(fd); | ||
327 | return obj; | ||
328 | } | ||
329 | |||
330 | Lerr: | ||
331 | free(obj); | ||
332 | return NULL; | ||
333 | } | ||
334 | |||
335 | static bool fw_save(struct fw_object_t *obj, const char *filename) | ||
336 | { | ||
337 | if(obj->type == FW_ELF) | ||
338 | { | ||
339 | FILE *fd = fopen(filename, "wb"); | ||
340 | if(fd == NULL) | ||
341 | { | ||
342 | printf("Cannot open '%s' for writing: %m\n", filename); | ||
343 | return false; | ||
344 | } | ||
345 | elf_write_file(obj->u.elf, elf_std_write, generic_std_printf, fd); | ||
346 | fclose(fd); | ||
347 | return true; | ||
348 | } | ||
349 | else if(obj->type == FW_SB2) | ||
350 | { | ||
351 | /* sb_read_file will fill real key and IV but we don't want to override | ||
352 | * them when looping back otherwise the output will be inconsistent and | ||
353 | * garbage */ | ||
354 | obj->u.sb->override_real_key = false; | ||
355 | obj->u.sb->override_crypto_iv = false; | ||
356 | enum sb_error_t err = sb_write_file(obj->u.sb, filename, NULL, generic_std_printf); | ||
357 | if(err != SB_SUCCESS) | ||
358 | { | ||
359 | printf("Cannot write '%s': %d\n", filename, err); | ||
360 | return false; | ||
361 | } | ||
362 | return true; | ||
363 | } | ||
364 | else if(obj->type == FW_BIN) | ||
365 | { | ||
366 | FILE *fd = fopen(filename, "wb"); | ||
367 | if(fd == NULL) | ||
368 | { | ||
369 | printf("Cannot open '%s' for writing: %m\n", filename); | ||
370 | return false; | ||
371 | } | ||
372 | fwrite(obj->u.bin->data, 1, obj->u.bin->size, fd); | ||
373 | fclose(fd); | ||
374 | return true; | ||
375 | } | ||
376 | else if(obj->type == FW_EDOC) | ||
377 | { | ||
378 | FILE *fd = fopen(filename, "wb"); | ||
379 | if(fd == NULL) | ||
380 | { | ||
381 | printf("Cannot open '%s' for writing: %m\n", filename); | ||
382 | return false; | ||
383 | } | ||
384 | struct edoc_header_t hdr; | ||
385 | strncpy(hdr.magic, "EDOC", 4); | ||
386 | hdr.zero = 0; | ||
387 | hdr.total_size = 4; | ||
388 | for(int i = 0; i < obj->u.edoc->nr_sections; i++) | ||
389 | hdr.total_size += sizeof(struct edoc_section_header_t) + | ||
390 | obj->u.edoc->sections[i].size; | ||
391 | fwrite(&hdr, sizeof(hdr), 1, fd); | ||
392 | for(int i = 0; i < obj->u.edoc->nr_sections; i++) | ||
393 | { | ||
394 | struct edoc_section_header_t shdr; | ||
395 | shdr.addr = obj->u.edoc->sections[i].addr; | ||
396 | shdr.size = obj->u.edoc->sections[i].size; | ||
397 | shdr.checksum = edoc_checksum(obj->u.edoc->sections[i].data, shdr.size); | ||
398 | fwrite(&shdr, sizeof(shdr), 1, fd); | ||
399 | fwrite(obj->u.edoc->sections[i].data, shdr.size, 1, fd); | ||
400 | } | ||
401 | fclose(fd); | ||
402 | return true; | ||
403 | } | ||
404 | else | ||
405 | { | ||
406 | printf("Unimplemented fw_save\n"); | ||
407 | return false; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | static void fw_free(struct fw_object_t *obj) | ||
412 | { | ||
413 | switch(obj->type) | ||
414 | { | ||
415 | case FW_SB1: | ||
416 | sb1_free(obj->u.sb1); | ||
417 | break; | ||
418 | case FW_SB2: | ||
419 | sb_free(obj->u.sb); | ||
420 | break; | ||
421 | case FW_ELF: | ||
422 | elf_release(obj->u.elf); | ||
423 | free(obj->u.elf); | ||
424 | break; | ||
425 | case FW_BIN: | ||
426 | free(obj->u.bin->data); | ||
427 | free(obj->u.bin); | ||
428 | case FW_EDOC: | ||
429 | for(int i = 0; i < obj->u.edoc->nr_sections; i++) | ||
430 | free(obj->u.edoc->sections[i].data); | ||
431 | free(obj->u.edoc->sections); | ||
432 | free(obj->u.edoc); | ||
433 | default: | ||
434 | break; | ||
435 | } | ||
436 | free(obj); | ||
437 | } | ||
438 | |||
439 | static struct elf_section_t *elf_find_section(struct elf_params_t *elf, fw_addr_t addr) | ||
440 | { | ||
441 | struct elf_section_t *match = NULL; | ||
442 | for(struct elf_section_t *sec = elf->first_section; sec; sec = sec->next) | ||
443 | { | ||
444 | if(addr.section && strcmp(addr.section, sec->name) != 0) | ||
445 | continue; | ||
446 | if(addr.addr < sec->addr || addr.addr >= sec->addr + sec->size) | ||
447 | continue; | ||
448 | if(match != NULL) | ||
449 | { | ||
450 | printf("Error: there are several match for address %#x@%s\n", | ||
451 | (unsigned)addr.addr, addr.section); | ||
452 | return NULL; | ||
453 | } | ||
454 | match = sec; | ||
455 | } | ||
456 | if(match == NULL) | ||
457 | { | ||
458 | printf("Error: there is no match for address %#x@%s\n", (unsigned)addr.addr, | ||
459 | addr.section); | ||
460 | } | ||
461 | return match; | ||
462 | } | ||
463 | |||
464 | static bool fw_elf_rw(struct elf_params_t *elf, fw_addr_t addr, void *buffer, size_t size, bool read) | ||
465 | { | ||
466 | struct elf_section_t *sec = elf_find_section(elf, addr); | ||
467 | if(sec == NULL) | ||
468 | return false; | ||
469 | if(addr.addr + size > sec->addr + sec->size) | ||
470 | { | ||
471 | printf("Unsupported read/write across section boundary in ELF firmware\n"); | ||
472 | return false; | ||
473 | } | ||
474 | if(sec->type != EST_LOAD) | ||
475 | { | ||
476 | printf("Error: unimplemented read/write to a fill section (ELF)\n"); | ||
477 | return false; | ||
478 | } | ||
479 | void *data = sec->section + addr.addr - sec->addr; | ||
480 | if(read) | ||
481 | memcpy(buffer, data, size); | ||
482 | else | ||
483 | memcpy(data, buffer, size); | ||
484 | return true; | ||
485 | } | ||
486 | |||
487 | static struct sb_inst_t *sb2_find_section(struct sb_file_t *sb_file, fw_addr_t addr) | ||
488 | { | ||
489 | struct sb_inst_t *match = NULL; | ||
490 | uint32_t sec_id = 0xffffffff; | ||
491 | int inst_nr = -1; | ||
492 | if(addr.section) | ||
493 | { | ||
494 | /* must be of the form name[.index] */ | ||
495 | const char *mid = strchr(addr.section, '.'); | ||
496 | char *end; | ||
497 | if(mid) | ||
498 | { | ||
499 | inst_nr = strtol(mid + 1, &end, 0); | ||
500 | if(*end) | ||
501 | { | ||
502 | printf("Warning: ignoring invalid section name '%s' (invalid inst nr)\n", addr.section); | ||
503 | goto Lscan; | ||
504 | } | ||
505 | } | ||
506 | else | ||
507 | mid = addr.section + strlen(addr.section); | ||
508 | if(mid - addr.section > 4) | ||
509 | { | ||
510 | printf("Warning: ignoring invalid section name '%s' (sec id too long)\n", addr.section); | ||
511 | goto Lscan; | ||
512 | } | ||
513 | sec_id = 0; | ||
514 | for(int i = 0; i < mid - addr.section; i++) | ||
515 | sec_id = sec_id << 8 | addr.section[i]; | ||
516 | } | ||
517 | |||
518 | Lscan: | ||
519 | for(int i = 0; i < sb_file->nr_sections; i++) | ||
520 | { | ||
521 | struct sb_section_t *sec = &sb_file->sections[i]; | ||
522 | if(addr.section && sec->identifier != sec_id) | ||
523 | continue; | ||
524 | int cur_blob = 0; | ||
525 | for(int j = 0; j < sec->nr_insts; j++) | ||
526 | { | ||
527 | struct sb_inst_t *inst = &sec->insts[j]; | ||
528 | if(inst->inst == SB_INST_CALL || inst->inst == SB_INST_JUMP) | ||
529 | cur_blob++; | ||
530 | if(inst_nr >= 0 && cur_blob != inst_nr) | ||
531 | continue; | ||
532 | if(inst->inst != SB_INST_LOAD && inst->inst != SB_INST_FILL && inst->inst != SB_INST_DATA) | ||
533 | continue; | ||
534 | /* only consider data sections if section has been explicitely stated */ | ||
535 | if(inst->inst == SB_INST_DATA && !addr.section) | ||
536 | continue; | ||
537 | /* for data sections, address will be 0 */ | ||
538 | if(addr.addr < inst->addr || addr.addr > inst->addr + inst->size) | ||
539 | continue; | ||
540 | if(match != NULL) | ||
541 | { | ||
542 | printf("Error: there are several match for address %#x@%s\n", | ||
543 | (unsigned)addr.addr, addr.section); | ||
544 | return NULL; | ||
545 | } | ||
546 | match = inst; | ||
547 | } | ||
548 | } | ||
549 | if(match == NULL) | ||
550 | { | ||
551 | printf("Error: there is no match for address %#x@%s\n", (unsigned)addr.addr, | ||
552 | addr.section); | ||
553 | } | ||
554 | return match; | ||
555 | } | ||
556 | |||
557 | static bool fw_sb2_rw(struct sb_file_t *sb_file, fw_addr_t addr, void *buffer, size_t size, bool read) | ||
558 | { | ||
559 | struct sb_inst_t *inst = sb2_find_section(sb_file, addr); | ||
560 | if(inst == NULL) | ||
561 | return false; | ||
562 | if(addr.addr + size > inst->addr + inst->size) | ||
563 | { | ||
564 | printf("Unsupported read/write across instruction boundary in SB firmware\n"); | ||
565 | return false; | ||
566 | } | ||
567 | if(inst->inst != SB_INST_LOAD && inst->inst != SB_INST_DATA) | ||
568 | { | ||
569 | printf("Error: unimplemented read/write to a fill instruction (SB)\n"); | ||
570 | return false; | ||
571 | } | ||
572 | void *data = inst->data + addr.addr - inst->addr; | ||
573 | if(read) | ||
574 | memcpy(buffer, data, size); | ||
575 | else | ||
576 | memcpy(data, buffer, size); | ||
577 | return true; | ||
578 | } | ||
579 | |||
580 | static bool fw_bin_rw(struct bin_file_t *bin_file, fw_addr_t addr, void *buffer, size_t size, bool read) | ||
581 | { | ||
582 | if(addr.addr + size > bin_file->size) | ||
583 | { | ||
584 | printf("Unsupport read/write accross boundary in binary firmware\n"); | ||
585 | return false; | ||
586 | } | ||
587 | void *data = bin_file->data + addr.addr; | ||
588 | if(read) | ||
589 | memcpy(buffer, data, size); | ||
590 | else | ||
591 | memcpy(data, buffer, size); | ||
592 | return true; | ||
593 | } | ||
594 | |||
595 | static bool fw_edoc_rw(struct edoc_file_t *edoc_file, fw_addr_t addr, void *buffer, size_t size, bool read) | ||
596 | { | ||
597 | for(int i = 0; i < edoc_file->nr_sections; i++) | ||
598 | { | ||
599 | if(addr.addr < edoc_file->sections[i].addr || | ||
600 | addr.addr + size >= edoc_file->sections[i].addr + edoc_file->sections[i].size) | ||
601 | continue; | ||
602 | void *data = edoc_file->sections[i].data + addr.addr - edoc_file->sections[i].addr; | ||
603 | if(read) | ||
604 | memcpy(buffer, data, size); | ||
605 | else | ||
606 | memcpy(data, buffer, size); | ||
607 | return true; | ||
608 | } | ||
609 | printf("Unsupport read/write accross boundary in EDOC firmware\n"); | ||
610 | return false; | ||
611 | } | ||
612 | |||
613 | static bool fw_rw(struct fw_object_t *obj, fw_addr_t addr, void *buffer, size_t size, bool read) | ||
614 | { | ||
615 | switch(obj->type) | ||
616 | { | ||
617 | case FW_ELF: return fw_elf_rw(obj->u.elf, addr, buffer, size, read); | ||
618 | case FW_SB2: return fw_sb2_rw(obj->u.sb, addr, buffer, size, read); | ||
619 | case FW_BIN: return fw_bin_rw(obj->u.bin, addr, buffer, size, read); | ||
620 | case FW_EDOC: return fw_edoc_rw(obj->u.edoc, addr, buffer, size, read); | ||
621 | default: | ||
622 | printf("Error: unimplemented read/write for type %d\n", obj->type); | ||
623 | return false; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | static bool fw_read(struct fw_object_t *obj, fw_addr_t addr, void *buffer, size_t size) | ||
628 | { | ||
629 | return fw_rw(obj, addr, buffer, size, true); | ||
630 | } | ||
631 | |||
632 | static bool fw_write(struct fw_object_t *obj, fw_addr_t addr, const void *buffer, size_t size) | ||
633 | { | ||
634 | return fw_rw(obj, addr, (void *)buffer, size, false); | ||
635 | } | ||
636 | |||
637 | static bool elf_find_sym(struct elf_params_t *elf, fw_sym_addr_t addr, fw_addr_t *out_addr) | ||
638 | { | ||
639 | bool found = false; | ||
640 | for(struct elf_symbol_t *cur = elf->first_symbol; cur; cur = cur->next) | ||
641 | { | ||
642 | if(strcmp(cur->name, addr.name) != 0) | ||
643 | continue; | ||
644 | if(addr.section && strcmp(cur->section, addr.section) != 0) | ||
645 | continue; | ||
646 | if(found) | ||
647 | { | ||
648 | printf("Error: there are several match for symbol %s@%s\n", addr.name, addr.section); | ||
649 | return false; | ||
650 | } | ||
651 | out_addr->addr = cur->addr; | ||
652 | out_addr->section = cur->section; | ||
653 | found = true; | ||
654 | } | ||
655 | return found; | ||
656 | } | ||
657 | |||
658 | static bool fw_find_sym(struct fw_object_t *obj, fw_sym_addr_t addr, fw_addr_t *out_addr) | ||
659 | { | ||
660 | switch(obj->type) | ||
661 | { | ||
662 | case FW_ELF: return elf_find_sym(obj->u.elf, addr, out_addr); | ||
663 | case FW_SB2: case FW_SB1: case FW_BIN: return false; | ||
664 | default: | ||
665 | printf("Error: unimplemented find addr for type %d\n", obj->type); | ||
666 | return false; | ||
667 | } | ||
668 | } | ||
669 | |||
670 | static bool fw_bin_section_info(struct bin_file_t *obj, const char *sec, struct fw_section_info_t *out) | ||
671 | { | ||
672 | // the only valid section names are NULL and "" | ||
673 | if(sec != NULL && strlen(sec) != 0) | ||
674 | return false; | ||
675 | out->addr = 0; | ||
676 | out->size = obj->size; | ||
677 | return true; | ||
678 | } | ||
679 | |||
680 | static bool fw_section_info(struct fw_object_t *obj, const char *sec, struct fw_section_info_t *out) | ||
681 | { | ||
682 | switch(obj->type) | ||
683 | { | ||
684 | case FW_BIN: return fw_bin_section_info(obj->u.bin, sec, out); | ||
685 | default: | ||
686 | printf("Error: unimplemented get section info for type %d\n", obj->type); | ||
687 | return false; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | /** | ||
692 | * LUA library | ||
693 | */ | ||
694 | |||
695 | struct fw_object_t *my_lua_get_object(lua_State *state, int index) | ||
696 | { | ||
697 | struct fw_object_t *obj = lua_touserdata(state, index); | ||
698 | if(obj == NULL || obj->magic != FWOBJ_MAGIC) | ||
699 | luaL_error(state, "invalid parameter: not a firmware object"); | ||
700 | return obj; | ||
701 | } | ||
702 | |||
703 | const char *my_lua_get_string(lua_State *state, int index) | ||
704 | { | ||
705 | return luaL_checkstring(state, index); | ||
706 | } | ||
707 | |||
708 | lua_Unsigned my_lua_get_unsigned(lua_State *state, int index) | ||
709 | { | ||
710 | lua_Integer i = luaL_checkinteger(state, index); | ||
711 | if(i < 0) | ||
712 | luaL_error(state, "invalid parameter: not an unsigned value"); | ||
713 | return i; | ||
714 | } | ||
715 | |||
716 | fw_addr_t my_lua_get_addr(lua_State *state, int index) | ||
717 | { | ||
718 | if(!lua_istable(state, index)) | ||
719 | luaL_error(state, "invalid parameter: not an address table"); | ||
720 | lua_getfield(state, index, "addr"); | ||
721 | if(lua_isnil(state, -1)) | ||
722 | luaL_error(state, "invalid parameter: address has not field 'addr'"); | ||
723 | uint32_t addr = my_lua_get_unsigned(state, -1); | ||
724 | lua_pop(state, 1); | ||
725 | |||
726 | char *sec = NULL; | ||
727 | lua_getfield(state, index, "section"); | ||
728 | if(!lua_isnil(state, -1)) | ||
729 | sec = strdup(my_lua_get_string(state, -1)); | ||
730 | lua_pop(state, 1); | ||
731 | return make_addr(addr, sec); | ||
732 | } | ||
733 | |||
734 | void my_lua_pushbuffer(lua_State *state, void *buffer, size_t len) | ||
735 | { | ||
736 | uint8_t *p = buffer; | ||
737 | lua_createtable(state, len, 0); | ||
738 | for(int i = 0; i < len; i++) | ||
739 | { | ||
740 | lua_pushinteger(state, i + 1); | ||
741 | lua_pushinteger(state, p[i]); | ||
742 | lua_settable(state, -3); | ||
743 | } | ||
744 | } | ||
745 | |||
746 | void *my_lua_get_buffer(lua_State *state, int index, size_t *len) | ||
747 | { | ||
748 | if(!lua_istable(state, index)) | ||
749 | luaL_error(state, "invalid parameter: not a data table"); | ||
750 | *len = lua_rawlen(state, index); | ||
751 | uint8_t *buf = xmalloc(*len); | ||
752 | for(int i = 0; i < *len; i++) | ||
753 | { | ||
754 | lua_pushinteger(state, i + 1); | ||
755 | lua_gettable(state, index); | ||
756 | if(lua_isnil(state, -1)) | ||
757 | { | ||
758 | free(buf); | ||
759 | luaL_error(state, "invalid parameter: not a data table, missing some fields"); | ||
760 | } | ||
761 | int v = luaL_checkinteger(state, -1); | ||
762 | lua_pop(state, 1); | ||
763 | if(v < 0 || v > 0xff) | ||
764 | { | ||
765 | free(buf); | ||
766 | luaL_error(state, "invalid parameter: not a data table, field is not a byte"); | ||
767 | } | ||
768 | buf[i] = v; | ||
769 | } | ||
770 | return buf; | ||
771 | } | ||
772 | |||
773 | int my_lua_load_file(lua_State *state) | ||
774 | { | ||
775 | int n = lua_gettop(state); | ||
776 | if(n != 1) | ||
777 | return luaL_error(state, "load_file takes one argument: a filename"); | ||
778 | enum fw_type_t type = lua_tounsigned(state, lua_upvalueindex(1)); | ||
779 | const char *filename = my_lua_get_string(state, 1); | ||
780 | struct fw_object_t *obj = fw_load(filename, type); | ||
781 | if(obj) | ||
782 | lua_pushlightuserdata(state, obj); | ||
783 | else | ||
784 | lua_pushnil(state); | ||
785 | return 1; | ||
786 | } | ||
787 | |||
788 | int my_lua_save_file(lua_State *state) | ||
789 | { | ||
790 | int n = lua_gettop(state); | ||
791 | if(n != 2) | ||
792 | return luaL_error(state, "load_file takes two arguments: a firmware and a filename"); | ||
793 | struct fw_object_t *obj = my_lua_get_object(state, 1); | ||
794 | const char *filename = my_lua_get_string(state, 2); | ||
795 | lua_pushboolean(state, fw_save(obj, filename)); | ||
796 | return 1; | ||
797 | } | ||
798 | |||
799 | int my_lua_read(lua_State *state) | ||
800 | { | ||
801 | int n = lua_gettop(state); | ||
802 | if(n != 3) | ||
803 | return luaL_error(state, "read takes three arguments: a firmware, an address and a length"); | ||
804 | struct fw_object_t *obj = my_lua_get_object(state, 1); | ||
805 | fw_addr_t addr = my_lua_get_addr(state, 2); | ||
806 | size_t len = my_lua_get_unsigned(state, 3); | ||
807 | void *buffer = xmalloc(len); | ||
808 | bool ret = fw_read(obj, addr, buffer, len); | ||
809 | if(ret) | ||
810 | my_lua_pushbuffer(state, buffer, len); | ||
811 | else | ||
812 | lua_pushnil(state); | ||
813 | free(buffer); | ||
814 | return 1; | ||
815 | } | ||
816 | |||
817 | int my_lua_write(lua_State *state) | ||
818 | { | ||
819 | int n = lua_gettop(state); | ||
820 | if(n != 3) | ||
821 | return luaL_error(state, "write takes three arguments: a firmware, an address and a data table"); | ||
822 | struct fw_object_t *obj = my_lua_get_object(state, 1); | ||
823 | fw_addr_t addr = my_lua_get_addr(state, 2); | ||
824 | size_t len; | ||
825 | void *buf = my_lua_get_buffer(state, 3, &len); | ||
826 | fw_write(obj, addr, buf, len); | ||
827 | free(buf); | ||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | int my_lua_section_info(lua_State *state) | ||
832 | { | ||
833 | int n = lua_gettop(state); | ||
834 | if(n != 2) | ||
835 | return luaL_error(state, "section_info takes two arguments: a firmware and a section name"); | ||
836 | struct fw_object_t *obj = my_lua_get_object(state, 1); | ||
837 | const char *secname = my_lua_get_string(state, 2); | ||
838 | struct fw_section_info_t seci; | ||
839 | if(fw_section_info(obj, secname, &seci)) | ||
840 | { | ||
841 | lua_createtable(state, 0, 0); | ||
842 | lua_pushinteger(state, seci.addr); | ||
843 | lua_setfield(state, -2, "addr"); | ||
844 | lua_pushinteger(state, seci.size); | ||
845 | lua_setfield(state, -2, "size"); | ||
846 | } | ||
847 | else | ||
848 | lua_pushnil(state); | ||
849 | return 1; | ||
850 | } | ||
851 | |||
852 | /* compute MD5 sum of a buffer */ | ||
853 | static bool compute_md5sum_buf(void *buf, size_t sz, uint8_t file_md5sum[16]) | ||
854 | { | ||
855 | md5_context ctx; | ||
856 | md5_starts(&ctx); | ||
857 | md5_update(&ctx, buf, sz); | ||
858 | md5_finish(&ctx, file_md5sum); | ||
859 | return true; | ||
860 | } | ||
861 | |||
862 | /* read a file to a buffer */ | ||
863 | static bool read_file(const char *file, void **buffer, size_t *size) | ||
864 | { | ||
865 | FILE *f = fopen(file, "rb"); | ||
866 | if(f == NULL) | ||
867 | { | ||
868 | printf("Error: cannot open file for reading: %m\n"); | ||
869 | return false; | ||
870 | } | ||
871 | fseek(f, 0, SEEK_END); | ||
872 | *size = ftell(f); | ||
873 | fseek(f, 0, SEEK_SET); | ||
874 | *buffer = xmalloc(*size); | ||
875 | if(fread(*buffer, *size, 1, f) != 1) | ||
876 | { | ||
877 | printf("Error: cannot read file: %m\n"); | ||
878 | free(*buffer); | ||
879 | fclose(f); | ||
880 | return false; | ||
881 | } | ||
882 | fclose(f); | ||
883 | return true; | ||
884 | } | ||
885 | |||
886 | /* compute MD5 of a file */ | ||
887 | static bool compute_md5sum(const char *file, uint8_t file_md5sum[16]) | ||
888 | { | ||
889 | void *buf; | ||
890 | size_t sz; | ||
891 | if(!read_file(file, &buf, &sz)) | ||
892 | return false; | ||
893 | compute_md5sum_buf(buf, sz, file_md5sum); | ||
894 | free(buf); | ||
895 | return true; | ||
896 | } | ||
897 | |||
898 | int my_lua_md5sum(lua_State *state) | ||
899 | { | ||
900 | int n = lua_gettop(state); | ||
901 | if(n != 1) | ||
902 | return luaL_error(state, "md5sum takes one argument: a filename"); | ||
903 | const char *filename = my_lua_get_string(state, 1); | ||
904 | uint8_t md5sum[16]; | ||
905 | if(!compute_md5sum(filename, md5sum)) | ||
906 | return luaL_error(state, "cannot compute md5sum of the file"); | ||
907 | my_lua_pushbuffer(state, md5sum, sizeof(md5sum)); | ||
908 | return 1; | ||
909 | } | ||
910 | |||
911 | static bool init_lua_hwp(void) | ||
912 | { | ||
913 | lua_pushunsigned(g_lua, FW_UNK); | ||
914 | lua_pushcclosure(g_lua, my_lua_load_file, 1); | ||
915 | lua_setfield(g_lua, -2, "load_file"); | ||
916 | |||
917 | lua_pushunsigned(g_lua, FW_ELF); | ||
918 | lua_pushcclosure(g_lua, my_lua_load_file, 1); | ||
919 | lua_setfield(g_lua, -2, "load_elf_file"); | ||
920 | |||
921 | lua_pushunsigned(g_lua, FW_SB2); | ||
922 | lua_pushcclosure(g_lua, my_lua_load_file, 1); | ||
923 | lua_setfield(g_lua, -2, "load_sb_file"); | ||
924 | |||
925 | lua_pushunsigned(g_lua, FW_SB1); | ||
926 | lua_pushcclosure(g_lua, my_lua_load_file, 1); | ||
927 | lua_setfield(g_lua, -2, "load_sb1_file"); | ||
928 | |||
929 | lua_pushunsigned(g_lua, FW_BIN); | ||
930 | lua_pushcclosure(g_lua, my_lua_load_file, 1); | ||
931 | lua_setfield(g_lua, -2, "load_bin_file"); | ||
932 | |||
933 | lua_pushcfunction(g_lua, my_lua_save_file); | ||
934 | lua_setfield(g_lua, -2, "save_file"); | ||
935 | |||
936 | lua_pushcfunction(g_lua, my_lua_read); | ||
937 | lua_setfield(g_lua, -2, "read"); | ||
938 | |||
939 | lua_pushcfunction(g_lua, my_lua_write); | ||
940 | lua_setfield(g_lua, -2, "write"); | ||
941 | |||
942 | lua_pushcfunction(g_lua, my_lua_section_info); | ||
943 | lua_setfield(g_lua, -2, "section_info"); | ||
944 | |||
945 | lua_pushcfunction(g_lua, my_lua_md5sum); | ||
946 | lua_setfield(g_lua, -2, "md5sum"); | ||
947 | |||
948 | return true; | ||
949 | } | ||
950 | |||
951 | int my_lua_exit(lua_State *state) | ||
952 | { | ||
953 | g_exit = true; | ||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | bool my_lua_create_arg(lua_State *state, int argc, char **argv) | ||
958 | { | ||
959 | lua_newtable(state); // arg | ||
960 | for(int i = 0; i < argc; i++) | ||
961 | { | ||
962 | lua_pushinteger(state, i + 1); | ||
963 | lua_pushstring(state, argv[i]); | ||
964 | lua_settable(state, -3); | ||
965 | } | ||
966 | lua_setglobal(state, "arg"); | ||
967 | return true; | ||
968 | } | ||
969 | |||
970 | static bool init_lua(void) | ||
971 | { | ||
972 | g_lua = luaL_newstate(); | ||
973 | if(g_lua == NULL) | ||
974 | { | ||
975 | printf("Cannot create lua state\n"); | ||
976 | return 1; | ||
977 | } | ||
978 | // open all standard libraires | ||
979 | luaL_openlibs(g_lua); | ||
980 | |||
981 | lua_newtable(g_lua); // hwp | ||
982 | if(!init_lua_hwp()) | ||
983 | return false; | ||
984 | lua_setglobal(g_lua, "hwp"); | ||
985 | |||
986 | lua_pushcfunction(g_lua, my_lua_exit); | ||
987 | lua_setglobal(g_lua, "exit"); | ||
988 | |||
989 | lua_pushcfunction(g_lua, my_lua_exit); | ||
990 | lua_setglobal(g_lua, "quit"); | ||
991 | |||
992 | return true; | ||
993 | } | ||
994 | |||
995 | static void usage(void) | ||
996 | { | ||
997 | printf("Usage: hwpatcher [options] [--] [arguments]\n"); | ||
998 | printf("Options:\n"); | ||
999 | printf(" -?/--help Display this message\n"); | ||
1000 | printf(" -d/--debug Enable debug output\n"); | ||
1001 | printf(" -n/--no-color Disable color output\n"); | ||
1002 | printf(" -i/--interactive Enter interactive mode after all files have run\n"); | ||
1003 | printf(" -f/--do-file <f> Do lua file\n"); | ||
1004 | printf(" -k <file> Add key file\n"); | ||
1005 | printf(" -z Add zero key\n"); | ||
1006 | printf(" --add-key <key> Add single key (hex)\n"); | ||
1007 | printf(" -x Use default sb1 key\n"); | ||
1008 | printf("All files executed are provided with the extra arguments in the 'arg' table\n"); | ||
1009 | exit(1); | ||
1010 | } | ||
1011 | |||
1012 | int main(int argc, char **argv) | ||
1013 | { | ||
1014 | bool interactive = false; | ||
1015 | if(argc <= 1) | ||
1016 | usage(); | ||
1017 | if(!init_lua()) | ||
1018 | return 1; | ||
1019 | char **do_files = xmalloc(argc * sizeof(char *)); | ||
1020 | int nr_do_files = 0; | ||
1021 | while(1) | ||
1022 | { | ||
1023 | static struct option long_options[] = | ||
1024 | { | ||
1025 | {"help", no_argument, 0, '?'}, | ||
1026 | {"debug", no_argument, 0, 'd'}, | ||
1027 | {"no-color", no_argument, 0, 'n'}, | ||
1028 | {"interactive", no_argument, 0, 'i'}, | ||
1029 | {"do-file", required_argument, 0, 'f'}, | ||
1030 | {"add-key", required_argument, 0, 'a'}, | ||
1031 | {0, 0, 0, 0} | ||
1032 | }; | ||
1033 | |||
1034 | int c = getopt_long(argc, argv, "?dif:zx", long_options, NULL); | ||
1035 | if(c == -1) | ||
1036 | break; | ||
1037 | switch(c) | ||
1038 | { | ||
1039 | case -1: | ||
1040 | break; | ||
1041 | case 'n': | ||
1042 | enable_color(false); | ||
1043 | break; | ||
1044 | case 'd': | ||
1045 | g_debug = true; | ||
1046 | break; | ||
1047 | case '?': | ||
1048 | usage(); | ||
1049 | break; | ||
1050 | case 'i': | ||
1051 | interactive = true; | ||
1052 | break; | ||
1053 | case 'f': | ||
1054 | do_files[nr_do_files++] = optarg; | ||
1055 | break; | ||
1056 | case 'z': | ||
1057 | { | ||
1058 | struct crypto_key_t g_zero_key; | ||
1059 | sb_get_zero_key(&g_zero_key); | ||
1060 | add_keys(&g_zero_key, 1); | ||
1061 | break; | ||
1062 | } | ||
1063 | case 'x': | ||
1064 | { | ||
1065 | struct crypto_key_t key; | ||
1066 | sb1_get_default_key(&key); | ||
1067 | add_keys(&key, 1); | ||
1068 | break; | ||
1069 | } | ||
1070 | case 'a': | ||
1071 | { | ||
1072 | struct crypto_key_t key; | ||
1073 | char *s = optarg; | ||
1074 | if(!parse_key(&s, &key)) | ||
1075 | bug("Invalid key specified as argument\n"); | ||
1076 | if(*s != 0) | ||
1077 | bug("Trailing characters after key specified as argument\n"); | ||
1078 | add_keys(&key, 1); | ||
1079 | break; | ||
1080 | } | ||
1081 | default: | ||
1082 | printf("Internal error: unknown option '%c'\n", c); | ||
1083 | return 1; | ||
1084 | } | ||
1085 | } | ||
1086 | |||
1087 | if(!my_lua_create_arg(g_lua, argc - optind, argv + optind)) | ||
1088 | return 1; | ||
1089 | |||
1090 | for(int i = 0; i < nr_do_files; i++) | ||
1091 | { | ||
1092 | if(luaL_dofile(g_lua, do_files[i])) | ||
1093 | { | ||
1094 | printf("error in %s: %s\n", do_files[i], lua_tostring(g_lua, -1)); | ||
1095 | return 1; | ||
1096 | } | ||
1097 | lua_pop(g_lua, lua_gettop(g_lua)); | ||
1098 | } | ||
1099 | |||
1100 | if(nr_do_files == 0 && optind < argc) | ||
1101 | printf("Warning: extra unused arguments on command lines\n"); | ||
1102 | |||
1103 | if(interactive) | ||
1104 | { | ||
1105 | printf("Entering interactive mode. You can use 'quit()' or 'exit()' to quit.\n"); | ||
1106 | rl_bind_key('\t', rl_complete); | ||
1107 | while(!g_exit) | ||
1108 | { | ||
1109 | char *input = readline("> "); | ||
1110 | if(!input) | ||
1111 | break; | ||
1112 | add_history(input); | ||
1113 | // evaluate string | ||
1114 | if(luaL_dostring(g_lua, input)) | ||
1115 | printf("error: %s\n", lua_tostring(g_lua, -1)); | ||
1116 | // pop everything to start from a clean stack | ||
1117 | lua_pop(g_lua, lua_gettop(g_lua)); | ||
1118 | free(input); | ||
1119 | } | ||
1120 | } | ||
1121 | |||
1122 | return 0; | ||
1123 | } | ||