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 static struct stack_info *unwind_find_next_stack(const struct unwind_state *state, 64 unsigned long sp, 65 unsigned long size) 66 { 67 for (int i = 0; i < state->nr_stacks; i++) { 68 struct stack_info *info = &state->stacks[i]; 69 70 if (stackinfo_on_stack(info, sp, size)) 71 return info; 72 } 73 74 return NULL; 75 } 76 77 /** 78 * unwind_consume_stack() - Check if an object is on an accessible stack, 79 * updating stack boundaries so that future unwind steps cannot consume this 80 * object again. 81 * 82 * @state: the current unwind state. 83 * @sp: the base address of the object. 84 * @size: the size of the object. 85 * 86 * Return: 0 upon success, an error code otherwise. 87 */ 88 static inline int unwind_consume_stack(struct unwind_state *state, 89 unsigned long sp, 90 unsigned long size) 91 { 92 struct stack_info *next; 93 94 if (stackinfo_on_stack(&state->stack, sp, size)) 95 goto found; 96 97 next = unwind_find_next_stack(state, sp, size); 98 if (!next) 99 return -EINVAL; 100 101 /* 102 * Stack transitions are strictly one-way, and once we've 103 * transitioned from one stack to another, it's never valid to 104 * unwind back to the old stack. 105 * 106 * Remove the current stack from the list of stacks so that it cannot 107 * be found on a subsequent transition. 108 * 109 * Note that stacks can nest in several valid orders, e.g. 110 * 111 * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL 112 * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW 113 * HYP -> OVERFLOW 114 * 115 * ... so we do not check the specific order of stack 116 * transitions. 117 */ 118 state->stack = *next; 119 *next = stackinfo_get_unknown(); 120 121 found: 122 /* 123 * Future unwind steps can only consume stack above this frame record. 124 * Update the current stack to start immediately above it. 125 */ 126 state->stack.low = sp + size; 127 return 0; 128 } 129 130 /** 131 * unwind_next_frame_record() - Unwind to the next frame record. 132 * 133 * @state: the current unwind state. 134 * 135 * Return: 0 upon success, an error code otherwise. 136 */ 137 static inline int 138 unwind_next_frame_record(struct unwind_state *state) 139 { 140 unsigned long fp = state->fp; 141 int err; 142 143 if (fp & 0x7) 144 return -EINVAL; 145 146 err = unwind_consume_stack(state, fp, 16); 147 if (err) 148 return err; 149 150 /* 151 * Record this frame record's values. 152 */ 153 state->fp = READ_ONCE(*(unsigned long *)(fp)); 154 state->pc = READ_ONCE(*(unsigned long *)(fp + 8)); 155 156 return 0; 157 } 158 159 #endif /* __ASM_STACKTRACE_COMMON_H */ 160
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.