[PATCH v2 5/7] arm64: make arm uprobes code reusable by arm64

From: Maciej Slodczyk
Date: Wed Sep 26 2018 - 08:12:25 EST


There are many segments of ARM32 uprobes code that is very specific to
32-bit ARM arch and many differences between the two architectures that
could be made portable (e.g. register numbers). Exclude the ARM32 specific
code from ARM64 compilation process and make adjustments in ARM32 uprobes
code to be accessible by ARM64 code.

Signed-off-by: Maciej Slodczyk <m.slodczyk2@xxxxxxxxxxxxxxxxxxx>
---
arch/arm/include/asm/probes.h | 8 ++
arch/arm/include/asm/ptrace.h | 32 ++++++++
arch/arm/include/asm/uprobes.h | 2 +-
arch/arm/probes/uprobes/core.c | 6 +-
arch/arm64/include/asm/probes.h | 24 ++++--
arch/arm64/include/asm/ptrace.h | 21 +++++
arch/arm64/include/asm/uprobes.h | 21 ++++-
arch/arm64/kernel/probes/Makefile | 2 +
arch/arm64/kernel/probes/decode-insn.c | 28 +++----
arch/arm64/kernel/probes/decode-insn.h | 15 ++--
arch/arm64/kernel/probes/kprobes.c | 4 +-
arch/arm64/kernel/probes/simulate-insn.c | 16 ++--
arch/arm64/kernel/probes/simulate-insn.h | 16 ++--
arch/arm64/kernel/probes/uprobes.c | 12 +--
lib/probes/Makefile | 1 +
lib/probes/arm/Makefile | 5 ++
lib/probes/arm/actions-arm.c | 130 ++++++++++++++++++++++++++++---
lib/probes/arm/decode-arm.c | 47 ++++++-----
lib/probes/arm/decode.c | 12 ++-
lib/probes/arm/decode.h | 41 ++++++----
20 files changed, 335 insertions(+), 108 deletions(-)

diff --git a/arch/arm/include/asm/probes.h b/arch/arm/include/asm/probes.h
index 991c912..b24938f 100644
--- a/arch/arm/include/asm/probes.h
+++ b/arch/arm/include/asm/probes.h
@@ -21,6 +21,13 @@

#ifndef __ASSEMBLY__

+enum probes_insn {
+ INSN_REJECTED,
+ INSN_GOOD_NO_SLOT,
+ INSN_GOOD,
+};
+
+
typedef u32 probes_opcode_t;

struct arch_probes_insn;
@@ -45,6 +52,7 @@ struct arch_probes_insn {
bool kprobe_direct_exec;
};

+extern probes_check_cc * const probes_condition_checks[];
#endif /* __ASSEMBLY__ */

/*
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index c7cdbb4..99f19f2 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -93,6 +93,8 @@ static inline long regs_return_value(struct pt_regs *regs)
}

#define instruction_pointer(regs) (regs)->ARM_pc
+#define link_register(regs) ((regs)->ARM_lr)
+#define state_register(regs) ((regs)->ARM_cpsr)

#ifdef CONFIG_THUMB2_KERNEL
#define frame_pointer(regs) (regs)->ARM_r7
@@ -106,6 +108,35 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
instruction_pointer(regs) = val;
}

+static inline void link_register_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ link_register(regs) = val;
+}
+
+static inline void state_register_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ state_register(regs) = val;
+}
+
+/*
+ * Read a register given an architectural register index r.
+ */
+static inline unsigned long pt_regs_read_reg(const struct pt_regs *regs, int r)
+{
+ return regs->uregs[r];
+}
+
+/*
+ * Write a register given an architectural register index r.
+ */
+static inline void pt_regs_write_reg(struct pt_regs *regs, int r,
+ unsigned long val)
+{
+ regs->uregs[r] = val;
+}
+
#ifdef CONFIG_SMP
extern unsigned long profile_pc(struct pt_regs *regs);
#else
@@ -167,5 +198,6 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs)
((current_stack_pointer | (THREAD_SIZE - 1)) - 7) - 1; \
})

+#define ARM_COMPAT_LR_OFFSET 0
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm/include/asm/uprobes.h b/arch/arm/include/asm/uprobes.h
index 9472c20..536af7f 100644
--- a/arch/arm/include/asm/uprobes.h
+++ b/arch/arm/include/asm/uprobes.h
@@ -39,7 +39,7 @@ struct arch_uprobe {
void (*posthandler)(struct arch_uprobe *auprobe,
struct arch_uprobe_task *autask,
struct pt_regs *regs);
- struct arch_probes_insn asi;
+ struct arch_probes_insn api;
};

#endif
diff --git a/arch/arm/probes/uprobes/core.c b/arch/arm/probes/uprobes/core.c
index 90d0954..8abec17 100644
--- a/arch/arm/probes/uprobes/core.c
+++ b/arch/arm/probes/uprobes/core.c
@@ -38,7 +38,7 @@ int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,

bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
{
- if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
+ if (!auprobe->api.insn_check_cc(regs->ARM_cpsr)) {
regs->ARM_pc += 4;
return true;
}
@@ -55,7 +55,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)

opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);

- auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
+ auprobe->api.insn_singlestep(opcode, &auprobe->api, regs);

return true;
}
@@ -87,7 +87,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
auprobe->ixol[0] = __opcode_to_mem_arm(insn);
auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);

- ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
+ ret = arm_probes_decode_insn(insn, &auprobe->api, false,
uprobes_probes_actions, NULL);
switch (ret) {
case INSN_REJECTED:
diff --git a/arch/arm64/include/asm/probes.h b/arch/arm64/include/asm/probes.h
index 1747e9a..78c0788 100644
--- a/arch/arm64/include/asm/probes.h
+++ b/arch/arm64/include/asm/probes.h
@@ -15,25 +15,33 @@
#ifndef _ARM_PROBES_H
#define _ARM_PROBES_H

-typedef u32 probe_opcode_t;
-struct arch_probe_insn;
+enum probes_insn {
+ INSN_REJECTED,
+ INSN_GOOD_NO_SLOT,
+ INSN_GOOD,
+};
+
+typedef u32 probes_opcode_t;
+struct arch_probes_insn;

-typedef void (probes_handler_t) (u32 opcode,
- struct arch_probe_insn *api,
+typedef void (probes_insn_handler_t) (u32 opcode,
+ struct arch_probes_insn *api,
struct pt_regs *);

+typedef unsigned long (probes_check_cc)(unsigned long);
+
/* architecture specific copy of original instruction */
-struct arch_probe_insn {
- probe_opcode_t *insn;
+struct arch_probes_insn {
+ probes_opcode_t *insn;
pstate_check_t *pstate_cc;
- probes_handler_t *handler;
+ probes_insn_handler_t *insn_handler;
/* restore address after step xol */
unsigned long restore;
};
#ifdef CONFIG_KPROBES
typedef u32 kprobe_opcode_t;
struct arch_specific_insn {
- struct arch_probe_insn api;
+ struct arch_probes_insn api;
};
#endif

diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 177b851..a884fd6 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -301,8 +301,29 @@ static inline void procedure_link_pointer_set(struct pt_regs *regs,
procedure_link_pointer(regs) = val;
}

+
+#define link_register(regs) ((regs)->compat_lr)
+
+static inline void link_register_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ link_register(regs) = val;
+}
+
+#define state_register(regs) ((regs)->pstate)
+
+static inline void state_register_set(struct pt_regs *regs,
+ unsigned long val)
+{
+ state_register(regs) = val;
+}
+
+
#undef profile_pc
extern unsigned long profile_pc(struct pt_regs *regs);

+
+#define ARM_COMPAT_LR_OFFSET 4
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm64/include/asm/uprobes.h b/arch/arm64/include/asm/uprobes.h
index 8d00407..b984634 100644
--- a/arch/arm64/include/asm/uprobes.h
+++ b/arch/arm64/include/asm/uprobes.h
@@ -22,6 +22,12 @@
typedef u32 uprobe_opcode_t;

struct arch_uprobe_task {
+ u64 backup;
+};
+
+enum uprobe_arch {
+ UPROBE_AARCH64,
+ UPROBE_AARCH32
};

struct arch_uprobe {
@@ -29,8 +35,21 @@ struct arch_uprobe {
u8 insn[MAX_UINSN_BYTES];
u8 ixol[MAX_UINSN_BYTES];
};
- struct arch_probe_insn api;
+
+ probes_opcode_t orig_insn;
+ probes_opcode_t bp_insn;
+
+ struct arch_probes_insn api;
bool simulate;
+ u64 pcreg;
+ enum uprobe_arch arch;
+
+ void (*prehandler)(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs);
+ void (*posthandler)(struct arch_uprobe *auprobe,
+ struct arch_uprobe_task *autask,
+ struct pt_regs *regs);
};

#endif
diff --git a/arch/arm64/kernel/probes/Makefile b/arch/arm64/kernel/probes/Makefile
index 8e4be92..c5d57e3 100644
--- a/arch/arm64/kernel/probes/Makefile
+++ b/arch/arm64/kernel/probes/Makefile
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(srctree)/lib/probes/arm/
+ccflags-y += -I$(srctree)/arch/arm64/kernel/probes/
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o \
kprobes_trampoline.o \
simulate-insn.o
diff --git a/arch/arm64/kernel/probes/decode-insn.c b/arch/arm64/kernel/probes/decode-insn.c
index 6bf6657..a0597a2 100644
--- a/arch/arm64/kernel/probes/decode-insn.c
+++ b/arch/arm64/kernel/probes/decode-insn.c
@@ -77,8 +77,8 @@ static bool __kprobes aarch64_insn_is_steppable(u32 insn)
* INSN_GOOD If instruction is supported and uses instruction slot,
* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
*/
-enum probe_insn __kprobes
-arm_probe_decode_insn(probe_opcode_t insn, struct arch_probe_insn *api)
+enum probes_insn __kprobes
+arm_probe_decode_insn(probes_opcode_t insn, struct arch_probes_insn *api)
{
/*
* Instructions reading or modifying the PC won't work from the XOL
@@ -88,26 +88,26 @@ arm_probe_decode_insn(probe_opcode_t insn, struct arch_probe_insn *api)
return INSN_GOOD;

if (aarch64_insn_is_bcond(insn)) {
- api->handler = simulate_b_cond;
+ api->insn_handler = simulate_b_cond;
} else if (aarch64_insn_is_cbz(insn) ||
aarch64_insn_is_cbnz(insn)) {
- api->handler = simulate_cbz_cbnz;
+ api->insn_handler = simulate_cbz_cbnz;
} else if (aarch64_insn_is_tbz(insn) ||
aarch64_insn_is_tbnz(insn)) {
- api->handler = simulate_tbz_tbnz;
+ api->insn_handler = simulate_tbz_tbnz;
} else if (aarch64_insn_is_adr_adrp(insn)) {
- api->handler = simulate_adr_adrp;
+ api->insn_handler = simulate_adr_adrp;
} else if (aarch64_insn_is_b(insn) ||
aarch64_insn_is_bl(insn)) {
- api->handler = simulate_b_bl;
+ api->insn_handler = simulate_b_bl;
} else if (aarch64_insn_is_br(insn) ||
aarch64_insn_is_blr(insn) ||
aarch64_insn_is_ret(insn)) {
- api->handler = simulate_br_blr_ret;
+ api->insn_handler = simulate_br_blr_ret;
} else if (aarch64_insn_is_ldr_lit(insn)) {
- api->handler = simulate_ldr_literal;
+ api->insn_handler = simulate_ldr_literal;
} else if (aarch64_insn_is_ldrsw_lit(insn)) {
- api->handler = simulate_ldrsw_literal;
+ api->insn_handler = simulate_ldrsw_literal;
} else {
/*
* Instruction cannot be stepped out-of-line and we don't
@@ -138,12 +138,12 @@ is_probed_address_atomic(kprobe_opcode_t *scan_start, kprobe_opcode_t *scan_end)
return false;
}

-enum probe_insn __kprobes
+enum probes_insn __kprobes
arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi)
{
- enum probe_insn decoded;
- probe_opcode_t insn = le32_to_cpu(*addr);
- probe_opcode_t *scan_end = NULL;
+ enum probes_insn decoded;
+ probes_opcode_t insn = le32_to_cpu(*addr);
+ probes_opcode_t *scan_end = NULL;
unsigned long size = 0, offset = 0;

/*
diff --git a/arch/arm64/kernel/probes/decode-insn.h b/arch/arm64/kernel/probes/decode-insn.h
index 192ab00..93e021f 100644
--- a/arch/arm64/kernel/probes/decode-insn.h
+++ b/arch/arm64/kernel/probes/decode-insn.h
@@ -16,7 +16,9 @@
#ifndef _ARM_KERNEL_KPROBES_ARM64_H
#define _ARM_KERNEL_KPROBES_ARM64_H

+#include <asm/probes.h>
#include <asm/kprobes.h>
+#include "decode.h"

/*
* ARM strongly recommends a limit of 128 bytes between LoadExcl and
@@ -25,17 +27,12 @@
*/
#define MAX_ATOMIC_CONTEXT_SIZE (128 / sizeof(kprobe_opcode_t))

-enum probe_insn {
- INSN_REJECTED,
- INSN_GOOD_NO_SLOT,
- INSN_GOOD,
-};
-
#ifdef CONFIG_KPROBES
-enum probe_insn __kprobes
+enum probes_insn __kprobes
arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi);
#endif
-enum probe_insn __kprobes
-arm_probe_decode_insn(probe_opcode_t insn, struct arch_probe_insn *asi);
+enum probes_insn __kprobes
+arm_probe_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi);

+extern const union decode_action uprobes_probes_actions[];
#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 3988967..6b392e1 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -68,8 +68,8 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
{
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

- if (p->ainsn.api.handler)
- p->ainsn.api.handler((u32)p->opcode, &p->ainsn.api, regs);
+ if (p->ainsn.api.insn_handler)
+ p->ainsn.api.insn_handler((u32)p->opcode, &p->ainsn.api, regs);

/* single step simulated, now go for post processing */
post_kprobe_handler(kcb, regs);
diff --git a/arch/arm64/kernel/probes/simulate-insn.c b/arch/arm64/kernel/probes/simulate-insn.c
index 22dc7a7..9898c41 100644
--- a/arch/arm64/kernel/probes/simulate-insn.c
+++ b/arch/arm64/kernel/probes/simulate-insn.c
@@ -92,7 +92,7 @@ static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
* instruction simulation functions
*/
void __kprobes
-simulate_adr_adrp(u32 opcode, struct arch_probe_insn *api,
+simulate_adr_adrp(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
long imm, xn, val;
@@ -112,7 +112,7 @@ simulate_adr_adrp(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_b_bl(u32 opcode, struct arch_probe_insn *api,
+simulate_b_bl(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
int disp = bbl_displacement(opcode);
@@ -126,7 +126,7 @@ simulate_b_bl(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_b_cond(u32 opcode, struct arch_probe_insn *api,
+simulate_b_cond(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
int disp = 4;
@@ -139,7 +139,7 @@ simulate_b_cond(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_br_blr_ret(u32 opcode, struct arch_probe_insn *api,
+simulate_br_blr_ret(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
int xn = (opcode >> 5) & 0x1f;
@@ -154,7 +154,7 @@ simulate_br_blr_ret(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_cbz_cbnz(u32 opcode, struct arch_probe_insn *api,
+simulate_cbz_cbnz(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
int disp = 4;
@@ -171,7 +171,7 @@ simulate_cbz_cbnz(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_tbz_tbnz(u32 opcode, struct arch_probe_insn *api,
+simulate_tbz_tbnz(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
int disp = 4;
@@ -188,7 +188,7 @@ simulate_tbz_tbnz(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_ldr_literal(u32 opcode, struct arch_probe_insn *api,
+simulate_ldr_literal(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
u64 *load_addr;
@@ -208,7 +208,7 @@ simulate_ldr_literal(u32 opcode, struct arch_probe_insn *api,
}

void __kprobes
-simulate_ldrsw_literal(u32 opcode, struct arch_probe_insn *api,
+simulate_ldrsw_literal(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs)
{
s32 *load_addr;
diff --git a/arch/arm64/kernel/probes/simulate-insn.h b/arch/arm64/kernel/probes/simulate-insn.h
index 31b3840..822c3c4 100644
--- a/arch/arm64/kernel/probes/simulate-insn.h
+++ b/arch/arm64/kernel/probes/simulate-insn.h
@@ -16,21 +16,21 @@
#ifndef _ARM_KERNEL_KPROBES_SIMULATE_INSN_H
#define _ARM_KERNEL_KPROBES_SIMULATE_INSN_H

-void simulate_adr_adrp(u32 opcode, struct arch_probe_insn *api,
+void simulate_adr_adrp(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_b_bl(u32 opcode, struct arch_probe_insn *api,
+void simulate_b_bl(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_b_cond(u32 opcode, struct arch_probe_insn *api,
+void simulate_b_cond(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_br_blr_ret(u32 opcode, struct arch_probe_insn *api,
+void simulate_br_blr_ret(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_cbz_cbnz(u32 opcode, struct arch_probe_insn *api,
+void simulate_cbz_cbnz(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_tbz_tbnz(u32 opcode, struct arch_probe_insn *api,
+void simulate_tbz_tbnz(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_ldr_literal(u32 opcode, struct arch_probe_insn *api,
+void simulate_ldr_literal(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);
-void simulate_ldrsw_literal(u32 opcode, struct arch_probe_insn *api,
+void simulate_ldrsw_literal(u32 opcode, struct arch_probes_insn *api,
struct pt_regs *regs);

#endif /* _ARM_KERNEL_KPROBES_SIMULATE_INSN_H */
diff --git a/arch/arm64/kernel/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c
index a83642c..f99b50b 100644
--- a/arch/arm64/kernel/probes/uprobes.c
+++ b/arch/arm64/kernel/probes/uprobes.c
@@ -37,7 +37,7 @@ unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
unsigned long addr)
{
- probe_opcode_t insn;
+ probes_opcode_t insn;

/* TODO: Currently we do not support AARCH32 instruction probing */
if (mm->context.flags & MMCF_AARCH32)
@@ -45,7 +45,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
else if (!IS_ALIGNED(addr, AARCH64_INSN_SIZE))
return -EINVAL;

- insn = *(probe_opcode_t *)(&auprobe->insn[0]);
+ insn = *(probes_opcode_t *)(&auprobe->insn[0]);

switch (arm_probe_decode_insn(insn, &auprobe->api)) {
case INSN_REJECTED:
@@ -105,16 +105,16 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)

bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
{
- probe_opcode_t insn;
+ probes_opcode_t insn;
unsigned long addr;

if (!auprobe->simulate)
return false;

- insn = *(probe_opcode_t *)(&auprobe->insn[0]);
+ insn = *(probes_opcode_t *)(&auprobe->insn[0]);

- if (auprobe->api.handler)
- auprobe->api.handler(insn, &auprobe->api, regs);
+ if (auprobe->api.insn_handler)
+ auprobe->api.insn_handler(insn, &auprobe->api, regs);

return true;
}
diff --git a/lib/probes/Makefile b/lib/probes/Makefile
index 534a2b7..682e2df 100644
--- a/lib/probes/Makefile
+++ b/lib/probes/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_ARM) += arm/
+obj-$(CONFIG_ARM64) += arm/
diff --git a/lib/probes/arm/Makefile b/lib/probes/arm/Makefile
index 1c97b8f..239deac 100644
--- a/lib/probes/arm/Makefile
+++ b/lib/probes/arm/Makefile
@@ -1,4 +1,9 @@
+ccflags-y += -I$(srctree)/lib/uprobes/arm/
+ifdef CONFIG_ARM64
+ccflags-y += -I$(srctree)/arch/arm64/kernel/probes/
+else
ccflags-y += -I$(srctree)/arch/arm/probes/uprobes/
+endif
obj-$(CONFIG_UPROBES) += decode.o decode-arm.o actions-arm.o
obj-$(CONFIG_KPROBES) += decode.o
ifndef CONFIG_THUMB2_KERNEL
diff --git a/lib/probes/arm/actions-arm.c b/lib/probes/arm/actions-arm.c
index db6017d..2393573 100644
--- a/lib/probes/arm/actions-arm.c
+++ b/lib/probes/arm/actions-arm.c
@@ -15,7 +15,10 @@

#include "decode.h"
#include "decode-arm.h"
-#include "core.h"
+
+#ifdef CONFIG_ARM64
+#include <../../../arm/include/asm/opcodes.h>
+#endif /* CONFIG_ARM64 */

static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
{
@@ -72,8 +75,8 @@ static void uprobe_set_pc(struct arch_uprobe *auprobe,
{
u32 pcreg = auprobe->pcreg;

- autask->backup = regs->uregs[pcreg];
- regs->uregs[pcreg] = regs->ARM_pc + 8;
+ autask->backup = pt_regs_read_reg(regs, pcreg);
+ pt_regs_write_reg(regs, pcreg, instruction_pointer(regs) + 8);
}

static void uprobe_unset_pc(struct arch_uprobe *auprobe,
@@ -81,7 +84,7 @@ static void uprobe_unset_pc(struct arch_uprobe *auprobe,
struct pt_regs *regs)
{
/* PC will be taken care of by common code */
- regs->uregs[auprobe->pcreg] = autask->backup;
+ pt_regs_write_reg(regs, auprobe->pcreg, autask->backup);
}

static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
@@ -90,8 +93,8 @@ static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
{
u32 pcreg = auprobe->pcreg;

- alu_write_pc(regs->uregs[pcreg], regs);
- regs->uregs[pcreg] = autask->backup;
+ alu_write_pc(pt_regs_read_reg(regs, pcreg), regs);
+ pt_regs_write_reg(regs, pcreg, autask->backup);
}

static void uprobe_write_pc(struct arch_uprobe *auprobe,
@@ -100,8 +103,8 @@ static void uprobe_write_pc(struct arch_uprobe *auprobe,
{
u32 pcreg = auprobe->pcreg;

- load_write_pc(regs->uregs[pcreg], regs);
- regs->uregs[pcreg] = autask->backup;
+ load_write_pc(pt_regs_read_reg(regs, pcreg), regs);
+ pt_regs_write_reg(regs, pcreg, autask->backup);
}

enum probes_insn
@@ -109,12 +112,13 @@ decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
const struct decode_header *d)
{
struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
- asi);
+ api);
+
struct decode_emulate *decode = (struct decode_emulate *) d;
u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
int reg;

- reg = uprobes_substitute_pc(&auprobe->ixol[0], regs);
+ reg = uprobes_substitute_pc((unsigned long *)&auprobe->ixol[0], regs);
if (reg == 15)
return INSN_GOOD;

@@ -133,7 +137,8 @@ decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
const struct decode_header *d, bool alu)
{
struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
- asi);
+ api);
+
enum probes_insn ret = decode_pc_ro(insn, asi, d);

if (((insn >> 12) & 0xf) == 15)
@@ -158,13 +163,110 @@ decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi,
return decode_wb_pc(insn, asi, d, false);
}

+/*
+ * based on arm kprobes implementation
+ */
+static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+ int rn = (insn >> 16) & 0xf;
+ int lbit = insn & (1 << 20);
+ int wbit = insn & (1 << 21);
+ int ubit = insn & (1 << 23);
+ int pbit = insn & (1 << 24);
+ int reg;
+ u32 *addr = (u32 *)pt_regs_read_reg(regs, rn);
+ unsigned int reg_bit_vector;
+ unsigned int reg_count;
+
+ reg_count = 0;
+ reg_bit_vector = insn & 0xffff;
+
+ while (reg_bit_vector) {
+ reg_bit_vector &= (reg_bit_vector - 1);
+ ++reg_count;
+ }
+ if (!ubit)
+ addr -= reg_count;
+ addr += (!pbit == !ubit);
+
+ reg_bit_vector = insn & 0xffff;
+
+ while (reg_bit_vector) {
+ reg = __ffs(reg_bit_vector);
+ reg_bit_vector &= (reg_bit_vector - 1);
+ if (lbit) { /* LDM */
+ if (reg == 15)
+ instruction_pointer_set(regs, (*addr++) - 4);
+ else
+ pt_regs_write_reg(regs, reg, *addr++);
+ } else { /* STM */
+ if (reg == 15)
+ *addr++ = instruction_pointer(regs);
+ else
+ *addr++ = pt_regs_read_reg(regs, reg);
+ }
+ }
+
+ /* write back new value of Rn */
+ if (wbit) {
+ if (!ubit)
+ addr -= reg_count;
+ addr -= (!pbit == !ubit);
+ if (rn == 15)
+ instruction_pointer_set(regs, (long)addr);
+ else
+ pt_regs_write_reg(regs, rn, (long)addr);
+ }
+
+ instruction_pointer_set(regs, instruction_pointer(regs) + 4);
+}
+
+static void __kprobes simulate_stm1_pc(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+ unsigned long addr = instruction_pointer(regs) - 4;
+
+ instruction_pointer_set(regs, (long)addr + str_pc_offset);
+ simulate_ldm1stm1(insn, asi, regs);
+ instruction_pointer_set(regs, (long)addr + 4);
+}
+
+static void __kprobes simulate_ldm1_pc(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ struct pt_regs *regs)
+{
+ simulate_ldm1stm1(insn, asi, regs);
+ load_write_pc(instruction_pointer(regs), regs);
+}
+
+enum probes_insn
+uprobe_decode_ldmstm_aarch64(probes_opcode_t insn,
+ struct arch_probes_insn *asi,
+ const struct decode_header *d)
+{
+ probes_insn_handler_t *handler = 0;
+ unsigned int reglist = insn & 0xffff;
+ int is_ldm = insn & 0x100000;
+
+ /* PC on a reglist? */
+ if (reglist & 0x8000)
+ handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc;
+ else
+ handler = simulate_ldm1stm1;
+ asi->insn_handler = handler;
+ return INSN_GOOD_NO_SLOT;
+}
+
enum probes_insn
uprobe_decode_ldmstm(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *d)
{
struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
- asi);
+ api);
unsigned int reglist = insn & 0xffff;
int rn = (insn >> 16) & 0xf;
int lbit = insn & (1 << 20);
@@ -228,5 +330,9 @@ const union decode_action uprobes_probes_actions[] = {
[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
[PROBES_BRANCH] = {.handler = simulate_bbl},
+#ifdef CONFIG_ARM64
+ [PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm_aarch64}
+#else
[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
+#endif
};
diff --git a/lib/probes/arm/decode-arm.c b/lib/probes/arm/decode-arm.c
index 3aa2e58..36eb939 100644
--- a/lib/probes/arm/decode-arm.c
+++ b/lib/probes/arm/decode-arm.c
@@ -61,39 +61,45 @@
void __kprobes simulate_bbl(probes_opcode_t insn,
struct arch_probes_insn *asi, struct pt_regs *regs)
{
- long iaddr = (long) regs->ARM_pc - 4;
- int disp = branch_displacement(insn);
+ long iaddr = (long) instruction_pointer(regs) + ARM_COMPAT_LR_OFFSET;
+ int disp = branch_displacement(insn);

if (insn & (1 << 24))
- regs->ARM_lr = iaddr + 4;
+ link_register_set(regs, iaddr);

- regs->ARM_pc = iaddr + 8 + disp;
+ instruction_pointer_set(regs, iaddr + 4 + disp);
}

void __kprobes simulate_blx1(probes_opcode_t insn,
struct arch_probes_insn *asi, struct pt_regs *regs)
{
- long iaddr = (long) regs->ARM_pc - 4;
+ long iaddr = (long) instruction_pointer(regs) + ARM_COMPAT_LR_OFFSET;
int disp = branch_displacement(insn);
+ long cpsr;

- regs->ARM_lr = iaddr + 4;
- regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
- regs->ARM_cpsr |= PSR_T_BIT;
+ link_register_set(regs, iaddr);
+ instruction_pointer_set(regs, iaddr + 4 + disp + ((insn >> 23) & 0x2));
+ cpsr = state_register(regs) | PSR_T_BIT;
+ state_register_set(regs, cpsr);
}

void __kprobes simulate_blx2bx(probes_opcode_t insn,
struct arch_probes_insn *asi, struct pt_regs *regs)
{
int rm = insn & 0xf;
- long rmv = regs->uregs[rm];
+ long rmv = pt_regs_read_reg(regs, rm);
+ long cpsr;

if (insn & (1 << 5))
- regs->ARM_lr = (long) regs->ARM_pc;
-
- regs->ARM_pc = rmv & ~0x1;
- regs->ARM_cpsr &= ~PSR_T_BIT;
- if (rmv & 0x1)
- regs->ARM_cpsr |= PSR_T_BIT;
+ link_register_set(regs, (long) instruction_pointer(regs));
+
+ instruction_pointer_set(regs, rmv & ~0x1);
+ cpsr = state_register(regs) & ~PSR_T_BIT;
+ state_register_set(regs, cpsr);
+ if (rmv & 0x1) {
+ cpsr = state_register(regs) | PSR_T_BIT;
+ state_register_set(regs, cpsr);
+ }
}

void __kprobes simulate_mrs(probes_opcode_t insn,
@@ -102,13 +108,13 @@ void __kprobes simulate_mrs(probes_opcode_t insn,
int rd = (insn >> 12) & 0xf;
unsigned long mask = 0xf8ff03df; /* Mask out execution state */

- regs->uregs[rd] = regs->ARM_cpsr & mask;
+ pt_regs_write_reg(regs, rd, state_register(regs) & mask);
}

void __kprobes simulate_mov_ipsp(probes_opcode_t insn,
struct arch_probes_insn *asi, struct pt_regs *regs)
{
- regs->uregs[12] = regs->uregs[13];
+ pt_regs_write_reg(regs, 12, pt_regs_read_reg(regs, 13));
}

/*
@@ -246,13 +252,14 @@ static const union decode_item arm_cccc_0000_____1001_table[] = {

static const union decode_item arm_cccc_0001_____1001_table[] = {
/* Synchronization primitives */
-
+#ifndef CONFIG_ARM64
#if __LINUX_ARM_ARCH__ < 6
/* Deprecated on ARMv6 and may be UNDEFINED on v7 */
/* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP,
REGS(NOPC, NOPC, 0, 0, NOPC)),
#endif
+#endif /* CONFIG_ARM64 */
/* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
/* And unallocated instructions... */
DECODE_END
@@ -709,7 +716,7 @@ EXPORT_SYMBOL_GPL(probes_decode_arm_table);
static void __kprobes arm_singlestep(probes_opcode_t insn,
struct arch_probes_insn *asi, struct pt_regs *regs)
{
- regs->ARM_pc += 4;
+ instruction_pointer_set(regs, instruction_pointer(regs) + 4);
asi->insn_handler(insn, asi, regs);
}

@@ -730,8 +737,10 @@ arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
bool emulate, const union decode_action *actions,
const struct decode_checker *checkers[])
{
+#ifndef CONFIG_ARM64
asi->insn_singlestep = arm_singlestep;
asi->insn_check_cc = probes_condition_checks[insn>>28];
+#endif
return probes_decode_insn(insn, asi, probes_decode_arm_table, false,
emulate, actions, checkers);
}
diff --git a/lib/probes/arm/decode.c b/lib/probes/arm/decode.c
index 6840054..7a31042 100644
--- a/lib/probes/arm/decode.c
+++ b/lib/probes/arm/decode.c
@@ -13,13 +13,18 @@

#include <linux/kernel.h>
#include <linux/types.h>
+#ifdef CONFIG_ARM64
+#include <asm/insn.h>
+#include <../../../arm/include/asm/opcodes.h>
+#else /* CONFIG_ARM64 */
#include <asm/system_info.h>
#include <asm/ptrace.h>
+#endif /* CONFIG_ARM64 */
#include <linux/bug.h>
-
#include "decode.h"


+
#ifndef find_str_pc_offset

/*
@@ -189,7 +194,9 @@ void __kprobes probes_emulate_none(probes_opcode_t opcode,
struct arch_probes_insn *asi,
struct pt_regs *regs)
{
+#ifndef CONFIG_ARM64
asi->insn_fn();
+#endif /* CONFIG_ARM64 */
}

/*
@@ -430,6 +437,7 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
*/
probes_opcode_t origin_insn = insn;

+#ifndef CONFIG_ARM64
/*
* stack_space is initialized to 0 here. Checker functions
* should update is value if they find this is a stack store
@@ -446,7 +454,7 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
* registers are used', to prevent any potential optimization.
*/
asi->register_usage_flags = ~0UL;
-
+#endif /* CONFIG_ARM64 */
if (emulate)
insn = prepare_emulated_insn(insn, asi, thumb);

diff --git a/lib/probes/arm/decode.h b/lib/probes/arm/decode.h
index 43b02fd..b1fd9ab 100644
--- a/lib/probes/arm/decode.h
+++ b/lib/probes/arm/decode.h
@@ -23,10 +23,20 @@
#include <linux/stddef.h>
#include <asm/probes.h>
#include <asm/kprobes.h>
+#ifdef CONFIG_ARM64
+#include <asm/ptrace.h>
+#include <asm/insn.h>

-void __init arm_probes_decode_init(void);
+#define PSR_T_BIT PSR_AA32_T_BIT
+
+#define str_pc_offset 8
+#define load_write_pc_interworks true
+#define alu_write_pc_interworks true
+#define find_str_pc_offset()
+#define test_load_write_pc_interworking()
+#define test_alu_write_pc_interworking()

-extern probes_check_cc * const probes_condition_checks[16];
+#else /* CONFIG_ARM64 */

#if __LINUX_ARM_ARCH__ >= 7

@@ -40,8 +50,7 @@ extern probes_check_cc * const probes_condition_checks[16];
extern int str_pc_offset;
void __init find_str_pc_offset(void);

-#endif
-
+#endif /* __LINUX_ARM_ARCH__ */

/*
* Update ITSTATE after normal execution of an IT block instruction.
@@ -69,9 +78,13 @@ static inline unsigned long it_advance(unsigned long cpsr)
return cpsr;
}

+#endif /* CONFIG_ARM64 */
+
+void __init arm_probes_decode_init(void);
+
static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
{
- long cpsr = regs->ARM_cpsr;
+ long cpsr = state_register(regs);

if (pcv & 0x1) {
cpsr |= PSR_T_BIT;
@@ -80,11 +93,11 @@ static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
cpsr &= ~PSR_T_BIT;
pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */
}
- regs->ARM_cpsr = cpsr;
- regs->ARM_pc = pcv;
+ state_register_set(regs, cpsr);
+ instruction_pointer_set(regs, pcv);
}

-
+#ifndef CONFIG_ARM64
#if __LINUX_ARM_ARCH__ >= 6

/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */
@@ -98,16 +111,18 @@ extern bool load_write_pc_interworks;
void __init test_load_write_pc_interworking(void);

#endif
+#endif /* CONFIG_ARM64 */

static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs)
{
if (load_write_pc_interworks)
bx_write_pc(pcv, regs);
else
- regs->ARM_pc = pcv;
+ instruction_pointer_set(regs, pcv);
}


+#ifndef CONFIG_ARM64
#if __LINUX_ARM_ARCH__ >= 7

#define alu_write_pc_interworks true
@@ -126,13 +141,14 @@ extern bool alu_write_pc_interworks;
void __init test_alu_write_pc_interworking(void);

#endif /* __LINUX_ARM_ARCH__ == 6 */
+#endif /* CONFIG_ARM64 */

static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
{
if (alu_write_pc_interworks)
bx_write_pc(pcv, regs);
else
- regs->ARM_pc = pcv;
+ instruction_pointer_set(regs, pcv);
}


@@ -395,11 +411,6 @@ struct decode_or {
#define DECODE_OR(_mask, _value) \
DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0)

-enum probes_insn {
- INSN_REJECTED,
- INSN_GOOD,
- INSN_GOOD_NO_SLOT
-};

struct decode_reject {
struct decode_header header;
--
2.7.4