summaryrefslogtreecommitdiffstats
path: root/utils/hwpatcher
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2014-06-24 18:04:17 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2014-06-24 18:07:56 +0200
commitc9a028cc183d638c16ca9a8858b783b1830be16f (patch)
tree1bd29843ec0b8d3eb80a6209d1c4e32144175dfe /utils/hwpatcher
parent761f59c9e3be0ffd77d2dc1b8095a3b877badeda (diff)
downloadrockbox-c9a028cc183d638c16ca9a8858b783b1830be16f.tar.gz
rockbox-c9a028cc183d638c16ca9a8858b783b1830be16f.tar.bz2
rockbox-c9a028cc183d638c16ca9a8858b783b1830be16f.zip
Introduce hwpatcher, a tool to patch binaries
This tool is a scriptable (lua) tool to patch binaries, it supports: - raw binary - ELF - SB(v1/v2) It also contains some basic routines to parse and generate useful arm/thumb code like jump or register load/store. This is very useful to take a firmware and patch an interrupt vector or some code to jump to an extra payload added to the binary. Examples are provided for several STMP based target which the payload is expected to be hwstub, and also for the Sansa View. A typical patcher usually requires three elements: - the lua patcher itself - the payload (hwstub for example) - (optional) a small stub either to jump properly to the payload or determine under which circumstance to do the jump (hold a key for example) Change-Id: I6d36020a3bc9e636615ac8221b7591ade5f251e3
Diffstat (limited to 'utils/hwpatcher')
-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 @@
+CC=gcc
+CXX=g++
+LD=g++
+SBTOOLS_DIR=../imxtools/sbtools
+CFLAGS=-Wall -O3 -std=c99 -g -I$(SBTOOLS_DIR) `pkg-config --cflags lua5.2`
+LDFLAGS=`pkg-config --libs lua5.2` -L$(REGTOOLS_LIB_DIR) `xml2-config --libs` -lreadline
+EXEC=hwpatcher
+SBTOOLS_SRC=elf.c crypto.c sb.c sb1.c aes128.c crc.c misc.c sha1.c xorcrypt.c
+SBTOOLS_OBJ=$(SBTOOLS_SRC:.c=.o)
+SRC=$(wildcard *.c)
+OBJ=$(SRC:.c=.o)
+
+all: $(EXEC)
+
+%.o: $(SBTOOLS_DIR)/%.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+%.o: $%.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+hwpatcher: hwpatcher.o md5.o $(SBTOOLS_OBJ)
+ $(LD) -o $@ $^ $(LDFLAGS)
+
+clean:
+ rm -rf $(SBTOOLS_OBJ) $(OBJ) $(EXEC)
+
+
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 @@
+PREFIX?=arm-elf-eabi-
+AS=$(PREFIX)gcc
+ASFLAGS=-nostdlib -ffreestanding -mcpu=arm926ej-s
+OC=$(PREFIX)objcopy
+TARGETS=fuzeplus zenxfi2 zenxfi3 zen nwz zenxfistyle
+OPT_fuzeplus=-DSANSA_FUZEPLUS
+OPT_zenxfi2=-DCREATIVE_ZENXFI2
+OPT_zenxfi3=-DCREATIVE_ZENXFI3
+OPT_zen=-DCREATIVE_ZEN
+OPT_nwz=-DSONY_NWZ
+OPT_zenxfistyle=-DCREATIVE_ZENXFISTYLE
+
+BOOTBINS=$(patsubst %, patch_%.bin, $(TARGETS))
+BOOTELF=$(patsubst %, patch_%.elf, $(TARGETS))
+
+all: $(BOOTBINS)
+
+patch_%.bin: patch_%.elf
+ $(OC) -O binary $^ $@
+
+patch_%.elf: patch.S
+ $(AS) $(ASFLAGS) $(OPT_$(@:patch_%.elf=%)) -o $@ $<
+
+clean:
+ 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 @@
+--[[
+hwpatcher arm decoding/encoding library
+
+]]--
+arm = {}
+
+-- determines whether an address is in Thumb code or not
+function arm.is_thumb(addr)
+ return bit32.extract(addr.addr, 0) == 1
+end
+
+-- translate address to real address (ie without Thumb bit)
+-- produces an error if address is not properly aligned in ARM mode
+function arm.xlate_addr(addr)
+ local res = hwp.deepcopy(addr)
+ if arm.is_thumb(addr) then
+ res.addr = bit32.replace(addr.addr, 0, 0)
+ elseif bit32.extract(addr.addr, 0, 2) ~= 0 then
+ error("ARM address is not word-aligned")
+ end
+ return res
+end
+
+-- switch between arm and thumb
+function arm.to_thumb(addr)
+ local res = hwp.deepcopy(addr)
+ res.addr = bit32.bor(addr.addr, 1)
+ return res
+end
+
+function arm.to_arm(addr)
+ return arm.xlate_addr(addr)
+end
+
+-- sign extend a value to 32-bits
+-- only the lower 'bits' bits are considered, everything else is trashed
+-- watch out arithmetic vs logical shift !
+function arm.sign32(v)
+ if bit32.extract(v, 31) == 1 then
+ return -1 - bit32.bnot(v)
+ else
+ return v
+ end
+end
+function arm.sign_extend(val, bits)
+ return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits))
+end
+
+-- check that a signed value fits in some field
+function arm.check_sign_truncation(val, bits)
+ return val == arm.sign_extend(val, bits)
+end
+
+-- create a branch description
+function arm.make_branch(addr, link)
+ local t = {type = "branch", addr = addr, link = link}
+ local branch_to_string = function(self)
+ return string.format("branch(%s,%s)", self.addr, self.link)
+ end
+ setmetatable(t, {__tostring = branch_to_string})
+ return t
+end
+
+-- parse a jump and returns its description
+function arm.parse_branch(fw, addr)
+ local opcode = hwp.read32(fw, arm.xlate_addr(addr))
+ if arm.is_thumb(addr) then
+ if bit32.band(opcode, 0xf800) ~= 0xf000 then
+ error("first instruction is not a bl(x) prefix")
+ end
+ local to_thumb = false
+ if bit32.band(opcode, 0xf8000000) == 0xf8000000 then
+ to_thumb = true
+ elseif bit32.band(opcode, 0xf8000000) ~= 0xe8000000 then
+ error("second instruction is not a bl(x) suffix")
+ end
+ local dest = hwp.make_addr(bit32.lshift(arm.sign_extend(opcode, 11), 12) +
+ arm.xlate_addr(addr).addr + 4 +
+ bit32.rshift(bit32.band(opcode, 0x7ff0000), 16) * 2, addr.section)
+ if to_thumb then
+ dest = arm.to_thumb(dest)
+ else
+ dest.addr = bit32.replace(dest.addr, 0, 0, 2)
+ end
+ return arm.make_branch(dest, true)
+ else
+ if bit32.band(opcode, 0xfe000000) == 0xfa000000 then -- BLX
+ local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
+ bit32.extract(opcode, 24) * 2 + arm.xlate_addr(addr).addr, addr.section)
+ return arm.make_branch(arm.to_thumb(dest), true)
+ elseif bit32.band(opcode, 0xfe000000) == 0xea000000 then -- B(L)
+ local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
+ arm.xlate_addr(addr).addr, addr.section)
+ return arm.make_branch(arm.to_arm(dest), bit32.extract(opcode, 24))
+ else
+ error("instruction is not a valid branch")
+ end
+ end
+end
+
+-- generate the encoding of a branch
+-- if the branch cannot be encoded using an immediate branch, it is generated
+-- with an indirect form like a ldr, using a pool
+function arm.write_branch(fw, addr, dest, pool)
+ local offset = arm.xlate_addr(dest.addr).addr - arm.xlate_addr(addr).addr
+ local exchange = arm.is_thumb(addr) ~= arm.is_thumb(dest.addr)
+ local opcode = 0
+ if arm.is_thumb(addr) then
+ if not dest.link then
+ return arm.write_load_pc(fw, addr, dest, pool)
+ end
+ offset = offset - 4 -- in Thumb, PC+4 relative
+ -- NOTE: BLX is undefined if the resulting offset has bit 0 set, follow
+ -- the procedure from the manual to ensure correct operation
+ if bit32.extract(offset, 1) ~= 0 then
+ offset = offset + 2
+ end
+ offset = offset / 2
+ if not arm.check_sign_truncation(offset, 22) then
+ error("destination is too far for immediate branch from thumb")
+ end
+ opcode = 0xf000 + -- BL/BLX prefix
+ bit32.band(bit32.rshift(offset, 11), 0x7ff) + -- offset (high part)
+ bit32.lshift(exchange and 0xe800 or 0xf800,16) + -- BLX suffix
+ bit32.lshift(bit32.band(offset, 0x7ff), 16) -- offset (low part)
+ else
+ offset = offset - 8 -- in ARM, PC+8 relative
+ if exchange and not dest.link then
+ return arm.write_load_pc(fw, addr, dest, pool)
+ end
+ offset = offset / 4
+ if not arm.check_sign_truncation(offset, 24) then
+ if pool == nil then
+ error("destination is too far for immediate branch from arm (no pool available)")
+ else
+ return arm.write_load_pc(fw, addr, dest, pool)
+ end
+ end
+ opcode = bit32.lshift(exchange and 0xf or 0xe, 28) + -- BLX / AL cond
+ bit32.lshift(0xa, 24) + -- branch
+ bit32.lshift(exchange and bit32.extract(offset, 1) or dest.link and 1 or 0, 24) + -- link / bit1
+ bit32.band(offset, 0xffffff)
+ end
+ return hwp.write32(fw, arm.xlate_addr(addr), opcode)
+end
+
+function arm.write_load_pc(fw, addr, dest, pool)
+ -- write pool
+ hwp.write32(fw, pool, dest.addr.addr)
+ -- write "ldr pc, [pool]"
+ local opcode
+ if arm.is_thumb(addr) then
+ error("unsupported pc load in thumb mode")
+ else
+ local offset = pool.addr - arm.xlate_addr(addr).addr - 8 -- ARM is PC+8 relative
+ local add = offset >= 0 and 1 or 0
+ offset = math.abs(offset)
+ opcode = bit32.lshift(0xe, 28) + -- AL cond
+ bit32.lshift(1, 26) + -- ldr/str
+ bit32.lshift(1, 24) + -- P
+ bit32.lshift(add, 23) + -- U
+ bit32.lshift(1, 20) + -- ldr
+ bit32.lshift(15, 16) + -- Rn=PC
+ bit32.lshift(15, 12) + -- Rd=PC
+ bit32.band(offset, 0xfff)
+ end
+ return hwp.write32(fw, arm.xlate_addr(addr), opcode)
+end
+
+-- generate the encoding of a "bx lr"
+function arm.write_return(fw, addr)
+ if arm.is_thumb(addr) then
+ error("unsupported return from thumb code")
+ end
+ local opcode = bit32.lshift(0xe, 28) + -- AL cond
+ bit32.lshift(0x12, 20) + -- BX
+ bit32.lshift(1, 4) + -- BX
+ bit32.lshift(0xfff, 8) + -- SBO
+ 14 -- LR
+ hwp.write32(fw, arm.xlate_addr(addr), opcode)
+end
+
+function arm.write_xxx_regs(fw, addr, load)
+ if arm.is_thumb(addr) then
+ error("unsupported save/restore regs from thumb code")
+ end
+ -- STMFD sp!,{r0-r12, lr}
+ local opcode = bit32.lshift(0xe, 28) + -- AL cond
+ bit32.lshift(0x4, 25) + -- STM/LDM
+ bit32.lshift(load and 0 or 1,24) + -- P
+ bit32.lshift(load and 1 or 0, 23) + -- U
+ bit32.lshift(1, 21) +-- W
+ bit32.lshift(load and 1 or 0, 20) + -- L
+ bit32.lshift(13, 16) + -- base = SP
+ 0x5fff -- R0-R12,LR
+ return hwp.write32(fw, addr, opcode)
+end
+
+function arm.write_save_regs(fw, addr)
+ return arm.write_xxx_regs(fw, addr, false)
+end
+
+function arm.write_restore_regs(fw, addr)
+ return arm.write_xxx_regs(fw, addr, true)
+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 @@
+--[[
+Creative ZEN hacking
+required argument (in order):
+- path to firmware
+- path to output firmware
+- path to blob
+- path to stub
+]]--
+
+if #arg < 4 then
+ error("not enough argument to fuzep patcher")
+end
+
+local fw = hwp.load_file(arg[1])
+local irq_addr_pool = hwp.make_addr(0x38)
+local proxy_addr = arm.to_arm(hwp.make_addr(0x402519A0))
+-- read old IRQ address pool
+local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
+print(string.format("Old IRQ address: %s", old_irq_addr))
+-- put stub at the beginning of the proxy
+local stub = hwp.load_bin_file(arg[4])
+local stub_info = hwp.section_info(stub, "")
+local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
+hwp.write(fw, proxy_addr, stub_data)
+local stub_addr = proxy_addr
+proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
+-- modify irq
+hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
+print(string.format("New IRQ address: %s", proxy_addr))
+-- in proxy, save registers
+arm.write_save_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- load blob
+local blob = hwp.load_bin_file(arg[3])
+local blob_info = hwp.section_info(blob, "")
+-- patch blob with stub address
+hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
+-- write it !
+local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
+hwp.write(fw, proxy_addr, blob_data)
+proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
+-- restore registers
+arm.write_restore_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- branch to old code
+local branch_to_old = arm.make_branch(old_irq_addr, false)
+arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
+-- save
+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 @@
+--[[
+Fuze+ 2.36.8 hacking
+required argument (in order):
+- path to firmware
+- path to output firmware
+- path to blob
+- path to stub
+]]--
+
+if #arg < 4 then
+ error("not enough argument to fuzep patcher")
+end
+
+local fw = hwp.load_file(arg[1])
+local irq_addr_pool = hwp.make_addr(0x41172d44)
+local proxy_addr = arm.to_arm(hwp.make_addr(0x40f35874))
+-- read old IRQ address pool
+local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
+print(string.format("Old IRQ address: %s", old_irq_addr))
+-- put stub at the beginning of the proxy
+local stub = hwp.load_bin_file(arg[4])
+local stub_info = hwp.section_info(stub, "")
+local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
+hwp.write(fw, proxy_addr, stub_data)
+local stub_addr = proxy_addr
+proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
+-- modify irq
+hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
+print(string.format("New IRQ address: %s", proxy_addr))
+-- in proxy, save registers
+arm.write_save_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- load blob
+local blob = hwp.load_bin_file(arg[3])
+local blob_info = hwp.section_info(blob, "")
+-- patch blob with stub address
+hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
+-- write it !
+local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
+hwp.write(fw, proxy_addr, blob_data)
+proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
+-- restore registers
+arm.write_restore_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- branch to old code
+local branch_to_old = arm.make_branch(old_irq_addr, false)
+arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
+-- save
+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 @@
+--[[
+Fuze+ RB hacking
+required argument (in order):
+- path to firmware
+- path to output firmware
+- path to blob
+]]--
+
+if #arg < 3 then
+ error("not enough argument to fuzep patcher")
+end
+
+local fw = hwp.load_file(arg[1])
+local irq_addr_pool = hwp.make_addr(0x38)
+local proxy_addr = arm.to_arm(hwp.make_addr(0x60115ba4))
+-- read old IRQ address pool
+local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
+print(string.format("Old IRQ address: %s", old_irq_addr))
+-- modify it
+hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
+print(string.format("New IRQ address: %s", proxy_addr))
+-- in proxy, save registers
+arm.write_save_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- do some work
+local blob = hwp.load_bin_file(arg[3])
+local blob_info = hwp.section_info(blob, "")
+local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
+hwp.write(fw, proxy_addr, blob_data)
+proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
+-- restore registers
+arm.write_restore_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- branch to old code
+local branch_to_old = arm.make_branch(old_irq_addr, false)
+arm.write_branch(fw, proxy_addr, branch_to_old)
+-- save
+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 @@
+--[[
+Generic STMP hacking
+required argument (in order):
+- path to firmware
+- path to output firmware
+- path to blob
+- path to stub
+]]--
+require("lib")
+require("arm")
+
+if #arg < 4 then
+ error("usage: <fw file> <out file> <blob> <stub>")
+end
+
+-- compute MD5
+print("Computing MD5 sum of the firmware...")
+local md5 = hwp.md5sum(arg[1])
+print("=> " .. hwp.md5str(md5))
+
+local md5_db =
+{
+ ["d0047f8a87d456a0032297b3c802a1ff"] =
+ {
+ model = "Sony NWZ-E3600 1.0.0",
+ irq_addr_pool = 0x40A314E4,
+ irq_addr_pool_sec = "play.1",
+ -- proxy_addr = 0x4005C1E0,
+ -- proxy_addr_sec = "play.1"
+ proxy_addr = 0x4007C258,
+ proxy_addr_sec = "play.1",
+ -- stub_addr = 0x1971C8,
+ -- stub_addr_virt = 0x2971C8,
+ -- stub_addr_sec = "pvmi",
+ },
+ ["f42742d4d90d88e2fb6ff468c1389f5f"] =
+ {
+ model = "Creative ZEN X-Fi Style 1.03.04",
+ irq_addr_pool = 0x402D3A64,
+ irq_addr_pool_sec = "play.1",
+ proxy_addr = 0x402E076C,
+ proxy_addr_sec = "play.1"
+ },
+ ["c180f57e2b2d62620f87a1d853f349ff"] =
+ {
+ model = "Creative ZEN X-Fi3 1.00.25e",
+ irq_addr_pool = 0x405916f0,
+ proxy_addr = 0x40384674,
+ }
+}
+
+local db_entry = md5_db[hwp.md5str(md5)]
+if db_entry == nil then
+ error("Cannot find device in the DB")
+ os.exit(1)
+end
+print("Model: " .. db_entry.model)
+
+local fw = hwp.load_file(arg[1])
+local irq_addr_pool = hwp.make_addr(db_entry.irq_addr_pool, db_entry.irq_addr_pool_sec)
+local proxy_addr = arm.to_arm(hwp.make_addr(db_entry.proxy_addr, db_entry.proxy_addr_sec))
+-- read old IRQ address pool
+local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
+print(string.format("Old IRQ address: %s", old_irq_addr))
+-- put stub at the beginning of the proxy
+local stub = hwp.load_bin_file(arg[4])
+local stub_info = hwp.section_info(stub, "")
+local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
+local stub_addr = nil
+local stub_addr_virt = nil
+if db_entry.stub_addr ~= nil then
+ stub_addr = arm.to_arm(hwp.make_addr(db_entry.stub_addr, db_entry.stub_addr_sec))
+ if db_entry.stub_addr_virt ~= nil then
+ stub_addr_virt = arm.to_arm(hwp.make_addr(db_entry.stub_addr_virt, db_entry.stub_addr_sec))
+ else
+ stub_addr_virt = stub_addr
+ end
+ hwp.write(fw, stub_addr, stub_data)
+else
+ stub_addr = proxy_addr
+ stub_addr_virt = stub_addr
+ hwp.write(fw, stub_addr, stub_data)
+ proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
+end
+-- modify irq
+hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
+print(string.format("New IRQ address: %s", proxy_addr))
+-- in proxy, save registers
+arm.write_save_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- load blob
+local blob = hwp.load_bin_file(arg[3])
+local blob_info = hwp.section_info(blob, "")
+-- patch blob with stub address
+hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr_virt.addr)
+-- write it !
+local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
+hwp.write(fw, proxy_addr, blob_data)
+proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
+-- restore registers
+arm.write_restore_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- branch to old code
+local branch_to_old = arm.make_branch(old_irq_addr, false)
+arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
+-- save
+hwp.save_file(fw, arg[2])
+
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2013 Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#define _ISOC99_SOURCE /* snprintf() */
+#define _POSIX_C_SOURCE 200809L /* for strdup */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+#include <strings.h>
+#include <getopt.h>
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#if LUA_VERSION_NUM < 502
+#warning You need at least lua 5.2
+#endif
+
+#include "crypto.h"
+#include "elf.h"
+#include "sb.h"
+#include "sb1.h"
+#include "misc.h"
+#include "md5.h"
+
+lua_State *g_lua;
+bool g_exit = false;
+
+/**
+ * FW object library
+ */
+
+enum fw_type_t
+{
+ FW_UNK, FW_ELF, FW_SB1, FW_SB2, FW_BIN, FW_EDOC
+};
+
+struct bin_file_t
+{
+ size_t size;
+ void *data;
+};
+
+struct edoc_section_t
+{
+ uint32_t addr;
+ size_t size;
+ void *data;
+};
+
+struct edoc_file_t
+{
+ int nr_sections;
+ struct edoc_section_t *sections;
+};
+
+struct edoc_header_t
+{
+ char magic[4];
+ uint32_t total_size;
+ uint32_t zero;
+} __attribute__((packed));
+
+struct edoc_section_header_t
+{
+ uint32_t addr;
+ uint32_t size;
+ uint32_t checksum;
+} __attribute__((packed));
+
+uint32_t edoc_checksum(void *buffer, size_t size)
+{
+ uint32_t c = 0;
+ uint32_t *p = buffer;
+ while(size >= 4)
+ {
+ c += *p + (*p >> 16);
+ p++;
+ size -= 4;
+ }
+ if(size != 0)
+ printf("[Checksum section size is not a multiple of 4 bytes !]\n");
+ return c & 0xffff;
+}
+
+#define FWOBJ_MAGIC ('F' | 'W' << 8 | 'O' << 16 | 'B' << 24)
+
+struct fw_object_t
+{
+ uint32_t magic;
+ enum fw_type_t type;
+ union
+ {
+ struct sb_file_t *sb;
+ struct sb1_file_t *sb1;
+ struct elf_params_t *elf;
+ struct bin_file_t *bin;
+ struct edoc_file_t *edoc;
+ }u;
+};
+
+typedef struct fw_addr_t
+{
+ uint32_t addr;
+ const char *section;
+}fw_addr_t;
+
+#define INVALID_FW_ADDR ((struct fw_addr_t){.addr = (uint32_t)-1, .section = ""})
+#define IS_VALID_FW_ADDR(x) !(x.addr == (uint32_t)-1 && x.section && strlen(x.section) == 0)
+
+typedef struct fw_sym_addr_t
+{
+ const char *name;
+ const char *section;
+}fw_sym_addr_t;
+
+struct fw_section_info_t
+{
+ uint32_t addr;
+ uint32_t size;
+};
+
+static inline struct fw_addr_t make_addr(uint32_t addr, const char *section)
+{
+ return (struct fw_addr_t){.addr = addr, .section = section};
+}
+
+static enum fw_type_t fw_guess(const char *filename)
+{
+ enum sb_version_guess_t ver = guess_sb_version(filename);
+ if(ver == SB_VERSION_ERR)
+ {
+ printf("Cannot open/read SB file: %m\n");
+ return FW_UNK;
+ }
+ if(ver == SB_VERSION_1) return FW_SB1;
+ if(ver == SB_VERSION_2) return FW_SB2;
+ FILE *fd = fopen(filename, "rb");
+ if(fd == NULL)
+ {
+ printf("Cannot open '%s' for reading: %m\n", filename);
+ return FW_UNK;
+ }
+ uint8_t sig[4];
+ if(fread(sig, 4, 1, fd) == 1)
+ {
+ if(sig[0] == 'E' && sig[1] == 'D' && sig[2] == 'O' && sig[3] == 'C')
+ return FW_EDOC;
+ }
+ bool is_elf = elf_guess(elf_std_read, fd);
+ fclose(fd);
+ return is_elf ? FW_ELF : FW_BIN;
+}
+
+static const char *fw_type_name(enum fw_type_t t)
+{
+ switch(t)
+ {
+ case FW_SB1: return "SB1";
+ case FW_SB2: return "SB2";
+ case FW_ELF: return "ELF";
+ case FW_BIN: return "binary";
+ case FW_EDOC: return "EDOC";
+ default: return "<unk>";
+ }
+}
+
+static struct fw_object_t *fw_load(const char *filename, enum fw_type_t force)
+{
+ if(force == FW_UNK)
+ force = fw_guess(filename);
+ if(force == FW_UNK)
+ {
+ printf("Cannot guess type of file: %m. This probably indicates the file cannot be read.\n");
+ return NULL;
+ }
+
+ struct fw_object_t *obj = malloc(sizeof(struct fw_object_t));
+ memset(obj, 0, sizeof(struct fw_object_t));
+ obj->magic = FWOBJ_MAGIC;
+
+ printf("Loading '%s' as %s file...\n", filename, fw_type_name(force));
+ color(OFF);
+ if(force == FW_SB2)
+ {
+ enum sb_error_t err;
+ obj->type = FW_SB2;
+ obj->u.sb = sb_read_file(filename, false, NULL, generic_std_printf, &err);
+ if(obj->u.sb == NULL)
+ {
+ printf("SB read failed: %d\n", err);
+ goto Lerr;
+ }
+ return obj;
+ }
+ else if(force == FW_SB1)
+ {
+ enum sb1_error_t err;
+ obj->type = FW_SB1;
+ obj->u.sb1 = sb1_read_file(filename, NULL, generic_std_printf, &err);
+ if(obj->u.sb1 == NULL)
+ {
+ printf("SB1 read failed: %d\n", err);
+ goto Lerr;
+ }
+ return obj;
+ }
+ else if(force == FW_ELF)
+ {
+ FILE *fd = fopen(filename, "rb");
+ if(fd == NULL)
+ {
+ printf("cannot open '%s' for reading: %m\n", filename);
+ goto Lerr;
+ }
+ obj->type = FW_ELF;
+ obj->u.elf = malloc(sizeof(struct elf_params_t));
+ elf_init(obj->u.elf);
+ bool loaded = elf_read_file(obj->u.elf, elf_std_read, generic_std_printf, fd);
+ fclose(fd);
+ if(!loaded)
+ {
+ printf("error loading elf file '%s'\n", filename);
+ free(obj->u.elf);
+ goto Lerr;
+ }
+ return obj;
+ }
+ else if(force == FW_EDOC)
+ {
+ FILE *fd = fopen(filename, "rb");
+ if(fd == NULL)
+ {
+ printf("cannot open '%s' for reading: %m\n", filename);
+ goto Lerr;
+ }
+ struct edoc_header_t hdr;
+ if(fread(&hdr, sizeof(hdr), 1, fd) != 1)
+ {
+ printf("cannot read EDOC header: %m\n");
+ goto Lerr;
+ }
+ if(strncmp(hdr.magic, "EDOC", 4) != 0)
+ {
+ printf("EDOC signature mismatch\n");
+ goto Lerr;
+ }
+ struct edoc_file_t *file = xmalloc(sizeof(struct edoc_file_t));
+ memset(file, 0, sizeof(struct edoc_file_t));
+ for(size_t pos = sizeof(hdr); pos < hdr.total_size + 8;)
+ {
+ struct edoc_section_header_t shdr;
+ if(fread(&shdr, sizeof(shdr), 1, fd) != 1)
+ {
+ printf("cannot read EDOC section header: %m\n");
+ goto Lerr;
+ }
+ file->sections = realloc(file->sections, (file->nr_sections + 1) *
+ sizeof(struct edoc_section_t));
+ file->sections[file->nr_sections].addr = shdr.addr;
+ file->sections[file->nr_sections].size = shdr.size;
+ file->sections[file->nr_sections].data = xmalloc(shdr.size);
+ if(fread(file->sections[file->nr_sections].data, shdr.size, 1, fd) != 1)
+ {
+ printf("cannot read EDOC section: %m\n");
+ goto Lerr;
+ }
+ if(edoc_checksum(file->sections[file->nr_sections].data, shdr.size) != shdr.checksum)
+ {
+ printf("EDOC section checksum mismatch\n");
+ goto Lerr;
+ }
+ file->nr_sections++;
+ pos += sizeof(shdr) + shdr.size;
+ }
+ fclose(fd);
+ obj->type = FW_EDOC;
+ obj->u.edoc = file;
+ return obj;
+ }
+ else
+ {
+ FILE *fd = fopen(filename, "rb");
+ if(fd == NULL)
+ {
+ printf("cannot open '%s' for reading: %m\n", filename);
+ goto Lerr;
+ }
+ obj->u.bin = malloc(sizeof(struct bin_file_t));
+ obj->type = FW_BIN;
+ fseek(fd, 0, SEEK_END);
+ obj->u.bin->size = ftell(fd);
+ fseek(fd, 0, SEEK_SET);
+ obj->u.bin->data = xmalloc(obj->u.bin->size);
+ if(fread(obj->u.bin->data, obj->u.bin->size, 1, fd) != 1)
+ {
+ printf("cannot read '%s': %m\n", filename);
+ free(obj->u.bin->data);
+ free(obj->u.bin);
+ goto Lerr;
+ }
+ fclose(fd);
+ return obj;
+ }
+
+Lerr:
+ free(obj);
+ return NULL;
+}
+
+static bool fw_save(struct fw_object_t *obj, const char *filename)
+{
+ if(obj->type == FW_ELF)
+ {
+ FILE *fd = fopen(filename, "wb");
+ if(fd == NULL)
+ {
+ printf("Cannot open '%s' for writing: %m\n", filename);
+ return false;
+ }
+ elf_write_file(obj->u.elf, elf_std_write, generic_std_printf, fd);
+ fclose(fd);
+ return true;
+ }
+ else if(obj->type == FW_SB2)
+ {
+ /* sb_read_file will fill real key and IV but we don't want to override
+ * them when looping back otherwise the output will be inconsistent and
+ * garbage */
+ obj->u.sb->override_real_key = false;
+ obj->u.sb->override_crypto_iv = false;
+ enum sb_error_t err = sb_write_file(obj->u.sb, filename, NULL, generic_std_printf);
+ if(err != SB_SUCCESS)
+ {
+ printf("Cannot write '%s': %d\n", filename, err);
+ return false;
+ }
+ return true;
+ }
+ else if(obj->type == FW_BIN)
+ {
+ FILE *fd = fopen(filename, "wb");
+ if(fd == NULL)
+ {
+ printf("Cannot open '%s' for writing: %m\n", filename);
+ return false;
+ }
+ fwrite(obj->u.bin->data, 1, obj->u.bin->size, fd);
+ fclose(fd);
+ return true;
+ }
+ else if(obj->type == FW_EDOC)
+ {
+ FILE *fd = fopen(filename, "wb");
+ if(fd == NULL)
+ {
+ printf("Cannot open '%s' for writing: %m\n", filename);
+ return false;
+ }
+ struct edoc_header_t hdr;
+ strncpy(hdr.magic, "EDOC", 4);
+ hdr.zero = 0;
+ hdr.total_size = 4;
+ for(int i = 0; i < obj->u.edoc->nr_sections; i++)
+ hdr.total_size += sizeof(struct edoc_section_header_t) +
+ obj->u.edoc->sections[i].size;
+ fwrite(&hdr, sizeof(hdr), 1, fd);
+ for(int i = 0; i < obj->u.edoc->nr_sections; i++)
+ {
+ struct edoc_section_header_t shdr;
+ shdr.addr = obj->u.edoc->sections[i].addr;
+ shdr.size = obj->u.edoc->sections[i].size;
+ shdr.checksum = edoc_checksum(obj->u.edoc->sections[i].data, shdr.size);
+ fwrite(&shdr, sizeof(shdr), 1, fd);
+ fwrite(obj->u.edoc->sections[i].data, shdr.size, 1, fd);
+ }
+ fclose(fd);
+ return true;
+ }
+ else
+ {
+ printf("Unimplemented fw_save\n");
+ return false;
+ }
+}
+
+static void fw_free(struct fw_object_t *obj)
+{
+ switch(obj->type)
+ {
+ case FW_SB1:
+ sb1_free(obj->u.sb1);
+ break;
+ case FW_SB2:
+ sb_free(obj->u.sb);
+ break;
+ case FW_ELF:
+ elf_release(obj->u.elf);
+ free(obj->u.elf);
+ break;
+ case FW_BIN:
+ free(obj->u.bin->data);
+ free(obj->u.bin);
+ case FW_EDOC:
+ for(int i = 0; i < obj->u.edoc->nr_sections; i++)
+ free(obj->u.edoc->sections[i].data);
+ free(obj->u.edoc->sections);
+ free(obj->u.edoc);
+ default:
+ break;
+ }
+ free(obj);
+}
+
+static struct elf_section_t *elf_find_section(struct elf_params_t *elf, fw_addr_t addr)
+{
+ struct elf_section_t *match = NULL;
+ for(struct elf_section_t *sec = elf->first_section; sec; sec = sec->next)
+ {
+ if(addr.section && strcmp(addr.section, sec->name) != 0)
+ continue;
+ if(addr.addr < sec->addr || addr.addr >= sec->addr + sec->size)
+ continue;
+ if(match != NULL)
+ {
+ printf("Error: there are several match for address %#x@%s\n",
+ (unsigned)addr.addr, addr.section);
+ return NULL;
+ }
+ match = sec;
+ }
+ if(match == NULL)
+ {
+ printf("Error: there is no match for address %#x@%s\n", (unsigned)addr.addr,
+ addr.section);
+ }
+ return match;
+}
+
+static bool fw_elf_rw(struct elf_params_t *elf, fw_addr_t addr, void *buffer, size_t size, bool read)
+{
+ struct elf_section_t *sec = elf_find_section(elf, addr);
+ if(sec == NULL)
+ return false;
+ if(addr.addr + size > sec->addr + sec->size)
+ {
+ printf("Unsupported read/write across section boundary in ELF firmware\n");
+ return false;
+ }
+ if(sec->type != EST_LOAD)
+ {
+ printf("Error: unimplemented read/write to a fill section (ELF)\n");
+ return false;
+ }
+ void *data = sec->section + addr.addr - sec->addr;
+ if(read)
+ memcpy(buffer, data, size);
+ else
+ memcpy(data, buffer, size);
+ return true;
+}
+
+static struct sb_inst_t *sb2_find_section(struct sb_file_t *sb_file, fw_addr_t addr)
+{
+ struct sb_inst_t *match = NULL;
+ uint32_t sec_id = 0xffffffff;
+ int inst_nr = -1;
+ if(addr.section)
+ {
+ /* must be of the form name[.index] */
+ const char *mid = strchr(addr.section, '.');
+ char *end;
+ if(mid)
+ {
+ inst_nr = strtol(mid + 1, &end, 0);
+ if(*end)
+ {
+ printf("Warning: ignoring invalid section name '%s' (invalid inst nr)\n", addr.section);
+ goto Lscan;
+ }
+ }
+ else
+ mid = addr.section + strlen(addr.section);
+ if(mid - addr.section > 4)
+ {
+ printf("Warning: ignoring invalid section name '%s' (sec id too long)\n", addr.section);
+ goto Lscan;
+ }
+ sec_id = 0;
+ for(int i = 0; i < mid - addr.section; i++)
+ sec_id = sec_id << 8 | addr.section[i];
+ }
+
+ Lscan:
+ for(int i = 0; i < sb_file->nr_sections; i++)
+ {
+ struct sb_section_t *sec = &sb_file->sections[i];
+ if(addr.section && sec->identifier != sec_id)
+ continue;
+ int cur_blob = 0;
+ for(int j = 0; j < sec->nr_insts; j++)
+ {
+ struct sb_inst_t *inst = &sec->insts[j];
+ if(inst->inst == SB_INST_CALL || inst->inst == SB_INST_JUMP)
+ cur_blob++;
+ if(inst_nr >= 0 && cur_blob != inst_nr)
+ continue;
+ if(inst->inst != SB_INST_LOAD && inst->inst != SB_INST_FILL && inst->inst != SB_INST_DATA)
+ continue;
+ /* only consider data sections if section has been explicitely stated */
+ if(inst->inst == SB_INST_DATA && !addr.section)
+ continue;
+ /* for data sections, address will be 0 */
+ if(addr.addr < inst->addr || addr.addr > inst->addr + inst->size)
+ continue;
+ if(match != NULL)
+ {
+ printf("Error: there are several match for address %#x@%s\n",
+ (unsigned)addr.addr, addr.section);
+ return NULL;
+ }
+ match = inst;
+ }
+ }
+ if(match == NULL)
+ {
+ printf("Error: there is no match for address %#x@%s\n", (unsigned)addr.addr,
+ addr.section);
+ }
+ return match;
+}
+
+static bool fw_sb2_rw(struct sb_file_t *sb_file, fw_addr_t addr, void *buffer, size_t size, bool read)
+{
+ struct sb_inst_t *inst = sb2_find_section(sb_file, addr);
+ if(inst == NULL)
+ return false;
+ if(addr.addr + size > inst->addr + inst->size)
+ {
+ printf("Unsupported read/write across instruction boundary in SB firmware\n");
+ return false;
+ }
+ if(inst->inst != SB_INST_LOAD && inst->inst != SB_INST_DATA)
+ {
+ printf("Error: unimplemented read/write to a fill instruction (SB)\n");
+ return false;
+ }
+ void *data = inst->data + addr.addr - inst->addr;
+ if(read)
+ memcpy(buffer, data, size);
+ else
+ memcpy(data, buffer, size);
+ return true;
+}
+
+static bool fw_bin_rw(struct bin_file_t *bin_file, fw_addr_t addr, void *buffer, size_t size, bool read)
+{
+ if(addr.addr + size > bin_file->size)
+ {
+ printf("Unsupport read/write accross boundary in binary firmware\n");
+ return false;
+ }
+ void *data = bin_file->data + addr.addr;
+ if(read)
+ memcpy(buffer, data, size);
+ else
+ memcpy(data, buffer, size);
+ return true;
+}
+
+static bool fw_edoc_rw(struct edoc_file_t *edoc_file, fw_addr_t addr, void *buffer, size_t size, bool read)
+{
+ for(int i = 0; i < edoc_file->nr_sections; i++)
+ {
+ if(addr.addr < edoc_file->sections[i].addr ||
+ addr.addr + size >= edoc_file->sections[i].addr + edoc_file->sections[i].size)
+ continue;
+ void *data = edoc_file->sections[i].data + addr.addr - edoc_file->sections[i].addr;
+ if(read)
+ memcpy(buffer, data, size);
+ else
+ memcpy(data, buffer, size);
+ return true;
+ }
+ printf("Unsupport read/write accross boundary in EDOC firmware\n");
+ return false;
+}
+
+static bool fw_rw(struct fw_object_t *obj, fw_addr_t addr, void *buffer, size_t size, bool read)
+{
+ switch(obj->type)
+ {
+ case FW_ELF: return fw_elf_rw(obj->u.elf, addr, buffer, size, read);
+ case FW_SB2: return fw_sb2_rw(obj->u.sb, addr, buffer, size, read);
+ case FW_BIN: return fw_bin_rw(obj->u.bin, addr, buffer, size, read);
+ case FW_EDOC: return fw_edoc_rw(obj->u.edoc, addr, buffer, size, read);
+ default:
+ printf("Error: unimplemented read/write for type %d\n", obj->type);
+ return false;
+ }
+}
+
+static bool fw_read(struct fw_object_t *obj, fw_addr_t addr, void *buffer, size_t size)
+{
+ return fw_rw(obj, addr, buffer, size, true);
+}
+
+static bool fw_write(struct fw_object_t *obj, fw_addr_t addr, const void *buffer, size_t size)
+{
+ return fw_rw(obj, addr, (void *)buffer, size, false);
+}
+
+static bool elf_find_sym(struct elf_params_t *elf, fw_sym_addr_t addr, fw_addr_t *out_addr)
+{
+ bool found = false;
+ for(struct elf_symbol_t *cur = elf->first_symbol; cur; cur = cur->next)
+ {
+ if(strcmp(cur->name, addr.name) != 0)
+ continue;
+ if(addr.section && strcmp(cur->section, addr.section) != 0)
+ continue;
+ if(found)
+ {
+ printf("Error: there are several match for symbol %s@%s\n", addr.name, addr.section);
+ return false;
+ }
+ out_addr->addr = cur->addr;
+ out_addr->section = cur->section;
+ found = true;
+ }
+ return found;
+}
+
+static bool fw_find_sym(struct fw_object_t *obj, fw_sym_addr_t addr, fw_addr_t *out_addr)
+{
+ switch(obj->type)
+ {
+ case FW_ELF: return elf_find_sym(obj->u.elf, addr, out_addr);
+ case FW_SB2: case FW_SB1: case FW_BIN: return false;
+ default:
+ printf("Error: unimplemented find addr for type %d\n", obj->type);
+ return false;
+ }
+}
+
+static bool fw_bin_section_info(struct bin_file_t *obj, const char *sec, struct fw_section_info_t *out)
+{
+ // the only valid section names are NULL and ""
+ if(sec != NULL && strlen(sec) != 0)
+ return false;
+ out->addr = 0;
+ out->size = obj->size;
+ return true;
+}
+
+static bool fw_section_info(struct fw_object_t *obj, const char *sec, struct fw_section_info_t *out)
+{
+ switch(obj->type)
+ {
+ case FW_BIN: return fw_bin_section_info(obj->u.bin, sec, out);
+ default:
+ printf("Error: unimplemented get section info for type %d\n", obj->type);
+ return false;
+ }
+}
+
+/**
+ * LUA library
+ */
+
+struct fw_object_t *my_lua_get_object(lua_State *state, int index)
+{
+ struct fw_object_t *obj = lua_touserdata(state, index);
+ if(obj == NULL || obj->magic != FWOBJ_MAGIC)
+ luaL_error(state, "invalid parameter: not a firmware object");
+ return obj;
+}
+
+const char *my_lua_get_string(lua_State *state, int index)
+{
+ return luaL_checkstring(state, index);
+}
+
+lua_Unsigned my_lua_get_unsigned(lua_State *state, int index)
+{
+ lua_Integer i = luaL_checkinteger(state, index);
+ if(i < 0)
+ luaL_error(state, "invalid parameter: not an unsigned value");
+ return i;
+}
+
+fw_addr_t my_lua_get_addr(lua_State *state, int index)
+{
+ if(!lua_istable(state, index))
+ luaL_error(state, "invalid parameter: not an address table");
+ lua_getfield(state, index, "addr");
+ if(lua_isnil(state, -1))
+ luaL_error(state, "invalid parameter: address has not field 'addr'");
+ uint32_t addr = my_lua_get_unsigned(state, -1);
+ lua_pop(state, 1);
+
+ char *sec = NULL;
+ lua_getfield(state, index, "section");
+ if(!lua_isnil(state, -1))
+ sec = strdup(my_lua_get_string(state, -1));
+ lua_pop(state, 1);
+ return make_addr(addr, sec);
+}
+
+void my_lua_pushbuffer(lua_State *state, void *buffer, size_t len)
+{
+ uint8_t *p = buffer;
+ lua_createtable(state, len, 0);
+ for(int i = 0; i < len; i++)
+ {
+ lua_pushinteger(state, i + 1);
+ lua_pushinteger(state, p[i]);
+ lua_settable(state, -3);
+ }
+}
+
+void *my_lua_get_buffer(lua_State *state, int index, size_t *len)
+{
+ if(!lua_istable(state, index))
+ luaL_error(state, "invalid parameter: not a data table");
+ *len = lua_rawlen(state, index);
+ uint8_t *buf = xmalloc(*len);
+ for(int i = 0; i < *len; i++)
+ {
+ lua_pushinteger(state, i + 1);
+ lua_gettable(state, index);
+ if(lua_isnil(state, -1))
+ {
+ free(buf);
+ luaL_error(state, "invalid parameter: not a data table, missing some fields");
+ }
+ int v = luaL_checkinteger(state, -1);
+ lua_pop(state, 1);
+ if(v < 0 || v > 0xff)
+ {
+ free(buf);
+ luaL_error(state, "invalid parameter: not a data table, field is not a byte");
+ }
+ buf[i] = v;
+ }
+ return buf;
+}
+
+int my_lua_load_file(lua_State *state)
+{
+ int n = lua_gettop(state);
+ if(n != 1)
+ return luaL_error(state, "load_file takes one argument: a filename");
+ enum fw_type_t type = lua_tounsigned(state, lua_upvalueindex(1));
+ const char *filename = my_lua_get_string(state, 1);
+ struct fw_object_t *obj = fw_load(filename, type);
+ if(obj)
+ lua_pushlightuserdata(state, obj);
+ else
+ lua_pushnil(state);
+ return 1;
+}
+
+int my_lua_save_file(lua_State *state)
+{
+ int n = lua_gettop(state);
+ if(n != 2)
+ return luaL_error(state, "load_file takes two arguments: a firmware and a filename");
+ struct fw_object_t *obj = my_lua_get_object(state, 1);
+ const char *filename = my_lua_get_string(state, 2);
+ lua_pushboolean(state, fw_save(obj, filename));
+ return 1;
+}
+
+int my_lua_read(lua_State *state)
+{
+ int n = lua_gettop(state);
+ if(n != 3)
+ return luaL_error(state, "read takes three arguments: a firmware, an address and a length");
+ struct fw_object_t *obj = my_lua_get_object(state, 1);
+ fw_addr_t addr = my_lua_get_addr(state, 2);
+ size_t len = my_lua_get_unsigned(state, 3);
+ void *buffer = xmalloc(len);
+ bool ret = fw_read(obj, addr, buffer, len);
+ if(ret)
+ my_lua_pushbuffer(state, buffer, len);
+ else
+ lua_pushnil(state);
+ free(buffer);
+ return 1;
+}
+
+int my_lua_write(lua_State *state)
+{
+ int n = lua_gettop(state);
+ if(n != 3)
+ return luaL_error(state, "write takes three arguments: a firmware, an address and a data table");
+ struct fw_object_t *obj = my_lua_get_object(state, 1);
+ fw_addr_t addr = my_lua_get_addr(state, 2);
+ size_t len;
+ void *buf = my_lua_get_buffer(state, 3, &len);
+ fw_write(obj, addr, buf, len);
+ free(buf);
+ return 0;
+}
+
+int my_lua_section_info(lua_State *state)
+{
+ int n = lua_gettop(state);
+ if(n != 2)
+ return luaL_error(state, "section_info takes two arguments: a firmware and a section name");
+ struct fw_object_t *obj = my_lua_get_object(state, 1);
+ const char *secname = my_lua_get_string(state, 2);
+ struct fw_section_info_t seci;
+ if(fw_section_info(obj, secname, &seci))
+ {
+ lua_createtable(state, 0, 0);
+ lua_pushinteger(state, seci.addr);
+ lua_setfield(state, -2, "addr");
+ lua_pushinteger(state, seci.size);
+ lua_setfield(state, -2, "size");
+ }
+ else
+ lua_pushnil(state);
+ return 1;
+}
+
+/* compute MD5 sum of a buffer */
+static bool compute_md5sum_buf(void *buf, size_t sz, uint8_t file_md5sum[16])
+{
+ md5_context ctx;
+ md5_starts(&ctx);
+ md5_update(&ctx, buf, sz);
+ md5_finish(&ctx, file_md5sum);
+ return true;
+}
+
+/* read a file to a buffer */
+static bool read_file(const char *file, void **buffer, size_t *size)
+{
+ FILE *f = fopen(file, "rb");
+ if(f == NULL)
+ {
+ printf("Error: cannot open file for reading: %m\n");
+ return false;
+ }
+ fseek(f, 0, SEEK_END);
+ *size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ *buffer = xmalloc(*size);
+ if(fread(*buffer, *size, 1, f) != 1)
+ {
+ printf("Error: cannot read file: %m\n");
+ free(*buffer);
+ fclose(f);
+ return false;
+ }
+ fclose(f);
+ return true;
+}
+
+/* compute MD5 of a file */
+static bool compute_md5sum(const char *file, uint8_t file_md5sum[16])
+{
+ void *buf;
+ size_t sz;
+ if(!read_file(file, &buf, &sz))
+ return false;
+ compute_md5sum_buf(buf, sz, file_md5sum);
+ free(buf);
+ return true;
+}
+
+int my_lua_md5sum(lua_State *state)
+{
+ int n = lua_gettop(state);
+ if(n != 1)
+ return luaL_error(state, "md5sum takes one argument: a filename");
+ const char *filename = my_lua_get_string(state, 1);
+ uint8_t md5sum[16];
+ if(!compute_md5sum(filename, md5sum))
+ return luaL_error(state, "cannot compute md5sum of the file");
+ my_lua_pushbuffer(state, md5sum, sizeof(md5sum));
+ return 1;
+}
+
+static bool init_lua_hwp(void)
+{
+ lua_pushunsigned(g_lua, FW_UNK);
+ lua_pushcclosure(g_lua, my_lua_load_file, 1);
+ lua_setfield(g_lua, -2, "load_file");
+
+ lua_pushunsigned(g_lua, FW_ELF);
+ lua_pushcclosure(g_lua, my_lua_load_file, 1);
+ lua_setfield(g_lua, -2, "load_elf_file");
+
+ lua_pushunsigned(g_lua, FW_SB2);
+ lua_pushcclosure(g_lua, my_lua_load_file, 1);
+ lua_setfield(g_lua, -2, "load_sb_file");
+
+ lua_pushunsigned(g_lua, FW_SB1);
+ lua_pushcclosure(g_lua, my_lua_load_file, 1);
+ lua_setfield(g_lua, -2, "load_sb1_file");
+
+ lua_pushunsigned(g_lua, FW_BIN);
+ lua_pushcclosure(g_lua, my_lua_load_file, 1);
+ lua_setfield(g_lua, -2, "load_bin_file");
+
+ lua_pushcfunction(g_lua, my_lua_save_file);
+ lua_setfield(g_lua, -2, "save_file");
+
+ lua_pushcfunction(g_lua, my_lua_read);
+ lua_setfield(g_lua, -2, "read");
+
+ lua_pushcfunction(g_lua, my_lua_write);
+ lua_setfield(g_lua, -2, "write");
+
+ lua_pushcfunction(g_lua, my_lua_section_info);
+ lua_setfield(g_lua, -2, "section_info");
+
+ lua_pushcfunction(g_lua, my_lua_md5sum);
+ lua_setfield(g_lua, -2, "md5sum");
+
+ return true;
+}
+
+int my_lua_exit(lua_State *state)
+{
+ g_exit = true;
+ return 0;
+}
+
+bool my_lua_create_arg(lua_State *state, int argc, char **argv)
+{
+ lua_newtable(state); // arg
+ for(int i = 0; i < argc; i++)
+ {
+ lua_pushinteger(state, i + 1);
+ lua_pushstring(state, argv[i]);
+ lua_settable(state, -3);
+ }
+ lua_setglobal(state, "arg");
+ return true;
+}
+
+static bool init_lua(void)
+{
+ g_lua = luaL_newstate();
+ if(g_lua == NULL)
+ {
+ printf("Cannot create lua state\n");
+ return 1;
+ }
+ // open all standard libraires
+ luaL_openlibs(g_lua);
+
+ lua_newtable(g_lua); // hwp
+ if(!init_lua_hwp())
+ return false;
+ lua_setglobal(g_lua, "hwp");
+
+ lua_pushcfunction(g_lua, my_lua_exit);
+ lua_setglobal(g_lua, "exit");
+
+ lua_pushcfunction(g_lua, my_lua_exit);
+ lua_setglobal(g_lua, "quit");
+
+ return true;
+}
+
+static void usage(void)
+{
+ printf("Usage: hwpatcher [options] [--] [arguments]\n");
+ printf("Options:\n");
+ printf(" -?/--help Display this message\n");
+ printf(" -d/--debug Enable debug output\n");
+ printf(" -n/--no-color Disable color output\n");
+ printf(" -i/--interactive Enter interactive mode after all files have run\n");
+ printf(" -f/--do-file <f> Do lua file\n");
+ printf(" -k <file> Add key file\n");
+ printf(" -z Add zero key\n");
+ printf(" --add-key <key> Add single key (hex)\n");
+ printf(" -x Use default sb1 key\n");
+ printf("All files executed are provided with the extra arguments in the 'arg' table\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ bool interactive = false;
+ if(argc <= 1)
+ usage();
+ if(!init_lua())
+ return 1;
+ char **do_files = xmalloc(argc * sizeof(char *));
+ int nr_do_files = 0;
+ while(1)
+ {
+ static struct option long_options[] =
+ {
+ {"help", no_argument, 0, '?'},
+ {"debug", no_argument, 0, 'd'},
+ {"no-color", no_argument, 0, 'n'},
+ {"interactive", no_argument, 0, 'i'},
+ {"do-file", required_argument, 0, 'f'},
+ {"add-key", required_argument, 0, 'a'},
+ {0, 0, 0, 0}
+ };
+
+ int c = getopt_long(argc, argv, "?dif:zx", long_options, NULL);
+ if(c == -1)
+ break;
+ switch(c)
+ {
+ case -1:
+ break;
+ case 'n':
+ enable_color(false);
+ break;
+ case 'd':
+ g_debug = true;
+ break;
+ case '?':
+ usage();
+ break;
+ case 'i':
+ interactive = true;
+ break;
+ case 'f':
+ do_files[nr_do_files++] = optarg;
+ break;
+ case 'z':
+ {
+ struct crypto_key_t g_zero_key;
+ sb_get_zero_key(&g_zero_key);
+ add_keys(&g_zero_key, 1);
+ break;
+ }
+ case 'x':
+ {
+ struct crypto_key_t key;
+ sb1_get_default_key(&key);
+ add_keys(&key, 1);
+ break;
+ }
+ case 'a':
+ {
+ struct crypto_key_t key;
+ char *s = optarg;
+ if(!parse_key(&s, &key))
+ bug("Invalid key specified as argument\n");
+ if(*s != 0)
+ bug("Trailing characters after key specified as argument\n");
+ add_keys(&key, 1);
+ break;
+ }
+ default:
+ printf("Internal error: unknown option '%c'\n", c);
+ return 1;
+ }
+ }
+
+ if(!my_lua_create_arg(g_lua, argc - optind, argv + optind))
+ return 1;
+
+ for(int i = 0; i < nr_do_files; i++)
+ {
+ if(luaL_dofile(g_lua, do_files[i]))
+ {
+ printf("error in %s: %s\n", do_files[i], lua_tostring(g_lua, -1));
+ return 1;
+ }
+ lua_pop(g_lua, lua_gettop(g_lua));
+ }
+
+ if(nr_do_files == 0 && optind < argc)
+ printf("Warning: extra unused arguments on command lines\n");
+
+ if(interactive)
+ {
+ printf("Entering interactive mode. You can use 'quit()' or 'exit()' to quit.\n");
+ rl_bind_key('\t', rl_complete);
+ while(!g_exit)
+ {
+ char *input = readline("> ");
+ if(!input)
+ break;
+ add_history(input);
+ // evaluate string
+ if(luaL_dostring(g_lua, input))
+ printf("error: %s\n", lua_tostring(g_lua, -1));
+ // pop everything to start from a clean stack
+ lua_pop(g_lua, lua_gettop(g_lua));
+ free(input);
+ }
+ }
+
+ return 0;
+}
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 @@
+--[[
+hwpatcher library
+
+The C code provides the following functions.
+
+At global level:
+- quit() Quit the interactive mode
+- exit() Same as quit()
+
+In the hwp table:
+- load_file(filename) Load a firmware and guess type
+- load_elf_file(filename) Load a firmware as ELF
+- load_sb_file(filename) Load a firmware as SB
+- load_sb1_file(filename) Load a firmware as SB1
+- load_bin_file(filename) Load a firmware as binary
+- save_file(obj, filename) Save a firmware to a file
+- read(obj, addr, len) Read data from a firmware
+- write(obj, addr, data) Write data to a firmware
+- section_info(obj, sec) Return information about a section in a table (or nil)
+- md5sum(filename) Compute the MD5 sum of a file
+
+Data read/written from/to a firmware must must be an array of bytes.
+The address must be a table of the following fields:
+- address: contain the address
+- section: optional section name
+Data section information is a table with the following fields:
+- address: first address if the section
+- size: size of the section
+We provide the following functions to help dealing with addresses:
+- make_addr
+
+]]--
+
+function hwp.deepcopy(o, seen)
+ seen = seen or {}
+ if o == nil then return nil end
+ if seen[o] then return seen[o] end
+
+ local no
+ if type(o) == 'table' then
+ no = {}
+ seen[o] = no
+
+ for k, v in next, o, nil do
+ no[hwp.deepcopy(k, seen)] = hwp.deepcopy(v, seen)
+ end
+ setmetatable(no, hwp.deepcopy(getmetatable(o), seen))
+ else -- number, string, boolean, etc
+ no = o
+ end
+ return no
+end
+
+function hwp.make_addr(addr, section)
+ local t = {addr = addr, section = section}
+ local addr_to_string = function(self)
+ if self.section == nil then
+ return string.format("%#x", self.addr)
+ else
+ return string.format("%#x@%s", self.addr, self.section)
+ end
+ end
+ setmetatable(t, {__tostring = addr_to_string})
+ return t
+end
+
+function hwp.inc_addr(addr, amount)
+ return hwp.make_addr(addr.addr + amount, addr.section)
+end
+
+-- pack an array of bytes in a integer (little-endian)
+function hwp.pack(arr)
+ local v = 0
+ for i = #arr, 1, -1 do
+ v = bit32.bor(bit32.lshift(v, 8),bit32.band(arr[i], 0xff))
+ end
+ return v
+end
+
+-- do the converse
+function hwp.unpack(v, n)
+ local t = {}
+ for i = 1, n do
+ t[i] = bit32.band(v, 0xff)
+ v = bit32.rshift(v, 8)
+ end
+ return t
+end
+
+-- read a 32-bit value
+function hwp.read32(obj, addr)
+ return hwp.pack(hwp.read(obj, addr, 4))
+end
+
+-- write a 32-bit value
+function hwp.write32(obj, addr, v)
+ return hwp.write(obj, addr, hwp.unpack(v, 4))
+end
+
+-- convert a MD5 hash to a string
+function hwp.md5str(md5)
+ local s = ""
+ for i = 1, #md5 do
+ s = s .. string.format("%02x", md5[i])
+ end
+ return s
+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 @@
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string.h>
+
+#include "md5.h"
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint32) (b)[(i) ] ) \
+ | ( (uint32) (b)[(i) + 1] << 8 ) \
+ | ( (uint32) (b)[(i) + 2] << 16 ) \
+ | ( (uint32) (b)[(i) + 3] << 24 ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (uint8) ( (n) ); \
+ (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \
+ (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \
+ (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \
+}
+
+void md5_starts( md5_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+}
+
+void md5_process( md5_context *ctx, uint8 data[64] )
+{
+ uint32 X[16], A, B, C, D;
+
+ GET_UINT32( X[0], data, 0 );
+ GET_UINT32( X[1], data, 4 );
+ GET_UINT32( X[2], data, 8 );
+ GET_UINT32( X[3], data, 12 );
+ GET_UINT32( X[4], data, 16 );
+ GET_UINT32( X[5], data, 20 );
+ GET_UINT32( X[6], data, 24 );
+ GET_UINT32( X[7], data, 28 );
+ GET_UINT32( X[8], data, 32 );
+ GET_UINT32( X[9], data, 36 );
+ GET_UINT32( X[10], data, 40 );
+ GET_UINT32( X[11], data, 44 );
+ GET_UINT32( X[12], data, 48 );
+ GET_UINT32( X[13], data, 52 );
+ GET_UINT32( X[14], data, 56 );
+ GET_UINT32( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) \
+{ \
+ a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P( A, B, C, D, 0, 7, 0xD76AA478 );
+ P( D, A, B, C, 1, 12, 0xE8C7B756 );
+ P( C, D, A, B, 2, 17, 0x242070DB );
+ P( B, C, D, A, 3, 22, 0xC1BDCEEE );
+ P( A, B, C, D, 4, 7, 0xF57C0FAF );
+ P( D, A, B, C, 5, 12, 0x4787C62A );
+ P( C, D, A, B, 6, 17, 0xA8304613 );
+ P( B, C, D, A, 7, 22, 0xFD469501 );
+ P( A, B, C, D, 8, 7, 0x698098D8 );
+ P( D, A, B, C, 9, 12, 0x8B44F7AF );
+ P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+ P( B, C, D, A, 11, 22, 0x895CD7BE );
+ P( A, B, C, D, 12, 7, 0x6B901122 );
+ P( D, A, B, C, 13, 12, 0xFD987193 );
+ P( C, D, A, B, 14, 17, 0xA679438E );
+ P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P( A, B, C, D, 1, 5, 0xF61E2562 );
+ P( D, A, B, C, 6, 9, 0xC040B340 );
+ P( C, D, A, B, 11, 14, 0x265E5A51 );
+ P( B, C, D, A, 0, 20, 0xE9B6C7AA );
+ P( A, B, C, D, 5, 5, 0xD62F105D );
+ P( D, A, B, C, 10, 9, 0x02441453 );
+ P( C, D, A, B, 15, 14, 0xD8A1E681 );
+ P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
+ P( A, B, C, D, 9, 5, 0x21E1CDE6 );
+ P( D, A, B, C, 14, 9, 0xC33707D6 );
+ P( C, D, A, B, 3, 14, 0xF4D50D87 );
+ P( B, C, D, A, 8, 20, 0x455A14ED );
+ P( A, B, C, D, 13, 5, 0xA9E3E905 );
+ P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
+ P( C, D, A, B, 7, 14, 0x676F02D9 );
+ P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+
+ P( A, B, C, D, 5, 4, 0xFFFA3942 );
+ P( D, A, B, C, 8, 11, 0x8771F681 );
+ P( C, D, A, B, 11, 16, 0x6D9D6122 );
+ P( B, C, D, A, 14, 23, 0xFDE5380C );
+ P( A, B, C, D, 1, 4, 0xA4BEEA44 );
+ P( D, A, B, C, 4, 11, 0x4BDECFA9 );
+ P( C, D, A, B, 7, 16, 0xF6BB4B60 );
+ P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+ P( A, B, C, D, 13, 4, 0x289B7EC6 );
+ P( D, A, B, C, 0, 11, 0xEAA127FA );
+ P( C, D, A, B, 3, 16, 0xD4EF3085 );
+ P( B, C, D, A, 6, 23, 0x04881D05 );
+ P( A, B, C, D, 9, 4, 0xD9D4D039 );
+ P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+ P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+ P( B, C, D, A, 2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P( A, B, C, D, 0, 6, 0xF4292244 );
+ P( D, A, B, C, 7, 10, 0x432AFF97 );
+ P( C, D, A, B, 14, 15, 0xAB9423A7 );
+ P( B, C, D, A, 5, 21, 0xFC93A039 );
+ P( A, B, C, D, 12, 6, 0x655B59C3 );
+ P( D, A, B, C, 3, 10, 0x8F0CCC92 );
+ P( C, D, A, B, 10, 15, 0xFFEFF47D );
+ P( B, C, D, A, 1, 21, 0x85845DD1 );
+ P( A, B, C, D, 8, 6, 0x6FA87E4F );
+ P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+ P( C, D, A, B, 6, 15, 0xA3014314 );
+ P( B, C, D, A, 13, 21, 0x4E0811A1 );
+ P( A, B, C, D, 4, 6, 0xF7537E82 );
+ P( D, A, B, C, 11, 10, 0xBD3AF235 );
+ P( C, D, A, B, 2, 15, 0x2AD7D2BB );
+ P( B, C, D, A, 9, 21, 0xEB86D391 );
+
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+void md5_update( md5_context *ctx, uint8 *input, uint32 length )
+{
+ uint32 left, fill;
+
+ if( ! length ) return;
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += length;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if( ctx->total[0] < length )
+ ctx->total[1]++;
+
+ if( left && length >= fill )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, fill );
+ md5_process( ctx, ctx->buffer );
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+ while( length >= 64 )
+ {
+ md5_process( ctx, input );
+ length -= 64;
+ input += 64;
+ }
+
+ if( length )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, length );
+ }
+}
+
+static uint8 md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void md5_finish( md5_context *ctx, uint8 digest[16] )
+{
+ uint32 last, padn;
+ uint32 high, low;
+ uint8 msglen[8];
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_UINT32( low, msglen, 0 );
+ PUT_UINT32( high, msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ md5_update( ctx, md5_padding, padn );
+ md5_update( ctx, msglen, 8 );
+
+ PUT_UINT32( ctx->state[0], digest, 0 );
+ PUT_UINT32( ctx->state[1], digest, 4 );
+ PUT_UINT32( ctx->state[2], digest, 8 );
+ PUT_UINT32( ctx->state[3], digest, 12 );
+}
+
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 @@
+#ifndef _MD5_H
+#define _MD5_H
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+ uint32 total[2];
+ uint32 state[4];
+ uint8 buffer[64];
+}
+md5_context;
+
+void md5_starts( md5_context *ctx );
+void md5_update( md5_context *ctx, uint8 *input, uint32 length );
+void md5_finish( md5_context *ctx, uint8 digest[16] );
+
+#endif /* md5.h */
+
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 @@
+.text
+.global _start
+_start:
+ b exec
+branch_addr:
+ .word kill
+hw_power_sts:
+#if defined(CREATIVE_ZEN)
+ .word 0x800440b0 /* STMP3700 */
+#else
+ .word 0x800440c0 /* IMX233 */
+#endif
+hw_pinctrl_din0:
+ .word 0x80018600
+hw_pinctrl_din1:
+ .word 0x80018610
+hw_pinctrl_din2:
+ .word 0x80018620
+kill:
+ ldr pc, branch_addr
+exec:
+#if defined(SANSA_FUZEPLUS)
+ /* check PSWITCH=1 (power button pressed) */
+ ldr r0, hw_power_sts
+ ldr r0, [r0]
+ mov r0, r0, lsr #20
+ and r0, #3
+ cmp r0, #1
+ bne ret
+ /* check B1P30=0 (volume down pressed) */
+ ldr r0, hw_pinctrl_din1
+ ldr r0, [r0]
+ mov r0, r0, lsr #30
+ ands r0, #1
+ beq kill
+#elif defined(CREATIVE_ZENXFI3)
+ /* check PSWITCH=1 (power button pressed) */
+ ldr r0, hw_power_sts
+ ldr r0, [r0]
+ mov r0, r0, lsr #20
+ and r0, #3
+ cmp r0, #1
+ bne ret
+ /* check B2P07=0 (volume down pressed) */
+ ldr r0, hw_pinctrl_din2
+ ldr r0, [r0]
+ mov r0, r0, lsr #7
+ ands r0, #1
+ beq kill
+#elif defined(CREATIVE_ZENXFI2)
+ /* check B0P11=0 (power button pressed) and B0P14 (select button pressed) */
+ ldr r0, hw_pinctrl_din0
+ ldr r0, [r0]
+ mov r0, r0, lsr #11
+ tst r0, #1
+ bne ret
+ mov r0, r0, lsr #3
+ tst r0, #1
+ beq kill
+#elif defined(CREATIVE_ZEN)
+ /* check PSWITCH=1 (power button pressed) */
+ ldr r0, hw_power_sts
+ ldr r0, [r0]
+ mov r0, r0, lsr #18
+ and r0, #3
+ cmp r0, #0
+ bne kill
+#elif defined(SONY_NWZ)
+ /* check PSWITCH=3 (power button pressed) */
+ ldr r0, hw_power_sts
+ ldr r0, [r0]
+ mov r0, r0, lsr #20
+ and r0, #3
+ cmp r0, #3
+ beq kill
+#elif defined(CREATIVE_ZENXFISTYLE)
+ /* check PSWITCH=1 (power button pressed) */
+ ldr r0, hw_power_sts
+ ldr r0, [r0]
+ mov r0, r0, lsr #20
+ and r0, #3
+ cmp r0, #1
+ beq kill
+#else
+#error implement me
+#endif
+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 @@
+--[[
+Sansa View bootloader hacking
+required argument (in order):
+- path to bootloader
+- path to output bootloader
+- path to stub
+]]--
+require("lib")
+require("arm")
+
+if #arg < 3 then
+ error("not enough argument to fuzep patcher")
+end
+
+local md5 = hwp.md5sum(arg[1])
+if hwp.md5str(md5) ~= "4bc1760327c37b9ffd00315c8aa7f376" then
+ error("MD5 sum of the file doesn't match")
+end
+
+local fw = hwp.load_file(arg[1])
+local jump_instr_addr = arm.to_thumb(hwp.make_addr(0x753C))
+local stub_addr = hwp.make_addr(0x137B0)
+-- read old jump address
+--local old_jump = arm.parse_branch(fw, jump_instr_addr)
+--print(string.format("Old jump address: %s", old_jump))
+-- put stub at the right place
+local stub = hwp.load_bin_file(arg[3])
+local stub_info = hwp.section_info(stub, "")
+local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
+hwp.write(fw, stub_addr, stub_data)
+-- patch jump
+local branch_to_stub = arm.make_branch(arm.to_arm(stub_addr), true)
+arm.write_branch(fw, jump_instr_addr, branch_to_stub, hwp.inc_addr(stub_addr, stub_info.size))
+-- read jump address
+local new_jump = arm.parse_branch(fw, jump_instr_addr)
+print(string.format("New jump address: %s", new_jump))
+-- save
+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 @@
+--[[
+Zen X-Fi2 1.23.01e NAND hacking
+required argument (in order):
+- path to firmware
+- path to output firmware
+- path to blob
+- path to stub
+]]--
+
+if #arg < 4 then
+ error("not enough argument to fuzep patcher")
+end
+
+local fw = hwp.load_file(arg[1])
+local irq_addr_pool = hwp.make_addr(0x4035e154, "play")
+local proxy_addr = arm.to_arm(hwp.make_addr(0x402f06f8, "play"))
+-- read old IRQ address pool
+local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
+print(string.format("Old IRQ address: %s", old_irq_addr))
+-- put stub at the beginning of the proxy
+local stub = hwp.load_bin_file(arg[4])
+local stub_info = hwp.section_info(stub, "")
+local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
+hwp.write(fw, proxy_addr, stub_data)
+local stub_addr = proxy_addr
+proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
+-- modify irq
+hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
+print(string.format("New IRQ address: %s", proxy_addr))
+-- in proxy, save registers
+arm.write_save_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- load blob
+local blob = hwp.load_bin_file(arg[3])
+local blob_info = hwp.section_info(blob, "")
+-- patch blob with stub address
+hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
+-- write it !
+local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
+hwp.write(fw, proxy_addr, blob_data)
+proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
+-- restore registers
+arm.write_restore_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- branch to old code
+local branch_to_old = arm.make_branch(old_irq_addr, false)
+arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
+-- save
+hwp.save_file(fw, arg[2])
+
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 @@
+--[[
+Zen X-Fi3 1.00.25e hacking
+required argument (in order):
+- path to firmware
+- path to output firmware
+- path to blob
+- path to stub
+]]--
+
+if #arg < 4 then
+ error("not enough argument to fuzep patcher")
+end
+
+local fw = hwp.load_file(arg[1])
+local irq_addr_pool = hwp.make_addr(0x405916f0)
+local proxy_addr = arm.to_arm(hwp.make_addr(0x40384674))
+-- read old IRQ address pool
+local old_irq_addr = hwp.make_addr(hwp.read32(fw, irq_addr_pool))
+print(string.format("Old IRQ address: %s", old_irq_addr))
+-- put stub at the beginning of the proxy
+local stub = hwp.load_bin_file(arg[4])
+local stub_info = hwp.section_info(stub, "")
+local stub_data = hwp.read(stub, hwp.make_addr(stub_info.addr, ""), stub_info.size)
+hwp.write(fw, proxy_addr, stub_data)
+local stub_addr = proxy_addr
+proxy_addr = hwp.inc_addr(proxy_addr, stub_info.size)
+-- modify irq
+hwp.write32(fw, irq_addr_pool, proxy_addr.addr)
+print(string.format("New IRQ address: %s", proxy_addr))
+-- in proxy, save registers
+arm.write_save_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- load blob
+local blob = hwp.load_bin_file(arg[3])
+local blob_info = hwp.section_info(blob, "")
+-- patch blob with stub address
+hwp.write32(blob, hwp.make_addr(blob_info.addr + 4, ""), stub_addr.addr)
+-- write it !
+local blob_data = hwp.read(blob, hwp.make_addr(blob_info.addr, ""), blob_info.size)
+hwp.write(fw, proxy_addr, blob_data)
+proxy_addr = hwp.inc_addr(proxy_addr, blob_info.size)
+-- restore registers
+arm.write_restore_regs(fw, proxy_addr)
+proxy_addr = hwp.inc_addr(proxy_addr, 4)
+-- branch to old code
+local branch_to_old = arm.make_branch(old_irq_addr, false)
+arm.write_branch(fw, proxy_addr, branch_to_old, hwp.inc_addr(proxy_addr, 4))
+-- save
+hwp.save_file(fw, arg[2])