summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--runtime/uprobes/uprobes.c4
-rw-r--r--runtime/uprobes/uprobes_ppc64.c111
-rw-r--r--runtime/uprobes/uprobes_ppc64.h23
3 files changed, 124 insertions, 14 deletions
diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c
index 9f5fddd0..50930709 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 d80676e9..819ac73d 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 9f56119a..1e274a94 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 */