diff options
| -rw-r--r-- | arch/arm64/include/asm/insn.h | 10 | ||||
| -rw-r--r-- | arch/arm64/kernel/insn.c | 40 |
2 files changed, 50 insertions, 0 deletions
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index fb4466022bd0..c44ad39ed310 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h | |||
| @@ -66,6 +66,11 @@ enum aarch64_insn_imm_type { | |||
| 66 | AARCH64_INSN_IMM_MAX | 66 | AARCH64_INSN_IMM_MAX |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | enum aarch64_insn_branch_type { | ||
| 70 | AARCH64_INSN_BRANCH_NOLINK, | ||
| 71 | AARCH64_INSN_BRANCH_LINK, | ||
| 72 | }; | ||
| 73 | |||
| 69 | #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ | 74 | #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ |
| 70 | static __always_inline bool aarch64_insn_is_##abbr(u32 code) \ | 75 | static __always_inline bool aarch64_insn_is_##abbr(u32 code) \ |
| 71 | { return (code & (mask)) == (val); } \ | 76 | { return (code & (mask)) == (val); } \ |
| @@ -89,6 +94,11 @@ int aarch64_insn_write(void *addr, u32 insn); | |||
| 89 | enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn); | 94 | enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn); |
| 90 | u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | 95 | u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, |
| 91 | u32 insn, u64 imm); | 96 | u32 insn, u64 imm); |
| 97 | u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | ||
| 98 | enum aarch64_insn_branch_type type); | ||
| 99 | u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); | ||
| 100 | u32 aarch64_insn_gen_nop(void); | ||
| 101 | |||
| 92 | bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); | 102 | bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); |
| 93 | 103 | ||
| 94 | int aarch64_insn_patch_text_nosync(void *addr, u32 insn); | 104 | int aarch64_insn_patch_text_nosync(void *addr, u32 insn); |
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index 7df08075fc7a..92f36835486b 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | */ | 16 | */ |
| 17 | #include <linux/bitops.h> | ||
| 17 | #include <linux/compiler.h> | 18 | #include <linux/compiler.h> |
| 18 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
| 19 | #include <linux/smp.h> | 20 | #include <linux/smp.h> |
| @@ -262,3 +263,42 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | |||
| 262 | 263 | ||
| 263 | return insn; | 264 | return insn; |
| 264 | } | 265 | } |
| 266 | |||
| 267 | u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | ||
| 268 | enum aarch64_insn_branch_type type) | ||
| 269 | { | ||
| 270 | u32 insn; | ||
| 271 | long offset; | ||
| 272 | |||
| 273 | /* | ||
| 274 | * PC: A 64-bit Program Counter holding the address of the current | ||
| 275 | * instruction. A64 instructions must be word-aligned. | ||
| 276 | */ | ||
| 277 | BUG_ON((pc & 0x3) || (addr & 0x3)); | ||
| 278 | |||
| 279 | /* | ||
| 280 | * B/BL support [-128M, 128M) offset | ||
| 281 | * ARM64 virtual address arrangement guarantees all kernel and module | ||
| 282 | * texts are within +/-128M. | ||
| 283 | */ | ||
| 284 | offset = ((long)addr - (long)pc); | ||
| 285 | BUG_ON(offset < -SZ_128M || offset >= SZ_128M); | ||
| 286 | |||
| 287 | if (type == AARCH64_INSN_BRANCH_LINK) | ||
| 288 | insn = aarch64_insn_get_bl_value(); | ||
| 289 | else | ||
| 290 | insn = aarch64_insn_get_b_value(); | ||
| 291 | |||
| 292 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn, | ||
| 293 | offset >> 2); | ||
| 294 | } | ||
| 295 | |||
| 296 | u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) | ||
| 297 | { | ||
| 298 | return aarch64_insn_get_hint_value() | op; | ||
| 299 | } | ||
| 300 | |||
| 301 | u32 __kprobes aarch64_insn_gen_nop(void) | ||
| 302 | { | ||
| 303 | return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP); | ||
| 304 | } | ||
