159 lines
4.1 KiB
C
159 lines
4.1 KiB
C
// 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 - PAGE_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 - PAGE_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);
|
|
}
|