162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * KVM nVHE hypervisor stack tracing support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The unwinder implementation depends on the nVHE mode: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * 1) Non-protected nVHE mode - the host can directly access the 862306a36Sopenharmony_ci * HYP stack pages and unwind the HYP stack in EL1. This saves having 962306a36Sopenharmony_ci * to allocate shared buffers for the host to read the unwinded 1062306a36Sopenharmony_ci * stacktrace. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * 2) pKVM (protected nVHE) mode - the host cannot directly access 1362306a36Sopenharmony_ci * the HYP memory. The stack is unwinded in EL2 and dumped to a shared 1462306a36Sopenharmony_ci * buffer where the host can read and print the stacktrace. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Copyright (C) 2022 Google LLC 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/kvm.h> 2062306a36Sopenharmony_ci#include <linux/kvm_host.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/stacktrace/nvhe.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic struct stack_info stackinfo_get_overflow(void) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct kvm_nvhe_stacktrace_info *stacktrace_info 2762306a36Sopenharmony_ci = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); 2862306a36Sopenharmony_ci unsigned long low = (unsigned long)stacktrace_info->overflow_stack_base; 2962306a36Sopenharmony_ci unsigned long high = low + OVERFLOW_STACK_SIZE; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return (struct stack_info) { 3262306a36Sopenharmony_ci .low = low, 3362306a36Sopenharmony_ci .high = high, 3462306a36Sopenharmony_ci }; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct stack_info stackinfo_get_overflow_kern_va(void) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci unsigned long low = (unsigned long)this_cpu_ptr_nvhe_sym(overflow_stack); 4062306a36Sopenharmony_ci unsigned long high = low + OVERFLOW_STACK_SIZE; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return (struct stack_info) { 4362306a36Sopenharmony_ci .low = low, 4462306a36Sopenharmony_ci .high = high, 4562306a36Sopenharmony_ci }; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct stack_info stackinfo_get_hyp(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct kvm_nvhe_stacktrace_info *stacktrace_info 5162306a36Sopenharmony_ci = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); 5262306a36Sopenharmony_ci unsigned long low = (unsigned long)stacktrace_info->stack_base; 5362306a36Sopenharmony_ci unsigned long high = low + PAGE_SIZE; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return (struct stack_info) { 5662306a36Sopenharmony_ci .low = low, 5762306a36Sopenharmony_ci .high = high, 5862306a36Sopenharmony_ci }; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct stack_info stackinfo_get_hyp_kern_va(void) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned long low = (unsigned long)*this_cpu_ptr(&kvm_arm_hyp_stack_page); 6462306a36Sopenharmony_ci unsigned long high = low + PAGE_SIZE; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return (struct stack_info) { 6762306a36Sopenharmony_ci .low = low, 6862306a36Sopenharmony_ci .high = high, 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * kvm_nvhe_stack_kern_va - Convert KVM nVHE HYP stack addresses to a kernel VAs 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * The nVHE hypervisor stack is mapped in the flexible 'private' VA range, to 7662306a36Sopenharmony_ci * allow for guard pages below the stack. Consequently, the fixed offset address 7762306a36Sopenharmony_ci * translation macros won't work here. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * The kernel VA is calculated as an offset from the kernel VA of the hypervisor 8062306a36Sopenharmony_ci * stack base. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Returns true on success and updates @addr to its corresponding kernel VA; 8362306a36Sopenharmony_ci * otherwise returns false. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic bool kvm_nvhe_stack_kern_va(unsigned long *addr, unsigned long size) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct stack_info stack_hyp, stack_kern; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci stack_hyp = stackinfo_get_hyp(); 9062306a36Sopenharmony_ci stack_kern = stackinfo_get_hyp_kern_va(); 9162306a36Sopenharmony_ci if (stackinfo_on_stack(&stack_hyp, *addr, size)) 9262306a36Sopenharmony_ci goto found; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci stack_hyp = stackinfo_get_overflow(); 9562306a36Sopenharmony_ci stack_kern = stackinfo_get_overflow_kern_va(); 9662306a36Sopenharmony_ci if (stackinfo_on_stack(&stack_hyp, *addr, size)) 9762306a36Sopenharmony_ci goto found; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return false; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cifound: 10262306a36Sopenharmony_ci *addr = *addr - stack_hyp.low + stack_kern.low; 10362306a36Sopenharmony_ci return true; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Convert a KVN nVHE HYP frame record address to a kernel VA 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic bool kvm_nvhe_stack_kern_record_va(unsigned long *addr) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci return kvm_nvhe_stack_kern_va(addr, 16); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int unwind_next(struct unwind_state *state) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * The FP is in the hypervisor VA space. Convert it to the kernel VA 11862306a36Sopenharmony_ci * space so it can be unwound by the regular unwind functions. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci if (!kvm_nvhe_stack_kern_record_va(&state->fp)) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return unwind_next_frame_record(state); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void unwind(struct unwind_state *state, 12762306a36Sopenharmony_ci stack_trace_consume_fn consume_entry, void *cookie) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci while (1) { 13062306a36Sopenharmony_ci int ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!consume_entry(cookie, state->pc)) 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci ret = unwind_next(state); 13562306a36Sopenharmony_ci if (ret < 0) 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * kvm_nvhe_dump_backtrace_entry - Symbolize and print an nVHE backtrace entry 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * @arg : the hypervisor offset, used for address translation 14462306a36Sopenharmony_ci * @where : the program counter corresponding to the stack frame 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic bool kvm_nvhe_dump_backtrace_entry(void *arg, unsigned long where) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long va_mask = GENMASK_ULL(vabits_actual - 1, 0); 14962306a36Sopenharmony_ci unsigned long hyp_offset = (unsigned long)arg; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Mask tags and convert to kern addr */ 15262306a36Sopenharmony_ci where = (where & va_mask) + hyp_offset; 15362306a36Sopenharmony_ci kvm_err(" [<%016lx>] %pB\n", where, (void *)(where + kaslr_offset())); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return true; 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void kvm_nvhe_dump_backtrace_start(void) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci kvm_err("nVHE call trace:\n"); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void kvm_nvhe_dump_backtrace_end(void) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci kvm_err("---[ end nVHE call trace ]---\n"); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * hyp_dump_backtrace - Dump the non-protected nVHE backtrace. 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * @hyp_offset: hypervisor offset, used for address translation. 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * The host can directly access HYP stack pages in non-protected 17462306a36Sopenharmony_ci * mode, so the unwinding is done directly from EL1. This removes 17562306a36Sopenharmony_ci * the need for shared buffers between host and hypervisor for 17662306a36Sopenharmony_ci * the stacktrace. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_cistatic void hyp_dump_backtrace(unsigned long hyp_offset) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct kvm_nvhe_stacktrace_info *stacktrace_info; 18162306a36Sopenharmony_ci struct stack_info stacks[] = { 18262306a36Sopenharmony_ci stackinfo_get_overflow_kern_va(), 18362306a36Sopenharmony_ci stackinfo_get_hyp_kern_va(), 18462306a36Sopenharmony_ci }; 18562306a36Sopenharmony_ci struct unwind_state state = { 18662306a36Sopenharmony_ci .stacks = stacks, 18762306a36Sopenharmony_ci .nr_stacks = ARRAY_SIZE(stacks), 18862306a36Sopenharmony_ci }; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci stacktrace_info = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci kvm_nvhe_unwind_init(&state, stacktrace_info->fp, stacktrace_info->pc); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci kvm_nvhe_dump_backtrace_start(); 19562306a36Sopenharmony_ci unwind(&state, kvm_nvhe_dump_backtrace_entry, (void *)hyp_offset); 19662306a36Sopenharmony_ci kvm_nvhe_dump_backtrace_end(); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci#ifdef CONFIG_PROTECTED_NVHE_STACKTRACE 20062306a36Sopenharmony_ciDECLARE_KVM_NVHE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], 20162306a36Sopenharmony_ci pkvm_stacktrace); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/* 20462306a36Sopenharmony_ci * pkvm_dump_backtrace - Dump the protected nVHE HYP backtrace. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * @hyp_offset: hypervisor offset, used for address translation. 20762306a36Sopenharmony_ci * 20862306a36Sopenharmony_ci * Dumping of the pKVM HYP backtrace is done by reading the 20962306a36Sopenharmony_ci * stack addresses from the shared stacktrace buffer, since the 21062306a36Sopenharmony_ci * host cannot directly access hypervisor memory in protected 21162306a36Sopenharmony_ci * mode. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic void pkvm_dump_backtrace(unsigned long hyp_offset) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci unsigned long *stacktrace 21662306a36Sopenharmony_ci = (unsigned long *) this_cpu_ptr_nvhe_sym(pkvm_stacktrace); 21762306a36Sopenharmony_ci int i; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci kvm_nvhe_dump_backtrace_start(); 22062306a36Sopenharmony_ci /* The saved stacktrace is terminated by a null entry */ 22162306a36Sopenharmony_ci for (i = 0; 22262306a36Sopenharmony_ci i < ARRAY_SIZE(kvm_nvhe_sym(pkvm_stacktrace)) && stacktrace[i]; 22362306a36Sopenharmony_ci i++) 22462306a36Sopenharmony_ci kvm_nvhe_dump_backtrace_entry((void *)hyp_offset, stacktrace[i]); 22562306a36Sopenharmony_ci kvm_nvhe_dump_backtrace_end(); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci#else /* !CONFIG_PROTECTED_NVHE_STACKTRACE */ 22862306a36Sopenharmony_cistatic void pkvm_dump_backtrace(unsigned long hyp_offset) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci kvm_err("Cannot dump pKVM nVHE stacktrace: !CONFIG_PROTECTED_NVHE_STACKTRACE\n"); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci#endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * kvm_nvhe_dump_backtrace - Dump KVM nVHE hypervisor backtrace. 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * @hyp_offset: hypervisor offset, used for address translation. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_civoid kvm_nvhe_dump_backtrace(unsigned long hyp_offset) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci if (is_protected_kvm_enabled()) 24262306a36Sopenharmony_ci pkvm_dump_backtrace(hyp_offset); 24362306a36Sopenharmony_ci else 24462306a36Sopenharmony_ci hyp_dump_backtrace(hyp_offset); 24562306a36Sopenharmony_ci} 246