178c98f90SMartin Schwidefsky /* SPDX-License-Identifier: GPL-2.0 */
278c98f90SMartin Schwidefsky #include <linux/sched.h>
378c98f90SMartin Schwidefsky #include <linux/sched/task.h>
478c98f90SMartin Schwidefsky #include <linux/sched/task_stack.h>
578c98f90SMartin Schwidefsky #include <linux/interrupt.h>
678c98f90SMartin Schwidefsky #include <asm/sections.h>
778c98f90SMartin Schwidefsky #include <asm/ptrace.h>
878c98f90SMartin Schwidefsky #include <asm/bitops.h>
978c98f90SMartin Schwidefsky #include <asm/stacktrace.h>
1078c98f90SMartin Schwidefsky #include <asm/unwind.h>
1178c98f90SMartin Schwidefsky
unwind_get_return_address(struct unwind_state * state)1278c98f90SMartin Schwidefsky unsigned long unwind_get_return_address(struct unwind_state *state)
1378c98f90SMartin Schwidefsky {
1478c98f90SMartin Schwidefsky if (unwind_done(state))
1578c98f90SMartin Schwidefsky return 0;
1678c98f90SMartin Schwidefsky return __kernel_text_address(state->ip) ? state->ip : 0;
1778c98f90SMartin Schwidefsky }
1878c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_get_return_address);
1978c98f90SMartin Schwidefsky
outside_of_stack(struct unwind_state * state,unsigned long sp)2078c98f90SMartin Schwidefsky static bool outside_of_stack(struct unwind_state *state, unsigned long sp)
2178c98f90SMartin Schwidefsky {
2278c98f90SMartin Schwidefsky return (sp <= state->sp) ||
239a159190SVasily Gorbik (sp > state->stack_info.end - sizeof(struct stack_frame));
2478c98f90SMartin Schwidefsky }
2578c98f90SMartin Schwidefsky
update_stack_info(struct unwind_state * state,unsigned long sp)2678c98f90SMartin Schwidefsky static bool update_stack_info(struct unwind_state *state, unsigned long sp)
2778c98f90SMartin Schwidefsky {
2878c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info;
2978c98f90SMartin Schwidefsky unsigned long *mask = &state->stack_mask;
3078c98f90SMartin Schwidefsky
3178c98f90SMartin Schwidefsky /* New stack pointer leaves the current stack */
3278c98f90SMartin Schwidefsky if (get_stack_info(sp, state->task, info, mask) != 0 ||
3378c98f90SMartin Schwidefsky !on_stack(info, sp, sizeof(struct stack_frame)))
3478c98f90SMartin Schwidefsky /* 'sp' does not point to a valid stack */
3578c98f90SMartin Schwidefsky return false;
3678c98f90SMartin Schwidefsky return true;
3778c98f90SMartin Schwidefsky }
3878c98f90SMartin Schwidefsky
is_final_pt_regs(struct unwind_state * state,struct pt_regs * regs)39eef06cbfSVasily Gorbik static inline bool is_final_pt_regs(struct unwind_state *state,
40e76e6961SVasily Gorbik struct pt_regs *regs)
41e76e6961SVasily Gorbik {
42eef06cbfSVasily Gorbik /* user mode or kernel thread pt_regs at the bottom of task stack */
43eef06cbfSVasily Gorbik if (task_pt_regs(state->task) == regs)
44eef06cbfSVasily Gorbik return true;
45eef06cbfSVasily Gorbik
46eef06cbfSVasily Gorbik /* user mode pt_regs at the bottom of irq stack */
47eef06cbfSVasily Gorbik return state->stack_info.type == STACK_TYPE_IRQ &&
48eef06cbfSVasily Gorbik state->stack_info.end - sizeof(struct pt_regs) == (unsigned long)regs &&
49eef06cbfSVasily Gorbik READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE;
50e76e6961SVasily Gorbik }
51e76e6961SVasily Gorbik
52*7e17eac2SIlya Leoshkevich /* Avoid KMSAN false positives from touching uninitialized frames. */
53*7e17eac2SIlya Leoshkevich __no_kmsan_checks
unwind_next_frame(struct unwind_state * state)5478c98f90SMartin Schwidefsky bool unwind_next_frame(struct unwind_state *state)
5578c98f90SMartin Schwidefsky {
5678c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info;
5778c98f90SMartin Schwidefsky struct stack_frame *sf;
5878c98f90SMartin Schwidefsky struct pt_regs *regs;
5978c98f90SMartin Schwidefsky unsigned long sp, ip;
6078c98f90SMartin Schwidefsky bool reliable;
6178c98f90SMartin Schwidefsky
6278c98f90SMartin Schwidefsky regs = state->regs;
6378c98f90SMartin Schwidefsky if (unlikely(regs)) {
64a1d863acSIlya Leoshkevich sp = state->sp;
6578c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp;
6620955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]);
6778c98f90SMartin Schwidefsky reliable = false;
6878c98f90SMartin Schwidefsky regs = NULL;
69708b1376SVasily Gorbik /* skip bogus %r14 or if is the same as regs->psw.addr */
70708b1376SVasily Gorbik if (!__kernel_text_address(ip) || state->ip == unwind_recover_ret_addr(state, ip)) {
71bf018ee6SVasily Gorbik state->regs = NULL;
72bf018ee6SVasily Gorbik return unwind_next_frame(state);
73bf018ee6SVasily Gorbik }
7478c98f90SMartin Schwidefsky } else {
7578c98f90SMartin Schwidefsky sf = (struct stack_frame *) state->sp;
7620955746SVasily Gorbik sp = READ_ONCE_NOCHECK(sf->back_chain);
7778c98f90SMartin Schwidefsky if (likely(sp)) {
7878c98f90SMartin Schwidefsky /* Non-zero back-chain points to the previous frame */
7978c98f90SMartin Schwidefsky if (unlikely(outside_of_stack(state, sp))) {
8078c98f90SMartin Schwidefsky if (!update_stack_info(state, sp))
8178c98f90SMartin Schwidefsky goto out_err;
8278c98f90SMartin Schwidefsky }
8378c98f90SMartin Schwidefsky sf = (struct stack_frame *) sp;
8420955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]);
8578c98f90SMartin Schwidefsky reliable = true;
8678c98f90SMartin Schwidefsky } else {
8778c98f90SMartin Schwidefsky /* No back-chain, look for a pt_regs structure */
8878c98f90SMartin Schwidefsky sp = state->sp + STACK_FRAME_OVERHEAD;
8978c98f90SMartin Schwidefsky if (!on_stack(info, sp, sizeof(struct pt_regs)))
9067f55934SVasily Gorbik goto out_err;
9178c98f90SMartin Schwidefsky regs = (struct pt_regs *) sp;
92eef06cbfSVasily Gorbik if (is_final_pt_regs(state, regs))
9378c98f90SMartin Schwidefsky goto out_stop;
9420955746SVasily Gorbik ip = READ_ONCE_NOCHECK(regs->psw.addr);
9597806dfbSVasily Gorbik sp = READ_ONCE_NOCHECK(regs->gprs[15]);
9697806dfbSVasily Gorbik if (unlikely(outside_of_stack(state, sp))) {
9797806dfbSVasily Gorbik if (!update_stack_info(state, sp))
9897806dfbSVasily Gorbik goto out_err;
9997806dfbSVasily Gorbik }
10078c98f90SMartin Schwidefsky reliable = true;
10178c98f90SMartin Schwidefsky }
10278c98f90SMartin Schwidefsky }
10378c98f90SMartin Schwidefsky
104be2d11b2SMiroslav Benes /* Sanity check: ABI requires SP to be aligned 8 bytes. */
105be2d11b2SMiroslav Benes if (sp & 0x7)
106be2d11b2SMiroslav Benes goto out_err;
107be2d11b2SMiroslav Benes
10878c98f90SMartin Schwidefsky /* Update unwind state */
10978c98f90SMartin Schwidefsky state->sp = sp;
11078c98f90SMartin Schwidefsky state->regs = regs;
11178c98f90SMartin Schwidefsky state->reliable = reliable;
112d81675b6SVasily Gorbik state->ip = unwind_recover_ret_addr(state, ip);
11378c98f90SMartin Schwidefsky return true;
11478c98f90SMartin Schwidefsky
11578c98f90SMartin Schwidefsky out_err:
11678c98f90SMartin Schwidefsky state->error = true;
11778c98f90SMartin Schwidefsky out_stop:
11878c98f90SMartin Schwidefsky state->stack_info.type = STACK_TYPE_UNKNOWN;
11978c98f90SMartin Schwidefsky return false;
12078c98f90SMartin Schwidefsky }
12178c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(unwind_next_frame);
12278c98f90SMartin Schwidefsky
123*7e17eac2SIlya Leoshkevich /* Avoid KMSAN false positives from touching uninitialized frames. */
124*7e17eac2SIlya Leoshkevich __no_kmsan_checks
__unwind_start(struct unwind_state * state,struct task_struct * task,struct pt_regs * regs,unsigned long first_frame)12578c98f90SMartin Schwidefsky void __unwind_start(struct unwind_state *state, struct task_struct *task,
126222ee908SVasily Gorbik struct pt_regs *regs, unsigned long first_frame)
12778c98f90SMartin Schwidefsky {
12878c98f90SMartin Schwidefsky struct stack_info *info = &state->stack_info;
12978c98f90SMartin Schwidefsky struct stack_frame *sf;
130222ee908SVasily Gorbik unsigned long ip, sp;
13178c98f90SMartin Schwidefsky
13278c98f90SMartin Schwidefsky memset(state, 0, sizeof(*state));
13378c98f90SMartin Schwidefsky state->task = task;
13478c98f90SMartin Schwidefsky state->regs = regs;
13578c98f90SMartin Schwidefsky
13678c98f90SMartin Schwidefsky /* Don't even attempt to start from user mode regs: */
13778c98f90SMartin Schwidefsky if (regs && user_mode(regs)) {
13878c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN;
13978c98f90SMartin Schwidefsky return;
14078c98f90SMartin Schwidefsky }
14178c98f90SMartin Schwidefsky
142222ee908SVasily Gorbik /* Get the instruction pointer from pt_regs or the stack frame */
143222ee908SVasily Gorbik if (regs) {
144222ee908SVasily Gorbik ip = regs->psw.addr;
145222ee908SVasily Gorbik sp = regs->gprs[15];
146222ee908SVasily Gorbik } else if (task == current) {
147222ee908SVasily Gorbik sp = current_frame_address();
148222ee908SVasily Gorbik } else {
149222ee908SVasily Gorbik sp = task->thread.ksp;
150222ee908SVasily Gorbik }
151222ee908SVasily Gorbik
15278c98f90SMartin Schwidefsky /* Get current stack pointer and initialize stack info */
153222ee908SVasily Gorbik if (!update_stack_info(state, sp)) {
15478c98f90SMartin Schwidefsky /* Something is wrong with the stack pointer */
15578c98f90SMartin Schwidefsky info->type = STACK_TYPE_UNKNOWN;
15678c98f90SMartin Schwidefsky state->error = true;
15778c98f90SMartin Schwidefsky return;
15878c98f90SMartin Schwidefsky }
15978c98f90SMartin Schwidefsky
160222ee908SVasily Gorbik if (!regs) {
161222ee908SVasily Gorbik /* Stack frame is within valid stack */
16278c98f90SMartin Schwidefsky sf = (struct stack_frame *)sp;
16320955746SVasily Gorbik ip = READ_ONCE_NOCHECK(sf->gprs[8]);
16478c98f90SMartin Schwidefsky }
16578c98f90SMartin Schwidefsky
16678c98f90SMartin Schwidefsky /* Update unwind state */
16778c98f90SMartin Schwidefsky state->sp = sp;
168222ee908SVasily Gorbik state->reliable = true;
169d81675b6SVasily Gorbik state->ip = unwind_recover_ret_addr(state, ip);
170222ee908SVasily Gorbik
171222ee908SVasily Gorbik if (!first_frame)
172222ee908SVasily Gorbik return;
173222ee908SVasily Gorbik /* Skip through the call chain to the specified starting frame */
174222ee908SVasily Gorbik while (!unwind_done(state)) {
175222ee908SVasily Gorbik if (on_stack(&state->stack_info, first_frame, sizeof(struct stack_frame))) {
176222ee908SVasily Gorbik if (state->sp >= first_frame)
177222ee908SVasily Gorbik break;
178222ee908SVasily Gorbik }
179222ee908SVasily Gorbik unwind_next_frame(state);
180222ee908SVasily Gorbik }
18178c98f90SMartin Schwidefsky }
18278c98f90SMartin Schwidefsky EXPORT_SYMBOL_GPL(__unwind_start);
183