From eca23f3dbde340a85adbeb3ea4e81df5cbc10c0b Mon Sep 17 00:00:00 2001 From: srikar Date: Thu, 25 Oct 2007 13:54:41 +0000 Subject: [PATCH] uprobes ppc64 ssol changes --- runtime/uprobes/uprobes.c | 4 ++ runtime/uprobes/uprobes_ppc64.c | 111 ++++++++++++++++++++++++++++++++ runtime/uprobes/uprobes_ppc64.h | 23 +++---- 3 files changed, 124 insertions(+), 14 deletions(-) diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c index 9f5fddd08..509307095 100644 --- a/runtime/uprobes/uprobes.c +++ b/runtime/uprobes/uprobes.c @@ -40,6 +40,8 @@ #define SET_ENGINE_FLAGS 1 #define CLEAR_ENGINE_FLAGS 0 +#define MAX_SSOL_SLOTS 1024 + extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); static int utask_fake_quiesce(struct uprobe_task *utask); @@ -1240,6 +1242,8 @@ static noinline void uprobe_init_ssol(struct uprobe_process *uproc) return; area->nfree = area->nslots = PAGE_SIZE / MAX_UINSN_BYTES; + if (area->nslots > MAX_SSOL_SLOTS) + area->nfree = area->nslots = MAX_SSOL_SLOTS; area->slots = (struct uprobe_ssol_slot *) kzalloc(sizeof(struct uprobe_ssol_slot) * area->nslots, GFP_USER); diff --git a/runtime/uprobes/uprobes_ppc64.c b/runtime/uprobes/uprobes_ppc64.c index d80676e92..819ac73da 100644 --- a/runtime/uprobes/uprobes_ppc64.c +++ b/runtime/uprobes/uprobes_ppc64.c @@ -31,6 +31,117 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, struct pt_regs *regs, struct uprobe_task *utask) { unsigned long orig_ret_addr = regs->link; + regs->link = trampoline_address; return orig_ret_addr; } + +/* + * Get an instruction slot from the process's SSOL area, containing the + * instruction at ppt's probepoint. Point the eip at that slot, in preparation + * for single-stepping out of line. + */ +static +void uprobe_pre_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt, + struct pt_regs *regs) +{ + struct uprobe_ssol_slot *slot; + + slot = uprobe_get_insn_slot(ppt); + if (!slot) { + utask->doomed = 1; + return; + } + regs->nip = (long)slot->insn; +} + + +static inline void calc_offset(struct uprobe_probept *ppt, + struct pt_regs *regs) +{ + int offset = 0; + unsigned int opcode = 0; + unsigned int insn = *ppt->insn; + + opcode = insn >> 26; + switch (opcode) { + case 16: /* bc */ + if ((insn & 2) == 0) { + offset = (signed short)(insn & 0xfffc); + regs->nip = ppt->vaddr + offset; + } + if (insn & 1) + regs->link = ppt->vaddr + MAX_UINSN_BYTES; + break; + case 17: /* sc */ + /* Do we need to do anything */ + break; + case 18: /* b */ + if ((insn & 2) == 0) { + offset = insn & 0x03fffffc; + if (offset & 0x02000000) + offset -= 0x04000000; + regs->nip = ppt->vaddr + offset; + } + if (insn & 1) + regs->link = ppt->vaddr + MAX_UINSN_BYTES; + break; + } +#ifdef UPROBES_DEBUG + printk (KERN_ERR "ppt->vaddr=%p, regs->nip=%p, offset=%ld\n", + ppt->vaddr, regs->nip, offset); + if (insn & 1) + printk (KERN_ERR "regs->link=%p \n", regs->link); +#endif + return; +} + +/* + * Called after single-stepping. ppt->vaddr is the address of the + * instruction which was replaced by a breakpoint instruction. To avoid + * the SMP problems that can occur when we temporarily put back the + * original opcode to single-step, we single-stepped a copy of the + * instruction. + * + * This function prepares to return from the post-single-step + * interrupt. + * + * 1) Typically, the new nip is relative to the copied instruction. We + * need to make it relative to the original instruction. Exceptions are + * branch instructions. + * + * 2) For branch instructions, update the nip if the branch uses + * relative addressing. Update the link instruction to the instruction + * following the original instruction address. + */ + +static +void uprobe_post_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt, + struct pt_regs *regs) +{ + unsigned long copy_nip; + + copy_nip = (unsigned long) ppt->slot->insn; + up_read(&ppt->slot->rwsem); + + /* + * If the single stepped instruction is non-branch instruction + * then update the IP to be relative to probepoint. + */ + if (regs->nip == copy_nip + MAX_UINSN_BYTES) + regs->nip = ppt->vaddr + MAX_UINSN_BYTES; + else + calc_offset(ppt,regs); +} + +static +int arch_validate_probed_insn(struct uprobe_probept *ppt, + struct task_struct *tsk) +{ + if ((unsigned long)ppt->vaddr & 0x03) { + printk(KERN_WARNING + "Attempt to register uprobe at an unaligned addr\n"); + return -EINVAL; + } + return 0; +} diff --git a/runtime/uprobes/uprobes_ppc64.h b/runtime/uprobes/uprobes_ppc64.h index 9f56119a4..1e274a945 100644 --- a/runtime/uprobes/uprobes_ppc64.h +++ b/runtime/uprobes/uprobes_ppc64.h @@ -27,7 +27,7 @@ #define SSTEP_SIGNAL SIGTRAP /* Normally defined in Kconfig */ -#undef CONFIG_UPROBES_SSOL +#define CONFIG_UPROBES_SSOL #define CONFIG_URETPROBES 1 typedef unsigned int uprobe_opcode_t; @@ -46,12 +46,6 @@ struct uprobe_probept; struct uprobe_task; struct task_struct; -static inline int arch_validate_probed_insn(struct uprobe_probept *ppt, - struct task_struct *tsk) -{ - return 0; -} - /* On powerpc, nip points to the trap. */ static inline unsigned long arch_get_probept(struct pt_regs *regs) { @@ -62,14 +56,15 @@ static inline void arch_reset_ip_for_sstep(struct pt_regs *regs) { } -#ifdef CONFIG_URETPROBES -static inline void arch_restore_uret_addr(unsigned long ret_addr, - struct pt_regs *regs) -{ - regs->nip = ret_addr; -} -#endif /* CONFIG_URETPROBES */ +static inline int arch_validate_probed_insn(struct uprobe_probept *ppt, + struct task_struct *tsk); static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr, struct pt_regs *regs, struct uprobe_task *utask); + +static inline void arch_restore_uret_addr(unsigned long ret_addr, + struct pt_regs *regs) +{ + regs->nip = ret_addr; +} #endif /* _ASM_UPROBES_H */ -- 2.43.5