1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* 3 * Common arm64 stack unwinder code. 4 * 5 * See: arch/arm64/kernel/stacktrace.c for the reference implementation. 6 * 7 * Copyright (C) 2012 ARM Ltd. 8 */ 9 #ifndef __ASM_STACKTRACE_COMMON_H 10 #define __ASM_STACKTRACE_COMMON_H 11 12 #include <linux/types.h> 13 14 struct stack_info { 15 unsigned long low; 16 unsigned long high; 17 }; 18 19 /** 20 * struct unwind_state - state used for robust unwinding. 21 * 22 * @fp: The fp value in the frame record (or the real fp) 23 * @pc: The lr value in the frame record (or the real lr) 24 * 25 * @stack: The stack currently being unwound. 26 * @stacks: An array of stacks which can be unwound. 27 * @nr_stacks: The number of stacks in @stacks. 28 */ 29 struct unwind_state { 30 unsigned long fp; 31 unsigned long pc; 32 33 struct stack_info stack; 34 struct stack_info *stacks; 35 int nr_stacks; 36 }; 37 38 static inline struct stack_info stackinfo_get_unknown(void) 39 { 40 return (struct stack_info) { 41 .low = 0, 42 .high = 0, 43 }; 44 } 45 46 static inline bool stackinfo_on_stack(const struct stack_info *info, 47 unsigned long sp, unsigned long size) 48 { 49 if (!info->low) 50 return false; 51 52 if (sp < info->low || sp + size < sp || sp + size > info->high) 53 return false; 54 55 return true; 56 } 57 58 static inline void unwind_init_common(struct unwind_state *state) 59 { 60 state->stack = stackinfo_get_unknown(); 61 } 62 63 /** 64 * unwind_find_stack() - Find the accessible stack which entirely contains an 65 * object. 66 * 67 * @state: the current unwind state. 68 * @sp: the base address of the object. 69 * @size: the size of the object. 70 * 71 * Return: a pointer to the relevant stack_info if found; NULL otherwise. 72 */ 73 static struct stack_info *unwind_find_stack(struct unwind_state *state, 74 unsigned long sp, 75 unsigned long size) 76 { 77 struct stack_info *info = &state->stack; 78 79 if (stackinfo_on_stack(info, sp, size)) 80 return info; 81 82 for (int i = 0; i < state->nr_stacks; i++) { 83 info = &state->stacks[i]; 84 if (stackinfo_on_stack(info, sp, size)) 85 return info; 86 } 87 88 return NULL; 89 } 90 91 /** 92 * unwind_consume_stack() - Update stack boundaries so that future unwind steps 93 * cannot consume this object again. 94 * 95 * @state: the current unwind state. 96 * @info: the stack_info of the stack containing the object. 97 * @sp: the base address of the object. 98 * @size: the size of the object. 99 * 100 * Return: 0 upon success, an error code otherwise. 101 */ 102 static inline void unwind_consume_stack(struct unwind_state *state, 103 struct stack_info *info, 104 unsigned long sp, 105 unsigned long size) 106 { 107 struct stack_info tmp; 108 109 /* 110 * Stack transitions are strictly one-way, and once we've 111 * transitioned from one stack to another, it's never valid to 112 * unwind back to the old stack. 113 * 114 * Destroy the old stack info so that it cannot be found upon a 115 * subsequent transition. If the stack has not changed, we'll 116 * immediately restore the current stack info. 117 * 118 * Note that stacks can nest in several valid orders, e.g. 119 * 120 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL 121 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW 122 * HYP -> OVERFLOW 123 * 124 * ... so we do not check the specific order of stack 125 * transitions. 126 */ 127 tmp = *info; 128 *info = stackinfo_get_unknown(); 129 state->stack = tmp; 130 131 /* 132 * Future unwind steps can only consume stack above this frame record. 133 * Update the current stack to start immediately above it. 134 */ 135 state->stack.low = sp + size; 136 } 137 138 /** 139 * unwind_next_frame_record() - Unwind to the next frame record. 140 * 141 * @state: the current unwind state. 142 * 143 * Return: 0 upon success, an error code otherwise. 144 */ 145 static inline int 146 unwind_next_frame_record(struct unwind_state *state) 147 { 148 struct stack_info *info; 149 struct frame_record *record; 150 unsigned long fp = state->fp; 151 152 if (fp & 0x7) 153 return -EINVAL; 154 155 info = unwind_find_stack(state, fp, sizeof(*record)); 156 if (!info) 157 return -EINVAL; 158 159 unwind_consume_stack(state, info, fp, sizeof(*record)); 160 161 /* 162 * Record this frame record's values. 163 */ 164 record = (struct frame_record *)fp; 165 state->fp = READ_ONCE(record->fp); 166 state->pc = READ_ONCE(record->lr); 167 168 return 0; 169 } 170 171 #endif /* __ASM_STACKTRACE_COMMON_H */ 172