Commit 617d2fbc authored by Zi Shen Lim's avatar Zi Shen Lim Committed by Will Deacon

arm64: introduce aarch64_insn_gen_comp_branch_imm()

Introduce function to generate compare & branch (immediate)
instructions.
Signed-off-by: default avatarZi Shen Lim <zlim.lnx@gmail.com>
Acked-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent a4ceab1a
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
* Copyright (C) 2013 Huawei Ltd. * Copyright (C) 2013 Huawei Ltd.
* Author: Jiang Liu <liuj97@gmail.com> * Author: Jiang Liu <liuj97@gmail.com>
* *
* Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com>
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
...@@ -67,9 +69,58 @@ enum aarch64_insn_imm_type { ...@@ -67,9 +69,58 @@ enum aarch64_insn_imm_type {
AARCH64_INSN_IMM_MAX AARCH64_INSN_IMM_MAX
}; };
enum aarch64_insn_register_type {
AARCH64_INSN_REGTYPE_RT,
};
enum aarch64_insn_register {
AARCH64_INSN_REG_0 = 0,
AARCH64_INSN_REG_1 = 1,
AARCH64_INSN_REG_2 = 2,
AARCH64_INSN_REG_3 = 3,
AARCH64_INSN_REG_4 = 4,
AARCH64_INSN_REG_5 = 5,
AARCH64_INSN_REG_6 = 6,
AARCH64_INSN_REG_7 = 7,
AARCH64_INSN_REG_8 = 8,
AARCH64_INSN_REG_9 = 9,
AARCH64_INSN_REG_10 = 10,
AARCH64_INSN_REG_11 = 11,
AARCH64_INSN_REG_12 = 12,
AARCH64_INSN_REG_13 = 13,
AARCH64_INSN_REG_14 = 14,
AARCH64_INSN_REG_15 = 15,
AARCH64_INSN_REG_16 = 16,
AARCH64_INSN_REG_17 = 17,
AARCH64_INSN_REG_18 = 18,
AARCH64_INSN_REG_19 = 19,
AARCH64_INSN_REG_20 = 20,
AARCH64_INSN_REG_21 = 21,
AARCH64_INSN_REG_22 = 22,
AARCH64_INSN_REG_23 = 23,
AARCH64_INSN_REG_24 = 24,
AARCH64_INSN_REG_25 = 25,
AARCH64_INSN_REG_26 = 26,
AARCH64_INSN_REG_27 = 27,
AARCH64_INSN_REG_28 = 28,
AARCH64_INSN_REG_29 = 29,
AARCH64_INSN_REG_FP = 29, /* Frame pointer */
AARCH64_INSN_REG_30 = 30,
AARCH64_INSN_REG_LR = 30, /* Link register */
AARCH64_INSN_REG_ZR = 31, /* Zero: as source register */
AARCH64_INSN_REG_SP = 31 /* Stack pointer: as load/store base reg */
};
enum aarch64_insn_variant {
AARCH64_INSN_VARIANT_32BIT,
AARCH64_INSN_VARIANT_64BIT
};
enum aarch64_insn_branch_type { enum aarch64_insn_branch_type {
AARCH64_INSN_BRANCH_NOLINK, AARCH64_INSN_BRANCH_NOLINK,
AARCH64_INSN_BRANCH_LINK, AARCH64_INSN_BRANCH_LINK,
AARCH64_INSN_BRANCH_COMP_ZERO,
AARCH64_INSN_BRANCH_COMP_NONZERO,
}; };
#define __AARCH64_INSN_FUNCS(abbr, mask, val) \ #define __AARCH64_INSN_FUNCS(abbr, mask, val) \
...@@ -80,6 +131,8 @@ static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \ ...@@ -80,6 +131,8 @@ static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \
__AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000) __AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000)
__AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000) __AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000)
__AARCH64_INSN_FUNCS(cbz, 0xFE000000, 0x34000000)
__AARCH64_INSN_FUNCS(cbnz, 0xFE000000, 0x35000000)
__AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001) __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001)
__AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002) __AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002)
__AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003) __AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003)
...@@ -97,6 +150,10 @@ u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, ...@@ -97,6 +150,10 @@ u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
u32 insn, u64 imm); u32 insn, u64 imm);
u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
enum aarch64_insn_branch_type type); enum aarch64_insn_branch_type type);
u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
enum aarch64_insn_register reg,
enum aarch64_insn_variant variant,
enum aarch64_insn_branch_type type);
u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op);
u32 aarch64_insn_gen_nop(void); u32 aarch64_insn_gen_nop(void);
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
* Copyright (C) 2013 Huawei Ltd. * Copyright (C) 2013 Huawei Ltd.
* Author: Jiang Liu <liuj97@gmail.com> * Author: Jiang Liu <liuj97@gmail.com>
* *
* Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com>
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
...@@ -23,6 +25,8 @@ ...@@ -23,6 +25,8 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/insn.h> #include <asm/insn.h>
#define AARCH64_INSN_SF_BIT BIT(31)
static int aarch64_insn_encoding_class[] = { static int aarch64_insn_encoding_class[] = {
AARCH64_INSN_CLS_UNKNOWN, AARCH64_INSN_CLS_UNKNOWN,
AARCH64_INSN_CLS_UNKNOWN, AARCH64_INSN_CLS_UNKNOWN,
...@@ -264,10 +268,36 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, ...@@ -264,10 +268,36 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
return insn; return insn;
} }
u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type,
enum aarch64_insn_branch_type type) u32 insn,
enum aarch64_insn_register reg)
{
int shift;
if (reg < AARCH64_INSN_REG_0 || reg > AARCH64_INSN_REG_SP) {
pr_err("%s: unknown register encoding %d\n", __func__, reg);
return 0;
}
switch (type) {
case AARCH64_INSN_REGTYPE_RT:
shift = 0;
break;
default:
pr_err("%s: unknown register type encoding %d\n", __func__,
type);
return 0;
}
insn &= ~(GENMASK(4, 0) << shift);
insn |= reg << shift;
return insn;
}
static inline long branch_imm_common(unsigned long pc, unsigned long addr,
long range)
{ {
u32 insn;
long offset; long offset;
/* /*
...@@ -276,13 +306,24 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, ...@@ -276,13 +306,24 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
*/ */
BUG_ON((pc & 0x3) || (addr & 0x3)); BUG_ON((pc & 0x3) || (addr & 0x3));
offset = ((long)addr - (long)pc);
BUG_ON(offset < -range || offset >= range);
return offset;
}
u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
enum aarch64_insn_branch_type type)
{
u32 insn;
long offset;
/* /*
* B/BL support [-128M, 128M) offset * B/BL support [-128M, 128M) offset
* ARM64 virtual address arrangement guarantees all kernel and module * ARM64 virtual address arrangement guarantees all kernel and module
* texts are within +/-128M. * texts are within +/-128M.
*/ */
offset = ((long)addr - (long)pc); offset = branch_imm_common(pc, addr, SZ_128M);
BUG_ON(offset < -SZ_128M || offset >= SZ_128M);
if (type == AARCH64_INSN_BRANCH_LINK) if (type == AARCH64_INSN_BRANCH_LINK)
insn = aarch64_insn_get_bl_value(); insn = aarch64_insn_get_bl_value();
...@@ -293,6 +334,43 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, ...@@ -293,6 +334,43 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
offset >> 2); offset >> 2);
} }
u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
enum aarch64_insn_register reg,
enum aarch64_insn_variant variant,
enum aarch64_insn_branch_type type)
{
u32 insn;
long offset;
offset = branch_imm_common(pc, addr, SZ_1M);
switch (type) {
case AARCH64_INSN_BRANCH_COMP_ZERO:
insn = aarch64_insn_get_cbz_value();
break;
case AARCH64_INSN_BRANCH_COMP_NONZERO:
insn = aarch64_insn_get_cbnz_value();
break;
default:
BUG_ON(1);
}
switch (variant) {
case AARCH64_INSN_VARIANT_32BIT:
break;
case AARCH64_INSN_VARIANT_64BIT:
insn |= AARCH64_INSN_SF_BIT;
break;
default:
BUG_ON(1);
}
insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
offset >> 2);
}
u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op)
{ {
return aarch64_insn_get_hint_value() | op; return aarch64_insn_get_hint_value() | op;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment