diff options
author | Zi Shen Lim <zlim.lnx@gmail.com> | 2014-08-27 00:15:17 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2014-09-08 09:39:19 -0400 |
commit | 617d2fbc45233bed182accd3507d0df4d213492c (patch) | |
tree | 291332444d75fb00827784f4f0375bb963b52e7d | |
parent | a4ceab1adbe960c781e9e2f659d7f7840eefd786 (diff) |
arm64: introduce aarch64_insn_gen_comp_branch_imm()
Introduce function to generate compare & branch (immediate)
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 | 57 | ||||
-rw-r--r-- | arch/arm64/kernel/insn.c | 88 |
2 files changed, 140 insertions, 5 deletions
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index dc1f73b13e74..a98c4954b380 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h | |||
@@ -2,6 +2,8 @@ | |||
2 | * Copyright (C) 2013 Huawei Ltd. | 2 | * Copyright (C) 2013 Huawei Ltd. |
3 | * Author: Jiang Liu <liuj97@gmail.com> | 3 | * Author: Jiang Liu <liuj97@gmail.com> |
4 | * | 4 | * |
5 | * Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com> | ||
6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
@@ -67,9 +69,58 @@ enum aarch64_insn_imm_type { | |||
67 | AARCH64_INSN_IMM_MAX | 69 | AARCH64_INSN_IMM_MAX |
68 | }; | 70 | }; |
69 | 71 | ||
72 | enum aarch64_insn_register_type { | ||
73 | AARCH64_INSN_REGTYPE_RT, | ||
74 | }; | ||
75 | |||
76 | enum aarch64_insn_register { | ||
77 | AARCH64_INSN_REG_0 = 0, | ||
78 | AARCH64_INSN_REG_1 = 1, | ||
79 | AARCH64_INSN_REG_2 = 2, | ||
80 | AARCH64_INSN_REG_3 = 3, | ||
81 | AARCH64_INSN_REG_4 = 4, | ||
82 | AARCH64_INSN_REG_5 = 5, | ||
83 | AARCH64_INSN_REG_6 = 6, | ||
84 | AARCH64_INSN_REG_7 = 7, | ||
85 | AARCH64_INSN_REG_8 = 8, | ||
86 | AARCH64_INSN_REG_9 = 9, | ||
87 | AARCH64_INSN_REG_10 = 10, | ||
88 | AARCH64_INSN_REG_11 = 11, | ||
89 | AARCH64_INSN_REG_12 = 12, | ||
90 | AARCH64_INSN_REG_13 = 13, | ||
91 | AARCH64_INSN_REG_14 = 14, | ||
92 | AARCH64_INSN_REG_15 = 15, | ||
93 | AARCH64_INSN_REG_16 = 16, | ||
94 | AARCH64_INSN_REG_17 = 17, | ||
95 | AARCH64_INSN_REG_18 = 18, | ||
96 | AARCH64_INSN_REG_19 = 19, | ||
97 | AARCH64_INSN_REG_20 = 20, | ||
98 | AARCH64_INSN_REG_21 = 21, | ||
99 | AARCH64_INSN_REG_22 = 22, | ||
100 | AARCH64_INSN_REG_23 = 23, | ||
101 | AARCH64_INSN_REG_24 = 24, | ||
102 | AARCH64_INSN_REG_25 = 25, | ||
103 | AARCH64_INSN_REG_26 = 26, | ||
104 | AARCH64_INSN_REG_27 = 27, | ||
105 | AARCH64_INSN_REG_28 = 28, | ||
106 | AARCH64_INSN_REG_29 = 29, | ||
107 | AARCH64_INSN_REG_FP = 29, /* Frame pointer */ | ||
108 | AARCH64_INSN_REG_30 = 30, | ||
109 | AARCH64_INSN_REG_LR = 30, /* Link register */ | ||
110 | AARCH64_INSN_REG_ZR = 31, /* Zero: as source register */ | ||
111 | AARCH64_INSN_REG_SP = 31 /* Stack pointer: as load/store base reg */ | ||
112 | }; | ||
113 | |||
114 | enum aarch64_insn_variant { | ||
115 | AARCH64_INSN_VARIANT_32BIT, | ||
116 | AARCH64_INSN_VARIANT_64BIT | ||
117 | }; | ||
118 | |||
70 | enum aarch64_insn_branch_type { | 119 | enum aarch64_insn_branch_type { |
71 | AARCH64_INSN_BRANCH_NOLINK, | 120 | AARCH64_INSN_BRANCH_NOLINK, |
72 | AARCH64_INSN_BRANCH_LINK, | 121 | AARCH64_INSN_BRANCH_LINK, |
122 | AARCH64_INSN_BRANCH_COMP_ZERO, | ||
123 | AARCH64_INSN_BRANCH_COMP_NONZERO, | ||
73 | }; | 124 | }; |
74 | 125 | ||
75 | #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ | 126 | #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ |
@@ -80,6 +131,8 @@ static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \ | |||
80 | 131 | ||
81 | __AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000) | 132 | __AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000) |
82 | __AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000) | 133 | __AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000) |
134 | __AARCH64_INSN_FUNCS(cbz, 0xFE000000, 0x34000000) | ||
135 | __AARCH64_INSN_FUNCS(cbnz, 0xFE000000, 0x35000000) | ||
83 | __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001) | 136 | __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001) |
84 | __AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002) | 137 | __AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002) |
85 | __AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003) | 138 | __AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003) |
@@ -97,6 +150,10 @@ u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | |||
97 | u32 insn, u64 imm); | 150 | u32 insn, u64 imm); |
98 | u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | 151 | u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, |
99 | enum aarch64_insn_branch_type type); | 152 | enum aarch64_insn_branch_type type); |
153 | u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr, | ||
154 | enum aarch64_insn_register reg, | ||
155 | enum aarch64_insn_variant variant, | ||
156 | enum aarch64_insn_branch_type type); | ||
100 | u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); | 157 | u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); |
101 | u32 aarch64_insn_gen_nop(void); | 158 | u32 aarch64_insn_gen_nop(void); |
102 | 159 | ||
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index 92f36835486b..d9f7827c5058 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * Copyright (C) 2013 Huawei Ltd. | 2 | * Copyright (C) 2013 Huawei Ltd. |
3 | * Author: Jiang Liu <liuj97@gmail.com> | 3 | * Author: Jiang Liu <liuj97@gmail.com> |
4 | * | 4 | * |
5 | * Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com> | ||
6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
@@ -23,6 +25,8 @@ | |||
23 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
24 | #include <asm/insn.h> | 26 | #include <asm/insn.h> |
25 | 27 | ||
28 | #define AARCH64_INSN_SF_BIT BIT(31) | ||
29 | |||
26 | static int aarch64_insn_encoding_class[] = { | 30 | static int aarch64_insn_encoding_class[] = { |
27 | AARCH64_INSN_CLS_UNKNOWN, | 31 | AARCH64_INSN_CLS_UNKNOWN, |
28 | AARCH64_INSN_CLS_UNKNOWN, | 32 | AARCH64_INSN_CLS_UNKNOWN, |
@@ -264,10 +268,36 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | |||
264 | return insn; | 268 | return insn; |
265 | } | 269 | } |
266 | 270 | ||
267 | u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | 271 | static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type, |
268 | enum aarch64_insn_branch_type type) | 272 | u32 insn, |
273 | enum aarch64_insn_register reg) | ||
274 | { | ||
275 | int shift; | ||
276 | |||
277 | if (reg < AARCH64_INSN_REG_0 || reg > AARCH64_INSN_REG_SP) { | ||
278 | pr_err("%s: unknown register encoding %d\n", __func__, reg); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | switch (type) { | ||
283 | case AARCH64_INSN_REGTYPE_RT: | ||
284 | shift = 0; | ||
285 | break; | ||
286 | default: | ||
287 | pr_err("%s: unknown register type encoding %d\n", __func__, | ||
288 | type); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | insn &= ~(GENMASK(4, 0) << shift); | ||
293 | insn |= reg << shift; | ||
294 | |||
295 | return insn; | ||
296 | } | ||
297 | |||
298 | static inline long branch_imm_common(unsigned long pc, unsigned long addr, | ||
299 | long range) | ||
269 | { | 300 | { |
270 | u32 insn; | ||
271 | long offset; | 301 | long offset; |
272 | 302 | ||
273 | /* | 303 | /* |
@@ -276,13 +306,24 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | |||
276 | */ | 306 | */ |
277 | BUG_ON((pc & 0x3) || (addr & 0x3)); | 307 | BUG_ON((pc & 0x3) || (addr & 0x3)); |
278 | 308 | ||
309 | offset = ((long)addr - (long)pc); | ||
310 | BUG_ON(offset < -range || offset >= range); | ||
311 | |||
312 | return offset; | ||
313 | } | ||
314 | |||
315 | u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | ||
316 | enum aarch64_insn_branch_type type) | ||
317 | { | ||
318 | u32 insn; | ||
319 | long offset; | ||
320 | |||
279 | /* | 321 | /* |
280 | * B/BL support [-128M, 128M) offset | 322 | * B/BL support [-128M, 128M) offset |
281 | * ARM64 virtual address arrangement guarantees all kernel and module | 323 | * ARM64 virtual address arrangement guarantees all kernel and module |
282 | * texts are within +/-128M. | 324 | * texts are within +/-128M. |
283 | */ | 325 | */ |
284 | offset = ((long)addr - (long)pc); | 326 | offset = branch_imm_common(pc, addr, SZ_128M); |
285 | BUG_ON(offset < -SZ_128M || offset >= SZ_128M); | ||
286 | 327 | ||
287 | if (type == AARCH64_INSN_BRANCH_LINK) | 328 | if (type == AARCH64_INSN_BRANCH_LINK) |
288 | insn = aarch64_insn_get_bl_value(); | 329 | insn = aarch64_insn_get_bl_value(); |
@@ -293,6 +334,43 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | |||
293 | offset >> 2); | 334 | offset >> 2); |
294 | } | 335 | } |
295 | 336 | ||
337 | u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr, | ||
338 | enum aarch64_insn_register reg, | ||
339 | enum aarch64_insn_variant variant, | ||
340 | enum aarch64_insn_branch_type type) | ||
341 | { | ||
342 | u32 insn; | ||
343 | long offset; | ||
344 | |||
345 | offset = branch_imm_common(pc, addr, SZ_1M); | ||
346 | |||
347 | switch (type) { | ||
348 | case AARCH64_INSN_BRANCH_COMP_ZERO: | ||
349 | insn = aarch64_insn_get_cbz_value(); | ||
350 | break; | ||
351 | case AARCH64_INSN_BRANCH_COMP_NONZERO: | ||
352 | insn = aarch64_insn_get_cbnz_value(); | ||
353 | break; | ||
354 | default: | ||
355 | BUG_ON(1); | ||
356 | } | ||
357 | |||
358 | switch (variant) { | ||
359 | case AARCH64_INSN_VARIANT_32BIT: | ||
360 | break; | ||
361 | case AARCH64_INSN_VARIANT_64BIT: | ||
362 | insn |= AARCH64_INSN_SF_BIT; | ||
363 | break; | ||
364 | default: | ||
365 | BUG_ON(1); | ||
366 | } | ||
367 | |||
368 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); | ||
369 | |||
370 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, | ||
371 | offset >> 2); | ||
372 | } | ||
373 | |||
296 | u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) | 374 | u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) |
297 | { | 375 | { |
298 | return aarch64_insn_get_hint_value() | op; | 376 | return aarch64_insn_get_hint_value() | op; |