ARM: probes: move all probe code to dedicate directory
In discussion on LKML (https://lkml.org/lkml/2014/11/28/158), Russell King suggests to move all probe related code to arch/arm/probes. This patch does the work. Due to dependency on 'arch/arm/kernel/patch.h', this patch also moves patch.h to 'arch/arm/include/asm/patch.h', and related '#include' directives are also midified to '#include <asm/patch.h>'. Following is an overview of this patch: ./arch/arm/kernel/ ./arch/arm/probes/ |-- Makefile |-- Makefile |-- probes-arm.c ==> |-- decode-arm.c |-- probes-arm.h ==> |-- decode-arm.h |-- probes-thumb.c ==> |-- decode-thumb.c |-- probes-thumb.h ==> |-- decode-thumb.h |-- probes.c ==> |-- decode.c |-- probes.h ==> |-- decode.h | |-- kprobes | | |-- Makefile |-- kprobes-arm.c ==> | |-- actions-arm.c |-- kprobes-common.c ==> | |-- actions-common.c |-- kprobes-thumb.c ==> | |-- actions-thumb.c |-- kprobes.c ==> | |-- core.c |-- kprobes.h ==> | |-- core.h |-- kprobes-test-arm.c ==> | |-- test-arm.c |-- kprobes-test.c ==> | |-- test-core.c |-- kprobes-test.h ==> | |-- test-core.h |-- kprobes-test-thumb.c ==> | `-- test-thumb.c | `-- uprobes | |-- Makefile |-- uprobes-arm.c ==> |-- actions-arm.c |-- uprobes.c ==> |-- core.c |-- uprobes.h ==> `-- core.h | `-- patch.h ==> arch/arm/include/asm/patch.h Signed-off-by: Wang Nan <wangnan0@huawei.com> Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Jon Medhurst <tixy@linaro.org>
This commit is contained in:
@@ -51,20 +51,8 @@ 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_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o
|
||||
obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o
|
||||
else
|
||||
obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o
|
||||
endif
|
||||
obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
|
||||
test-kprobes-objs := kprobes-test.o
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
test-kprobes-objs += kprobes-test-thumb.o
|
||||
else
|
||||
test-kprobes-objs += kprobes-test-arm.o
|
||||
endif
|
||||
# Main staffs in KPROBES are in arch/arm/probes/ .
|
||||
obj-$(CONFIG_KPROBES) += patch.o
|
||||
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
|
||||
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
|
||||
obj-$(CONFIG_KGDB) += kgdb.o patch.o
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/patch.h>
|
||||
|
||||
#include "insn.h"
|
||||
#include "patch.h"
|
||||
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
|
||||
|
@@ -14,10 +14,9 @@
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/patch.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "patch.h"
|
||||
|
||||
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||
{
|
||||
{ "r0", 4, offsetof(struct pt_regs, ARM_r0)},
|
||||
|
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* *) Otherwise, a modified form of the instruction is
|
||||
* 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 simplify processing, the
|
||||
* undefined instruction used for kprobes must be reserved
|
||||
* exclusively for kprobes use.
|
||||
*
|
||||
* TODO: ifdef out some instruction decoding based on architecture.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-arm.h"
|
||||
|
||||
#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
|
||||
|
||||
static void __kprobes
|
||||
emulate_ldrdstrd(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 4;
|
||||
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" (asi->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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 4;
|
||||
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" (asi->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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long rtpc = regs->ARM_pc - 4 + str_pc_offset;
|
||||
unsigned long rnpc = regs->ARM_pc + 4;
|
||||
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" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
if (is_writeback(insn))
|
||||
regs->uregs[rn] = rnv;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
emulate_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 4;
|
||||
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" (asi->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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
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" (asi->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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
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" (asi->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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
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" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rd] = rdv;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
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" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rdlo] = rdlov;
|
||||
regs->uregs[rdhi] = rdhiv;
|
||||
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
|
||||
}
|
||||
|
||||
const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
|
||||
[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
|
||||
[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
|
||||
[PROBES_MRS] = {.handler = simulate_mrs},
|
||||
[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
|
||||
[PROBES_CLZ] = {.handler = emulate_rd12rm0_noflags_nopc},
|
||||
[PROBES_SATURATING_ARITHMETIC] = {
|
||||
.handler = emulate_rd12rn16rm0_rwflags_nopc},
|
||||
[PROBES_MUL1] = {.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
|
||||
[PROBES_MUL2] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
|
||||
[PROBES_SWP] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
|
||||
[PROBES_LDRSTRD] = {.handler = emulate_ldrdstrd},
|
||||
[PROBES_LOAD_EXTRA] = {.handler = emulate_ldr},
|
||||
[PROBES_LOAD] = {.handler = emulate_ldr},
|
||||
[PROBES_STORE_EXTRA] = {.handler = emulate_str},
|
||||
[PROBES_STORE] = {.handler = emulate_str},
|
||||
[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
|
||||
[PROBES_DATA_PROCESSING_REG] = {
|
||||
.handler = emulate_rd12rn16rm0rs8_rwflags},
|
||||
[PROBES_DATA_PROCESSING_IMM] = {
|
||||
.handler = emulate_rd12rn16rm0rs8_rwflags},
|
||||
[PROBES_MOV_HALFWORD] = {.handler = emulate_rd12rm0_noflags_nopc},
|
||||
[PROBES_SEV] = {.handler = probes_emulate_none},
|
||||
[PROBES_WFE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SATURATE] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
|
||||
[PROBES_REV] = {.handler = emulate_rd12rm0_noflags_nopc},
|
||||
[PROBES_MMI] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
|
||||
[PROBES_PACK] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
|
||||
[PROBES_EXTEND] = {.handler = emulate_rd12rm0_noflags_nopc},
|
||||
[PROBES_EXTEND_ADD] = {.handler = emulate_rd12rn16rm0_rwflags_nopc},
|
||||
[PROBES_MUL_ADD_LONG] = {
|
||||
.handler = emulate_rdlo12rdhi16rn0rm8_rwflags_nopc},
|
||||
[PROBES_MUL_ADD] = {.handler = emulate_rd16rn12rm0rs8_rwflags_nopc},
|
||||
[PROBES_BITFIELD] = {.handler = emulate_rd12rm0_noflags_nopc},
|
||||
[PROBES_BRANCH] = {.handler = simulate_bbl},
|
||||
[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
|
||||
};
|
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-common.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@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 <asm/opcodes.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
|
||||
|
||||
static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int lbit = insn & (1 << 20);
|
||||
int wbit = insn & (1 << 21);
|
||||
int ubit = insn & (1 << 23);
|
||||
int pbit = insn & (1 << 24);
|
||||
long *addr = (long *)regs->uregs[rn];
|
||||
int reg_bit_vector;
|
||||
int reg_count;
|
||||
|
||||
reg_count = 0;
|
||||
reg_bit_vector = insn & 0xffff;
|
||||
while (reg_bit_vector) {
|
||||
reg_bit_vector &= (reg_bit_vector - 1);
|
||||
++reg_count;
|
||||
}
|
||||
|
||||
if (!ubit)
|
||||
addr -= reg_count;
|
||||
addr += (!pbit == !ubit);
|
||||
|
||||
reg_bit_vector = insn & 0xffff;
|
||||
while (reg_bit_vector) {
|
||||
int reg = __ffs(reg_bit_vector);
|
||||
reg_bit_vector &= (reg_bit_vector - 1);
|
||||
if (lbit)
|
||||
regs->uregs[reg] = *addr++;
|
||||
else
|
||||
*addr++ = regs->uregs[reg];
|
||||
}
|
||||
|
||||
if (wbit) {
|
||||
if (!ubit)
|
||||
addr -= reg_count;
|
||||
addr -= (!pbit == !ubit);
|
||||
regs->uregs[rn] = (long)addr;
|
||||
}
|
||||
}
|
||||
|
||||
static void __kprobes simulate_stm1_pc(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr = regs->ARM_pc - 4;
|
||||
|
||||
regs->ARM_pc = (long)addr + str_pc_offset;
|
||||
simulate_ldm1stm1(insn, asi, regs);
|
||||
regs->ARM_pc = (long)addr + 4;
|
||||
}
|
||||
|
||||
static void __kprobes simulate_ldm1_pc(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
simulate_ldm1stm1(insn, asi, regs);
|
||||
load_write_pc(regs->ARM_pc, regs);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
emulate_generic_r0_12_noflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
register void *rregs asm("r1") = regs;
|
||||
register void *rfn asm("lr") = asi->insn_fn;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"stmdb sp!, {%[regs], r11} \n\t"
|
||||
"ldmia %[regs], {r0-r12} \n\t"
|
||||
#if __LINUX_ARM_ARCH__ >= 6
|
||||
"blx %[fn] \n\t"
|
||||
#else
|
||||
"str %[fn], [sp, #-4]! \n\t"
|
||||
"adr lr, 1f \n\t"
|
||||
"ldr pc, [sp], #4 \n\t"
|
||||
"1: \n\t"
|
||||
#endif
|
||||
"ldr lr, [sp], #4 \n\t" /* lr = regs */
|
||||
"stmia lr, {r0-r12} \n\t"
|
||||
"ldr r11, [sp], #4 \n\t"
|
||||
: [regs] "=r" (rregs), [fn] "=r" (rfn)
|
||||
: "0" (rregs), "1" (rfn)
|
||||
: "r0", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r12", "memory", "cc"
|
||||
);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
emulate_generic_r2_14_noflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
emulate_generic_r0_12_noflags(insn, asi,
|
||||
(struct pt_regs *)(regs->uregs+2));
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
emulate_ldm_r3_15(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
emulate_generic_r0_12_noflags(insn, asi,
|
||||
(struct pt_regs *)(regs->uregs+3));
|
||||
load_write_pc(regs->ARM_pc, regs);
|
||||
}
|
||||
|
||||
enum probes_insn __kprobes
|
||||
kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
probes_insn_handler_t *handler = 0;
|
||||
unsigned reglist = insn & 0xffff;
|
||||
int is_ldm = insn & 0x100000;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
|
||||
if (rn <= 12 && (reglist & 0xe000) == 0) {
|
||||
/* Instruction only uses registers in the range R0..R12 */
|
||||
handler = emulate_generic_r0_12_noflags;
|
||||
|
||||
} else if (rn >= 2 && (reglist & 0x8003) == 0) {
|
||||
/* Instruction only uses registers in the range R2..R14 */
|
||||
rn -= 2;
|
||||
reglist >>= 2;
|
||||
handler = emulate_generic_r2_14_noflags;
|
||||
|
||||
} else if (rn >= 3 && (reglist & 0x0007) == 0) {
|
||||
/* Instruction only uses registers in the range R3..R15 */
|
||||
if (is_ldm && (reglist & 0x8000)) {
|
||||
rn -= 3;
|
||||
reglist >>= 3;
|
||||
handler = emulate_ldm_r3_15;
|
||||
}
|
||||
}
|
||||
|
||||
if (handler) {
|
||||
/* We can emulate the instruction in (possibly) modified form */
|
||||
asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
|
||||
(rn << 16) | reglist);
|
||||
asi->insn_handler = handler;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
/* Fallback to slower simulation... */
|
||||
if (reglist & 0x8000)
|
||||
handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc;
|
||||
else
|
||||
handler = simulate_ldm1stm1;
|
||||
asi->insn_handler = handler;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,435 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-test.h
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define VERBOSE 0 /* Set to '1' for more logging of test cases */
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
#define NORMAL_ISA "16"
|
||||
#else
|
||||
#define NORMAL_ISA "32"
|
||||
#endif
|
||||
|
||||
|
||||
/* Flags used in kprobe_test_flags */
|
||||
#define TEST_FLAG_NO_ITBLOCK (1<<0)
|
||||
#define TEST_FLAG_FULL_ITBLOCK (1<<1)
|
||||
#define TEST_FLAG_NARROW_INSTR (1<<2)
|
||||
|
||||
extern int kprobe_test_flags;
|
||||
extern int kprobe_test_cc_position;
|
||||
|
||||
|
||||
#define TEST_MEMORY_SIZE 256
|
||||
|
||||
|
||||
/*
|
||||
* Test case structures.
|
||||
*
|
||||
* The arguments given to test cases can be one of three types.
|
||||
*
|
||||
* ARG_TYPE_REG
|
||||
* Load a register with the given value.
|
||||
*
|
||||
* ARG_TYPE_PTR
|
||||
* Load a register with a pointer into the stack buffer (SP + given value).
|
||||
*
|
||||
* ARG_TYPE_MEM
|
||||
* Store the given value into the stack buffer at [SP+index].
|
||||
*
|
||||
*/
|
||||
|
||||
#define ARG_TYPE_END 0
|
||||
#define ARG_TYPE_REG 1
|
||||
#define ARG_TYPE_PTR 2
|
||||
#define ARG_TYPE_MEM 3
|
||||
|
||||
#define ARG_FLAG_UNSUPPORTED 0x01
|
||||
#define ARG_FLAG_SUPPORTED 0x02
|
||||
#define ARG_FLAG_THUMB 0x10 /* Must be 16 so TEST_ISA can be used */
|
||||
#define ARG_FLAG_ARM 0x20 /* Must be 32 so TEST_ISA can be used */
|
||||
|
||||
struct test_arg {
|
||||
u8 type; /* ARG_TYPE_x */
|
||||
u8 _padding[7];
|
||||
};
|
||||
|
||||
struct test_arg_regptr {
|
||||
u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */
|
||||
u8 reg;
|
||||
u8 _padding[2];
|
||||
u32 val;
|
||||
};
|
||||
|
||||
struct test_arg_mem {
|
||||
u8 type; /* ARG_TYPE_MEM */
|
||||
u8 index;
|
||||
u8 _padding[2];
|
||||
u32 val;
|
||||
};
|
||||
|
||||
struct test_arg_end {
|
||||
u8 type; /* ARG_TYPE_END */
|
||||
u8 flags; /* ARG_FLAG_x */
|
||||
u16 code_offset;
|
||||
u16 branch_offset;
|
||||
u16 end_offset;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Building blocks for test cases.
|
||||
*
|
||||
* Each test case is wrapped between TESTCASE_START and TESTCASE_END.
|
||||
*
|
||||
* To specify arguments for a test case the TEST_ARG_{REG,PTR,MEM} macros are
|
||||
* used followed by a terminating TEST_ARG_END.
|
||||
*
|
||||
* After this, the instruction to be tested is defined with TEST_INSTRUCTION.
|
||||
* Or for branches, TEST_BRANCH_B and TEST_BRANCH_F (branch forwards/backwards).
|
||||
*
|
||||
* Some specific test cases may make use of other custom constructs.
|
||||
*/
|
||||
|
||||
#if VERBOSE
|
||||
#define verbose(fmt, ...) pr_info(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define verbose(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define TEST_GROUP(title) \
|
||||
verbose("\n"); \
|
||||
verbose(title"\n"); \
|
||||
verbose("---------------------------------------------------------\n");
|
||||
|
||||
#define TESTCASE_START(title) \
|
||||
__asm__ __volatile__ ( \
|
||||
"bl __kprobes_test_case_start \n\t" \
|
||||
".pushsection .rodata \n\t" \
|
||||
"10: \n\t" \
|
||||
/* don't use .asciz here as 'title' may be */ \
|
||||
/* multiple strings to be concatenated. */ \
|
||||
".ascii "#title" \n\t" \
|
||||
".byte 0 \n\t" \
|
||||
".popsection \n\t" \
|
||||
".word 10b \n\t"
|
||||
|
||||
#define TEST_ARG_REG(reg, val) \
|
||||
".byte "__stringify(ARG_TYPE_REG)" \n\t" \
|
||||
".byte "#reg" \n\t" \
|
||||
".short 0 \n\t" \
|
||||
".word "#val" \n\t"
|
||||
|
||||
#define TEST_ARG_PTR(reg, val) \
|
||||
".byte "__stringify(ARG_TYPE_PTR)" \n\t" \
|
||||
".byte "#reg" \n\t" \
|
||||
".short 0 \n\t" \
|
||||
".word "#val" \n\t"
|
||||
|
||||
#define TEST_ARG_MEM(index, val) \
|
||||
".byte "__stringify(ARG_TYPE_MEM)" \n\t" \
|
||||
".byte "#index" \n\t" \
|
||||
".short 0 \n\t" \
|
||||
".word "#val" \n\t"
|
||||
|
||||
#define TEST_ARG_END(flags) \
|
||||
".byte "__stringify(ARG_TYPE_END)" \n\t" \
|
||||
".byte "TEST_ISA flags" \n\t" \
|
||||
".short 50f-0f \n\t" \
|
||||
".short 2f-0f \n\t" \
|
||||
".short 99f-0f \n\t" \
|
||||
".code "TEST_ISA" \n\t" \
|
||||
"0: \n\t"
|
||||
|
||||
#define TEST_INSTRUCTION(instruction) \
|
||||
"50: nop \n\t" \
|
||||
"1: "instruction" \n\t" \
|
||||
" nop \n\t"
|
||||
|
||||
#define TEST_BRANCH_F(instruction) \
|
||||
TEST_INSTRUCTION(instruction) \
|
||||
" b 99f \n\t" \
|
||||
"2: nop \n\t"
|
||||
|
||||
#define TEST_BRANCH_B(instruction) \
|
||||
" b 50f \n\t" \
|
||||
" b 99f \n\t" \
|
||||
"2: nop \n\t" \
|
||||
" b 99f \n\t" \
|
||||
TEST_INSTRUCTION(instruction)
|
||||
|
||||
#define TEST_BRANCH_FX(instruction, codex) \
|
||||
TEST_INSTRUCTION(instruction) \
|
||||
" b 99f \n\t" \
|
||||
codex" \n\t" \
|
||||
" b 99f \n\t" \
|
||||
"2: nop \n\t"
|
||||
|
||||
#define TEST_BRANCH_BX(instruction, codex) \
|
||||
" b 50f \n\t" \
|
||||
" b 99f \n\t" \
|
||||
"2: nop \n\t" \
|
||||
" b 99f \n\t" \
|
||||
codex" \n\t" \
|
||||
TEST_INSTRUCTION(instruction)
|
||||
|
||||
#define TESTCASE_END \
|
||||
"2: \n\t" \
|
||||
"99: \n\t" \
|
||||
" bl __kprobes_test_case_end_"TEST_ISA" \n\t" \
|
||||
".code "NORMAL_ISA" \n\t" \
|
||||
: : \
|
||||
: "r0", "r1", "r2", "r3", "ip", "lr", "memory", "cc" \
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Macros to define test cases.
|
||||
*
|
||||
* Those of the form TEST_{R,P,M}* can be used to define test cases
|
||||
* which take combinations of the three basic types of arguments. E.g.
|
||||
*
|
||||
* TEST_R One register argument
|
||||
* TEST_RR Two register arguments
|
||||
* TEST_RPR A register, a pointer, then a register argument
|
||||
*
|
||||
* For testing instructions which may branch, there are macros TEST_BF_*
|
||||
* and TEST_BB_* for branching forwards and backwards.
|
||||
*
|
||||
* TEST_SUPPORTED and TEST_UNSUPPORTED don't cause the code to be executed,
|
||||
* the just verify that a kprobe is or is not allowed on the given instruction.
|
||||
*/
|
||||
|
||||
#define TEST(code) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_UNSUPPORTED(code) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("|"__stringify(ARG_FLAG_UNSUPPORTED)) \
|
||||
TEST_INSTRUCTION(code) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_SUPPORTED(code) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("|"__stringify(ARG_FLAG_SUPPORTED)) \
|
||||
TEST_INSTRUCTION(code) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_R(code1, reg, val, code2) \
|
||||
TESTCASE_START(code1 #reg code2) \
|
||||
TEST_ARG_REG(reg, val) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg code2) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RR(code1, reg1, val1, code2, reg2, val2, code3) \
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_REG(reg3, val3) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RRRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4, reg4, val4) \
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_REG(reg3, val3) \
|
||||
TEST_ARG_REG(reg4, val4) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4 #reg4) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_P(code1, reg1, val1, code2) \
|
||||
TESTCASE_START(code1 #reg1 code2) \
|
||||
TEST_ARG_PTR(reg1, val1) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_PR(code1, reg1, val1, code2, reg2, val2, code3) \
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3) \
|
||||
TEST_ARG_PTR(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RP(code1, reg1, val1, code2, reg2, val2, code3) \
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_PTR(reg2, val2) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_PRR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TEST_ARG_PTR(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_REG(reg3, val3) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RPR(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_PTR(reg2, val2) \
|
||||
TEST_ARG_REG(reg3, val3) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RRP(code1, reg1, val1, code2, reg2, val2, code3, reg3, val3, code4)\
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_PTR(reg3, val3) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg1 code2 #reg2 code3 #reg3 code4) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BF_P(code1, reg1, val1, code2) \
|
||||
TESTCASE_START(code1 #reg1 code2) \
|
||||
TEST_ARG_PTR(reg1, val1) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_F(code1 #reg1 code2) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BF(code) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_F(code) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BB(code) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_B(code) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BF_R(code1, reg, val, code2) \
|
||||
TESTCASE_START(code1 #reg code2) \
|
||||
TEST_ARG_REG(reg, val) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_F(code1 #reg code2) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BB_R(code1, reg, val, code2) \
|
||||
TESTCASE_START(code1 #reg code2) \
|
||||
TEST_ARG_REG(reg, val) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_B(code1 #reg code2) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BF_RR(code1, reg1, val1, code2, reg2, val2, code3) \
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_F(code1 #reg1 code2 #reg2 code3) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BF_X(code, codex) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_FX(code, codex) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BB_X(code, codex) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_BX(code, codex) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_BF_RX(code1, reg, val, code2, codex) \
|
||||
TESTCASE_START(code1 #reg code2) \
|
||||
TEST_ARG_REG(reg, val) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_BRANCH_FX(code1 #reg code2, codex) \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_X(code, codex) \
|
||||
TESTCASE_START(code) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code) \
|
||||
" b 99f \n\t" \
|
||||
" "codex" \n\t" \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RX(code1, reg, val, code2, codex) \
|
||||
TESTCASE_START(code1 #reg code2) \
|
||||
TEST_ARG_REG(reg, val) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 __stringify(reg) code2) \
|
||||
" b 99f \n\t" \
|
||||
" "codex" \n\t" \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RRX(code1, reg1, val1, code2, reg2, val2, code3, codex) \
|
||||
TESTCASE_START(code1 #reg1 code2 #reg2 code3) \
|
||||
TEST_ARG_REG(reg1, val1) \
|
||||
TEST_ARG_REG(reg2, val2) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 __stringify(reg1) code2 __stringify(reg2) code3) \
|
||||
" b 99f \n\t" \
|
||||
" "codex" \n\t" \
|
||||
TESTCASE_END
|
||||
|
||||
|
||||
/*
|
||||
* Macros for defining space directives spread over multiple lines.
|
||||
* These are required so the compiler guesses better the length of inline asm
|
||||
* code and will spill the literal pool early enough to avoid generating PC
|
||||
* relative loads with out of range offsets.
|
||||
*/
|
||||
#define TWICE(x) x x
|
||||
#define SPACE_0x8 TWICE(".space 4\n\t")
|
||||
#define SPACE_0x10 TWICE(SPACE_0x8)
|
||||
#define SPACE_0x20 TWICE(SPACE_0x10)
|
||||
#define SPACE_0x40 TWICE(SPACE_0x20)
|
||||
#define SPACE_0x80 TWICE(SPACE_0x40)
|
||||
#define SPACE_0x100 TWICE(SPACE_0x80)
|
||||
#define SPACE_0x200 TWICE(SPACE_0x100)
|
||||
#define SPACE_0x400 TWICE(SPACE_0x200)
|
||||
#define SPACE_0x800 TWICE(SPACE_0x400)
|
||||
#define SPACE_0x1000 TWICE(SPACE_0x800)
|
||||
|
||||
|
||||
/* Various values used in test cases... */
|
||||
#define N(val) (val ^ 0xffffffff)
|
||||
#define VAL1 0x12345678
|
||||
#define VAL2 N(VAL1)
|
||||
#define VAL3 0xa5f801
|
||||
#define VAL4 N(VAL3)
|
||||
#define VALM 0x456789ab
|
||||
#define VALR 0xdeaddead
|
||||
#define HH1 0x0123fecb
|
||||
#define HH2 0xa9874567
|
||||
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
void kprobe_thumb16_test_cases(void);
|
||||
void kprobe_thumb32_test_cases(void);
|
||||
#else
|
||||
void kprobe_arm_test_cases(void);
|
||||
#endif
|
@@ -1,666 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-thumb.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-thumb.h"
|
||||
|
||||
/* These emulation encodings are functionally equivalent... */
|
||||
#define t32_emulate_rd8rn16rm0ra12_noflags \
|
||||
t32_emulate_rdlo12rdhi8rn16rm0_noflags
|
||||
|
||||
/* t32 thumb actions */
|
||||
|
||||
static void __kprobes
|
||||
t32_simulate_table_branch(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int rm = insn & 0xf;
|
||||
|
||||
unsigned long rnv = (rn == 15) ? pc : regs->uregs[rn];
|
||||
unsigned long rmv = regs->uregs[rm];
|
||||
unsigned int halfwords;
|
||||
|
||||
if (insn & 0x10) /* TBH */
|
||||
halfwords = ((u16 *)rnv)[rmv];
|
||||
else /* TBB */
|
||||
halfwords = ((u8 *)rnv)[rmv];
|
||||
|
||||
regs->ARM_pc = pc + 2 * halfwords;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_simulate_mrs(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
int rd = (insn >> 8) & 0xf;
|
||||
unsigned long mask = 0xf8ff03df; /* Mask out execution state */
|
||||
regs->uregs[rd] = regs->ARM_cpsr & mask;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_simulate_cond_branch(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc;
|
||||
|
||||
long offset = insn & 0x7ff; /* imm11 */
|
||||
offset += (insn & 0x003f0000) >> 5; /* imm6 */
|
||||
offset += (insn & 0x00002000) << 4; /* J1 */
|
||||
offset += (insn & 0x00000800) << 7; /* J2 */
|
||||
offset -= (insn & 0x04000000) >> 7; /* Apply sign bit */
|
||||
|
||||
regs->ARM_pc = pc + (offset * 2);
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t32_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
int cc = (insn >> 22) & 0xf;
|
||||
asi->insn_check_cc = probes_condition_checks[cc];
|
||||
asi->insn_handler = t32_simulate_cond_branch;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_simulate_branch(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc;
|
||||
|
||||
long offset = insn & 0x7ff; /* imm11 */
|
||||
offset += (insn & 0x03ff0000) >> 5; /* imm10 */
|
||||
offset += (insn & 0x00002000) << 9; /* J1 */
|
||||
offset += (insn & 0x00000800) << 10; /* J2 */
|
||||
if (insn & 0x04000000)
|
||||
offset -= 0x00800000; /* Apply sign bit */
|
||||
else
|
||||
offset ^= 0x00600000; /* Invert J1 and J2 */
|
||||
|
||||
if (insn & (1 << 14)) {
|
||||
/* BL or BLX */
|
||||
regs->ARM_lr = regs->ARM_pc | 1;
|
||||
if (!(insn & (1 << 12))) {
|
||||
/* BLX so switch to ARM mode */
|
||||
regs->ARM_cpsr &= ~PSR_T_BIT;
|
||||
pc &= ~3;
|
||||
}
|
||||
}
|
||||
|
||||
regs->ARM_pc = pc + (offset * 2);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_simulate_ldr_literal(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr = regs->ARM_pc & ~3;
|
||||
int rt = (insn >> 12) & 0xf;
|
||||
unsigned long rtv;
|
||||
|
||||
long offset = insn & 0xfff;
|
||||
if (insn & 0x00800000)
|
||||
addr += offset;
|
||||
else
|
||||
addr -= offset;
|
||||
|
||||
if (insn & 0x00400000) {
|
||||
/* LDR */
|
||||
rtv = *(unsigned long *)addr;
|
||||
if (rt == 15) {
|
||||
bx_write_pc(rtv, regs);
|
||||
return;
|
||||
}
|
||||
} else if (insn & 0x00200000) {
|
||||
/* LDRH */
|
||||
if (insn & 0x01000000)
|
||||
rtv = *(s16 *)addr;
|
||||
else
|
||||
rtv = *(u16 *)addr;
|
||||
} else {
|
||||
/* LDRB */
|
||||
if (insn & 0x01000000)
|
||||
rtv = *(s8 *)addr;
|
||||
else
|
||||
rtv = *(u8 *)addr;
|
||||
}
|
||||
|
||||
regs->uregs[rt] = rtv;
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d);
|
||||
|
||||
/* Fixup modified instruction to have halfwords in correct order...*/
|
||||
insn = __mem_to_opcode_arm(asi->insn[0]);
|
||||
((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16);
|
||||
((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_emulate_ldrdstrd(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc & ~3;
|
||||
int rt1 = (insn >> 12) & 0xf;
|
||||
int rt2 = (insn >> 8) & 0xf;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
|
||||
register unsigned long rt1v asm("r0") = regs->uregs[rt1];
|
||||
register unsigned long rt2v asm("r1") = regs->uregs[rt2];
|
||||
register unsigned long rnv asm("r2") = (rn == 15) ? pc
|
||||
: regs->uregs[rn];
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"blx %[fn]"
|
||||
: "=r" (rt1v), "=r" (rt2v), "=r" (rnv)
|
||||
: "0" (rt1v), "1" (rt2v), "2" (rnv), [fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
if (rn != 15)
|
||||
regs->uregs[rn] = rnv; /* Writeback base register */
|
||||
regs->uregs[rt1] = rt1v;
|
||||
regs->uregs[rt2] = rt2v;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_emulate_ldrstr(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
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 rnv asm("r2") = regs->uregs[rn];
|
||||
register unsigned long rmv asm("r3") = regs->uregs[rm];
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"blx %[fn]"
|
||||
: "=r" (rtv), "=r" (rnv)
|
||||
: "0" (rtv), "1" (rnv), "r" (rmv), [fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rn] = rnv; /* Writeback base register */
|
||||
if (rt == 15) /* Can't be true for a STR as they aren't allowed */
|
||||
bx_write_pc(rtv, regs);
|
||||
else
|
||||
regs->uregs[rt] = rtv;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_emulate_rd8rn16rm0_rwflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
int rd = (insn >> 8) & 0xf;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int rm = insn & 0xf;
|
||||
|
||||
register unsigned long rdv asm("r1") = 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] \n\t"
|
||||
"mrs %[cpsr], cpsr \n\t"
|
||||
: "=r" (rdv), [cpsr] "=r" (cpsr)
|
||||
: "0" (rdv), "r" (rnv), "r" (rmv),
|
||||
"1" (cpsr), [fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rd] = rdv;
|
||||
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_emulate_rd8pc16_noflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc;
|
||||
int rd = (insn >> 8) & 0xf;
|
||||
|
||||
register unsigned long rdv asm("r1") = regs->uregs[rd];
|
||||
register unsigned long rnv asm("r2") = pc & ~3;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"blx %[fn]"
|
||||
: "=r" (rdv)
|
||||
: "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rd] = rdv;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_emulate_rd8rn16_noflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
int rd = (insn >> 8) & 0xf;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
|
||||
register unsigned long rdv asm("r1") = regs->uregs[rd];
|
||||
register unsigned long rnv asm("r2") = regs->uregs[rn];
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"blx %[fn]"
|
||||
: "=r" (rdv)
|
||||
: "0" (rdv), "r" (rnv), [fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rd] = rdv;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t32_emulate_rdlo12rdhi8rn16rm0_noflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
int rdlo = (insn >> 12) & 0xf;
|
||||
int rdhi = (insn >> 8) & 0xf;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int rm = insn & 0xf;
|
||||
|
||||
register unsigned long rdlov asm("r0") = regs->uregs[rdlo];
|
||||
register unsigned long rdhiv asm("r1") = regs->uregs[rdhi];
|
||||
register unsigned long rnv asm("r2") = regs->uregs[rn];
|
||||
register unsigned long rmv asm("r3") = regs->uregs[rm];
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"blx %[fn]"
|
||||
: "=r" (rdlov), "=r" (rdhiv)
|
||||
: "0" (rdlov), "1" (rdhiv), "r" (rnv), "r" (rmv),
|
||||
[fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
regs->uregs[rdlo] = rdlov;
|
||||
regs->uregs[rdhi] = rdhiv;
|
||||
}
|
||||
/* t16 thumb actions */
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_bxblx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 2;
|
||||
int rm = (insn >> 3) & 0xf;
|
||||
unsigned long rmv = (rm == 15) ? pc : regs->uregs[rm];
|
||||
|
||||
if (insn & (1 << 7)) /* BLX ? */
|
||||
regs->ARM_lr = regs->ARM_pc | 1;
|
||||
|
||||
bx_write_pc(rmv, regs);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_ldr_literal(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long *base = (unsigned long *)((regs->ARM_pc + 2) & ~3);
|
||||
long index = insn & 0xff;
|
||||
int rt = (insn >> 8) & 0x7;
|
||||
regs->uregs[rt] = base[index];
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_ldrstr_sp_relative(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long* base = (unsigned long *)regs->ARM_sp;
|
||||
long index = insn & 0xff;
|
||||
int rt = (insn >> 8) & 0x7;
|
||||
if (insn & 0x800) /* LDR */
|
||||
regs->uregs[rt] = base[index];
|
||||
else /* STR */
|
||||
base[index] = regs->uregs[rt];
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_reladr(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long base = (insn & 0x800) ? regs->ARM_sp
|
||||
: ((regs->ARM_pc + 2) & ~3);
|
||||
long offset = insn & 0xff;
|
||||
int rt = (insn >> 8) & 0x7;
|
||||
regs->uregs[rt] = base + offset * 4;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_add_sp_imm(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
long imm = insn & 0x7f;
|
||||
if (insn & 0x80) /* SUB */
|
||||
regs->ARM_sp -= imm * 4;
|
||||
else /* ADD */
|
||||
regs->ARM_sp += imm * 4;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_cbz(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
int rn = insn & 0x7;
|
||||
probes_opcode_t nonzero = regs->uregs[rn] ? insn : ~insn;
|
||||
if (nonzero & 0x800) {
|
||||
long i = insn & 0x200;
|
||||
long imm5 = insn & 0xf8;
|
||||
unsigned long pc = regs->ARM_pc + 2;
|
||||
regs->ARM_pc = pc + (i >> 3) + (imm5 >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_it(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* The 8 IT state bits are split into two parts in CPSR:
|
||||
* ITSTATE<1:0> are in CPSR<26:25>
|
||||
* ITSTATE<7:2> are in CPSR<15:10>
|
||||
* The new IT state is in the lower byte of insn.
|
||||
*/
|
||||
unsigned long cpsr = regs->ARM_cpsr;
|
||||
cpsr &= ~PSR_IT_MASK;
|
||||
cpsr |= (insn & 0xfc) << 8;
|
||||
cpsr |= (insn & 0x03) << 25;
|
||||
regs->ARM_cpsr = cpsr;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_singlestep_it(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_pc += 2;
|
||||
t16_simulate_it(insn, asi, regs);
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t16_decode_it(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
asi->insn_singlestep = t16_singlestep_it;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_cond_branch(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 2;
|
||||
long offset = insn & 0x7f;
|
||||
offset -= insn & 0x80; /* Apply sign bit */
|
||||
regs->ARM_pc = pc + (offset * 2);
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t16_decode_cond_branch(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
int cc = (insn >> 8) & 0xf;
|
||||
asi->insn_check_cc = probes_condition_checks[cc];
|
||||
asi->insn_handler = t16_simulate_cond_branch;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_simulate_branch(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 2;
|
||||
long offset = insn & 0x3ff;
|
||||
offset -= insn & 0x400; /* Apply sign bit */
|
||||
regs->ARM_pc = pc + (offset * 2);
|
||||
}
|
||||
|
||||
static unsigned long __kprobes
|
||||
t16_emulate_loregs(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long oldcpsr = regs->ARM_cpsr;
|
||||
unsigned long newcpsr;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"msr cpsr_fs, %[oldcpsr] \n\t"
|
||||
"ldmia %[regs], {r0-r7} \n\t"
|
||||
"blx %[fn] \n\t"
|
||||
"stmia %[regs], {r0-r7} \n\t"
|
||||
"mrs %[newcpsr], cpsr \n\t"
|
||||
: [newcpsr] "=r" (newcpsr)
|
||||
: [oldcpsr] "r" (oldcpsr), [regs] "r" (regs),
|
||||
[fn] "r" (asi->insn_fn)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"lr", "memory", "cc"
|
||||
);
|
||||
|
||||
return (oldcpsr & ~APSR_MASK) | (newcpsr & APSR_MASK);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_emulate_loregs_rwflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_cpsr = t16_emulate_loregs(insn, asi, regs);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_emulate_loregs_noitrwflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long cpsr = t16_emulate_loregs(insn, asi, regs);
|
||||
if (!in_it_block(cpsr))
|
||||
regs->ARM_cpsr = cpsr;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_emulate_hiregs(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long pc = regs->ARM_pc + 2;
|
||||
int rdn = (insn & 0x7) | ((insn & 0x80) >> 4);
|
||||
int rm = (insn >> 3) & 0xf;
|
||||
|
||||
register unsigned long rdnv asm("r1");
|
||||
register unsigned long rmv asm("r0");
|
||||
unsigned long cpsr = regs->ARM_cpsr;
|
||||
|
||||
rdnv = (rdn == 15) ? pc : regs->uregs[rdn];
|
||||
rmv = (rm == 15) ? pc : regs->uregs[rm];
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"msr cpsr_fs, %[cpsr] \n\t"
|
||||
"blx %[fn] \n\t"
|
||||
"mrs %[cpsr], cpsr \n\t"
|
||||
: "=r" (rdnv), [cpsr] "=r" (cpsr)
|
||||
: "0" (rdnv), "r" (rmv), "1" (cpsr), [fn] "r" (asi->insn_fn)
|
||||
: "lr", "memory", "cc"
|
||||
);
|
||||
|
||||
if (rdn == 15)
|
||||
rdnv &= ~1;
|
||||
|
||||
regs->uregs[rdn] = rdnv;
|
||||
regs->ARM_cpsr = (regs->ARM_cpsr & ~APSR_MASK) | (cpsr & APSR_MASK);
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
insn &= ~0x00ff;
|
||||
insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
|
||||
((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn);
|
||||
asi->insn_handler = t16_emulate_hiregs;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_emulate_push(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"ldr r9, [%[regs], #13*4] \n\t"
|
||||
"ldr r8, [%[regs], #14*4] \n\t"
|
||||
"ldmia %[regs], {r0-r7} \n\t"
|
||||
"blx %[fn] \n\t"
|
||||
"str r9, [%[regs], #13*4] \n\t"
|
||||
:
|
||||
: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9",
|
||||
"lr", "memory", "cc"
|
||||
);
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
/*
|
||||
* To simulate a PUSH we use a Thumb-2 "STMDB R9!, {registers}"
|
||||
* and call it with R9=SP and LR in the register list represented
|
||||
* by R8.
|
||||
*/
|
||||
/* 1st half STMDB R9!,{} */
|
||||
((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929);
|
||||
/* 2nd half (register list) */
|
||||
((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
|
||||
asi->insn_handler = t16_emulate_push;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_emulate_pop_nopc(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"ldr r9, [%[regs], #13*4] \n\t"
|
||||
"ldmia %[regs], {r0-r7} \n\t"
|
||||
"blx %[fn] \n\t"
|
||||
"stmia %[regs], {r0-r7} \n\t"
|
||||
"str r9, [%[regs], #13*4] \n\t"
|
||||
:
|
||||
: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9",
|
||||
"lr", "memory", "cc"
|
||||
);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
t16_emulate_pop_pc(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
register unsigned long pc asm("r8");
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"ldr r9, [%[regs], #13*4] \n\t"
|
||||
"ldmia %[regs], {r0-r7} \n\t"
|
||||
"blx %[fn] \n\t"
|
||||
"stmia %[regs], {r0-r7} \n\t"
|
||||
"str r9, [%[regs], #13*4] \n\t"
|
||||
: "=r" (pc)
|
||||
: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r9",
|
||||
"lr", "memory", "cc"
|
||||
);
|
||||
|
||||
bx_write_pc(pc, regs);
|
||||
}
|
||||
|
||||
static enum probes_insn __kprobes
|
||||
t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
/*
|
||||
* To simulate a POP we use a Thumb-2 "LDMDB R9!, {registers}"
|
||||
* and call it with R9=SP and PC in the register list represented
|
||||
* by R8.
|
||||
*/
|
||||
/* 1st half LDMIA R9!,{} */
|
||||
((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9);
|
||||
/* 2nd half (register list) */
|
||||
((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
|
||||
asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc
|
||||
: t16_emulate_pop_nopc;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
const union decode_action kprobes_t16_actions[NUM_PROBES_T16_ACTIONS] = {
|
||||
[PROBES_T16_ADD_SP] = {.handler = t16_simulate_add_sp_imm},
|
||||
[PROBES_T16_CBZ] = {.handler = t16_simulate_cbz},
|
||||
[PROBES_T16_SIGN_EXTEND] = {.handler = t16_emulate_loregs_rwflags},
|
||||
[PROBES_T16_PUSH] = {.decoder = t16_decode_push},
|
||||
[PROBES_T16_POP] = {.decoder = t16_decode_pop},
|
||||
[PROBES_T16_SEV] = {.handler = probes_emulate_none},
|
||||
[PROBES_T16_WFE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_T16_IT] = {.decoder = t16_decode_it},
|
||||
[PROBES_T16_CMP] = {.handler = t16_emulate_loregs_rwflags},
|
||||
[PROBES_T16_ADDSUB] = {.handler = t16_emulate_loregs_noitrwflags},
|
||||
[PROBES_T16_LOGICAL] = {.handler = t16_emulate_loregs_noitrwflags},
|
||||
[PROBES_T16_LDR_LIT] = {.handler = t16_simulate_ldr_literal},
|
||||
[PROBES_T16_BLX] = {.handler = t16_simulate_bxblx},
|
||||
[PROBES_T16_HIREGOPS] = {.decoder = t16_decode_hiregs},
|
||||
[PROBES_T16_LDRHSTRH] = {.handler = t16_emulate_loregs_rwflags},
|
||||
[PROBES_T16_LDRSTR] = {.handler = t16_simulate_ldrstr_sp_relative},
|
||||
[PROBES_T16_ADR] = {.handler = t16_simulate_reladr},
|
||||
[PROBES_T16_LDMSTM] = {.handler = t16_emulate_loregs_rwflags},
|
||||
[PROBES_T16_BRANCH_COND] = {.decoder = t16_decode_cond_branch},
|
||||
[PROBES_T16_BRANCH] = {.handler = t16_simulate_branch},
|
||||
};
|
||||
|
||||
const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = {
|
||||
[PROBES_T32_LDMSTM] = {.decoder = t32_decode_ldmstm},
|
||||
[PROBES_T32_LDRDSTRD] = {.handler = t32_emulate_ldrdstrd},
|
||||
[PROBES_T32_TABLE_BRANCH] = {.handler = t32_simulate_table_branch},
|
||||
[PROBES_T32_TST] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_MOV] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_ADDSUB] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_LOGICAL] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_CMP] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_ADDWSUBW_PC] = {.handler = t32_emulate_rd8pc16_noflags,},
|
||||
[PROBES_T32_ADDWSUBW] = {.handler = t32_emulate_rd8rn16_noflags},
|
||||
[PROBES_T32_MOVW] = {.handler = t32_emulate_rd8rn16_noflags},
|
||||
[PROBES_T32_SAT] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_BITFIELD] = {.handler = t32_emulate_rd8rn16_noflags},
|
||||
[PROBES_T32_SEV] = {.handler = probes_emulate_none},
|
||||
[PROBES_T32_WFE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_T32_MRS] = {.handler = t32_simulate_mrs},
|
||||
[PROBES_T32_BRANCH_COND] = {.decoder = t32_decode_cond_branch},
|
||||
[PROBES_T32_BRANCH] = {.handler = t32_simulate_branch},
|
||||
[PROBES_T32_PLDI] = {.handler = probes_simulate_nop},
|
||||
[PROBES_T32_LDR_LIT] = {.handler = t32_simulate_ldr_literal},
|
||||
[PROBES_T32_LDRSTR] = {.handler = t32_emulate_ldrstr},
|
||||
[PROBES_T32_SIGN_EXTEND] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_MEDIA] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_REVERSE] = {.handler = t32_emulate_rd8rn16_noflags},
|
||||
[PROBES_T32_MUL_ADD] = {.handler = t32_emulate_rd8rn16rm0_rwflags},
|
||||
[PROBES_T32_MUL_ADD2] = {.handler = t32_emulate_rd8rn16rm0ra12_noflags},
|
||||
[PROBES_T32_MUL_ADD_LONG] = {
|
||||
.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
|
||||
};
|
@@ -1,628 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes.c
|
||||
*
|
||||
* Kprobes on ARM
|
||||
*
|
||||
* Abhishek Sagar <sagar.abhishek@gmail.com>
|
||||
* Copyright (C) 2006, 2007 Motorola Inc.
|
||||
*
|
||||
* Nicolas Pitre <nico@marvell.com>
|
||||
* Copyright (C) 2007 Marvell Ltd.
|
||||
*
|
||||
* 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 <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stop_machine.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "probes-thumb.h"
|
||||
#include "patch.h"
|
||||
|
||||
#define MIN_STACK_SIZE(addr) \
|
||||
min((unsigned long)MAX_STACK_SIZE, \
|
||||
(unsigned long)current_thread_info() + THREAD_START_SP - (addr))
|
||||
|
||||
#define flush_insns(addr, size) \
|
||||
flush_icache_range((unsigned long)(addr), \
|
||||
(unsigned long)(addr) + \
|
||||
(size))
|
||||
|
||||
/* Used as a marker in ARM_pc to note when we're in a jprobe. */
|
||||
#define JPROBE_MAGIC_ADDR 0xffffffff
|
||||
|
||||
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
|
||||
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
|
||||
|
||||
|
||||
int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
{
|
||||
kprobe_opcode_t insn;
|
||||
kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
|
||||
unsigned long addr = (unsigned long)p->addr;
|
||||
bool thumb;
|
||||
kprobe_decode_insn_t *decode_insn;
|
||||
const union decode_action *actions;
|
||||
int is;
|
||||
|
||||
if (in_exception_text(addr))
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
thumb = true;
|
||||
addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
|
||||
insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]);
|
||||
if (is_wide_instruction(insn)) {
|
||||
u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]);
|
||||
insn = __opcode_thumb32_compose(insn, inst2);
|
||||
decode_insn = thumb32_probes_decode_insn;
|
||||
actions = kprobes_t32_actions;
|
||||
} else {
|
||||
decode_insn = thumb16_probes_decode_insn;
|
||||
actions = kprobes_t16_actions;
|
||||
}
|
||||
#else /* !CONFIG_THUMB2_KERNEL */
|
||||
thumb = false;
|
||||
if (addr & 0x3)
|
||||
return -EINVAL;
|
||||
insn = __mem_to_opcode_arm(*p->addr);
|
||||
decode_insn = arm_probes_decode_insn;
|
||||
actions = kprobes_arm_actions;
|
||||
#endif
|
||||
|
||||
p->opcode = insn;
|
||||
p->ainsn.insn = tmp_insn;
|
||||
|
||||
switch ((*decode_insn)(insn, &p->ainsn, true, actions)) {
|
||||
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,
|
||||
sizeof(p->ainsn.insn[0]) * MAX_INSN_SIZE);
|
||||
p->ainsn.insn_fn = (probes_insn_fn_t *)
|
||||
((uintptr_t)p->ainsn.insn | thumb);
|
||||
break;
|
||||
|
||||
case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */
|
||||
p->ainsn.insn = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes arch_arm_kprobe(struct kprobe *p)
|
||||
{
|
||||
unsigned int brkp;
|
||||
void *addr;
|
||||
|
||||
if (IS_ENABLED(CONFIG_THUMB2_KERNEL)) {
|
||||
/* Remove any Thumb flag */
|
||||
addr = (void *)((uintptr_t)p->addr & ~1);
|
||||
|
||||
if (is_wide_instruction(p->opcode))
|
||||
brkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
|
||||
else
|
||||
brkp = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
|
||||
} else {
|
||||
kprobe_opcode_t insn = p->opcode;
|
||||
|
||||
addr = p->addr;
|
||||
brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
|
||||
|
||||
if (insn >= 0xe0000000)
|
||||
brkp |= 0xe0000000; /* Unconditional instruction */
|
||||
else
|
||||
brkp |= insn & 0xf0000000; /* Copy condition from insn */
|
||||
}
|
||||
|
||||
patch_text(addr, brkp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The actual disarming is done here on each CPU and synchronized using
|
||||
* stop_machine. This synchronization is necessary on SMP to avoid removing
|
||||
* a probe between the moment the 'Undefined Instruction' exception is raised
|
||||
* and the moment the exception handler reads the faulting instruction from
|
||||
* memory. It is also needed to atomically set the two half-words of a 32-bit
|
||||
* Thumb breakpoint.
|
||||
*/
|
||||
int __kprobes __arch_disarm_kprobe(void *p)
|
||||
{
|
||||
struct kprobe *kp = p;
|
||||
void *addr = (void *)((uintptr_t)kp->addr & ~1);
|
||||
|
||||
__patch_text(addr, kp->opcode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes arch_disarm_kprobe(struct kprobe *p)
|
||||
{
|
||||
stop_machine(__arch_disarm_kprobe, p, cpu_online_mask);
|
||||
}
|
||||
|
||||
void __kprobes arch_remove_kprobe(struct kprobe *p)
|
||||
{
|
||||
if (p->ainsn.insn) {
|
||||
free_insn_slot(p->ainsn.insn, 0);
|
||||
p->ainsn.insn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
|
||||
kcb->kprobe_status = kcb->prev_kprobe.status;
|
||||
}
|
||||
|
||||
static void __kprobes set_current_kprobe(struct kprobe *p)
|
||||
{
|
||||
__this_cpu_write(current_kprobe, p);
|
||||
}
|
||||
|
||||
static void __kprobes
|
||||
singlestep_skip(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
|
||||
if (is_wide_instruction(p->opcode))
|
||||
regs->ARM_pc += 4;
|
||||
else
|
||||
regs->ARM_pc += 2;
|
||||
#else
|
||||
regs->ARM_pc += 4;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void __kprobes
|
||||
singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
|
||||
{
|
||||
p->ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with IRQs disabled. 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.
|
||||
*/
|
||||
void __kprobes kprobe_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe *p, *cur;
|
||||
struct kprobe_ctlblk *kcb;
|
||||
|
||||
kcb = get_kprobe_ctlblk();
|
||||
cur = kprobe_running();
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
/*
|
||||
* First look for a probe which was registered using an address with
|
||||
* bit 0 set, this is the usual situation for pointers to Thumb code.
|
||||
* If not found, fallback to looking for one with bit 0 clear.
|
||||
*/
|
||||
p = get_kprobe((kprobe_opcode_t *)(regs->ARM_pc | 1));
|
||||
if (!p)
|
||||
p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
|
||||
|
||||
#else /* ! CONFIG_THUMB2_KERNEL */
|
||||
p = get_kprobe((kprobe_opcode_t *)regs->ARM_pc);
|
||||
#endif
|
||||
|
||||
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);
|
||||
restore_previous_kprobe(kcb);
|
||||
break;
|
||||
default:
|
||||
/* impossible cases */
|
||||
BUG();
|
||||
}
|
||||
} else if (p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
|
||||
/* Probe hit and conditional execution check ok. */
|
||||
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);
|
||||
if (p->post_handler) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
||||
p->post_handler(p, regs, 0);
|
||||
}
|
||||
reset_current_kprobe();
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Probe hit but conditional execution check failed,
|
||||
* so just skip the instruction and continue as if
|
||||
* nothing had happened.
|
||||
*/
|
||||
singlestep_skip(p, regs);
|
||||
}
|
||||
} else if (cur) {
|
||||
/* We probably hit a jprobe. Call its break handler. */
|
||||
if (cur->break_handler && cur->break_handler(cur, regs)) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SS;
|
||||
singlestep(cur, regs, kcb);
|
||||
if (cur->post_handler) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
||||
cur->post_handler(cur, regs, 0);
|
||||
}
|
||||
}
|
||||
reset_current_kprobe();
|
||||
} else {
|
||||
/*
|
||||
* The probe was removed and a race is in progress.
|
||||
* There is nothing we can do about it. Let's restart
|
||||
* the instruction. By the time we can restart, the
|
||||
* real instruction will be there.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
static int __kprobes kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
|
||||
{
|
||||
unsigned long flags;
|
||||
local_irq_save(flags);
|
||||
kprobe_handler(regs);
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
/*
|
||||
* notify_die() is currently never called on ARM,
|
||||
* so this callback is currently empty.
|
||||
*/
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a retprobed function returns, trampoline_handler() is called,
|
||||
* calling the kretprobe's handler. We construct a struct pt_regs to
|
||||
* give a view of registers r0-r11 to the user return-handler. This is
|
||||
* not a complete pt_regs structure, but that should be plenty sufficient
|
||||
* for kretprobe handlers which should normally be interested in r0 only
|
||||
* anyway.
|
||||
*/
|
||||
void __naked __kprobes kretprobe_trampoline(void)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"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"
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
"bx lr \n\t"
|
||||
#else
|
||||
"mov pc, lr \n\t"
|
||||
#endif
|
||||
: : : "memory");
|
||||
}
|
||||
|
||||
/* Called from kretprobe_trampoline */
|
||||
static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kretprobe_instance *ri = NULL;
|
||||
struct hlist_head *head, empty_rp;
|
||||
struct hlist_node *tmp;
|
||||
unsigned long flags, orig_ret_address = 0;
|
||||
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
||||
|
||||
INIT_HLIST_HEAD(&empty_rp);
|
||||
kretprobe_hash_lock(current, &head, &flags);
|
||||
|
||||
/*
|
||||
* 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 than one 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, tmp, head, hlist) {
|
||||
if (ri->task != current)
|
||||
/* another task is sharing our hash bucket */
|
||||
continue;
|
||||
|
||||
if (ri->rp && ri->rp->handler) {
|
||||
__this_cpu_write(current_kprobe, &ri->rp->kp);
|
||||
get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
ri->rp->handler(ri, regs);
|
||||
__this_cpu_write(current_kprobe, NULL);
|
||||
}
|
||||
|
||||
orig_ret_address = (unsigned long)ri->ret_addr;
|
||||
recycle_rp_inst(ri, &empty_rp);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
kretprobe_assert(ri, orig_ret_address, trampoline_address);
|
||||
kretprobe_hash_unlock(current, &flags);
|
||||
|
||||
hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
|
||||
hlist_del(&ri->hlist);
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
return (void *)orig_ret_address;
|
||||
}
|
||||
|
||||
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
|
||||
|
||||
/* Replace the return addr with trampoline addr. */
|
||||
regs->ARM_lr = (unsigned long)&kretprobe_trampoline;
|
||||
}
|
||||
|
||||
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;
|
||||
long cpsr;
|
||||
|
||||
kcb->jprobe_saved_regs = *regs;
|
||||
memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
|
||||
regs->ARM_pc = (long)jp->entry;
|
||||
|
||||
cpsr = regs->ARM_cpsr | PSR_I_BIT;
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
/* Set correct Thumb state in cpsr */
|
||||
if (regs->ARM_pc & 1)
|
||||
cpsr |= PSR_T_BIT;
|
||||
else
|
||||
cpsr &= ~PSR_T_BIT;
|
||||
#endif
|
||||
regs->ARM_cpsr = cpsr;
|
||||
|
||||
preempt_disable();
|
||||
return 1;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* We allocate some slack between the original SP and start of
|
||||
* our fabricated regs. To be precise we want to have worst case
|
||||
* covered which is STMFD with all 16 regs so we allocate 2 *
|
||||
* sizeof(struct_pt_regs)).
|
||||
*
|
||||
* This is to prevent any simulated instruction from writing
|
||||
* over the regs when they are accessing the stack.
|
||||
*/
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
"sub r0, %0, %1 \n\t"
|
||||
"mov sp, r0 \n\t"
|
||||
#else
|
||||
"sub sp, %0, %1 \n\t"
|
||||
#endif
|
||||
"ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t"
|
||||
"str %0, [sp, %2] \n\t"
|
||||
"str r0, [sp, %3] \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.
|
||||
*/
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
"ldr lr, [sp, %2] \n\t" /* lr = saved sp */
|
||||
"ldrd r0, r1, [sp, %5] \n\t" /* r0,r1 = saved lr,pc */
|
||||
"ldr r2, [sp, %4] \n\t" /* r2 = saved psr */
|
||||
"stmdb lr!, {r0, r1, r2} \n\t" /* push saved lr and */
|
||||
/* rfe context */
|
||||
"ldmia sp, {r0 - r12} \n\t"
|
||||
"mov sp, lr \n\t"
|
||||
"ldr lr, [sp], #4 \n\t"
|
||||
"rfeia sp! \n\t"
|
||||
#else
|
||||
"ldr r0, [sp, %4] \n\t"
|
||||
"msr cpsr_cxsf, r0 \n\t"
|
||||
"ldmia sp, {r0 - pc} \n\t"
|
||||
#endif
|
||||
:
|
||||
: "r" (kcb->jprobe_saved_regs.ARM_sp),
|
||||
"I" (sizeof(struct pt_regs) * 2),
|
||||
"J" (offsetof(struct pt_regs, ARM_sp)),
|
||||
"J" (offsetof(struct pt_regs, ARM_pc)),
|
||||
"J" (offsetof(struct pt_regs, ARM_cpsr)),
|
||||
"J" (offsetof(struct pt_regs, ARM_lr))
|
||||
: "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 (regs->ARM_pc == JPROBE_MAGIC_ADDR) {
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __kprobes arch_trampoline_kprobe(struct kprobe *p)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
|
||||
static struct undef_hook kprobes_thumb16_break_hook = {
|
||||
.instr_mask = 0xffff,
|
||||
.instr_val = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION,
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = SVC_MODE,
|
||||
.fn = kprobe_trap_handler,
|
||||
};
|
||||
|
||||
static struct undef_hook kprobes_thumb32_break_hook = {
|
||||
.instr_mask = 0xffffffff,
|
||||
.instr_val = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION,
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = SVC_MODE,
|
||||
.fn = kprobe_trap_handler,
|
||||
};
|
||||
|
||||
#else /* !CONFIG_THUMB2_KERNEL */
|
||||
|
||||
static struct undef_hook kprobes_arm_break_hook = {
|
||||
.instr_mask = 0x0fffffff,
|
||||
.instr_val = KPROBE_ARM_BREAKPOINT_INSTRUCTION,
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = SVC_MODE,
|
||||
.fn = kprobe_trap_handler,
|
||||
};
|
||||
|
||||
#endif /* !CONFIG_THUMB2_KERNEL */
|
||||
|
||||
int __init arch_init_kprobes()
|
||||
{
|
||||
arm_probes_decode_init();
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
register_undef_hook(&kprobes_thumb16_break_hook);
|
||||
register_undef_hook(&kprobes_thumb32_break_hook);
|
||||
#else
|
||||
register_undef_hook(&kprobes_arm_break_hook);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes.h
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
* Some contents moved here from arch/arm/include/asm/kprobes.h 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ARM_KERNEL_KPROBES_H
|
||||
#define _ARM_KERNEL_KPROBES_H
|
||||
|
||||
#include "probes.h"
|
||||
|
||||
/*
|
||||
* These undefined instructions must be unique and
|
||||
* reserved solely for kprobes' use.
|
||||
*/
|
||||
#define KPROBE_ARM_BREAKPOINT_INSTRUCTION 0x07f001f8
|
||||
#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18
|
||||
#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018
|
||||
|
||||
enum probes_insn __kprobes
|
||||
kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *h);
|
||||
|
||||
typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t,
|
||||
struct arch_probes_insn *,
|
||||
bool,
|
||||
const union decode_action *);
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
|
||||
extern const union decode_action kprobes_t32_actions[];
|
||||
extern const union decode_action kprobes_t16_actions[];
|
||||
|
||||
#else /* !CONFIG_THUMB2_KERNEL */
|
||||
|
||||
extern const union decode_action kprobes_arm_actions[];
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _ARM_KERNEL_KPROBES_H */
|
@@ -8,8 +8,7 @@
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/opcodes.h>
|
||||
|
||||
#include "patch.h"
|
||||
#include <asm/patch.h>
|
||||
|
||||
struct patch {
|
||||
void *addr;
|
||||
|
@@ -1,17 +0,0 @@
|
||||
#ifndef _ARM_KERNEL_PATCH_H
|
||||
#define _ARM_KERNEL_PATCH_H
|
||||
|
||||
void patch_text(void *addr, unsigned int insn);
|
||||
void __patch_text_real(void *addr, unsigned int insn, bool remap);
|
||||
|
||||
static inline void __patch_text(void *addr, unsigned int insn)
|
||||
{
|
||||
__patch_text_real(addr, insn, true);
|
||||
}
|
||||
|
||||
static inline void __patch_text_early(void *addr, unsigned int insn)
|
||||
{
|
||||
__patch_text_real(addr, insn, false);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,734 +0,0 @@
|
||||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
|
||||
#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
|
||||
|
||||
#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25)
|
||||
|
||||
/*
|
||||
* To avoid 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 performance.
|
||||
*
|
||||
* 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 perform register writeback)
|
||||
* 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.
|
||||
*/
|
||||
|
||||
void __kprobes simulate_bbl(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
long iaddr = (long) regs->ARM_pc - 4;
|
||||
int disp = branch_displacement(insn);
|
||||
|
||||
if (insn & (1 << 24))
|
||||
regs->ARM_lr = iaddr + 4;
|
||||
|
||||
regs->ARM_pc = iaddr + 8 + disp;
|
||||
}
|
||||
|
||||
void __kprobes simulate_blx1(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
long iaddr = (long) regs->ARM_pc - 4;
|
||||
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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
int rm = insn & 0xf;
|
||||
long rmv = regs->uregs[rm];
|
||||
|
||||
if (insn & (1 << 5))
|
||||
regs->ARM_lr = (long) regs->ARM_pc;
|
||||
|
||||
regs->ARM_pc = rmv & ~0x1;
|
||||
regs->ARM_cpsr &= ~PSR_T_BIT;
|
||||
if (rmv & 0x1)
|
||||
regs->ARM_cpsr |= PSR_T_BIT;
|
||||
}
|
||||
|
||||
void __kprobes simulate_mrs(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
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(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
regs->uregs[12] = regs->uregs[13];
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 const union decode_item arm_1111_table[] = {
|
||||
/* Unconditional instructions */
|
||||
|
||||
/* memory hint 1111 0100 x001 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* 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, PROBES_PRELOAD_IMM),
|
||||
|
||||
/* 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, PROBES_PRELOAD_REG),
|
||||
|
||||
/* BLX (immediate) 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_SIMULATE (0xfe000000, 0xfa000000, PROBES_BRANCH_IMM),
|
||||
|
||||
/* CPS 1111 0001 0000 xxx0 xxxx xxxx xx0x xxxx */
|
||||
/* SETEND 1111 0001 0000 0001 xxxx xxxx 0000 xxxx */
|
||||
/* SRS 1111 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* RFE 1111 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
|
||||
/* Coprocessor instructions... */
|
||||
/* MCRR2 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* MRRC2 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDC2 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STC2 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* CDP2 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* MCR2 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
|
||||
/* MRC2 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
|
||||
|
||||
/* Other unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_0001_0xx0____0xxx_table[] = {
|
||||
/* Miscellaneous instructions */
|
||||
|
||||
/* MRS cpsr cccc 0001 0000 xxxx xxxx xxxx 0000 xxxx */
|
||||
DECODE_SIMULATEX(0x0ff000f0, 0x01000000, PROBES_MRS,
|
||||
REGS(0, NOPC, 0, 0, 0)),
|
||||
|
||||
/* BX cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx */
|
||||
DECODE_SIMULATE (0x0ff000f0, 0x01200010, PROBES_BRANCH_REG),
|
||||
|
||||
/* BLX (register) cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx */
|
||||
DECODE_SIMULATEX(0x0ff000f0, 0x01200030, PROBES_BRANCH_REG,
|
||||
REGS(0, 0, 0, 0, NOPC)),
|
||||
|
||||
/* CLZ cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx */
|
||||
DECODE_EMULATEX (0x0ff000f0, 0x01600010, PROBES_CLZ,
|
||||
REGS(0, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* QADD cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* QSUB cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* QDADD cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* QDSUB cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx */
|
||||
DECODE_EMULATEX (0x0f9000f0, 0x01000050, PROBES_SATURATING_ARITHMETIC,
|
||||
REGS(NOPC, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* BXJ cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx */
|
||||
/* MSR cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* MRS spsr cccc 0001 0100 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* BKPT 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* SMC cccc 0001 0110 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* And unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_0001_0xx0____1xx0_table[] = {
|
||||
/* Halfword multiply and multiply-accumulate */
|
||||
|
||||
/* SMLALxy cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx */
|
||||
DECODE_EMULATEX (0x0ff00090, 0x01400080, PROBES_MUL1,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
/* SMULWy cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx */
|
||||
DECODE_OR (0x0ff000b0, 0x012000a0),
|
||||
/* SMULxy cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx */
|
||||
DECODE_EMULATEX (0x0ff00090, 0x01600080, PROBES_MUL2,
|
||||
REGS(NOPC, 0, NOPC, 0, NOPC)),
|
||||
|
||||
/* SMLAxy cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx */
|
||||
DECODE_OR (0x0ff00090, 0x01000080),
|
||||
/* SMLAWy cccc 0001 0010 xxxx xxxx xxxx 1x00 xxxx */
|
||||
DECODE_EMULATEX (0x0ff000b0, 0x01200080, PROBES_MUL2,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_0000_____1001_table[] = {
|
||||
/* Multiply and multiply-accumulate */
|
||||
|
||||
/* MUL cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* MULS cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx */
|
||||
DECODE_EMULATEX (0x0fe000f0, 0x00000090, PROBES_MUL2,
|
||||
REGS(NOPC, 0, NOPC, 0, NOPC)),
|
||||
|
||||
/* MLA cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* MLAS cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx */
|
||||
DECODE_OR (0x0fe000f0, 0x00200090),
|
||||
/* MLS cccc 0000 0110 xxxx xxxx xxxx 1001 xxxx */
|
||||
DECODE_EMULATEX (0x0ff000f0, 0x00600090, PROBES_MUL2,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
/* UMAAL cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx */
|
||||
DECODE_OR (0x0ff000f0, 0x00400090),
|
||||
/* UMULL cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* UMULLS cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* UMLAL cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* UMLALS cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* SMULL cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* SMULLS cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* SMLAL cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* SMLALS cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx */
|
||||
DECODE_EMULATEX (0x0f8000f0, 0x00800090, PROBES_MUL1,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_0001_____1001_table[] = {
|
||||
/* Synchronization primitives */
|
||||
|
||||
#if __LINUX_ARM_ARCH__ < 6
|
||||
/* Deprecated on ARMv6 and may be UNDEFINED on v7 */
|
||||
/* SMP/SWPB cccc 0001 0x00 xxxx xxxx xxxx 1001 xxxx */
|
||||
DECODE_EMULATEX (0x0fb000f0, 0x01000090, PROBES_SWP,
|
||||
REGS(NOPC, NOPC, 0, 0, NOPC)),
|
||||
#endif
|
||||
/* LDREX/STREX{,D,B,H} cccc 0001 1xxx xxxx xxxx xxxx 1001 xxxx */
|
||||
/* And unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_000x_____1xx1_table[] = {
|
||||
/* Extra load/store instructions */
|
||||
|
||||
/* STRHT cccc 0000 xx10 xxxx xxxx xxxx 1011 xxxx */
|
||||
/* ??? cccc 0000 xx10 xxxx xxxx xxxx 11x1 xxxx */
|
||||
/* LDRHT cccc 0000 xx11 xxxx xxxx xxxx 1011 xxxx */
|
||||
/* LDRSBT cccc 0000 xx11 xxxx xxxx xxxx 1101 xxxx */
|
||||
/* LDRSHT cccc 0000 xx11 xxxx xxxx xxxx 1111 xxxx */
|
||||
DECODE_REJECT (0x0f200090, 0x00200090),
|
||||
|
||||
/* LDRD/STRD lr,pc,{... cccc 000x x0x0 xxxx 111x xxxx 1101 xxxx */
|
||||
DECODE_REJECT (0x0e10e0d0, 0x0000e0d0),
|
||||
|
||||
/* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx */
|
||||
/* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx */
|
||||
DECODE_EMULATEX (0x0e5000d0, 0x000000d0, PROBES_LDRSTRD,
|
||||
REGS(NOPCWB, NOPCX, 0, 0, NOPC)),
|
||||
|
||||
/* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx */
|
||||
/* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx */
|
||||
DECODE_EMULATEX (0x0e5000d0, 0x004000d0, PROBES_LDRSTRD,
|
||||
REGS(NOPCWB, NOPCX, 0, 0, 0)),
|
||||
|
||||
/* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx */
|
||||
DECODE_EMULATEX (0x0e5000f0, 0x000000b0, PROBES_STORE_EXTRA,
|
||||
REGS(NOPCWB, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* LDRH (register) cccc 000x x0x1 xxxx xxxx xxxx 1011 xxxx */
|
||||
/* LDRSB (register) cccc 000x x0x1 xxxx xxxx xxxx 1101 xxxx */
|
||||
/* LDRSH (register) cccc 000x x0x1 xxxx xxxx xxxx 1111 xxxx */
|
||||
DECODE_EMULATEX (0x0e500090, 0x00100090, PROBES_LOAD_EXTRA,
|
||||
REGS(NOPCWB, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* STRH (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1011 xxxx */
|
||||
DECODE_EMULATEX (0x0e5000f0, 0x004000b0, PROBES_STORE_EXTRA,
|
||||
REGS(NOPCWB, NOPC, 0, 0, 0)),
|
||||
|
||||
/* LDRH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1011 xxxx */
|
||||
/* LDRSB (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1101 xxxx */
|
||||
/* LDRSH (immediate) cccc 000x x1x1 xxxx xxxx xxxx 1111 xxxx */
|
||||
DECODE_EMULATEX (0x0e500090, 0x00500090, PROBES_LOAD_EXTRA,
|
||||
REGS(NOPCWB, NOPC, 0, 0, 0)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_000x_table[] = {
|
||||
/* Data-processing (register) */
|
||||
|
||||
/* <op>S PC, ... cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0x0e10f000, 0x0010f000),
|
||||
|
||||
/* MOV IP, SP 1110 0001 1010 0000 1100 0000 0000 1101 */
|
||||
DECODE_SIMULATE (0xffffffff, 0xe1a0c00d, PROBES_MOV_IP_SP),
|
||||
|
||||
/* TST (register) cccc 0001 0001 xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* TEQ (register) cccc 0001 0011 xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* CMP (register) cccc 0001 0101 xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* CMN (register) cccc 0001 0111 xxxx xxxx xxxx xxx0 xxxx */
|
||||
DECODE_EMULATEX (0x0f900010, 0x01100000, PROBES_DATA_PROCESSING_REG,
|
||||
REGS(ANY, 0, 0, 0, ANY)),
|
||||
|
||||
/* MOV (register) cccc 0001 101x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* MVN (register) cccc 0001 111x xxxx xxxx xxxx xxx0 xxxx */
|
||||
DECODE_EMULATEX (0x0fa00010, 0x01a00000, PROBES_DATA_PROCESSING_REG,
|
||||
REGS(0, ANY, 0, 0, ANY)),
|
||||
|
||||
/* AND (register) cccc 0000 000x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* EOR (register) cccc 0000 001x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* SUB (register) cccc 0000 010x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* RSB (register) cccc 0000 011x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* ADD (register) cccc 0000 100x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* ADC (register) cccc 0000 101x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* SBC (register) cccc 0000 110x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* RSC (register) cccc 0000 111x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* ORR (register) cccc 0001 100x xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* BIC (register) cccc 0001 110x xxxx xxxx xxxx xxx0 xxxx */
|
||||
DECODE_EMULATEX (0x0e000010, 0x00000000, PROBES_DATA_PROCESSING_REG,
|
||||
REGS(ANY, ANY, 0, 0, ANY)),
|
||||
|
||||
/* TST (reg-shift reg) cccc 0001 0001 xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* TEQ (reg-shift reg) cccc 0001 0011 xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* CMP (reg-shift reg) cccc 0001 0101 xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* CMN (reg-shift reg) cccc 0001 0111 xxxx xxxx xxxx 0xx1 xxxx */
|
||||
DECODE_EMULATEX (0x0f900090, 0x01100010, PROBES_DATA_PROCESSING_REG,
|
||||
REGS(NOPC, 0, NOPC, 0, NOPC)),
|
||||
|
||||
/* MOV (reg-shift reg) cccc 0001 101x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* MVN (reg-shift reg) cccc 0001 111x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
DECODE_EMULATEX (0x0fa00090, 0x01a00010, PROBES_DATA_PROCESSING_REG,
|
||||
REGS(0, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
/* AND (reg-shift reg) cccc 0000 000x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* EOR (reg-shift reg) cccc 0000 001x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* SUB (reg-shift reg) cccc 0000 010x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* RSB (reg-shift reg) cccc 0000 011x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* ADD (reg-shift reg) cccc 0000 100x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* ADC (reg-shift reg) cccc 0000 101x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* SBC (reg-shift reg) cccc 0000 110x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* RSC (reg-shift reg) cccc 0000 111x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* ORR (reg-shift reg) cccc 0001 100x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
/* BIC (reg-shift reg) cccc 0001 110x xxxx xxxx xxxx 0xx1 xxxx */
|
||||
DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_001x_table[] = {
|
||||
/* Data-processing (immediate) */
|
||||
|
||||
/* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
|
||||
REGS(0, NOPC, 0, 0, 0)),
|
||||
|
||||
/* 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, 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, 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 */
|
||||
DECODE_REJECT (0x0fb00000, 0x03200000),
|
||||
|
||||
/* <op>S PC, ... cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0x0e10f000, 0x0210f000),
|
||||
|
||||
/* TST (immediate) cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* TEQ (immediate) cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* CMP (immediate) cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* CMN (immediate) cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0f900000, 0x03100000, PROBES_DATA_PROCESSING_IMM,
|
||||
REGS(ANY, 0, 0, 0, 0)),
|
||||
|
||||
/* MOV (immediate) cccc 0011 101x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* MVN (immediate) cccc 0011 111x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0fa00000, 0x03a00000, PROBES_DATA_PROCESSING_IMM,
|
||||
REGS(0, ANY, 0, 0, 0)),
|
||||
|
||||
/* AND (immediate) cccc 0010 000x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* EOR (immediate) cccc 0010 001x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* SUB (immediate) cccc 0010 010x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* RSB (immediate) cccc 0010 011x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ADD (immediate) cccc 0010 100x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ADC (immediate) cccc 0010 101x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* SBC (immediate) cccc 0010 110x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* RSC (immediate) cccc 0010 111x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ORR (immediate) cccc 0011 100x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* BIC (immediate) cccc 0011 110x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0e000000, 0x02000000, PROBES_DATA_PROCESSING_IMM,
|
||||
REGS(ANY, ANY, 0, 0, 0)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_0110_____xxx1_table[] = {
|
||||
/* Media instructions */
|
||||
|
||||
/* SEL cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx */
|
||||
DECODE_EMULATEX (0x0ff000f0, 0x068000b0, PROBES_SATURATE,
|
||||
REGS(NOPC, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* SSAT cccc 0110 101x xxxx xxxx xxxx xx01 xxxx */
|
||||
/* USAT cccc 0110 111x xxxx xxxx xxxx xx01 xxxx */
|
||||
DECODE_OR(0x0fa00030, 0x06a00010),
|
||||
/* SSAT16 cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx */
|
||||
/* USAT16 cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx */
|
||||
DECODE_EMULATEX (0x0fb000f0, 0x06a00030, PROBES_SATURATE,
|
||||
REGS(0, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* REV cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx */
|
||||
/* REV16 cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx */
|
||||
/* RBIT cccc 0110 1111 xxxx xxxx xxxx 0011 xxxx */
|
||||
/* REVSH cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx */
|
||||
DECODE_EMULATEX (0x0fb00070, 0x06b00030, PROBES_REV,
|
||||
REGS(0, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* ??? cccc 0110 0x00 xxxx xxxx xxxx xxx1 xxxx */
|
||||
DECODE_REJECT (0x0fb00010, 0x06000010),
|
||||
/* ??? cccc 0110 0xxx xxxx xxxx xxxx 1011 xxxx */
|
||||
DECODE_REJECT (0x0f8000f0, 0x060000b0),
|
||||
/* ??? cccc 0110 0xxx xxxx xxxx xxxx 1101 xxxx */
|
||||
DECODE_REJECT (0x0f8000f0, 0x060000d0),
|
||||
/* SADD16 cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx */
|
||||
/* SADDSUBX cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx */
|
||||
/* SSUBADDX cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* SSUB16 cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* SADD8 cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* SSUB8 cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx */
|
||||
/* 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 */
|
||||
/* UADDSUBX cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx */
|
||||
/* USUBADDX cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* USUB16 cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* UADD8 cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx */
|
||||
/* USUB8 cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx */
|
||||
/* 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 */
|
||||
DECODE_EMULATEX (0x0f800010, 0x06000010, PROBES_MMI,
|
||||
REGS(NOPC, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* PKHBT cccc 0110 1000 xxxx xxxx xxxx x001 xxxx */
|
||||
/* PKHTB cccc 0110 1000 xxxx xxxx xxxx x101 xxxx */
|
||||
DECODE_EMULATEX (0x0ff00030, 0x06800010, PROBES_PACK,
|
||||
REGS(NOPC, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* ??? cccc 0110 1001 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* ??? cccc 0110 1101 xxxx xxxx xxxx 0111 xxxx */
|
||||
DECODE_REJECT (0x0fb000f0, 0x06900070),
|
||||
|
||||
/* SXTB16 cccc 0110 1000 1111 xxxx xxxx 0111 xxxx */
|
||||
/* SXTB cccc 0110 1010 1111 xxxx xxxx 0111 xxxx */
|
||||
/* SXTH cccc 0110 1011 1111 xxxx xxxx 0111 xxxx */
|
||||
/* UXTB16 cccc 0110 1100 1111 xxxx xxxx 0111 xxxx */
|
||||
/* UXTB cccc 0110 1110 1111 xxxx xxxx 0111 xxxx */
|
||||
/* UXTH cccc 0110 1111 1111 xxxx xxxx 0111 xxxx */
|
||||
DECODE_EMULATEX (0x0f8f00f0, 0x068f0070, PROBES_EXTEND,
|
||||
REGS(0, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* SXTAB16 cccc 0110 1000 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 */
|
||||
DECODE_EMULATEX (0x0f8000f0, 0x06800070, PROBES_EXTEND_ADD,
|
||||
REGS(NOPCX, NOPC, 0, 0, NOPC)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_0111_____xxx1_table[] = {
|
||||
/* Media instructions */
|
||||
|
||||
/* UNDEFINED cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */
|
||||
DECODE_REJECT (0x0ff000f0, 0x07f000f0),
|
||||
|
||||
/* SMLALD cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx */
|
||||
/* SMLSLD cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx */
|
||||
DECODE_EMULATEX (0x0ff00090, 0x07400010, PROBES_MUL_ADD_LONG,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
/* SMUAD cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx */
|
||||
/* SMUSD cccc 0111 0000 xxxx 1111 xxxx 01x1 xxxx */
|
||||
DECODE_OR (0x0ff0f090, 0x0700f010),
|
||||
/* SMMUL cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx */
|
||||
DECODE_OR (0x0ff0f0d0, 0x0750f010),
|
||||
/* USAD8 cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx */
|
||||
DECODE_EMULATEX (0x0ff0f0f0, 0x0780f010, PROBES_MUL_ADD,
|
||||
REGS(NOPC, 0, NOPC, 0, NOPC)),
|
||||
|
||||
/* SMLAD cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx */
|
||||
/* SMLSD cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx */
|
||||
DECODE_OR (0x0ff00090, 0x07000010),
|
||||
/* SMMLA cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx */
|
||||
DECODE_OR (0x0ff000d0, 0x07500010),
|
||||
/* USADA8 cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx */
|
||||
DECODE_EMULATEX (0x0ff000f0, 0x07800010, PROBES_MUL_ADD,
|
||||
REGS(NOPC, NOPCX, NOPC, 0, NOPC)),
|
||||
|
||||
/* SMMLS cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx */
|
||||
DECODE_EMULATEX (0x0ff000d0, 0x075000d0, PROBES_MUL_ADD,
|
||||
REGS(NOPC, NOPC, NOPC, 0, NOPC)),
|
||||
|
||||
/* SBFX cccc 0111 101x xxxx xxxx xxxx x101 xxxx */
|
||||
/* UBFX cccc 0111 111x xxxx xxxx xxxx x101 xxxx */
|
||||
DECODE_EMULATEX (0x0fa00070, 0x07a00050, PROBES_BITFIELD,
|
||||
REGS(0, NOPC, 0, 0, NOPC)),
|
||||
|
||||
/* BFC cccc 0111 110x xxxx xxxx xxxx x001 1111 */
|
||||
DECODE_EMULATEX (0x0fe0007f, 0x07c0001f, PROBES_BITFIELD,
|
||||
REGS(0, NOPC, 0, 0, 0)),
|
||||
|
||||
/* BFI cccc 0111 110x xxxx xxxx xxxx x001 xxxx */
|
||||
DECODE_EMULATEX (0x0fe00070, 0x07c00010, PROBES_BITFIELD,
|
||||
REGS(0, NOPC, 0, 0, NOPCX)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_01xx_table[] = {
|
||||
/* Load/store word and unsigned byte */
|
||||
|
||||
/* LDRB/STRB pc,[...] cccc 01xx x0xx xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0x0c40f000, 0x0440f000),
|
||||
|
||||
/* STRT cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRT cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STRBT cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRBT cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0x0d200000, 0x04200000),
|
||||
|
||||
/* STR (immediate) cccc 010x x0x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STRB (immediate) cccc 010x x1x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0e100000, 0x04000000, PROBES_STORE,
|
||||
REGS(NOPCWB, ANY, 0, 0, 0)),
|
||||
|
||||
/* LDR (immediate) cccc 010x x0x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRB (immediate) cccc 010x x1x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0e100000, 0x04100000, PROBES_LOAD,
|
||||
REGS(NOPCWB, ANY, 0, 0, 0)),
|
||||
|
||||
/* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0e100000, 0x06000000, PROBES_STORE,
|
||||
REGS(NOPCWB, ANY, 0, 0, NOPC)),
|
||||
|
||||
/* LDR (register) cccc 011x x0x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRB (register) cccc 011x x1x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0e100000, 0x06100000, PROBES_LOAD,
|
||||
REGS(NOPCWB, ANY, 0, 0, NOPC)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item arm_cccc_100x_table[] = {
|
||||
/* Block data transfer instructions */
|
||||
|
||||
/* LDM cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STM cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0x0e400000, 0x08000000, PROBES_LDMSTM),
|
||||
|
||||
/* STM (user registers) cccc 100x x1x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDM (user registers) cccc 100x x1x1 xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* LDM (exception ret) cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
const union decode_item probes_decode_arm_table[] = {
|
||||
/*
|
||||
* Unconditional instructions
|
||||
* 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xf0000000, 0xf0000000, arm_1111_table),
|
||||
|
||||
/*
|
||||
* Miscellaneous instructions
|
||||
* cccc 0001 0xx0 xxxx xxxx xxxx 0xxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0f900080, 0x01000000, arm_cccc_0001_0xx0____0xxx_table),
|
||||
|
||||
/*
|
||||
* Halfword multiply and multiply-accumulate
|
||||
* cccc 0001 0xx0 xxxx xxxx xxxx 1xx0 xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0f900090, 0x01000080, arm_cccc_0001_0xx0____1xx0_table),
|
||||
|
||||
/*
|
||||
* Multiply and multiply-accumulate
|
||||
* cccc 0000 xxxx xxxx xxxx xxxx 1001 xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0f0000f0, 0x00000090, arm_cccc_0000_____1001_table),
|
||||
|
||||
/*
|
||||
* Synchronization primitives
|
||||
* cccc 0001 xxxx xxxx xxxx xxxx 1001 xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0f0000f0, 0x01000090, arm_cccc_0001_____1001_table),
|
||||
|
||||
/*
|
||||
* Extra load/store instructions
|
||||
* cccc 000x xxxx xxxx xxxx xxxx 1xx1 xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0e000090, 0x00000090, arm_cccc_000x_____1xx1_table),
|
||||
|
||||
/*
|
||||
* Data-processing (register)
|
||||
* cccc 000x xxxx xxxx xxxx xxxx xxx0 xxxx
|
||||
* Data-processing (register-shifted register)
|
||||
* cccc 000x xxxx xxxx xxxx xxxx 0xx1 xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0e000000, 0x00000000, arm_cccc_000x_table),
|
||||
|
||||
/*
|
||||
* Data-processing (immediate)
|
||||
* cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0e000000, 0x02000000, arm_cccc_001x_table),
|
||||
|
||||
/*
|
||||
* Media instructions
|
||||
* cccc 011x xxxx xxxx xxxx xxxx xxx1 xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0f000010, 0x06000010, arm_cccc_0110_____xxx1_table),
|
||||
DECODE_TABLE (0x0f000010, 0x07000010, arm_cccc_0111_____xxx1_table),
|
||||
|
||||
/*
|
||||
* Load/store word and unsigned byte
|
||||
* cccc 01xx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0c000000, 0x04000000, arm_cccc_01xx_table),
|
||||
|
||||
/*
|
||||
* Block data transfer instructions
|
||||
* cccc 100x xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0x0e000000, 0x08000000, arm_cccc_100x_table),
|
||||
|
||||
/* B cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx */
|
||||
/* BL cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_SIMULATE (0x0e000000, 0x0a000000, PROBES_BRANCH),
|
||||
|
||||
/*
|
||||
* Supervisor Call, and coprocessor instructions
|
||||
*/
|
||||
|
||||
/* MCRR cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* MRRC cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDC cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STC cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* CDP cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx */
|
||||
/* MCR cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx */
|
||||
/* MRC cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx */
|
||||
/* SVC cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0x0c000000, 0x0c000000),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
|
||||
EXPORT_SYMBOL_GPL(probes_decode_arm_table);
|
||||
#endif
|
||||
|
||||
static void __kprobes arm_singlestep(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_pc += 4;
|
||||
asi->insn_handler(insn, asi, regs);
|
||||
}
|
||||
|
||||
/* 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 probes_insn __kprobes
|
||||
arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions)
|
||||
{
|
||||
asi->insn_singlestep = arm_singlestep;
|
||||
asi->insn_check_cc = probes_condition_checks[insn>>28];
|
||||
return probes_decode_insn(insn, asi, probes_decode_arm_table, false,
|
||||
emulate, actions);
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-arm.h
|
||||
*
|
||||
* Copyright 2013 Linaro Ltd.
|
||||
* Written by: David A. Long
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#ifndef _ARM_KERNEL_PROBES_ARM_H
|
||||
#define _ARM_KERNEL_PROBES_ARM_H
|
||||
|
||||
enum probes_arm_action {
|
||||
PROBES_EMULATE_NONE,
|
||||
PROBES_SIMULATE_NOP,
|
||||
PROBES_PRELOAD_IMM,
|
||||
PROBES_PRELOAD_REG,
|
||||
PROBES_BRANCH_IMM,
|
||||
PROBES_BRANCH_REG,
|
||||
PROBES_MRS,
|
||||
PROBES_CLZ,
|
||||
PROBES_SATURATING_ARITHMETIC,
|
||||
PROBES_MUL1,
|
||||
PROBES_MUL2,
|
||||
PROBES_SWP,
|
||||
PROBES_LDRSTRD,
|
||||
PROBES_LOAD,
|
||||
PROBES_STORE,
|
||||
PROBES_LOAD_EXTRA,
|
||||
PROBES_STORE_EXTRA,
|
||||
PROBES_MOV_IP_SP,
|
||||
PROBES_DATA_PROCESSING_REG,
|
||||
PROBES_DATA_PROCESSING_IMM,
|
||||
PROBES_MOV_HALFWORD,
|
||||
PROBES_SEV,
|
||||
PROBES_WFE,
|
||||
PROBES_SATURATE,
|
||||
PROBES_REV,
|
||||
PROBES_MMI,
|
||||
PROBES_PACK,
|
||||
PROBES_EXTEND,
|
||||
PROBES_EXTEND_ADD,
|
||||
PROBES_MUL_ADD_LONG,
|
||||
PROBES_MUL_ADD,
|
||||
PROBES_BITFIELD,
|
||||
PROBES_BRANCH,
|
||||
PROBES_LDMSTM,
|
||||
NUM_PROBES_ARM_ACTIONS
|
||||
};
|
||||
|
||||
void __kprobes simulate_bbl(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs);
|
||||
void __kprobes simulate_blx1(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs);
|
||||
void __kprobes simulate_blx2bx(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs);
|
||||
void __kprobes simulate_mrs(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs);
|
||||
void __kprobes simulate_mov_ipsp(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi, struct pt_regs *regs);
|
||||
|
||||
extern const union decode_item probes_decode_arm_table[];
|
||||
|
||||
enum probes_insn arm_probes_decode_insn(probes_opcode_t,
|
||||
struct arch_probes_insn *, bool emulate,
|
||||
const union decode_action *actions);
|
||||
|
||||
#endif
|
@@ -1,882 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-thumb.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
* 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/stddef.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-thumb.h"
|
||||
|
||||
|
||||
static const union decode_item t32_table_1110_100x_x0xx[] = {
|
||||
/* Load/store multiple instructions */
|
||||
|
||||
/* Rn is PC 1110 100x x0xx 1111 xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfe4f0000, 0xe80f0000),
|
||||
|
||||
/* SRS 1110 1000 00x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* RFE 1110 1000 00x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xffc00000, 0xe8000000),
|
||||
/* SRS 1110 1001 10x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* RFE 1110 1001 10x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xffc00000, 0xe9800000),
|
||||
|
||||
/* STM Rn, {...pc} 1110 100x x0x0 xxxx 1xxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfe508000, 0xe8008000),
|
||||
/* LDM Rn, {...lr,pc} 1110 100x x0x1 xxxx 11xx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfe50c000, 0xe810c000),
|
||||
/* LDM/STM Rn, {...sp} 1110 100x x0xx xxxx xx1x xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfe402000, 0xe8002000),
|
||||
|
||||
/* STMIA 1110 1000 10x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDMIA 1110 1000 10x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STMDB 1110 1001 00x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDMDB 1110 1001 00x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0xfe400000, 0xe8000000, PROBES_T32_LDMSTM),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1110_100x_x1xx[] = {
|
||||
/* Load/store dual, load/store exclusive, table branch */
|
||||
|
||||
/* STRD (immediate) 1110 1000 x110 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRD (immediate) 1110 1000 x111 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_OR (0xff600000, 0xe8600000),
|
||||
/* STRD (immediate) 1110 1001 x1x0 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRD (immediate) 1110 1001 x1x1 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xff400000, 0xe9400000, PROBES_T32_LDRDSTRD,
|
||||
REGS(NOPCWB, NOSPPC, NOSPPC, 0, 0)),
|
||||
|
||||
/* TBB 1110 1000 1101 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* TBH 1110 1000 1101 xxxx xxxx xxxx 0001 xxxx */
|
||||
DECODE_SIMULATEX(0xfff000e0, 0xe8d00000, PROBES_T32_TABLE_BRANCH,
|
||||
REGS(NOSP, 0, 0, 0, NOSPPC)),
|
||||
|
||||
/* STREX 1110 1000 0100 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDREX 1110 1000 0101 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STREXB 1110 1000 1100 xxxx xxxx xxxx 0100 xxxx */
|
||||
/* STREXH 1110 1000 1100 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* STREXD 1110 1000 1100 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* LDREXB 1110 1000 1101 xxxx xxxx xxxx 0100 xxxx */
|
||||
/* LDREXH 1110 1000 1101 xxxx xxxx xxxx 0101 xxxx */
|
||||
/* LDREXD 1110 1000 1101 xxxx xxxx xxxx 0111 xxxx */
|
||||
/* And unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1110_101x[] = {
|
||||
/* Data-processing (shifted register) */
|
||||
|
||||
/* TST 1110 1010 0001 xxxx xxxx 1111 xxxx xxxx */
|
||||
/* TEQ 1110 1010 1001 xxxx xxxx 1111 xxxx xxxx */
|
||||
DECODE_EMULATEX (0xff700f00, 0xea100f00, PROBES_T32_TST,
|
||||
REGS(NOSPPC, 0, 0, 0, NOSPPC)),
|
||||
|
||||
/* CMN 1110 1011 0001 xxxx xxxx 1111 xxxx xxxx */
|
||||
DECODE_OR (0xfff00f00, 0xeb100f00),
|
||||
/* CMP 1110 1011 1011 xxxx xxxx 1111 xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfff00f00, 0xebb00f00, PROBES_T32_TST,
|
||||
REGS(NOPC, 0, 0, 0, NOSPPC)),
|
||||
|
||||
/* MOV 1110 1010 010x 1111 xxxx xxxx xxxx xxxx */
|
||||
/* MVN 1110 1010 011x 1111 xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xffcf0000, 0xea4f0000, PROBES_T32_MOV,
|
||||
REGS(0, 0, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
/* ??? 1110 1010 101x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ??? 1110 1010 111x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xffa00000, 0xeaa00000),
|
||||
/* ??? 1110 1011 001x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xffe00000, 0xeb200000),
|
||||
/* ??? 1110 1011 100x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xffe00000, 0xeb800000),
|
||||
/* ??? 1110 1011 111x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xffe00000, 0xebe00000),
|
||||
|
||||
/* ADD/SUB SP, SP, Rm, LSL #0..3 */
|
||||
/* 1110 1011 x0xx 1101 x000 1101 xx00 xxxx */
|
||||
DECODE_EMULATEX (0xff4f7f30, 0xeb0d0d00, PROBES_T32_ADDSUB,
|
||||
REGS(SP, 0, SP, 0, NOSPPC)),
|
||||
|
||||
/* ADD/SUB SP, SP, Rm, shift */
|
||||
/* 1110 1011 x0xx 1101 xxxx 1101 xxxx xxxx */
|
||||
DECODE_REJECT (0xff4f0f00, 0xeb0d0d00),
|
||||
|
||||
/* ADD/SUB Rd, SP, Rm, shift */
|
||||
/* 1110 1011 x0xx 1101 xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xff4f0000, 0xeb0d0000, PROBES_T32_ADDSUB,
|
||||
REGS(SP, 0, NOPC, 0, NOSPPC)),
|
||||
|
||||
/* AND 1110 1010 000x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* BIC 1110 1010 001x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ORR 1110 1010 010x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ORN 1110 1010 011x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* EOR 1110 1010 100x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* PKH 1110 1010 110x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ADD 1110 1011 000x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* ADC 1110 1011 010x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* SBC 1110 1011 011x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* SUB 1110 1011 101x xxxx xxxx xxxx xxxx xxxx */
|
||||
/* RSB 1110 1011 110x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfe000000, 0xea000000, PROBES_T32_LOGICAL,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_0x0x___0[] = {
|
||||
/* Data-processing (modified immediate) */
|
||||
|
||||
/* TST 1111 0x00 0001 xxxx 0xxx 1111 xxxx xxxx */
|
||||
/* TEQ 1111 0x00 1001 xxxx 0xxx 1111 xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfb708f00, 0xf0100f00, PROBES_T32_TST,
|
||||
REGS(NOSPPC, 0, 0, 0, 0)),
|
||||
|
||||
/* CMN 1111 0x01 0001 xxxx 0xxx 1111 xxxx xxxx */
|
||||
DECODE_OR (0xfbf08f00, 0xf1100f00),
|
||||
/* CMP 1111 0x01 1011 xxxx 0xxx 1111 xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbf08f00, 0xf1b00f00, PROBES_T32_CMP,
|
||||
REGS(NOPC, 0, 0, 0, 0)),
|
||||
|
||||
/* MOV 1111 0x00 010x 1111 0xxx xxxx xxxx xxxx */
|
||||
/* MVN 1111 0x00 011x 1111 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbcf8000, 0xf04f0000, PROBES_T32_MOV,
|
||||
REGS(0, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* ??? 1111 0x00 101x xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfbe08000, 0xf0a00000),
|
||||
/* ??? 1111 0x00 110x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* ??? 1111 0x00 111x xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfbc08000, 0xf0c00000),
|
||||
/* ??? 1111 0x01 001x xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfbe08000, 0xf1200000),
|
||||
/* ??? 1111 0x01 100x xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfbe08000, 0xf1800000),
|
||||
/* ??? 1111 0x01 111x xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfbe08000, 0xf1e00000),
|
||||
|
||||
/* ADD Rd, SP, #imm 1111 0x01 000x 1101 0xxx xxxx xxxx xxxx */
|
||||
/* SUB Rd, SP, #imm 1111 0x01 101x 1101 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfb4f8000, 0xf10d0000, PROBES_T32_ADDSUB,
|
||||
REGS(SP, 0, NOPC, 0, 0)),
|
||||
|
||||
/* AND 1111 0x00 000x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* BIC 1111 0x00 001x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* ORR 1111 0x00 010x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* ORN 1111 0x00 011x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* EOR 1111 0x00 100x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* ADD 1111 0x01 000x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* ADC 1111 0x01 010x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* SBC 1111 0x01 011x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* SUB 1111 0x01 101x xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* RSB 1111 0x01 110x xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfa008000, 0xf0000000, PROBES_T32_LOGICAL,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, 0)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_0x1x___0[] = {
|
||||
/* Data-processing (plain binary immediate) */
|
||||
|
||||
/* ADDW Rd, PC, #imm 1111 0x10 0000 1111 0xxx xxxx xxxx xxxx */
|
||||
DECODE_OR (0xfbff8000, 0xf20f0000),
|
||||
/* SUBW Rd, PC, #imm 1111 0x10 1010 1111 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbff8000, 0xf2af0000, PROBES_T32_ADDWSUBW_PC,
|
||||
REGS(PC, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* ADDW SP, SP, #imm 1111 0x10 0000 1101 0xxx 1101 xxxx xxxx */
|
||||
DECODE_OR (0xfbff8f00, 0xf20d0d00),
|
||||
/* SUBW SP, SP, #imm 1111 0x10 1010 1101 0xxx 1101 xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbff8f00, 0xf2ad0d00, PROBES_T32_ADDWSUBW,
|
||||
REGS(SP, 0, SP, 0, 0)),
|
||||
|
||||
/* ADDW 1111 0x10 0000 xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_OR (0xfbf08000, 0xf2000000),
|
||||
/* SUBW 1111 0x10 1010 xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbf08000, 0xf2a00000, PROBES_T32_ADDWSUBW,
|
||||
REGS(NOPCX, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* MOVW 1111 0x10 0100 xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* MOVT 1111 0x10 1100 xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfb708000, 0xf2400000, PROBES_T32_MOVW,
|
||||
REGS(0, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* SSAT16 1111 0x11 0010 xxxx 0000 xxxx 00xx xxxx */
|
||||
/* SSAT 1111 0x11 00x0 xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* USAT16 1111 0x11 1010 xxxx 0000 xxxx 00xx xxxx */
|
||||
/* USAT 1111 0x11 10x0 xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfb508000, 0xf3000000, PROBES_T32_SAT,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* SFBX 1111 0x11 0100 xxxx 0xxx xxxx xxxx xxxx */
|
||||
/* UFBX 1111 0x11 1100 xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfb708000, 0xf3400000, PROBES_T32_BITFIELD,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* BFC 1111 0x11 0110 1111 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbff8000, 0xf36f0000, PROBES_T32_BITFIELD,
|
||||
REGS(0, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/* BFI 1111 0x11 0110 xxxx 0xxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfbf08000, 0xf3600000, PROBES_T32_BITFIELD,
|
||||
REGS(NOSPPCX, 0, NOSPPC, 0, 0)),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_0xxx___1[] = {
|
||||
/* Branches and miscellaneous control */
|
||||
|
||||
/* 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, PROBES_T32_SEV),
|
||||
/* 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, PROBES_T32_WFE),
|
||||
|
||||
/* MRS Rd, CPSR 1111 0011 1110 xxxx 10x0 xxxx xxxx xxxx */
|
||||
DECODE_SIMULATEX(0xfff0d000, 0xf3e08000, PROBES_T32_MRS,
|
||||
REGS(0, 0, NOSPPC, 0, 0)),
|
||||
|
||||
/*
|
||||
* Unsupported instructions
|
||||
* 1111 0x11 1xxx xxxx 10x0 xxxx xxxx xxxx
|
||||
*
|
||||
* MSR 1111 0011 100x xxxx 10x0 xxxx xxxx xxxx
|
||||
* DBG hint 1111 0011 1010 xxxx 10x0 x000 1111 xxxx
|
||||
* Unallocated hints 1111 0011 1010 xxxx 10x0 x000 xxxx xxxx
|
||||
* CPS 1111 0011 1010 xxxx 10x0 xxxx xxxx xxxx
|
||||
* CLREX/DSB/DMB/ISB 1111 0011 1011 xxxx 10x0 xxxx xxxx xxxx
|
||||
* BXJ 1111 0011 1100 xxxx 10x0 xxxx xxxx xxxx
|
||||
* SUBS PC,LR,#<imm8> 1111 0011 1101 xxxx 10x0 xxxx xxxx xxxx
|
||||
* MRS Rd, SPSR 1111 0011 1111 xxxx 10x0 xxxx xxxx xxxx
|
||||
* SMC 1111 0111 1111 xxxx 1000 xxxx xxxx xxxx
|
||||
* UNDEFINED 1111 0111 1111 xxxx 1010 xxxx xxxx xxxx
|
||||
* ??? 1111 0111 1xxx xxxx 1010 xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_REJECT (0xfb80d000, 0xf3808000),
|
||||
|
||||
/* Bcc 1111 0xxx xxxx xxxx 10x0 xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0xf800d000, 0xf0008000, PROBES_T32_BRANCH_COND),
|
||||
|
||||
/* BLX 1111 0xxx xxxx xxxx 11x0 xxxx xxxx xxx0 */
|
||||
DECODE_OR (0xf800d001, 0xf000c000),
|
||||
/* B 1111 0xxx xxxx xxxx 10x1 xxxx xxxx xxxx */
|
||||
/* BL 1111 0xxx xxxx xxxx 11x1 xxxx xxxx xxxx */
|
||||
DECODE_SIMULATE (0xf8009000, 0xf0009000, PROBES_T32_BRANCH),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_100x_x0x1__1111[] = {
|
||||
/* Memory hints */
|
||||
|
||||
/* PLD (literal) 1111 1000 x001 1111 1111 xxxx xxxx xxxx */
|
||||
/* PLI (literal) 1111 1001 x001 1111 1111 xxxx xxxx xxxx */
|
||||
DECODE_SIMULATE (0xfe7ff000, 0xf81ff000, PROBES_T32_PLDI),
|
||||
|
||||
/* PLD{W} (immediate) 1111 1000 10x1 xxxx 1111 xxxx xxxx xxxx */
|
||||
DECODE_OR (0xffd0f000, 0xf890f000),
|
||||
/* PLD{W} (immediate) 1111 1000 00x1 xxxx 1111 1100 xxxx xxxx */
|
||||
DECODE_OR (0xffd0ff00, 0xf810fc00),
|
||||
/* 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, PROBES_T32_PLDI,
|
||||
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, PROBES_T32_PLDI,
|
||||
REGS(NOPCX, 0, 0, 0, NOSPPC)),
|
||||
|
||||
/* Other unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_100x[] = {
|
||||
/* Store/Load single data item */
|
||||
|
||||
/* ??? 1111 100x x11x xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfe600000, 0xf8600000),
|
||||
|
||||
/* ??? 1111 1001 0101 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xfff00000, 0xf9500000),
|
||||
|
||||
/* ??? 1111 100x 0xxx xxxx xxxx 10x0 xxxx xxxx */
|
||||
DECODE_REJECT (0xfe800d00, 0xf8000800),
|
||||
|
||||
/* STRBT 1111 1000 0000 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* STRHT 1111 1000 0010 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* STRT 1111 1000 0100 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* LDRBT 1111 1000 0001 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* LDRSBT 1111 1001 0001 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* LDRHT 1111 1000 0011 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* LDRSHT 1111 1001 0011 xxxx xxxx 1110 xxxx xxxx */
|
||||
/* LDRT 1111 1000 0101 xxxx xxxx 1110 xxxx xxxx */
|
||||
DECODE_REJECT (0xfe800f00, 0xf8000e00),
|
||||
|
||||
/* STR{,B,H} Rn,[PC...] 1111 1000 xxx0 1111 xxxx xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xff1f0000, 0xf80f0000),
|
||||
|
||||
/* STR{,B,H} PC,[Rn...] 1111 1000 xxx0 xxxx 1111 xxxx xxxx xxxx */
|
||||
DECODE_REJECT (0xff10f000, 0xf800f000),
|
||||
|
||||
/* LDR (literal) 1111 1000 x101 1111 xxxx xxxx xxxx xxxx */
|
||||
DECODE_SIMULATEX(0xff7f0000, 0xf85f0000, PROBES_T32_LDR_LIT,
|
||||
REGS(PC, ANY, 0, 0, 0)),
|
||||
|
||||
/* STR (immediate) 1111 1000 0100 xxxx xxxx 1xxx xxxx xxxx */
|
||||
/* LDR (immediate) 1111 1000 0101 xxxx xxxx 1xxx xxxx xxxx */
|
||||
DECODE_OR (0xffe00800, 0xf8400800),
|
||||
/* STR (immediate) 1111 1000 1100 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDR (immediate) 1111 1000 1101 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xffe00000, 0xf8c00000, PROBES_T32_LDRSTR,
|
||||
REGS(NOPCX, ANY, 0, 0, 0)),
|
||||
|
||||
/* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
|
||||
/* LDR (register) 1111 1000 0101 xxxx xxxx 0000 00xx xxxx */
|
||||
DECODE_EMULATEX (0xffe00fc0, 0xf8400000, PROBES_T32_LDRSTR,
|
||||
REGS(NOPCX, ANY, 0, 0, NOSPPC)),
|
||||
|
||||
/* LDRB (literal) 1111 1000 x001 1111 xxxx xxxx xxxx xxxx */
|
||||
/* LDRSB (literal) 1111 1001 x001 1111 xxxx xxxx xxxx xxxx */
|
||||
/* LDRH (literal) 1111 1000 x011 1111 xxxx xxxx xxxx xxxx */
|
||||
/* LDRSH (literal) 1111 1001 x011 1111 xxxx xxxx xxxx xxxx */
|
||||
DECODE_SIMULATEX(0xfe5f0000, 0xf81f0000, PROBES_T32_LDR_LIT,
|
||||
REGS(PC, NOSPPCX, 0, 0, 0)),
|
||||
|
||||
/* STRB (immediate) 1111 1000 0000 xxxx xxxx 1xxx xxxx xxxx */
|
||||
/* STRH (immediate) 1111 1000 0010 xxxx xxxx 1xxx xxxx xxxx */
|
||||
/* LDRB (immediate) 1111 1000 0001 xxxx xxxx 1xxx xxxx xxxx */
|
||||
/* LDRSB (immediate) 1111 1001 0001 xxxx xxxx 1xxx xxxx xxxx */
|
||||
/* LDRH (immediate) 1111 1000 0011 xxxx xxxx 1xxx xxxx xxxx */
|
||||
/* LDRSH (immediate) 1111 1001 0011 xxxx xxxx 1xxx xxxx xxxx */
|
||||
DECODE_OR (0xfec00800, 0xf8000800),
|
||||
/* STRB (immediate) 1111 1000 1000 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* STRH (immediate) 1111 1000 1010 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRB (immediate) 1111 1000 1001 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRSB (immediate) 1111 1001 1001 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRH (immediate) 1111 1000 1011 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* LDRSH (immediate) 1111 1001 1011 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0xfec00000, 0xf8800000, PROBES_T32_LDRSTR,
|
||||
REGS(NOPCX, NOSPPCX, 0, 0, 0)),
|
||||
|
||||
/* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
|
||||
/* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
|
||||
/* LDRB (register) 1111 1000 0001 xxxx xxxx 0000 00xx xxxx */
|
||||
/* LDRSB (register) 1111 1001 0001 xxxx xxxx 0000 00xx xxxx */
|
||||
/* LDRH (register) 1111 1000 0011 xxxx xxxx 0000 00xx xxxx */
|
||||
/* LDRSH (register) 1111 1001 0011 xxxx xxxx 0000 00xx xxxx */
|
||||
DECODE_EMULATEX (0xfe800fc0, 0xf8000000, PROBES_T32_LDRSTR,
|
||||
REGS(NOPCX, NOSPPCX, 0, 0, NOSPPC)),
|
||||
|
||||
/* Other unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_1010___1111[] = {
|
||||
/* Data-processing (register) */
|
||||
|
||||
/* ??? 1111 1010 011x xxxx 1111 xxxx 1xxx xxxx */
|
||||
DECODE_REJECT (0xffe0f080, 0xfa60f080),
|
||||
|
||||
/* SXTH 1111 1010 0000 1111 1111 xxxx 1xxx xxxx */
|
||||
/* UXTH 1111 1010 0001 1111 1111 xxxx 1xxx xxxx */
|
||||
/* SXTB16 1111 1010 0010 1111 1111 xxxx 1xxx xxxx */
|
||||
/* UXTB16 1111 1010 0011 1111 1111 xxxx 1xxx xxxx */
|
||||
/* SXTB 1111 1010 0100 1111 1111 xxxx 1xxx xxxx */
|
||||
/* UXTB 1111 1010 0101 1111 1111 xxxx 1xxx xxxx */
|
||||
DECODE_EMULATEX (0xff8ff080, 0xfa0ff080, PROBES_T32_SIGN_EXTEND,
|
||||
REGS(0, 0, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
|
||||
/* ??? 1111 1010 1xxx xxxx 1111 xxxx 0x11 xxxx */
|
||||
DECODE_REJECT (0xff80f0b0, 0xfa80f030),
|
||||
/* ??? 1111 1010 1x11 xxxx 1111 xxxx 0xxx xxxx */
|
||||
DECODE_REJECT (0xffb0f080, 0xfab0f000),
|
||||
|
||||
/* SADD16 1111 1010 1001 xxxx 1111 xxxx 0000 xxxx */
|
||||
/* SASX 1111 1010 1010 xxxx 1111 xxxx 0000 xxxx */
|
||||
/* SSAX 1111 1010 1110 xxxx 1111 xxxx 0000 xxxx */
|
||||
/* SSUB16 1111 1010 1101 xxxx 1111 xxxx 0000 xxxx */
|
||||
/* SADD8 1111 1010 1000 xxxx 1111 xxxx 0000 xxxx */
|
||||
/* SSUB8 1111 1010 1100 xxxx 1111 xxxx 0000 xxxx */
|
||||
|
||||
/* QADD16 1111 1010 1001 xxxx 1111 xxxx 0001 xxxx */
|
||||
/* QASX 1111 1010 1010 xxxx 1111 xxxx 0001 xxxx */
|
||||
/* QSAX 1111 1010 1110 xxxx 1111 xxxx 0001 xxxx */
|
||||
/* QSUB16 1111 1010 1101 xxxx 1111 xxxx 0001 xxxx */
|
||||
/* QADD8 1111 1010 1000 xxxx 1111 xxxx 0001 xxxx */
|
||||
/* QSUB8 1111 1010 1100 xxxx 1111 xxxx 0001 xxxx */
|
||||
|
||||
/* SHADD16 1111 1010 1001 xxxx 1111 xxxx 0010 xxxx */
|
||||
/* SHASX 1111 1010 1010 xxxx 1111 xxxx 0010 xxxx */
|
||||
/* SHSAX 1111 1010 1110 xxxx 1111 xxxx 0010 xxxx */
|
||||
/* SHSUB16 1111 1010 1101 xxxx 1111 xxxx 0010 xxxx */
|
||||
/* SHADD8 1111 1010 1000 xxxx 1111 xxxx 0010 xxxx */
|
||||
/* SHSUB8 1111 1010 1100 xxxx 1111 xxxx 0010 xxxx */
|
||||
|
||||
/* UADD16 1111 1010 1001 xxxx 1111 xxxx 0100 xxxx */
|
||||
/* UASX 1111 1010 1010 xxxx 1111 xxxx 0100 xxxx */
|
||||
/* USAX 1111 1010 1110 xxxx 1111 xxxx 0100 xxxx */
|
||||
/* USUB16 1111 1010 1101 xxxx 1111 xxxx 0100 xxxx */
|
||||
/* UADD8 1111 1010 1000 xxxx 1111 xxxx 0100 xxxx */
|
||||
/* USUB8 1111 1010 1100 xxxx 1111 xxxx 0100 xxxx */
|
||||
|
||||
/* UQADD16 1111 1010 1001 xxxx 1111 xxxx 0101 xxxx */
|
||||
/* UQASX 1111 1010 1010 xxxx 1111 xxxx 0101 xxxx */
|
||||
/* UQSAX 1111 1010 1110 xxxx 1111 xxxx 0101 xxxx */
|
||||
/* UQSUB16 1111 1010 1101 xxxx 1111 xxxx 0101 xxxx */
|
||||
/* UQADD8 1111 1010 1000 xxxx 1111 xxxx 0101 xxxx */
|
||||
/* UQSUB8 1111 1010 1100 xxxx 1111 xxxx 0101 xxxx */
|
||||
|
||||
/* UHADD16 1111 1010 1001 xxxx 1111 xxxx 0110 xxxx */
|
||||
/* UHASX 1111 1010 1010 xxxx 1111 xxxx 0110 xxxx */
|
||||
/* UHSAX 1111 1010 1110 xxxx 1111 xxxx 0110 xxxx */
|
||||
/* UHSUB16 1111 1010 1101 xxxx 1111 xxxx 0110 xxxx */
|
||||
/* UHADD8 1111 1010 1000 xxxx 1111 xxxx 0110 xxxx */
|
||||
/* UHSUB8 1111 1010 1100 xxxx 1111 xxxx 0110 xxxx */
|
||||
DECODE_OR (0xff80f080, 0xfa80f000),
|
||||
|
||||
/* SXTAH 1111 1010 0000 xxxx 1111 xxxx 1xxx xxxx */
|
||||
/* UXTAH 1111 1010 0001 xxxx 1111 xxxx 1xxx xxxx */
|
||||
/* SXTAB16 1111 1010 0010 xxxx 1111 xxxx 1xxx xxxx */
|
||||
/* UXTAB16 1111 1010 0011 xxxx 1111 xxxx 1xxx xxxx */
|
||||
/* SXTAB 1111 1010 0100 xxxx 1111 xxxx 1xxx xxxx */
|
||||
/* UXTAB 1111 1010 0101 xxxx 1111 xxxx 1xxx xxxx */
|
||||
DECODE_OR (0xff80f080, 0xfa00f080),
|
||||
|
||||
/* QADD 1111 1010 1000 xxxx 1111 xxxx 1000 xxxx */
|
||||
/* QDADD 1111 1010 1000 xxxx 1111 xxxx 1001 xxxx */
|
||||
/* QSUB 1111 1010 1000 xxxx 1111 xxxx 1010 xxxx */
|
||||
/* QDSUB 1111 1010 1000 xxxx 1111 xxxx 1011 xxxx */
|
||||
DECODE_OR (0xfff0f0c0, 0xfa80f080),
|
||||
|
||||
/* SEL 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
|
||||
DECODE_OR (0xfff0f0f0, 0xfaa0f080),
|
||||
|
||||
/* LSL 1111 1010 000x xxxx 1111 xxxx 0000 xxxx */
|
||||
/* LSR 1111 1010 001x xxxx 1111 xxxx 0000 xxxx */
|
||||
/* ASR 1111 1010 010x xxxx 1111 xxxx 0000 xxxx */
|
||||
/* ROR 1111 1010 011x xxxx 1111 xxxx 0000 xxxx */
|
||||
DECODE_EMULATEX (0xff80f0f0, 0xfa00f000, PROBES_T32_MEDIA,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
/* CLZ 1111 1010 1010 xxxx 1111 xxxx 1000 xxxx */
|
||||
DECODE_OR (0xfff0f0f0, 0xfab0f080),
|
||||
|
||||
/* REV 1111 1010 1001 xxxx 1111 xxxx 1000 xxxx */
|
||||
/* REV16 1111 1010 1001 xxxx 1111 xxxx 1001 xxxx */
|
||||
/* RBIT 1111 1010 1001 xxxx 1111 xxxx 1010 xxxx */
|
||||
/* REVSH 1111 1010 1001 xxxx 1111 xxxx 1011 xxxx */
|
||||
DECODE_EMULATEX (0xfff0f0c0, 0xfa90f080, PROBES_T32_REVERSE,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, SAMEAS16)),
|
||||
|
||||
/* Other unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_1011_0[] = {
|
||||
/* Multiply, multiply accumulate, and absolute difference */
|
||||
|
||||
/* ??? 1111 1011 0000 xxxx 1111 xxxx 0001 xxxx */
|
||||
DECODE_REJECT (0xfff0f0f0, 0xfb00f010),
|
||||
/* ??? 1111 1011 0111 xxxx 1111 xxxx 0001 xxxx */
|
||||
DECODE_REJECT (0xfff0f0f0, 0xfb70f010),
|
||||
|
||||
/* SMULxy 1111 1011 0001 xxxx 1111 xxxx 00xx xxxx */
|
||||
DECODE_OR (0xfff0f0c0, 0xfb10f000),
|
||||
/* MUL 1111 1011 0000 xxxx 1111 xxxx 0000 xxxx */
|
||||
/* SMUAD{X} 1111 1011 0010 xxxx 1111 xxxx 000x xxxx */
|
||||
/* SMULWy 1111 1011 0011 xxxx 1111 xxxx 000x xxxx */
|
||||
/* SMUSD{X} 1111 1011 0100 xxxx 1111 xxxx 000x xxxx */
|
||||
/* SMMUL{R} 1111 1011 0101 xxxx 1111 xxxx 000x xxxx */
|
||||
/* USAD8 1111 1011 0111 xxxx 1111 xxxx 0000 xxxx */
|
||||
DECODE_EMULATEX (0xff80f0e0, 0xfb00f000, PROBES_T32_MUL_ADD,
|
||||
REGS(NOSPPC, 0, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
/* ??? 1111 1011 0111 xxxx xxxx xxxx 0001 xxxx */
|
||||
DECODE_REJECT (0xfff000f0, 0xfb700010),
|
||||
|
||||
/* SMLAxy 1111 1011 0001 xxxx xxxx xxxx 00xx xxxx */
|
||||
DECODE_OR (0xfff000c0, 0xfb100000),
|
||||
/* MLA 1111 1011 0000 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* MLS 1111 1011 0000 xxxx xxxx xxxx 0001 xxxx */
|
||||
/* SMLAD{X} 1111 1011 0010 xxxx xxxx xxxx 000x xxxx */
|
||||
/* SMLAWy 1111 1011 0011 xxxx xxxx xxxx 000x xxxx */
|
||||
/* SMLSD{X} 1111 1011 0100 xxxx xxxx xxxx 000x xxxx */
|
||||
/* SMMLA{R} 1111 1011 0101 xxxx xxxx xxxx 000x xxxx */
|
||||
/* SMMLS{R} 1111 1011 0110 xxxx xxxx xxxx 000x xxxx */
|
||||
/* USADA8 1111 1011 0111 xxxx xxxx xxxx 0000 xxxx */
|
||||
DECODE_EMULATEX (0xff8000c0, 0xfb000000, PROBES_T32_MUL_ADD2,
|
||||
REGS(NOSPPC, NOSPPCX, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
/* Other unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
static const union decode_item t32_table_1111_1011_1[] = {
|
||||
/* Long multiply, long multiply accumulate, and divide */
|
||||
|
||||
/* UMAAL 1111 1011 1110 xxxx xxxx xxxx 0110 xxxx */
|
||||
DECODE_OR (0xfff000f0, 0xfbe00060),
|
||||
/* SMLALxy 1111 1011 1100 xxxx xxxx xxxx 10xx xxxx */
|
||||
DECODE_OR (0xfff000c0, 0xfbc00080),
|
||||
/* SMLALD{X} 1111 1011 1100 xxxx xxxx xxxx 110x xxxx */
|
||||
/* SMLSLD{X} 1111 1011 1101 xxxx xxxx xxxx 110x xxxx */
|
||||
DECODE_OR (0xffe000e0, 0xfbc000c0),
|
||||
/* SMULL 1111 1011 1000 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* UMULL 1111 1011 1010 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* SMLAL 1111 1011 1100 xxxx xxxx xxxx 0000 xxxx */
|
||||
/* UMLAL 1111 1011 1110 xxxx xxxx xxxx 0000 xxxx */
|
||||
DECODE_EMULATEX (0xff9000f0, 0xfb800000, PROBES_T32_MUL_ADD_LONG,
|
||||
REGS(NOSPPC, NOSPPC, NOSPPC, 0, NOSPPC)),
|
||||
|
||||
/* SDIV 1111 1011 1001 xxxx xxxx xxxx 1111 xxxx */
|
||||
/* UDIV 1111 1011 1011 xxxx xxxx xxxx 1111 xxxx */
|
||||
/* Other unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
const union decode_item probes_decode_thumb32_table[] = {
|
||||
|
||||
/*
|
||||
* Load/store multiple instructions
|
||||
* 1110 100x x0xx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfe400000, 0xe8000000, t32_table_1110_100x_x0xx),
|
||||
|
||||
/*
|
||||
* Load/store dual, load/store exclusive, table branch
|
||||
* 1110 100x x1xx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfe400000, 0xe8400000, t32_table_1110_100x_x1xx),
|
||||
|
||||
/*
|
||||
* Data-processing (shifted register)
|
||||
* 1110 101x xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfe000000, 0xea000000, t32_table_1110_101x),
|
||||
|
||||
/*
|
||||
* Coprocessor instructions
|
||||
* 1110 11xx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_REJECT (0xfc000000, 0xec000000),
|
||||
|
||||
/*
|
||||
* Data-processing (modified immediate)
|
||||
* 1111 0x0x xxxx xxxx 0xxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfa008000, 0xf0000000, t32_table_1111_0x0x___0),
|
||||
|
||||
/*
|
||||
* Data-processing (plain binary immediate)
|
||||
* 1111 0x1x xxxx xxxx 0xxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfa008000, 0xf2000000, t32_table_1111_0x1x___0),
|
||||
|
||||
/*
|
||||
* Branches and miscellaneous control
|
||||
* 1111 0xxx xxxx xxxx 1xxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xf8008000, 0xf0008000, t32_table_1111_0xxx___1),
|
||||
|
||||
/*
|
||||
* Advanced SIMD element or structure load/store instructions
|
||||
* 1111 1001 xxx0 xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_REJECT (0xff100000, 0xf9000000),
|
||||
|
||||
/*
|
||||
* Memory hints
|
||||
* 1111 100x x0x1 xxxx 1111 xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfe50f000, 0xf810f000, t32_table_1111_100x_x0x1__1111),
|
||||
|
||||
/*
|
||||
* Store single data item
|
||||
* 1111 1000 xxx0 xxxx xxxx xxxx xxxx xxxx
|
||||
* Load single data items
|
||||
* 1111 100x xxx1 xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xfe000000, 0xf8000000, t32_table_1111_100x),
|
||||
|
||||
/*
|
||||
* Data-processing (register)
|
||||
* 1111 1010 xxxx xxxx 1111 xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xff00f000, 0xfa00f000, t32_table_1111_1010___1111),
|
||||
|
||||
/*
|
||||
* Multiply, multiply accumulate, and absolute difference
|
||||
* 1111 1011 0xxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xff800000, 0xfb000000, t32_table_1111_1011_0),
|
||||
|
||||
/*
|
||||
* Long multiply, long multiply accumulate, and divide
|
||||
* 1111 1011 1xxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xff800000, 0xfb800000, t32_table_1111_1011_1),
|
||||
|
||||
/*
|
||||
* Coprocessor instructions
|
||||
* 1111 11xx xxxx xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_END
|
||||
};
|
||||
#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
|
||||
EXPORT_SYMBOL_GPL(probes_decode_thumb32_table);
|
||||
#endif
|
||||
|
||||
static const union decode_item t16_table_1011[] = {
|
||||
/* Miscellaneous 16-bit instructions */
|
||||
|
||||
/* ADD (SP plus immediate) 1011 0000 0xxx xxxx */
|
||||
/* SUB (SP minus immediate) 1011 0000 1xxx xxxx */
|
||||
DECODE_SIMULATE (0xff00, 0xb000, PROBES_T16_ADD_SP),
|
||||
|
||||
/* CBZ 1011 00x1 xxxx xxxx */
|
||||
/* CBNZ 1011 10x1 xxxx xxxx */
|
||||
DECODE_SIMULATE (0xf500, 0xb100, PROBES_T16_CBZ),
|
||||
|
||||
/* SXTH 1011 0010 00xx xxxx */
|
||||
/* SXTB 1011 0010 01xx xxxx */
|
||||
/* UXTH 1011 0010 10xx xxxx */
|
||||
/* UXTB 1011 0010 11xx xxxx */
|
||||
/* REV 1011 1010 00xx xxxx */
|
||||
/* REV16 1011 1010 01xx xxxx */
|
||||
/* ??? 1011 1010 10xx xxxx */
|
||||
/* REVSH 1011 1010 11xx xxxx */
|
||||
DECODE_REJECT (0xffc0, 0xba80),
|
||||
DECODE_EMULATE (0xf500, 0xb000, PROBES_T16_SIGN_EXTEND),
|
||||
|
||||
/* PUSH 1011 010x xxxx xxxx */
|
||||
DECODE_CUSTOM (0xfe00, 0xb400, PROBES_T16_PUSH),
|
||||
/* POP 1011 110x xxxx xxxx */
|
||||
DECODE_CUSTOM (0xfe00, 0xbc00, PROBES_T16_POP),
|
||||
|
||||
/*
|
||||
* If-Then, and hints
|
||||
* 1011 1111 xxxx xxxx
|
||||
*/
|
||||
|
||||
/* YIELD 1011 1111 0001 0000 */
|
||||
DECODE_OR (0xffff, 0xbf10),
|
||||
/* SEV 1011 1111 0100 0000 */
|
||||
DECODE_EMULATE (0xffff, 0xbf40, PROBES_T16_SEV),
|
||||
/* NOP 1011 1111 0000 0000 */
|
||||
/* WFE 1011 1111 0010 0000 */
|
||||
/* WFI 1011 1111 0011 0000 */
|
||||
DECODE_SIMULATE (0xffcf, 0xbf00, PROBES_T16_WFE),
|
||||
/* Unassigned hints 1011 1111 xxxx 0000 */
|
||||
DECODE_REJECT (0xff0f, 0xbf00),
|
||||
/* IT 1011 1111 xxxx xxxx */
|
||||
DECODE_CUSTOM (0xff00, 0xbf00, PROBES_T16_IT),
|
||||
|
||||
/* SETEND 1011 0110 010x xxxx */
|
||||
/* CPS 1011 0110 011x xxxx */
|
||||
/* BKPT 1011 1110 xxxx xxxx */
|
||||
/* And unallocated instructions... */
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
const union decode_item probes_decode_thumb16_table[] = {
|
||||
|
||||
/*
|
||||
* Shift (immediate), add, subtract, move, and compare
|
||||
* 00xx xxxx xxxx xxxx
|
||||
*/
|
||||
|
||||
/* CMP (immediate) 0010 1xxx xxxx xxxx */
|
||||
DECODE_EMULATE (0xf800, 0x2800, PROBES_T16_CMP),
|
||||
|
||||
/* ADD (register) 0001 100x xxxx xxxx */
|
||||
/* SUB (register) 0001 101x xxxx xxxx */
|
||||
/* LSL (immediate) 0000 0xxx xxxx xxxx */
|
||||
/* LSR (immediate) 0000 1xxx xxxx xxxx */
|
||||
/* ASR (immediate) 0001 0xxx xxxx xxxx */
|
||||
/* ADD (immediate, Thumb) 0001 110x xxxx xxxx */
|
||||
/* SUB (immediate, Thumb) 0001 111x xxxx xxxx */
|
||||
/* MOV (immediate) 0010 0xxx xxxx xxxx */
|
||||
/* ADD (immediate, Thumb) 0011 0xxx xxxx xxxx */
|
||||
/* SUB (immediate, Thumb) 0011 1xxx xxxx xxxx */
|
||||
DECODE_EMULATE (0xc000, 0x0000, PROBES_T16_ADDSUB),
|
||||
|
||||
/*
|
||||
* 16-bit Thumb data-processing instructions
|
||||
* 0100 00xx xxxx xxxx
|
||||
*/
|
||||
|
||||
/* TST (register) 0100 0010 00xx xxxx */
|
||||
DECODE_EMULATE (0xffc0, 0x4200, PROBES_T16_CMP),
|
||||
/* CMP (register) 0100 0010 10xx xxxx */
|
||||
/* CMN (register) 0100 0010 11xx xxxx */
|
||||
DECODE_EMULATE (0xff80, 0x4280, PROBES_T16_CMP),
|
||||
/* AND (register) 0100 0000 00xx xxxx */
|
||||
/* EOR (register) 0100 0000 01xx xxxx */
|
||||
/* LSL (register) 0100 0000 10xx xxxx */
|
||||
/* LSR (register) 0100 0000 11xx xxxx */
|
||||
/* ASR (register) 0100 0001 00xx xxxx */
|
||||
/* ADC (register) 0100 0001 01xx xxxx */
|
||||
/* SBC (register) 0100 0001 10xx xxxx */
|
||||
/* ROR (register) 0100 0001 11xx xxxx */
|
||||
/* RSB (immediate) 0100 0010 01xx xxxx */
|
||||
/* ORR (register) 0100 0011 00xx xxxx */
|
||||
/* MUL 0100 0011 00xx xxxx */
|
||||
/* BIC (register) 0100 0011 10xx xxxx */
|
||||
/* MVN (register) 0100 0011 10xx xxxx */
|
||||
DECODE_EMULATE (0xfc00, 0x4000, PROBES_T16_LOGICAL),
|
||||
|
||||
/*
|
||||
* Special data instructions and branch and exchange
|
||||
* 0100 01xx xxxx xxxx
|
||||
*/
|
||||
|
||||
/* BLX pc 0100 0111 1111 1xxx */
|
||||
DECODE_REJECT (0xfff8, 0x47f8),
|
||||
|
||||
/* BX (register) 0100 0111 0xxx xxxx */
|
||||
/* BLX (register) 0100 0111 1xxx xxxx */
|
||||
DECODE_SIMULATE (0xff00, 0x4700, PROBES_T16_BLX),
|
||||
|
||||
/* ADD pc, pc 0100 0100 1111 1111 */
|
||||
DECODE_REJECT (0xffff, 0x44ff),
|
||||
|
||||
/* ADD (register) 0100 0100 xxxx xxxx */
|
||||
/* CMP (register) 0100 0101 xxxx xxxx */
|
||||
/* MOV (register) 0100 0110 xxxx xxxx */
|
||||
DECODE_CUSTOM (0xfc00, 0x4400, PROBES_T16_HIREGOPS),
|
||||
|
||||
/*
|
||||
* Load from Literal Pool
|
||||
* LDR (literal) 0100 1xxx xxxx xxxx
|
||||
*/
|
||||
DECODE_SIMULATE (0xf800, 0x4800, PROBES_T16_LDR_LIT),
|
||||
|
||||
/*
|
||||
* 16-bit Thumb Load/store instructions
|
||||
* 0101 xxxx xxxx xxxx
|
||||
* 011x xxxx xxxx xxxx
|
||||
* 100x xxxx xxxx xxxx
|
||||
*/
|
||||
|
||||
/* STR (register) 0101 000x xxxx xxxx */
|
||||
/* STRH (register) 0101 001x xxxx xxxx */
|
||||
/* STRB (register) 0101 010x xxxx xxxx */
|
||||
/* LDRSB (register) 0101 011x xxxx xxxx */
|
||||
/* LDR (register) 0101 100x xxxx xxxx */
|
||||
/* LDRH (register) 0101 101x xxxx xxxx */
|
||||
/* LDRB (register) 0101 110x xxxx xxxx */
|
||||
/* LDRSH (register) 0101 111x xxxx xxxx */
|
||||
/* STR (immediate, Thumb) 0110 0xxx xxxx xxxx */
|
||||
/* LDR (immediate, Thumb) 0110 1xxx xxxx xxxx */
|
||||
/* STRB (immediate, Thumb) 0111 0xxx xxxx xxxx */
|
||||
/* LDRB (immediate, Thumb) 0111 1xxx xxxx xxxx */
|
||||
DECODE_EMULATE (0xc000, 0x4000, PROBES_T16_LDRHSTRH),
|
||||
/* STRH (immediate, Thumb) 1000 0xxx xxxx xxxx */
|
||||
/* LDRH (immediate, Thumb) 1000 1xxx xxxx xxxx */
|
||||
DECODE_EMULATE (0xf000, 0x8000, PROBES_T16_LDRHSTRH),
|
||||
/* STR (immediate, Thumb) 1001 0xxx xxxx xxxx */
|
||||
/* LDR (immediate, Thumb) 1001 1xxx xxxx xxxx */
|
||||
DECODE_SIMULATE (0xf000, 0x9000, PROBES_T16_LDRSTR),
|
||||
|
||||
/*
|
||||
* Generate PC-/SP-relative address
|
||||
* ADR (literal) 1010 0xxx xxxx xxxx
|
||||
* ADD (SP plus immediate) 1010 1xxx xxxx xxxx
|
||||
*/
|
||||
DECODE_SIMULATE (0xf000, 0xa000, PROBES_T16_ADR),
|
||||
|
||||
/*
|
||||
* Miscellaneous 16-bit instructions
|
||||
* 1011 xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_TABLE (0xf000, 0xb000, t16_table_1011),
|
||||
|
||||
/* STM 1100 0xxx xxxx xxxx */
|
||||
/* LDM 1100 1xxx xxxx xxxx */
|
||||
DECODE_EMULATE (0xf000, 0xc000, PROBES_T16_LDMSTM),
|
||||
|
||||
/*
|
||||
* Conditional branch, and Supervisor Call
|
||||
*/
|
||||
|
||||
/* Permanently UNDEFINED 1101 1110 xxxx xxxx */
|
||||
/* SVC 1101 1111 xxxx xxxx */
|
||||
DECODE_REJECT (0xfe00, 0xde00),
|
||||
|
||||
/* Conditional branch 1101 xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0xf000, 0xd000, PROBES_T16_BRANCH_COND),
|
||||
|
||||
/*
|
||||
* Unconditional branch
|
||||
* B 1110 0xxx xxxx xxxx
|
||||
*/
|
||||
DECODE_SIMULATE (0xf800, 0xe000, PROBES_T16_BRANCH),
|
||||
|
||||
DECODE_END
|
||||
};
|
||||
#ifdef CONFIG_ARM_KPROBES_TEST_MODULE
|
||||
EXPORT_SYMBOL_GPL(probes_decode_thumb16_table);
|
||||
#endif
|
||||
|
||||
static unsigned long __kprobes thumb_check_cc(unsigned long cpsr)
|
||||
{
|
||||
if (unlikely(in_it_block(cpsr)))
|
||||
return probes_condition_checks[current_cond(cpsr)](cpsr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __kprobes thumb16_singlestep(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_pc += 2;
|
||||
asi->insn_handler(opcode, asi, regs);
|
||||
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
|
||||
}
|
||||
|
||||
static void __kprobes thumb32_singlestep(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_pc += 4;
|
||||
asi->insn_handler(opcode, asi, regs);
|
||||
regs->ARM_cpsr = it_advance(regs->ARM_cpsr);
|
||||
}
|
||||
|
||||
enum probes_insn __kprobes
|
||||
thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions)
|
||||
{
|
||||
asi->insn_singlestep = thumb16_singlestep;
|
||||
asi->insn_check_cc = thumb_check_cc;
|
||||
return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true,
|
||||
emulate, actions);
|
||||
}
|
||||
|
||||
enum probes_insn __kprobes
|
||||
thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions)
|
||||
{
|
||||
asi->insn_singlestep = thumb32_singlestep;
|
||||
asi->insn_check_cc = thumb_check_cc;
|
||||
return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true,
|
||||
emulate, actions);
|
||||
}
|
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-thumb.h
|
||||
*
|
||||
* Copyright 2013 Linaro Ltd.
|
||||
* Written by: David A. Long
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#ifndef _ARM_KERNEL_PROBES_THUMB_H
|
||||
#define _ARM_KERNEL_PROBES_THUMB_H
|
||||
|
||||
/*
|
||||
* True if current instruction is in an IT block.
|
||||
*/
|
||||
#define in_it_block(cpsr) ((cpsr & 0x06000c00) != 0x00000000)
|
||||
|
||||
/*
|
||||
* Return the condition code to check for the currently executing instruction.
|
||||
* This is in ITSTATE<7:4> which is in CPSR<15:12> but is only valid if
|
||||
* in_it_block returns true.
|
||||
*/
|
||||
#define current_cond(cpsr) ((cpsr >> 12) & 0xf)
|
||||
|
||||
enum probes_t32_action {
|
||||
PROBES_T32_EMULATE_NONE,
|
||||
PROBES_T32_SIMULATE_NOP,
|
||||
PROBES_T32_LDMSTM,
|
||||
PROBES_T32_LDRDSTRD,
|
||||
PROBES_T32_TABLE_BRANCH,
|
||||
PROBES_T32_TST,
|
||||
PROBES_T32_CMP,
|
||||
PROBES_T32_MOV,
|
||||
PROBES_T32_ADDSUB,
|
||||
PROBES_T32_LOGICAL,
|
||||
PROBES_T32_ADDWSUBW_PC,
|
||||
PROBES_T32_ADDWSUBW,
|
||||
PROBES_T32_MOVW,
|
||||
PROBES_T32_SAT,
|
||||
PROBES_T32_BITFIELD,
|
||||
PROBES_T32_SEV,
|
||||
PROBES_T32_WFE,
|
||||
PROBES_T32_MRS,
|
||||
PROBES_T32_BRANCH_COND,
|
||||
PROBES_T32_BRANCH,
|
||||
PROBES_T32_PLDI,
|
||||
PROBES_T32_LDR_LIT,
|
||||
PROBES_T32_LDRSTR,
|
||||
PROBES_T32_SIGN_EXTEND,
|
||||
PROBES_T32_MEDIA,
|
||||
PROBES_T32_REVERSE,
|
||||
PROBES_T32_MUL_ADD,
|
||||
PROBES_T32_MUL_ADD2,
|
||||
PROBES_T32_MUL_ADD_LONG,
|
||||
NUM_PROBES_T32_ACTIONS
|
||||
};
|
||||
|
||||
enum probes_t16_action {
|
||||
PROBES_T16_ADD_SP,
|
||||
PROBES_T16_CBZ,
|
||||
PROBES_T16_SIGN_EXTEND,
|
||||
PROBES_T16_PUSH,
|
||||
PROBES_T16_POP,
|
||||
PROBES_T16_SEV,
|
||||
PROBES_T16_WFE,
|
||||
PROBES_T16_IT,
|
||||
PROBES_T16_CMP,
|
||||
PROBES_T16_ADDSUB,
|
||||
PROBES_T16_LOGICAL,
|
||||
PROBES_T16_BLX,
|
||||
PROBES_T16_HIREGOPS,
|
||||
PROBES_T16_LDR_LIT,
|
||||
PROBES_T16_LDRHSTRH,
|
||||
PROBES_T16_LDRSTR,
|
||||
PROBES_T16_ADR,
|
||||
PROBES_T16_LDMSTM,
|
||||
PROBES_T16_BRANCH_COND,
|
||||
PROBES_T16_BRANCH,
|
||||
NUM_PROBES_T16_ACTIONS
|
||||
};
|
||||
|
||||
extern const union decode_item probes_decode_thumb32_table[];
|
||||
extern const union decode_item probes_decode_thumb16_table[];
|
||||
|
||||
enum probes_insn __kprobes
|
||||
thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions);
|
||||
enum probes_insn __kprobes
|
||||
thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions);
|
||||
|
||||
#endif
|
@@ -1,456 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@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/types.h>
|
||||
#include <asm/system_info.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include "probes.h"
|
||||
|
||||
|
||||
#ifndef find_str_pc_offset
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
int str_pc_offset;
|
||||
|
||||
void __init find_str_pc_offset(void)
|
||||
{
|
||||
int addr, scratch, 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));
|
||||
|
||||
str_pc_offset = ret;
|
||||
}
|
||||
|
||||
#endif /* !find_str_pc_offset */
|
||||
|
||||
|
||||
#ifndef test_load_write_pc_interworking
|
||||
|
||||
bool load_write_pc_interworks;
|
||||
|
||||
void __init test_load_write_pc_interworking(void)
|
||||
{
|
||||
int arch = cpu_architecture();
|
||||
BUG_ON(arch == CPU_ARCH_UNKNOWN);
|
||||
load_write_pc_interworks = arch >= CPU_ARCH_ARMv5T;
|
||||
}
|
||||
|
||||
#endif /* !test_load_write_pc_interworking */
|
||||
|
||||
|
||||
#ifndef test_alu_write_pc_interworking
|
||||
|
||||
bool alu_write_pc_interworks;
|
||||
|
||||
void __init test_alu_write_pc_interworking(void)
|
||||
{
|
||||
int arch = cpu_architecture();
|
||||
BUG_ON(arch == CPU_ARCH_UNKNOWN);
|
||||
alu_write_pc_interworks = arch >= CPU_ARCH_ARMv7;
|
||||
}
|
||||
|
||||
#endif /* !test_alu_write_pc_interworking */
|
||||
|
||||
|
||||
void __init arm_probes_decode_init(void)
|
||||
{
|
||||
find_str_pc_offset();
|
||||
test_load_write_pc_interworking();
|
||||
test_alu_write_pc_interworking();
|
||||
}
|
||||
|
||||
|
||||
static unsigned long __kprobes __check_eq(unsigned long cpsr)
|
||||
{
|
||||
return cpsr & PSR_Z_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_ne(unsigned long cpsr)
|
||||
{
|
||||
return (~cpsr) & PSR_Z_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_cs(unsigned long cpsr)
|
||||
{
|
||||
return cpsr & PSR_C_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_cc(unsigned long cpsr)
|
||||
{
|
||||
return (~cpsr) & PSR_C_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_mi(unsigned long cpsr)
|
||||
{
|
||||
return cpsr & PSR_N_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_pl(unsigned long cpsr)
|
||||
{
|
||||
return (~cpsr) & PSR_N_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_vs(unsigned long cpsr)
|
||||
{
|
||||
return cpsr & PSR_V_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_vc(unsigned long cpsr)
|
||||
{
|
||||
return (~cpsr) & PSR_V_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_hi(unsigned long cpsr)
|
||||
{
|
||||
cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
|
||||
return cpsr & PSR_C_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_ls(unsigned long cpsr)
|
||||
{
|
||||
cpsr &= ~(cpsr >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
|
||||
return (~cpsr) & PSR_C_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_ge(unsigned long cpsr)
|
||||
{
|
||||
cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||
return (~cpsr) & PSR_N_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_lt(unsigned long cpsr)
|
||||
{
|
||||
cpsr ^= (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||
return cpsr & PSR_N_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_gt(unsigned long cpsr)
|
||||
{
|
||||
unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||
temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
|
||||
return (~temp) & PSR_N_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_le(unsigned long cpsr)
|
||||
{
|
||||
unsigned long temp = cpsr ^ (cpsr << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||
temp |= (cpsr << 1); /* PSR_N_BIT |= PSR_Z_BIT */
|
||||
return temp & PSR_N_BIT;
|
||||
}
|
||||
|
||||
static unsigned long __kprobes __check_al(unsigned long cpsr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
probes_check_cc * const probes_condition_checks[16] = {
|
||||
&__check_eq, &__check_ne, &__check_cs, &__check_cc,
|
||||
&__check_mi, &__check_pl, &__check_vs, &__check_vc,
|
||||
&__check_hi, &__check_ls, &__check_ge, &__check_lt,
|
||||
&__check_gt, &__check_le, &__check_al, &__check_al
|
||||
};
|
||||
|
||||
|
||||
void __kprobes probes_simulate_nop(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
void __kprobes probes_emulate_none(probes_opcode_t opcode,
|
||||
struct arch_probes_insn *asi,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
asi->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 probes_opcode_t __kprobes
|
||||
prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool thumb)
|
||||
{
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
if (thumb) {
|
||||
u16 *thumb_insn = (u16 *)asi->insn;
|
||||
/* Thumb bx lr */
|
||||
thumb_insn[1] = __opcode_to_mem_thumb16(0x4770);
|
||||
thumb_insn[2] = __opcode_to_mem_thumb16(0x4770);
|
||||
return insn;
|
||||
}
|
||||
asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */
|
||||
#else
|
||||
asi->insn[1] = __opcode_to_mem_arm(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(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool thumb)
|
||||
{
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
if (thumb) {
|
||||
u16 *ip = (u16 *)asi->insn;
|
||||
if (is_wide_instruction(insn))
|
||||
*ip++ = __opcode_to_mem_thumb16(insn >> 16);
|
||||
*ip++ = __opcode_to_mem_thumb16(insn);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
asi->insn[0] = __opcode_to_mem_arm(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(probes_opcode_t *pinsn, u32 regs, bool modify)
|
||||
{
|
||||
probes_opcode_t insn = *pinsn;
|
||||
probes_opcode_t mask = 0xf; /* Start at least significant nibble */
|
||||
|
||||
for (; regs != 0; regs >>= 4, mask <<= 4) {
|
||||
|
||||
probes_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;
|
||||
}
|
||||
|
||||
if (modify)
|
||||
*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)
|
||||
};
|
||||
|
||||
/*
|
||||
* probes_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
|
||||
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const union decode_item *table, bool thumb,
|
||||
bool emulate, const union decode_action *actions)
|
||||
{
|
||||
const struct decode_header *h = (struct decode_header *)table;
|
||||
const struct decode_header *next;
|
||||
bool matched = false;
|
||||
|
||||
if (emulate)
|
||||
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, emulate))
|
||||
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 actions[d->decoder.action].decoder(insn, asi, h);
|
||||
}
|
||||
|
||||
case DECODE_TYPE_SIMULATE: {
|
||||
struct decode_simulate *d = (struct decode_simulate *)h;
|
||||
asi->insn_handler = actions[d->handler.action].handler;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
case DECODE_TYPE_EMULATE: {
|
||||
struct decode_emulate *d = (struct decode_emulate *)h;
|
||||
|
||||
if (!emulate)
|
||||
return actions[d->handler.action].decoder(insn,
|
||||
asi, h);
|
||||
|
||||
asi->insn_handler = actions[d->handler.action].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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,407 +0,0 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes.h
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
* Some contents moved here from arch/arm/include/asm/kprobes.h 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _ARM_KERNEL_PROBES_H
|
||||
#define _ARM_KERNEL_PROBES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <asm/probes.h>
|
||||
|
||||
void __init arm_probes_decode_init(void);
|
||||
|
||||
extern probes_check_cc * const probes_condition_checks[16];
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 7
|
||||
|
||||
/* str_pc_offset is architecturally defined from ARMv7 onwards */
|
||||
#define str_pc_offset 8
|
||||
#define find_str_pc_offset()
|
||||
|
||||
#else /* __LINUX_ARM_ARCH__ < 7 */
|
||||
|
||||
/* We need a run-time check to determine str_pc_offset */
|
||||
extern int str_pc_offset;
|
||||
void __init find_str_pc_offset(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Update ITSTATE after normal execution of an IT block instruction.
|
||||
*
|
||||
* The 8 IT state bits are split into two parts in CPSR:
|
||||
* ITSTATE<1:0> are in CPSR<26:25>
|
||||
* ITSTATE<7:2> are in CPSR<15:10>
|
||||
*/
|
||||
static inline unsigned long it_advance(unsigned long cpsr)
|
||||
{
|
||||
if ((cpsr & 0x06000400) == 0) {
|
||||
/* ITSTATE<2:0> == 0 means end of IT block, so clear IT state */
|
||||
cpsr &= ~PSR_IT_MASK;
|
||||
} else {
|
||||
/* We need to shift left ITSTATE<4:0> */
|
||||
const unsigned long mask = 0x06001c00; /* Mask ITSTATE<4:0> */
|
||||
unsigned long it = cpsr & mask;
|
||||
it <<= 1;
|
||||
it |= it >> (27 - 10); /* Carry ITSTATE<2> to correct place */
|
||||
it &= mask;
|
||||
cpsr &= ~mask;
|
||||
cpsr |= it;
|
||||
}
|
||||
return cpsr;
|
||||
}
|
||||
|
||||
static inline void __kprobes bx_write_pc(long pcv, struct pt_regs *regs)
|
||||
{
|
||||
long cpsr = regs->ARM_cpsr;
|
||||
if (pcv & 0x1) {
|
||||
cpsr |= PSR_T_BIT;
|
||||
pcv &= ~0x1;
|
||||
} else {
|
||||
cpsr &= ~PSR_T_BIT;
|
||||
pcv &= ~0x2; /* Avoid UNPREDICTABLE address allignment */
|
||||
}
|
||||
regs->ARM_cpsr = cpsr;
|
||||
regs->ARM_pc = pcv;
|
||||
}
|
||||
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 6
|
||||
|
||||
/* Kernels built for >= ARMv6 should never run on <= ARMv5 hardware, so... */
|
||||
#define load_write_pc_interworks true
|
||||
#define test_load_write_pc_interworking()
|
||||
|
||||
#else /* __LINUX_ARM_ARCH__ < 6 */
|
||||
|
||||
/* We need run-time testing to determine if load_write_pc() should interwork. */
|
||||
extern bool load_write_pc_interworks;
|
||||
void __init test_load_write_pc_interworking(void);
|
||||
|
||||
#endif
|
||||
|
||||
static inline void __kprobes load_write_pc(long pcv, struct pt_regs *regs)
|
||||
{
|
||||
if (load_write_pc_interworks)
|
||||
bx_write_pc(pcv, regs);
|
||||
else
|
||||
regs->ARM_pc = pcv;
|
||||
}
|
||||
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 7
|
||||
|
||||
#define alu_write_pc_interworks true
|
||||
#define test_alu_write_pc_interworking()
|
||||
|
||||
#elif __LINUX_ARM_ARCH__ <= 5
|
||||
|
||||
/* Kernels built for <= ARMv5 should never run on >= ARMv6 hardware, so... */
|
||||
#define alu_write_pc_interworks false
|
||||
#define test_alu_write_pc_interworking()
|
||||
|
||||
#else /* __LINUX_ARM_ARCH__ == 6 */
|
||||
|
||||
/* We could be an ARMv6 binary on ARMv7 hardware so we need a run-time check. */
|
||||
extern bool alu_write_pc_interworks;
|
||||
void __init test_alu_write_pc_interworking(void);
|
||||
|
||||
#endif /* __LINUX_ARM_ARCH__ == 6 */
|
||||
|
||||
static inline void __kprobes alu_write_pc(long pcv, struct pt_regs *regs)
|
||||
{
|
||||
if (alu_write_pc_interworks)
|
||||
bx_write_pc(pcv, regs);
|
||||
else
|
||||
regs->ARM_pc = pcv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test if load/store instructions writeback the address register.
|
||||
* if P (bit 24) == 0 or W (bit 21) == 1
|
||||
*/
|
||||
#define is_writeback(insn) ((insn ^ 0x01000000) & 0x01200000)
|
||||
|
||||
/*
|
||||
* The following definitions and macros are used to build instruction
|
||||
* decoding tables for use by probes_decode_insn.
|
||||
*
|
||||
* These tables are a concatenation of entries each of which consist of one of
|
||||
* the decode_* structs. All of the fields in every type of decode structure
|
||||
* are of the union type decode_item, therefore the entire decode table can be
|
||||
* viewed as an array of these and declared like:
|
||||
*
|
||||
* static const union decode_item table_name[] = {};
|
||||
*
|
||||
* In order to construct each entry in the table, macros are used to
|
||||
* initialise a number of sequential decode_item values in a layout which
|
||||
* matches the relevant struct. E.g. DECODE_SIMULATE initialise a struct
|
||||
* decode_simulate by initialising four decode_item objects like this...
|
||||
*
|
||||
* {.bits = _type},
|
||||
* {.bits = _mask},
|
||||
* {.bits = _value},
|
||||
* {.action = _handler},
|
||||
*
|
||||
* Initialising a specified member of the union means that the compiler
|
||||
* will produce a warning if the argument is of an incorrect type.
|
||||
*
|
||||
* Below is a list of each of the macros used to initialise entries and a
|
||||
* description of the action performed when that entry is matched to an
|
||||
* instruction. A match is found when (instruction & mask) == value.
|
||||
*
|
||||
* DECODE_TABLE(mask, value, table)
|
||||
* Instruction decoding jumps to parsing the new sub-table 'table'.
|
||||
*
|
||||
* DECODE_CUSTOM(mask, value, decoder)
|
||||
* The value of 'decoder' is used as an index into the array of
|
||||
* action functions, and the retrieved decoder function is invoked
|
||||
* to complete decoding of the instruction.
|
||||
*
|
||||
* DECODE_SIMULATE(mask, value, handler)
|
||||
* The probes instruction handler is set to the value found by
|
||||
* indexing into the action array using the value of 'handler'. This
|
||||
* will be used to simulate the instruction when the probe is hit.
|
||||
* Decoding returns with INSN_GOOD_NO_SLOT.
|
||||
*
|
||||
* DECODE_EMULATE(mask, value, handler)
|
||||
* The probes instruction handler is set to the value found by
|
||||
* indexing into the action array using the value of 'handler'. This
|
||||
* will be used to emulate the instruction when the probe is hit. The
|
||||
* modified instruction (see below) is placed in the probes instruction
|
||||
* slot so it may be called by the emulation code. Decoding returns
|
||||
* with INSN_GOOD.
|
||||
*
|
||||
* DECODE_REJECT(mask, value)
|
||||
* Instruction decoding fails with INSN_REJECTED
|
||||
*
|
||||
* DECODE_OR(mask, value)
|
||||
* This allows the mask/value test of multiple table entries to be
|
||||
* logically ORed. Once an 'or' entry is matched the decoding action to
|
||||
* be performed is that of the next entry which isn't an 'or'. E.g.
|
||||
*
|
||||
* DECODE_OR (mask1, value1)
|
||||
* DECODE_OR (mask2, value2)
|
||||
* DECODE_SIMULATE (mask3, value3, simulation_handler)
|
||||
*
|
||||
* This means that if any of the three mask/value pairs match the
|
||||
* instruction being decoded, then 'simulation_handler' will be used
|
||||
* for it.
|
||||
*
|
||||
* Both the SIMULATE and EMULATE macros have a second form which take an
|
||||
* additional 'regs' argument.
|
||||
*
|
||||
* DECODE_SIMULATEX(mask, value, handler, regs)
|
||||
* DECODE_EMULATEX (mask, value, handler, regs)
|
||||
*
|
||||
* These are used to specify what kind of CPU register is encoded in each of the
|
||||
* least significant 5 nibbles of the instruction being decoded. The regs value
|
||||
* is specified using the REGS macro, this takes any of the REG_TYPE_* values
|
||||
* from enum decode_reg_type as arguments; only the '*' part of the name is
|
||||
* given. E.g.
|
||||
*
|
||||
* REGS(0, ANY, NOPC, 0, ANY)
|
||||
*
|
||||
* This indicates an instruction is encoded like:
|
||||
*
|
||||
* bits 19..16 ignore
|
||||
* bits 15..12 any register allowed here
|
||||
* bits 11.. 8 any register except PC allowed here
|
||||
* bits 7.. 4 ignore
|
||||
* bits 3.. 0 any register allowed here
|
||||
*
|
||||
* This register specification is checked after a decode table entry is found to
|
||||
* match an instruction (through the mask/value test). Any invalid register then
|
||||
* found in the instruction will cause decoding to fail with INSN_REJECTED. In
|
||||
* the above example this would happen if bits 11..8 of the instruction were
|
||||
* 1111, indicating R15 or PC.
|
||||
*
|
||||
* As well as checking for legal combinations of registers, this data is also
|
||||
* used to modify the registers encoded in the instructions so that an
|
||||
* emulation routines can use it. (See decode_regs() and INSN_NEW_BITS.)
|
||||
*
|
||||
* Here is a real example which matches ARM instructions of the form
|
||||
* "AND <Rd>,<Rn>,<Rm>,<shift> <Rs>"
|
||||
*
|
||||
* DECODE_EMULATEX (0x0e000090, 0x00000010, PROBES_DATA_PROCESSING_REG,
|
||||
* REGS(ANY, ANY, NOPC, 0, ANY)),
|
||||
* ^ ^ ^ ^
|
||||
* Rn Rd Rs Rm
|
||||
*
|
||||
* Decoding the instruction "AND R4, R5, R6, ASL R15" will be rejected because
|
||||
* Rs == R15
|
||||
*
|
||||
* Decoding the instruction "AND R4, R5, R6, ASL R7" will be accepted and the
|
||||
* instruction will be modified to "AND R0, R2, R3, ASL R1" and then placed into
|
||||
* the kprobes instruction slot. This can then be called later by the handler
|
||||
* function emulate_rd12rn16rm0rs8_rwflags (a pointer to which is retrieved from
|
||||
* the indicated slot in the action array), in order to simulate the instruction.
|
||||
*/
|
||||
|
||||
enum decode_type {
|
||||
DECODE_TYPE_END,
|
||||
DECODE_TYPE_TABLE,
|
||||
DECODE_TYPE_CUSTOM,
|
||||
DECODE_TYPE_SIMULATE,
|
||||
DECODE_TYPE_EMULATE,
|
||||
DECODE_TYPE_OR,
|
||||
DECODE_TYPE_REJECT,
|
||||
NUM_DECODE_TYPES /* Must be last enum */
|
||||
};
|
||||
|
||||
#define DECODE_TYPE_BITS 4
|
||||
#define DECODE_TYPE_MASK ((1 << DECODE_TYPE_BITS) - 1)
|
||||
|
||||
enum decode_reg_type {
|
||||
REG_TYPE_NONE = 0, /* Not a register, ignore */
|
||||
REG_TYPE_ANY, /* Any register allowed */
|
||||
REG_TYPE_SAMEAS16, /* Register should be same as that at bits 19..16 */
|
||||
REG_TYPE_SP, /* Register must be SP */
|
||||
REG_TYPE_PC, /* Register must be PC */
|
||||
REG_TYPE_NOSP, /* Register must not be SP */
|
||||
REG_TYPE_NOSPPC, /* Register must not be SP or PC */
|
||||
REG_TYPE_NOPC, /* Register must not be PC */
|
||||
REG_TYPE_NOPCWB, /* No PC if load/store write-back flag also set */
|
||||
|
||||
/* The following types are used when the encoding for PC indicates
|
||||
* another instruction form. This distiction only matters for test
|
||||
* case coverage checks.
|
||||
*/
|
||||
REG_TYPE_NOPCX, /* Register must not be PC */
|
||||
REG_TYPE_NOSPPCX, /* Register must not be SP or PC */
|
||||
|
||||
/* Alias to allow '0' arg to be used in REGS macro. */
|
||||
REG_TYPE_0 = REG_TYPE_NONE
|
||||
};
|
||||
|
||||
#define REGS(r16, r12, r8, r4, r0) \
|
||||
(((REG_TYPE_##r16) << 16) + \
|
||||
((REG_TYPE_##r12) << 12) + \
|
||||
((REG_TYPE_##r8) << 8) + \
|
||||
((REG_TYPE_##r4) << 4) + \
|
||||
(REG_TYPE_##r0))
|
||||
|
||||
union decode_item {
|
||||
u32 bits;
|
||||
const union decode_item *table;
|
||||
int action;
|
||||
};
|
||||
|
||||
struct decode_header;
|
||||
typedef enum probes_insn (probes_custom_decode_t)(probes_opcode_t,
|
||||
struct arch_probes_insn *,
|
||||
const struct decode_header *);
|
||||
|
||||
union decode_action {
|
||||
probes_insn_handler_t *handler;
|
||||
probes_custom_decode_t *decoder;
|
||||
};
|
||||
|
||||
#define DECODE_END \
|
||||
{.bits = DECODE_TYPE_END}
|
||||
|
||||
|
||||
struct decode_header {
|
||||
union decode_item type_regs;
|
||||
union decode_item mask;
|
||||
union decode_item value;
|
||||
};
|
||||
|
||||
#define DECODE_HEADER(_type, _mask, _value, _regs) \
|
||||
{.bits = (_type) | ((_regs) << DECODE_TYPE_BITS)}, \
|
||||
{.bits = (_mask)}, \
|
||||
{.bits = (_value)}
|
||||
|
||||
|
||||
struct decode_table {
|
||||
struct decode_header header;
|
||||
union decode_item table;
|
||||
};
|
||||
|
||||
#define DECODE_TABLE(_mask, _value, _table) \
|
||||
DECODE_HEADER(DECODE_TYPE_TABLE, _mask, _value, 0), \
|
||||
{.table = (_table)}
|
||||
|
||||
|
||||
struct decode_custom {
|
||||
struct decode_header header;
|
||||
union decode_item decoder;
|
||||
};
|
||||
|
||||
#define DECODE_CUSTOM(_mask, _value, _decoder) \
|
||||
DECODE_HEADER(DECODE_TYPE_CUSTOM, _mask, _value, 0), \
|
||||
{.action = (_decoder)}
|
||||
|
||||
|
||||
struct decode_simulate {
|
||||
struct decode_header header;
|
||||
union decode_item handler;
|
||||
};
|
||||
|
||||
#define DECODE_SIMULATEX(_mask, _value, _handler, _regs) \
|
||||
DECODE_HEADER(DECODE_TYPE_SIMULATE, _mask, _value, _regs), \
|
||||
{.action = (_handler)}
|
||||
|
||||
#define DECODE_SIMULATE(_mask, _value, _handler) \
|
||||
DECODE_SIMULATEX(_mask, _value, _handler, 0)
|
||||
|
||||
|
||||
struct decode_emulate {
|
||||
struct decode_header header;
|
||||
union decode_item handler;
|
||||
};
|
||||
|
||||
#define DECODE_EMULATEX(_mask, _value, _handler, _regs) \
|
||||
DECODE_HEADER(DECODE_TYPE_EMULATE, _mask, _value, _regs), \
|
||||
{.action = (_handler)}
|
||||
|
||||
#define DECODE_EMULATE(_mask, _value, _handler) \
|
||||
DECODE_EMULATEX(_mask, _value, _handler, 0)
|
||||
|
||||
|
||||
struct decode_or {
|
||||
struct decode_header header;
|
||||
};
|
||||
|
||||
#define DECODE_OR(_mask, _value) \
|
||||
DECODE_HEADER(DECODE_TYPE_OR, _mask, _value, 0)
|
||||
|
||||
enum probes_insn {
|
||||
INSN_REJECTED,
|
||||
INSN_GOOD,
|
||||
INSN_GOOD_NO_SLOT
|
||||
};
|
||||
|
||||
struct decode_reject {
|
||||
struct decode_header header;
|
||||
};
|
||||
|
||||
#define DECODE_REJECT(_mask, _value) \
|
||||
DECODE_HEADER(DECODE_TYPE_REJECT, _mask, _value, 0)
|
||||
|
||||
probes_insn_handler_t probes_simulate_nop;
|
||||
probes_insn_handler_t probes_emulate_none;
|
||||
|
||||
int __kprobes
|
||||
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const union decode_item *table, bool thumb, bool emulate,
|
||||
const union decode_action *actions);
|
||||
|
||||
#endif
|
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "uprobes.h"
|
||||
|
||||
static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
|
||||
{
|
||||
probes_opcode_t insn = __mem_to_opcode_arm(*pinsn);
|
||||
probes_opcode_t temp;
|
||||
probes_opcode_t mask;
|
||||
int freereg;
|
||||
u32 free = 0xffff;
|
||||
u32 regs;
|
||||
|
||||
for (regs = oregs; regs; regs >>= 4, insn >>= 4) {
|
||||
if ((regs & 0xf) == REG_TYPE_NONE)
|
||||
continue;
|
||||
|
||||
free &= ~(1 << (insn & 0xf));
|
||||
}
|
||||
|
||||
/* No PC, no problem */
|
||||
if (free & (1 << 15))
|
||||
return 15;
|
||||
|
||||
if (!free)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* fls instead of ffs ensures that for "ldrd r0, r1, [pc]" we would
|
||||
* pick LR instead of R1.
|
||||
*/
|
||||
freereg = free = fls(free) - 1;
|
||||
|
||||
temp = __mem_to_opcode_arm(*pinsn);
|
||||
insn = temp;
|
||||
regs = oregs;
|
||||
mask = 0xf;
|
||||
|
||||
for (; regs; regs >>= 4, mask <<= 4, free <<= 4, temp >>= 4) {
|
||||
if ((regs & 0xf) == REG_TYPE_NONE)
|
||||
continue;
|
||||
|
||||
if ((temp & 0xf) != 15)
|
||||
continue;
|
||||
|
||||
insn &= ~mask;
|
||||
insn |= free & mask;
|
||||
}
|
||||
|
||||
*pinsn = __opcode_to_mem_arm(insn);
|
||||
return freereg;
|
||||
}
|
||||
|
||||
static void uprobe_set_pc(struct arch_uprobe *auprobe,
|
||||
struct arch_uprobe_task *autask,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
u32 pcreg = auprobe->pcreg;
|
||||
|
||||
autask->backup = regs->uregs[pcreg];
|
||||
regs->uregs[pcreg] = regs->ARM_pc + 8;
|
||||
}
|
||||
|
||||
static void uprobe_unset_pc(struct arch_uprobe *auprobe,
|
||||
struct arch_uprobe_task *autask,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
/* PC will be taken care of by common code */
|
||||
regs->uregs[auprobe->pcreg] = autask->backup;
|
||||
}
|
||||
|
||||
static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
|
||||
struct arch_uprobe_task *autask,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
u32 pcreg = auprobe->pcreg;
|
||||
|
||||
alu_write_pc(regs->uregs[pcreg], regs);
|
||||
regs->uregs[pcreg] = autask->backup;
|
||||
}
|
||||
|
||||
static void uprobe_write_pc(struct arch_uprobe *auprobe,
|
||||
struct arch_uprobe_task *autask,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
u32 pcreg = auprobe->pcreg;
|
||||
|
||||
load_write_pc(regs->uregs[pcreg], regs);
|
||||
regs->uregs[pcreg] = autask->backup;
|
||||
}
|
||||
|
||||
enum probes_insn
|
||||
decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
|
||||
asi);
|
||||
struct decode_emulate *decode = (struct decode_emulate *) d;
|
||||
u32 regs = decode->header.type_regs.bits >> DECODE_TYPE_BITS;
|
||||
int reg;
|
||||
|
||||
reg = uprobes_substitute_pc(&auprobe->ixol[0], regs);
|
||||
if (reg == 15)
|
||||
return INSN_GOOD;
|
||||
|
||||
if (reg == -1)
|
||||
return INSN_REJECTED;
|
||||
|
||||
auprobe->pcreg = reg;
|
||||
auprobe->prehandler = uprobe_set_pc;
|
||||
auprobe->posthandler = uprobe_unset_pc;
|
||||
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
enum probes_insn
|
||||
decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d, bool alu)
|
||||
{
|
||||
struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
|
||||
asi);
|
||||
enum probes_insn ret = decode_pc_ro(insn, asi, d);
|
||||
|
||||
if (((insn >> 12) & 0xf) == 15)
|
||||
auprobe->posthandler = alu ? uprobe_aluwrite_pc
|
||||
: uprobe_write_pc;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum probes_insn
|
||||
decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
return decode_wb_pc(insn, asi, d, true);
|
||||
}
|
||||
|
||||
enum probes_insn
|
||||
decode_ldr(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
return decode_wb_pc(insn, asi, d, false);
|
||||
}
|
||||
|
||||
enum probes_insn
|
||||
uprobe_decode_ldmstm(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *d)
|
||||
{
|
||||
struct arch_uprobe *auprobe = container_of(asi, struct arch_uprobe,
|
||||
asi);
|
||||
unsigned reglist = insn & 0xffff;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int lbit = insn & (1 << 20);
|
||||
unsigned used = reglist | (1 << rn);
|
||||
|
||||
if (rn == 15)
|
||||
return INSN_REJECTED;
|
||||
|
||||
if (!(used & (1 << 15)))
|
||||
return INSN_GOOD;
|
||||
|
||||
if (used & (1 << 14))
|
||||
return INSN_REJECTED;
|
||||
|
||||
/* Use LR instead of PC */
|
||||
insn ^= 0xc000;
|
||||
|
||||
auprobe->pcreg = 14;
|
||||
auprobe->ixol[0] = __opcode_to_mem_arm(insn);
|
||||
|
||||
auprobe->prehandler = uprobe_set_pc;
|
||||
if (lbit)
|
||||
auprobe->posthandler = uprobe_write_pc;
|
||||
else
|
||||
auprobe->posthandler = uprobe_unset_pc;
|
||||
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
const union decode_action uprobes_probes_actions[] = {
|
||||
[PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
|
||||
[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
|
||||
[PROBES_MRS] = {.handler = simulate_mrs},
|
||||
[PROBES_BRANCH_REG] = {.handler = simulate_blx2bx},
|
||||
[PROBES_CLZ] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SATURATING_ARITHMETIC] = {.handler = probes_simulate_nop},
|
||||
[PROBES_MUL1] = {.handler = probes_simulate_nop},
|
||||
[PROBES_MUL2] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SWP] = {.handler = probes_simulate_nop},
|
||||
[PROBES_LDRSTRD] = {.decoder = decode_pc_ro},
|
||||
[PROBES_LOAD_EXTRA] = {.decoder = decode_pc_ro},
|
||||
[PROBES_LOAD] = {.decoder = decode_ldr},
|
||||
[PROBES_STORE_EXTRA] = {.decoder = decode_pc_ro},
|
||||
[PROBES_STORE] = {.decoder = decode_pc_ro},
|
||||
[PROBES_MOV_IP_SP] = {.handler = simulate_mov_ipsp},
|
||||
[PROBES_DATA_PROCESSING_REG] = {
|
||||
.decoder = decode_rd12rn16rm0rs8_rwflags},
|
||||
[PROBES_DATA_PROCESSING_IMM] = {
|
||||
.decoder = decode_rd12rn16rm0rs8_rwflags},
|
||||
[PROBES_MOV_HALFWORD] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SEV] = {.handler = probes_simulate_nop},
|
||||
[PROBES_WFE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SATURATE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_REV] = {.handler = probes_simulate_nop},
|
||||
[PROBES_MMI] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PACK] = {.handler = probes_simulate_nop},
|
||||
[PROBES_EXTEND] = {.handler = probes_simulate_nop},
|
||||
[PROBES_EXTEND_ADD] = {.handler = probes_simulate_nop},
|
||||
[PROBES_MUL_ADD_LONG] = {.handler = probes_simulate_nop},
|
||||
[PROBES_MUL_ADD] = {.handler = probes_simulate_nop},
|
||||
[PROBES_BITFIELD] = {.handler = probes_simulate_nop},
|
||||
[PROBES_BRANCH] = {.handler = simulate_bbl},
|
||||
[PROBES_LDMSTM] = {.decoder = uprobe_decode_ldmstm}
|
||||
};
|
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
|
||||
*
|
||||
* 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/stddef.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "uprobes.h"
|
||||
|
||||
#define UPROBE_TRAP_NR UINT_MAX
|
||||
|
||||
bool is_swbp_insn(uprobe_opcode_t *insn)
|
||||
{
|
||||
return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
|
||||
(UPROBE_SWBP_ARM_INSN & 0x0fffffff);
|
||||
}
|
||||
|
||||
int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||
unsigned long vaddr)
|
||||
{
|
||||
return uprobe_write_opcode(mm, vaddr,
|
||||
__opcode_to_mem_arm(auprobe->bpinsn));
|
||||
}
|
||||
|
||||
bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
|
||||
regs->ARM_pc += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
probes_opcode_t opcode;
|
||||
|
||||
if (!auprobe->simulate)
|
||||
return false;
|
||||
|
||||
opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
|
||||
|
||||
auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long orig_ret_vaddr;
|
||||
|
||||
orig_ret_vaddr = regs->ARM_lr;
|
||||
/* Replace the return addr with trampoline addr */
|
||||
regs->ARM_lr = trampoline_vaddr;
|
||||
return orig_ret_vaddr;
|
||||
}
|
||||
|
||||
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned int insn;
|
||||
unsigned int bpinsn;
|
||||
enum probes_insn ret;
|
||||
|
||||
/* Thumb not yet support */
|
||||
if (addr & 0x3)
|
||||
return -EINVAL;
|
||||
|
||||
insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
|
||||
auprobe->ixol[0] = __opcode_to_mem_arm(insn);
|
||||
auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
|
||||
|
||||
ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
|
||||
uprobes_probes_actions);
|
||||
switch (ret) {
|
||||
case INSN_REJECTED:
|
||||
return -EINVAL;
|
||||
|
||||
case INSN_GOOD_NO_SLOT:
|
||||
auprobe->simulate = true;
|
||||
break;
|
||||
|
||||
case INSN_GOOD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
|
||||
if (insn >= 0xe0000000)
|
||||
bpinsn |= 0xe0000000; /* Unconditional instruction */
|
||||
else
|
||||
bpinsn |= insn & 0xf0000000; /* Copy condition from insn */
|
||||
|
||||
auprobe->bpinsn = bpinsn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
|
||||
void *src, unsigned long len)
|
||||
{
|
||||
void *xol_page_kaddr = kmap_atomic(page);
|
||||
void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK);
|
||||
|
||||
preempt_disable();
|
||||
|
||||
/* Initialize the slot */
|
||||
memcpy(dst, src, len);
|
||||
|
||||
/* flush caches (dcache/icache) */
|
||||
flush_uprobe_xol_access(page, vaddr, dst, len);
|
||||
|
||||
preempt_enable();
|
||||
|
||||
kunmap_atomic(xol_page_kaddr);
|
||||
}
|
||||
|
||||
|
||||
int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
struct uprobe_task *utask = current->utask;
|
||||
|
||||
if (auprobe->prehandler)
|
||||
auprobe->prehandler(auprobe, &utask->autask, regs);
|
||||
|
||||
utask->autask.saved_trap_no = current->thread.trap_no;
|
||||
current->thread.trap_no = UPROBE_TRAP_NR;
|
||||
regs->ARM_pc = utask->xol_vaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
struct uprobe_task *utask = current->utask;
|
||||
|
||||
WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
|
||||
|
||||
current->thread.trap_no = utask->autask.saved_trap_no;
|
||||
regs->ARM_pc = utask->vaddr + 4;
|
||||
|
||||
if (auprobe->posthandler)
|
||||
auprobe->posthandler(auprobe, &utask->autask, regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool arch_uprobe_xol_was_trapped(struct task_struct *t)
|
||||
{
|
||||
if (t->thread.trap_no != UPROBE_TRAP_NR)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
struct uprobe_task *utask = current->utask;
|
||||
|
||||
current->thread.trap_no = utask->autask.saved_trap_no;
|
||||
instruction_pointer_set(regs, utask->vaddr);
|
||||
}
|
||||
|
||||
int arch_uprobe_exception_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
instr &= 0x0fffffff;
|
||||
if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
|
||||
uprobe_pre_sstep_notifier(regs);
|
||||
else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
|
||||
uprobe_post_sstep_notifier(regs);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
|
||||
{
|
||||
return instruction_pointer(regs);
|
||||
}
|
||||
|
||||
static struct undef_hook uprobes_arm_break_hook = {
|
||||
.instr_mask = 0x0fffffff,
|
||||
.instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = USR_MODE,
|
||||
.fn = uprobe_trap_handler,
|
||||
};
|
||||
|
||||
static struct undef_hook uprobes_arm_ss_hook = {
|
||||
.instr_mask = 0x0fffffff,
|
||||
.instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff),
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = USR_MODE,
|
||||
.fn = uprobe_trap_handler,
|
||||
};
|
||||
|
||||
static int arch_uprobes_init(void)
|
||||
{
|
||||
register_undef_hook(&uprobes_arm_break_hook);
|
||||
register_undef_hook(&uprobes_arm_ss_hook);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(arch_uprobes_init);
|
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __ARM_KERNEL_UPROBES_H
|
||||
#define __ARM_KERNEL_UPROBES_H
|
||||
|
||||
enum probes_insn uprobe_decode_ldmstm(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *d);
|
||||
|
||||
enum probes_insn decode_ldr(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *d);
|
||||
|
||||
enum probes_insn
|
||||
decode_rd12rn16rm0rs8_rwflags(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *d);
|
||||
|
||||
enum probes_insn
|
||||
decode_wb_pc(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d, bool alu);
|
||||
|
||||
enum probes_insn
|
||||
decode_pc_ro(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *d);
|
||||
|
||||
extern const union decode_action uprobes_probes_actions[];
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user