| /* |
| * OpenRISC unwinder.c |
| * |
| * Reusable arch specific api for unwinding stacks. |
| * |
| * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> |
| * |
| * This file is licensed under the terms of the GNU General Public License |
| * version 2. This program is licensed "as is" without any warranty of any |
| * kind, whether express or implied. |
| */ |
| |
| #include <linux/sched/task_stack.h> |
| #include <linux/kernel.h> |
| |
| #include <asm/unwinder.h> |
| |
| #ifdef CONFIG_FRAME_POINTER |
| struct or1k_frameinfo { |
| unsigned long *fp; |
| unsigned long ra; |
| unsigned long top; |
| }; |
| |
| /* |
| * Verify a frameinfo structure. The return address should be a valid text |
| * address. The frame pointer may be null if its the last frame, otherwise |
| * the frame pointer should point to a location in the stack after the the |
| * top of the next frame up. |
| */ |
| static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo) |
| { |
| return (frameinfo->fp == NULL || |
| (!kstack_end(frameinfo->fp) && |
| frameinfo->fp > &frameinfo->top)) && |
| __kernel_text_address(frameinfo->ra); |
| } |
| |
| /* |
| * Create a stack trace doing scanning which is frame pointer aware. We can |
| * get reliable stack traces by matching the previously found frame |
| * pointer with the top of the stack address every time we find a valid |
| * or1k_frameinfo. |
| * |
| * Ideally the stack parameter will be passed as FP, but it can not be |
| * guaranteed. Therefore we scan each address looking for the first sign |
| * of a return address. |
| * |
| * The OpenRISC stack frame looks something like the following. The |
| * location SP is held in r1 and location FP is held in r2 when frame pointers |
| * enabled. |
| * |
| * SP -> (top of stack) |
| * - (callee saved registers) |
| * - (local variables) |
| * FP-8 -> previous FP \ |
| * FP-4 -> return address |- or1k_frameinfo |
| * FP -> (previous top of stack) / |
| */ |
| void unwind_stack(void *data, unsigned long *stack, |
| void (*trace)(void *data, unsigned long addr, int reliable)) |
| { |
| unsigned long *next_fp = NULL; |
| struct or1k_frameinfo *frameinfo = NULL; |
| int reliable = 0; |
| |
| while (!kstack_end(stack)) { |
| frameinfo = container_of(stack, |
| struct or1k_frameinfo, |
| top); |
| |
| if (__kernel_text_address(frameinfo->ra)) { |
| if (or1k_frameinfo_valid(frameinfo) && |
| (next_fp == NULL || |
| next_fp == &frameinfo->top)) { |
| reliable = 1; |
| next_fp = frameinfo->fp; |
| } else |
| reliable = 0; |
| |
| trace(data, frameinfo->ra, reliable); |
| } |
| stack++; |
| } |
| } |
| |
| #else /* CONFIG_FRAME_POINTER */ |
| |
| /* |
| * Create a stack trace by doing a simple scan treating all text addresses |
| * as return addresses. |
| */ |
| void unwind_stack(void *data, unsigned long *stack, |
| void (*trace)(void *data, unsigned long addr, int reliable)) |
| { |
| unsigned long addr; |
| |
| while (!kstack_end(stack)) { |
| addr = *stack++; |
| if (__kernel_text_address(addr)) |
| trace(data, addr, 0); |
| } |
| } |
| #endif /* CONFIG_FRAME_POINTER */ |
| |