diff options
Diffstat (limited to 'arch/loongarch/kernel/stacktrace.c')
-rw-r--r-- | arch/loongarch/kernel/stacktrace.c | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c index e690c1c769f2..3a690f96f00c 100644 --- a/arch/loongarch/kernel/stacktrace.c +++ b/arch/loongarch/kernel/stacktrace.c @@ -6,6 +6,7 @@ */ #include <linux/sched.h> #include <linux/stacktrace.h> +#include <linux/uaccess.h> #include <asm/stacktrace.h> #include <asm/unwind.h> @@ -35,3 +36,43 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, break; } } + +static int +copy_stack_frame(unsigned long fp, struct stack_frame *frame) +{ + int ret = 1; + unsigned long err; + unsigned long __user *user_frame_tail; + + user_frame_tail = (unsigned long *)(fp - sizeof(struct stack_frame)); + if (!access_ok(user_frame_tail, sizeof(*frame))) + return 0; + + pagefault_disable(); + err = (__copy_from_user_inatomic(frame, user_frame_tail, sizeof(*frame))); + if (err || (unsigned long)user_frame_tail >= frame->fp) + ret = 0; + pagefault_enable(); + + return ret; +} + +void arch_stack_walk_user(stack_trace_consume_fn consume_entry, void *cookie, + const struct pt_regs *regs) +{ + unsigned long fp = regs->regs[22]; + + while (fp && !((unsigned long)fp & 0xf)) { + struct stack_frame frame; + + frame.fp = 0; + frame.ra = 0; + if (!copy_stack_frame(fp, &frame)) + break; + if (!frame.ra) + break; + if (!consume_entry(cookie, frame.ra)) + break; + fp = frame.fp; + } +} |