123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * KVM nVHE hypervisor stack tracing support.
- *
- * Copyright (C) 2022 Google LLC
- */
- #include <asm/kvm_asm.h>
- #include <asm/kvm_hyp.h>
- #include <asm/memory.h>
- #include <asm/percpu.h>
- DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
- __aligned(16);
- DEFINE_PER_CPU(struct kvm_nvhe_stacktrace_info, kvm_stacktrace_info);
- /*
- * hyp_prepare_backtrace - Prepare non-protected nVHE backtrace.
- *
- * @fp : frame pointer at which to start the unwinding.
- * @pc : program counter at which to start the unwinding.
- *
- * Save the information needed by the host to unwind the non-protected
- * nVHE hypervisor stack in EL1.
- */
- static void hyp_prepare_backtrace(unsigned long fp, unsigned long pc)
- {
- struct kvm_nvhe_stacktrace_info *stacktrace_info = this_cpu_ptr(&kvm_stacktrace_info);
- struct kvm_nvhe_init_params *params = this_cpu_ptr(&kvm_init_params);
- stacktrace_info->stack_base = (unsigned long)(params->stack_hyp_va - NVHE_STACK_SIZE);
- stacktrace_info->overflow_stack_base = (unsigned long)this_cpu_ptr(overflow_stack);
- stacktrace_info->fp = fp;
- stacktrace_info->pc = pc;
- }
- #ifdef CONFIG_PROTECTED_NVHE_STACKTRACE
- #include <asm/stacktrace/nvhe.h>
- DEFINE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], pkvm_stacktrace);
- static struct stack_info stackinfo_get_overflow(void)
- {
- unsigned long low = (unsigned long)this_cpu_ptr(overflow_stack);
- unsigned long high = low + OVERFLOW_STACK_SIZE;
- return (struct stack_info) {
- .low = low,
- .high = high,
- };
- }
- static struct stack_info stackinfo_get_hyp(void)
- {
- struct kvm_nvhe_init_params *params = this_cpu_ptr(&kvm_init_params);
- unsigned long high = params->stack_hyp_va;
- unsigned long low = high - NVHE_STACK_SIZE;
- return (struct stack_info) {
- .low = low,
- .high = high,
- };
- }
- static int unwind_next(struct unwind_state *state)
- {
- return unwind_next_frame_record(state);
- }
- static void notrace unwind(struct unwind_state *state,
- stack_trace_consume_fn consume_entry,
- void *cookie)
- {
- while (1) {
- int ret;
- if (!consume_entry(cookie, state->pc))
- break;
- ret = unwind_next(state);
- if (ret < 0)
- break;
- }
- }
- /*
- * pkvm_save_backtrace_entry - Saves a protected nVHE HYP stacktrace entry
- *
- * @arg : index of the entry in the stacktrace buffer
- * @where : the program counter corresponding to the stack frame
- *
- * Save the return address of a stack frame to the shared stacktrace buffer.
- * The host can access this shared buffer from EL1 to dump the backtrace.
- */
- static bool pkvm_save_backtrace_entry(void *arg, unsigned long where)
- {
- unsigned long *stacktrace = this_cpu_ptr(pkvm_stacktrace);
- int *idx = (int *)arg;
- /*
- * Need 2 free slots: 1 for current entry and 1 for the
- * delimiter.
- */
- if (*idx > ARRAY_SIZE(pkvm_stacktrace) - 2)
- return false;
- stacktrace[*idx] = where;
- stacktrace[++*idx] = 0UL;
- return true;
- }
- /*
- * pkvm_save_backtrace - Saves the protected nVHE HYP stacktrace
- *
- * @fp : frame pointer at which to start the unwinding.
- * @pc : program counter at which to start the unwinding.
- *
- * Save the unwinded stack addresses to the shared stacktrace buffer.
- * The host can access this shared buffer from EL1 to dump the backtrace.
- */
- static void pkvm_save_backtrace(unsigned long fp, unsigned long pc)
- {
- struct stack_info stacks[] = {
- stackinfo_get_overflow(),
- stackinfo_get_hyp(),
- };
- struct unwind_state state = {
- .stacks = stacks,
- .nr_stacks = ARRAY_SIZE(stacks),
- };
- int idx = 0;
- kvm_nvhe_unwind_init(&state, fp, pc);
- unwind(&state, pkvm_save_backtrace_entry, &idx);
- }
- #else /* !CONFIG_PROTECTED_NVHE_STACKTRACE */
- static void pkvm_save_backtrace(unsigned long fp, unsigned long pc)
- {
- }
- #endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */
- /*
- * kvm_nvhe_prepare_backtrace - prepare to dump the nVHE backtrace
- *
- * @fp : frame pointer at which to start the unwinding.
- * @pc : program counter at which to start the unwinding.
- *
- * Saves the information needed by the host to dump the nVHE hypervisor
- * backtrace.
- */
- void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc)
- {
- if (is_protected_kvm_enabled())
- pkvm_save_backtrace(fp, pc);
- else
- hyp_prepare_backtrace(fp, pc);
- }
|