|
@@ -1348,8 +1348,8 @@ early_initcall(init_hw_perf_events);
|
|
|
* Callchain handling code.
|
|
* Callchain handling code.
|
|
|
*/
|
|
*/
|
|
|
struct frame_tail {
|
|
struct frame_tail {
|
|
|
- struct frame_tail __user *fp;
|
|
|
|
|
- unsigned long lr;
|
|
|
|
|
|
|
+ struct frame_tail __user *fp;
|
|
|
|
|
+ unsigned long lr;
|
|
|
} __attribute__((packed));
|
|
} __attribute__((packed));
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1386,22 +1386,80 @@ user_backtrace(struct frame_tail __user *tail,
|
|
|
return buftail.fp;
|
|
return buftail.fp;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
|
+ * The registers we're interested in are at the end of the variable
|
|
|
|
|
+ * length saved register structure. The fp points at the end of this
|
|
|
|
|
+ * structure so the address of this struct is:
|
|
|
|
|
+ * (struct compat_frame_tail *)(xxx->fp)-1
|
|
|
|
|
+ *
|
|
|
|
|
+ * This code has been adapted from the ARM OProfile support.
|
|
|
|
|
+ */
|
|
|
|
|
+struct compat_frame_tail {
|
|
|
|
|
+ compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */
|
|
|
|
|
+ u32 sp;
|
|
|
|
|
+ u32 lr;
|
|
|
|
|
+} __attribute__((packed));
|
|
|
|
|
+
|
|
|
|
|
+static struct compat_frame_tail __user *
|
|
|
|
|
+compat_user_backtrace(struct compat_frame_tail __user *tail,
|
|
|
|
|
+ struct perf_callchain_entry *entry)
|
|
|
|
|
+{
|
|
|
|
|
+ struct compat_frame_tail buftail;
|
|
|
|
|
+ unsigned long err;
|
|
|
|
|
+
|
|
|
|
|
+ /* Also check accessibility of one struct frame_tail beyond */
|
|
|
|
|
+ if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
|
|
+ pagefault_disable();
|
|
|
|
|
+ err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
|
|
|
|
|
+ pagefault_enable();
|
|
|
|
|
+
|
|
|
|
|
+ if (err)
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
|
|
+ perf_callchain_store(entry, buftail.lr);
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * Frame pointers should strictly progress back up the stack
|
|
|
|
|
+ * (towards higher addresses).
|
|
|
|
|
+ */
|
|
|
|
|
+ if (tail + 1 >= (struct compat_frame_tail __user *)
|
|
|
|
|
+ compat_ptr(buftail.fp))
|
|
|
|
|
+ return NULL;
|
|
|
|
|
+
|
|
|
|
|
+ return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void perf_callchain_user(struct perf_callchain_entry *entry,
|
|
void perf_callchain_user(struct perf_callchain_entry *entry,
|
|
|
struct pt_regs *regs)
|
|
struct pt_regs *regs)
|
|
|
{
|
|
{
|
|
|
- struct frame_tail __user *tail;
|
|
|
|
|
-
|
|
|
|
|
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
|
|
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
|
|
|
/* We don't support guest os callchain now */
|
|
/* We don't support guest os callchain now */
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
perf_callchain_store(entry, regs->pc);
|
|
perf_callchain_store(entry, regs->pc);
|
|
|
- tail = (struct frame_tail __user *)regs->regs[29];
|
|
|
|
|
|
|
|
|
|
- while (entry->nr < PERF_MAX_STACK_DEPTH &&
|
|
|
|
|
- tail && !((unsigned long)tail & 0xf))
|
|
|
|
|
- tail = user_backtrace(tail, entry);
|
|
|
|
|
|
|
+ if (!compat_user_mode(regs)) {
|
|
|
|
|
+ /* AARCH64 mode */
|
|
|
|
|
+ struct frame_tail __user *tail;
|
|
|
|
|
+
|
|
|
|
|
+ tail = (struct frame_tail __user *)regs->regs[29];
|
|
|
|
|
+
|
|
|
|
|
+ while (entry->nr < PERF_MAX_STACK_DEPTH &&
|
|
|
|
|
+ tail && !((unsigned long)tail & 0xf))
|
|
|
|
|
+ tail = user_backtrace(tail, entry);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ /* AARCH32 compat mode */
|
|
|
|
|
+ struct compat_frame_tail __user *tail;
|
|
|
|
|
+
|
|
|
|
|
+ tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
|
|
|
|
|
+
|
|
|
|
|
+ while ((entry->nr < PERF_MAX_STACK_DEPTH) &&
|
|
|
|
|
+ tail && !((unsigned long)tail & 0x3))
|
|
|
|
|
+ tail = compat_user_backtrace(tail, entry);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1429,6 +1487,7 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,
|
|
|
frame.fp = regs->regs[29];
|
|
frame.fp = regs->regs[29];
|
|
|
frame.sp = regs->sp;
|
|
frame.sp = regs->sp;
|
|
|
frame.pc = regs->pc;
|
|
frame.pc = regs->pc;
|
|
|
|
|
+
|
|
|
walk_stackframe(&frame, callchain_trace, entry);
|
|
walk_stackframe(&frame, callchain_trace, entry);
|
|
|
}
|
|
}
|
|
|
|
|
|