unwinder.c (2871B)
1/* 2 * OpenRISC unwinder.c 3 * 4 * Reusable arch specific api for unwinding stacks. 5 * 6 * Copyright (C) 2017 Stafford Horne <shorne@gmail.com> 7 * 8 * This file is licensed under the terms of the GNU General Public License 9 * version 2. This program is licensed "as is" without any warranty of any 10 * kind, whether express or implied. 11 */ 12 13#include <linux/sched/task_stack.h> 14#include <linux/kernel.h> 15 16#include <asm/unwinder.h> 17 18#ifdef CONFIG_FRAME_POINTER 19struct or1k_frameinfo { 20 unsigned long *fp; 21 unsigned long ra; 22 unsigned long top; 23}; 24 25/* 26 * Verify a frameinfo structure. The return address should be a valid text 27 * address. The frame pointer may be null if its the last frame, otherwise 28 * the frame pointer should point to a location in the stack after the 29 * top of the next frame up. 30 */ 31static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo) 32{ 33 return (frameinfo->fp == NULL || 34 (!kstack_end(frameinfo->fp) && 35 frameinfo->fp > &frameinfo->top)) && 36 __kernel_text_address(frameinfo->ra); 37} 38 39/* 40 * Create a stack trace doing scanning which is frame pointer aware. We can 41 * get reliable stack traces by matching the previously found frame 42 * pointer with the top of the stack address every time we find a valid 43 * or1k_frameinfo. 44 * 45 * Ideally the stack parameter will be passed as FP, but it can not be 46 * guaranteed. Therefore we scan each address looking for the first sign 47 * of a return address. 48 * 49 * The OpenRISC stack frame looks something like the following. The 50 * location SP is held in r1 and location FP is held in r2 when frame pointers 51 * enabled. 52 * 53 * SP -> (top of stack) 54 * - (callee saved registers) 55 * - (local variables) 56 * FP-8 -> previous FP \ 57 * FP-4 -> return address |- or1k_frameinfo 58 * FP -> (previous top of stack) / 59 */ 60void unwind_stack(void *data, unsigned long *stack, 61 void (*trace)(void *data, unsigned long addr, int reliable)) 62{ 63 unsigned long *next_fp = NULL; 64 struct or1k_frameinfo *frameinfo = NULL; 65 int reliable = 0; 66 67 while (!kstack_end(stack)) { 68 frameinfo = container_of(stack, 69 struct or1k_frameinfo, 70 top); 71 72 if (__kernel_text_address(frameinfo->ra)) { 73 if (or1k_frameinfo_valid(frameinfo) && 74 (next_fp == NULL || 75 next_fp == &frameinfo->top)) { 76 reliable = 1; 77 next_fp = frameinfo->fp; 78 } else 79 reliable = 0; 80 81 trace(data, frameinfo->ra, reliable); 82 } 83 stack++; 84 } 85} 86 87#else /* CONFIG_FRAME_POINTER */ 88 89/* 90 * Create a stack trace by doing a simple scan treating all text addresses 91 * as return addresses. 92 */ 93void unwind_stack(void *data, unsigned long *stack, 94 void (*trace)(void *data, unsigned long addr, int reliable)) 95{ 96 unsigned long addr; 97 98 while (!kstack_end(stack)) { 99 addr = *stack++; 100 if (__kernel_text_address(addr)) 101 trace(data, addr, 0); 102 } 103} 104#endif /* CONFIG_FRAME_POINTER */ 105