Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 297598
b: refs/heads/master
c: b21d55e
h: refs/heads/master
v: v3
  • Loading branch information
Rabin Vincent authored and Russell King committed Mar 24, 2012
1 parent 395d58c commit c3674de
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 64 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d82227cf8f0b42ff42c21ed47025fdf54cb1698d
refs/heads/master: b21d55e98ac2bbcbbeec9a8cb091f717fd95b072
3 changes: 2 additions & 1 deletion trunk/arch/arm/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
ifdef CONFIG_FUNCTION_TRACER
CFLAGS_REMOVE_ftrace.o = -pg
CFLAGS_REMOVE_insn.o = -pg
CFLAGS_REMOVE_patch.o = -pg
endif

CFLAGS_REMOVE_return_address.o = -pg
Expand Down Expand Up @@ -38,7 +39,7 @@ obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-common.o patch.o
ifdef CONFIG_THUMB2_KERNEL
obj-$(CONFIG_KPROBES) += kprobes-thumb.o
else
Expand Down
86 changes: 24 additions & 62 deletions trunk/arch/arm/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <asm/cacheflush.h>

#include "kprobes.h"
#include "patch.h"

#define MIN_STACK_SIZE(addr) \
min((unsigned long)MAX_STACK_SIZE, \
Expand Down Expand Up @@ -103,57 +104,33 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
return 0;
}

#ifdef CONFIG_THUMB2_KERNEL

/*
* For a 32-bit Thumb breakpoint spanning two memory words we need to take
* special precautions to insert the breakpoint atomically, especially on SMP
* systems. This is achieved by calling this arming function using stop_machine.
*/
static int __kprobes set_t32_breakpoint(void *addr)
{
((u16 *)addr)[0] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION >> 16;
((u16 *)addr)[1] = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION & 0xffff;
flush_insns(addr, 2*sizeof(u16));
return 0;
}

void __kprobes arch_arm_kprobe(struct kprobe *p)
{
uintptr_t addr = (uintptr_t)p->addr & ~1; /* Remove any Thumb flag */

if (!is_wide_instruction(p->opcode)) {
*(u16 *)addr = KPROBE_THUMB16_BREAKPOINT_INSTRUCTION;
flush_insns(addr, sizeof(u16));
} else if (addr & 2) {
/* A 32-bit instruction spanning two words needs special care */
stop_machine(set_t32_breakpoint, (void *)addr, &cpu_online_map);
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 {
/* Word aligned 32-bit instruction can be written atomically */
u32 bkp = KPROBE_THUMB32_BREAKPOINT_INSTRUCTION;
#ifndef __ARMEB__ /* Swap halfwords for little-endian */
bkp = (bkp >> 16) | (bkp << 16);
#endif
*(u32 *)addr = bkp;
flush_insns(addr, sizeof(u32));
}
}
kprobe_opcode_t insn = p->opcode;

#else /* !CONFIG_THUMB2_KERNEL */
addr = p->addr;
brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;

void __kprobes arch_arm_kprobe(struct kprobe *p)
{
kprobe_opcode_t insn = p->opcode;
kprobe_opcode_t brkp = KPROBE_ARM_BREAKPOINT_INSTRUCTION;
if (insn >= 0xe0000000)
brkp |= 0xe0000000; /* Unconditional instruction */
else
brkp |= insn & 0xf0000000; /* Copy condition from insn */
*p->addr = brkp;
flush_insns(p->addr, sizeof(p->addr[0]));
}
if (insn >= 0xe0000000)
brkp |= 0xe0000000; /* Unconditional instruction */
else
brkp |= insn & 0xf0000000; /* Copy condition from insn */
}

#endif /* !CONFIG_THUMB2_KERNEL */
patch_text(addr, brkp);
}

/*
* The actual disarming is done here on each CPU and synchronized using
Expand All @@ -166,25 +143,10 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
int __kprobes __arch_disarm_kprobe(void *p)
{
struct kprobe *kp = p;
#ifdef CONFIG_THUMB2_KERNEL
u16 *addr = (u16 *)((uintptr_t)kp->addr & ~1);
kprobe_opcode_t insn = kp->opcode;
unsigned int len;
void *addr = (void *)((uintptr_t)kp->addr & ~1);

if (is_wide_instruction(insn)) {
((u16 *)addr)[0] = insn>>16;
((u16 *)addr)[1] = insn;
len = 2*sizeof(u16);
} else {
((u16 *)addr)[0] = insn;
len = sizeof(u16);
}
flush_insns(addr, len);
__patch_text(addr, kp->opcode);

#else /* !CONFIG_THUMB2_KERNEL */
*kp->addr = kp->opcode;
flush_insns(kp->addr, sizeof(kp->addr[0]));
#endif
return 0;
}

Expand Down
75 changes: 75 additions & 0 deletions trunk/arch/arm/kernel/patch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/stop_machine.h>

#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <asm/opcodes.h>

#include "patch.h"

struct patch {
void *addr;
unsigned int insn;
};

void __kprobes __patch_text(void *addr, unsigned int insn)
{
bool thumb2 = IS_ENABLED(CONFIG_THUMB2_KERNEL);
int size;

if (thumb2 && __opcode_is_thumb16(insn)) {
*(u16 *)addr = __opcode_to_mem_thumb16(insn);
size = sizeof(u16);
} else if (thumb2 && ((uintptr_t)addr & 2)) {
u16 first = __opcode_thumb32_first(insn);
u16 second = __opcode_thumb32_second(insn);
u16 *addrh = addr;

addrh[0] = __opcode_to_mem_thumb16(first);
addrh[1] = __opcode_to_mem_thumb16(second);

size = sizeof(u32);
} else {
if (thumb2)
insn = __opcode_to_mem_thumb32(insn);
else
insn = __opcode_to_mem_arm(insn);

*(u32 *)addr = insn;
size = sizeof(u32);
}

flush_icache_range((uintptr_t)(addr),
(uintptr_t)(addr) + size);
}

static int __kprobes patch_text_stop_machine(void *data)
{
struct patch *patch = data;

__patch_text(patch->addr, patch->insn);

return 0;
}

void __kprobes patch_text(void *addr, unsigned int insn)
{
struct patch patch = {
.addr = addr,
.insn = insn,
};

if (cache_ops_need_broadcast()) {
stop_machine(patch_text_stop_machine, &patch, cpu_online_mask);
} else {
bool straddles_word = IS_ENABLED(CONFIG_THUMB2_KERNEL)
&& __opcode_is_thumb32(insn)
&& ((uintptr_t)addr & 2);

if (straddles_word)
stop_machine(patch_text_stop_machine, &patch, NULL);
else
__patch_text(addr, insn);
}
}
7 changes: 7 additions & 0 deletions trunk/arch/arm/kernel/patch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#ifndef _ARM_KERNEL_PATCH_H
#define _ARM_KERNEL_PATCH_H

void patch_text(void *addr, unsigned int insn);
void __patch_text(void *addr, unsigned int insn);

#endif

0 comments on commit c3674de

Please sign in to comment.