diff options
author | Zi Shen Lim <zlim.lnx@gmail.com> | 2014-08-27 00:15:20 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2014-09-08 09:39:19 -0400 |
commit | 17cac179888166a4e8e252d00ad511e999859293 (patch) | |
tree | 4522e67daecfff6e8b054de692bfa1274df1c292 | |
parent | 345e0d35ecdd7aff31881462a6f7786fda3241d9 (diff) |
arm64: introduce aarch64_insn_gen_load_store_reg()
Introduce function to generate load/store (register offset)
instructions.
Signed-off-by: Zi Shen Lim <zlim.lnx@gmail.com>
Acked-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r-- | arch/arm64/include/asm/insn.h | 20 | ||||
-rw-r--r-- | arch/arm64/kernel/insn.c | 62 |
2 files changed, 82 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index 86a8a9cc9ec6..5bc1cc391985 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h | |||
@@ -72,6 +72,7 @@ enum aarch64_insn_imm_type { | |||
72 | enum aarch64_insn_register_type { | 72 | enum aarch64_insn_register_type { |
73 | AARCH64_INSN_REGTYPE_RT, | 73 | AARCH64_INSN_REGTYPE_RT, |
74 | AARCH64_INSN_REGTYPE_RN, | 74 | AARCH64_INSN_REGTYPE_RN, |
75 | AARCH64_INSN_REGTYPE_RM, | ||
75 | }; | 76 | }; |
76 | 77 | ||
77 | enum aarch64_insn_register { | 78 | enum aarch64_insn_register { |
@@ -143,12 +144,26 @@ enum aarch64_insn_branch_type { | |||
143 | AARCH64_INSN_BRANCH_COMP_NONZERO, | 144 | AARCH64_INSN_BRANCH_COMP_NONZERO, |
144 | }; | 145 | }; |
145 | 146 | ||
147 | enum aarch64_insn_size_type { | ||
148 | AARCH64_INSN_SIZE_8, | ||
149 | AARCH64_INSN_SIZE_16, | ||
150 | AARCH64_INSN_SIZE_32, | ||
151 | AARCH64_INSN_SIZE_64, | ||
152 | }; | ||
153 | |||
154 | enum aarch64_insn_ldst_type { | ||
155 | AARCH64_INSN_LDST_LOAD_REG_OFFSET, | ||
156 | AARCH64_INSN_LDST_STORE_REG_OFFSET, | ||
157 | }; | ||
158 | |||
146 | #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ | 159 | #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ |
147 | static __always_inline bool aarch64_insn_is_##abbr(u32 code) \ | 160 | static __always_inline bool aarch64_insn_is_##abbr(u32 code) \ |
148 | { return (code & (mask)) == (val); } \ | 161 | { return (code & (mask)) == (val); } \ |
149 | static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \ | 162 | static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \ |
150 | { return (val); } | 163 | { return (val); } |
151 | 164 | ||
165 | __AARCH64_INSN_FUNCS(str_reg, 0x3FE0EC00, 0x38206800) | ||
166 | __AARCH64_INSN_FUNCS(ldr_reg, 0x3FE0EC00, 0x38606800) | ||
152 | __AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000) | 167 | __AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000) |
153 | __AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000) | 168 | __AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000) |
154 | __AARCH64_INSN_FUNCS(cbz, 0xFE000000, 0x34000000) | 169 | __AARCH64_INSN_FUNCS(cbz, 0xFE000000, 0x34000000) |
@@ -184,6 +199,11 @@ u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); | |||
184 | u32 aarch64_insn_gen_nop(void); | 199 | u32 aarch64_insn_gen_nop(void); |
185 | u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg, | 200 | u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg, |
186 | enum aarch64_insn_branch_type type); | 201 | enum aarch64_insn_branch_type type); |
202 | u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg, | ||
203 | enum aarch64_insn_register base, | ||
204 | enum aarch64_insn_register offset, | ||
205 | enum aarch64_insn_size_type size, | ||
206 | enum aarch64_insn_ldst_type type); | ||
187 | 207 | ||
188 | bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); | 208 | bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); |
189 | 209 | ||
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index b65edc02cf81..b882c85527dc 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c | |||
@@ -286,6 +286,9 @@ static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type, | |||
286 | case AARCH64_INSN_REGTYPE_RN: | 286 | case AARCH64_INSN_REGTYPE_RN: |
287 | shift = 5; | 287 | shift = 5; |
288 | break; | 288 | break; |
289 | case AARCH64_INSN_REGTYPE_RM: | ||
290 | shift = 16; | ||
291 | break; | ||
289 | default: | 292 | default: |
290 | pr_err("%s: unknown register type encoding %d\n", __func__, | 293 | pr_err("%s: unknown register type encoding %d\n", __func__, |
291 | type); | 294 | type); |
@@ -298,6 +301,35 @@ static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type, | |||
298 | return insn; | 301 | return insn; |
299 | } | 302 | } |
300 | 303 | ||
304 | static u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type, | ||
305 | u32 insn) | ||
306 | { | ||
307 | u32 size; | ||
308 | |||
309 | switch (type) { | ||
310 | case AARCH64_INSN_SIZE_8: | ||
311 | size = 0; | ||
312 | break; | ||
313 | case AARCH64_INSN_SIZE_16: | ||
314 | size = 1; | ||
315 | break; | ||
316 | case AARCH64_INSN_SIZE_32: | ||
317 | size = 2; | ||
318 | break; | ||
319 | case AARCH64_INSN_SIZE_64: | ||
320 | size = 3; | ||
321 | break; | ||
322 | default: | ||
323 | pr_err("%s: unknown size encoding %d\n", __func__, type); | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | insn &= ~GENMASK(31, 30); | ||
328 | insn |= size << 30; | ||
329 | |||
330 | return insn; | ||
331 | } | ||
332 | |||
301 | static inline long branch_imm_common(unsigned long pc, unsigned long addr, | 333 | static inline long branch_imm_common(unsigned long pc, unsigned long addr, |
302 | long range) | 334 | long range) |
303 | { | 335 | { |
@@ -428,3 +460,33 @@ u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg, | |||
428 | 460 | ||
429 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, reg); | 461 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, reg); |
430 | } | 462 | } |
463 | |||
464 | u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg, | ||
465 | enum aarch64_insn_register base, | ||
466 | enum aarch64_insn_register offset, | ||
467 | enum aarch64_insn_size_type size, | ||
468 | enum aarch64_insn_ldst_type type) | ||
469 | { | ||
470 | u32 insn; | ||
471 | |||
472 | switch (type) { | ||
473 | case AARCH64_INSN_LDST_LOAD_REG_OFFSET: | ||
474 | insn = aarch64_insn_get_ldr_reg_value(); | ||
475 | break; | ||
476 | case AARCH64_INSN_LDST_STORE_REG_OFFSET: | ||
477 | insn = aarch64_insn_get_str_reg_value(); | ||
478 | break; | ||
479 | default: | ||
480 | BUG_ON(1); | ||
481 | } | ||
482 | |||
483 | insn = aarch64_insn_encode_ldst_size(size, insn); | ||
484 | |||
485 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); | ||
486 | |||
487 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, | ||
488 | base); | ||
489 | |||
490 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, | ||
491 | offset); | ||
492 | } | ||