~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm64/kvm/stacktrace.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* SPDX-License-Identifier: GPL-2.0-only */
  2 /*
  3  * KVM nVHE hypervisor stack tracing support.
  4  *
  5  * The unwinder implementation depends on the nVHE mode:
  6  *
  7  *   1) Non-protected nVHE mode - the host can directly access the
  8  *      HYP stack pages and unwind the HYP stack in EL1. This saves having
  9  *      to allocate shared buffers for the host to read the unwinded
 10  *      stacktrace.
 11  *
 12  *   2) pKVM (protected nVHE) mode - the host cannot directly access
 13  *      the HYP memory. The stack is unwinded in EL2 and dumped to a shared
 14  *      buffer where the host can read and print the stacktrace.
 15  *
 16  * Copyright (C) 2022 Google LLC
 17  */
 18 
 19 #include <linux/kvm.h>
 20 #include <linux/kvm_host.h>
 21 
 22 #include <asm/stacktrace/nvhe.h>
 23 
 24 static struct stack_info stackinfo_get_overflow(void)
 25 {
 26         struct kvm_nvhe_stacktrace_info *stacktrace_info
 27                                 = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info);
 28         unsigned long low = (unsigned long)stacktrace_info->overflow_stack_base;
 29         unsigned long high = low + OVERFLOW_STACK_SIZE;
 30 
 31         return (struct stack_info) {
 32                 .low = low,
 33                 .high = high,
 34         };
 35 }
 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 
 48 static struct stack_info stackinfo_get_hyp(void)
 49 {
 50         struct kvm_nvhe_stacktrace_info *stacktrace_info
 51                                 = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info);
 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);
 64         unsigned long high = low + PAGE_SIZE;
 65 
 66         return (struct stack_info) {
 67                 .low = low,
 68                 .high = high,
 69         };
 70 }
 71 
 72 /*
 73  * kvm_nvhe_stack_kern_va - Convert KVM nVHE HYP stack addresses to a kernel VAs
 74  *
 75  * The nVHE hypervisor stack is mapped in the flexible 'private' VA range, to
 76  * allow for guard pages below the stack. Consequently, the fixed offset address
 77  * translation macros won't work here.
 78  *
 79  * The kernel VA is calculated as an offset from the kernel VA of the hypervisor
 80  * stack base.
 81  *
 82  * Returns true on success and updates @addr to its corresponding kernel VA;
 83  * otherwise returns false.
 84  */
 85 static bool kvm_nvhe_stack_kern_va(unsigned long *addr, unsigned long size)
 86 {
 87         struct stack_info stack_hyp, stack_kern;
 88 
 89         stack_hyp = stackinfo_get_hyp();
 90         stack_kern = stackinfo_get_hyp_kern_va();
 91         if (stackinfo_on_stack(&stack_hyp, *addr, size))
 92                 goto found;
 93 
 94         stack_hyp = stackinfo_get_overflow();
 95         stack_kern = stackinfo_get_overflow_kern_va();
 96         if (stackinfo_on_stack(&stack_hyp, *addr, size))
 97                 goto found;
 98 
 99         return false;
100 
101 found:
102         *addr = *addr - stack_hyp.low + stack_kern.low;
103         return true;
104 }
105 
106 /*
107  * Convert a KVN nVHE HYP frame record address to a kernel VA
108  */
109 static bool kvm_nvhe_stack_kern_record_va(unsigned long *addr)
110 {
111         return kvm_nvhe_stack_kern_va(addr, 16);
112 }
113 
114 static int unwind_next(struct unwind_state *state)
115 {
116         /*
117          * The FP is in the hypervisor VA space. Convert it to the kernel VA
118          * space so it can be unwound by the regular unwind functions.
119          */
120         if (!kvm_nvhe_stack_kern_record_va(&state->fp))
121                 return -EINVAL;
122 
123         return unwind_next_frame_record(state);
124 }
125 
126 static void unwind(struct unwind_state *state,
127                    stack_trace_consume_fn consume_entry, void *cookie)
128 {
129         while (1) {
130                 int ret;
131 
132                 if (!consume_entry(cookie, state->pc))
133                         break;
134                 ret = unwind_next(state);
135                 if (ret < 0)
136                         break;
137         }
138 }
139 
140 /*
141  * kvm_nvhe_dump_backtrace_entry - Symbolize and print an nVHE backtrace entry
142  *
143  * @arg    : the hypervisor offset, used for address translation
144  * @where  : the program counter corresponding to the stack frame
145  */
146 static bool kvm_nvhe_dump_backtrace_entry(void *arg, unsigned long where)
147 {
148         unsigned long va_mask = GENMASK_ULL(vabits_actual - 1, 0);
149         unsigned long hyp_offset = (unsigned long)arg;
150 
151         /* Mask tags and convert to kern addr */
152         where = (where & va_mask) + hyp_offset;
153         kvm_err(" [<%016lx>] %pB\n", where, (void *)(where + kaslr_offset()));
154 
155         return true;
156 }
157 
158 static void kvm_nvhe_dump_backtrace_start(void)
159 {
160         kvm_err("nVHE call trace:\n");
161 }
162 
163 static void kvm_nvhe_dump_backtrace_end(void)
164 {
165         kvm_err("---[ end nVHE call trace ]---\n");
166 }
167 
168 /*
169  * hyp_dump_backtrace - Dump the non-protected nVHE backtrace.
170  *
171  * @hyp_offset: hypervisor offset, used for address translation.
172  *
173  * The host can directly access HYP stack pages in non-protected
174  * mode, so the unwinding is done directly from EL1. This removes
175  * the need for shared buffers between host and hypervisor for
176  * the stacktrace.
177  */
178 static void hyp_dump_backtrace(unsigned long hyp_offset)
179 {
180         struct kvm_nvhe_stacktrace_info *stacktrace_info;
181         struct stack_info stacks[] = {
182                 stackinfo_get_overflow_kern_va(),
183                 stackinfo_get_hyp_kern_va(),
184         };
185         struct unwind_state state = {
186                 .stacks = stacks,
187                 .nr_stacks = ARRAY_SIZE(stacks),
188         };
189 
190         stacktrace_info = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info);
191 
192         kvm_nvhe_unwind_init(&state, stacktrace_info->fp, stacktrace_info->pc);
193 
194         kvm_nvhe_dump_backtrace_start();
195         unwind(&state, kvm_nvhe_dump_backtrace_entry, (void *)hyp_offset);
196         kvm_nvhe_dump_backtrace_end();
197 }
198 
199 #ifdef CONFIG_PROTECTED_NVHE_STACKTRACE
200 DECLARE_KVM_NVHE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)],
201                          pkvm_stacktrace);
202 
203 /*
204  * pkvm_dump_backtrace - Dump the protected nVHE HYP backtrace.
205  *
206  * @hyp_offset: hypervisor offset, used for address translation.
207  *
208  * Dumping of the pKVM HYP backtrace is done by reading the
209  * stack addresses from the shared stacktrace buffer, since the
210  * host cannot directly access hypervisor memory in protected
211  * mode.
212  */
213 static void pkvm_dump_backtrace(unsigned long hyp_offset)
214 {
215         unsigned long *stacktrace
216                 = (unsigned long *) this_cpu_ptr_nvhe_sym(pkvm_stacktrace);
217         int i;
218 
219         kvm_nvhe_dump_backtrace_start();
220         /* The saved stacktrace is terminated by a null entry */
221         for (i = 0;
222              i < ARRAY_SIZE(kvm_nvhe_sym(pkvm_stacktrace)) && stacktrace[i];
223              i++)
224                 kvm_nvhe_dump_backtrace_entry((void *)hyp_offset, stacktrace[i]);
225         kvm_nvhe_dump_backtrace_end();
226 }
227 #else   /* !CONFIG_PROTECTED_NVHE_STACKTRACE */
228 static void pkvm_dump_backtrace(unsigned long hyp_offset)
229 {
230         kvm_err("Cannot dump pKVM nVHE stacktrace: !CONFIG_PROTECTED_NVHE_STACKTRACE\n");
231 }
232 #endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */
233 
234 /*
235  * kvm_nvhe_dump_backtrace - Dump KVM nVHE hypervisor backtrace.
236  *
237  * @hyp_offset: hypervisor offset, used for address translation.
238  */
239 void kvm_nvhe_dump_backtrace(unsigned long hyp_offset)
240 {
241         if (is_protected_kvm_enabled())
242                 pkvm_dump_backtrace(hyp_offset);
243         else
244                 hyp_dump_backtrace(hyp_offset);
245 }
246 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php