diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 17:25:38 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 17:25:38 -0700 |
commit | e404f91ed2180dfecbab15dd4d39c543353385fb (patch) | |
tree | c256e29b1c738d5e5b5478f19b369b1fd90bd1e2 /arch/tile/kernel/single_step.c | |
parent | 18a043f9413277523cf5011e594caa1747db4948 (diff) | |
parent | e18105c128734b1671739ad4d85e216ebec28c61 (diff) | |
download | op-kernel-dev-e404f91ed2180dfecbab15dd4d39c543353385fb.zip op-kernel-dev-e404f91ed2180dfecbab15dd4d39c543353385fb.tar.gz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile
* git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile:
arch/tile: convert a BUG_ON to BUILD_BUG_ON
arch/tile: make ptrace() work properly for TILE-Gx COMPAT mode
arch/tile: support new info op generated by compiler
arch/tile: minor whitespace/naming changes for string support files
arch/tile: enable single-step support for TILE-Gx
arch/tile: parameterize system PLs to support KVM port
arch/tile: add Tilera's <arch/sim.h> header as an open-source header
arch/tile: Bomb C99 comments to C89 comments in tile's <arch/sim_def.h>
arch/tile: prevent corrupt top frame from causing backtracer runaway
arch/tile: various top-level Makefile cleanups
arch/tile: change lower bound on syscall error return to -4095
arch/tile: properly export __mb_incoherent for modules
arch/tile: provide a definition of MAP_STACK
kmemleak: add TILE to the list of supported architectures.
char: hvc: check for error case
arch/tile: Add a warning if we try to allocate too much vmalloc memory.
arch/tile: update some comments to clarify register usage.
arch/tile: use better "punctuation" for VMSPLIT_3_5G and friends
arch/tile: Use <asm-generic/syscalls.h>
tile: replace some BUG_ON checks with BUILD_BUG_ON checks
Diffstat (limited to 'arch/tile/kernel/single_step.c')
-rw-r--r-- | arch/tile/kernel/single_step.c | 73 |
1 files changed, 72 insertions, 1 deletions
diff --git a/arch/tile/kernel/single_step.c b/arch/tile/kernel/single_step.c index 5ec4b9c..1eb3b39 100644 --- a/arch/tile/kernel/single_step.c +++ b/arch/tile/kernel/single_step.c @@ -15,7 +15,7 @@ * Derived from iLib's single-stepping code. */ -#ifndef __tilegx__ /* No support for single-step yet. */ +#ifndef __tilegx__ /* Hardware support for single step unavailable. */ /* These functions are only used on the TILE platform */ #include <linux/slab.h> @@ -660,4 +660,75 @@ void single_step_once(struct pt_regs *regs) regs->pc += 8; } +#else +#include <linux/smp.h> +#include <linux/ptrace.h> +#include <arch/spr_def.h> + +static DEFINE_PER_CPU(unsigned long, ss_saved_pc); + + +/* + * Called directly on the occasion of an interrupt. + * + * If the process doesn't have single step set, then we use this as an + * opportunity to turn single step off. + * + * It has been mentioned that we could conditionally turn off single stepping + * on each entry into the kernel and rely on single_step_once to turn it + * on for the processes that matter (as we already do), but this + * implementation is somewhat more efficient in that we muck with registers + * once on a bum interrupt rather than on every entry into the kernel. + * + * If SINGLE_STEP_CONTROL_K has CANCELED set, then an interrupt occurred, + * so we have to run through this process again before we can say that an + * instruction has executed. + * + * swint will set CANCELED, but it's a legitimate instruction. Fortunately + * it changes the PC. If it hasn't changed, then we know that the interrupt + * wasn't generated by swint and we'll need to run this process again before + * we can say an instruction has executed. + * + * If either CANCELED == 0 or the PC's changed, we send out SIGTRAPs and get + * on with our lives. + */ + +void gx_singlestep_handle(struct pt_regs *regs, int fault_num) +{ + unsigned long *ss_pc = &__get_cpu_var(ss_saved_pc); + struct thread_info *info = (void *)current_thread_info(); + int is_single_step = test_ti_thread_flag(info, TIF_SINGLESTEP); + unsigned long control = __insn_mfspr(SPR_SINGLE_STEP_CONTROL_K); + + if (is_single_step == 0) { + __insn_mtspr(SPR_SINGLE_STEP_EN_K_K, 0); + + } else if ((*ss_pc != regs->pc) || + (!(control & SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK))) { + + ptrace_notify(SIGTRAP); + control |= SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK; + control |= SPR_SINGLE_STEP_CONTROL_1__INHIBIT_MASK; + __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control); + } +} + + +/* + * Called from need_singlestep. Set up the control registers and the enable + * register, then return back. + */ + +void single_step_once(struct pt_regs *regs) +{ + unsigned long *ss_pc = &__get_cpu_var(ss_saved_pc); + unsigned long control = __insn_mfspr(SPR_SINGLE_STEP_CONTROL_K); + + *ss_pc = regs->pc; + control |= SPR_SINGLE_STEP_CONTROL_1__CANCELED_MASK; + control |= SPR_SINGLE_STEP_CONTROL_1__INHIBIT_MASK; + __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control); + __insn_mtspr(SPR_SINGLE_STEP_EN_K_K, 1 << USER_PL); +} + #endif /* !__tilegx__ */ |