summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/hwpatcher/Makefile27
-rw-r--r--utils/hwpatcher/Makefile.patch25
-rw-r--r--utils/hwpatcher/arm.lua205
-rw-r--r--utils/hwpatcher/creative.lua49
-rw-r--r--utils/hwpatcher/fuzep.lua49
-rw-r--r--utils/hwpatcher/fuzep_rb.lua38
-rw-r--r--utils/hwpatcher/generic_stmp.lua108
-rw-r--r--utils/hwpatcher/hwpatcher.c1123
-rw-r--r--utils/hwpatcher/lib.lua107
-rw-r--r--utils/hwpatcher/md5.c246
-rw-r--r--utils/hwpatcher/md5.h25
-rw-r--r--utils/hwpatcher/patch.S87
-rw-r--r--utils/hwpatcher/patch_viewbl.lua1
-rw-r--r--utils/hwpatcher/view.lua38
-rw-r--r--utils/hwpatcher/zen.lua1
-rw-r--r--utils/hwpatcher/zxfi2.lua50
-rw-r--r--utils/hwpatcher/zxfi3.lua49
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 @@
1CC=gcc
2CXX=g++
3LD=g++
4SBTOOLS_DIR=../imxtools/sbtools
5CFLAGS=-Wall -O3 -std=c99 -g -I$(SBTOOLS_DIR) `pkg-config --cflags lua5.2`
6LDFLAGS=`pkg-config --libs lua5.2` -L$(REGTOOLS_LIB_DIR) `xml2-config --libs` -lreadline
7EXEC=hwpatcher
8SBTOOLS_SRC=elf.c crypto.c sb.c sb1.c aes128.c crc.c misc.c sha1.c xorcrypt.c
9SBTOOLS_OBJ=$(SBTOOLS_SRC:.c=.o)
10SRC=$(wildcard *.c)
11OBJ=$(SRC:.c=.o)
12
13all: $(EXEC)
14
15%.o: $(SBTOOLS_DIR)/%.c
16 $(CC) $(CFLAGS) -c -o $@ $<
17
18%.o: $%.c
19 $(CC) $(CFLAGS) -c -o $@ $<
20
21hwpatcher: hwpatcher.o md5.o $(SBTOOLS_OBJ)
22 $(LD) -o $@ $^ $(LDFLAGS)
23
24clean:
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 @@
1PREFIX?=arm-elf-eabi-
2AS=$(PREFIX)gcc
3ASFLAGS=-nostdlib -ffreestanding -mcpu=arm926ej-s
4OC=$(PREFIX)objcopy
5TARGETS=fuzeplus zenxfi2 zenxfi3 zen nwz zenxfistyle
6OPT_fuzeplus=-DSANSA_FUZEPLUS
7OPT_zenxfi2=-DCREATIVE_ZENXFI2
8OPT_zenxfi3=-DCREATIVE_ZENXFI3
9OPT_zen=-DCREATIVE_ZEN
10OPT_nwz=-DSONY_NWZ
11OPT_zenxfistyle=-DCREATIVE_ZENXFISTYLE
12
13BOOTBINS=$(patsubst %, patch_%.bin, $(TARGETS))
14BOOTELF=$(patsubst %, patch_%.elf, $(TARGETS))
15
16all: $(BOOTBINS)
17
18patch_%.bin: patch_%.elf
19 $(OC) -O binary $^ $@
20
21patch_%.elf: patch.S
22 $(AS) $(ASFLAGS) $(OPT_$(@:patch_%.elf=%)) -o $@ $<
23
24clean:
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--[[
2hwpatcher arm decoding/encoding library
3
4]]--
5arm = {}
6
7-- determines whether an address is in Thumb code or not
8function arm.is_thumb(addr)
9 return bit32.extract(addr.addr, 0) == 1
10end
11
12-- translate address to real address (ie without Thumb bit)
13-- produces an error if address is not properly aligned in ARM mode
14function 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
22end
23
24-- switch between arm and thumb
25function arm.to_thumb(addr)
26 local res = hwp.deepcopy(addr)
27 res.addr = bit32.bor(addr.addr, 1)
28 return res
29end
30
31function arm.to_arm(addr)
32 return arm.xlate_addr(addr)
33end
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 !
38function arm.sign32(v)
39 if bit32.extract(v, 31) == 1 then
40 return -1 - bit32.bnot(v)
41 else
42 return v
43 end
44end
45function arm.sign_extend(val, bits)
46 return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits))
47end
48
49-- check that a signed value fits in some field
50function arm.check_sign_truncation(val, bits)
51 return val == arm.sign_extend(val, bits)
52end
53
54-- create a branch description
55function 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
62end
63
64-- parse a jump and returns its description
65function 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
99end
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
104function 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)
145end
146
147function 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)
168end
169
170-- generate the encoding of a "bx lr"
171function 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)
181end
182
183function 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)
197end
198
199function arm.write_save_regs(fw, addr)
200 return arm.write_xxx_regs(fw, addr, false)
201end
202
203function arm.write_restore_regs(fw, addr)
204 return arm.write_xxx_regs(fw, addr, true)
205end \ 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--[[
2Creative ZEN hacking
3required argument (in order):
4- path to firmware
5- path to output firmware
6- path to blob
7- path to stub
8]]--
9
10if #arg < 4 then
11 error("not enough argument to fuzep patcher")
12end
13
14local fw = hwp.load_file(arg[1])
15local irq_addr_pool = hwp.make_addr(0x38)
16local proxy_addr = arm.to_arm(hwp.make_addr(0x402519A0))
17-- read old IRQ address pool
18local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
19print(string.format("Old IRQ address: %s", old_irq_addr))
20-- put stub at the beginning of the proxy
21local stub = hwp.load_bin_file(arg[4])
22local stub_info = hwp.section_info(stub, "")
23local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
24hwp.write(fw, proxy_addr, stub_data)
25local stub_addr = proxy_addr
26proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
27-- modify irq
28hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
29print(string.format("New IRQ address: %s", proxy_addr))
30-- in proxy, save registers
31arm.write_save_regs(fw, proxy_addr)
32proxy_addr = hwp.inc_addr(proxy_addr, 4)
33-- load blob
34local blob = hwp.load_bin_file(arg[3])
35local blob_info = hwp.section_info(blob, "")
36-- patch blob with stub address
37hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
38-- write it !
39local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
40hwp.write(fw, proxy_addr, blob_data)
41proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
42-- restore registers
43arm.write_restore_regs(fw, proxy_addr)
44proxy_addr = hwp.inc_addr(proxy_addr, 4)
45-- branch to old code
46local branch_to_old = arm.make_branch(old_irq_addr, false)
47arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
48-- save
49hwp.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--[[
2Fuze+ 2.36.8 hacking
3required argument (in order):
4- path to firmware
5- path to output firmware
6- path to blob
7- path to stub
8]]--
9
10if #arg < 4 then
11 error("not enough argument to fuzep patcher")
12end
13
14local fw = hwp.load_file(arg[1])
15local irq_addr_pool = hwp.make_addr(0x41172d44)
16local proxy_addr = arm.to_arm(hwp.make_addr(0x40f35874))
17-- read old IRQ address pool
18local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
19print(string.format("Old IRQ address: %s", old_irq_addr))
20-- put stub at the beginning of the proxy
21local stub = hwp.load_bin_file(arg[4])
22local stub_info = hwp.section_info(stub, "")
23local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
24hwp.write(fw, proxy_addr, stub_data)
25local stub_addr = proxy_addr
26proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
27-- modify irq
28hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
29print(string.format("New IRQ address: %s", proxy_addr))
30-- in proxy, save registers
31arm.write_save_regs(fw, proxy_addr)
32proxy_addr = hwp.inc_addr(proxy_addr, 4)
33-- load blob
34local blob = hwp.load_bin_file(arg[3])
35local blob_info = hwp.section_info(blob, "")
36-- patch blob with stub address
37hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
38-- write it !
39local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
40hwp.write(fw, proxy_addr, blob_data)
41proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
42-- restore registers
43arm.write_restore_regs(fw, proxy_addr)
44proxy_addr = hwp.inc_addr(proxy_addr, 4)
45-- branch to old code
46local branch_to_old = arm.make_branch(old_irq_addr, false)
47arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
48-- save
49hwp.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--[[
2Fuze+ RB hacking
3required argument (in order):
4- path to firmware
5- path to output firmware
6- path to blob
7]]--
8
9if #arg < 3 then
10 error("not enough argument to fuzep patcher")
11end
12
13local fw = hwp.load_file(arg[1])
14local irq_addr_pool = hwp.make_addr(0x38)
15local proxy_addr = arm.to_arm(hwp.make_addr(0x60115ba4))
16-- read old IRQ address pool
17local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
18print(string.format("Old IRQ address: %s", old_irq_addr))
19-- modify it
20hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
21print(string.format("New IRQ address: %s", proxy_addr))
22-- in proxy, save registers
23arm.write_save_regs(fw, proxy_addr)
24proxy_addr = hwp.inc_addr(proxy_addr, 4)
25-- do some work
26local blob = hwp.load_bin_file(arg[3])
27local blob_info = hwp.section_info(blob, "")
28local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
29hwp.write(fw, proxy_addr, blob_data)
30proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
31-- restore registers
32arm.write_restore_regs(fw, proxy_addr)
33proxy_addr = hwp.inc_addr(proxy_addr, 4)
34-- branch to old code
35local branch_to_old = arm.make_branch(old_irq_addr, false)
36arm.write_branch(fw, proxy_addr, branch_to_old)
37-- save
38hwp.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--[[
2Generic STMP hacking
3required argument (in order):
4- path to firmware
5- path to output firmware
6- path to blob
7- path to stub
8]]--
9require("lib")
10require("arm")
11
12if #arg < 4 then
13 error("usage: <fw file> <out file> <blob> <stub>")
14end
15
16-- compute MD5
17print("Computing MD5 sum of the firmware...")
18local md5 = hwp.md5sum(arg[1])
19print("=> " .. hwp.md5str(md5))
20
21local 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
52local db_entry = md5_db[hwp.md5str(md5)]
53if db_entry == nil then
54 error("Cannot find device in the DB")
55 os.exit(1)
56end
57print("Model: " .. db_entry.model)
58
59local fw = hwp.load_file(arg[1])
60local irq_addr_pool = hwp.make_addr(db_entry.irq_addr_pool, db_entry.irq_addr_pool_sec)
61local proxy_addr = arm.to_arm(hwp.make_addr(db_entry.proxy_addr, db_entry.proxy_addr_sec))
62-- read old IRQ address pool
63local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
64print(string.format("Old IRQ address: %s", old_irq_addr))
65-- put stub at the beginning of the proxy
66local stub = hwp.load_bin_file(arg[4])
67local stub_info = hwp.section_info(stub, "")
68local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
69local stub_addr = nil
70local stub_addr_virt = nil
71if 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)
79else
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)
84end
85-- modify irq
86hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
87print(string.format("New IRQ address: %s", proxy_addr))
88-- in proxy, save registers
89arm.write_save_regs(fw, proxy_addr)
90proxy_addr = hwp.inc_addr(proxy_addr, 4)
91-- load blob
92local blob = hwp.load_bin_file(arg[3])
93local blob_info = hwp.section_info(blob, "")
94-- patch blob with stub address
95hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr_virt.addr)
96-- write it !
97local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
98hwp.write(fw, proxy_addr, blob_data)
99proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
100-- restore registers
101arm.write_restore_regs(fw, proxy_addr)
102proxy_addr = hwp.inc_addr(proxy_addr, 4)
103-- branch to old code
104local branch_to_old = arm.make_branch(old_irq_addr, false)
105arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
106-- save
107hwp.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
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}
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--[[
2hwpatcher library
3
4The C code provides the following functions.
5
6At global level:
7- quit() Quit the interactive mode
8- exit() Same as quit()
9
10In 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
22Data read/written from/to a firmware must must be an array of bytes.
23The address must be a table of the following fields:
24- address: contain the address
25- section: optional section name
26Data section information is a table with the following fields:
27- address: first address if the section
28- size: size of the section
29We provide the following functions to help dealing with addresses:
30- make_addr
31
32]]--
33
34function 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
52end
53
54function 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
65end
66
67function hwp.inc_addr(addr, amount)
68 return hwp.make_addr(addr.addr + amount, addr.section)
69end
70
71-- pack an array of bytes in a integer (little-endian)
72function 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
78end
79
80-- do the converse
81function 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
88end
89
90-- read a 32-bit value
91function hwp.read32(obj, addr)
92 return hwp.pack(hwp.read(obj, addr, 4))
93end
94
95-- write a 32-bit value
96function hwp.write32(obj, addr, v)
97 return hwp.write(obj, addr, hwp.unpack(v, 4))
98end
99
100-- convert a MD5 hash to a string
101function 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
107end
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
41void 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
52void 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
175void 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
214static 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
222void 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
12typedef struct
13{
14 uint32 total[2];
15 uint32 state[4];
16 uint8 buffer[64];
17}
18md5_context;
19
20void md5_starts( md5_context *ctx );
21void md5_update( md5_context *ctx, uint8 *input, uint32 length );
22void 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
5branch_addr:
6 .word kill
7hw_power_sts:
8#if defined(CREATIVE_ZEN)
9 .word 0x800440b0 /* STMP3700 */
10#else
11 .word 0x800440c0 /* IMX233 */
12#endif
13hw_pinctrl_din0:
14 .word 0x80018600
15hw_pinctrl_din1:
16 .word 0x80018610
17hw_pinctrl_din2:
18 .word 0x80018620
19kill:
20 ldr pc, branch_addr
21exec:
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
87ret: \ 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--[[
2Sansa View bootloader hacking
3required argument (in order):
4- path to bootloader
5- path to output bootloader
6- path to stub
7]]--
8require("lib")
9require("arm")
10
11if #arg < 3 then
12 error("not enough argument to fuzep patcher")
13end
14
15local md5 = hwp.md5sum(arg[1])
16if hwp.md5str(md5) ~= "4bc1760327c37b9ffd00315c8aa7f376" then
17 error("MD5 sum of the file doesn't match")
18end
19
20local fw = hwp.load_file(arg[1])
21local jump_instr_addr = arm.to_thumb(hwp.make_addr(0x753C))
22local 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
27local stub = hwp.load_bin_file(arg[3])
28local stub_info = hwp.section_info(stub, "")
29local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
30hwp.write(fw, stub_addr, stub_data)
31-- patch jump
32local branch_to_stub = arm.make_branch(arm.to_arm(stub_addr), true)
33arm.write_branch(fw, jump_instr_addr, branch_to_stub, hwp.inc_addr(stub_addr, stub_info.size))
34-- read jump address
35local new_jump = arm.parse_branch(fw, jump_instr_addr)
36print(string.format("New jump address: %s", new_jump))
37-- save
38hwp.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--[[
2Zen X-Fi2 1.23.01e NAND hacking
3required argument (in order):
4- path to firmware
5- path to output firmware
6- path to blob
7- path to stub
8]]--
9
10if #arg < 4 then
11 error("not enough argument to fuzep patcher")
12end
13
14local fw = hwp.load_file(arg[1])
15local irq_addr_pool = hwp.make_addr(0x4035e154, "play")
16local proxy_addr = arm.to_arm(hwp.make_addr(0x402f06f8, "play"))
17-- read old IRQ address pool
18local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
19print(string.format("Old IRQ address: %s", old_irq_addr))
20-- put stub at the beginning of the proxy
21local stub = hwp.load_bin_file(arg[4])
22local stub_info = hwp.section_info(stub, "")
23local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
24hwp.write(fw, proxy_addr, stub_data)
25local stub_addr = proxy_addr
26proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
27-- modify irq
28hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
29print(string.format("New IRQ address: %s", proxy_addr))
30-- in proxy, save registers
31arm.write_save_regs(fw, proxy_addr)
32proxy_addr = hwp.inc_addr(proxy_addr, 4)
33-- load blob
34local blob = hwp.load_bin_file(arg[3])
35local blob_info = hwp.section_info(blob, "")
36-- patch blob with stub address
37hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
38-- write it !
39local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
40hwp.write(fw, proxy_addr, blob_data)
41proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
42-- restore registers
43arm.write_restore_regs(fw, proxy_addr)
44proxy_addr = hwp.inc_addr(proxy_addr, 4)
45-- branch to old code
46local branch_to_old = arm.make_branch(old_irq_addr, false)
47arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
48-- save
49hwp.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--[[
2Zen X-Fi3 1.00.25e hacking
3required argument (in order):
4- path to firmware
5- path to output firmware
6- path to blob
7- path to stub
8]]--
9
10if #arg < 4 then
11 error("not enough argument to fuzep patcher")
12end
13
14local fw = hwp.load_file(arg[1])
15local irq_addr_pool = hwp.make_addr(0x405916f0)
16local proxy_addr = arm.to_arm(hwp.make_addr(0x40384674))
17-- read old IRQ address pool
18local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
19print(string.format("Old IRQ address: %s", old_irq_addr))
20-- put stub at the beginning of the proxy
21local stub = hwp.load_bin_file(arg[4])
22local stub_info = hwp.section_info(stub, "")
23local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
24hwp.write(fw, proxy_addr, stub_data)
25local stub_addr = proxy_addr
26proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
27-- modify irq
28hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
29print(string.format("New IRQ address: %s", proxy_addr))
30-- in proxy, save registers
31arm.write_save_regs(fw, proxy_addr)
32proxy_addr = hwp.inc_addr(proxy_addr, 4)
33-- load blob
34local blob = hwp.load_bin_file(arg[3])
35local blob_info = hwp.section_info(blob, "")
36-- patch blob with stub address
37hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
38-- write it !
39local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
40hwp.write(fw, proxy_addr, blob_data)
41proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
42-- restore registers
43arm.write_restore_regs(fw, proxy_addr)
44proxy_addr = hwp.inc_addr(proxy_addr, 4)
45-- branch to old code
46local branch_to_old = arm.make_branch(old_irq_addr, false)
47arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
48-- save
49hwp.save_file(fw, arg[2])