Merge branches 'at91', 'ep93xx', 'iop', 'kprobes', 'ks8695', 'misc', 'msm', 's3c2410', 'sa1100' and 'vfp' into devel
* at91: (24 commits) [ARM] 4615/4: sam926[13]ek buttons updated [ARM] 4765/1: [AT91] AT91CAP9A-DK board support [ARM] 4764/1: [AT91] AT91CAP9 core support [ARM] 4738/1: at91sam9261: Remove udc pullup enabling in board initialisation [ARM] 4761/1: [AT91] Board-support for NEW_LEDs [ARM] 4760/1: [AT91] SPI CS0 errata on AT91RM9200 [ARM] 4759/1: [AT91] Buttons on CSB300 [ARM] 4758/1: [AT91] LEDs [ARM] 4757/1: [AT91] UART initialization [ARM] 4756/1: [AT91] Makefile cleanup [ARM] 4755/1: [AT91] NAND update [ARM] 4754/1: [AT91] SSC library support [ARM] 4753/1: [AT91] Use DMA_BIT_MASK [ARM] 4752/1: [AT91] RTT, RTC and WDT peripherals on SAM9 [ARM] 4751/1: [AT91] ISI peripheral on SAM9263 [ARM] 4750/1: [AT91] STN LCD displays on SAM9261 [ARM] 4734/1: at91sam9263ek: include IRQ for Ethernet PHY [ARM] 4646/1: AT91: configurable HZ, default to 128 [ARM] 4688/1: at91: speed-up irq processing [ARM] 4657/1: AT91: Header definition update ... * ep93xx: [ARM] 4671/1: ep93xx: remove obsolete gpio_line_* operations [ARM] 4670/1: ep93xx: implement IRQT_BOTHEDGE gpio irq sense type [ARM] 4669/1: ep93xx: simplify GPIO code and cleanups [ARM] 4668/1: ep93xx: implement new GPIO API * iop: [ARM] 4770/1: GLAN Tank: correct physmap_flash_data width field [ARM] 4732/1: GLAN Tank: register rtc-rs5c372 i2c device [ARM] 4708/1: iop: update defconfigs for 2.6.24 * kprobes: ARM kprobes: let's enable it ARM kprobes: special hook for the kprobes breakpoint handler ARM kprobes: prevent some functions involved with kprobes from being probed ARM kprobes: don't let a single-stepped stmdb corrupt the exception stack ARM kprobes: add the kprobes hook to the page fault handler ARM kprobes: core code ARM kprobes: instruction single-stepping support * ks8695: [ARM] 4603/1: KS8695: debugfs interface to view pin state [ARM] 4601/1: KS8695: PCI support * misc: [ARM] remove duplicate includes [ARM] CONFIG_DEBUG_STACK_USAGE [ARM] 4689/1: small comment wrap fix [ARM] 4687/1: Trivial arch/arm/kernel/entry-common.S comment fix [ARM] 4666/1: ixp4xx: fix sparse warnings in include/asm-arm/arch-ixp4xx/io.h [ARM] remove reference to non-existent MTD_OBSOLETE_CHIPS [SERIAL] 21285: Report baud rate back via termios [ARM] Remove pointless casts from void pointers, [ARM] Misc minor interrupt handler cleanups [ARM] Remove at91_lcdc.h [ARM] ARRAY_SIZE() cleanup [ARM] Update mach-types * msm: [ARM] msm: dma support for MSM7X00A [ARM] msm: board file for MACH_HALIBUT (QCT MSM7200A) [ARM] msm: irq and timer support for ARCH_MSM7X00A [ARM] msm: core platform support for ARCH_MSM7X00A * s3c2410: (33 commits) [ARM] 4795/1: S3C244X: Add armclk and setparent call [ARM] 4794/1: S3C24XX: Comonise S3C2440 and S3C2442 clock code [ARM] 4793/1: S3C24XX: Add IRQ->GPIO pin mapping function [ARM] 4792/1: S3C24XX: Remove warnings from debug-macro.S [ARM] 4791/1: S3C2412: Make fclk a parent of msysclk [ARM] 4790/1: S3C2412: Fix parent selection for msysclk. [ARM] 4789/1: S3C2412: Add missing CLKDIVN register values [ARM] 4788/1: S3C24XX: Fix paramet to s3c2410_dma_ctrl if S3C2410_DMAF_AUTOSTART used. [ARM] 4787/1: S3C24XX: s3c2410_dma_request() should return the allocated channel number [ARM] 4786/1: S3C2412: Add SPI FIFO controll constants [ARM] 4785/1: S3C24XX: Add _SHIFT definitions for S3C2410_BANKCON registers [ARM] 4784/1: S3C24XX: Fix GPIO restore glitches [ARM] 4783/1: S3C24XX: Add s3c2410_gpio_getpull() [ARM] 4782/1: S3C24XX: Define FIQ_START for any FIQ users [ARM] 4781/1: S3C24XX: DMA suspend and resume support [ARM] 4780/1: S3C2412: Allow for seperate DMA channels for TX and RX [ARM] 4779/1: S3C2412: Add s3c2412_gpio_set_sleepcfg() call [ARM] 4778/1: S3C2412: Add armclk and init from DVS state [ARM] 4777/1: S3C24XX: Ensure clk_set_rate() checks the set_rate method for the clk [ARM] 4775/1: s3c2410: fix compilation error if only s3c2442 cpu is selected ... * sa1100: [ARM] sa1100: add clock source support * vfp: [ARM] 4584/2: ARMv7: Add Advanced SIMD (NEON) extension support [ARM] 4583/1: ARMv7: Add VFPv3 support [ARM] 4582/2: Add support for the common VFP subarchitecture
This commit is contained in:
committed by
Russell King
@@ -19,6 +19,7 @@ obj-$(CONFIG_ISA_DMA) += dma-isa.o
|
||||
obj-$(CONFIG_PCI) += bios32.o isa.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
|
||||
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
|
||||
|
||||
obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
|
||||
|
@@ -216,7 +216,7 @@ void __init isa_init_dma(dma_t *dma)
|
||||
|
||||
request_dma(DMA_ISA_CASCADE, "cascade");
|
||||
|
||||
for (i = 0; i < sizeof(dma_resources) / sizeof(dma_resources[0]); i++)
|
||||
for (i = 0; i < ARRAY_SIZE(dma_resources); i++)
|
||||
request_resource(&ioport_resource, dma_resources + i);
|
||||
}
|
||||
}
|
||||
|
@@ -11,8 +11,8 @@
|
||||
*
|
||||
* Low-level vector interface routines
|
||||
*
|
||||
* Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes
|
||||
* it to save wrong values... Be aware!
|
||||
* Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction
|
||||
* that causes it to save wrong values... Be aware!
|
||||
*/
|
||||
|
||||
#include <asm/memory.h>
|
||||
@@ -58,6 +58,12 @@
|
||||
|
||||
.endm
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
.section .kprobes.text,"ax",%progbits
|
||||
#else
|
||||
.text
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Invalid mode handlers
|
||||
*/
|
||||
@@ -112,8 +118,8 @@ common_invalid:
|
||||
#define SPFIX(code...)
|
||||
#endif
|
||||
|
||||
.macro svc_entry
|
||||
sub sp, sp, #S_FRAME_SIZE
|
||||
.macro svc_entry, stack_hole=0
|
||||
sub sp, sp, #(S_FRAME_SIZE + \stack_hole)
|
||||
SPFIX( tst sp, #4 )
|
||||
SPFIX( bicne sp, sp, #4 )
|
||||
stmib sp, {r1 - r12}
|
||||
@@ -121,7 +127,7 @@ common_invalid:
|
||||
ldmia r0, {r1 - r3}
|
||||
add r5, sp, #S_SP @ here for interlock avoidance
|
||||
mov r4, #-1 @ "" "" "" ""
|
||||
add r0, sp, #S_FRAME_SIZE @ "" "" "" ""
|
||||
add r0, sp, #(S_FRAME_SIZE + \stack_hole)
|
||||
SPFIX( addne r0, r0, #4 )
|
||||
str r1, [sp] @ save the "real" r0 copied
|
||||
@ from the exception stack
|
||||
@@ -242,7 +248,14 @@ svc_preempt:
|
||||
|
||||
.align 5
|
||||
__und_svc:
|
||||
#ifdef CONFIG_KPROBES
|
||||
@ If a kprobe is about to simulate a "stmdb sp..." instruction,
|
||||
@ it obviously needs free stack space which then will belong to
|
||||
@ the saved context.
|
||||
svc_entry 64
|
||||
#else
|
||||
svc_entry
|
||||
#endif
|
||||
|
||||
@
|
||||
@ call emulation code, which returns using r9 if it has emulated
|
||||
@@ -480,6 +493,13 @@ __und_usr:
|
||||
* co-processor instructions. However, we have to watch out
|
||||
* for the ARM6/ARM7 SWI bug.
|
||||
*
|
||||
* NEON is a special case that has to be handled here. Not all
|
||||
* NEON instructions are co-processor instructions, so we have
|
||||
* to make a special case of checking for them. Plus, there's
|
||||
* five groups of them, so we have a table of mask/opcode pairs
|
||||
* to check against, and if any match then we branch off into the
|
||||
* NEON handler code.
|
||||
*
|
||||
* Emulators may wish to make use of the following registers:
|
||||
* r0 = instruction opcode.
|
||||
* r2 = PC+4
|
||||
@@ -488,6 +508,23 @@ __und_usr:
|
||||
* lr = unrecognised instruction return address
|
||||
*/
|
||||
call_fpe:
|
||||
#ifdef CONFIG_NEON
|
||||
adr r6, .LCneon_opcodes
|
||||
2:
|
||||
ldr r7, [r6], #4 @ mask value
|
||||
cmp r7, #0 @ end mask?
|
||||
beq 1f
|
||||
and r8, r0, r7
|
||||
ldr r7, [r6], #4 @ opcode bits matching in mask
|
||||
cmp r8, r7 @ NEON instruction?
|
||||
bne 2b
|
||||
get_thread_info r10
|
||||
mov r7, #1
|
||||
strb r7, [r10, #TI_USED_CP + 10] @ mark CP#10 as used
|
||||
strb r7, [r10, #TI_USED_CP + 11] @ mark CP#11 as used
|
||||
b do_vfp @ let VFP handler handle this
|
||||
1:
|
||||
#endif
|
||||
tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27
|
||||
#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710)
|
||||
and r8, r0, #0x0f000000 @ mask out op-code bits
|
||||
@@ -537,6 +574,20 @@ call_fpe:
|
||||
mov pc, lr @ CP#14 (Debug)
|
||||
mov pc, lr @ CP#15 (Control)
|
||||
|
||||
#ifdef CONFIG_NEON
|
||||
.align 6
|
||||
|
||||
.LCneon_opcodes:
|
||||
.word 0xfe000000 @ mask
|
||||
.word 0xf2000000 @ opcode
|
||||
|
||||
.word 0xff100000 @ mask
|
||||
.word 0xf4000000 @ opcode
|
||||
|
||||
.word 0x00000000 @ mask
|
||||
.word 0x00000000 @ opcode
|
||||
#endif
|
||||
|
||||
do_fpe:
|
||||
enable_irq
|
||||
ldr r4, .LCfp
|
||||
@@ -555,7 +606,7 @@ do_fpe:
|
||||
.data
|
||||
ENTRY(fp_enter)
|
||||
.word no_fp
|
||||
.text
|
||||
.previous
|
||||
|
||||
no_fp: mov pc, lr
|
||||
|
||||
|
@@ -72,7 +72,7 @@ no_work_pending:
|
||||
ldr r1, [sp, #S_PSR] @ get calling cpsr
|
||||
ldr lr, [sp, #S_PC]! @ get pc
|
||||
msr spsr_cxsf, r1 @ save in spsr_svc
|
||||
ldmdb sp, {r0 - lr}^ @ get calling r1 - lr
|
||||
ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
|
||||
mov r0, r0
|
||||
add sp, sp, #S_FRAME_SIZE - S_PC
|
||||
movs pc, lr @ return & move spsr_svc into cpsr
|
||||
|
1529
arch/arm/kernel/kprobes-decode.c
Normal file
1529
arch/arm/kernel/kprobes-decode.c
Normal file
File diff suppressed because it is too large
Load Diff
447
arch/arm/kernel/kprobes.c
Normal file
447
arch/arm/kernel/kprobes.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes.c
|
||||
*
|
||||
* Kprobes on ARM
|
||||
*
|
||||
* Abhishek Sagar <sagar.abhishek@gmail.com>
|
||||
* Copyright (C) 2006, 2007 Motorola Inc.
|
||||
*
|
||||
* Nicolas Pitre <nico@marvell.com>
|
||||
* Copyright (C) 2007 Marvell Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/cacheflush.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, cnt) \
|
||||
flush_icache_range((unsigned long)(addr), \
|
||||
(unsigned long)(addr) + \
|
||||
sizeof(kprobe_opcode_t) * (cnt))
|
||||
|
||||
/* Used as a marker in ARM_pc to note when we're in a jprobe. */
|
||||
#define JPROBE_MAGIC_ADDR 0xffffffff
|
||||
|
||||
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
|
||||
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
|
||||
|
||||
|
||||
int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
{
|
||||
kprobe_opcode_t insn;
|
||||
kprobe_opcode_t tmp_insn[MAX_INSN_SIZE];
|
||||
unsigned long addr = (unsigned long)p->addr;
|
||||
int is;
|
||||
|
||||
if (addr & 0x3 || in_exception_text(addr))
|
||||
return -EINVAL;
|
||||
|
||||
insn = *p->addr;
|
||||
p->opcode = insn;
|
||||
p->ainsn.insn = tmp_insn;
|
||||
|
||||
switch (arm_kprobe_decode_insn(insn, &p->ainsn)) {
|
||||
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, MAX_INSN_SIZE);
|
||||
break;
|
||||
|
||||
case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */
|
||||
p->ainsn.insn = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes arch_arm_kprobe(struct kprobe *p)
|
||||
{
|
||||
*p->addr = KPROBE_BREAKPOINT_INSTRUCTION;
|
||||
flush_insns(p->addr, 1);
|
||||
}
|
||||
|
||||
void __kprobes arch_disarm_kprobe(struct kprobe *p)
|
||||
{
|
||||
*p->addr = p->opcode;
|
||||
flush_insns(p->addr, 1);
|
||||
}
|
||||
|
||||
void __kprobes arch_remove_kprobe(struct kprobe *p)
|
||||
{
|
||||
if (p->ainsn.insn) {
|
||||
mutex_lock(&kprobe_mutex);
|
||||
free_insn_slot(p->ainsn.insn, 0);
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
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)
|
||||
{
|
||||
__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
|
||||
kcb->kprobe_status = kcb->prev_kprobe.status;
|
||||
}
|
||||
|
||||
static void __kprobes set_current_kprobe(struct kprobe *p)
|
||||
{
|
||||
__get_cpu_var(current_kprobe) = p;
|
||||
}
|
||||
|
||||
static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs,
|
||||
struct kprobe_ctlblk *kcb)
|
||||
{
|
||||
regs->ARM_pc += 4;
|
||||
p->ainsn.insn_handler(p, regs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with IRQs disabled. IRQs must remain disabled from that point
|
||||
* all the way until processing this kprobe is complete. The current
|
||||
* kprobes implementation cannot process more than one nested level of
|
||||
* kprobe, and that level is reserved for user kprobe handlers, so we can't
|
||||
* risk encountering a new kprobe in an interrupt handler.
|
||||
*/
|
||||
void __kprobes kprobe_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe *p, *cur;
|
||||
struct kprobe_ctlblk *kcb;
|
||||
kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc;
|
||||
|
||||
kcb = get_kprobe_ctlblk();
|
||||
cur = kprobe_running();
|
||||
p = get_kprobe(addr);
|
||||
|
||||
if (p) {
|
||||
if (cur) {
|
||||
/* Kprobe is pending, so we're recursing. */
|
||||
switch (kcb->kprobe_status) {
|
||||
case KPROBE_HIT_ACTIVE:
|
||||
case KPROBE_HIT_SSDONE:
|
||||
/* A pre- or post-handler probe got us here. */
|
||||
kprobes_inc_nmissed_count(p);
|
||||
save_previous_kprobe(kcb);
|
||||
set_current_kprobe(p);
|
||||
kcb->kprobe_status = KPROBE_REENTER;
|
||||
singlestep(p, regs, kcb);
|
||||
restore_previous_kprobe(kcb);
|
||||
break;
|
||||
default:
|
||||
/* impossible cases */
|
||||
BUG();
|
||||
}
|
||||
} else {
|
||||
set_current_kprobe(p);
|
||||
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
|
||||
/*
|
||||
* If we have no pre-handler or it returned 0, we
|
||||
* continue with normal processing. If we have a
|
||||
* pre-handler and it returned non-zero, it prepped
|
||||
* for calling the break_handler below on re-entry,
|
||||
* so get out doing nothing more here.
|
||||
*/
|
||||
if (!p->pre_handler || !p->pre_handler(p, regs)) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SS;
|
||||
singlestep(p, regs, kcb);
|
||||
if (p->post_handler) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
||||
p->post_handler(p, regs, 0);
|
||||
}
|
||||
reset_current_kprobe();
|
||||
}
|
||||
}
|
||||
} else if (cur) {
|
||||
/* We probably hit a jprobe. Call its break handler. */
|
||||
if (cur->break_handler && cur->break_handler(cur, regs)) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SS;
|
||||
singlestep(cur, regs, kcb);
|
||||
if (cur->post_handler) {
|
||||
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
||||
cur->post_handler(cur, regs, 0);
|
||||
}
|
||||
}
|
||||
reset_current_kprobe();
|
||||
} else {
|
||||
/*
|
||||
* The probe was removed and a race is in progress.
|
||||
* There is nothing we can do about it. Let's restart
|
||||
* the instruction. By the time we can restart, the
|
||||
* real instruction will be there.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
|
||||
{
|
||||
kprobe_handler(regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
|
||||
{
|
||||
struct kprobe *cur = kprobe_running();
|
||||
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
||||
|
||||
switch (kcb->kprobe_status) {
|
||||
case KPROBE_HIT_SS:
|
||||
case KPROBE_REENTER:
|
||||
/*
|
||||
* We are here because the instruction being single
|
||||
* stepped caused a page fault. We reset the current
|
||||
* kprobe and the PC to point back to the probe address
|
||||
* and allow the page fault handler to continue as a
|
||||
* normal page fault.
|
||||
*/
|
||||
regs->ARM_pc = (long)cur->addr;
|
||||
if (kcb->kprobe_status == KPROBE_REENTER) {
|
||||
restore_previous_kprobe(kcb);
|
||||
} else {
|
||||
reset_current_kprobe();
|
||||
}
|
||||
break;
|
||||
|
||||
case KPROBE_HIT_ACTIVE:
|
||||
case KPROBE_HIT_SSDONE:
|
||||
/*
|
||||
* We increment the nmissed count for accounting,
|
||||
* we can also use npre/npostfault count for accounting
|
||||
* these specific fault cases.
|
||||
*/
|
||||
kprobes_inc_nmissed_count(cur);
|
||||
|
||||
/*
|
||||
* We come here because instructions in the pre/post
|
||||
* handler caused the page_fault, this could happen
|
||||
* if handler tries to access user space by
|
||||
* copy_from_user(), get_user() etc. Let the
|
||||
* user-specified handler try to fix it.
|
||||
*/
|
||||
if (cur->fault_handler && cur->fault_handler(cur, regs, fsr))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
/*
|
||||
* notify_die() is currently never called on ARM,
|
||||
* so this callback is currently empty.
|
||||
*/
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a retprobed function returns, trampoline_handler() is called,
|
||||
* calling the kretprobe's handler. We construct a struct pt_regs to
|
||||
* give a view of registers r0-r11 to the user return-handler. This is
|
||||
* not a complete pt_regs structure, but that should be plenty sufficient
|
||||
* for kretprobe handlers which should normally be interested in r0 only
|
||||
* anyway.
|
||||
*/
|
||||
static void __attribute__((naked)) __kprobes kretprobe_trampoline(void)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"stmdb sp!, {r0 - r11} \n\t"
|
||||
"mov r0, sp \n\t"
|
||||
"bl trampoline_handler \n\t"
|
||||
"mov lr, r0 \n\t"
|
||||
"ldmia sp!, {r0 - r11} \n\t"
|
||||
"mov pc, lr \n\t"
|
||||
: : : "memory");
|
||||
}
|
||||
|
||||
/* Called from kretprobe_trampoline */
|
||||
static __used __kprobes void *trampoline_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kretprobe_instance *ri = NULL;
|
||||
struct hlist_head *head, empty_rp;
|
||||
struct hlist_node *node, *tmp;
|
||||
unsigned long flags, orig_ret_address = 0;
|
||||
unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
|
||||
|
||||
INIT_HLIST_HEAD(&empty_rp);
|
||||
spin_lock_irqsave(&kretprobe_lock, flags);
|
||||
head = kretprobe_inst_table_head(current);
|
||||
|
||||
/*
|
||||
* It is possible to have multiple instances associated with a given
|
||||
* task either because multiple functions in the call path have
|
||||
* a return probe installed on them, and/or more than one return
|
||||
* probe was registered for a target function.
|
||||
*
|
||||
* We can handle this because:
|
||||
* - instances are always inserted at the head of the list
|
||||
* - when multiple return probes are registered for the same
|
||||
* function, the first instance's ret_addr will point to the
|
||||
* real return address, and all the rest will point to
|
||||
* kretprobe_trampoline
|
||||
*/
|
||||
hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
|
||||
if (ri->task != current)
|
||||
/* another task is sharing our hash bucket */
|
||||
continue;
|
||||
|
||||
if (ri->rp && ri->rp->handler) {
|
||||
__get_cpu_var(current_kprobe) = &ri->rp->kp;
|
||||
get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
ri->rp->handler(ri, regs);
|
||||
__get_cpu_var(current_kprobe) = NULL;
|
||||
}
|
||||
|
||||
orig_ret_address = (unsigned long)ri->ret_addr;
|
||||
recycle_rp_inst(ri, &empty_rp);
|
||||
|
||||
if (orig_ret_address != trampoline_address)
|
||||
/*
|
||||
* This is the real return address. Any other
|
||||
* instances associated with this task are for
|
||||
* other calls deeper on the call stack
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
kretprobe_assert(ri, orig_ret_address, trampoline_address);
|
||||
spin_unlock_irqrestore(&kretprobe_lock, flags);
|
||||
|
||||
hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
|
||||
hlist_del(&ri->hlist);
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
return (void *)orig_ret_address;
|
||||
}
|
||||
|
||||
/* Called with kretprobe_lock held. */
|
||||
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr;
|
||||
|
||||
/* Replace the return addr with trampoline addr. */
|
||||
regs->ARM_lr = (unsigned long)&kretprobe_trampoline;
|
||||
}
|
||||
|
||||
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct jprobe *jp = container_of(p, struct jprobe, kp);
|
||||
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
||||
long sp_addr = regs->ARM_sp;
|
||||
|
||||
kcb->jprobe_saved_regs = *regs;
|
||||
memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr));
|
||||
regs->ARM_pc = (long)jp->entry;
|
||||
regs->ARM_cpsr |= PSR_I_BIT;
|
||||
preempt_disable();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void __kprobes jprobe_return(void)
|
||||
{
|
||||
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
||||
|
||||
__asm__ __volatile__ (
|
||||
/*
|
||||
* Setup an empty pt_regs. Fill SP and PC fields as
|
||||
* they're needed by longjmp_break_handler.
|
||||
*/
|
||||
"sub sp, %0, %1 \n\t"
|
||||
"ldr r0, ="__stringify(JPROBE_MAGIC_ADDR)"\n\t"
|
||||
"str %0, [sp, %2] \n\t"
|
||||
"str r0, [sp, %3] \n\t"
|
||||
"mov r0, sp \n\t"
|
||||
"bl kprobe_handler \n\t"
|
||||
|
||||
/*
|
||||
* Return to the context saved by setjmp_pre_handler
|
||||
* and restored by longjmp_break_handler.
|
||||
*/
|
||||
"ldr r0, [sp, %4] \n\t"
|
||||
"msr cpsr_cxsf, r0 \n\t"
|
||||
"ldmia sp, {r0 - pc} \n\t"
|
||||
:
|
||||
: "r" (kcb->jprobe_saved_regs.ARM_sp),
|
||||
"I" (sizeof(struct pt_regs)),
|
||||
"J" (offsetof(struct pt_regs, ARM_sp)),
|
||||
"J" (offsetof(struct pt_regs, ARM_pc)),
|
||||
"J" (offsetof(struct pt_regs, ARM_cpsr))
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
||||
long stack_addr = kcb->jprobe_saved_regs.ARM_sp;
|
||||
long orig_sp = regs->ARM_sp;
|
||||
struct jprobe *jp = container_of(p, struct jprobe, kp);
|
||||
|
||||
if (regs->ARM_pc == JPROBE_MAGIC_ADDR) {
|
||||
if (orig_sp != stack_addr) {
|
||||
struct pt_regs *saved_regs =
|
||||
(struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp;
|
||||
printk("current sp %lx does not match saved sp %lx\n",
|
||||
orig_sp, stack_addr);
|
||||
printk("Saved registers for jprobe %p\n", jp);
|
||||
show_regs(saved_regs);
|
||||
printk("Current registers\n");
|
||||
show_regs(regs);
|
||||
BUG();
|
||||
}
|
||||
*regs = kcb->jprobe_saved_regs;
|
||||
memcpy((void *)stack_addr, kcb->jprobes_stack,
|
||||
MIN_STACK_SIZE(stack_addr));
|
||||
preempt_enable_no_resched();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct undef_hook kprobes_break_hook = {
|
||||
.instr_mask = 0xffffffff,
|
||||
.instr_val = KPROBE_BREAKPOINT_INSTRUCTION,
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = SVC_MODE,
|
||||
.fn = kprobe_trap_handler,
|
||||
};
|
||||
|
||||
int __init arch_init_kprobes()
|
||||
{
|
||||
arm_kprobe_decode_init();
|
||||
register_undef_hook(&kprobes_break_hook);
|
||||
return 0;
|
||||
}
|
@@ -19,6 +19,7 @@
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@@ -46,15 +47,6 @@ __setup("user_debug=", user_debug_setup);
|
||||
|
||||
static void dump_mem(const char *str, unsigned long bottom, unsigned long top);
|
||||
|
||||
static inline int in_exception_text(unsigned long ptr)
|
||||
{
|
||||
extern char __exception_text_start[];
|
||||
extern char __exception_text_end[];
|
||||
|
||||
return ptr >= (unsigned long)&__exception_text_start &&
|
||||
ptr < (unsigned long)&__exception_text_end;
|
||||
}
|
||||
|
||||
void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
|
||||
{
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
@@ -322,6 +314,17 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
|
||||
get_user(instr, (u32 __user *)pc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
/*
|
||||
* It is possible to have recursive kprobes, so we can't call
|
||||
* the kprobe trap handler with the undef_lock held.
|
||||
*/
|
||||
if (instr == KPROBE_BREAKPOINT_INSTRUCTION && !user_mode(regs)) {
|
||||
kprobe_trap_handler(regs, instr);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&undef_lock, flags);
|
||||
list_for_each_entry(hook, &undef_hook, node) {
|
||||
if ((instr & hook->instr_mask) == hook->instr_val &&
|
||||
|
@@ -94,6 +94,7 @@ SECTIONS
|
||||
TEXT_TEXT
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
KPROBES_TEXT
|
||||
#ifdef CONFIG_MMU
|
||||
*(.fixup)
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user