Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: stacktrace: track hyp stacks in unwinder's address space

Currently unwind_next_frame_record() has an optional callback to convert
the address space of the FP. This is necessary for the NVHE unwinder,
which tracks the stacks in the hyp VA space, but accesses the frame
records in the kernel VA space.

This is a bit unfortunate since it clutters unwind_next_frame_record(),
which will get in the way of future rework.

Instead, this patch changes the NVHE unwinder to track the stacks in the
kernel's VA space and translate to FP prior to calling
unwind_next_frame_record(). This removes the need for the translate_fp()
callback, as all unwinders consistently track stacks in the native
address space of the unwinder.

At the same time, this patch consolidates the generation of the stack
addresses behind the stackinfo_get_*() helpers.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Kalesh Singh <kaleshsingh@google.com>
Reviewed-by: Madhavan T. Venkataraman <madvenka@linux.microsoft.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
Cc: Fuad Tabba <tabba@google.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20220901130646.1316937-10-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Mark Rutland and committed by
Catalin Marinas
4b5e694e 8df13730

+46 -49
+4 -25
arch/arm64/include/asm/stacktrace/common.h
··· 77 77 state->stack = stackinfo_get_unknown(); 78 78 } 79 79 80 - /** 81 - * typedef stack_trace_translate_fp_fn() - Translates a non-kernel frame 82 - * pointer to a kernel address. 83 - * 84 - * @fp: the frame pointer to be updated to its kernel address. 85 - * 86 - * Return: true if the VA can be translated, false otherwise. 87 - * 88 - * Upon success @fp is updated to the corresponding kernel virtual address. 89 - */ 90 - typedef bool (*stack_trace_translate_fp_fn)(unsigned long *fp); 91 - 92 80 static struct stack_info *unwind_find_next_stack(const struct unwind_state *state, 93 81 unsigned long sp, 94 82 unsigned long size) ··· 148 160 * unwind_next_frame_record() - Unwind to the next frame record. 149 161 * 150 162 * @state: the current unwind state. 151 - * @translate_fp: translates the fp prior to access (may be NULL) 152 163 * 153 164 * Return: 0 upon success, an error code otherwise. 154 165 */ 155 166 static inline int 156 - unwind_next_frame_record(struct unwind_state *state, 157 - stack_trace_translate_fp_fn translate_fp) 167 + unwind_next_frame_record(struct unwind_state *state) 158 168 { 159 - unsigned long fp = state->fp, kern_fp = fp; 169 + unsigned long fp = state->fp; 160 170 int err; 161 171 162 172 if (fp & 0x7) ··· 165 179 return err; 166 180 167 181 /* 168 - * If fp is not from the current address space perform the necessary 169 - * translation before dereferencing it to get the next fp. 170 - */ 171 - if (translate_fp && !translate_fp(&kern_fp)) 172 - return -EINVAL; 173 - 174 - /* 175 182 * Record this frame record's values. 176 183 */ 177 - state->fp = READ_ONCE(*(unsigned long *)(kern_fp)); 178 - state->pc = READ_ONCE(*(unsigned long *)(kern_fp + 8)); 184 + state->fp = READ_ONCE(*(unsigned long *)(fp)); 185 + state->pc = READ_ONCE(*(unsigned long *)(fp + 8)); 179 186 180 187 return 0; 181 188 }
+1 -1
arch/arm64/kernel/stacktrace.c
··· 84 84 if (fp == (unsigned long)task_pt_regs(tsk)->stackframe) 85 85 return -ENOENT; 86 86 87 - err = unwind_next_frame_record(state, NULL); 87 + err = unwind_next_frame_record(state); 88 88 if (err) 89 89 return err; 90 90
+1 -1
arch/arm64/kvm/hyp/nvhe/stacktrace.c
··· 64 64 65 65 static int unwind_next(struct unwind_state *state) 66 66 { 67 - return unwind_next_frame_record(state, NULL); 67 + return unwind_next_frame_record(state); 68 68 } 69 69 70 70 static void notrace unwind(struct unwind_state *state,
+40 -22
arch/arm64/kvm/stacktrace.c
··· 34 34 }; 35 35 } 36 36 37 + static struct stack_info stackinfo_get_overflow_kern_va(void) 38 + { 39 + unsigned long low = (unsigned long)this_cpu_ptr_nvhe_sym(overflow_stack); 40 + unsigned long high = low + OVERFLOW_STACK_SIZE; 41 + 42 + return (struct stack_info) { 43 + .low = low, 44 + .high = high, 45 + }; 46 + } 47 + 37 48 static struct stack_info stackinfo_get_hyp(void) 38 49 { 39 50 struct kvm_nvhe_stacktrace_info *stacktrace_info 40 51 = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); 41 52 unsigned long low = (unsigned long)stacktrace_info->stack_base; 53 + unsigned long high = low + PAGE_SIZE; 54 + 55 + return (struct stack_info) { 56 + .low = low, 57 + .high = high, 58 + }; 59 + } 60 + 61 + static struct stack_info stackinfo_get_hyp_kern_va(void) 62 + { 63 + unsigned long low = (unsigned long)*this_cpu_ptr(&kvm_arm_hyp_stack_page); 42 64 unsigned long high = low + PAGE_SIZE; 43 65 44 66 return (struct stack_info) { ··· 84 62 */ 85 63 static bool kvm_nvhe_stack_kern_va(unsigned long *addr, unsigned long size) 86 64 { 87 - struct kvm_nvhe_stacktrace_info *stacktrace_info; 88 - unsigned long hyp_base, kern_base, hyp_offset; 89 - struct stack_info stack; 65 + struct stack_info stack_hyp, stack_kern; 90 66 91 - stacktrace_info = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); 92 - 93 - stack = stackinfo_get_hyp(); 94 - if (stackinfo_on_stack(&stack, *addr, size)) { 95 - kern_base = (unsigned long)*this_cpu_ptr(&kvm_arm_hyp_stack_page); 96 - hyp_base = (unsigned long)stacktrace_info->stack_base; 67 + stack_hyp = stackinfo_get_hyp(); 68 + stack_kern = stackinfo_get_hyp_kern_va(); 69 + if (stackinfo_on_stack(&stack_hyp, *addr, size)) 97 70 goto found; 98 - } 99 71 100 - stack = stackinfo_get_overflow(); 101 - if (stackinfo_on_stack(&stack, *addr, size)) { 102 - kern_base = (unsigned long)this_cpu_ptr_nvhe_sym(overflow_stack); 103 - hyp_base = (unsigned long)stacktrace_info->overflow_stack_base; 72 + stack_hyp = stackinfo_get_overflow(); 73 + stack_kern = stackinfo_get_overflow_kern_va(); 74 + if (stackinfo_on_stack(&stack_hyp, *addr, size)) 104 75 goto found; 105 - } 106 76 107 77 return false; 108 78 109 79 found: 110 - hyp_offset = *addr - hyp_base; 111 - 112 - *addr = kern_base + hyp_offset; 113 - 80 + *addr = *addr - stack_hyp.low + stack_kern.low; 114 81 return true; 115 82 } 116 83 ··· 113 102 114 103 static int unwind_next(struct unwind_state *state) 115 104 { 116 - return unwind_next_frame_record(state, kvm_nvhe_stack_kern_record_va); 105 + /* 106 + * The FP is in the hypervisor VA space. Convert it to the kernel VA 107 + * space so it can be unwound by the regular unwind functions. 108 + */ 109 + if (!kvm_nvhe_stack_kern_record_va(&state->fp)) 110 + return -EINVAL; 111 + 112 + return unwind_next_frame_record(state); 117 113 } 118 114 119 115 static void unwind(struct unwind_state *state, ··· 179 161 { 180 162 struct kvm_nvhe_stacktrace_info *stacktrace_info; 181 163 struct stack_info stacks[] = { 182 - stackinfo_get_overflow(), 183 - stackinfo_get_hyp(), 164 + stackinfo_get_overflow_kern_va(), 165 + stackinfo_get_hyp_kern_va(), 184 166 }; 185 167 struct unwind_state state = { 186 168 .stacks = stacks,