summaryrefslogtreecommitdiff
path: root/utils/hwpatcher/arm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwpatcher/arm.lua')
-rw-r--r--utils/hwpatcher/arm.lua205
1 files changed, 205 insertions, 0 deletions
diff --git a/utils/hwpatcher/arm.lua b/utils/hwpatcher/arm.lua
new file mode 100644
index 0000000000..0211244d11
--- /dev/null
+++ b/utils/hwpatcher/arm.lua
@@ -0,0 +1,205 @@
1--[[
2hwpatcher arm decoding/encoding library
3
4]]--
5arm = {}
6
7-- determines whether an address is in Thumb code or not
8function arm.is_thumb(addr)
9 return bit32.extract(addr.addr, 0) == 1
10end
11
12-- translate address to real address (ie without Thumb bit)
13-- produces an error if address is not properly aligned in ARM mode
14function arm.xlate_addr(addr)
15 local res = hwp.deepcopy(addr)
16 if arm.is_thumb(addr) then
17 res.addr = bit32.replace(addr.addr, 0, 0)
18 elseif bit32.extract(addr.addr, 0, 2) ~= 0 then
19 error("ARM address is not word-aligned")
20 end
21 return res
22end
23
24-- switch between arm and thumb
25function arm.to_thumb(addr)
26 local res = hwp.deepcopy(addr)
27 res.addr = bit32.bor(addr.addr, 1)
28 return res
29end
30
31function arm.to_arm(addr)
32 return arm.xlate_addr(addr)
33end
34
35-- sign extend a value to 32-bits
36-- only the lower 'bits' bits are considered, everything else is trashed
37-- watch out arithmetic vs logical shift !
38function arm.sign32(v)
39 if bit32.extract(v, 31) == 1 then
40 return -1 - bit32.bnot(v)
41 else
42 return v
43 end
44end
45function arm.sign_extend(val, bits)
46 return arm.sign32(bit32.arshift(bit32.lshift(val, 32 - bits), 32 - bits))
47end
48
49-- check that a signed value fits in some field
50function arm.check_sign_truncation(val, bits)
51 return val == arm.sign_extend(val, bits)
52end
53
54-- create a branch description
55function arm.make_branch(addr, link)
56 local t = {type = "branch", addr = addr, link = link}
57 local branch_to_string = function(self)
58 return string.format("branch(%s,%s)", self.addr, self.link)
59 end
60 setmetatable(t, {__tostring = branch_to_string})
61 return t
62end
63
64-- parse a jump and returns its description
65function arm.parse_branch(fw, addr)
66 local opcode = hwp.read32(fw, arm.xlate_addr(addr))
67 if arm.is_thumb(addr) then
68 if bit32.band(opcode, 0xf800) ~= 0xf000 then
69 error("first instruction is not a bl(x) prefix")
70 end
71 local to_thumb = false
72 if bit32.band(opcode, 0xf8000000) == 0xf8000000 then
73 to_thumb = true
74 elseif bit32.band(opcode, 0xf8000000) ~= 0xe8000000 then
75 error("second instruction is not a bl(x) suffix")
76 end
77 local dest = hwp.make_addr(bit32.lshift(arm.sign_extend(opcode, 11), 12) +
78 arm.xlate_addr(addr).addr + 4 +
79 bit32.rshift(bit32.band(opcode, 0x7ff0000), 16) * 2, addr.section)
80 if to_thumb then
81 dest = arm.to_thumb(dest)
82 else
83 dest.addr = bit32.replace(dest.addr, 0, 0, 2)
84 end
85 return arm.make_branch(dest, true)
86 else
87 if bit32.band(opcode, 0xfe000000) == 0xfa000000 then -- BLX
88 local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
89 bit32.extract(opcode, 24) * 2 + arm.xlate_addr(addr).addr, addr.section)
90 return arm.make_branch(arm.to_thumb(dest), true)
91 elseif bit32.band(opcode, 0xfe000000) == 0xea000000 then -- B(L)
92 local dest = hwp.make_addr(arm.sign_extend(opcode, 24) * 4 + 8 +
93 arm.xlate_addr(addr).addr, addr.section)
94 return arm.make_branch(arm.to_arm(dest), bit32.extract(opcode, 24))
95 else
96 error("instruction is not a valid branch")
97 end
98 end
99end
100
101-- generate the encoding of a branch
102-- if the branch cannot be encoded using an immediate branch, it is generated
103-- with an indirect form like a ldr, using a pool
104function arm.write_branch(fw, addr, dest, pool)
105 local offset = arm.xlate_addr(dest.addr).addr - arm.xlate_addr(addr).addr
106 local exchange = arm.is_thumb(addr) ~= arm.is_thumb(dest.addr)
107 local opcode = 0
108 if arm.is_thumb(addr) then
109 if not dest.link then
110 return arm.write_load_pc(fw, addr, dest, pool)
111 end
112 offset = offset - 4 -- in Thumb, PC+4 relative
113 -- NOTE: BLX is undefined if the resulting offset has bit 0 set, follow
114 -- the procedure from the manual to ensure correct operation
115 if bit32.extract(offset, 1) ~= 0 then
116 offset = offset + 2
117 end
118 offset = offset / 2
119 if not arm.check_sign_truncation(offset, 22) then
120 error("destination is too far for immediate branch from thumb")
121 end
122 opcode = 0xf000 + -- BL/BLX prefix
123 bit32.band(bit32.rshift(offset, 11), 0x7ff) + -- offset (high part)
124 bit32.lshift(exchange and 0xe800 or 0xf800,16) + -- BLX suffix
125 bit32.lshift(bit32.band(offset, 0x7ff), 16) -- offset (low part)
126 else
127 offset = offset - 8 -- in ARM, PC+8 relative
128 if exchange and not dest.link then
129 return arm.write_load_pc(fw, addr, dest, pool)
130 end
131 offset = offset / 4
132 if not arm.check_sign_truncation(offset, 24) then
133 if pool == nil then
134 error("destination is too far for immediate branch from arm (no pool available)")
135 else
136 return arm.write_load_pc(fw, addr, dest, pool)
137 end
138 end
139 opcode = bit32.lshift(exchange and 0xf or 0xe, 28) + -- BLX / AL cond
140 bit32.lshift(0xa, 24) + -- branch
141 bit32.lshift(exchange and bit32.extract(offset, 1) or dest.link and 1 or 0, 24) + -- link / bit1
142 bit32.band(offset, 0xffffff)
143 end
144 return hwp.write32(fw, arm.xlate_addr(addr), opcode)
145end
146
147function arm.write_load_pc(fw, addr, dest, pool)
148 -- write pool
149 hwp.write32(fw, pool, dest.addr.addr)
150 -- write "ldr pc, [pool]"
151 local opcode
152 if arm.is_thumb(addr) then
153 error("unsupported pc load in thumb mode")
154 else
155 local offset = pool.addr - arm.xlate_addr(addr).addr - 8 -- ARM is PC+8 relative
156 local add = offset >= 0 and 1 or 0
157 offset = math.abs(offset)
158 opcode = bit32.lshift(0xe, 28) + -- AL cond
159 bit32.lshift(1, 26) + -- ldr/str
160 bit32.lshift(1, 24) + -- P
161 bit32.lshift(add, 23) + -- U
162 bit32.lshift(1, 20) + -- ldr
163 bit32.lshift(15, 16) + -- Rn=PC
164 bit32.lshift(15, 12) + -- Rd=PC
165 bit32.band(offset, 0xfff)
166 end
167 return hwp.write32(fw, arm.xlate_addr(addr), opcode)
168end
169
170-- generate the encoding of a "bx lr"
171function arm.write_return(fw, addr)
172 if arm.is_thumb(addr) then
173 error("unsupported return from thumb code")
174 end
175 local opcode = bit32.lshift(0xe, 28) + -- AL cond
176 bit32.lshift(0x12, 20) + -- BX
177 bit32.lshift(1, 4) + -- BX
178 bit32.lshift(0xfff, 8) + -- SBO
179 14 -- LR
180 hwp.write32(fw, arm.xlate_addr(addr), opcode)
181end
182
183function arm.write_xxx_regs(fw, addr, load)
184 if arm.is_thumb(addr) then
185 error("unsupported save/restore regs from thumb code")
186 end
187 -- STMFD sp!,{r0-r12, lr}
188 local opcode = bit32.lshift(0xe, 28) + -- AL cond
189 bit32.lshift(0x4, 25) + -- STM/LDM
190 bit32.lshift(load and 0 or 1,24) + -- P
191 bit32.lshift(load and 1 or 0, 23) + -- U
192 bit32.lshift(1, 21) +-- W
193 bit32.lshift(load and 1 or 0, 20) + -- L
194 bit32.lshift(13, 16) + -- base = SP
195 0x5fff -- R0-R12,LR
196 return hwp.write32(fw, addr, opcode)
197end
198
199function arm.write_save_regs(fw, addr)
200 return arm.write_xxx_regs(fw, addr, false)
201end
202
203function arm.write_restore_regs(fw, addr)
204 return arm.write_xxx_regs(fw, addr, true)
205end \ No newline at end of file