You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-next/kprobes' into for-next/core
* kprobes: arm64: kprobes: Add KASAN instrumentation around stack accesses arm64: kprobes: Cleanup jprobe_return arm64: kprobes: Fix overflow when saving stack arm64: kprobes: WARN if attempting to step with PSTATE.D=1 kprobes: Add arm64 case in kprobe example module arm64: Add kernel return probes support (kretprobes) arm64: Add trampoline code for kretprobes arm64: kprobes instruction simulation support arm64: Treat all entry code as non-kprobe-able arm64: Blacklist non-kprobe-able symbol arm64: Kprobes with single stepping support arm64: add conditional instruction simulation support arm64: Add more test functions to insn.c arm64: Add HAVE_REGS_AND_STACK_ACCESS_API feature
This commit is contained in:
@@ -121,7 +121,6 @@ extern unsigned long profile_pc(struct pt_regs *regs);
|
||||
#define MAX_REG_OFFSET (offsetof(struct pt_regs, ARM_ORIG_r0))
|
||||
|
||||
extern int regs_query_register_offset(const char *name);
|
||||
extern const char *regs_query_register_name(unsigned int offset);
|
||||
extern bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr);
|
||||
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
|
||||
unsigned int n);
|
||||
|
||||
@@ -86,8 +86,11 @@ config ARM64
|
||||
select HAVE_PERF_EVENTS
|
||||
select HAVE_PERF_REGS
|
||||
select HAVE_PERF_USER_STACK_DUMP
|
||||
select HAVE_REGS_AND_STACK_ACCESS_API
|
||||
select HAVE_RCU_TABLE_FREE
|
||||
select HAVE_SYSCALL_TRACEPOINTS
|
||||
select HAVE_KPROBES
|
||||
select HAVE_KRETPROBES if HAVE_KPROBES
|
||||
select IOMMU_DMA if IOMMU_SUPPORT
|
||||
select IRQ_DOMAIN
|
||||
select IRQ_FORCED_THREADING
|
||||
|
||||
@@ -66,6 +66,11 @@
|
||||
|
||||
#define CACHE_FLUSH_IS_SAFE 1
|
||||
|
||||
/* kprobes BRK opcodes with ESR encoding */
|
||||
#define BRK64_ESR_MASK 0xFFFF
|
||||
#define BRK64_ESR_KPROBES 0x0004
|
||||
#define BRK64_OPCODE_KPROBES (AARCH64_BREAK_MON | (BRK64_ESR_KPROBES << 5))
|
||||
|
||||
/* AArch32 */
|
||||
#define DBG_ESR_EVT_BKPT 0x4
|
||||
#define DBG_ESR_EVT_VECC 0x5
|
||||
|
||||
@@ -120,6 +120,29 @@ enum aarch64_insn_register {
|
||||
AARCH64_INSN_REG_SP = 31 /* Stack pointer: as load/store base reg */
|
||||
};
|
||||
|
||||
enum aarch64_insn_special_register {
|
||||
AARCH64_INSN_SPCLREG_SPSR_EL1 = 0xC200,
|
||||
AARCH64_INSN_SPCLREG_ELR_EL1 = 0xC201,
|
||||
AARCH64_INSN_SPCLREG_SP_EL0 = 0xC208,
|
||||
AARCH64_INSN_SPCLREG_SPSEL = 0xC210,
|
||||
AARCH64_INSN_SPCLREG_CURRENTEL = 0xC212,
|
||||
AARCH64_INSN_SPCLREG_DAIF = 0xDA11,
|
||||
AARCH64_INSN_SPCLREG_NZCV = 0xDA10,
|
||||
AARCH64_INSN_SPCLREG_FPCR = 0xDA20,
|
||||
AARCH64_INSN_SPCLREG_DSPSR_EL0 = 0xDA28,
|
||||
AARCH64_INSN_SPCLREG_DLR_EL0 = 0xDA29,
|
||||
AARCH64_INSN_SPCLREG_SPSR_EL2 = 0xE200,
|
||||
AARCH64_INSN_SPCLREG_ELR_EL2 = 0xE201,
|
||||
AARCH64_INSN_SPCLREG_SP_EL1 = 0xE208,
|
||||
AARCH64_INSN_SPCLREG_SPSR_INQ = 0xE218,
|
||||
AARCH64_INSN_SPCLREG_SPSR_ABT = 0xE219,
|
||||
AARCH64_INSN_SPCLREG_SPSR_UND = 0xE21A,
|
||||
AARCH64_INSN_SPCLREG_SPSR_FIQ = 0xE21B,
|
||||
AARCH64_INSN_SPCLREG_SPSR_EL3 = 0xF200,
|
||||
AARCH64_INSN_SPCLREG_ELR_EL3 = 0xF201,
|
||||
AARCH64_INSN_SPCLREG_SP_EL2 = 0xF210
|
||||
};
|
||||
|
||||
enum aarch64_insn_variant {
|
||||
AARCH64_INSN_VARIANT_32BIT,
|
||||
AARCH64_INSN_VARIANT_64BIT
|
||||
@@ -223,8 +246,15 @@ static __always_inline bool aarch64_insn_is_##abbr(u32 code) \
|
||||
static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \
|
||||
{ return (val); }
|
||||
|
||||
__AARCH64_INSN_FUNCS(adr_adrp, 0x1F000000, 0x10000000)
|
||||
__AARCH64_INSN_FUNCS(prfm_lit, 0xFF000000, 0xD8000000)
|
||||
__AARCH64_INSN_FUNCS(str_reg, 0x3FE0EC00, 0x38206800)
|
||||
__AARCH64_INSN_FUNCS(ldr_reg, 0x3FE0EC00, 0x38606800)
|
||||
__AARCH64_INSN_FUNCS(ldr_lit, 0xBF000000, 0x18000000)
|
||||
__AARCH64_INSN_FUNCS(ldrsw_lit, 0xFF000000, 0x98000000)
|
||||
__AARCH64_INSN_FUNCS(exclusive, 0x3F800000, 0x08000000)
|
||||
__AARCH64_INSN_FUNCS(load_ex, 0x3F400000, 0x08400000)
|
||||
__AARCH64_INSN_FUNCS(store_ex, 0x3F400000, 0x08000000)
|
||||
__AARCH64_INSN_FUNCS(stp_post, 0x7FC00000, 0x28800000)
|
||||
__AARCH64_INSN_FUNCS(ldp_post, 0x7FC00000, 0x28C00000)
|
||||
__AARCH64_INSN_FUNCS(stp_pre, 0x7FC00000, 0x29800000)
|
||||
@@ -273,10 +303,15 @@ __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001)
|
||||
__AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002)
|
||||
__AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003)
|
||||
__AARCH64_INSN_FUNCS(brk, 0xFFE0001F, 0xD4200000)
|
||||
__AARCH64_INSN_FUNCS(exception, 0xFF000000, 0xD4000000)
|
||||
__AARCH64_INSN_FUNCS(hint, 0xFFFFF01F, 0xD503201F)
|
||||
__AARCH64_INSN_FUNCS(br, 0xFFFFFC1F, 0xD61F0000)
|
||||
__AARCH64_INSN_FUNCS(blr, 0xFFFFFC1F, 0xD63F0000)
|
||||
__AARCH64_INSN_FUNCS(ret, 0xFFFFFC1F, 0xD65F0000)
|
||||
__AARCH64_INSN_FUNCS(eret, 0xFFFFFFFF, 0xD69F03E0)
|
||||
__AARCH64_INSN_FUNCS(mrs, 0xFFF00000, 0xD5300000)
|
||||
__AARCH64_INSN_FUNCS(msr_imm, 0xFFF8F01F, 0xD500401F)
|
||||
__AARCH64_INSN_FUNCS(msr_reg, 0xFFF00000, 0xD5100000)
|
||||
|
||||
#undef __AARCH64_INSN_FUNCS
|
||||
|
||||
@@ -286,6 +321,8 @@ bool aarch64_insn_is_branch_imm(u32 insn);
|
||||
int aarch64_insn_read(void *addr, u32 *insnp);
|
||||
int aarch64_insn_write(void *addr, u32 insn);
|
||||
enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn);
|
||||
bool aarch64_insn_uses_literal(u32 insn);
|
||||
bool aarch64_insn_is_branch(u32 insn);
|
||||
u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn);
|
||||
u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
|
||||
u32 insn, u64 imm);
|
||||
@@ -367,9 +404,13 @@ bool aarch32_insn_is_wide(u32 insn);
|
||||
#define A32_RT_OFFSET 12
|
||||
#define A32_RT2_OFFSET 0
|
||||
|
||||
u32 aarch64_insn_extract_system_reg(u32 insn);
|
||||
u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
|
||||
u32 aarch32_insn_mcr_extract_opc2(u32 insn);
|
||||
u32 aarch32_insn_mcr_extract_crm(u32 insn);
|
||||
|
||||
typedef bool (pstate_check_t)(unsigned long);
|
||||
extern pstate_check_t * const aarch32_opcode_cond_checks[16];
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* __ASM_INSN_H */
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* arch/arm64/include/asm/kprobes.h
|
||||
*
|
||||
* Copyright (C) 2013 Linaro Limited
|
||||
*
|
||||
* 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_KPROBES_H
|
||||
#define _ARM_KPROBES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
#define __ARCH_WANT_KPROBES_INSN_SLOT
|
||||
#define MAX_INSN_SIZE 1
|
||||
#define MAX_STACK_SIZE 128
|
||||
|
||||
#define flush_insn_slot(p) do { } while (0)
|
||||
#define kretprobe_blacklist_size 0
|
||||
|
||||
#include <asm/probes.h>
|
||||
|
||||
struct prev_kprobe {
|
||||
struct kprobe *kp;
|
||||
unsigned int status;
|
||||
};
|
||||
|
||||
/* Single step context for kprobe */
|
||||
struct kprobe_step_ctx {
|
||||
unsigned long ss_pending;
|
||||
unsigned long match_addr;
|
||||
};
|
||||
|
||||
/* per-cpu kprobe control block */
|
||||
struct kprobe_ctlblk {
|
||||
unsigned int kprobe_status;
|
||||
unsigned long saved_irqflag;
|
||||
struct prev_kprobe prev_kprobe;
|
||||
struct kprobe_step_ctx ss_ctx;
|
||||
struct pt_regs jprobe_saved_regs;
|
||||
char jprobes_stack[MAX_STACK_SIZE];
|
||||
};
|
||||
|
||||
void arch_remove_kprobe(struct kprobe *);
|
||||
int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);
|
||||
int kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data);
|
||||
int kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr);
|
||||
int kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr);
|
||||
void kretprobe_trampoline(void);
|
||||
void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
|
||||
|
||||
#endif /* _ARM_KPROBES_H */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* arch/arm64/include/asm/probes.h
|
||||
*
|
||||
* Copyright (C) 2013 Linaro Limited
|
||||
*
|
||||
* 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_PROBES_H
|
||||
#define _ARM_PROBES_H
|
||||
|
||||
#include <asm/opcodes.h>
|
||||
|
||||
struct kprobe;
|
||||
struct arch_specific_insn;
|
||||
|
||||
typedef u32 kprobe_opcode_t;
|
||||
typedef void (kprobes_handler_t) (u32 opcode, long addr, struct pt_regs *);
|
||||
|
||||
/* architecture specific copy of original instruction */
|
||||
struct arch_specific_insn {
|
||||
kprobe_opcode_t *insn;
|
||||
pstate_check_t *pstate_cc;
|
||||
kprobes_handler_t *handler;
|
||||
/* restore address after step xol */
|
||||
unsigned long restore;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -73,6 +73,7 @@
|
||||
#define COMPAT_PT_DATA_ADDR 0x10004
|
||||
#define COMPAT_PT_TEXT_END_ADDR 0x10008
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <linux/bug.h>
|
||||
|
||||
/* sizeof(struct user) for AArch32 */
|
||||
#define COMPAT_USER_SZ 296
|
||||
@@ -118,6 +119,8 @@ struct pt_regs {
|
||||
u64 syscallno;
|
||||
};
|
||||
|
||||
#define MAX_REG_OFFSET offsetof(struct pt_regs, pstate)
|
||||
|
||||
#define arch_has_single_step() (1)
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
@@ -143,9 +146,59 @@ struct pt_regs {
|
||||
#define fast_interrupts_enabled(regs) \
|
||||
(!((regs)->pstate & PSR_F_BIT))
|
||||
|
||||
#define user_stack_pointer(regs) \
|
||||
#define GET_USP(regs) \
|
||||
(!compat_user_mode(regs) ? (regs)->sp : (regs)->compat_sp)
|
||||
|
||||
#define SET_USP(ptregs, value) \
|
||||
(!compat_user_mode(regs) ? ((regs)->sp = value) : ((regs)->compat_sp = value))
|
||||
|
||||
extern int regs_query_register_offset(const char *name);
|
||||
extern const char *regs_query_register_name(unsigned int offset);
|
||||
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
|
||||
unsigned int n);
|
||||
|
||||
/**
|
||||
* regs_get_register() - get register value from its offset
|
||||
* @regs: pt_regs from which register value is gotten
|
||||
* @offset: offset of the register.
|
||||
*
|
||||
* regs_get_register returns the value of a register whose offset from @regs.
|
||||
* The @offset is the offset of the register in struct pt_regs.
|
||||
* If @offset is bigger than MAX_REG_OFFSET, this returns 0.
|
||||
*/
|
||||
static inline u64 regs_get_register(struct pt_regs *regs, unsigned int offset)
|
||||
{
|
||||
u64 val = 0;
|
||||
|
||||
WARN_ON(offset & 7);
|
||||
|
||||
offset >>= 3;
|
||||
switch (offset) {
|
||||
case 0 ... 30:
|
||||
val = regs->regs[offset];
|
||||
break;
|
||||
case offsetof(struct pt_regs, sp) >> 3:
|
||||
val = regs->sp;
|
||||
break;
|
||||
case offsetof(struct pt_regs, pc) >> 3:
|
||||
val = regs->pc;
|
||||
break;
|
||||
case offsetof(struct pt_regs, pstate) >> 3:
|
||||
val = regs->pstate;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Valid only for Kernel mode traps. */
|
||||
static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
|
||||
{
|
||||
return regs->sp;
|
||||
}
|
||||
|
||||
static inline unsigned long regs_return_value(struct pt_regs *regs)
|
||||
{
|
||||
return regs->regs[0];
|
||||
@@ -155,8 +208,15 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
|
||||
struct task_struct;
|
||||
int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task);
|
||||
|
||||
#define instruction_pointer(regs) ((unsigned long)(regs)->pc)
|
||||
#define GET_IP(regs) ((unsigned long)(regs)->pc)
|
||||
#define SET_IP(regs, value) ((regs)->pc = ((u64) (value)))
|
||||
|
||||
#define GET_FP(ptregs) ((unsigned long)(ptregs)->regs[29])
|
||||
#define SET_FP(ptregs, value) ((ptregs)->regs[29] = ((u64) (value)))
|
||||
|
||||
#include <asm-generic/ptrace.h>
|
||||
|
||||
#undef profile_pc
|
||||
extern unsigned long profile_pc(struct pt_regs *regs);
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
@@ -26,8 +26,7 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE
|
||||
$(call if_changed,objcopy)
|
||||
|
||||
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
|
||||
sys_compat.o entry32.o \
|
||||
../../arm/kernel/opcodes.o
|
||||
sys_compat.o entry32.o
|
||||
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
|
||||
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
|
||||
arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
|
||||
@@ -49,7 +48,7 @@ arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o
|
||||
arm64-obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o \
|
||||
cpu-reset.o
|
||||
|
||||
obj-y += $(arm64-obj-y) vdso/
|
||||
obj-y += $(arm64-obj-y) vdso/ probes/
|
||||
obj-m += $(arm64-obj-m)
|
||||
head-y := head.o
|
||||
extra-y += $(head-y) vmlinux.lds
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include <asm/checksum.h>
|
||||
|
||||
@@ -68,6 +69,7 @@ EXPORT_SYMBOL(test_and_change_bit);
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
EXPORT_SYMBOL(_mcount);
|
||||
NOKPROBE_SYMBOL(_mcount);
|
||||
#endif
|
||||
|
||||
/* arm-smccc */
|
||||
|
||||
@@ -344,6 +344,21 @@ static int emulate_swpX(unsigned int address, unsigned int *data,
|
||||
return res;
|
||||
}
|
||||
|
||||
#define ARM_OPCODE_CONDITION_UNCOND 0xf
|
||||
|
||||
static unsigned int __kprobes aarch32_check_condition(u32 opcode, u32 psr)
|
||||
{
|
||||
u32 cc_bits = opcode >> 28;
|
||||
|
||||
if (cc_bits != ARM_OPCODE_CONDITION_UNCOND) {
|
||||
if ((*aarch32_opcode_cond_checks[cc_bits])(psr))
|
||||
return ARM_OPCODE_CONDTEST_PASS;
|
||||
else
|
||||
return ARM_OPCODE_CONDTEST_FAIL;
|
||||
}
|
||||
return ARM_OPCODE_CONDTEST_UNCOND;
|
||||
}
|
||||
|
||||
/*
|
||||
* swp_handler logs the id of calling process, dissects the instruction, sanity
|
||||
* checks the memory location, calls emulate_swpX for the actual operation and
|
||||
@@ -358,7 +373,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
|
||||
|
||||
type = instr & TYPE_SWPB;
|
||||
|
||||
switch (arm_check_condition(instr, regs->pstate)) {
|
||||
switch (aarch32_check_condition(instr, regs->pstate)) {
|
||||
case ARM_OPCODE_CONDTEST_PASS:
|
||||
break;
|
||||
case ARM_OPCODE_CONDTEST_FAIL:
|
||||
@@ -440,7 +455,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
|
||||
{
|
||||
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
|
||||
|
||||
switch (arm_check_condition(instr, regs->pstate)) {
|
||||
switch (aarch32_check_condition(instr, regs->pstate)) {
|
||||
case ARM_OPCODE_CONDTEST_PASS:
|
||||
break;
|
||||
case ARM_OPCODE_CONDTEST_FAIL:
|
||||
|
||||
@@ -51,6 +51,17 @@ int main(void)
|
||||
DEFINE(S_X5, offsetof(struct pt_regs, regs[5]));
|
||||
DEFINE(S_X6, offsetof(struct pt_regs, regs[6]));
|
||||
DEFINE(S_X7, offsetof(struct pt_regs, regs[7]));
|
||||
DEFINE(S_X8, offsetof(struct pt_regs, regs[8]));
|
||||
DEFINE(S_X10, offsetof(struct pt_regs, regs[10]));
|
||||
DEFINE(S_X12, offsetof(struct pt_regs, regs[12]));
|
||||
DEFINE(S_X14, offsetof(struct pt_regs, regs[14]));
|
||||
DEFINE(S_X16, offsetof(struct pt_regs, regs[16]));
|
||||
DEFINE(S_X18, offsetof(struct pt_regs, regs[18]));
|
||||
DEFINE(S_X20, offsetof(struct pt_regs, regs[20]));
|
||||
DEFINE(S_X22, offsetof(struct pt_regs, regs[22]));
|
||||
DEFINE(S_X24, offsetof(struct pt_regs, regs[24]));
|
||||
DEFINE(S_X26, offsetof(struct pt_regs, regs[26]));
|
||||
DEFINE(S_X28, offsetof(struct pt_regs, regs[28]));
|
||||
DEFINE(S_LR, offsetof(struct pt_regs, regs[30]));
|
||||
DEFINE(S_SP, offsetof(struct pt_regs, sp));
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@@ -48,6 +49,7 @@ static void mdscr_write(u32 mdscr)
|
||||
asm volatile("msr mdscr_el1, %0" :: "r" (mdscr));
|
||||
local_dbg_restore(flags);
|
||||
}
|
||||
NOKPROBE_SYMBOL(mdscr_write);
|
||||
|
||||
static u32 mdscr_read(void)
|
||||
{
|
||||
@@ -55,6 +57,7 @@ static u32 mdscr_read(void)
|
||||
asm volatile("mrs %0, mdscr_el1" : "=r" (mdscr));
|
||||
return mdscr;
|
||||
}
|
||||
NOKPROBE_SYMBOL(mdscr_read);
|
||||
|
||||
/*
|
||||
* Allow root to disable self-hosted debug from userspace.
|
||||
@@ -103,6 +106,7 @@ void enable_debug_monitors(enum dbg_active_el el)
|
||||
mdscr_write(mdscr);
|
||||
}
|
||||
}
|
||||
NOKPROBE_SYMBOL(enable_debug_monitors);
|
||||
|
||||
void disable_debug_monitors(enum dbg_active_el el)
|
||||
{
|
||||
@@ -123,6 +127,7 @@ void disable_debug_monitors(enum dbg_active_el el)
|
||||
mdscr_write(mdscr);
|
||||
}
|
||||
}
|
||||
NOKPROBE_SYMBOL(disable_debug_monitors);
|
||||
|
||||
/*
|
||||
* OS lock clearing.
|
||||
@@ -167,11 +172,13 @@ static void set_regs_spsr_ss(struct pt_regs *regs)
|
||||
{
|
||||
regs->pstate |= DBG_SPSR_SS;
|
||||
}
|
||||
NOKPROBE_SYMBOL(set_regs_spsr_ss);
|
||||
|
||||
static void clear_regs_spsr_ss(struct pt_regs *regs)
|
||||
{
|
||||
regs->pstate &= ~DBG_SPSR_SS;
|
||||
}
|
||||
NOKPROBE_SYMBOL(clear_regs_spsr_ss);
|
||||
|
||||
/* EL1 Single Step Handler hooks */
|
||||
static LIST_HEAD(step_hook);
|
||||
@@ -215,6 +222,7 @@ static int call_step_hook(struct pt_regs *regs, unsigned int esr)
|
||||
|
||||
return retval;
|
||||
}
|
||||
NOKPROBE_SYMBOL(call_step_hook);
|
||||
|
||||
static void send_user_sigtrap(int si_code)
|
||||
{
|
||||
@@ -256,6 +264,10 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
|
||||
*/
|
||||
user_rewind_single_step(current);
|
||||
} else {
|
||||
#ifdef CONFIG_KPROBES
|
||||
if (kprobe_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
|
||||
return 0;
|
||||
#endif
|
||||
if (call_step_hook(regs, esr) == DBG_HOOK_HANDLED)
|
||||
return 0;
|
||||
|
||||
@@ -269,6 +281,7 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
|
||||
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(single_step_handler);
|
||||
|
||||
/*
|
||||
* Breakpoint handler is re-entrant as another breakpoint can
|
||||
@@ -306,19 +319,28 @@ static int call_break_hook(struct pt_regs *regs, unsigned int esr)
|
||||
|
||||
return fn ? fn(regs, esr) : DBG_HOOK_ERROR;
|
||||
}
|
||||
NOKPROBE_SYMBOL(call_break_hook);
|
||||
|
||||
static int brk_handler(unsigned long addr, unsigned int esr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if (user_mode(regs)) {
|
||||
send_user_sigtrap(TRAP_BRKPT);
|
||||
} else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
|
||||
pr_warning("Unexpected kernel BRK exception at EL1\n");
|
||||
}
|
||||
#ifdef CONFIG_KPROBES
|
||||
else if ((esr & BRK64_ESR_MASK) == BRK64_ESR_KPROBES) {
|
||||
if (kprobe_breakpoint_handler(regs, esr) != DBG_HOOK_HANDLED)
|
||||
return -EFAULT;
|
||||
}
|
||||
#endif
|
||||
else if (call_break_hook(regs, esr) != DBG_HOOK_HANDLED) {
|
||||
pr_warn("Unexpected kernel BRK exception at EL1\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(brk_handler);
|
||||
|
||||
int aarch32_break_handler(struct pt_regs *regs)
|
||||
{
|
||||
@@ -355,6 +377,7 @@ int aarch32_break_handler(struct pt_regs *regs)
|
||||
send_user_sigtrap(TRAP_BRKPT);
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(aarch32_break_handler);
|
||||
|
||||
static int __init debug_traps_init(void)
|
||||
{
|
||||
@@ -376,6 +399,7 @@ void user_rewind_single_step(struct task_struct *task)
|
||||
if (test_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP))
|
||||
set_regs_spsr_ss(task_pt_regs(task));
|
||||
}
|
||||
NOKPROBE_SYMBOL(user_rewind_single_step);
|
||||
|
||||
void user_fastforward_single_step(struct task_struct *task)
|
||||
{
|
||||
@@ -391,6 +415,7 @@ void kernel_enable_single_step(struct pt_regs *regs)
|
||||
mdscr_write(mdscr_read() | DBG_MDSCR_SS);
|
||||
enable_debug_monitors(DBG_ACTIVE_EL1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(kernel_enable_single_step);
|
||||
|
||||
void kernel_disable_single_step(void)
|
||||
{
|
||||
@@ -398,12 +423,14 @@ void kernel_disable_single_step(void)
|
||||
mdscr_write(mdscr_read() & ~DBG_MDSCR_SS);
|
||||
disable_debug_monitors(DBG_ACTIVE_EL1);
|
||||
}
|
||||
NOKPROBE_SYMBOL(kernel_disable_single_step);
|
||||
|
||||
int kernel_active_single_step(void)
|
||||
{
|
||||
WARN_ON(!irqs_disabled());
|
||||
return mdscr_read() & DBG_MDSCR_SS;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kernel_active_single_step);
|
||||
|
||||
/* ptrace API */
|
||||
void user_enable_single_step(struct task_struct *task)
|
||||
@@ -411,8 +438,10 @@ void user_enable_single_step(struct task_struct *task)
|
||||
set_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
|
||||
set_regs_spsr_ss(task_pt_regs(task));
|
||||
}
|
||||
NOKPROBE_SYMBOL(user_enable_single_step);
|
||||
|
||||
void user_disable_single_step(struct task_struct *task)
|
||||
{
|
||||
clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
|
||||
}
|
||||
NOKPROBE_SYMBOL(user_disable_single_step);
|
||||
|
||||
@@ -242,6 +242,7 @@ tsk .req x28 // current thread_info
|
||||
/*
|
||||
* Exception vectors.
|
||||
*/
|
||||
.pushsection ".entry.text", "ax"
|
||||
|
||||
.align 11
|
||||
ENTRY(vectors)
|
||||
@@ -784,6 +785,8 @@ __ni_sys_trace:
|
||||
bl do_ni_syscall
|
||||
b __sys_trace_return
|
||||
|
||||
.popsection // .entry.text
|
||||
|
||||
/*
|
||||
* Special system call wrappers.
|
||||
*/
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/smp.h>
|
||||
@@ -127,6 +128,7 @@ static u64 read_wb_reg(int reg, int n)
|
||||
|
||||
return val;
|
||||
}
|
||||
NOKPROBE_SYMBOL(read_wb_reg);
|
||||
|
||||
static void write_wb_reg(int reg, int n, u64 val)
|
||||
{
|
||||
@@ -140,6 +142,7 @@ static void write_wb_reg(int reg, int n, u64 val)
|
||||
}
|
||||
isb();
|
||||
}
|
||||
NOKPROBE_SYMBOL(write_wb_reg);
|
||||
|
||||
/*
|
||||
* Convert a breakpoint privilege level to the corresponding exception
|
||||
@@ -157,6 +160,7 @@ static enum dbg_active_el debug_exception_level(int privilege)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
NOKPROBE_SYMBOL(debug_exception_level);
|
||||
|
||||
enum hw_breakpoint_ops {
|
||||
HW_BREAKPOINT_INSTALL,
|
||||
@@ -575,6 +579,7 @@ static void toggle_bp_registers(int reg, enum dbg_active_el el, int enable)
|
||||
write_wb_reg(reg, i, ctrl);
|
||||
}
|
||||
}
|
||||
NOKPROBE_SYMBOL(toggle_bp_registers);
|
||||
|
||||
/*
|
||||
* Debug exception handlers.
|
||||
@@ -654,6 +659,7 @@ unlock:
|
||||
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(breakpoint_handler);
|
||||
|
||||
static int watchpoint_handler(unsigned long addr, unsigned int esr,
|
||||
struct pt_regs *regs)
|
||||
@@ -756,6 +762,7 @@ unlock:
|
||||
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(watchpoint_handler);
|
||||
|
||||
/*
|
||||
* Handle single-step exception.
|
||||
@@ -813,6 +820,7 @@ int reinstall_suspended_bps(struct pt_regs *regs)
|
||||
|
||||
return !handled_exception;
|
||||
}
|
||||
NOKPROBE_SYMBOL(reinstall_suspended_bps);
|
||||
|
||||
/*
|
||||
* Context-switcher for restoring suspended breakpoints.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/insn.h>
|
||||
|
||||
#define AARCH64_INSN_SF_BIT BIT(31)
|
||||
@@ -162,6 +163,32 @@ static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn)
|
||||
aarch64_insn_is_nop(insn);
|
||||
}
|
||||
|
||||
bool __kprobes aarch64_insn_uses_literal(u32 insn)
|
||||
{
|
||||
/* ldr/ldrsw (literal), prfm */
|
||||
|
||||
return aarch64_insn_is_ldr_lit(insn) ||
|
||||
aarch64_insn_is_ldrsw_lit(insn) ||
|
||||
aarch64_insn_is_adr_adrp(insn) ||
|
||||
aarch64_insn_is_prfm_lit(insn);
|
||||
}
|
||||
|
||||
bool __kprobes aarch64_insn_is_branch(u32 insn)
|
||||
{
|
||||
/* b, bl, cb*, tb*, b.cond, br, blr */
|
||||
|
||||
return aarch64_insn_is_b(insn) ||
|
||||
aarch64_insn_is_bl(insn) ||
|
||||
aarch64_insn_is_cbz(insn) ||
|
||||
aarch64_insn_is_cbnz(insn) ||
|
||||
aarch64_insn_is_tbz(insn) ||
|
||||
aarch64_insn_is_tbnz(insn) ||
|
||||
aarch64_insn_is_ret(insn) ||
|
||||
aarch64_insn_is_br(insn) ||
|
||||
aarch64_insn_is_blr(insn) ||
|
||||
aarch64_insn_is_bcond(insn);
|
||||
}
|
||||
|
||||
/*
|
||||
* ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a
|
||||
* Section B2.6.5 "Concurrent modification and execution of instructions":
|
||||
@@ -1175,6 +1202,14 @@ u32 aarch64_set_branch_offset(u32 insn, s32 offset)
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the Op/CR data from a msr/mrs instruction.
|
||||
*/
|
||||
u32 aarch64_insn_extract_system_reg(u32 insn)
|
||||
{
|
||||
return (insn & 0x1FFFE0) >> 5;
|
||||
}
|
||||
|
||||
bool aarch32_insn_is_wide(u32 insn)
|
||||
{
|
||||
return insn >= 0xe800;
|
||||
@@ -1200,3 +1235,101 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn)
|
||||
{
|
||||
return insn & CRM_MASK;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_eq(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_Z_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_ne(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_Z_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_cs(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_C_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_cc(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_C_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_mi(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_N_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_pl(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_N_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_vs(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_V_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_vc(unsigned long pstate)
|
||||
{
|
||||
return (pstate & PSR_V_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_hi(unsigned long pstate)
|
||||
{
|
||||
pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
|
||||
return (pstate & PSR_C_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_ls(unsigned long pstate)
|
||||
{
|
||||
pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
|
||||
return (pstate & PSR_C_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_ge(unsigned long pstate)
|
||||
{
|
||||
pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||
return (pstate & PSR_N_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_lt(unsigned long pstate)
|
||||
{
|
||||
pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||
return (pstate & PSR_N_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_gt(unsigned long pstate)
|
||||
{
|
||||
/*PSR_N_BIT ^= PSR_V_BIT */
|
||||
unsigned long temp = pstate ^ (pstate << 3);
|
||||
|
||||
temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
|
||||
return (temp & PSR_N_BIT) == 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_le(unsigned long pstate)
|
||||
{
|
||||
/*PSR_N_BIT ^= PSR_V_BIT */
|
||||
unsigned long temp = pstate ^ (pstate << 3);
|
||||
|
||||
temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
|
||||
return (temp & PSR_N_BIT) != 0;
|
||||
}
|
||||
|
||||
static bool __kprobes __check_al(unsigned long pstate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that the ARMv8 ARM calls condition code 0b1111 "nv", but states that
|
||||
* it behaves identically to 0b1110 ("al").
|
||||
*/
|
||||
pstate_check_t * const aarch32_opcode_cond_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
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
|
||||
@@ -230,6 +231,7 @@ static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr)
|
||||
kgdb_handle_exception(1, SIGTRAP, 0, regs);
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_brk_fn)
|
||||
|
||||
static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr)
|
||||
{
|
||||
@@ -238,12 +240,14 @@ static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr)
|
||||
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
|
||||
|
||||
static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)
|
||||
{
|
||||
kgdb_handle_exception(1, SIGTRAP, 0, regs);
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_step_brk_fn);
|
||||
|
||||
static struct break_hook kgdb_brkpt_hook = {
|
||||
.esr_mask = 0xffffffff,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o \
|
||||
kprobes_trampoline.o \
|
||||
simulate-insn.o
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* arch/arm64/kernel/probes/decode-insn.c
|
||||
*
|
||||
* Copyright (C) 2013 Linaro Limited.
|
||||
*
|
||||
* 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 <asm/kprobes.h>
|
||||
#include <asm/insn.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
#include "decode-insn.h"
|
||||
#include "simulate-insn.h"
|
||||
|
||||
static bool __kprobes aarch64_insn_is_steppable(u32 insn)
|
||||
{
|
||||
/*
|
||||
* Branch instructions will write a new value into the PC which is
|
||||
* likely to be relative to the XOL address and therefore invalid.
|
||||
* Deliberate generation of an exception during stepping is also not
|
||||
* currently safe. Lastly, MSR instructions can do any number of nasty
|
||||
* things we can't handle during single-stepping.
|
||||
*/
|
||||
if (aarch64_get_insn_class(insn) == AARCH64_INSN_CLS_BR_SYS) {
|
||||
if (aarch64_insn_is_branch(insn) ||
|
||||
aarch64_insn_is_msr_imm(insn) ||
|
||||
aarch64_insn_is_msr_reg(insn) ||
|
||||
aarch64_insn_is_exception(insn) ||
|
||||
aarch64_insn_is_eret(insn))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The MRS instruction may not return a correct value when
|
||||
* executing in the single-stepping environment. We do make one
|
||||
* exception, for reading the DAIF bits.
|
||||
*/
|
||||
if (aarch64_insn_is_mrs(insn))
|
||||
return aarch64_insn_extract_system_reg(insn)
|
||||
!= AARCH64_INSN_SPCLREG_DAIF;
|
||||
|
||||
/*
|
||||
* The HINT instruction is is problematic when single-stepping,
|
||||
* except for the NOP case.
|
||||
*/
|
||||
if (aarch64_insn_is_hint(insn))
|
||||
return aarch64_insn_is_nop(insn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Instructions which load PC relative literals are not going to work
|
||||
* when executed from an XOL slot. Instructions doing an exclusive
|
||||
* load/store are not going to complete successfully when single-step
|
||||
* exception handling happens in the middle of the sequence.
|
||||
*/
|
||||
if (aarch64_insn_uses_literal(insn) ||
|
||||
aarch64_insn_is_exclusive(insn))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
static enum kprobe_insn __kprobes
|
||||
arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
|
||||
{
|
||||
/*
|
||||
* Instructions reading or modifying the PC won't work from the XOL
|
||||
* slot.
|
||||
*/
|
||||
if (aarch64_insn_is_steppable(insn))
|
||||
return INSN_GOOD;
|
||||
|
||||
if (aarch64_insn_is_bcond(insn)) {
|
||||
asi->handler = simulate_b_cond;
|
||||
} else if (aarch64_insn_is_cbz(insn) ||
|
||||
aarch64_insn_is_cbnz(insn)) {
|
||||
asi->handler = simulate_cbz_cbnz;
|
||||
} else if (aarch64_insn_is_tbz(insn) ||
|
||||
aarch64_insn_is_tbnz(insn)) {
|
||||
asi->handler = simulate_tbz_tbnz;
|
||||
} else if (aarch64_insn_is_adr_adrp(insn)) {
|
||||
asi->handler = simulate_adr_adrp;
|
||||
} else if (aarch64_insn_is_b(insn) ||
|
||||
aarch64_insn_is_bl(insn)) {
|
||||
asi->handler = simulate_b_bl;
|
||||
} else if (aarch64_insn_is_br(insn) ||
|
||||
aarch64_insn_is_blr(insn) ||
|
||||
aarch64_insn_is_ret(insn)) {
|
||||
asi->handler = simulate_br_blr_ret;
|
||||
} else if (aarch64_insn_is_ldr_lit(insn)) {
|
||||
asi->handler = simulate_ldr_literal;
|
||||
} else if (aarch64_insn_is_ldrsw_lit(insn)) {
|
||||
asi->handler = simulate_ldrsw_literal;
|
||||
} else {
|
||||
/*
|
||||
* Instruction cannot be stepped out-of-line and we don't
|
||||
* (yet) simulate it.
|
||||
*/
|
||||
return INSN_REJECTED;
|
||||
}
|
||||
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
static bool __kprobes
|
||||
is_probed_address_atomic(kprobe_opcode_t *scan_start, kprobe_opcode_t *scan_end)
|
||||
{
|
||||
while (scan_start > scan_end) {
|
||||
/*
|
||||
* atomic region starts from exclusive load and ends with
|
||||
* exclusive store.
|
||||
*/
|
||||
if (aarch64_insn_is_store_ex(le32_to_cpu(*scan_start)))
|
||||
return false;
|
||||
else if (aarch64_insn_is_load_ex(le32_to_cpu(*scan_start)))
|
||||
return true;
|
||||
scan_start--;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum kprobe_insn __kprobes
|
||||
arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi)
|
||||
{
|
||||
enum kprobe_insn decoded;
|
||||
kprobe_opcode_t insn = le32_to_cpu(*addr);
|
||||
kprobe_opcode_t *scan_start = addr - 1;
|
||||
kprobe_opcode_t *scan_end = addr - MAX_ATOMIC_CONTEXT_SIZE;
|
||||
#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
|
||||
struct module *mod;
|
||||
#endif
|
||||
|
||||
if (addr >= (kprobe_opcode_t *)_text &&
|
||||
scan_end < (kprobe_opcode_t *)_text)
|
||||
scan_end = (kprobe_opcode_t *)_text;
|
||||
#if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
|
||||
else {
|
||||
preempt_disable();
|
||||
mod = __module_address((unsigned long)addr);
|
||||
if (mod && within_module_init((unsigned long)addr, mod) &&
|
||||
!within_module_init((unsigned long)scan_end, mod))
|
||||
scan_end = (kprobe_opcode_t *)mod->init_layout.base;
|
||||
else if (mod && within_module_core((unsigned long)addr, mod) &&
|
||||
!within_module_core((unsigned long)scan_end, mod))
|
||||
scan_end = (kprobe_opcode_t *)mod->core_layout.base;
|
||||
preempt_enable();
|
||||
}
|
||||
#endif
|
||||
decoded = arm_probe_decode_insn(insn, asi);
|
||||
|
||||
if (decoded == INSN_REJECTED ||
|
||||
is_probed_address_atomic(scan_start, scan_end))
|
||||
return INSN_REJECTED;
|
||||
|
||||
return decoded;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* arch/arm64/kernel/probes/decode-insn.h
|
||||
*
|
||||
* Copyright (C) 2013 Linaro Limited.
|
||||
*
|
||||
* 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_ARM64_H
|
||||
#define _ARM_KERNEL_KPROBES_ARM64_H
|
||||
|
||||
/*
|
||||
* ARM strongly recommends a limit of 128 bytes between LoadExcl and
|
||||
* StoreExcl instructions in a single thread of execution. So keep the
|
||||
* max atomic context size as 32.
|
||||
*/
|
||||
#define MAX_ATOMIC_CONTEXT_SIZE (128 / sizeof(kprobe_opcode_t))
|
||||
|
||||
enum kprobe_insn {
|
||||
INSN_REJECTED,
|
||||
INSN_GOOD_NO_SLOT,
|
||||
INSN_GOOD,
|
||||
};
|
||||
|
||||
enum kprobe_insn __kprobes
|
||||
arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi);
|
||||
|
||||
#endif /* _ARM_KERNEL_KPROBES_ARM64_H */
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user