Signed-off-by: Eugene Teo Documentation/kprobes.txt | 1 arch/arm/Kconfig | 7 arch/arm/kernel/Kconfig | 13 arch/arm/kernel/Makefile | 1 arch/arm/kernel/entry-armv.S | 21 arch/arm/kernel/kprobes-decode.c | 1599 +++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/kprobes.c | 534 +++++++++++++ arch/arm/kernel/traps.c | 16 arch/arm/kernel/vmlinux.lds.S | 1 arch/arm/mm/fault.c | 52 + include/asm-arm/kdebug.h | 35 include/asm-arm/kprobes.h | 84 ++ include/asm-arm/ptrace.h | 2 13 files changed, 2358 insertions(+), 8 deletions(-) diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/Documentation/kprobes.txt kernel-source-rx-34-2.6.18/Documentation/kprobes.txt --- kernel-source-rx-34-2.6.18.default/Documentation/kprobes.txt 2006-11-08 20:18:36.000000000 +0800 +++ kernel-source-rx-34-2.6.18/Documentation/kprobes.txt 2007-07-07 13:27:13.000000000 +0800 @@ -140,6 +140,7 @@ architectures: - ppc64 - ia64 (Does not support probes on instruction slot1.) - sparc64 (Return probes not yet implemented.) +- arm 3. Configuring Kprobes diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/Kconfig kernel-source-rx-34-2.6.18/arch/arm/Kconfig --- kernel-source-rx-34-2.6.18.default/arch/arm/Kconfig 2006-11-08 20:18:37.000000000 +0800 +++ kernel-source-rx-34-2.6.18/arch/arm/Kconfig 2007-07-07 13:27:13.000000000 +0800 @@ -907,8 +907,15 @@ endmenu source "fs/Kconfig" +menu "Instrumentation Support" + depends on EXPERIMENTAL + source "arch/arm/oprofile/Kconfig" +source "arch/arm/kernel/Kconfig" + +endmenu + source "arch/arm/Kconfig.debug" source "security/Kconfig" diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/Kconfig kernel-source-rx-34-2.6.18/arch/arm/kernel/Kconfig --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/Kconfig 1970-01-01 07:30:00.000000000 +0730 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/Kconfig 2007-07-07 13:27:13.000000000 +0800 @@ -0,0 +1,13 @@ +menu "Kernel Probes(Kprobes)" + depends on KALLSYMS && EXPERIMENTAL && MODULES + +config KPROBES + bool "Kernel Probes (EXPERIMENTAL)" + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +endmenu diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/Makefile kernel-source-rx-34-2.6.18/arch/arm/kernel/Makefile --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/Makefile 2006-11-08 20:18:38.000000000 +0800 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/Makefile 2007-07-07 13:28:20.000000000 +0800 @@ -19,6 +19,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/entry-armv.S kernel-source-rx-34-2.6.18/arch/arm/kernel/entry-armv.S --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/entry-armv.S 2006-11-08 20:18:38.000000000 +0800 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/entry-armv.S 2007-07-07 13:28:25.000000000 +0800 @@ -11,8 +11,8 @@ * * Low-level vector interface routines * - * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes - * it to save wrong values... Be aware! + * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction + * that causes it to save wrong values... Be aware! */ #include @@ -57,6 +57,12 @@ .endm +#ifdef CONFIG_KPROBES + .section .kprobes.text,"ax",%progbits +#else + .text +#endif + /* * Invalid mode handlers */ @@ -235,7 +241,16 @@ svc_preempt: .align 5 __und_svc: +#ifdef CONFIG_KPROBES + @ Give kprobe'd instruction stack space to finish in case of STM. + sub sp, sp, #64 +#endif svc_entry +#ifdef CONFIG_KPROBES + @ Adjust saved SP back to its original value. + add r0, r0, #64 + str r0, [sp, #S_SP] +#endif @ @ call emulation code, which returns using r9 if it has emulated @@ -533,7 +548,7 @@ do_fpe: .data ENTRY(fp_enter) .word fpundefinstr - .text + .previous fpundefinstr: mov r0, sp diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/kprobes-decode.c kernel-source-rx-34-2.6.18/arch/arm/kernel/kprobes-decode.c --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/kprobes-decode.c 1970-01-01 07:30:00.000000000 +0730 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/kprobes-decode.c 2007-07-07 13:27:13.000000000 +0800 @@ -0,0 +1,1599 @@ +/* arch/arm/kernel/kprobes-decode.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. + */ + +/* We do not have hardware single-stepping on ARM, This + * effort is further complicated by the ARM not having a + * "next PC" register. Instructions that change the PC + * can't be safely single-stepped in a MP environment, so + * we have a lot of work to do: + * + * In the prepare phase: + * *) If it is an instruction that does anything + * with the CPU mode, we reject it for a kprobe. + * (This is out of laziness rather than need. The + * instructions could be simulated.) + * + * *) Otherwise, decode the instruction rewriting its + * registers to take fixed, ordered registers and + * setting a handler for it to run the instruction. + * + * In the execution phase by an instruction's handler: + * + * *) If the PC is written to by the instruction, the + * instruction must be fully simulated in software. + * If it is a conditional instruction, the handler + * will use insn[0] to copy its condition code to + * set r0 to 1 and insn[1] to "mov pc, lr" to return. + * + * *) Otherwise, a modified form of the instruction can + * directly executed. Its handler calls the + * instruction in insn[0]. In insn[1] is a + * "mov pc, lr" to return. + * + * Before calling, load up the reordered registers + * from the original instruction's registers. If one + * of the original input registers is the PC, compute + * and adjust the appropriate input register. + * + * After call completes, copy the output registers to + * the original instruction's original registers. + * + * We don't use a real breakpoint instruction since that + * would have us in the kernel go from SVC mode to SVC + * mode losing the link register. Instead we use an + * undefined instruction. To simply processing, the + * undefined instruction used for kprobes must be reserved + * exclusively for kprobes use. + */ + +/* TODO: ifdef instruction decoding based on architecture. + */ + +#include +#include +#include + +#define array_cnt(x) (sizeof((x))/sizeof(*(x))) + +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) + +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) + +#define PSR_fs (PSR_f|PSR_s) + +#define SET_R0_TRUE_INSTRUCTION 0xe3a00001 /* mov r0, #1 */ +#define truecc_insn(insn) ( ((insn) & 0xf0000000) | \ + (SET_R0_TRUE_INSTRUCTION & 0x0fffffff) ) + +typedef long (insn_0arg_fn_t)(void); +typedef long (insn_1arg_fn_t)(long); +typedef long (insn_2arg_fn_t)(long,long); +typedef long (insn_3arg_fn_t)(long,long,long); +typedef long (insn_4arg_fn_t)(long,long,long,long); +typedef long long (insn_llret_0arg_fn_t)(void); +typedef long long (insn_llret_3arg_fn_t)(long,long,long); +typedef long long (insn_llret_4arg_fn_t)(long,long,long,long); + +typedef union { + long long dr; +#ifdef __LITTLE_ENDIAN + struct { long r0, r1; }; +#else + struct { long r1, r0; }; +#endif +} reg_pair; + +extern int kprobes_str_pc_offset; + + +/* The insnslot_?arg_r[w]flags() functions below are to keep the + * msr -> *fn -> mrs instruction sequences indivisible so that + * the state of the CPSR flags aren't inadvertently modified + * just before or just after the call. + */ + +static inline long __kprobes insnslot_0arg_rflags(long cpsr, insn_0arg_fn_t *fn) +{ + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + +static inline long long __kprobes insnslot_llret_0arg_rflags(long cpsr, + insn_llret_0arg_fn_t *fn) +{ + register long ret0 asm("r0"); + register long ret1 asm("r1"); + reg_pair fnr; + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret0), "=r" (ret1) + : [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + fnr.r0 = ret0; + fnr.r1 = ret1; + return fnr.dr; +} + + +static inline long __kprobes insnslot_1arg_rflags(long r0, long cpsr, + insn_1arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long __kprobes insnslot_2arg_rflags(long r0, long r1, long cpsr, + insn_2arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), "r" (rr1), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long __kprobes insnslot_3arg_rflags(long r0, long r1, long r2, + long cpsr, insn_3arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), "r" (rr1), "r" (rr2), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long long __kprobes insnslot_llret_3arg_rflags(long r0, long r1, + long r2, long cpsr, + insn_llret_3arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long ret0 asm("r0"); + register long ret1 asm("r1"); + reg_pair fnr; + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret0), "=r" (ret1) + : "0" (rr0), "r" (rr1), "r" (rr2), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + fnr.r0 = ret0; + fnr.r1 = ret1; + return fnr.dr; +} + + +static inline long __kprobes insnslot_4arg_rflags(long r0, long r1, long r2, + long r3, long cpsr, + insn_4arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long rr3 asm("r3") = r3; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long __kprobes insnslot_1arg_rwflags(long r0, long *cpsr, + insn_1arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long __kprobes insnslot_2arg_rwflags(long r0, long r1, long *cpsr, + insn_2arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long __kprobes insnslot_3arg_rwflags(long r0, long r1, long r2, + long *cpsr, + insn_3arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), "r" (rr2), + [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long __kprobes insnslot_4arg_rwflags(long r0, long r1, long r2, + long r3, long *cpsr, + insn_4arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long rr3 asm("r3") = r3; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), + [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long long __kprobes insnslot_llret_4arg_rwflags(long r0, long r1, + long r2, long r3, long *cpsr, + insn_llret_4arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long rr3 asm("r3") = r3; + register long ret0 asm("r0"); + register long ret1 asm("r1"); + long oldcpsr = *cpsr; + long newcpsr; + reg_pair fnr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret0), "=r" (ret1), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), + [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + fnr.r0 = ret0; + fnr.r1 = ret1; + return fnr.dr; +} + + +/* + * To avoid having to the complications of mimicing single-stepping + * on a processor without a Next-PC or a single-step mode, and to + * avoid having to deal with the side-effects of boosting, we + * simulate or emulate (almost) all ARM instructions. + * + * "Simulation" is where the instruction's behavior is duplicated in + * C code. "Emulation" is where the original instruction is rewritten + * and executed, often by altering its registers. + * + * By having all behavior of the kprobe'd instruction completed before + * returning from the kprobe_handler(), all locks (scheduler and + * interrupt) can safely be released. There is no need for secondary + * breakpoints, no race with MP or preemptable kernels, nor having to + * clean up resources counts at a later time impacting overall system + * performance. By rewriting the instruction, only the minimum registers + * need to be loaded and saved back optimizing perfoamance. + * + * Calling the insnslot_*_rwflags version of a function doesn't hurt + * anything even when the CPSR flags aren't updated by the + * instruction. It's just a little slower in return for saving + * a little space by not having a duplicate function that doesn't + * update the flags. (The same optimization can be said for + * instructions that do or don't update the writeback register.) + * Also, instructions can either read the flags, only write the + * flags, or read and write the flags. To save combinations + * rather than for sheer performance, flag functions just assume + * read and write of flags. + */ + +static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long iaddr = (long)p->addr; + int disp = branch_displacement(insn); + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + 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) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + if (insn & (1 << 5)) + regs->ARM_lr = (long)p->addr + 4; + + regs->ARM_pc = rmv & ~0x1; + regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_T_BIT) | ((rmv & 0x1) << 5); +} + + +static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rn = (insn >> 16) & 0xf; + int lbit = (insn >> 20) & 0x1; + int wbit = (insn >> 21) & 0x1; + int ubit = (insn >> 23) & 0x1; + int pbit = (insn >> 24) & 0x1; + long *addr = (long *)regs->uregs[rn]; + int reg_bit_vector; + int reg_count; + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + reg_count = 0; + reg_bit_vector = insn & 0xffff; + + while (reg_bit_vector) { + reg_bit_vector &= ~(1 << __ffs(reg_bit_vector)); + ++reg_count; + } + + if (!ubit) addr -= reg_count; + if (!(pbit ^ ubit)) addr += 1; + + reg_bit_vector = insn & 0xffff; + + while (reg_bit_vector) { + int reg = __ffs(reg_bit_vector); + reg_bit_vector &= ~(1 << reg); + if (lbit) { + regs->uregs[reg] = *addr++; + } else { + *addr++ = regs->uregs[reg]; + } + } + + if (wbit) { + if (!ubit) addr -= reg_count; + if (!(pbit ^ ubit)) addr -= 1; + regs->uregs[rn] = (long)addr; + } +} + + +static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + regs->ARM_pc = (long)p->addr + kprobes_str_pc_offset; + simulate_ldm1stm1(p,regs); + regs->ARM_pc = (long)p->addr + 4; +} + + +static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs) +{ + regs->uregs[12] = regs->uregs[13]; +} + + +static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rn = (insn >> 16) & 0xf; + long rnv = regs->uregs[rn]; + + /* Save Rn in case of writeback. */ + regs->uregs[rn] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + + /* Not following the C calling convention here, so need asm(). */ + __asm__("mov r0, %3 \n\t" + "mov r1, %4 \n\t" + "msr cpsr_fs, %5 \n\t" + "mov lr, pc \n\t" + "mov pc, %5 \n\t" + "str r0, %0 \n\t" /* Rn in case of writeback. */ + "str r2, %1 \n\t" /* Rd */ + "str r3, %2 \n\t" /* R(d+1) */ + : "=m" (regs->uregs[rn]), + "=m" (regs->uregs[rd]), + "=m" (regs->uregs[rd+1]) + : "r" (rnv), "r" (rmv), "r" (i_fn), "r" (regs->ARM_cpsr) + : "r0", "r1", "r2", "r3", "lr", "cc" + ); +} + + +static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs) +{ + insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + + regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd], + regs->uregs[rd+1], + regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs) +{ + insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + reg_pair fnr; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rdv; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + long cpsr = regs->ARM_cpsr; + + fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn); + regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */ + rdv = fnr.r1; + + if (rd == 15) { +#if __LINUX_ARM_ARCH__ >= 5 + rdv &= ~0x1; + regs->ARM_cpsr = (cpsr & ~PSR_T_BIT) | ((rdv & 0x1) << 5); +#else + rdv &= ~0x2; +#endif + } + + regs->uregs[rd] = rdv; +} + + +static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long iaddr = (long)p->addr; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rdv = (rd == 15) ? iaddr + kprobes_str_pc_offset : + regs->uregs[rd]; + long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + + /* Save Rn in case of writeback. */ + regs->uregs[rn] = insnslot_3arg_rflags(rnv, rdv, rmv, + regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs) +{ + insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + reg_pair fnr; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + + fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn); + regs->uregs[rn] = fnr.r0; + regs->uregs[rd] = fnr.r1; +} + + +static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + long rnv = regs->uregs[rn]; + long rdv = regs->uregs[rd]; + + insnslot_2arg_rflags(rnv, rdv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + /* Writes Q flag */ + regs->uregs[rd] = insnslot_1arg_rwflags(rmv, ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_sel(struct kprobe *p, struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; + + /* Reads GE bits */ + regs->uregs[rd] = insnslot_2arg_rflags(rnv, rmv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs) +{ + insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0]; + + insnslot_0arg_rflags(regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs) +{ + insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + + regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int ird = (insn >> 12) & 0xf; + + insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rn = (insn >> 16) & 0xf; + long rnv = regs->uregs[rn]; + + insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_1arg_rflags(rmv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd12rn16rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_2arg_rwflags(rnv, rmv, + ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd16rn12rs8rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 16) & 0xf; + int rn = (insn >> 12) & 0xf; + int rs = (insn >> 8) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rsv = regs->uregs[rs]; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_3arg_rwflags(rnv, rsv, rmv, + ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd16rs8rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 16) & 0xf; + int rs = (insn >> 8) & 0xf; + int rm = insn & 0xf; + long rsv = regs->uregs[rs]; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_2arg_rwflags(rsv, rmv, + ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rdhi16rdlo12rs8rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_llret_4arg_fn_t *i_fn = (insn_llret_4arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + reg_pair fnr; + int rdhi = (insn >> 16) & 0xf; + int rdlo = (insn >> 12) & 0xf; + int rs = (insn >> 8) & 0xf; + int rm = insn & 0xf; + long rsv = regs->uregs[rs]; + long rmv = regs->uregs[rm]; + + fnr.dr = insnslot_llret_4arg_rwflags(regs->uregs[rdhi], + regs->uregs[rdlo], rsv, rmv, + ®s->ARM_cpsr, i_fn); + regs->uregs[rdhi] = fnr.r0; + regs->uregs[rdlo] = fnr.r1; +} + + +static void __kprobes emulate_alu_imm_rflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; + + regs->uregs[rd] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_alu_imm_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; + + regs->uregs[rd] = insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_alu_rflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long ppc = (long)p->addr + 8; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */ + int rs = (insn >> 8) & 0xf; /* invalid, don't care. */ + int rm = insn & 0xf; + long rnv = (rn == 15) ? ppc : regs->uregs[rn]; + long rmv = (rm == 15) ? ppc : regs->uregs[rm]; + long rsv = regs->uregs[rs]; + + regs->uregs[rd] = insnslot_3arg_rflags(rnv, rmv, rsv, + regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_alu_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long ppc = (long)p->addr + 8; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */ + int rs = (insn >> 8) & 0xf; /* invalid, don't care. */ + int rm = insn & 0xf; + long rnv = (rn == 15) ? ppc : regs->uregs[rn]; + long rmv = (rm == 15) ? ppc : regs->uregs[rm]; + long rsv = regs->uregs[rs]; + + regs->uregs[rd] = insnslot_3arg_rwflags(rnv, rmv, rsv, + ®s->ARM_cpsr, i_fn); +} + + +static enum kprobe_insn __kprobes prep_emulate_ldr_str(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + int ibit = (insn & (1 << 26)) ? 25 : 22; + + insn &= 0xfff00fff; + insn |= 0x00001000; /* Rn = r0, Rd = r1 */ + if (insn & (1 << ibit)) { + insn &= ~0xf; + insn |= 2; /* Rm = r2 */ + } + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_ldr : emulate_str; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes prep_emulate_rd12rm0(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd12rm0; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes prep_emulate_rd12(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xffff0fff; /* Rd = r0 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd12; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd12rn16rm0_rwflags; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff0f0f0; /* Rd = r0, Rs = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd16rs8rm0_rwflags; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff000f0; /* Rd = r0, Rn = r0 */ + insn |= 0x00000102; /* Rs = r1, Rm = r2 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd16rn12rs8rm0_rwflags; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff000f0; /* RdHi = r0, RdLo = r1 */ + insn |= 0x00001203; /* Rs = r2, Rm = r3 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rdhi16rdlo12rs8rm0_rwflags; + return INSN_GOOD; +} + + +/* + * For the instruction masking and comparisons in all the "space_*" + * functions below, Do _not_ rearrange the order of tests unless + * you're very, very sure of what you are doing. For the sake of + * efficiency, the masks for some tests sometimes assume other test + * have been done prior to them so the number of patterns to test + * for an instruction set can be as broad as possible to reduce the + * number of tests needed. + */ + +static enum kprobe_insn __kprobes space_1111(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx + * RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx + * SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx + */ + if ( (insn & 0xfff30020) == 0xf1020000 || + (insn & 0xfe500f00) == 0xf8100a00 || + (insn & 0xfe5f0f00) == 0xf84d0500 ) + return INSN_REJECTED; + + /* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */ + if ( (insn & 0xfd700000) == 0xf4500000 ) { + insn &= 0xfff0ffff; /* Rn = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_rn16; + return INSN_GOOD; + } + + /* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */ + if ( (insn & 0xfe000000) == 0xfa000000 ) { + asi->insn_handler = simulate_blx1; + return INSN_GOOD_NO_SLOT; + } + + /* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx : + * CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx : + */ + if ( (insn & 0xffff00f0) == 0xf1010000 || + (insn & 0xff000010) == 0xfe000000 ) { + asi->insn[0] = insn; + asi->insn_handler = emulate_none; + return INSN_GOOD; + } + + /* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) + * MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) + */ + if ( (insn & 0xffe00000) == 0xfc400000 ) { + insn &= 0xfff00fff; /* Rn = r0 */ + insn |= 0x00001000; /* Rd = r1 */ + + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? + emulate_mrrc : emulate_mcrr; + return INSN_GOOD; + } + + /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx : + * STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx : + */ + if ( (insn & 0xfe000000) == 0xfc000000 ) { + insn &= 0xfff0ffff; /* Rn = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_ldcstc; + return INSN_GOOD; + } + + /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx : + * MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx : + */ + insn &= 0xffff0fff; /* Rd = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_000x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + if ( (insn & 0x0f900010) == 0x01000000 ) { + /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */ + + /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx + * MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx + */ + if ( (insn & 0x0ff000f0) == 0x01200020 || + (insn & 0x0fb000f0) == 0x01200000 ) + return INSN_REJECTED; + + /* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx : */ + if ( (insn & 0x0fb00010) == 0x01000000 ) + return prep_emulate_rd12(insn, asi); + + /* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx : */ + if ( (insn & 0x0ff00090) == 0x01400080 ) + return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); + + /* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx : + * SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx : + */ + if ( (insn & 0x0ff000b0) == 0x012000a0 || + (insn & 0x0ff00090) == 0x01600080 ) + return prep_emulate_rd16rs8rm0_wflags(insn, asi); + + /* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx :Q + * SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 0x00 xxxx :Q + */ + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + + } else if ( (insn & 0x0f900090) == 0x01000010 ) { + /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */ + + /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ + if ( (insn & 0xfff000f0) == 0xe1200070 ) + return INSN_REJECTED; + + /* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx : + * BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx : + */ + + if ( (insn & 0x0ff000d0) == 0x01200010 ) { + asi->insn[0] = truecc_insn(insn); + asi->insn_handler = simulate_blx2bx; + return INSN_GOOD; + } + + /* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx : */ + if ( (insn & 0x0ff000f0) == 0x01600010 ) + return prep_emulate_rd12rm0(insn, asi); + + /* QADD : cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx :Q + * QSUB : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q + * QDADD : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q + * QDSUB : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q + */ + return prep_emulate_rd12rn16rm0_wflags(insn, asi); + + } else if ( (insn & 0x0f000090) == 0x00000090 ) { + /* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */ + + /* MUL : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx : + * MULS : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc + * MLA : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx : + * MLAS : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc + * UMAAL : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx : + * UMULL : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx : + * UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc + * UMLAL : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx : + * UMLALS : cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx :cc + * SMULL : cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx : + * SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc + * SMLAL : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx : + * SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc + */ + + if ( (insn & 0x0fe000f0) == 0x00000090 ) { + return prep_emulate_rd16rs8rm0_wflags(insn, asi); + } else if ( (insn & 0x0fe000f0) == 0x00200090 ) { + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + } else { + return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); + } + + } else if ( (insn & 0x0e000090) == 0x00000090 ) { + /* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */ + + /* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx : + * SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx : + * LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx : + * STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx : + * STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx : + * LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx : + * LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx : + * STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx : + * LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx : + * LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx : + */ + + if ( (insn & 0x0fb000f0) == 0x01000090 ) { /* SWP/SWPB */ + return prep_emulate_rd12rn16rm0_wflags(insn, asi); + } else if ( (insn & 0x0e1000d0) == 0x00000d0 ) { /* STRD/LDRD */ + insn &= 0xfff00fff; + insn |= 0x00002000; /* Rn = r0, Rd = r2 */ + + if (insn & (1 << 22)) { /* I bit */ + insn &= ~0xf; + insn |= 1; /* Rm = r1 */ + } + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 5)) ? + emulate_strd : + emulate_ldrd; + return INSN_GOOD; + } + + return prep_emulate_ldr_str(insn, asi); + } + + /* cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx */ + + /* ALU op with S bit and Rd == 15 : + * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx + */ + if ( (insn & 0x0e10f000) == 0x0010f000 ) + return INSN_REJECTED; + + /* MOV r12,r13 is the most common kprobe'd instruction by far. + * Check and optimize for it explicitly. + */ + + if (insn == 0xe1a0c00d) { + asi->insn_handler = simulate_mov_ipsp; + return INSN_GOOD_NO_SLOT; + } + + /* Data processing: Immediate-shift / Register-shift + * ALU op : cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx + * CPY : cccc 0001 1010 xxxx xxxx 0000 0000 xxxx + * MOV : cccc 0001 101x xxxx xxxx xxxx xxxx xxxx + * *S (bit 20) updates condition codes + * ADC/SBC/RSC reads the C flag + */ + + insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + + if (insn & 0x010) { + insn &= 0xfffff0ff; /* register shift */ + insn |= 0x00000200; /* Rs = r2 */ + } + + asi->insn[0] = insn; + asi->insn_handler = emulate_alu_rwflags; + asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ + emulate_alu_rwflags : emulate_alu_rflags; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_001x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx + * Undef : cccc 0011 0x00 xxxx xxxx xxxx xxxx xxxx + * ALU op with S bit and Rd == 15 : + * cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx + */ + + if ( (insn & 0x0f900000) == 0x03200000 || /* MSR & Undef */ + (insn & 0x0e10f000) == 0x0210f000 ) /* ALU s-bit, R15 */ + return INSN_REJECTED; + + /* Data processing: 32-bit Immediate + * ALU op : cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx + * MOV : cccc 0011 101x xxxx xxxx xxxx xxxx xxxx + * *S (bit 20) updates condition codes + * ADC/SBC/RSC reads the C flag + */ + + insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */ + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ + emulate_alu_imm_rwflags : emulate_alu_imm_rflags; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_0110__1(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */ + + if ( (insn & 0x0ff000f0) == 0x068000b0 ) { + insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_sel; + return INSN_GOOD; + } + + /* SSAT : cccc 0110 101x xxxx xxxx xxxx xx01 xxxx :Q + * USAT : cccc 0110 111x xxxx xxxx xxxx xx01 xxxx :Q + * SSAT16 : cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx :Q + * USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q + */ + + if ( (insn & 0x0fa00030) == 0x06a00010 || + (insn & 0x0fb000f0) == 0x06a00030 ) { + insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_sat; + return INSN_GOOD; + } + + /* + * REV : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx : + * REV16 : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx : + * REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx : + */ + + if ( (insn & 0x0ff00070) == 0x06b00030 || + (insn & 0x0ff000f0) == 0x06f000b0 ) + return prep_emulate_rd12rm0(insn, asi); + + /* SADD16 : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE + * SADDSUBX : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE + * SSUBADDX : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE + * SSUB16 : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE + * SADD8 : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE + * SSUB8 : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE + * QADD16 : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx : + * QADDSUBX : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx : + * QSUBADDX : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx : + * QSUB16 : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx : + * QADD8 : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx : + * QSUB8 : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx : + * SHADD16 : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx : + * SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx : + * SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx : + * SHSUB16 : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx : + * SHADD8 : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx : + * SHSUB8 : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx : + * UADD16 : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE + * UADDSUBX : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE + * USUBADDX : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE + * USUB16 : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE + * UADD8 : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE + * USUB8 : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE + * UQADD16 : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx : + * UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx : + * UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx : + * UQSUB16 : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx : + * UQADD8 : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx : + * UQSUB8 : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx : + * UHADD16 : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx : + * UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx : + * UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx : + * UHSUB16 : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx : + * UHADD8 : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx : + * UHSUB8 : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx : + * PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx : + * PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx : + * SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx : + * SXTB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : + * SXTAB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : + * SXTAH : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx : + * UXTAB16 : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx : + * UXTAB : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx : + * UXTAH : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx : + */ + + return prep_emulate_rd12rn16rm0_wflags(insn, asi); +} + +static enum kprobe_insn __kprobes space_cccc_0111__1(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* Undef : cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ + if ( (insn & 0x0ff000f0) == 0x03f000f0 ) + return INSN_REJECTED; + + /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx : + * USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx : + */ + + if ( (insn & 0x0ff000f0) == 0x07800010 ) + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + + /* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx : + * SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx : + */ + + if ( (insn & 0x0ff00090) == 0x07400010 ) + return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); + + /* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q + * SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q + * SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx : + * SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : + */ + + if ( (insn & 0x0ff00090) == 0x07000010 || + (insn & 0x0ff000d0) == 0x07500010 || + (insn & 0x0ff000d0) == 0x075000d0 ) + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + + /* SMUSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx : + * SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q + * SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : + */ + + return prep_emulate_rd16rs8rm0_wflags(insn, asi); +} + + +static enum kprobe_insn __kprobes space_cccc_01xx(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* LDR : cccc 01xx x0x1 xxxx xxxx xxxx xxxx xxxx : + * LDRB : cccc 01xx x1x1 xxxx xxxx xxxx xxxx xxxx : + * LDRBT : cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx : + * LDRT : cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx : + * STR : cccc 01xx x0x0 xxxx xxxx xxxx xxxx xxxx : + * STRB : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx : + * STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx : + * STRT : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx : + */ + + return prep_emulate_ldr_str(insn, asi); +} + + +static enum kprobe_insn __kprobes space_cccc_100x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* LDM(2) : cccc 100x x101 xxxx 0xxx xxxx xxxx xxxx + * LDM(3) : cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx + */ + + if ( (insn & 0x0e708000) == 0x85000000 || + (insn & 0x0e508000) == 0x85010000 ) + return INSN_REJECTED; + + /* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx : + * STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx : + */ + + asi->insn[0] = truecc_insn(insn); + asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */ + simulate_stm1_pc : simulate_ldm1stm1; + + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_101x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* B : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx : + * BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx : + */ + + asi->insn[0] = truecc_insn(insn); + asi->insn_handler = simulate_bbl; + + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_1100_010x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) + * MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) + */ + insn &= 0xfff00fff; + insn |= 0x00001000; /* Rn = r0, Rd = r1 */ + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_110x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx : + * STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx : + */ + insn &= 0xfff0ffff; /* Rn = r0 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_ldcstc; + + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_111x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx + * SWI : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx + */ + if ( (insn & 0xfff000f0) == 0xe1200070 || + (insn & 0x0f000000) == 0x0f000000 ) + return INSN_REJECTED; + + /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx : */ + if ( (insn & 0x0f000010) == 0x0e000000 ) { + asi->insn[0] = insn; + asi->insn_handler = emulate_none; + return INSN_GOOD; + } + + /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx : + * MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx : + */ + insn &= 0xffff0fff; /* Rd = r0 */ + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12; + return INSN_GOOD; +} + + +/* Return: + * INSN_REJECTED If instruction is one not allowed to kprobe, + * INSN_GOOD If instruction is supported and uses instruction slot, + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. + * + * For instructions we don't want to kprobe (INSN_REJECTED return result): + * These are generally ones that modify the processor state making + * them "hard" to simulate such as switches processor modes or + * make accesses in alternate modes. Any of these could be simulated + * if the work was put into it, but low return considering they + * should also be very rare. + */ + +enum kprobe_insn __kprobes kprobe_decode_insn(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + asi->insn[1] = KPROBE_RETURN_INSTRUCTION; + + if ( (insn & 0xf0000000) == 0xf0000000 ) { + + return space_1111(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x00000000 ) { + + return space_cccc_000x(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x02000000 ) { + + return space_cccc_001x(insn, asi); + + } else if ( (insn & 0x0f000010) == 0x06000010 ) { + + return space_cccc_0110__1(insn, asi); + + } else if ( (insn & 0x0f000010) == 0x07000010 ) { + + return space_cccc_0111__1(insn, asi); + + } else if ( (insn & 0x0c000000) == 0x04000000 ) { + + return space_cccc_01xx(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x08000000 ) { + + return space_cccc_100x(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x0a000000 ) { + + return space_cccc_101x(insn, asi); + + } else if ( (insn & 0x0fe00000) == 0x0c400000 ) { + + return space_cccc_1100_010x(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x0c400000 ) { + + return space_cccc_110x(insn, asi); + + } + + return space_cccc_111x(insn, asi); +} + + +/* + * All ARM instructions listed below. + * + * Instructions and their general purpose registers are given. + * If a particular register may not use R15, it it prefixed with a "!". + * If it is parked with a "*" means the value returned by readering R15 + * is implementation defined. + * + * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ + * TST: Rd, Rn, Rm, !Rs + * BX: Rm + * BLX(2): !Rm + * BX: Rm (R15 legal, but discouraged) + * BXJ: !Rm, + * CLZ: !Rd, !Rm + * CPY: Rd, Rm + * LDC/2,STC/2 immediate offset & unindex: Rn + * LDC/2,STC/2 immediate pre/post-indexed: !Rn + * LDM(1/3): !Rn, register_list + * LDM(2): !Rn, !register_list + * LDR,STR,PLD immediate offset: Rd, Rn + * LDR,STR,PLD register offset: Rd, Rn, !Rm + * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm + * LDR,STR immediate pre/post-indexed: Rd, !Rn + * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm + * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm + * LDRB,STRB immediate offset: !Rd, Rn + * LDRB,STRB register offset: !Rd, Rn, !Rm + * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm + * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn + * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm + * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm + * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn + * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm + * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm + * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn + * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm + * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn + * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm + * LDREX: !Rd, !Rn + * MCR/2: !Rd + * MCRR/2,MRRC/2: !Rd, !Rn + * MLA: !Rd, !Rn, !Rm, !Rs + * MOV: Rd + * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register) + * MRS,MSR: !Rd + * MUL: !Rd, !Rm, !Rs + * PKH{BT,TB}: !Rd, !Rn, !Rm + * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn + * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn + * REV/16/SH: !Rd, !Rm + * RFE: !Rn + * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm + * SEL: !Rd, !Rn, !Rm + * SMLA,SMLA{D,W},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs + * SMLAL,SMLA{D,LD},SMLSLD,SMMULL,SMULW: !RdHi, !RdLo, !Rm, !Rs + * SMMUL,SMUAD,SMUL,SMUSD: !Rd, !Rm, !Rs + * SSAT/16: !Rd, !Rm + * STM(1/2): !Rn, register_list* (R15 in reg list not recommended) + * STRT immediate pre/post-indexed: Rd*, !Rn + * STRT register pre/post-indexed: Rd*, !Rn, !Rm + * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm + * STREX: !Rd, !Rn, !Rm + * SWP/B: !Rd, !Rn, !Rm + * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm + * {S,U}XT{B,B16,H}: !Rd, !Rm + * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs + * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs + * + * May transfer control by writing R15 (possible mode changes or alternate + * mode accesses marked by "*"): + * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY, + * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI* + * + * Instructions that do not take general registers, nor transfer control: + * CDP/2, SETEND, SRS* + */ diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/kprobes.c kernel-source-rx-34-2.6.18/arch/arm/kernel/kprobes.c --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/kprobes.c 1970-01-01 07:30:00.000000000 +0730 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/kprobes.c 2007-07-07 13:31:35.000000000 +0800 @@ -0,0 +1,534 @@ +/* arch/arm/kernel/kprobes.c + * + * Kprobes on ARM + * + * Abhishek Sagar + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define flush_insns(addr, cnt) \ + flush_icache_range((unsigned long)(addr), \ + (unsigned long)(addr) + \ + sizeof(kprobe_opcode_t) * (cnt)) + +/* Used as a marker in ARM_pc to note when we're in a jprobe. */ +#define JPROBE_MAGIC_ADDR 0xffffffff + +void jprobe_return_end(void); +void kretprobe_trampoline(void); + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + +int kprobes_str_pc_offset; + + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + kprobe_opcode_t insn; + kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; + int is; + + if ((unsigned long)p->addr & 0x3) + return -EINVAL; + + insn = *p->addr; + + p->opcode = insn; + p->ainsn.insn = tmp_insn; + + switch (kprobe_decode_insn(insn, &p->ainsn)) { + case INSN_REJECTED: /* not supported */ + return -EINVAL; + + case INSN_GOOD: /* instruction uses slot */ + p->ainsn.insn = get_insn_slot(); + if (!p->ainsn.insn) + return -ENOMEM; + + for (is = 0; is < MAX_INSN_SIZE; ++is) + p->ainsn.insn[is] = tmp_insn[is]; + + flush_insns(&p->ainsn.insn, MAX_INSN_SIZE); + break; + + case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ + p->ainsn.insn = 0; + break; + } + + return 0; +} + + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = KPROBE_BREAKPOINT_INSTRUCTION; + flush_insns(p->addr, 1); +} + + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flush_insns(p->addr, 1); +} + + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + if (p->ainsn.insn) { + mutex_lock(&kprobe_mutex); + free_insn_slot(p->ainsn.insn); + mutex_unlock(&kprobe_mutex); + p->ainsn.insn = 0; + } +} + +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + + +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + kcb->kprobe_status = kcb->prev_kprobe.status; +} + + +static void __kprobes set_current_kprobe(struct kprobe *p) +{ + __get_cpu_var(current_kprobe) = p; +} + + +/* Called with kretprobe_lock held. */ +void __kprobes arch_prepare_kretprobe(struct kretprobe *rp, + struct pt_regs *regs) +{ + struct kretprobe_instance *ri; + + if ((ri = get_free_rp_inst(rp)) != NULL) { + ri->rp = rp; + ri->task = current; + ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; + + /* Replace the return addr with trampoline addr. */ + regs->ARM_lr = (unsigned long)&kretprobe_trampoline; + add_rp_inst(ri); + } else { + rp->nmissed++; + } +} + + +static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) +{ + regs->ARM_pc += 4; + + p->ainsn.insn_handler(p, regs); + + if ((kcb->kprobe_status != KPROBE_REENTER) && p->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + p->post_handler(p, regs, 0); + } + + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + } else { + reset_current_kprobe(); + } +} + + +/* IRQs were automatically disabled when executing the kprobe + * instruction. IRQs must remain disabled from that point all the + * way until processing this kprobe is complete. The current kprobes + * implementation cannot process more than one nested level of kprobe, + * and that level is reserved for user kprobe handlers, so we can't + * risk encountering a new kprobe in an interrupt handler. + */ + +int __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p, *cur; + struct kprobe_ctlblk *kcb; + kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc; + + kcb = get_kprobe_ctlblk(); + cur = kprobe_running(); + p = get_kprobe(addr); + + if (p) { + if (cur) { /* Kprobe is pending, so we're recursing. */ + switch (kcb->kprobe_status) { + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* A pre- or post-handler probe got us here. */ + kprobes_inc_nmissed_count(p); + save_previous_kprobe(kcb); + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_REENTER; + singlestep(p, regs, kcb); + break; + default: + /* impossible cases */ + BUG(); + } + } else { + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + /* If we have no pre-handler or it returned 0, we + * continue with normal processing. If we have a + * pre-handler and it returned non-zero, it prepped + * for calling the break_handler below on re-entry, + * so get out doing nothing more here. + */ + + if (!p->pre_handler || !p->pre_handler(p, regs)) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(p, regs, kcb); + } + } + } else { + /* Two ways we could have gotten here. Either the probe + * was removed and a race is in progress or we hit a + * jprobe. + * + * If it's a race, nothing we can do about it. Restart + * the instruction. By the time we can restart, the + * real instruction will be there. If we're in a jprobe + * call its break handler. + */ + + if (cur) { + if ( (addr == (kprobe_opcode_t *)JPROBE_MAGIC_ADDR) && + cur->break_handler && + cur->break_handler(cur, regs) ) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(cur, regs, kcb); + } else { + reset_current_kprobe(); + } + } + } + + return 1; +} + + +static int __kprobes kprobe_fault_handler(struct pt_regs *regs, + unsigned int fsr) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + switch (kcb->kprobe_status) { + case KPROBE_HIT_SS: + case KPROBE_REENTER: + /* + * We are here because the instruction being single + * stepped caused a page fault. We reset the current + * kprobe and the PC to point back to the probe address + * and allow the page fault handler to continue as a + * normal page fault. + */ + regs->ARM_pc = (long)cur->addr; + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + } else { + reset_current_kprobe(); + } + break; + + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* + * We increment the nmissed count for accounting, + * we can also use npre/npostfault count for accounting + * these specific fault cases. + */ + kprobes_inc_nmissed_count(cur); + + /* + * We come here because instructions in the pre/post + * handler caused the page_fault, this could happen + * if handler tries to access user space by + * copy_from_user(), get_user() etc. Let the + * user-specified handler try to fix it first. + */ + if (cur->fault_handler && cur->fault_handler(cur, regs, fsr)) + return 1; + + /* + * In case the user-specified fault handler returned + * zero, try to fix up. + */ + + if (fixup_exception(regs)) + return 1; + + /* + * fixup_exception() could not handle it, + * Let do_page_fault() fix it. + */ + break; + + default: + break; + } + + return 0; +} + + +/* + * Wrapper routine to for handling exceptions. + */ +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = (struct die_args *)data; + struct pt_regs *regs = args->regs; + int ret = NOTIFY_DONE; + + /* This function is in a very critical path. If in user + * mode, get out as quick as we can. */ + if (!regs || user_mode(regs)) + return ret; + + switch (val) { + case DIE_TRANS_FAULT: + case DIE_PAGE_FAULT: + /* To be potentially processing a kprobe fault and to + * trust the result from kprobe_running(), we have + * be non-preemptible. */ + if (!preemptible() && + kprobe_running() && + kprobe_fault_handler(regs, args->fsr)) { + ret = NOTIFY_STOP; + } + break; + default: + break; + } + + return ret; +} + + +/* + * When a retprobed function returns, trampoline_handler() is called, + * calling the kretprobe's handler. We contruct a struct pt_regs to + * give a view of registers r0-r11 to the user return-handler. + */ +void __attribute__((naked)) __kprobes kretprobe_trampoline_holder(void) +{ + __asm__ volatile ( + ".global kretprobe_trampoline \n\t" + "kretprobe_trampoline: \n\t" + "stmdb sp!, {r0-r11} \n\t" + "mov r0, sp \n\t" + "bl trampoline_handler \n\t" + "mov lr, r0 \n\t" + "ldmia sp!, {r0-r11} \n\t" + "mov pc, lr \n\t" + : + : + :"memory"); +} + + +/* + * Called from kretprobe_trampoline_holder + */ +void * __kprobes trampoline_handler(struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head, empty_rp; + struct hlist_node *node, *tmp; + unsigned long flags, orig_ret_address = 0; + unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline; + + INIT_HLIST_HEAD(&empty_rp); + spin_lock_irqsave(&kretprobe_lock, flags); + head = kretprobe_inst_table_head(current); + + /* + * It is possible to have multiple instances associated with a given + * task either because multiple functions in the call path have + * a return probe installed on them, and/or more then one return + * return probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) { + __get_cpu_var(current_kprobe) = &ri->rp->kp; + get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; + ri->rp->handler(ri, regs); + __get_cpu_var(current_kprobe) = NULL; + } + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri); + + if (orig_ret_address != trampoline_address) + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + + BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); + + spin_unlock_irqrestore(&kretprobe_lock, flags); + + hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + kfree(ri); + } + + return (void *)orig_ret_address; +} + + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long sp_addr = regs->ARM_sp; + + kcb->jprobe_saved_regs = *regs; + memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); + regs->ARM_pc = (long)jp->entry; + regs->ARM_cpsr |= PSR_I_BIT; + preempt_disable(); + return 1; +} + + +#define str(s) #s +#define xstr(s) str(s) + +void __kprobes jprobe_return(void) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + __asm__ volatile( + /* Setup an empty pt_regs. Fill SP and PC fields as + * they're needed by longjmp_break_handler. + */ + "sub sp, %0, %1 \n\t" + "ldr r0, =" xstr(JPROBE_MAGIC_ADDR) "\n\t" + "str r0, [sp, %3] \n\t" + "str %0, [sp, %2] \n\t" + + "mov r0, sp \n\t" + "bl kprobe_handler \n\t" + + /* Return to the context saved by setjmp_pre_handler + * and restored by longjmp_break_handler. */ + "ldr r0, [sp, %4] \n\t" + "msr cpsr_cxsf, r0 \n\t" + "ldmia sp, {r0-pc} \n\t" + : + : "r" (kcb->jprobe_saved_regs.ARM_sp), + "I"(sizeof(struct pt_regs)), + "J"(offsetof(struct pt_regs, ARM_sp)), + "J"(offsetof(struct pt_regs, ARM_pc)), + "J"(offsetof(struct pt_regs, ARM_cpsr)) + : "memory", "cc"); +} + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long stack_addr = kcb->jprobe_saved_regs.ARM_sp; + long orig_sp = regs->ARM_sp; + struct jprobe *jp = container_of(p, struct jprobe, kp); + + if (orig_sp != stack_addr) { + struct pt_regs *saved_regs = + (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp; + printk("current sp %lx does not match saved sp %lx\n", + orig_sp, stack_addr); + printk("Saved registers for jprobe %p\n", jp); + show_regs(saved_regs); + printk("Current registers\n"); + show_regs(regs); + BUG(); + } + + *regs = kcb->jprobe_saved_regs; + memcpy((void *)stack_addr, kcb->jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + preempt_enable_no_resched(); + + return 1; +} + + +/* + * For STR and STM instructions, an ARM core may choose to use either + * a +8 or a +12 displacement from the current instruction's address. + * Whichever value is chosen for a given core, it must be the same for + * both instructions and may not change. This function measures it. + */ + +static int __init find_str_pc_offset(void) +{ + int addr; + int scratch; + int ret; + + __asm__("sub %[ret], pc, #4 \n\t" + "str pc, %[addr] \n\t" + "ldr %[scr], %[addr] \n\t" + "sub %[ret], %[scr], %[ret] \n\t" + : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr) ); + + return ret; +} + +int __init arch_init_kprobes() +{ + kprobes_str_pc_offset = find_str_pc_offset(); + return 0; +} diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/traps.c kernel-source-rx-34-2.6.18/arch/arm/kernel/traps.c --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/traps.c 2006-11-08 20:18:38.000000000 +0800 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/traps.c 2007-07-07 13:27:13.000000000 +0800 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -272,7 +273,7 @@ void unregister_undef_hook(struct undef_ spin_unlock_irqrestore(&undef_lock, flags); } -asmlinkage void do_undefinstr(struct pt_regs *regs) +asmlinkage void __kprobes do_undefinstr(struct pt_regs *regs) { unsigned int correction = thumb_mode(regs) ? 2 : 4; unsigned int instr; @@ -287,6 +288,19 @@ asmlinkage void do_undefinstr(struct pt_ */ regs->ARM_pc -= correction; +#ifdef CONFIG_KPROBES + /* + * Redirect kernel mode undef exceptions to kprobes. + */ + if (!user_mode(regs) && !thumb_mode(regs)) { + if ( (*(unsigned long *)regs->ARM_pc == + KPROBE_BREAKPOINT_INSTRUCTION) && + kprobe_handler(regs) ) { + return; + } + } +#endif /* CONFIG_KPROBES */ + pc = (void __user *)instruction_pointer(regs); if (thumb_mode(regs)) { get_user(instr, (u16 __user *)pc); diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/kernel/vmlinux.lds.S kernel-source-rx-34-2.6.18/arch/arm/kernel/vmlinux.lds.S --- kernel-source-rx-34-2.6.18.default/arch/arm/kernel/vmlinux.lds.S 2006-11-08 20:18:38.000000000 +0800 +++ kernel-source-rx-34-2.6.18/arch/arm/kernel/vmlinux.lds.S 2007-07-07 13:27:13.000000000 +0800 @@ -90,6 +90,7 @@ SECTIONS *(.text) SCHED_TEXT LOCK_TEXT + KPROBES_TEXT #ifdef CONFIG_MMU *(.fixup) #endif diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/arch/arm/mm/fault.c kernel-source-rx-34-2.6.18/arch/arm/mm/fault.c --- kernel-source-rx-34-2.6.18.default/arch/arm/mm/fault.c 2006-11-08 20:18:41.000000000 +0800 +++ kernel-source-rx-34-2.6.18/arch/arm/mm/fault.c 2007-07-07 13:27:13.000000000 +0800 @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include +#include #include "fault.h" @@ -73,6 +75,44 @@ void show_pte(struct mm_struct *mm, unsi printk("\n"); } +#ifdef CONFIG_KPROBES +ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); + +/* Hook to register for page fault notifications */ +int register_page_fault_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(¬ify_page_fault_chain, nb); +} + +int unregister_page_fault_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb); +} + +static inline int __kprobes notify_page_fault(enum die_val val, const char *str, + unsigned long addr, + struct pt_regs *regs, + unsigned int fsr) +{ + struct die_args args = { + .regs = regs, + .str = str, + .addr = addr, + .fsr = fsr + }; + + return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args); +} +#else +static inline int notify_page_fault(enum die_val val, const char *str, + unsigned long addr, + struct pt_regs *regs, + unsigned int fsr) +{ + return NOTIFY_DONE; +} +#endif + /* * Oops. The kernel tried to access some page that wasn't present. */ @@ -215,7 +255,7 @@ out: return fault; } -static int +static int __kprobes do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { struct task_struct *tsk; @@ -225,6 +265,10 @@ do_page_fault(unsigned long addr, unsign tsk = current; mm = tsk->mm; + if (notify_page_fault(DIE_PAGE_FAULT, "page fault", addr, regs, + fsr) == NOTIFY_STOP) + return 0; + /* * If we're in an interrupt or have no user * context, we must not take the fault.. @@ -315,7 +359,7 @@ no_context: * interrupt or a critical region, and should only copy the information * from the master page table, nothing more. */ -static int +static int __kprobes do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { @@ -441,7 +485,7 @@ hook_fault_code(int nr, int (*fn)(unsign /* * Dispatch a data abort to the relevant handler. */ -asmlinkage void +asmlinkage void __kprobes do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); @@ -460,7 +504,7 @@ do_DataAbort(unsigned long addr, unsigne notify_die("", regs, &info, fsr, 0); } -asmlinkage void +asmlinkage void __kprobes do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { do_translation_fault(addr, 0, regs); diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/include/asm-arm/kdebug.h kernel-source-rx-34-2.6.18/include/asm-arm/kdebug.h --- kernel-source-rx-34-2.6.18.default/include/asm-arm/kdebug.h 1970-01-01 07:30:00.000000000 +0730 +++ kernel-source-rx-34-2.6.18/include/asm-arm/kdebug.h 2007-07-07 13:27:13.000000000 +0800 @@ -0,0 +1,35 @@ +#ifndef _ARM_KDEBUG_H +#define _ARM_KDEBUG_H + +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + unsigned long addr; + unsigned int fsr; +}; + +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); + +extern void notify_die(const char *str, struct pt_regs *regs, + struct siginfo *info, unsigned long err, + unsigned long trap); + +enum die_val { + DIE_TRANS_FAULT = 1, + DIE_PAGE_FAULT +}; + + +/* Die notifier kernel feature only used by kprobes. We don't use it + * for kprobes on ARM. Disable it with this dummy function. */ +static inline int register_die_notifier(struct notifier_block *nb) +{ + return 0; +} + +#endif /* _ARM_KDEBUG_H */ diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/include/asm-arm/kprobes.h kernel-source-rx-34-2.6.18/include/asm-arm/kprobes.h --- kernel-source-rx-34-2.6.18.default/include/asm-arm/kprobes.h 1970-01-01 07:30:00.000000000 +0730 +++ kernel-source-rx-34-2.6.18/include/asm-arm/kprobes.h 2007-07-07 13:27:13.000000000 +0800 @@ -0,0 +1,84 @@ +#ifndef _ARM_KPROBES_H +#define _ARM_KPROBES_H + +/* include/asm-arm/kprobes.h + * + * 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 +#include +#include +#include +#include + +#define ARCH_SUPPORTS_KRETPROBES +#define __ARCH_WANT_KPROBES_INSN_SLOT +#define MAX_INSN_SIZE 2 +#define ARCH_INACTIVE_KPROBE_COUNT 0 + +#define flush_insn_slot(p) do { } while (0) + +#define MAX_STACK_SIZE 64 /* Probably 32 should suffice */ +#define MIN_STACK_SIZE(addr) \ + min( (unsigned long)MAX_STACK_SIZE, \ + (unsigned long)current_thread_info() + \ + THREAD_START_SP - (addr) ) + +/* This undefined instruction must be unique and + * reserved solely for kprobes' use. */ +#define KPROBE_BREAKPOINT_INSTRUCTION 0xe7f001f8 + +#define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */ + +#define JPROBE_ENTRY(pentry) ((kprobe_opcode_t *)(pentry)) + +enum kprobe_insn { + INSN_REJECTED, + INSN_GOOD, + INSN_GOOD_NO_SLOT +}; + +typedef u32 kprobe_opcode_t; + +struct kprobe; +typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *); + +/* Architecture specific copy of original instruction. */ +struct arch_specific_insn { + kprobe_opcode_t *insn; + kprobe_insn_handler_t *insn_handler; +}; + +struct prev_kprobe { + struct kprobe *kp; + unsigned int status; +}; + + +/* per-cpu kprobe control block */ +struct kprobe_ctlblk { + unsigned int kprobe_status; + struct prev_kprobe prev_kprobe; + struct pt_regs jprobe_saved_regs; + char jprobes_stack[MAX_STACK_SIZE]; +}; + +extern void arch_flush_insn_slot(struct kprobe *); +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); +extern void arch_remove_kprobe(struct kprobe *); +extern enum kprobe_insn kprobe_decode_insn(kprobe_opcode_t, + struct arch_specific_insn *); +extern int kprobe_handler(struct pt_regs *regs); + +#endif /* _ARM_KPROBES_H */ diff -uprN -X kernel-source-rx-34-2.6.18.default/Documentation/dontdiff kernel-source-rx-34-2.6.18.default/include/asm-arm/ptrace.h kernel-source-rx-34-2.6.18/include/asm-arm/ptrace.h --- kernel-source-rx-34-2.6.18.default/include/asm-arm/ptrace.h 2006-11-08 20:19:11.000000000 +0800 +++ kernel-source-rx-34-2.6.18/include/asm-arm/ptrace.h 2007-07-07 13:27:13.000000000 +0800 @@ -144,6 +144,8 @@ static inline int valid_user_regs(struct #define instruction_pointer(regs) \ (pc_pointer((regs)->ARM_pc)) +#define regs_return_value(regs) ((regs)->ARM_r0) + #ifdef CONFIG_SMP extern unsigned long profile_pc(struct pt_regs *regs); #else