summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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])