-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
arm64: Kprobes with single stepping support
Add support for basic kernel probes(kprobes) and jump probes (jprobes) for ARM64. Kprobes utilizes software breakpoint and single step debug exceptions supported on ARM v8. A software breakpoint is placed at the probe address to trap the kernel execution into the kprobe handler. ARM v8 supports enabling single stepping before the break exception return (ERET), with next PC in exception return address (ELR_EL1). The kprobe handler prepares an executable memory slot for out-of-line execution with a copy of the original instruction being probed, and enables single stepping. The PC is set to the out-of-line slot address before the ERET. With this scheme, the instruction is executed with the exact same register context except for the PC (and DAIF) registers. Debug mask (PSTATE.D) is enabled only when single stepping a recursive kprobe, e.g.: during kprobes reenter so that probed instruction can be single stepped within the kprobe handler -exception- context. The recursion depth of kprobe is always 2, i.e. upon probe re-entry, any further re-entry is prevented by not calling handlers and the case counted as a missed kprobe). Single stepping from the x-o-l slot has a drawback for PC-relative accesses like branching and symbolic literals access as the offset from the new PC (slot address) may not be ensured to fit in the immediate value of the opcode. Such instructions need simulation, so reject probing them. Instructions generating exceptions or cpu mode change are rejected for probing. Exclusive load/store instructions are rejected too. Additionally, the code is checked to see if it is inside an exclusive load/store sequence (code from Pratyush). System instructions are mostly enabled for stepping, except MSR/MRS accesses to "DAIF" flags in PSTATE, which are not safe for probing. This also changes arch/arm64/include/asm/ptrace.h to use include/asm-generic/ptrace.h. Thanks to Steve Capper and Pratyush Anand for several suggested Changes. Signed-off-by: Sandeepa Prabhu <sandeepa.s.prabhu@gmail.com> Signed-off-by: David A. Long <dave.long@linaro.org> Signed-off-by: Pratyush Anand <panand@redhat.com> Acked-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
- Loading branch information
Sandeepa Prabhu
authored and
Catalin Marinas
committed
Jul 19, 2016
1 parent
2af3ec0
commit 2dd0e8d
Showing
14 changed files
with
859 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* 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); | ||
|
||
#endif /* _ARM_KPROBES_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* 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 | ||
|
||
struct kprobe; | ||
struct arch_specific_insn; | ||
|
||
typedef u32 kprobe_opcode_t; | ||
typedef unsigned long (kprobes_pstate_check_t)(unsigned long); | ||
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; | ||
kprobes_pstate_check_t *pstate_cc; | ||
kprobes_handler_t *handler; | ||
/* restore address after step xol */ | ||
unsigned long restore; | ||
}; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
* 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" | ||
|
||
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, | ||
*/ | ||
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; | ||
else | ||
return INSN_REJECTED; | ||
} | ||
|
||
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* 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, | ||
}; | ||
|
||
enum kprobe_insn __kprobes | ||
arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi); | ||
|
||
#endif /* _ARM_KERNEL_KPROBES_ARM64_H */ |
Oops, something went wrong.