summaryrefslogtreecommitdiff
path: root/utils/hwpatcher/hwpatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwpatcher/hwpatcher.c')
-rw-r--r--utils/hwpatcher/hwpatcher.c1123
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
50lua_State *g_lua;
51bool g_exit = false;
52
53/**
54 * FW object library
55 */
56
57enum fw_type_t
58{
59 FW_UNK, FW_ELF, FW_SB1, FW_SB2, FW_BIN, FW_EDOC
60};
61
62struct bin_file_t
63{
64 size_t size;
65 void *data;
66};
67
68struct edoc_section_t
69{
70 uint32_t addr;
71 size_t size;
72 void *data;
73};
74
75struct edoc_file_t
76{
77 int nr_sections;
78 struct edoc_section_t *sections;
79};
80
81struct edoc_header_t
82{
83 char magic[4];
84 uint32_t total_size;
85 uint32_t zero;
86} __attribute__((packed));
87
88struct edoc_section_header_t
89{
90 uint32_t addr;
91 uint32_t size;
92 uint32_t checksum;
93} __attribute__((packed));
94
95uint32_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
112struct 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
126typedef 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
135typedef struct fw_sym_addr_t
136{
137 const char *name;
138 const char *section;
139}fw_sym_addr_t;
140
141struct fw_section_info_t
142{
143 uint32_t addr;
144 uint32_t size;
145};
146
147static 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
152static 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
179static 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
192static 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
330Lerr:
331 free(obj);
332 return NULL;
333}
334
335static 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
411static 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
439static 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
464static 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
487static 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
557static 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
580static 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
595static 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
613static 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
627static 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
632static 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
637static 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
658static 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
670static 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
680static 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
695struct 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
703const char *my_lua_get_string(lua_State *state, int index)
704{
705 return luaL_checkstring(state, index);
706}
707
708lua_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
716fw_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
734void 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
746void *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
773int 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
788int 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
799int 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
817int 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
831int 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 */
853static 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 */
863static 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 */
887static 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
898int 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
911static 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
951int my_lua_exit(lua_State *state)
952{
953 g_exit = true;
954 return 0;
955}
956
957bool 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
970static 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
995static 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
1012int 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}