Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
KASAN_SANITIZE_actions-common.o := n
KASAN_SANITIZE_actions-arm.o := n
KASAN_SANITIZE_actions-thumb.o := n
obj-$(CONFIG_KPROBES) += core.o actions-common.o checkers-common.o
obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
test-kprobes-objs := test-core.o
ifdef CONFIG_THUMB2_KERNEL
obj-$(CONFIG_KPROBES) += actions-thumb.o checkers-thumb.o
test-kprobes-objs += test-thumb.o
else
obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o
obj-$(CONFIG_OPTPROBES) += opt-arm.o
test-kprobes-objs += test-arm.o
endif
@@ -0,0 +1,336 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/arm/probes/kprobes/actions-arm.c
*
* Copyright (C) 2006, 2007 Motorola Inc.
*/
/*
* 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 "../decode-arm.h"
#include "core.h"
#include "checkers.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_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}
};
const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, arm_regs_checker, NULL};
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/arm/probes/kprobes/actions-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.
*/
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <asm/opcodes.h>
#include "core.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__ (
ARM( "stmdb sp!, {%[regs], r11} \n\t" )
THUMB( "stmdb sp!, {%[regs], r7} \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"
ARM( "ldr r11, [sp], #4 \n\t" )
THUMB( "ldr r7, [sp], #4 \n\t" )
: [regs] "=r" (rregs), [fn] "=r" (rfn)
: "0" (rregs), "1" (rfn)
: "r0", "r2", "r3", "r4", "r5", "r6", ARM("r7") THUMB("r11"),
"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;
}
@@ -0,0 +1,675 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/arm/probes/kprobes/actions-thumb.c
*
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/kprobes.h>
#include "../decode-thumb.h"
#include "core.h"
#include "checkers.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"
"mov r11, r7 \n\t"
"ldmia %[regs], {r0-r7} \n\t"
"blx %[fn] \n\t"
"stmia %[regs], {r0-r7} \n\t"
"mov r7, r11 \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", "r11",
"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__ (
"mov r11, r7 \n\t"
"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"
"mov r7, r11 \n\t"
:
: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r8", "r9", "r11",
"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__ (
"mov r11, r7 \n\t"
"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"
"mov r7, r11 \n\t"
:
: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r9", "r11",
"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__ (
"mov r11, r7 \n\t"
"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"
"mov r7, r11 \n\t"
: "=r" (pc)
: [regs] "r" (regs), [fn] "r" (asi->insn_fn)
: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r9", "r11",
"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},
};
const struct decode_checker *kprobes_t32_checkers[] = {t32_stack_checker, NULL};
const struct decode_checker *kprobes_t16_checkers[] = {t16_stack_checker, NULL};
@@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/arm/probes/kprobes/checkers-arm.c
*
* Copyright (C) 2014 Huawei Inc.
*/
#include <linux/kernel.h>
#include "../decode.h"
#include "../decode-arm.h"
#include "checkers.h"
static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
/*
* PROBES_LDRSTRD, PROBES_LDMSTM, PROBES_STORE,
* PROBES_STORE_EXTRA may get here. Simply mark all normal
* insns as STACK_USE_NONE.
*/
static const union decode_item table[] = {
/*
* 'STR{,D,B,H}, Rt, [Rn, Rm]' should be marked as UNKNOWN
* if Rn or Rm is SP.
* x
* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx
* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx
*/
DECODE_OR (0x0e10000f, 0x0600000d),
DECODE_OR (0x0e1f0000, 0x060d0000),
/*
* x
* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx
*/
DECODE_OR (0x0e5000bf, 0x000000bd),
DECODE_CUSTOM (0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN),
/*
* For PROBES_LDMSTM, only stmdx sp, [...] need to examine
*
* Bit B/A (bit 24) encodes arithmetic operation order. 1 means
* before, 0 means after.
* Bit I/D (bit 23) encodes arithmetic operation. 1 means
* increment, 0 means decrement.
*
* So:
* B I
* / /
* A D | Rn |
* STMDX SP, [...] cccc 100x 00x0 xxxx xxxx xxxx xxxx xxxx
*/
DECODE_CUSTOM (0x0edf0000, 0x080d0000, STACK_USE_STMDX),
/* P U W | Rn | Rt | imm12 |*/
/* STR (immediate) cccc 010x x0x0 1101 xxxx xxxx xxxx xxxx */
/* STRB (immediate) cccc 010x x1x0 1101 xxxx xxxx xxxx xxxx */
/* P U W | Rn | Rt |imm4| |imm4|*/
/* STRD (immediate) cccc 000x x1x0 1101 xxxx xxxx 1111 xxxx */
/* STRH (immediate) cccc 000x x1x0 1101 xxxx xxxx 1011 xxxx */
/*
* index = (P == '1'); add = (U == '1').
* Above insns with:
* index == 0 (str{,d,h} rx, [sp], #+/-imm) or
* add == 1 (str{,d,h} rx, [sp, #+<imm>])
* should be STACK_USE_NONE.
* Only str{,b,d,h} rx,[sp,#-n] (P == 1 and U == 0) are
* required to be examined.
*/
/* STR{,B} Rt,[SP,#-n] cccc 0101 0xx0 1101 xxxx xxxx xxxx xxxx */
DECODE_CUSTOM (0x0f9f0000, 0x050d0000, STACK_USE_FIXED_XXX),
/* STR{D,H} Rt,[SP,#-n] cccc 0001 01x0 1101 xxxx xxxx 1x11 xxxx */
DECODE_CUSTOM (0x0fdf00b0, 0x014d00b0, STACK_USE_FIXED_X0X),
/* fall through */
DECODE_CUSTOM (0, 0, STACK_USE_NONE),
DECODE_END
};
return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
}
const struct decode_checker arm_stack_checker[NUM_PROBES_ARM_ACTIONS] = {
[PROBES_LDRSTRD] = {.checker = arm_check_stack},
[PROBES_STORE_EXTRA] = {.checker = arm_check_stack},
[PROBES_STORE] = {.checker = arm_check_stack},
[PROBES_LDMSTM] = {.checker = arm_check_stack},
};
static enum probes_insn __kprobes arm_check_regs_nouse(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
asi->register_usage_flags = 0;
return INSN_GOOD;
}
static enum probes_insn arm_check_regs_normal(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
int i;
asi->register_usage_flags = 0;
for (i = 0; i < 5; regs >>= 4, insn >>= 4, i++)
if (regs & 0xf)
asi->register_usage_flags |= 1 << (insn & 0xf);
return INSN_GOOD;
}
static enum probes_insn arm_check_regs_ldmstm(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
unsigned int reglist = insn & 0xffff;
unsigned int rn = (insn >> 16) & 0xf;
asi->register_usage_flags = reglist | (1 << rn);
return INSN_GOOD;
}
static enum probes_insn arm_check_regs_mov_ip_sp(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
/* Instruction is 'mov ip, sp' i.e. 'mov r12, r13' */
asi->register_usage_flags = (1 << 12) | (1<< 13);
return INSN_GOOD;
}
/*
* | Rn |Rt/d| | Rm |
* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx
* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
* | Rn |Rt/d| |imm4L|
* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx
* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx
*
* Such instructions access Rt/d and its next register, so different
* from others, a specific checker is required to handle this extra
* implicit register usage.
*/
static enum probes_insn arm_check_regs_ldrdstrd(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
int rdt = (insn >> 12) & 0xf;
arm_check_regs_normal(insn, asi, h);
asi->register_usage_flags |= 1 << (rdt + 1);
return INSN_GOOD;
}
const struct decode_checker arm_regs_checker[NUM_PROBES_ARM_ACTIONS] = {
[PROBES_MRS] = {.checker = arm_check_regs_normal},
[PROBES_SATURATING_ARITHMETIC] = {.checker = arm_check_regs_normal},
[PROBES_MUL1] = {.checker = arm_check_regs_normal},
[PROBES_MUL2] = {.checker = arm_check_regs_normal},
[PROBES_MUL_ADD_LONG] = {.checker = arm_check_regs_normal},
[PROBES_MUL_ADD] = {.checker = arm_check_regs_normal},
[PROBES_LOAD] = {.checker = arm_check_regs_normal},
[PROBES_LOAD_EXTRA] = {.checker = arm_check_regs_normal},
[PROBES_STORE] = {.checker = arm_check_regs_normal},
[PROBES_STORE_EXTRA] = {.checker = arm_check_regs_normal},
[PROBES_DATA_PROCESSING_REG] = {.checker = arm_check_regs_normal},
[PROBES_DATA_PROCESSING_IMM] = {.checker = arm_check_regs_normal},
[PROBES_SEV] = {.checker = arm_check_regs_nouse},
[PROBES_WFE] = {.checker = arm_check_regs_nouse},
[PROBES_SATURATE] = {.checker = arm_check_regs_normal},
[PROBES_REV] = {.checker = arm_check_regs_normal},
[PROBES_MMI] = {.checker = arm_check_regs_normal},
[PROBES_PACK] = {.checker = arm_check_regs_normal},
[PROBES_EXTEND] = {.checker = arm_check_regs_normal},
[PROBES_EXTEND_ADD] = {.checker = arm_check_regs_normal},
[PROBES_BITFIELD] = {.checker = arm_check_regs_normal},
[PROBES_LDMSTM] = {.checker = arm_check_regs_ldmstm},
[PROBES_MOV_IP_SP] = {.checker = arm_check_regs_mov_ip_sp},
[PROBES_LDRSTRD] = {.checker = arm_check_regs_ldrdstrd},
};
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/arm/probes/kprobes/checkers-common.c
*
* Copyright (C) 2014 Huawei Inc.
*/
#include <linux/kernel.h>
#include "../decode.h"
#include "../decode-arm.h"
#include "checkers.h"
enum probes_insn checker_stack_use_none(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
asi->stack_space = 0;
return INSN_GOOD_NO_SLOT;
}
enum probes_insn checker_stack_use_unknown(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
asi->stack_space = -1;
return INSN_GOOD_NO_SLOT;
}
#ifdef CONFIG_THUMB2_KERNEL
enum probes_insn checker_stack_use_imm_0xx(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
int imm = insn & 0xff;
asi->stack_space = imm;
return INSN_GOOD_NO_SLOT;
}
/*
* Different from other insn uses imm8, the real addressing offset of
* STRD in T32 encoding should be imm8 * 4. See ARMARM description.
*/
static enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
int imm = insn & 0xff;
asi->stack_space = imm << 2;
return INSN_GOOD_NO_SLOT;
}
#else
enum probes_insn checker_stack_use_imm_x0x(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
int imm = ((insn & 0xf00) >> 4) + (insn & 0xf);
asi->stack_space = imm;
return INSN_GOOD_NO_SLOT;
}
#endif
enum probes_insn checker_stack_use_imm_xxx(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
int imm = insn & 0xfff;
asi->stack_space = imm;
return INSN_GOOD_NO_SLOT;
}
enum probes_insn checker_stack_use_stmdx(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
unsigned int reglist = insn & 0xffff;
int pbit = insn & (1 << 24);
asi->stack_space = (hweight32(reglist) - (!pbit ? 1 : 0)) * 4;
return INSN_GOOD_NO_SLOT;
}
const union decode_action stack_check_actions[] = {
[STACK_USE_NONE] = {.decoder = checker_stack_use_none},
[STACK_USE_UNKNOWN] = {.decoder = checker_stack_use_unknown},
#ifdef CONFIG_THUMB2_KERNEL
[STACK_USE_FIXED_0XX] = {.decoder = checker_stack_use_imm_0xx},
[STACK_USE_T32STRD] = {.decoder = checker_stack_use_t32strd},
#else
[STACK_USE_FIXED_X0X] = {.decoder = checker_stack_use_imm_x0x},
#endif
[STACK_USE_FIXED_XXX] = {.decoder = checker_stack_use_imm_xxx},
[STACK_USE_STMDX] = {.decoder = checker_stack_use_stmdx},
};
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* arch/arm/probes/kprobes/checkers-thumb.c
*
* Copyright (C) 2014 Huawei Inc.
*/
#include <linux/kernel.h>
#include "../decode.h"
#include "../decode-thumb.h"
#include "checkers.h"
static enum probes_insn __kprobes t32_check_stack(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
/*
* PROBES_T32_LDMSTM, PROBES_T32_LDRDSTRD and PROBES_T32_LDRSTR
* may get here. Simply mark all normal insns as STACK_USE_NONE.
*/
static const union decode_item table[] = {
/*
* First, filter out all ldr insns to make our life easier.
* Following load insns may come here:
* LDM, LDRD, LDR.
* In T32 encoding, bit 20 is enough for distinguishing
* load and store. All load insns have this bit set, when
* all store insns have this bit clear.
*/
DECODE_CUSTOM (0x00100000, 0x00100000, STACK_USE_NONE),
/*
* Mark all 'STR{,B,H}, Rt, [Rn, Rm]' as STACK_USE_UNKNOWN
* if Rn or Rm is SP. T32 doesn't encode STRD.
*/
/* xx | Rn | Rt | | Rm |*/
/* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
/* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
/* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
/* INVALID INSN 1111 1000 0110 xxxx xxxx 0000 00xx xxxx */
/* By Introducing INVALID INSN, bit 21 and 22 can be ignored. */
DECODE_OR (0xff9f0fc0, 0xf80d0000),
DECODE_CUSTOM (0xff900fcf, 0xf800000d, STACK_USE_UNKNOWN),
/* xx | Rn | Rt | PUW| imm8 |*/
/* STR (imm 8) 1111 1000 0100 1101 xxxx 110x xxxx xxxx */
/* STRB (imm 8) 1111 1000 0000 1101 xxxx 110x xxxx xxxx */
/* STRH (imm 8) 1111 1000 0010 1101 xxxx 110x xxxx xxxx */
/* INVALID INSN 1111 1000 0110 1101 xxxx 110x xxxx xxxx */
/* Only consider U == 0 and P == 1: strx rx, [sp, #-<imm>] */
DECODE_CUSTOM (0xff9f0e00, 0xf80d0c00, STACK_USE_FIXED_0XX),
/* For STR{,B,H} (imm 12), offset is always positive, so ignore them. */
/* P U W | Rn | Rt | Rt2| imm8 |*/
/* STRD (immediate) 1110 1001 01x0 1101 xxxx xxxx xxxx xxxx */
/*
* Only consider U == 0 and P == 1.
* Also note that STRD in T32 encoding is special:
* imm = ZeroExtend(imm8:'00', 32)
*/
DECODE_CUSTOM (0xffdf0000, 0xe94d0000, STACK_USE_T32STRD),
/* | Rn | */
/* STMDB 1110 1001 00x0 1101 xxxx xxxx xxxx xxxx */
DECODE_CUSTOM (0xffdf0000, 0xe90d0000, STACK_USE_STMDX),
/* fall through */
DECODE_CUSTOM (0, 0, STACK_USE_NONE),
DECODE_END
};
return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
}
const struct decode_checker t32_stack_checker[NUM_PROBES_T32_ACTIONS] = {
[PROBES_T32_LDMSTM] = {.checker = t32_check_stack},
[PROBES_T32_LDRDSTRD] = {.checker = t32_check_stack},
[PROBES_T32_LDRSTR] = {.checker = t32_check_stack},
};
/*
* See following comments. This insn must be 'push'.
*/
static enum probes_insn __kprobes t16_check_stack(probes_opcode_t insn,
struct arch_probes_insn *asi,
const struct decode_header *h)
{
unsigned int reglist = insn & 0x1ff;
asi->stack_space = hweight32(reglist) * 4;
return INSN_GOOD;
}
/*
* T16 encoding is simple: only the 'push' insn can need extra stack space.
* Other insns, like str, can only use r0-r7 as Rn.
*/
const struct decode_checker t16_stack_checker[NUM_PROBES_T16_ACTIONS] = {
[PROBES_T16_PUSH] = {.checker = t16_check_stack},
};
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* arch/arm/probes/kprobes/checkers.h
*
* Copyright (C) 2014 Huawei Inc.
*/
#ifndef _ARM_KERNEL_PROBES_CHECKERS_H
#define _ARM_KERNEL_PROBES_CHECKERS_H
#include <linux/kernel.h>
#include <linux/types.h>
#include "../decode.h"
extern probes_check_t checker_stack_use_none;
extern probes_check_t checker_stack_use_unknown;
#ifdef CONFIG_THUMB2_KERNEL
extern probes_check_t checker_stack_use_imm_0xx;
#else
extern probes_check_t checker_stack_use_imm_x0x;
#endif
extern probes_check_t checker_stack_use_imm_xxx;
extern probes_check_t checker_stack_use_stmdx;
enum {
STACK_USE_NONE,
STACK_USE_UNKNOWN,
#ifdef CONFIG_THUMB2_KERNEL
STACK_USE_FIXED_0XX,
STACK_USE_T32STRD,
#else
STACK_USE_FIXED_X0X,
#endif
STACK_USE_FIXED_XXX,
STACK_USE_STMDX,
NUM_STACK_USE_TYPES
};
extern const union decode_action stack_check_actions[];
#ifndef CONFIG_THUMB2_KERNEL
extern const struct decode_checker arm_stack_checker[];
extern const struct decode_checker arm_regs_checker[];
#else
#endif
extern const struct decode_checker t32_stack_checker[];
extern const struct decode_checker t16_stack_checker[];
#endif
+485
View File
@@ -0,0 +1,485 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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.
*/
#define pr_fmt(fmt) "kprobes: " fmt
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stop_machine.h>
#include <linux/sched/debug.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 <asm/text-patching.h>
#include <asm/sections.h>
#include "../decode-arm.h"
#include "../decode-thumb.h"
#include "core.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))
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;
const struct decode_checker **checkers;
#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;
checkers = kprobes_t32_checkers;
} else {
decode_insn = thumb16_probes_decode_insn;
actions = kprobes_t16_actions;
checkers = kprobes_t16_checkers;
}
#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;
checkers = kprobes_arm_checkers;
#endif
p->opcode = insn;
p->ainsn.insn = tmp_insn;
switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) {
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;
}
/*
* Never instrument insn like 'str r0, [sp, +/-r1]'. Also, insn likes
* 'str r0, [sp, #-68]' should also be prohibited.
* See __und_svc.
*/
if ((p->ainsn.stack_space < 0) ||
(p->ainsn.stack_space > MAX_STACK_SIZE))
return -EINVAL;
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.
*/
struct patch {
void *addr;
unsigned int insn;
};
static int __kprobes_remove_breakpoint(void *data)
{
struct patch *p = data;
__patch_text(p->addr, p->insn);
return 0;
}
void __kprobes kprobes_remove_breakpoint(void *addr, unsigned int insn)
{
struct patch p = {
.addr = addr,
.insn = insn,
};
stop_machine_cpuslocked(__kprobes_remove_breakpoint, &p,
cpu_online_mask);
}
void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
kprobes_remove_breakpoint((void *)((uintptr_t)p->addr & ~1),
p->opcode);
}
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.
*/
static 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 (!p->ainsn.insn_check_cc(regs->ARM_cpsr)) {
/*
* Probe hit but conditional execution check failed,
* so just skip the instruction and continue as if
* nothing had happened.
* In this case, we can skip recursing check too.
*/
singlestep_skip(p, regs);
} else if (cur) {
/* Kprobe is pending, so we're recursing. */
switch (kcb->kprobe_status) {
case KPROBE_HIT_ACTIVE:
case KPROBE_HIT_SSDONE:
case KPROBE_HIT_SS:
/* 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;
case KPROBE_REENTER:
/* A nested probe was hit in FIQ, it is a BUG */
pr_warn("Failed to recover from reentered kprobes.\n");
dump_kprobe(p);
fallthrough;
default:
/* impossible cases */
BUG();
}
} else {
/* 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 will
* modify the execution path and no need to single
* stepping. Let's just reset current kprobe and exit.
*/
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 {
/*
* 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;
}
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, sp, lr, and pc to the user
* return-handler. This is not a complete pt_regs structure, but that
* should be enough for stacktrace from the return handler with or
* without pt_regs.
*/
void __naked __kprobes __kretprobe_trampoline(void)
{
__asm__ __volatile__ (
#ifdef CONFIG_FRAME_POINTER
"ldr lr, =__kretprobe_trampoline \n\t"
/* __kretprobe_trampoline makes a framepointer on pt_regs. */
#ifdef CONFIG_CC_IS_CLANG
"stmdb sp, {sp, lr, pc} \n\t"
"sub sp, sp, #12 \n\t"
/* In clang case, pt_regs->ip = lr. */
"stmdb sp!, {r0 - r11, lr} \n\t"
/* fp points regs->r11 (fp) */
"add fp, sp, #44 \n\t"
#else /* !CONFIG_CC_IS_CLANG */
/* In gcc case, pt_regs->ip = fp. */
"stmdb sp, {fp, sp, lr, pc} \n\t"
"sub sp, sp, #16 \n\t"
"stmdb sp!, {r0 - r11} \n\t"
/* fp points regs->r15 (pc) */
"add fp, sp, #60 \n\t"
#endif /* CONFIG_CC_IS_CLANG */
#else /* !CONFIG_FRAME_POINTER */
"sub sp, sp, #16 \n\t"
"stmdb sp!, {r0 - r11} \n\t"
#endif /* CONFIG_FRAME_POINTER */
"mov r0, sp \n\t"
"bl trampoline_handler \n\t"
"mov lr, r0 \n\t"
"ldmia sp!, {r0 - r11} \n\t"
"add sp, sp, #16 \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)
{
return (void *)kretprobe_trampoline_handler(regs, (void *)regs->ARM_fp);
}
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
ri->fp = (void *)regs->ARM_fp;
/* Replace the return addr with trampoline addr. */
regs->ARM_lr = (unsigned long)&__kretprobe_trampoline;
}
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(void)
{
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;
}
bool arch_within_kprobe_blacklist(unsigned long addr)
{
void *a = (void *)addr;
return __in_irqentry_text(addr) ||
in_entry_text(addr) ||
in_idmap_text(addr) ||
memory_contains(__kprobes_text_start, __kprobes_text_end, a, 1);
}
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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.
*/
#ifndef _ARM_KERNEL_KPROBES_H
#define _ARM_KERNEL_KPROBES_H
#include <asm/kprobes.h>
#include "../decode.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
extern void kprobes_remove_breakpoint(void *addr, unsigned int insn);
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 *,
const struct decode_checker *[]);
#ifdef CONFIG_THUMB2_KERNEL
extern const union decode_action kprobes_t32_actions[];
extern const union decode_action kprobes_t16_actions[];
extern const struct decode_checker *kprobes_t32_checkers[];
extern const struct decode_checker *kprobes_t16_checkers[];
#else /* !CONFIG_THUMB2_KERNEL */
extern const union decode_action kprobes_arm_actions[];
extern const struct decode_checker *kprobes_arm_checkers[];
#endif
#endif /* _ARM_KERNEL_KPROBES_H */
@@ -0,0 +1,358 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Kernel Probes Jump Optimization (Optprobes)
*
* Copyright (C) IBM Corporation, 2002, 2004
* Copyright (C) Hitachi Ltd., 2012
* Copyright (C) Huawei Inc., 2014
*/
#include <linux/kprobes.h>
#include <linux/jump_label.h>
#include <asm/kprobes.h>
#include <asm/cacheflush.h>
/* for arm_gen_branch */
#include <asm/insn.h>
/* for patch_text */
#include <asm/text-patching.h>
#include "core.h"
/*
* See register_usage_flags. If the probed instruction doesn't use PC,
* we can copy it into template and have it executed directly without
* simulation or emulation.
*/
#define ARM_REG_PC 15
#define can_kprobe_direct_exec(m) (!test_bit(ARM_REG_PC, &(m)))
/*
* NOTE: the first sub and add instruction will be modified according
* to the stack cost of the instruction.
*/
asm (
".global optprobe_template_entry\n"
"optprobe_template_entry:\n"
".global optprobe_template_sub_sp\n"
"optprobe_template_sub_sp:"
" sub sp, sp, #0xff\n"
" stmia sp, {r0 - r14} \n"
".global optprobe_template_add_sp\n"
"optprobe_template_add_sp:"
" add r3, sp, #0xff\n"
" str r3, [sp, #52]\n"
" mrs r4, cpsr\n"
" str r4, [sp, #64]\n"
" mov r1, sp\n"
" ldr r0, 1f\n"
" ldr r2, 2f\n"
/*
* AEABI requires an 8-bytes alignment stack. If
* SP % 8 != 0 (SP % 4 == 0 should be ensured),
* alloc more bytes here.
*/
" and r4, sp, #4\n"
" sub sp, sp, r4\n"
#if __LINUX_ARM_ARCH__ >= 5
" blx r2\n"
#else
" mov lr, pc\n"
" mov pc, r2\n"
#endif
" add sp, sp, r4\n"
" ldr r1, [sp, #64]\n"
" tst r1, #"__stringify(PSR_T_BIT)"\n"
" ldrne r2, [sp, #60]\n"
" orrne r2, #1\n"
" strne r2, [sp, #60] @ set bit0 of PC for thumb\n"
" msr cpsr_cxsf, r1\n"
".global optprobe_template_restore_begin\n"
"optprobe_template_restore_begin:\n"
" ldmia sp, {r0 - r15}\n"
".global optprobe_template_restore_orig_insn\n"
"optprobe_template_restore_orig_insn:\n"
" nop\n"
".global optprobe_template_restore_end\n"
"optprobe_template_restore_end:\n"
" nop\n"
".global optprobe_template_val\n"
"optprobe_template_val:\n"
"1: .long 0\n"
".global optprobe_template_call\n"
"optprobe_template_call:\n"
"2: .long 0\n"
".global optprobe_template_end\n"
"optprobe_template_end:\n");
#define TMPL_VAL_IDX \
((unsigned long *)optprobe_template_val - (unsigned long *)optprobe_template_entry)
#define TMPL_CALL_IDX \
((unsigned long *)optprobe_template_call - (unsigned long *)optprobe_template_entry)
#define TMPL_END_IDX \
((unsigned long *)optprobe_template_end - (unsigned long *)optprobe_template_entry)
#define TMPL_ADD_SP \
((unsigned long *)optprobe_template_add_sp - (unsigned long *)optprobe_template_entry)
#define TMPL_SUB_SP \
((unsigned long *)optprobe_template_sub_sp - (unsigned long *)optprobe_template_entry)
#define TMPL_RESTORE_BEGIN \
((unsigned long *)optprobe_template_restore_begin - (unsigned long *)optprobe_template_entry)
#define TMPL_RESTORE_ORIGN_INSN \
((unsigned long *)optprobe_template_restore_orig_insn - (unsigned long *)optprobe_template_entry)
#define TMPL_RESTORE_END \
((unsigned long *)optprobe_template_restore_end - (unsigned long *)optprobe_template_entry)
/*
* ARM can always optimize an instruction when using ARM ISA, except
* instructions like 'str r0, [sp, r1]' which store to stack and unable
* to determine stack space consumption statically.
*/
int arch_prepared_optinsn(struct arch_optimized_insn *optinsn)
{
return optinsn->insn != NULL;
}
/*
* In ARM ISA, kprobe opt always replace one instruction (4 bytes
* aligned and 4 bytes long). It is impossible to encounter another
* kprobe in the address range. So always return 0.
*/
int arch_check_optimized_kprobe(struct optimized_kprobe *op)
{
return 0;
}
/* Caller must ensure addr & 3 == 0 */
static int can_optimize(struct kprobe *kp)
{
if (kp->ainsn.stack_space < 0)
return 0;
/*
* 255 is the biggest imm can be used in 'sub r0, r0, #<imm>'.
* Number larger than 255 needs special encoding.
*/
if (kp->ainsn.stack_space > 255 - sizeof(struct pt_regs))
return 0;
return 1;
}
/* Free optimized instruction slot */
static void
__arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
{
if (op->optinsn.insn) {
free_optinsn_slot(op->optinsn.insn, dirty);
op->optinsn.insn = NULL;
}
}
static void
optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
{
unsigned long flags;
struct kprobe *p = &op->kp;
struct kprobe_ctlblk *kcb;
/* Save skipped registers */
regs->ARM_pc = (unsigned long)op->kp.addr;
regs->ARM_ORIG_r0 = ~0UL;
local_irq_save(flags);
kcb = get_kprobe_ctlblk();
if (kprobe_running()) {
kprobes_inc_nmissed_count(&op->kp);
} else {
__this_cpu_write(current_kprobe, &op->kp);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
opt_pre_handler(&op->kp, regs);
__this_cpu_write(current_kprobe, NULL);
}
/*
* We singlestep the replaced instruction only when it can't be
* executed directly during restore.
*/
if (!p->ainsn.kprobe_direct_exec)
op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
local_irq_restore(flags);
}
NOKPROBE_SYMBOL(optimized_callback)
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig)
{
kprobe_opcode_t *code;
unsigned long rel_chk;
unsigned long val;
unsigned long stack_protect = sizeof(struct pt_regs);
if (!can_optimize(orig))
return -EILSEQ;
code = get_optinsn_slot();
if (!code)
return -ENOMEM;
/*
* Verify if the address gap is in 32MiB range, because this uses
* a relative jump.
*
* kprobe opt use a 'b' instruction to branch to optinsn.insn.
* According to ARM manual, branch instruction is:
*
* 31 28 27 24 23 0
* +------+---+---+---+---+----------------+
* | cond | 1 | 0 | 1 | 0 | imm24 |
* +------+---+---+---+---+----------------+
*
* imm24 is a signed 24 bits integer. The real branch offset is computed
* by: imm32 = SignExtend(imm24:'00', 32);
*
* So the maximum forward branch should be:
* (0x007fffff << 2) = 0x01fffffc = 0x1fffffc
* The maximum backword branch should be:
* (0xff800000 << 2) = 0xfe000000 = -0x2000000
*
* We can simply check (rel & 0xfe000003):
* if rel is positive, (rel & 0xfe000000) shoule be 0
* if rel is negitive, (rel & 0xfe000000) should be 0xfe000000
* the last '3' is used for alignment checking.
*/
rel_chk = (unsigned long)((long)code -
(long)orig->addr + 8) & 0xfe000003;
if ((rel_chk != 0) && (rel_chk != 0xfe000000)) {
/*
* Different from x86, we free code buf directly instead of
* calling __arch_remove_optimized_kprobe() because
* we have not fill any field in op.
*/
free_optinsn_slot(code, 0);
return -ERANGE;
}
/* Copy arch-dep-instance from template. */
memcpy(code, (unsigned long *)optprobe_template_entry,
TMPL_END_IDX * sizeof(kprobe_opcode_t));
/* Adjust buffer according to instruction. */
BUG_ON(orig->ainsn.stack_space < 0);
stack_protect += orig->ainsn.stack_space;
/* Should have been filtered by can_optimize(). */
BUG_ON(stack_protect > 255);
/* Create a 'sub sp, sp, #<stack_protect>' */
code[TMPL_SUB_SP] = __opcode_to_mem_arm(0xe24dd000 | stack_protect);
/* Create a 'add r3, sp, #<stack_protect>' */
code[TMPL_ADD_SP] = __opcode_to_mem_arm(0xe28d3000 | stack_protect);
/* Set probe information */
val = (unsigned long)op;
code[TMPL_VAL_IDX] = val;
/* Set probe function call */
val = (unsigned long)optimized_callback;
code[TMPL_CALL_IDX] = val;
/* If possible, copy insn and have it executed during restore */
orig->ainsn.kprobe_direct_exec = false;
if (can_kprobe_direct_exec(orig->ainsn.register_usage_flags)) {
kprobe_opcode_t final_branch = arm_gen_branch(
(unsigned long)(&code[TMPL_RESTORE_END]),
(unsigned long)(op->kp.addr) + 4);
if (final_branch != 0) {
/*
* Replace original 'ldmia sp, {r0 - r15}' with
* 'ldmia {r0 - r14}', restore all registers except pc.
*/
code[TMPL_RESTORE_BEGIN] = __opcode_to_mem_arm(0xe89d7fff);
/* The original probed instruction */
code[TMPL_RESTORE_ORIGN_INSN] = __opcode_to_mem_arm(orig->opcode);
/* Jump back to next instruction */
code[TMPL_RESTORE_END] = __opcode_to_mem_arm(final_branch);
orig->ainsn.kprobe_direct_exec = true;
}
}
flush_icache_range((unsigned long)code,
(unsigned long)(&code[TMPL_END_IDX]));
/* Set op->optinsn.insn means prepared. */
op->optinsn.insn = code;
return 0;
}
void __kprobes arch_optimize_kprobes(struct list_head *oplist)
{
struct optimized_kprobe *op, *tmp;
list_for_each_entry_safe(op, tmp, oplist, list) {
unsigned long insn;
WARN_ON(kprobe_disabled(&op->kp));
/*
* Backup instructions which will be replaced
* by jump address
*/
memcpy(op->optinsn.copied_insn, op->kp.addr,
RELATIVEJUMP_SIZE);
insn = arm_gen_branch((unsigned long)op->kp.addr,
(unsigned long)op->optinsn.insn);
BUG_ON(insn == 0);
/*
* Make it a conditional branch if replaced insn
* is consitional
*/
insn = (__mem_to_opcode_arm(
op->optinsn.copied_insn[0]) & 0xf0000000) |
(insn & 0x0fffffff);
/*
* Similar to __arch_disarm_kprobe, operations which
* removing breakpoints must be wrapped by stop_machine
* to avoid racing.
*/
kprobes_remove_breakpoint(op->kp.addr, insn);
list_del_init(&op->list);
}
}
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
{
arch_arm_kprobe(&op->kp);
}
/*
* Recover original instructions and breakpoints from relative jumps.
* Caller must call with locking kprobe_mutex.
*/
void arch_unoptimize_kprobes(struct list_head *oplist,
struct list_head *done_list)
{
struct optimized_kprobe *op, *tmp;
list_for_each_entry_safe(op, tmp, oplist, list) {
arch_unoptimize_kprobe(op);
list_move(&op->list, done_list);
}
}
int arch_within_optimized_kprobe(struct optimized_kprobe *op,
kprobe_opcode_t *addr)
{
return (op->kp.addr <= addr &&
op->kp.addr + (RELATIVEJUMP_SIZE / sizeof(kprobe_opcode_t)) > addr);
}
void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
{
__arch_remove_optimized_kprobe(op, 1);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,460 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* arch/arm/probes/kprobes/test-core.h
*
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
*/
#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_TYPE_REG_MASKED 4
#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 or ARG_TYPE_REG_MASKED */
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, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
#define TEST_GROUP(title) \
verbose("\n"); \
verbose(title"\n"); \
verbose("---------------------------------------------------------\n");
#define TESTCASE_START(title) \
__asm__ __volatile__ ( \
".syntax unified \n\t" \
"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_REG_MASKED(reg, val) \
".byte "__stringify(ARG_TYPE_REG_MASKED)" \n\t" \
".byte "#reg" \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
#define TEST_RMASKED(code1, reg, mask, code2) \
TESTCASE_START(code1 #reg code2) \
TEST_ARG_REG_MASKED(reg, mask) \
TEST_ARG_END("") \
TEST_INSTRUCTION(code1 #reg code2) \
TESTCASE_END
/*
* We ignore the state of the imprecise abort disable flag (CPSR.A) because this
* can change randomly as the kernel doesn't take care to preserve or initialise
* this across context switches. Also, with Security Extensions, the flag may
* not be under control of the kernel; for this reason we ignore the state of
* the FIQ disable flag CPSR.F as well.
*/
#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT)
/*
* 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
void __kprobes_test_case_start(void);
void __kprobes_test_case_end_16(void);
void __kprobes_test_case_end_32(void);
File diff suppressed because it is too large Load Diff