[PATCH 7/9] ARM: Move uprobes/kprobes shared functions to common file
Jon Medhurst (Tixy)
tixy at linaro.org
Thu Aug 29 10:08:03 EDT 2013
On Thu, 2013-08-01 at 19:45 -0400, David Long wrote:
> From: "David A. Long" <dave.long at linaro.org>
>
> Separate the kprobe-only functions from the functions needed by
> both kprobes and uprobes.
>
> Signed-off-by: David A. Long <dave.long at linaro.org>
> ---
This re-factoring looks a bit back-to-front to me, e.g. the instruction
emulation routines in kprobes-arm.c, which are only used by kprobes are
moved into the common probes-arm.c file, whilst the common decoding
tables are left in the kprobes-arm.c. Surely it should be the other way
around, or have I missed something?
As is, it leads to things like emulate_rd12rn16rm0rs8_rwflags being
defined in probes-arm.c and used in kprobes-arm.c with a prototype added
to kprobes.h, when it could just stay completely local to kprobes-arm.c.
And in a later patch when uprobes is added, it has to link in
kprobes-arm.o to get the generic decoding table. That was the red flag
which made meet look again, as it seems wrong that after all this code
re-factoring uprobes should need any kprobe files.
I have no other comments on this patch, save you scrolling down :-)
but I'm leaving the patch quoted here for others' reference as I'm late
in replying and people may no longer have the original...
> arch/arm/kernel/Makefile | 2 +-
> arch/arm/kernel/kprobes-arm.c | 298 +------------------------------------
> arch/arm/kernel/kprobes-common.c | 266 ---------------------------------
> arch/arm/kernel/kprobes-thumb.c | 14 +-
> arch/arm/kernel/kprobes.h | 4 +-
> arch/arm/kernel/probes-arm.c | 311 +++++++++++++++++++++++++++++++++++++++
> arch/arm/kernel/probes.c | 286 +++++++++++++++++++++++++++++++++++
> arch/arm/kernel/probes.h | 23 +++
> 8 files changed, 635 insertions(+), 569 deletions(-)
> create mode 100644 arch/arm/kernel/probes-arm.c
> create mode 100644 arch/arm/kernel/probes.c
> create mode 100644 arch/arm/kernel/probes.h
>
> diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
> index 86d10dd..3292023 100644
> --- a/arch/arm/kernel/Makefile
> +++ b/arch/arm/kernel/Makefile
> @@ -49,7 +49,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o
> obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o
> obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o
> obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
> -obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o patch.o
> +obj-$(CONFIG_KPROBES) += probes.o probes-arm.o kprobes.o kprobes-common.o patch.o
> ifdef CONFIG_THUMB2_KERNEL
> obj-$(CONFIG_KPROBES) += kprobes-thumb.o
> else
> diff --git a/arch/arm/kernel/kprobes-arm.c b/arch/arm/kernel/kprobes-arm.c
> index 8a30c89..d6503cc 100644
> --- a/arch/arm/kernel/kprobes-arm.c
> +++ b/arch/arm/kernel/kprobes-arm.c
> @@ -62,19 +62,9 @@
> #include <linux/kprobes.h>
> #include <linux/module.h>
>
> +#include "probes.h"
> #include "kprobes.h"
>
> -#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> -
> -#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> -
> -#if __LINUX_ARM_ARCH__ >= 6
> -#define BLX(reg) "blx "reg" \n\t"
> -#else
> -#define BLX(reg) "mov lr, pc \n\t" \
> - "mov pc, "reg" \n\t"
> -#endif
> -
> /*
> * To avoid the complications of mimicing single-stepping on a
> * processor without a Next-PC or a single-step mode, and to
> @@ -105,284 +95,6 @@
> * read and write of flags.
> */
>
> -static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - long iaddr = (long)p->addr;
> - int disp = branch_displacement(insn);
> -
> - if (insn & (1 << 24))
> - regs->ARM_lr = iaddr + 4;
> -
> - regs->ARM_pc = iaddr + 8 + disp;
> -}
> -
> -static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - long iaddr = (long)p->addr;
> - int disp = branch_displacement(insn);
> -
> - regs->ARM_lr = iaddr + 4;
> - regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
> - regs->ARM_cpsr |= PSR_T_BIT;
> -}
> -
> -static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - int rm = insn & 0xf;
> - long rmv = regs->uregs[rm];
> -
> - if (insn & (1 << 5))
> - regs->ARM_lr = (long)p->addr + 4;
> -
> - regs->ARM_pc = rmv & ~0x1;
> - regs->ARM_cpsr &= ~PSR_T_BIT;
> - if (rmv & 0x1)
> - regs->ARM_cpsr |= PSR_T_BIT;
> -}
> -
> -static void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - int rd = (insn >> 12) & 0xf;
> - unsigned long mask = 0xf8ff03df; /* Mask out execution state */
> - regs->uregs[rd] = regs->ARM_cpsr & mask;
> -}
> -
> -static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
> -{
> - regs->uregs[12] = regs->uregs[13];
> -}
> -
> -static void __kprobes
> -emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - unsigned long pc = (unsigned long)p->addr + 8;
> - int rt = (insn >> 12) & 0xf;
> - int rn = (insn >> 16) & 0xf;
> - int rm = insn & 0xf;
> -
> - register unsigned long rtv asm("r0") = regs->uregs[rt];
> - register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
> - register unsigned long rnv asm("r2") = (rn == 15) ? pc
> - : regs->uregs[rn];
> - register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> - __asm__ __volatile__ (
> - BLX("%[fn]")
> - : "=r" (rtv), "=r" (rt2v), "=r" (rnv)
> - : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
> - [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - regs->uregs[rt] = rtv;
> - regs->uregs[rt+1] = rt2v;
> - if (is_writeback(insn))
> - regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_ldr(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - unsigned long pc = (unsigned long)p->addr + 8;
> - int rt = (insn >> 12) & 0xf;
> - int rn = (insn >> 16) & 0xf;
> - int rm = insn & 0xf;
> -
> - register unsigned long rtv asm("r0");
> - register unsigned long rnv asm("r2") = (rn == 15) ? pc
> - : regs->uregs[rn];
> - register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> - __asm__ __volatile__ (
> - BLX("%[fn]")
> - : "=r" (rtv), "=r" (rnv)
> - : "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - if (rt == 15)
> - load_write_pc(rtv, regs);
> - else
> - regs->uregs[rt] = rtv;
> -
> - if (is_writeback(insn))
> - regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_str(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
> - unsigned long rnpc = (unsigned long)p->addr + 8;
> - int rt = (insn >> 12) & 0xf;
> - int rn = (insn >> 16) & 0xf;
> - int rm = insn & 0xf;
> -
> - register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
> - : regs->uregs[rt];
> - register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
> - : regs->uregs[rn];
> - register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> - __asm__ __volatile__ (
> - BLX("%[fn]")
> - : "=r" (rnv)
> - : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - if (is_writeback(insn))
> - regs->uregs[rn] = rnv;
> -}
> -
> -static void __kprobes
> -emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - unsigned long pc = (unsigned long)p->addr + 8;
> - int rd = (insn >> 12) & 0xf;
> - int rn = (insn >> 16) & 0xf;
> - int rm = insn & 0xf;
> - int rs = (insn >> 8) & 0xf;
> -
> - register unsigned long rdv asm("r0") = regs->uregs[rd];
> - register unsigned long rnv asm("r2") = (rn == 15) ? pc
> - : regs->uregs[rn];
> - register unsigned long rmv asm("r3") = (rm == 15) ? pc
> - : regs->uregs[rm];
> - register unsigned long rsv asm("r1") = regs->uregs[rs];
> - unsigned long cpsr = regs->ARM_cpsr;
> -
> - __asm__ __volatile__ (
> - "msr cpsr_fs, %[cpsr] \n\t"
> - BLX("%[fn]")
> - "mrs %[cpsr], cpsr \n\t"
> - : "=r" (rdv), [cpsr] "=r" (cpsr)
> - : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> - "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - if (rd == 15)
> - alu_write_pc(rdv, regs);
> - else
> - regs->uregs[rd] = rdv;
> - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - int rd = (insn >> 12) & 0xf;
> - int rn = (insn >> 16) & 0xf;
> - int rm = insn & 0xf;
> -
> - register unsigned long rdv asm("r0") = regs->uregs[rd];
> - register unsigned long rnv asm("r2") = regs->uregs[rn];
> - register unsigned long rmv asm("r3") = regs->uregs[rm];
> - unsigned long cpsr = regs->ARM_cpsr;
> -
> - __asm__ __volatile__ (
> - "msr cpsr_fs, %[cpsr] \n\t"
> - BLX("%[fn]")
> - "mrs %[cpsr], cpsr \n\t"
> - : "=r" (rdv), [cpsr] "=r" (cpsr)
> - : "0" (rdv), "r" (rnv), "r" (rmv),
> - "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - regs->uregs[rd] = rdv;
> - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - int rd = (insn >> 16) & 0xf;
> - int rn = (insn >> 12) & 0xf;
> - int rm = insn & 0xf;
> - int rs = (insn >> 8) & 0xf;
> -
> - register unsigned long rdv asm("r2") = regs->uregs[rd];
> - register unsigned long rnv asm("r0") = regs->uregs[rn];
> - register unsigned long rmv asm("r3") = regs->uregs[rm];
> - register unsigned long rsv asm("r1") = regs->uregs[rs];
> - unsigned long cpsr = regs->ARM_cpsr;
> -
> - __asm__ __volatile__ (
> - "msr cpsr_fs, %[cpsr] \n\t"
> - BLX("%[fn]")
> - "mrs %[cpsr], cpsr \n\t"
> - : "=r" (rdv), [cpsr] "=r" (cpsr)
> - : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> - "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - regs->uregs[rd] = rdv;
> - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> -static void __kprobes
> -emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - int rd = (insn >> 12) & 0xf;
> - int rm = insn & 0xf;
> -
> - register unsigned long rdv asm("r0") = regs->uregs[rd];
> - register unsigned long rmv asm("r3") = regs->uregs[rm];
> -
> - __asm__ __volatile__ (
> - BLX("%[fn]")
> - : "=r" (rdv)
> - : "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - regs->uregs[rd] = rdv;
> -}
> -
> -static void __kprobes
> -emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> -{
> - kprobe_opcode_t insn = p->opcode;
> - int rdlo = (insn >> 12) & 0xf;
> - int rdhi = (insn >> 16) & 0xf;
> - int rn = insn & 0xf;
> - int rm = (insn >> 8) & 0xf;
> -
> - register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
> - register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
> - register unsigned long rnv asm("r3") = regs->uregs[rn];
> - register unsigned long rmv asm("r1") = regs->uregs[rm];
> - unsigned long cpsr = regs->ARM_cpsr;
> -
> - __asm__ __volatile__ (
> - "msr cpsr_fs, %[cpsr] \n\t"
> - BLX("%[fn]")
> - "mrs %[cpsr], cpsr \n\t"
> - : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
> - : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
> - "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> - : "lr", "memory", "cc"
> - );
> -
> - regs->uregs[rdlo] = rdlov;
> - regs->uregs[rdhi] = rdhiv;
> - regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> -}
> -
> /*
> * For the instruction masking and comparisons in all the "space_*"
> * functions below, Do _not_ rearrange the order of tests unless
> @@ -400,13 +112,13 @@ static const union decode_item arm_1111_table[] = {
> /* PLDI (immediate) 1111 0100 x101 xxxx xxxx xxxx xxxx xxxx */
> /* PLDW (immediate) 1111 0101 x001 xxxx xxxx xxxx xxxx xxxx */
> /* PLD (immediate) 1111 0101 x101 xxxx xxxx xxxx xxxx xxxx */
> - DECODE_SIMULATE (0xfe300000, 0xf4100000, kprobe_simulate_nop),
> + DECODE_SIMULATE (0xfe300000, 0xf4100000, probes_simulate_nop),
>
> /* memory hint 1111 0110 x001 xxxx xxxx xxxx xxx0 xxxx */
> /* PLDI (register) 1111 0110 x101 xxxx xxxx xxxx xxx0 xxxx */
> /* PLDW (register) 1111 0111 x001 xxxx xxxx xxxx xxx0 xxxx */
> /* PLD (register) 1111 0111 x101 xxxx xxxx xxxx xxx0 xxxx */
> - DECODE_SIMULATE (0xfe300010, 0xf6100000, kprobe_simulate_nop),
> + DECODE_SIMULATE (0xfe300010, 0xf6100000, probes_simulate_nop),
>
> /* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
> DECODE_SIMULATE (0xfe000000, 0xfa000000, simulate_blx1),
> @@ -649,11 +361,11 @@ static const union decode_item arm_cccc_001x_table[] = {
> /* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
> DECODE_OR (0x0fff00ff, 0x03200001),
> /* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
> - DECODE_EMULATE (0x0fff00ff, 0x03200004, kprobe_emulate_none),
> + DECODE_EMULATE (0x0fff00ff, 0x03200004, probes_emulate_none),
> /* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
> /* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
> /* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
> - DECODE_SIMULATE (0x0fff00fc, 0x03200000, kprobe_simulate_nop),
> + DECODE_SIMULATE (0x0fff00fc, 0x03200000, probes_simulate_nop),
> /* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
> /* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
> /* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
> diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c
> index 18a7628..b66e9f7 100644
> --- a/arch/arm/kernel/kprobes-common.c
> +++ b/arch/arm/kernel/kprobes-common.c
> @@ -173,15 +173,6 @@ kprobe_check_cc * const kprobe_condition_checks[16] = {
> };
>
>
> -void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs)
> -{
> -}
> -
> -void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs)
> -{
> - p->ainsn.insn_fn();
> -}
> -
> static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs)
> {
> kprobe_opcode_t insn = p->opcode;
> @@ -319,260 +310,3 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi)
> return INSN_GOOD_NO_SLOT;
> }
>
> -
> -/*
> - * Prepare an instruction slot to receive an instruction for emulating.
> - * This is done by placing a subroutine return after the location where the
> - * instruction will be placed. We also modify ARM instructions to be
> - * unconditional as the condition code will already be checked before any
> - * emulation handler is called.
> - */
> -static kprobe_opcode_t __kprobes
> -prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> - bool thumb)
> -{
> -#ifdef CONFIG_THUMB2_KERNEL
> - if (thumb) {
> - u16 *thumb_insn = (u16 *)asi->insn;
> - thumb_insn[1] = 0x4770; /* Thumb bx lr */
> - thumb_insn[2] = 0x4770; /* Thumb bx lr */
> - return insn;
> - }
> - asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
> -#else
> - asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
> -#endif
> - /* Make an ARM instruction unconditional */
> - if (insn < 0xe0000000)
> - insn = (insn | 0xe0000000) & ~0x10000000;
> - return insn;
> -}
> -
> -/*
> - * Write a (probably modified) instruction into the slot previously prepared by
> - * prepare_emulated_insn
> - */
> -static void __kprobes
> -set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> - bool thumb)
> -{
> -#ifdef CONFIG_THUMB2_KERNEL
> - if (thumb) {
> - u16 *ip = (u16 *)asi->insn;
> - if (is_wide_instruction(insn))
> - *ip++ = insn >> 16;
> - *ip++ = insn;
> - return;
> - }
> -#endif
> - asi->insn[0] = insn;
> -}
> -
> -/*
> - * When we modify the register numbers encoded in an instruction to be emulated,
> - * the new values come from this define. For ARM and 32-bit Thumb instructions
> - * this gives...
> - *
> - * bit position 16 12 8 4 0
> - * ---------------+---+---+---+---+---+
> - * register r2 r0 r1 -- r3
> - */
> -#define INSN_NEW_BITS 0x00020103
> -
> -/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
> -#define INSN_SAMEAS16_BITS 0x22222222
> -
> -/*
> - * Validate and modify each of the registers encoded in an instruction.
> - *
> - * Each nibble in regs contains a value from enum decode_reg_type. For each
> - * non-zero value, the corresponding nibble in pinsn is validated and modified
> - * according to the type.
> - */
> -static bool __kprobes decode_regs(kprobe_opcode_t* pinsn, u32 regs)
> -{
> - kprobe_opcode_t insn = *pinsn;
> - kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> -
> - for (; regs != 0; regs >>= 4, mask <<= 4) {
> -
> - kprobe_opcode_t new_bits = INSN_NEW_BITS;
> -
> - switch (regs & 0xf) {
> -
> - case REG_TYPE_NONE:
> - /* Nibble not a register, skip to next */
> - continue;
> -
> - case REG_TYPE_ANY:
> - /* Any register is allowed */
> - break;
> -
> - case REG_TYPE_SAMEAS16:
> - /* Replace register with same as at bit position 16 */
> - new_bits = INSN_SAMEAS16_BITS;
> - break;
> -
> - case REG_TYPE_SP:
> - /* Only allow SP (R13) */
> - if ((insn ^ 0xdddddddd) & mask)
> - goto reject;
> - break;
> -
> - case REG_TYPE_PC:
> - /* Only allow PC (R15) */
> - if ((insn ^ 0xffffffff) & mask)
> - goto reject;
> - break;
> -
> - case REG_TYPE_NOSP:
> - /* Reject SP (R13) */
> - if (((insn ^ 0xdddddddd) & mask) == 0)
> - goto reject;
> - break;
> -
> - case REG_TYPE_NOSPPC:
> - case REG_TYPE_NOSPPCX:
> - /* Reject SP and PC (R13 and R15) */
> - if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
> - goto reject;
> - break;
> -
> - case REG_TYPE_NOPCWB:
> - if (!is_writeback(insn))
> - break; /* No writeback, so any register is OK */
> - /* fall through... */
> - case REG_TYPE_NOPC:
> - case REG_TYPE_NOPCX:
> - /* Reject PC (R15) */
> - if (((insn ^ 0xffffffff) & mask) == 0)
> - goto reject;
> - break;
> - }
> -
> - /* Replace value of nibble with new register number... */
> - insn &= ~mask;
> - insn |= new_bits & mask;
> - }
> -
> - *pinsn = insn;
> - return true;
> -
> -reject:
> - return false;
> -}
> -
> -static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
> - [DECODE_TYPE_TABLE] = sizeof(struct decode_table),
> - [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom),
> - [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate),
> - [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate),
> - [DECODE_TYPE_OR] = sizeof(struct decode_or),
> - [DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
> -};
> -
> -/*
> - * kprobe_decode_insn operates on data tables in order to decode an ARM
> - * architecture instruction onto which a kprobe has been placed.
> - *
> - * These instruction decoding tables are a concatenation of entries each
> - * of which consist of one of the following structs:
> - *
> - * decode_table
> - * decode_custom
> - * decode_simulate
> - * decode_emulate
> - * decode_or
> - * decode_reject
> - *
> - * Each of these starts with a struct decode_header which has the following
> - * fields:
> - *
> - * type_regs
> - * mask
> - * value
> - *
> - * The least significant DECODE_TYPE_BITS of type_regs contains a value
> - * from enum decode_type, this indicates which of the decode_* structs
> - * the entry contains. The value DECODE_TYPE_END indicates the end of the
> - * table.
> - *
> - * When the table is parsed, each entry is checked in turn to see if it
> - * matches the instruction to be decoded using the test:
> - *
> - * (insn & mask) == value
> - *
> - * If no match is found before the end of the table is reached then decoding
> - * fails with INSN_REJECTED.
> - *
> - * When a match is found, decode_regs() is called to validate and modify each
> - * of the registers encoded in the instruction; the data it uses to do this
> - * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
> - * to fail with INSN_REJECTED.
> - *
> - * Once the instruction has passed the above tests, further processing
> - * depends on the type of the table entry's decode struct.
> - *
> - */
> -int __kprobes
> -kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> - const union decode_item *table, bool thumb)
> -{
> - const struct decode_header *h = (struct decode_header *)table;
> - const struct decode_header *next;
> - bool matched = false;
> -
> - insn = prepare_emulated_insn(insn, asi, thumb);
> -
> - for (;; h = next) {
> - enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> - u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
> -
> - if (type == DECODE_TYPE_END)
> - return INSN_REJECTED;
> -
> - next = (struct decode_header *)
> - ((uintptr_t)h + decode_struct_sizes[type]);
> -
> - if (!matched && (insn & h->mask.bits) != h->value.bits)
> - continue;
> -
> - if (!decode_regs(&insn, regs))
> - return INSN_REJECTED;
> -
> - switch (type) {
> -
> - case DECODE_TYPE_TABLE: {
> - struct decode_table *d = (struct decode_table *)h;
> - next = (struct decode_header *)d->table.table;
> - break;
> - }
> -
> - case DECODE_TYPE_CUSTOM: {
> - struct decode_custom *d = (struct decode_custom *)h;
> - return (*d->decoder.decoder)(insn, asi);
> - }
> -
> - case DECODE_TYPE_SIMULATE: {
> - struct decode_simulate *d = (struct decode_simulate *)h;
> - asi->insn_handler = d->handler.handler;
> - return INSN_GOOD_NO_SLOT;
> - }
> -
> - case DECODE_TYPE_EMULATE: {
> - struct decode_emulate *d = (struct decode_emulate *)h;
> - asi->insn_handler = d->handler.handler;
> - set_emulated_insn(insn, asi, thumb);
> - return INSN_GOOD;
> - }
> -
> - case DECODE_TYPE_OR:
> - matched = true;
> - break;
> -
> - case DECODE_TYPE_REJECT:
> - default:
> - return INSN_REJECTED;
> - }
> - }
> - }
> diff --git a/arch/arm/kernel/kprobes-thumb.c b/arch/arm/kernel/kprobes-thumb.c
> index 6123daf..173b2bc 100644
> --- a/arch/arm/kernel/kprobes-thumb.c
> +++ b/arch/arm/kernel/kprobes-thumb.c
> @@ -544,11 +544,11 @@ static const union decode_item t32_table_1111_0xxx___1[] = {
> /* YIELD 1111 0011 1010 xxxx 10x0 x000 0000 0001 */
> DECODE_OR (0xfff0d7ff, 0xf3a08001),
> /* SEV 1111 0011 1010 xxxx 10x0 x000 0000 0100 */
> - DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, kprobe_emulate_none),
> + DECODE_EMULATE (0xfff0d7ff, 0xf3a08004, probes_emulate_none),
> /* NOP 1111 0011 1010 xxxx 10x0 x000 0000 0000 */
> /* WFE 1111 0011 1010 xxxx 10x0 x000 0000 0010 */
> /* WFI 1111 0011 1010 xxxx 10x0 x000 0000 0011 */
> - DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, kprobe_simulate_nop),
> + DECODE_SIMULATE (0xfff0d7fc, 0xf3a08000, probes_simulate_nop),
>
> /* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
> DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, t32_simulate_mrs,
> @@ -589,7 +589,7 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
>
> /* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */
> /* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */
> - DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, kprobe_simulate_nop),
> + DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, probes_simulate_nop),
>
> /* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
> DECODE_OR (0xffd0f000, 0xf890f000),
> @@ -598,13 +598,13 @@ static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
> /* PLI (immediate) 1111 1001 1001 xxxx 1111 xxxx xxxx xxxx */
> DECODE_OR (0xfff0f000, 0xf990f000),
> /* PLI (immediate) 1111 1001 0001 xxxx 1111 1100 xxxx xxxx */
> - DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, kprobe_simulate_nop,
> + DECODE_SIMULATEX(0xfff0ff00, 0xf910fc00, probes_simulate_nop,
> REGS(NOPCX, 0, 0, 0, 0)),
>
> /* PLD{W} (register) 1111 1000 00x1 xxxx 1111 0000 00xx xxxx */
> DECODE_OR (0xffd0ffc0, 0xf810f000),
> /* PLI (register) 1111 1001 0001 xxxx 1111 0000 00xx xxxx */
> - DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, kprobe_simulate_nop,
> + DECODE_SIMULATEX(0xfff0ffc0, 0xf910f000, probes_simulate_nop,
> REGS(NOPCX, 0, 0, 0, NOSPPC)),
>
> /* Other unallocated instructions... */
> @@ -1274,11 +1274,11 @@ static const union decode_item t16_table_1011[] = {
> /* YIELD 1011 1111 0001 0000 */
> DECODE_OR (0xffff, 0xbf10),
> /* SEV 1011 1111 0100 0000 */
> - DECODE_EMULATE (0xffff, 0xbf40, kprobe_emulate_none),
> + DECODE_EMULATE (0xffff, 0xbf40, probes_emulate_none),
> /* NOP 1011 1111 0000 0000 */
> /* WFE 1011 1111 0010 0000 */
> /* WFI 1011 1111 0011 0000 */
> - DECODE_SIMULATE (0xffcf, 0xbf00, kprobe_simulate_nop),
> + DECODE_SIMULATE (0xffcf, 0xbf00, probes_simulate_nop),
> /* Unassigned hints 1011 1111 xxxx 0000 */
> DECODE_REJECT (0xff0f, 0xbf00),
> /* IT 1011 1111 xxxx xxxx */
> diff --git a/arch/arm/kernel/kprobes.h b/arch/arm/kernel/kprobes.h
> index 38945f7..9aa2f15 100644
> --- a/arch/arm/kernel/kprobes.h
> +++ b/arch/arm/kernel/kprobes.h
> @@ -161,8 +161,8 @@ static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
> }
>
>
> -void __kprobes kprobe_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> -void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs);
>
> enum kprobe_insn __kprobes
> kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi);
> diff --git a/arch/arm/kernel/probes-arm.c b/arch/arm/kernel/probes-arm.c
> new file mode 100644
> index 0000000..e1b1a6e
> --- /dev/null
> +++ b/arch/arm/kernel/probes-arm.c
> @@ -0,0 +1,311 @@
> +/*
> + * arch/arm/kernel/probes-arm.c
> + *
> + * Some code moved here from arch/arm/kernel/kprobes-arm.c
> + *
> + * Copyright (C) 2006, 2007 Motorola Inc.
> + *
> + * 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
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "probes.h"
> +#include "kprobes.h"
> +
> +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
> +
> +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
> +
> +#if __LINUX_ARM_ARCH__ >= 6
> +#define BLX(reg) "blx "reg" \n\t"
> +#else
> +#define BLX(reg) "mov lr, pc \n\t" \
> + "mov pc, "reg" \n\t"
> +#endif
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + long iaddr = (long)p->addr;
> + int disp = branch_displacement(insn);
> +
> + if (insn & (1 << 24))
> + regs->ARM_lr = iaddr + 4;
> +
> + regs->ARM_pc = iaddr + 8 + disp;
> +}
> +
> +void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + long iaddr = (long)p->addr;
> + int disp = branch_displacement(insn);
> +
> + regs->ARM_lr = iaddr + 4;
> + regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2);
> + regs->ARM_cpsr |= PSR_T_BIT;
> +}
> +
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + int rm = insn & 0xf;
> + long rmv = regs->uregs[rm];
> +
> + if (insn & (1 << 5))
> + regs->ARM_lr = (long)p->addr + 4;
> +
> + regs->ARM_pc = rmv & ~0x1;
> + regs->ARM_cpsr &= ~PSR_T_BIT;
> + if (rmv & 0x1)
> + regs->ARM_cpsr |= PSR_T_BIT;
> +}
> +
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + int rd = (insn >> 12) & 0xf;
> + unsigned long mask = 0xf8ff03df; /* Mask out execution state */
> + regs->uregs[rd] = regs->ARM_cpsr & mask;
> +}
> +
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs)
> +{
> + regs->uregs[12] = regs->uregs[13];
> +}
> +
> +void __kprobes
> +emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + unsigned long pc = (unsigned long)p->addr + 8;
> + int rt = (insn >> 12) & 0xf;
> + int rn = (insn >> 16) & 0xf;
> + int rm = insn & 0xf;
> +
> + register unsigned long rtv asm("r0") = regs->uregs[rt];
> + register unsigned long rt2v asm("r1") = regs->uregs[rt+1];
> + register unsigned long rnv asm("r2") = (rn == 15) ? pc
> + : regs->uregs[rn];
> + register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> + __asm__ __volatile__ (
> + BLX("%[fn]")
> + : "=r" (rtv), "=r" (rt2v), "=r" (rnv)
> + : "0" (rtv), "1" (rt2v), "2" (rnv), "r" (rmv),
> + [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + regs->uregs[rt] = rtv;
> + regs->uregs[rt+1] = rt2v;
> + if (is_writeback(insn))
> + regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_ldr(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + unsigned long pc = (unsigned long)p->addr + 8;
> + int rt = (insn >> 12) & 0xf;
> + int rn = (insn >> 16) & 0xf;
> + int rm = insn & 0xf;
> +
> + register unsigned long rtv asm("r0");
> + register unsigned long rnv asm("r2") = (rn == 15) ? pc
> + : regs->uregs[rn];
> + register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> + __asm__ __volatile__ (
> + BLX("%[fn]")
> + : "=r" (rtv), "=r" (rnv)
> + : "1" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + if (rt == 15)
> + load_write_pc(rtv, regs);
> + else
> + regs->uregs[rt] = rtv;
> +
> + if (is_writeback(insn))
> + regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_str(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + unsigned long rtpc = (unsigned long)p->addr + str_pc_offset;
> + unsigned long rnpc = (unsigned long)p->addr + 8;
> + int rt = (insn >> 12) & 0xf;
> + int rn = (insn >> 16) & 0xf;
> + int rm = insn & 0xf;
> +
> + register unsigned long rtv asm("r0") = (rt == 15) ? rtpc
> + : regs->uregs[rt];
> + register unsigned long rnv asm("r2") = (rn == 15) ? rnpc
> + : regs->uregs[rn];
> + register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> + __asm__ __volatile__ (
> + BLX("%[fn]")
> + : "=r" (rnv)
> + : "r" (rtv), "0" (rnv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + if (is_writeback(insn))
> + regs->uregs[rn] = rnv;
> +}
> +
> +void __kprobes
> +emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + unsigned long pc = (unsigned long)p->addr + 8;
> + int rd = (insn >> 12) & 0xf;
> + int rn = (insn >> 16) & 0xf;
> + int rm = insn & 0xf;
> + int rs = (insn >> 8) & 0xf;
> +
> + register unsigned long rdv asm("r0") = regs->uregs[rd];
> + register unsigned long rnv asm("r2") = (rn == 15) ? pc
> + : regs->uregs[rn];
> + register unsigned long rmv asm("r3") = (rm == 15) ? pc
> + : regs->uregs[rm];
> + register unsigned long rsv asm("r1") = regs->uregs[rs];
> + unsigned long cpsr = regs->ARM_cpsr;
> +
> + __asm__ __volatile__ (
> + "msr cpsr_fs, %[cpsr] \n\t"
> + BLX("%[fn]")
> + "mrs %[cpsr], cpsr \n\t"
> + : "=r" (rdv), [cpsr] "=r" (cpsr)
> + : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> + "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + if (rd == 15)
> + alu_write_pc(rdv, regs);
> + else
> + regs->uregs[rd] = rdv;
> + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + int rd = (insn >> 12) & 0xf;
> + int rn = (insn >> 16) & 0xf;
> + int rm = insn & 0xf;
> +
> + register unsigned long rdv asm("r0") = regs->uregs[rd];
> + register unsigned long rnv asm("r2") = regs->uregs[rn];
> + register unsigned long rmv asm("r3") = regs->uregs[rm];
> + unsigned long cpsr = regs->ARM_cpsr;
> +
> + __asm__ __volatile__ (
> + "msr cpsr_fs, %[cpsr] \n\t"
> + BLX("%[fn]")
> + "mrs %[cpsr], cpsr \n\t"
> + : "=r" (rdv), [cpsr] "=r" (cpsr)
> + : "0" (rdv), "r" (rnv), "r" (rmv),
> + "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + regs->uregs[rd] = rdv;
> + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + int rd = (insn >> 16) & 0xf;
> + int rn = (insn >> 12) & 0xf;
> + int rm = insn & 0xf;
> + int rs = (insn >> 8) & 0xf;
> +
> + register unsigned long rdv asm("r2") = regs->uregs[rd];
> + register unsigned long rnv asm("r0") = regs->uregs[rn];
> + register unsigned long rmv asm("r3") = regs->uregs[rm];
> + register unsigned long rsv asm("r1") = regs->uregs[rs];
> + unsigned long cpsr = regs->ARM_cpsr;
> +
> + __asm__ __volatile__ (
> + "msr cpsr_fs, %[cpsr] \n\t"
> + BLX("%[fn]")
> + "mrs %[cpsr], cpsr \n\t"
> + : "=r" (rdv), [cpsr] "=r" (cpsr)
> + : "0" (rdv), "r" (rnv), "r" (rmv), "r" (rsv),
> + "1" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + regs->uregs[rd] = rdv;
> + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> +
> +void __kprobes
> +emulate_rd12rm0_noflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + int rd = (insn >> 12) & 0xf;
> + int rm = insn & 0xf;
> +
> + register unsigned long rdv asm("r0") = regs->uregs[rd];
> + register unsigned long rmv asm("r3") = regs->uregs[rm];
> +
> + __asm__ __volatile__ (
> + BLX("%[fn]")
> + : "=r" (rdv)
> + : "0" (rdv), "r" (rmv), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + regs->uregs[rd] = rdv;
> +}
> +
> +void __kprobes
> +emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p, struct pt_regs *regs)
> +{
> + kprobe_opcode_t insn = p->opcode;
> + int rdlo = (insn >> 12) & 0xf;
> + int rdhi = (insn >> 16) & 0xf;
> + int rn = insn & 0xf;
> + int rm = (insn >> 8) & 0xf;
> +
> + register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
> + register unsigned long rdhiv asm("r2") = regs->uregs[rdhi];
> + register unsigned long rnv asm("r3") = regs->uregs[rn];
> + register unsigned long rmv asm("r1") = regs->uregs[rm];
> + unsigned long cpsr = regs->ARM_cpsr;
> +
> + __asm__ __volatile__ (
> + "msr cpsr_fs, %[cpsr] \n\t"
> + BLX("%[fn]")
> + "mrs %[cpsr], cpsr \n\t"
> + : "=r" (rdlov), "=r" (rdhiv), [cpsr] "=r" (cpsr)
> + : "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
> + "2" (cpsr), [fn] "r" (p->ainsn.insn_fn)
> + : "lr", "memory", "cc"
> + );
> +
> + regs->uregs[rdlo] = rdlov;
> + regs->uregs[rdhi] = rdhiv;
> + regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
> +}
> diff --git a/arch/arm/kernel/probes.c b/arch/arm/kernel/probes.c
> new file mode 100644
> index 0000000..86c63f3
> --- /dev/null
> +++ b/arch/arm/kernel/probes.c
> @@ -0,0 +1,286 @@
> +/*
> + * arch/arm/kernel/probes.c
> + *
> + * Some contents moved here from arch/arm/include/asm/kprobes-common.c
> + *
> + * Copyright (C) 2011 Jon Medhurst <tixy at yxit.co.uk>.
> + *
> + * Some contents moved here from arch/arm/include/asm/kprobes-arm.c which is
> + * Copyright (C) 2006, 2007 Motorola Inc.
> + *
> + * 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
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/kprobes.h>
> +
> +#include "probes.h"
> +#include "kprobes.h"
> +
> +void __kprobes probes_simulate_nop(struct kprobe *p, struct pt_regs *regs)
> +{
> +}
> +
> +void __kprobes probes_emulate_none(struct kprobe *p, struct pt_regs *regs)
> +{
> + p->ainsn.insn_fn();
> +}
> +
> +/*
> + * Prepare an instruction slot to receive an instruction for emulating.
> + * This is done by placing a subroutine return after the location where the
> + * instruction will be placed. We also modify ARM instructions to be
> + * unconditional as the condition code will already be checked before any
> + * emulation handler is called.
> + */
> +static kprobe_opcode_t __kprobes
> +prepare_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> + bool thumb)
> +{
> +#ifdef CONFIG_THUMB2_KERNEL
> + if (thumb) {
> + u16 *thumb_insn = (u16 *)asi->insn;
> + thumb_insn[1] = 0x4770; /* Thumb bx lr */
> + thumb_insn[2] = 0x4770; /* Thumb bx lr */
> + return insn;
> + }
> + asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
> +#else
> + asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
> +#endif
> + /* Make an ARM instruction unconditional */
> + if (insn < 0xe0000000)
> + insn = (insn | 0xe0000000) & ~0x10000000;
> + return insn;
> +}
> +
> +/*
> + * Write a (probably modified) instruction into the slot previously prepared by
> + * prepare_emulated_insn
> + */
> +static void __kprobes
> +set_emulated_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> + bool thumb)
> +{
> +#ifdef CONFIG_THUMB2_KERNEL
> + if (thumb) {
> + u16 *ip = (u16 *)asi->insn;
> + if (is_wide_instruction(insn))
> + *ip++ = insn >> 16;
> + *ip++ = insn;
> + return;
> + }
> +#endif
> + asi->insn[0] = insn;
> +}
> +
> +/*
> + * When we modify the register numbers encoded in an instruction to be emulated,
> + * the new values come from this define. For ARM and 32-bit Thumb instructions
> + * this gives...
> + *
> + * bit position 16 12 8 4 0
> + * ---------------+---+---+---+---+---+
> + * register r2 r0 r1 -- r3
> + */
> +#define INSN_NEW_BITS 0x00020103
> +
> +/* Each nibble has same value as that at INSN_NEW_BITS bit 16 */
> +#define INSN_SAMEAS16_BITS 0x22222222
> +
> +/*
> + * Validate and modify each of the registers encoded in an instruction.
> + *
> + * Each nibble in regs contains a value from enum decode_reg_type. For each
> + * non-zero value, the corresponding nibble in pinsn is validated and modified
> + * according to the type.
> + */
> +static bool __kprobes decode_regs(kprobe_opcode_t *pinsn, u32 regs)
> +{
> + kprobe_opcode_t insn = *pinsn;
> + kprobe_opcode_t mask = 0xf; /* Start at least significant nibble */
> +
> + for (; regs != 0; regs >>= 4, mask <<= 4) {
> +
> + kprobe_opcode_t new_bits = INSN_NEW_BITS;
> +
> + switch (regs & 0xf) {
> +
> + case REG_TYPE_NONE:
> + /* Nibble not a register, skip to next */
> + continue;
> +
> + case REG_TYPE_ANY:
> + /* Any register is allowed */
> + break;
> +
> + case REG_TYPE_SAMEAS16:
> + /* Replace register with same as at bit position 16 */
> + new_bits = INSN_SAMEAS16_BITS;
> + break;
> +
> + case REG_TYPE_SP:
> + /* Only allow SP (R13) */
> + if ((insn ^ 0xdddddddd) & mask)
> + goto reject;
> + break;
> +
> + case REG_TYPE_PC:
> + /* Only allow PC (R15) */
> + if ((insn ^ 0xffffffff) & mask)
> + goto reject;
> + break;
> +
> + case REG_TYPE_NOSP:
> + /* Reject SP (R13) */
> + if (((insn ^ 0xdddddddd) & mask) == 0)
> + goto reject;
> + break;
> +
> + case REG_TYPE_NOSPPC:
> + case REG_TYPE_NOSPPCX:
> + /* Reject SP and PC (R13 and R15) */
> + if (((insn ^ 0xdddddddd) & 0xdddddddd & mask) == 0)
> + goto reject;
> + break;
> +
> + case REG_TYPE_NOPCWB:
> + if (!is_writeback(insn))
> + break; /* No writeback, so any register is OK */
> + /* fall through... */
> + case REG_TYPE_NOPC:
> + case REG_TYPE_NOPCX:
> + /* Reject PC (R15) */
> + if (((insn ^ 0xffffffff) & mask) == 0)
> + goto reject;
> + break;
> + }
> +
> + /* Replace value of nibble with new register number... */
> + insn &= ~mask;
> + insn |= new_bits & mask;
> + }
> +
> + *pinsn = insn;
> + return true;
> +
> +reject:
> + return false;
> +}
> +
> +static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
> + [DECODE_TYPE_TABLE] = sizeof(struct decode_table),
> + [DECODE_TYPE_CUSTOM] = sizeof(struct decode_custom),
> + [DECODE_TYPE_SIMULATE] = sizeof(struct decode_simulate),
> + [DECODE_TYPE_EMULATE] = sizeof(struct decode_emulate),
> + [DECODE_TYPE_OR] = sizeof(struct decode_or),
> + [DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
> +};
> +
> +/*
> + * kprobe_decode_insn operates on data tables in order to decode an ARM
> + * architecture instruction onto which a kprobe has been placed.
> + *
> + * These instruction decoding tables are a concatenation of entries each
> + * of which consist of one of the following structs:
> + *
> + * decode_table
> + * decode_custom
> + * decode_simulate
> + * decode_emulate
> + * decode_or
> + * decode_reject
> + *
> + * Each of these starts with a struct decode_header which has the following
> + * fields:
> + *
> + * type_regs
> + * mask
> + * value
> + *
> + * The least significant DECODE_TYPE_BITS of type_regs contains a value
> + * from enum decode_type, this indicates which of the decode_* structs
> + * the entry contains. The value DECODE_TYPE_END indicates the end of the
> + * table.
> + *
> + * When the table is parsed, each entry is checked in turn to see if it
> + * matches the instruction to be decoded using the test:
> + *
> + * (insn & mask) == value
> + *
> + * If no match is found before the end of the table is reached then decoding
> + * fails with INSN_REJECTED.
> + *
> + * When a match is found, decode_regs() is called to validate and modify each
> + * of the registers encoded in the instruction; the data it uses to do this
> + * is (type_regs >> DECODE_TYPE_BITS). A validation failure will cause decoding
> + * to fail with INSN_REJECTED.
> + *
> + * Once the instruction has passed the above tests, further processing
> + * depends on the type of the table entry's decode struct.
> + *
> + */
> +int __kprobes
> +kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi,
> + const union decode_item *table, bool thumb)
> +{
> + const struct decode_header *h = (struct decode_header *)table;
> + const struct decode_header *next;
> + bool matched = false;
> +
> + insn = prepare_emulated_insn(insn, asi, thumb);
> +
> + for (;; h = next) {
> + enum decode_type type = h->type_regs.bits & DECODE_TYPE_MASK;
> + u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
> +
> + if (type == DECODE_TYPE_END)
> + return INSN_REJECTED;
> +
> + next = (struct decode_header *)
> + ((uintptr_t)h + decode_struct_sizes[type]);
> +
> + if (!matched && (insn & h->mask.bits) != h->value.bits)
> + continue;
> +
> + if (!decode_regs(&insn, regs))
> + return INSN_REJECTED;
> +
> + switch (type) {
> +
> + case DECODE_TYPE_TABLE: {
> + struct decode_table *d = (struct decode_table *)h;
> + next = (struct decode_header *)d->table.table;
> + break;
> + }
> +
> + case DECODE_TYPE_CUSTOM: {
> + struct decode_custom *d = (struct decode_custom *)h;
> + return (*d->decoder.decoder)(insn, asi);
> + }
> +
> + case DECODE_TYPE_SIMULATE: {
> + struct decode_simulate *d = (struct decode_simulate *)h;
> + asi->insn_handler = d->handler.handler;
> + return INSN_GOOD_NO_SLOT;
> + }
> +
> + case DECODE_TYPE_EMULATE: {
> + struct decode_emulate *d = (struct decode_emulate *)h;
> + asi->insn_handler = d->handler.handler;
> + set_emulated_insn(insn, asi, thumb);
> + return INSN_GOOD;
> + }
> +
> + case DECODE_TYPE_OR:
> + matched = true;
> + break;
> +
> + case DECODE_TYPE_REJECT:
> + default:
> + return INSN_REJECTED;
> + }
> + }
> +}
> diff --git a/arch/arm/kernel/probes.h b/arch/arm/kernel/probes.h
> new file mode 100644
> index 0000000..56eec12
> --- /dev/null
> +++ b/arch/arm/kernel/probes.h
> @@ -0,0 +1,23 @@
> +#ifndef _ARM_KERNEL_PROBES_H
> +#define _ARM_KERNEL_PROBES_H
> +
> +void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mrs(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldrdstrd(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0rs8_rwflags(struct kprobe *p,
> + struct pt_regs *regs);
> +void __kprobes emulate_rd12rn16rm0_rwflags_nopc(struct kprobe *p,
> + struct pt_regs *regs);
> +void __kprobes emulate_rd16rn12rm0rs8_rwflags_nopc(struct kprobe *p,
> + struct pt_regs *regs);
> +void __kprobes emulate_rd12rm0_noflags_nopc(struct kprobe *p,
> + struct pt_regs *regs);
> +void __kprobes emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(struct kprobe *p,
> + struct pt_regs *regs);
> +
> +#endif
--
Tixy
More information about the linux-arm-kernel
mailing list