diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/hwpatcher/Makefile | 27 | ||||
-rw-r--r-- | utils/hwpatcher/Makefile.patch | 25 | ||||
-rw-r--r-- | utils/hwpatcher/arm.lua | 205 | ||||
-rw-r--r-- | utils/hwpatcher/creative.lua | 49 | ||||
-rw-r--r-- | utils/hwpatcher/fuzep.lua | 49 | ||||
-rw-r--r-- | utils/hwpatcher/fuzep_rb.lua | 38 | ||||
-rw-r--r-- | utils/hwpatcher/generic_stmp.lua | 108 | ||||
-rw-r--r-- | utils/hwpatcher/hwpatcher.c | 1123 | ||||
-rw-r--r-- | utils/hwpatcher/lib.lua | 107 | ||||
-rw-r--r-- | utils/hwpatcher/md5.c | 246 | ||||
-rw-r--r-- | utils/hwpatcher/md5.h | 25 | ||||
-rw-r--r-- | utils/hwpatcher/patch.S | 87 | ||||
-rw-r--r-- | utils/hwpatcher/patch_viewbl.lua | 1 | ||||
-rw-r--r-- | utils/hwpatcher/view.lua | 38 | ||||
-rw-r--r-- | utils/hwpatcher/zen.lua | 1 | ||||
-rw-r--r-- | utils/hwpatcher/zxfi2.lua | 50 | ||||
-rw-r--r-- | utils/hwpatcher/zxfi3.lua | 49 |
17 files changed, 2228 insertions, 0 deletions
diff --git a/utils/hwpatcher/Makefile b/utils/hwpatcher/Makefile new file mode 100644 index 0000000000..c64ff302cc --- /dev/null +++ b/utils/hwpatcher/Makefile | |||
@@ -0,0 +1,27 @@ | |||
1 | CC=gcc | ||
2 | CXX=g++ | ||
3 | LD=g++ | ||
4 | SBTOOLS_DIR=../imxtools/sbtools | ||
5 | CFLAGS=-Wall -O3 -std=c99 -g -I$(SBTOOLS_DIR) `pkg-config --cflags lua5.2` | ||
6 | LDFLAGS=`pkg-config --libs lua5.2` -L$(REGTOOLS_LIB_DIR) `xml2-config --libs` -lreadline | ||
7 | EXEC=hwpatcher | ||
8 | SBTOOLS_SRC=elf.c crypto.c sb.c sb1.c aes128.c crc.c misc.c sha1.c xorcrypt.c | ||
9 | SBTOOLS_OBJ=$(SBTOOLS_SRC:.c=.o) | ||
10 | SRC=$(wildcard *.c) | ||
11 | OBJ=$(SRC:.c=.o) | ||
12 | |||
13 | all: $(EXEC) | ||
14 | |||
15 | %.o: $(SBTOOLS_DIR)/%.c | ||
16 | $(CC) $(CFLAGS) -c -o $@ $< | ||
17 | |||
18 | %.o: $%.c | ||
19 | $(CC) $(CFLAGS) -c -o $@ $< | ||
20 | |||
21 | hwpatcher: hwpatcher.o md5.o $(SBTOOLS_OBJ) | ||
22 | $(LD) -o $@ $^ $(LDFLAGS) | ||
23 | |||
24 | clean: | ||
25 | rm -rf $(SBTOOLS_OBJ) $(OBJ) $(EXEC) | ||
26 | |||
27 | |||
diff --git a/utils/hwpatcher/Makefile.patch b/utils/hwpatcher/Makefile.patch new file mode 100644 index 0000000000..61ea4f439e --- /dev/null +++ b/utils/hwpatcher/Makefile.patch | |||
@@ -0,0 +1,25 @@ | |||
1 | PREFIX?=arm-elf-eabi- | ||
2 | AS=$(PREFIX)gcc | ||
3 | ASFLAGS=-nostdlib -ffreestanding -mcpu=arm926ej-s | ||
4 | OC=$(PREFIX)objcopy | ||
5 | TARGETS=fuzeplus zenxfi2 zenxfi3 zen nwz zenxfistyle | ||
6 | OPT_fuzeplus=-DSANSA_FUZEPLUS | ||
7 | OPT_zenxfi2=-DCREATIVE_ZENXFI2 | ||
8 | OPT_zenxfi3=-DCREATIVE_ZENXFI3 | ||
9 | OPT_zen=-DCREATIVE_ZEN | ||
10 | OPT_nwz=-DSONY_NWZ | ||
11 | OPT_zenxfistyle=-DCREATIVE_ZENXFISTYLE | ||
12 | |||
13 | BOOTBINS=$(patsubst %, patch_%.bin, $(TARGETS)) | ||
14 | BOOTELF=$(patsubst %, patch_%.elf, $(TARGETS)) | ||
15 | |||
16 | all: $(BOOTBINS) | ||
17 | |||
18 | patch_%.bin: patch_%.elf | ||
19 | $(OC) -O binary $^ $@ | ||
20 | |||
21 | patch_%.elf: patch.S | ||
22 | $(AS) $(ASFLAGS) $(OPT_$(@:patch_%.elf=%)) -o $@ $< | ||
23 | |||
24 | clean: | ||
25 | rm -rf $(BOOTBINS) $(BOOTELF) | ||
diff --git a/utils/hwpatcher/arm.lua b/utils/hwpatcher/arm.lua new file mode 100644 index 0000000000..0211244d11 --- /dev/null +++ b/utils/hwpatcher/arm.lua | |||
@@ -0,0 +1,205 @@ | |||
1 | --[[ | ||
2 | hwpatcher arm decoding/encoding library | ||
3 | |||
4 | ]]-- | ||
5 | arm = {} | ||
6 | |||
7 | -- determines whether an address is in Thumb code or not | ||
8 | function arm.is_thumb(addr) | ||
9 | return bit32.extract(addr.addr, 0) == 1 | ||
10 | end | ||
11 | |||
12 | -- translate address to real address (ie without Thumb bit) | ||
13 | -- produces an error if address is not properly aligned in ARM mode | ||
14 | function arm.xlate_addr(addr) | ||
15 | local res = hwp.deepcopy(addr) | ||
16 | if arm.is_thumb(addr) then | ||
17 | res.addr = bit32.replace(addr.addr, 0, 0) | ||
18 | elseif bit32.extract(addr.addr, 0, 2) ~= 0 then | ||
19 | error("ARM address is not word-aligned") | ||
20 | end | ||
21 | return res | ||
22 | end | ||
23 | |||
24 | -- switch between arm and thumb | ||
25 | function arm.to_thumb(addr) | ||
26 | local res = hwp.deepcopy(addr) | ||
27 | res.addr = bit32.bor(addr.addr, 1) | ||
28 | return res | ||
29 | end | ||
30 | |||
31 | function arm.to_arm(addr) | ||
32 | return arm.xlate_addr(addr) | ||
33 | end | ||
34 | |||
35 | -- sign extend a value to 32-bits | ||
36 | -- only the lower 'bits' bits are considered, everything else is trashed | ||
37 | -- watch out arithmetic vs logical shift ! | ||
38 | function arm.sign32(v) | ||
39 | if bit32.extract(v, 31) == 1 then | ||
40 | return -1 - bit32.bnot(v) | ||
41 | else | ||
42 | return v | ||
43 | end | ||
44 | end | ||
45 | function arm.sign_extend(val, bits) | ||
46 | return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits)) | ||
47 | end | ||
48 | |||
49 | -- check that a signed value fits in some field | ||
50 | function arm.check_sign_truncation(val, bits) | ||
51 | return val == arm.sign_extend(val, bits) | ||
52 | end | ||
53 | |||
54 | -- create a branch description | ||
55 | function arm.make_branch(addr, link) | ||
56 | local t = {type = "branch", addr = addr, link = link} | ||
57 | local branch_to_string = function(self) | ||
58 | return string.format("branch(%s,%s)", self.addr, self.link) | ||
59 | end | ||
60 | setmetatable(t, {__tostring = branch_to_string}) | ||
61 | return t | ||
62 | end | ||
63 | |||
64 | -- parse a jump and returns its description | ||
65 | function arm.parse_branch(fw, addr) | ||
66 | local opcode = hwp.read32(fw, arm.xlate_addr(addr)) | ||
67 | if arm.is_thumb(addr) then | ||
68 | if bit32.band(opcode, 0xf800) ~= 0xf000 then | ||
69 | error("first instruction is not a bl(x) prefix") | ||
70 | end | ||
71 | local to_thumb = false | ||
72 | if bit32.band(opcode, 0xf8000000) == 0xf8000000 then | ||
73 | to_thumb = true | ||
74 | elseif bit32.band(opcode, 0xf8000000) ~= 0xe8000000 then | ||
75 | error("second instruction is not a bl(x) suffix") | ||
76 | end | ||
77 | local dest = hwp.make_addr(bit32.lshift(arm.sign_extend(opcode, 11), 12) + | ||
78 | arm.xlate_addr(addr).addr + 4 + | ||
79 | bit32.rshift(bit32.band(opcode, 0x7ff0000), 16) * 2, addr.section) | ||
80 | if to_thumb then | ||
81 | dest = arm.to_thumb(dest) | ||
82 | else | ||
83 | dest.addr = bit32.replace(dest.addr, 0, 0, 2) | ||
84 | end | ||
85 | return arm.make_branch(dest, true) | ||
86 | else | ||
87 | if bit32.band(opcode, 0xfe000000) == 0xfa000000 then -- BLX | ||
88 | local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 + | ||
89 | bit32.extract(opcode, 24) * 2 + arm.xlate_addr(addr).addr, addr.section) | ||
90 | return arm.make_branch(arm.to_thumb(dest), true) | ||
91 | elseif bit32.band(opcode, 0xfe000000) == 0xea000000 then -- B(L) | ||
92 | local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 + | ||
93 | arm.xlate_addr(addr).addr, addr.section) | ||
94 | return arm.make_branch(arm.to_arm(dest), bit32.extract(opcode, 24)) | ||
95 | else | ||
96 | error("instruction is not a valid branch") | ||
97 | end | ||
98 | end | ||
99 | end | ||
100 | |||
101 | -- generate the encoding of a branch | ||
102 | -- if the branch cannot be encoded using an immediate branch, it is generated | ||
103 | -- with an indirect form like a ldr, using a pool | ||
104 | function arm.write_branch(fw, addr, dest, pool) | ||
105 | local offset = arm.xlate_addr(dest.addr).addr - arm.xlate_addr(addr).addr | ||
106 | local exchange = arm.is_thumb(addr) ~= arm.is_thumb(dest.addr) | ||
107 | local opcode = 0 | ||
108 | if arm.is_thumb(addr) then | ||
109 | if not dest.link then | ||
110 | return arm.write_load_pc(fw, addr, dest, pool) | ||
111 | end | ||
112 | offset = offset - 4 -- in Thumb, PC+4 relative | ||
113 | -- NOTE: BLX is undefined if the resulting offset has bit 0 set, follow | ||
114 | -- the procedure from the manual to ensure correct operation | ||
115 | if bit32.extract(offset, 1) ~= 0 then | ||
116 | offset = offset + 2 | ||
117 | end | ||
118 | offset = offset / 2 | ||
119 | if not arm.check_sign_truncation(offset, 22) then | ||
120 | error("destination is too far for immediate branch from thumb") | ||
121 | end | ||
122 | opcode = 0xf000 + -- BL/BLX prefix | ||
123 | bit32.band(bit32.rshift(offset, 11), 0x7ff) + -- offset (high part) | ||
124 | bit32.lshift(exchange and 0xe800 or 0xf800,16) + -- BLX suffix | ||
125 | bit32.lshift(bit32.band(offset, 0x7ff), 16) -- offset (low part) | ||
126 | else | ||
127 | offset = offset - 8 -- in ARM, PC+8 relative | ||
128 | if exchange and not dest.link then | ||
129 | return arm.write_load_pc(fw, addr, dest, pool) | ||
130 | end | ||
131 | offset = offset / 4 | ||
132 | if not arm.check_sign_truncation(offset, 24) then | ||
133 | if pool == nil then | ||
134 | error("destination is too far for immediate branch from arm (no pool available)") | ||
135 | else | ||
136 | return arm.write_load_pc(fw, addr, dest, pool) | ||
137 | end | ||
138 | end | ||
139 | opcode = bit32.lshift(exchange and 0xf or 0xe, 28) + -- BLX / AL cond | ||
140 | bit32.lshift(0xa, 24) + -- branch | ||
141 | bit32.lshift(exchange and bit32.extract(offset, 1) or dest.link and 1 or 0, 24) + -- link / bit1 | ||
142 | bit32.band(offset, 0xffffff) | ||
143 | end | ||
144 | return hwp.write32(fw, arm.xlate_addr(addr), opcode) | ||
145 | end | ||
146 | |||
147 | function arm.write_load_pc(fw, addr, dest, pool) | ||
148 | -- write pool | ||
149 | hwp.write32(fw, pool, dest.addr.addr) | ||
150 | -- write "ldr pc, [pool]" | ||
151 | local opcode | ||
152 | if arm.is_thumb(addr) then | ||
153 | error("unsupported pc load in thumb mode") | ||
154 | else | ||
155 | local offset = pool.addr - arm.xlate_addr(addr).addr - 8 -- ARM is PC+8 relative | ||
156 | local add = offset >= 0 and 1 or 0 | ||
157 | offset = math.abs(offset) | ||
158 | opcode = bit32.lshift(0xe, 28) + -- AL cond | ||
159 | bit32.lshift(1, 26) + -- ldr/str | ||
160 | bit32.lshift(1, 24) + -- P | ||
161 | bit32.lshift(add, 23) + -- U | ||
162 | bit32.lshift(1, 20) + -- ldr | ||
163 | bit32.lshift(15, 16) + -- Rn=PC | ||
164 | bit32.lshift(15, 12) + -- Rd=PC | ||
165 | bit32.band(offset, 0xfff) | ||
166 | end | ||
167 | return hwp.write32(fw, arm.xlate_addr(addr), opcode) | ||
168 | end | ||
169 | |||
170 | -- generate the encoding of a "bx lr" | ||
171 | function arm.write_return(fw, addr) | ||
172 | if arm.is_thumb(addr) then | ||
173 | error("unsupported return from thumb code") | ||
174 | end | ||
175 | local opcode = bit32.lshift(0xe, 28) + -- AL cond | ||
176 | bit32.lshift(0x12, 20) + -- BX | ||
177 | bit32.lshift(1, 4) + -- BX | ||
178 | bit32.lshift(0xfff, 8) + -- SBO | ||
179 | 14 -- LR | ||
180 | hwp.write32(fw, arm.xlate_addr(addr), opcode) | ||
181 | end | ||
182 | |||
183 | function arm.write_xxx_regs(fw, addr, load) | ||
184 | if arm.is_thumb(addr) then | ||
185 | error("unsupported save/restore regs from thumb code") | ||
186 | end | ||
187 | -- STMFD sp!,{r0-r12, lr} | ||
188 | local opcode = bit32.lshift(0xe, 28) + -- AL cond | ||
189 | bit32.lshift(0x4, 25) + -- STM/LDM | ||
190 | bit32.lshift(load and 0 or 1,24) + -- P | ||
191 | bit32.lshift(load and 1 or 0, 23) + -- U | ||
192 | bit32.lshift(1, 21) +-- W | ||
193 | bit32.lshift(load and 1 or 0, 20) + -- L | ||
194 | bit32.lshift(13, 16) + -- base = SP | ||
195 | 0x5fff -- R0-R12,LR | ||
196 | return hwp.write32(fw, addr, opcode) | ||
197 | end | ||
198 | |||
199 | function arm.write_save_regs(fw, addr) | ||
200 | return arm.write_xxx_regs(fw, addr, false) | ||
201 | end | ||
202 | |||
203 | function arm.write_restore_regs(fw, addr) | ||
204 | return arm.write_xxx_regs(fw, addr, true) | ||
205 | end \ No newline at end of file | ||
diff --git a/utils/hwpatcher/creative.lua b/utils/hwpatcher/creative.lua new file mode 100644 index 0000000000..437785a0d5 --- /dev/null +++ b/utils/hwpatcher/creative.lua | |||
@@ -0,0 +1,49 @@ | |||
1 | --[[ | ||
2 | Creative ZEN hacking | ||
3 | required argument (in order): | ||
4 | - path to firmware | ||
5 | - path to output firmware | ||
6 | - path to blob | ||
7 | - path to stub | ||
8 | ]]-- | ||
9 | |||
10 | if #arg < 4 then | ||
11 | error("not enough argument to fuzep patcher") | ||
12 | end | ||
13 | |||
14 | local fw = hwp.load_file(arg[1]) | ||
15 | local irq_addr_pool = hwp.make_addr(0x38) | ||
16 | local proxy_addr = arm.to_arm(hwp.make_addr(0x402519A0)) | ||
17 | -- read old IRQ address pool | ||
18 | local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) | ||
19 | print(string.format("Old IRQ address: %s", old_irq_addr)) | ||
20 | -- put stub at the beginning of the proxy | ||
21 | local stub = hwp.load_bin_file(arg[4]) | ||
22 | local stub_info = hwp.section_info(stub, "") | ||
23 | local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) | ||
24 | hwp.write(fw, proxy_addr, stub_data) | ||
25 | local stub_addr = proxy_addr | ||
26 | proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) | ||
27 | -- modify irq | ||
28 | hwp.write32(fw, irq_addr_pool, proxy_addr.addr) | ||
29 | print(string.format("New IRQ address: %s", proxy_addr)) | ||
30 | -- in proxy, save registers | ||
31 | arm.write_save_regs(fw, proxy_addr) | ||
32 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
33 | -- load blob | ||
34 | local blob = hwp.load_bin_file(arg[3]) | ||
35 | local blob_info = hwp.section_info(blob, "") | ||
36 | -- patch blob with stub address | ||
37 | hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) | ||
38 | -- write it ! | ||
39 | local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) | ||
40 | hwp.write(fw, proxy_addr, blob_data) | ||
41 | proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) | ||
42 | -- restore registers | ||
43 | arm.write_restore_regs(fw, proxy_addr) | ||
44 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
45 | -- branch to old code | ||
46 | local branch_to_old = arm.make_branch(old_irq_addr, false) | ||
47 | arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) | ||
48 | -- save | ||
49 | hwp.save_file(fw, arg[2]) | ||
diff --git a/utils/hwpatcher/fuzep.lua b/utils/hwpatcher/fuzep.lua new file mode 100644 index 0000000000..6dfdad763a --- /dev/null +++ b/utils/hwpatcher/fuzep.lua | |||
@@ -0,0 +1,49 @@ | |||
1 | --[[ | ||
2 | Fuze+ 2.36.8 hacking | ||
3 | required argument (in order): | ||
4 | - path to firmware | ||
5 | - path to output firmware | ||
6 | - path to blob | ||
7 | - path to stub | ||
8 | ]]-- | ||
9 | |||
10 | if #arg < 4 then | ||
11 | error("not enough argument to fuzep patcher") | ||
12 | end | ||
13 | |||
14 | local fw = hwp.load_file(arg[1]) | ||
15 | local irq_addr_pool = hwp.make_addr(0x41172d44) | ||
16 | local proxy_addr = arm.to_arm(hwp.make_addr(0x40f35874)) | ||
17 | -- read old IRQ address pool | ||
18 | local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) | ||
19 | print(string.format("Old IRQ address: %s", old_irq_addr)) | ||
20 | -- put stub at the beginning of the proxy | ||
21 | local stub = hwp.load_bin_file(arg[4]) | ||
22 | local stub_info = hwp.section_info(stub, "") | ||
23 | local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) | ||
24 | hwp.write(fw, proxy_addr, stub_data) | ||
25 | local stub_addr = proxy_addr | ||
26 | proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) | ||
27 | -- modify irq | ||
28 | hwp.write32(fw, irq_addr_pool, proxy_addr.addr) | ||
29 | print(string.format("New IRQ address: %s", proxy_addr)) | ||
30 | -- in proxy, save registers | ||
31 | arm.write_save_regs(fw, proxy_addr) | ||
32 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
33 | -- load blob | ||
34 | local blob = hwp.load_bin_file(arg[3]) | ||
35 | local blob_info = hwp.section_info(blob, "") | ||
36 | -- patch blob with stub address | ||
37 | hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) | ||
38 | -- write it ! | ||
39 | local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) | ||
40 | hwp.write(fw, proxy_addr, blob_data) | ||
41 | proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) | ||
42 | -- restore registers | ||
43 | arm.write_restore_regs(fw, proxy_addr) | ||
44 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
45 | -- branch to old code | ||
46 | local branch_to_old = arm.make_branch(old_irq_addr, false) | ||
47 | arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) | ||
48 | -- save | ||
49 | hwp.save_file(fw, arg[2]) \ No newline at end of file | ||
diff --git a/utils/hwpatcher/fuzep_rb.lua b/utils/hwpatcher/fuzep_rb.lua new file mode 100644 index 0000000000..f47a4983fa --- /dev/null +++ b/utils/hwpatcher/fuzep_rb.lua | |||
@@ -0,0 +1,38 @@ | |||
1 | --[[ | ||
2 | Fuze+ RB hacking | ||
3 | required argument (in order): | ||
4 | - path to firmware | ||
5 | - path to output firmware | ||
6 | - path to blob | ||
7 | ]]-- | ||
8 | |||
9 | if #arg < 3 then | ||
10 | error("not enough argument to fuzep patcher") | ||
11 | end | ||
12 | |||
13 | local fw = hwp.load_file(arg[1]) | ||
14 | local irq_addr_pool = hwp.make_addr(0x38) | ||
15 | local proxy_addr = arm.to_arm(hwp.make_addr(0x60115ba4)) | ||
16 | -- read old IRQ address pool | ||
17 | local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) | ||
18 | print(string.format("Old IRQ address: %s", old_irq_addr)) | ||
19 | -- modify it | ||
20 | hwp.write32(fw, irq_addr_pool, proxy_addr.addr) | ||
21 | print(string.format("New IRQ address: %s", proxy_addr)) | ||
22 | -- in proxy, save registers | ||
23 | arm.write_save_regs(fw, proxy_addr) | ||
24 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
25 | -- do some work | ||
26 | local blob = hwp.load_bin_file(arg[3]) | ||
27 | local blob_info = hwp.section_info(blob, "") | ||
28 | local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) | ||
29 | hwp.write(fw, proxy_addr, blob_data) | ||
30 | proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) | ||
31 | -- restore registers | ||
32 | arm.write_restore_regs(fw, proxy_addr) | ||
33 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
34 | -- branch to old code | ||
35 | local branch_to_old = arm.make_branch(old_irq_addr, false) | ||
36 | arm.write_branch(fw, proxy_addr, branch_to_old) | ||
37 | -- save | ||
38 | hwp.save_file(fw, arg[2]) \ No newline at end of file | ||
diff --git a/utils/hwpatcher/generic_stmp.lua b/utils/hwpatcher/generic_stmp.lua new file mode 100644 index 0000000000..538e269e60 --- /dev/null +++ b/utils/hwpatcher/generic_stmp.lua | |||
@@ -0,0 +1,108 @@ | |||
1 | --[[ | ||
2 | Generic STMP hacking | ||
3 | required argument (in order): | ||
4 | - path to firmware | ||
5 | - path to output firmware | ||
6 | - path to blob | ||
7 | - path to stub | ||
8 | ]]-- | ||
9 | require("lib") | ||
10 | require("arm") | ||
11 | |||
12 | if #arg < 4 then | ||
13 | error("usage: <fw file> <out file> <blob> <stub>") | ||
14 | end | ||
15 | |||
16 | -- compute MD5 | ||
17 | print("Computing MD5 sum of the firmware...") | ||
18 | local md5 = hwp.md5sum(arg[1]) | ||
19 | print("=> " .. hwp.md5str(md5)) | ||
20 | |||
21 | local md5_db = | ||
22 | { | ||
23 | ["d0047f8a87d456a0032297b3c802a1ff"] = | ||
24 | { | ||
25 | model = "Sony NWZ-E3600 1.0.0", | ||
26 | irq_addr_pool = 0x40A314E4, | ||
27 | irq_addr_pool_sec = "play.1", | ||
28 | -- proxy_addr = 0x4005C1E0, | ||
29 | -- proxy_addr_sec = "play.1" | ||
30 | proxy_addr = 0x4007C258, | ||
31 | proxy_addr_sec = "play.1", | ||
32 | -- stub_addr = 0x1971C8, | ||
33 | -- stub_addr_virt = 0x2971C8, | ||
34 | -- stub_addr_sec = "pvmi", | ||
35 | }, | ||
36 | ["f42742d4d90d88e2fb6ff468c1389f5f"] = | ||
37 | { | ||
38 | model = "Creative ZEN X-Fi Style 1.03.04", | ||
39 | irq_addr_pool = 0x402D3A64, | ||
40 | irq_addr_pool_sec = "play.1", | ||
41 | proxy_addr = 0x402E076C, | ||
42 | proxy_addr_sec = "play.1" | ||
43 | }, | ||
44 | ["c180f57e2b2d62620f87a1d853f349ff"] = | ||
45 | { | ||
46 | model = "Creative ZEN X-Fi3 1.00.25e", | ||
47 | irq_addr_pool = 0x405916f0, | ||
48 | proxy_addr = 0x40384674, | ||
49 | } | ||
50 | } | ||
51 | |||
52 | local db_entry = md5_db[hwp.md5str(md5)] | ||
53 | if db_entry == nil then | ||
54 | error("Cannot find device in the DB") | ||
55 | os.exit(1) | ||
56 | end | ||
57 | print("Model: " .. db_entry.model) | ||
58 | |||
59 | local fw = hwp.load_file(arg[1]) | ||
60 | local irq_addr_pool = hwp.make_addr(db_entry.irq_addr_pool, db_entry.irq_addr_pool_sec) | ||
61 | local proxy_addr = arm.to_arm(hwp.make_addr(db_entry.proxy_addr, db_entry.proxy_addr_sec)) | ||
62 | -- read old IRQ address pool | ||
63 | local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) | ||
64 | print(string.format("Old IRQ address: %s", old_irq_addr)) | ||
65 | -- put stub at the beginning of the proxy | ||
66 | local stub = hwp.load_bin_file(arg[4]) | ||
67 | local stub_info = hwp.section_info(stub, "") | ||
68 | local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) | ||
69 | local stub_addr = nil | ||
70 | local stub_addr_virt = nil | ||
71 | if db_entry.stub_addr ~= nil then | ||
72 | stub_addr = arm.to_arm(hwp.make_addr(db_entry.stub_addr, db_entry.stub_addr_sec)) | ||
73 | if db_entry.stub_addr_virt ~= nil then | ||
74 | stub_addr_virt = arm.to_arm(hwp.make_addr(db_entry.stub_addr_virt, db_entry.stub_addr_sec)) | ||
75 | else | ||
76 | stub_addr_virt = stub_addr | ||
77 | end | ||
78 | hwp.write(fw, stub_addr, stub_data) | ||
79 | else | ||
80 | stub_addr = proxy_addr | ||
81 | stub_addr_virt = stub_addr | ||
82 | hwp.write(fw, stub_addr, stub_data) | ||
83 | proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) | ||
84 | end | ||
85 | -- modify irq | ||
86 | hwp.write32(fw, irq_addr_pool, proxy_addr.addr) | ||
87 | print(string.format("New IRQ address: %s", proxy_addr)) | ||
88 | -- in proxy, save registers | ||
89 | arm.write_save_regs(fw, proxy_addr) | ||
90 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
91 | -- load blob | ||
92 | local blob = hwp.load_bin_file(arg[3]) | ||
93 | local blob_info = hwp.section_info(blob, "") | ||
94 | -- patch blob with stub address | ||
95 | hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr_virt.addr) | ||
96 | -- write it ! | ||
97 | local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) | ||
98 | hwp.write(fw, proxy_addr, blob_data) | ||
99 | proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) | ||
100 | -- restore registers | ||
101 | arm.write_restore_regs(fw, proxy_addr) | ||
102 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
103 | -- branch to old code | ||
104 | local branch_to_old = arm.make_branch(old_irq_addr, false) | ||
105 | arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) | ||
106 | -- save | ||
107 | hwp.save_file(fw, arg[2]) | ||
108 | |||
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 | } | ||
diff --git a/utils/hwpatcher/lib.lua b/utils/hwpatcher/lib.lua new file mode 100644 index 0000000000..7a3e4a4115 --- /dev/null +++ b/utils/hwpatcher/lib.lua | |||
@@ -0,0 +1,107 @@ | |||
1 | --[[ | ||
2 | hwpatcher library | ||
3 | |||
4 | The C code provides the following functions. | ||
5 | |||
6 | At global level: | ||
7 | - quit() Quit the interactive mode | ||
8 | - exit() Same as quit() | ||
9 | |||
10 | In the hwp table: | ||
11 | - load_file(filename) Load a firmware and guess type | ||
12 | - load_elf_file(filename) Load a firmware as ELF | ||
13 | - load_sb_file(filename) Load a firmware as SB | ||
14 | - load_sb1_file(filename) Load a firmware as SB1 | ||
15 | - load_bin_file(filename) Load a firmware as binary | ||
16 | - save_file(obj, filename) Save a firmware to a file | ||
17 | - read(obj, addr, len) Read data from a firmware | ||
18 | - write(obj, addr, data) Write data to a firmware | ||
19 | - section_info(obj, sec) Return information about a section in a table (or nil) | ||
20 | - md5sum(filename) Compute the MD5 sum of a file | ||
21 | |||
22 | Data read/written from/to a firmware must must be an array of bytes. | ||
23 | The address must be a table of the following fields: | ||
24 | - address: contain the address | ||
25 | - section: optional section name | ||
26 | Data section information is a table with the following fields: | ||
27 | - address: first address if the section | ||
28 | - size: size of the section | ||
29 | We provide the following functions to help dealing with addresses: | ||
30 | - make_addr | ||
31 | |||
32 | ]]-- | ||
33 | |||
34 | function hwp.deepcopy(o, seen) | ||
35 | seen = seen or {} | ||
36 | if o == nil then return nil end | ||
37 | if seen[o] then return seen[o] end | ||
38 | |||
39 | local no | ||
40 | if type(o) == 'table' then | ||
41 | no = {} | ||
42 | seen[o] = no | ||
43 | |||
44 | for k, v in next, o, nil do | ||
45 | no[hwp.deepcopy(k, seen)] = hwp.deepcopy(v, seen) | ||
46 | end | ||
47 | setmetatable(no, hwp.deepcopy(getmetatable(o), seen)) | ||
48 | else -- number, string, boolean, etc | ||
49 | no = o | ||
50 | end | ||
51 | return no | ||
52 | end | ||
53 | |||
54 | function hwp.make_addr(addr, section) | ||
55 | local t = {addr = addr, section = section} | ||
56 | local addr_to_string = function(self) | ||
57 | if self.section == nil then | ||
58 | return string.format("%#x", self.addr) | ||
59 | else | ||
60 | return string.format("%#x@%s", self.addr, self.section) | ||
61 | end | ||
62 | end | ||
63 | setmetatable(t, {__tostring = addr_to_string}) | ||
64 | return t | ||
65 | end | ||
66 | |||
67 | function hwp.inc_addr(addr, amount) | ||
68 | return hwp.make_addr(addr.addr + amount, addr.section) | ||
69 | end | ||
70 | |||
71 | -- pack an array of bytes in a integer (little-endian) | ||
72 | function hwp.pack(arr) | ||
73 | local v = 0 | ||
74 | for i = #arr, 1, -1 do | ||
75 | v = bit32.bor(bit32.lshift(v, 8),bit32.band(arr[i], 0xff)) | ||
76 | end | ||
77 | return v | ||
78 | end | ||
79 | |||
80 | -- do the converse | ||
81 | function hwp.unpack(v, n) | ||
82 | local t = {} | ||
83 | for i = 1, n do | ||
84 | t[i] = bit32.band(v, 0xff) | ||
85 | v = bit32.rshift(v, 8) | ||
86 | end | ||
87 | return t | ||
88 | end | ||
89 | |||
90 | -- read a 32-bit value | ||
91 | function hwp.read32(obj, addr) | ||
92 | return hwp.pack(hwp.read(obj, addr, 4)) | ||
93 | end | ||
94 | |||
95 | -- write a 32-bit value | ||
96 | function hwp.write32(obj, addr, v) | ||
97 | return hwp.write(obj, addr, hwp.unpack(v, 4)) | ||
98 | end | ||
99 | |||
100 | -- convert a MD5 hash to a string | ||
101 | function hwp.md5str(md5) | ||
102 | local s = "" | ||
103 | for i = 1, #md5 do | ||
104 | s = s .. string.format("%02x", md5[i]) | ||
105 | end | ||
106 | return s | ||
107 | end | ||
diff --git a/utils/hwpatcher/md5.c b/utils/hwpatcher/md5.c new file mode 100644 index 0000000000..530d8df15a --- /dev/null +++ b/utils/hwpatcher/md5.c | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * RFC 1321 compliant MD5 implementation | ||
3 | * | ||
4 | * Copyright (C) 2001-2003 Christophe Devine | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <string.h> | ||
22 | |||
23 | #include "md5.h" | ||
24 | |||
25 | #define GET_UINT32(n,b,i) \ | ||
26 | { \ | ||
27 | (n) = ( (uint32) (b)[(i) ] ) \ | ||
28 | | ( (uint32) (b)[(i) + 1] << 8 ) \ | ||
29 | | ( (uint32) (b)[(i) + 2] << 16 ) \ | ||
30 | | ( (uint32) (b)[(i) + 3] << 24 ); \ | ||
31 | } | ||
32 | |||
33 | #define PUT_UINT32(n,b,i) \ | ||
34 | { \ | ||
35 | (b)[(i) ] = (uint8) ( (n) ); \ | ||
36 | (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \ | ||
37 | (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \ | ||
38 | (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \ | ||
39 | } | ||
40 | |||
41 | void md5_starts( md5_context *ctx ) | ||
42 | { | ||
43 | ctx->total[0] = 0; | ||
44 | ctx->total[1] = 0; | ||
45 | |||
46 | ctx->state[0] = 0x67452301; | ||
47 | ctx->state[1] = 0xEFCDAB89; | ||
48 | ctx->state[2] = 0x98BADCFE; | ||
49 | ctx->state[3] = 0x10325476; | ||
50 | } | ||
51 | |||
52 | void md5_process( md5_context *ctx, uint8 data[64] ) | ||
53 | { | ||
54 | uint32 X[16], A, B, C, D; | ||
55 | |||
56 | GET_UINT32( X[0], data, 0 ); | ||
57 | GET_UINT32( X[1], data, 4 ); | ||
58 | GET_UINT32( X[2], data, 8 ); | ||
59 | GET_UINT32( X[3], data, 12 ); | ||
60 | GET_UINT32( X[4], data, 16 ); | ||
61 | GET_UINT32( X[5], data, 20 ); | ||
62 | GET_UINT32( X[6], data, 24 ); | ||
63 | GET_UINT32( X[7], data, 28 ); | ||
64 | GET_UINT32( X[8], data, 32 ); | ||
65 | GET_UINT32( X[9], data, 36 ); | ||
66 | GET_UINT32( X[10], data, 40 ); | ||
67 | GET_UINT32( X[11], data, 44 ); | ||
68 | GET_UINT32( X[12], data, 48 ); | ||
69 | GET_UINT32( X[13], data, 52 ); | ||
70 | GET_UINT32( X[14], data, 56 ); | ||
71 | GET_UINT32( X[15], data, 60 ); | ||
72 | |||
73 | #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) | ||
74 | |||
75 | #define P(a,b,c,d,k,s,t) \ | ||
76 | { \ | ||
77 | a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ | ||
78 | } | ||
79 | |||
80 | A = ctx->state[0]; | ||
81 | B = ctx->state[1]; | ||
82 | C = ctx->state[2]; | ||
83 | D = ctx->state[3]; | ||
84 | |||
85 | #define F(x,y,z) (z ^ (x & (y ^ z))) | ||
86 | |||
87 | P( A, B, C, D, 0, 7, 0xD76AA478 ); | ||
88 | P( D, A, B, C, 1, 12, 0xE8C7B756 ); | ||
89 | P( C, D, A, B, 2, 17, 0x242070DB ); | ||
90 | P( B, C, D, A, 3, 22, 0xC1BDCEEE ); | ||
91 | P( A, B, C, D, 4, 7, 0xF57C0FAF ); | ||
92 | P( D, A, B, C, 5, 12, 0x4787C62A ); | ||
93 | P( C, D, A, B, 6, 17, 0xA8304613 ); | ||
94 | P( B, C, D, A, 7, 22, 0xFD469501 ); | ||
95 | P( A, B, C, D, 8, 7, 0x698098D8 ); | ||
96 | P( D, A, B, C, 9, 12, 0x8B44F7AF ); | ||
97 | P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); | ||
98 | P( B, C, D, A, 11, 22, 0x895CD7BE ); | ||
99 | P( A, B, C, D, 12, 7, 0x6B901122 ); | ||
100 | P( D, A, B, C, 13, 12, 0xFD987193 ); | ||
101 | P( C, D, A, B, 14, 17, 0xA679438E ); | ||
102 | P( B, C, D, A, 15, 22, 0x49B40821 ); | ||
103 | |||
104 | #undef F | ||
105 | |||
106 | #define F(x,y,z) (y ^ (z & (x ^ y))) | ||
107 | |||
108 | P( A, B, C, D, 1, 5, 0xF61E2562 ); | ||
109 | P( D, A, B, C, 6, 9, 0xC040B340 ); | ||
110 | P( C, D, A, B, 11, 14, 0x265E5A51 ); | ||
111 | P( B, C, D, A, 0, 20, 0xE9B6C7AA ); | ||
112 | P( A, B, C, D, 5, 5, 0xD62F105D ); | ||
113 | P( D, A, B, C, 10, 9, 0x02441453 ); | ||
114 | P( C, D, A, B, 15, 14, 0xD8A1E681 ); | ||
115 | P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); | ||
116 | P( A, B, C, D, 9, 5, 0x21E1CDE6 ); | ||
117 | P( D, A, B, C, 14, 9, 0xC33707D6 ); | ||
118 | P( C, D, A, B, 3, 14, 0xF4D50D87 ); | ||
119 | P( B, C, D, A, 8, 20, 0x455A14ED ); | ||
120 | P( A, B, C, D, 13, 5, 0xA9E3E905 ); | ||
121 | P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); | ||
122 | P( C, D, A, B, 7, 14, 0x676F02D9 ); | ||
123 | P( B, C, D, A, 12, 20, 0x8D2A4C8A ); | ||
124 | |||
125 | #undef F | ||
126 | |||
127 | #define F(x,y,z) (x ^ y ^ z) | ||
128 | |||
129 | P( A, B, C, D, 5, 4, 0xFFFA3942 ); | ||
130 | P( D, A, B, C, 8, 11, 0x8771F681 ); | ||
131 | P( C, D, A, B, 11, 16, 0x6D9D6122 ); | ||
132 | P( B, C, D, A, 14, 23, 0xFDE5380C ); | ||
133 | P( A, B, C, D, 1, 4, 0xA4BEEA44 ); | ||
134 | P( D, A, B, C, 4, 11, 0x4BDECFA9 ); | ||
135 | P( C, D, A, B, 7, 16, 0xF6BB4B60 ); | ||
136 | P( B, C, D, A, 10, 23, 0xBEBFBC70 ); | ||
137 | P( A, B, C, D, 13, 4, 0x289B7EC6 ); | ||
138 | P( D, A, B, C, 0, 11, 0xEAA127FA ); | ||
139 | P( C, D, A, B, 3, 16, 0xD4EF3085 ); | ||
140 | P( B, C, D, A, 6, 23, 0x04881D05 ); | ||
141 | P( A, B, C, D, 9, 4, 0xD9D4D039 ); | ||
142 | P( D, A, B, C, 12, 11, 0xE6DB99E5 ); | ||
143 | P( C, D, A, B, 15, 16, 0x1FA27CF8 ); | ||
144 | P( B, C, D, A, 2, 23, 0xC4AC5665 ); | ||
145 | |||
146 | #undef F | ||
147 | |||
148 | #define F(x,y,z) (y ^ (x | ~z)) | ||
149 | |||
150 | P( A, B, C, D, 0, 6, 0xF4292244 ); | ||
151 | P( D, A, B, C, 7, 10, 0x432AFF97 ); | ||
152 | P( C, D, A, B, 14, 15, 0xAB9423A7 ); | ||
153 | P( B, C, D, A, 5, 21, 0xFC93A039 ); | ||
154 | P( A, B, C, D, 12, 6, 0x655B59C3 ); | ||
155 | P( D, A, B, C, 3, 10, 0x8F0CCC92 ); | ||
156 | P( C, D, A, B, 10, 15, 0xFFEFF47D ); | ||
157 | P( B, C, D, A, 1, 21, 0x85845DD1 ); | ||
158 | P( A, B, C, D, 8, 6, 0x6FA87E4F ); | ||
159 | P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); | ||
160 | P( C, D, A, B, 6, 15, 0xA3014314 ); | ||
161 | P( B, C, D, A, 13, 21, 0x4E0811A1 ); | ||
162 | P( A, B, C, D, 4, 6, 0xF7537E82 ); | ||
163 | P( D, A, B, C, 11, 10, 0xBD3AF235 ); | ||
164 | P( C, D, A, B, 2, 15, 0x2AD7D2BB ); | ||
165 | P( B, C, D, A, 9, 21, 0xEB86D391 ); | ||
166 | |||
167 | #undef F | ||
168 | |||
169 | ctx->state[0] += A; | ||
170 | ctx->state[1] += B; | ||
171 | ctx->state[2] += C; | ||
172 | ctx->state[3] += D; | ||
173 | } | ||
174 | |||
175 | void md5_update( md5_context *ctx, uint8 *input, uint32 length ) | ||
176 | { | ||
177 | uint32 left, fill; | ||
178 | |||
179 | if( ! length ) return; | ||
180 | |||
181 | left = ctx->total[0] & 0x3F; | ||
182 | fill = 64 - left; | ||
183 | |||
184 | ctx->total[0] += length; | ||
185 | ctx->total[0] &= 0xFFFFFFFF; | ||
186 | |||
187 | if( ctx->total[0] < length ) | ||
188 | ctx->total[1]++; | ||
189 | |||
190 | if( left && length >= fill ) | ||
191 | { | ||
192 | memcpy( (void *) (ctx->buffer + left), | ||
193 | (void *) input, fill ); | ||
194 | md5_process( ctx, ctx->buffer ); | ||
195 | length -= fill; | ||
196 | input += fill; | ||
197 | left = 0; | ||
198 | } | ||
199 | |||
200 | while( length >= 64 ) | ||
201 | { | ||
202 | md5_process( ctx, input ); | ||
203 | length -= 64; | ||
204 | input += 64; | ||
205 | } | ||
206 | |||
207 | if( length ) | ||
208 | { | ||
209 | memcpy( (void *) (ctx->buffer + left), | ||
210 | (void *) input, length ); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | static uint8 md5_padding[64] = | ||
215 | { | ||
216 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
217 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
218 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
219 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
220 | }; | ||
221 | |||
222 | void md5_finish( md5_context *ctx, uint8 digest[16] ) | ||
223 | { | ||
224 | uint32 last, padn; | ||
225 | uint32 high, low; | ||
226 | uint8 msglen[8]; | ||
227 | |||
228 | high = ( ctx->total[0] >> 29 ) | ||
229 | | ( ctx->total[1] << 3 ); | ||
230 | low = ( ctx->total[0] << 3 ); | ||
231 | |||
232 | PUT_UINT32( low, msglen, 0 ); | ||
233 | PUT_UINT32( high, msglen, 4 ); | ||
234 | |||
235 | last = ctx->total[0] & 0x3F; | ||
236 | padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); | ||
237 | |||
238 | md5_update( ctx, md5_padding, padn ); | ||
239 | md5_update( ctx, msglen, 8 ); | ||
240 | |||
241 | PUT_UINT32( ctx->state[0], digest, 0 ); | ||
242 | PUT_UINT32( ctx->state[1], digest, 4 ); | ||
243 | PUT_UINT32( ctx->state[2], digest, 8 ); | ||
244 | PUT_UINT32( ctx->state[3], digest, 12 ); | ||
245 | } | ||
246 | |||
diff --git a/utils/hwpatcher/md5.h b/utils/hwpatcher/md5.h new file mode 100644 index 0000000000..71fa395548 --- /dev/null +++ b/utils/hwpatcher/md5.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef _MD5_H | ||
2 | #define _MD5_H | ||
3 | |||
4 | #ifndef uint8 | ||
5 | #define uint8 unsigned char | ||
6 | #endif | ||
7 | |||
8 | #ifndef uint32 | ||
9 | #define uint32 unsigned long int | ||
10 | #endif | ||
11 | |||
12 | typedef struct | ||
13 | { | ||
14 | uint32 total[2]; | ||
15 | uint32 state[4]; | ||
16 | uint8 buffer[64]; | ||
17 | } | ||
18 | md5_context; | ||
19 | |||
20 | void md5_starts( md5_context *ctx ); | ||
21 | void md5_update( md5_context *ctx, uint8 *input, uint32 length ); | ||
22 | void md5_finish( md5_context *ctx, uint8 digest[16] ); | ||
23 | |||
24 | #endif /* md5.h */ | ||
25 | |||
diff --git a/utils/hwpatcher/patch.S b/utils/hwpatcher/patch.S new file mode 100644 index 0000000000..6c27e299d9 --- /dev/null +++ b/utils/hwpatcher/patch.S | |||
@@ -0,0 +1,87 @@ | |||
1 | .text | ||
2 | .global _start | ||
3 | _start: | ||
4 | b exec | ||
5 | branch_addr: | ||
6 | .word kill | ||
7 | hw_power_sts: | ||
8 | #if defined(CREATIVE_ZEN) | ||
9 | .word 0x800440b0 /* STMP3700 */ | ||
10 | #else | ||
11 | .word 0x800440c0 /* IMX233 */ | ||
12 | #endif | ||
13 | hw_pinctrl_din0: | ||
14 | .word 0x80018600 | ||
15 | hw_pinctrl_din1: | ||
16 | .word 0x80018610 | ||
17 | hw_pinctrl_din2: | ||
18 | .word 0x80018620 | ||
19 | kill: | ||
20 | ldr pc, branch_addr | ||
21 | exec: | ||
22 | #if defined(SANSA_FUZEPLUS) | ||
23 | /* check PSWITCH=1 (power button pressed) */ | ||
24 | ldr r0, hw_power_sts | ||
25 | ldr r0, [r0] | ||
26 | mov r0, r0, lsr #20 | ||
27 | and r0, #3 | ||
28 | cmp r0, #1 | ||
29 | bne ret | ||
30 | /* check B1P30=0 (volume down pressed) */ | ||
31 | ldr r0, hw_pinctrl_din1 | ||
32 | ldr r0, [r0] | ||
33 | mov r0, r0, lsr #30 | ||
34 | ands r0, #1 | ||
35 | beq kill | ||
36 | #elif defined(CREATIVE_ZENXFI3) | ||
37 | /* check PSWITCH=1 (power button pressed) */ | ||
38 | ldr r0, hw_power_sts | ||
39 | ldr r0, [r0] | ||
40 | mov r0, r0, lsr #20 | ||
41 | and r0, #3 | ||
42 | cmp r0, #1 | ||
43 | bne ret | ||
44 | /* check B2P07=0 (volume down pressed) */ | ||
45 | ldr r0, hw_pinctrl_din2 | ||
46 | ldr r0, [r0] | ||
47 | mov r0, r0, lsr #7 | ||
48 | ands r0, #1 | ||
49 | beq kill | ||
50 | #elif defined(CREATIVE_ZENXFI2) | ||
51 | /* check B0P11=0 (power button pressed) and B0P14 (select button pressed) */ | ||
52 | ldr r0, hw_pinctrl_din0 | ||
53 | ldr r0, [r0] | ||
54 | mov r0, r0, lsr #11 | ||
55 | tst r0, #1 | ||
56 | bne ret | ||
57 | mov r0, r0, lsr #3 | ||
58 | tst r0, #1 | ||
59 | beq kill | ||
60 | #elif defined(CREATIVE_ZEN) | ||
61 | /* check PSWITCH=1 (power button pressed) */ | ||
62 | ldr r0, hw_power_sts | ||
63 | ldr r0, [r0] | ||
64 | mov r0, r0, lsr #18 | ||
65 | and r0, #3 | ||
66 | cmp r0, #0 | ||
67 | bne kill | ||
68 | #elif defined(SONY_NWZ) | ||
69 | /* check PSWITCH=3 (power button pressed) */ | ||
70 | ldr r0, hw_power_sts | ||
71 | ldr r0, [r0] | ||
72 | mov r0, r0, lsr #20 | ||
73 | and r0, #3 | ||
74 | cmp r0, #3 | ||
75 | beq kill | ||
76 | #elif defined(CREATIVE_ZENXFISTYLE) | ||
77 | /* check PSWITCH=1 (power button pressed) */ | ||
78 | ldr r0, hw_power_sts | ||
79 | ldr r0, [r0] | ||
80 | mov r0, r0, lsr #20 | ||
81 | and r0, #3 | ||
82 | cmp r0, #1 | ||
83 | beq kill | ||
84 | #else | ||
85 | #error implement me | ||
86 | #endif | ||
87 | ret: \ No newline at end of file | ||
diff --git a/utils/hwpatcher/patch_viewbl.lua b/utils/hwpatcher/patch_viewbl.lua new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/utils/hwpatcher/patch_viewbl.lua | |||
@@ -0,0 +1 @@ | |||
diff --git a/utils/hwpatcher/view.lua b/utils/hwpatcher/view.lua new file mode 100644 index 0000000000..35c383800f --- /dev/null +++ b/utils/hwpatcher/view.lua | |||
@@ -0,0 +1,38 @@ | |||
1 | --[[ | ||
2 | Sansa View bootloader hacking | ||
3 | required argument (in order): | ||
4 | - path to bootloader | ||
5 | - path to output bootloader | ||
6 | - path to stub | ||
7 | ]]-- | ||
8 | require("lib") | ||
9 | require("arm") | ||
10 | |||
11 | if #arg < 3 then | ||
12 | error("not enough argument to fuzep patcher") | ||
13 | end | ||
14 | |||
15 | local md5 = hwp.md5sum(arg[1]) | ||
16 | if hwp.md5str(md5) ~= "4bc1760327c37b9ffd00315c8aa7f376" then | ||
17 | error("MD5 sum of the file doesn't match") | ||
18 | end | ||
19 | |||
20 | local fw = hwp.load_file(arg[1]) | ||
21 | local jump_instr_addr = arm.to_thumb(hwp.make_addr(0x753C)) | ||
22 | local stub_addr = hwp.make_addr(0x137B0) | ||
23 | -- read old jump address | ||
24 | --local old_jump = arm.parse_branch(fw, jump_instr_addr) | ||
25 | --print(string.format("Old jump address: %s", old_jump)) | ||
26 | -- put stub at the right place | ||
27 | local stub = hwp.load_bin_file(arg[3]) | ||
28 | local stub_info = hwp.section_info(stub, "") | ||
29 | local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) | ||
30 | hwp.write(fw, stub_addr, stub_data) | ||
31 | -- patch jump | ||
32 | local branch_to_stub = arm.make_branch(arm.to_arm(stub_addr), true) | ||
33 | arm.write_branch(fw, jump_instr_addr, branch_to_stub, hwp.inc_addr(stub_addr, stub_info.size)) | ||
34 | -- read jump address | ||
35 | local new_jump = arm.parse_branch(fw, jump_instr_addr) | ||
36 | print(string.format("New jump address: %s", new_jump)) | ||
37 | -- save | ||
38 | hwp.save_file(fw, arg[2]) | ||
diff --git a/utils/hwpatcher/zen.lua b/utils/hwpatcher/zen.lua new file mode 100644 index 0000000000..8d1c8b69c3 --- /dev/null +++ b/utils/hwpatcher/zen.lua | |||
@@ -0,0 +1 @@ | |||
diff --git a/utils/hwpatcher/zxfi2.lua b/utils/hwpatcher/zxfi2.lua new file mode 100644 index 0000000000..fc3bdf800b --- /dev/null +++ b/utils/hwpatcher/zxfi2.lua | |||
@@ -0,0 +1,50 @@ | |||
1 | --[[ | ||
2 | Zen X-Fi2 1.23.01e NAND hacking | ||
3 | required argument (in order): | ||
4 | - path to firmware | ||
5 | - path to output firmware | ||
6 | - path to blob | ||
7 | - path to stub | ||
8 | ]]-- | ||
9 | |||
10 | if #arg < 4 then | ||
11 | error("not enough argument to fuzep patcher") | ||
12 | end | ||
13 | |||
14 | local fw = hwp.load_file(arg[1]) | ||
15 | local irq_addr_pool = hwp.make_addr(0x4035e154, "play") | ||
16 | local proxy_addr = arm.to_arm(hwp.make_addr(0x402f06f8, "play")) | ||
17 | -- read old IRQ address pool | ||
18 | local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) | ||
19 | print(string.format("Old IRQ address: %s", old_irq_addr)) | ||
20 | -- put stub at the beginning of the proxy | ||
21 | local stub = hwp.load_bin_file(arg[4]) | ||
22 | local stub_info = hwp.section_info(stub, "") | ||
23 | local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) | ||
24 | hwp.write(fw, proxy_addr, stub_data) | ||
25 | local stub_addr = proxy_addr | ||
26 | proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) | ||
27 | -- modify irq | ||
28 | hwp.write32(fw, irq_addr_pool, proxy_addr.addr) | ||
29 | print(string.format("New IRQ address: %s", proxy_addr)) | ||
30 | -- in proxy, save registers | ||
31 | arm.write_save_regs(fw, proxy_addr) | ||
32 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
33 | -- load blob | ||
34 | local blob = hwp.load_bin_file(arg[3]) | ||
35 | local blob_info = hwp.section_info(blob, "") | ||
36 | -- patch blob with stub address | ||
37 | hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) | ||
38 | -- write it ! | ||
39 | local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) | ||
40 | hwp.write(fw, proxy_addr, blob_data) | ||
41 | proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) | ||
42 | -- restore registers | ||
43 | arm.write_restore_regs(fw, proxy_addr) | ||
44 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
45 | -- branch to old code | ||
46 | local branch_to_old = arm.make_branch(old_irq_addr, false) | ||
47 | arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) | ||
48 | -- save | ||
49 | hwp.save_file(fw, arg[2]) | ||
50 | |||
diff --git a/utils/hwpatcher/zxfi3.lua b/utils/hwpatcher/zxfi3.lua new file mode 100644 index 0000000000..003f0f7482 --- /dev/null +++ b/utils/hwpatcher/zxfi3.lua | |||
@@ -0,0 +1,49 @@ | |||
1 | --[[ | ||
2 | Zen X-Fi3 1.00.25e hacking | ||
3 | required argument (in order): | ||
4 | - path to firmware | ||
5 | - path to output firmware | ||
6 | - path to blob | ||
7 | - path to stub | ||
8 | ]]-- | ||
9 | |||
10 | if #arg < 4 then | ||
11 | error("not enough argument to fuzep patcher") | ||
12 | end | ||
13 | |||
14 | local fw = hwp.load_file(arg[1]) | ||
15 | local irq_addr_pool = hwp.make_addr(0x405916f0) | ||
16 | local proxy_addr = arm.to_arm(hwp.make_addr(0x40384674)) | ||
17 | -- read old IRQ address pool | ||
18 | local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool)) | ||
19 | print(string.format("Old IRQ address: %s", old_irq_addr)) | ||
20 | -- put stub at the beginning of the proxy | ||
21 | local stub = hwp.load_bin_file(arg[4]) | ||
22 | local stub_info = hwp.section_info(stub, "") | ||
23 | local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size) | ||
24 | hwp.write(fw, proxy_addr, stub_data) | ||
25 | local stub_addr = proxy_addr | ||
26 | proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size) | ||
27 | -- modify irq | ||
28 | hwp.write32(fw, irq_addr_pool, proxy_addr.addr) | ||
29 | print(string.format("New IRQ address: %s", proxy_addr)) | ||
30 | -- in proxy, save registers | ||
31 | arm.write_save_regs(fw, proxy_addr) | ||
32 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
33 | -- load blob | ||
34 | local blob = hwp.load_bin_file(arg[3]) | ||
35 | local blob_info = hwp.section_info(blob, "") | ||
36 | -- patch blob with stub address | ||
37 | hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr) | ||
38 | -- write it ! | ||
39 | local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size) | ||
40 | hwp.write(fw, proxy_addr, blob_data) | ||
41 | proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size) | ||
42 | -- restore registers | ||
43 | arm.write_restore_regs(fw, proxy_addr) | ||
44 | proxy_addr = hwp.inc_addr(proxy_addr, 4) | ||
45 | -- branch to old code | ||
46 | local branch_to_old = arm.make_branch(old_irq_addr, false) | ||
47 | arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4)) | ||
48 | -- save | ||
49 | hwp.save_file(fw, arg[2]) | ||