aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZi Shen Lim <zlim.lnx@gmail.com>2014-08-27 00:15:17 -0400
committerWill Deacon <will.deacon@arm.com>2014-09-08 09:39:19 -0400
commit617d2fbc45233bed182accd3507d0df4d213492c (patch)
tree291332444d75fb00827784f4f0375bb963b52e7d
parenta4ceab1adbe960c781e9e2f659d7f7840eefd786 (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.h57
-rw-r--r--arch/arm64/kernel/insn.c88
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
72enum aarch64_insn_register_type {
73 AARCH64_INSN_REGTYPE_RT,
74};
75
76enum 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
114enum aarch64_insn_variant {
115 AARCH64_INSN_VARIANT_32BIT,
116 AARCH64_INSN_VARIANT_64BIT
117};
118
70enum aarch64_insn_branch_type { 119enum 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);
98u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, 151u32 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);
153u32 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);
100u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); 157u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op);
101u32 aarch64_insn_gen_nop(void); 158u32 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
26static int aarch64_insn_encoding_class[] = { 30static 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
267u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, 271static 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
298static 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
315u32 __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
337u32 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
296u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) 374u32 __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;