| // SPDX-License-Identifier: GPL-2.0 | |
| #include <linux/sched.h> | |
| #include <linux/sched/task_stack.h> | |
| #include <linux/stacktrace.h> | |
| #include <linux/kallsyms.h> | |
| #include <asm/thread_info.h> | |
| #include <asm/ptrace.h> | |
| static __always_inline unsigned long alpha_get_current_ksp(void) | |
| { | |
| unsigned long sp; | |
| asm volatile("mov $30, %0" : "=r"(sp)); | |
| return sp; | |
| } | |
| static void alpha_scan_kernel_stack(unsigned long ksp, | |
| stack_trace_consume_fn consume_entry, | |
| void *cookie) | |
| { | |
| unsigned long *p = (unsigned long *)ksp; | |
| if (unlikely(ksp & (sizeof(unsigned long) - 1))) | |
| return; | |
| while (!kstack_end(p)) { | |
| unsigned long addr = READ_ONCE_NOCHECK(*p++); | |
| if (!__kernel_text_address(addr)) | |
| continue; | |
| if (!consume_entry(cookie, addr)) | |
| break; | |
| } | |
| } | |
| noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, | |
| void *cookie, | |
| struct task_struct *task, | |
| struct pt_regs *regs) | |
| { | |
| unsigned long ksp; | |
| if (!task) | |
| task = current; | |
| if (regs && task == current) { | |
| /* | |
| * pt_regs is stored on the kernel stack; regs+1 matches | |
| * what arch/alpha/kernel/traps.c uses as the trace start. | |
| */ | |
| ksp = (unsigned long)(regs + 1); | |
| } else if (task == current) { | |
| ksp = alpha_get_current_ksp(); | |
| } else { | |
| ksp = task_thread_info(task)->pcb.ksp; | |
| } | |
| alpha_scan_kernel_stack(ksp, consume_entry, cookie); | |
| } |