mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-05 17:03:47 -04:00
Merge branch kvm-arm64/debug-6.14 into kvmarm-master/next
* kvm-arm64/debug-6.14: : . : Large rework of the debug code to make it a bit less horrid, : courtesy of Oliver Upton. From the original cover letter: : : "The debug code has become a bit difficult to reason about, especially : all the hacks and bandaids for state tracking + trap configuration. : : This series reworks the entire mess around using a single enumeration to : track the state of the debug registers (free, guest-owned, host-owned), : using that to drive trap configuration and save/restore. : : On top of that, this series wires most of the implementation into vCPU : load/put rather than the main KVM_RUN loop. This has been a long time : coming for VHE, as a lot of the trap configuration and EL1 state gets : loaded into hardware at that point anyway. : : The save/restore of the debug registers is simplified quite a bit as : well. KVM will now restore the registers for *any* access rather than : just writes, and keep doing so until the next vcpu_put() instead of : dropping it on the floor after the next exception." : . KVM: arm64: Promote guest ownership for DBGxVR/DBGxCR reads KVM: arm64: Fold DBGxVR/DBGxCR accessors into common set KVM: arm64: Avoid reading ID_AA64DFR0_EL1 for debug save/restore KVM: arm64: nv: Honor MDCR_EL2.TDE routing for debug exceptions KVM: arm64: Manage software step state at load/put KVM: arm64: Don't hijack guest context MDSCR_EL1 KVM: arm64: Compute MDCR_EL2 at vcpu_load() KVM: arm64: Reload vCPU for accesses to OSLAR_EL1 KVM: arm64: Use debug_owner to track if debug regs need save/restore KVM: arm64: Remove vestiges of debug_ptr KVM: arm64: Remove debug tracepoints KVM: arm64: Select debug state to save/restore based on debug owner KVM: arm64: Clean up KVM_SET_GUEST_DEBUG handler KVM: arm64: Evaluate debug owner at vcpu_load() KVM: arm64: Write MDCR_EL2 directly from kvm_arm_setup_mdcr_el2() KVM: arm64: Move host SME/SVE tracking flags to host data KVM: arm64: Track presence of SPE/TRBE in kvm_host_data instead of vCPU KVM: arm64: Get rid of __kvm_get_mdcr_el2() and related warts KVM: arm64: Drop MDSCR_EL1_DEBUG_MASK Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
@@ -53,8 +53,7 @@
|
||||
enum __kvm_host_smccc_func {
|
||||
/* Hypercalls available only prior to pKVM finalisation */
|
||||
/* __KVM_HOST_SMCCC_FUNC___kvm_hyp_init */
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_get_mdcr_el2 = __KVM_HOST_SMCCC_FUNC___kvm_hyp_init + 1,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_init,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_init = __KVM_HOST_SMCCC_FUNC___kvm_hyp_init + 1,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_create_private_mapping,
|
||||
__KVM_HOST_SMCCC_FUNC___pkvm_cpu_set_vector,
|
||||
__KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs,
|
||||
@@ -247,8 +246,6 @@ extern void __kvm_adjust_pc(struct kvm_vcpu *vcpu);
|
||||
extern u64 __vgic_v3_get_gic_config(void);
|
||||
extern void __vgic_v3_init_lrs(void);
|
||||
|
||||
extern u64 __kvm_get_mdcr_el2(void);
|
||||
|
||||
#define __KVM_EXTABLE(from, to) \
|
||||
" .pushsection __kvm_ex_table, \"a\"\n" \
|
||||
" .align 3\n" \
|
||||
|
||||
@@ -610,6 +610,12 @@ struct cpu_sve_state {
|
||||
* field.
|
||||
*/
|
||||
struct kvm_host_data {
|
||||
#define KVM_HOST_DATA_FLAG_HAS_SPE 0
|
||||
#define KVM_HOST_DATA_FLAG_HAS_TRBE 1
|
||||
#define KVM_HOST_DATA_FLAG_HOST_SVE_ENABLED 2
|
||||
#define KVM_HOST_DATA_FLAG_HOST_SME_ENABLED 3
|
||||
unsigned long flags;
|
||||
|
||||
struct kvm_cpu_context host_ctxt;
|
||||
|
||||
/*
|
||||
@@ -642,7 +648,7 @@ struct kvm_host_data {
|
||||
* host_debug_state contains the host registers which are
|
||||
* saved and restored during world switches.
|
||||
*/
|
||||
struct {
|
||||
struct {
|
||||
/* {Break,watch}point registers */
|
||||
struct kvm_guest_debug_arch regs;
|
||||
/* Statistical profiling extension */
|
||||
@@ -652,6 +658,13 @@ struct kvm_host_data {
|
||||
/* Values of trap registers for the host before guest entry. */
|
||||
u64 mdcr_el2;
|
||||
} host_debug_state;
|
||||
|
||||
/* Number of programmable event counters (PMCR_EL0.N) for this CPU */
|
||||
unsigned int nr_event_counters;
|
||||
|
||||
/* Number of debug breakpoints/watchpoints for this CPU (minus 1) */
|
||||
unsigned int debug_brps;
|
||||
unsigned int debug_wrps;
|
||||
};
|
||||
|
||||
struct kvm_host_psci_config {
|
||||
@@ -739,31 +752,22 @@ struct kvm_vcpu_arch {
|
||||
*
|
||||
* external_debug_state contains the debug values we want to debug the
|
||||
* guest. This is set via the KVM_SET_GUEST_DEBUG ioctl.
|
||||
*
|
||||
* debug_ptr points to the set of debug registers that should be loaded
|
||||
* onto the hardware when running the guest.
|
||||
*/
|
||||
struct kvm_guest_debug_arch *debug_ptr;
|
||||
struct kvm_guest_debug_arch vcpu_debug_state;
|
||||
struct kvm_guest_debug_arch external_debug_state;
|
||||
u64 external_mdscr_el1;
|
||||
|
||||
enum {
|
||||
VCPU_DEBUG_FREE,
|
||||
VCPU_DEBUG_HOST_OWNED,
|
||||
VCPU_DEBUG_GUEST_OWNED,
|
||||
} debug_owner;
|
||||
|
||||
/* VGIC state */
|
||||
struct vgic_cpu vgic_cpu;
|
||||
struct arch_timer_cpu timer_cpu;
|
||||
struct kvm_pmu pmu;
|
||||
|
||||
/*
|
||||
* Guest registers we preserve during guest debugging.
|
||||
*
|
||||
* These shadow registers are updated by the kvm_handle_sys_reg
|
||||
* trap handler if the guest accesses or updates them while we
|
||||
* are using guest debug.
|
||||
*/
|
||||
struct {
|
||||
u32 mdscr_el1;
|
||||
bool pstate_ss;
|
||||
} guest_debug_preserved;
|
||||
|
||||
/* vcpu power state */
|
||||
struct kvm_mp_state mp_state;
|
||||
spinlock_t mp_state_lock;
|
||||
@@ -906,29 +910,21 @@ struct kvm_vcpu_arch {
|
||||
#define EXCEPT_AA64_EL2_IRQ __vcpu_except_flags(5)
|
||||
#define EXCEPT_AA64_EL2_FIQ __vcpu_except_flags(6)
|
||||
#define EXCEPT_AA64_EL2_SERR __vcpu_except_flags(7)
|
||||
/* Guest debug is live */
|
||||
#define DEBUG_DIRTY __vcpu_single_flag(iflags, BIT(4))
|
||||
/* Save SPE context if active */
|
||||
#define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5))
|
||||
/* Save TRBE context if active */
|
||||
#define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6))
|
||||
|
||||
/* SVE enabled for host EL0 */
|
||||
#define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0))
|
||||
/* SME enabled for EL0 */
|
||||
#define HOST_SME_ENABLED __vcpu_single_flag(sflags, BIT(1))
|
||||
/* Physical CPU not in supported_cpus */
|
||||
#define ON_UNSUPPORTED_CPU __vcpu_single_flag(sflags, BIT(2))
|
||||
#define ON_UNSUPPORTED_CPU __vcpu_single_flag(sflags, BIT(0))
|
||||
/* WFIT instruction trapped */
|
||||
#define IN_WFIT __vcpu_single_flag(sflags, BIT(3))
|
||||
#define IN_WFIT __vcpu_single_flag(sflags, BIT(1))
|
||||
/* vcpu system registers loaded on physical CPU */
|
||||
#define SYSREGS_ON_CPU __vcpu_single_flag(sflags, BIT(4))
|
||||
/* Software step state is Active-pending */
|
||||
#define DBG_SS_ACTIVE_PENDING __vcpu_single_flag(sflags, BIT(5))
|
||||
#define SYSREGS_ON_CPU __vcpu_single_flag(sflags, BIT(2))
|
||||
/* Software step state is Active-pending for external debug */
|
||||
#define HOST_SS_ACTIVE_PENDING __vcpu_single_flag(sflags, BIT(3))
|
||||
/* Software step state is Active pending for guest debug */
|
||||
#define GUEST_SS_ACTIVE_PENDING __vcpu_single_flag(sflags, BIT(4))
|
||||
/* PMUSERENR for the guest EL0 is on physical CPU */
|
||||
#define PMUSERENR_ON_CPU __vcpu_single_flag(sflags, BIT(6))
|
||||
#define PMUSERENR_ON_CPU __vcpu_single_flag(sflags, BIT(5))
|
||||
/* WFI instruction trapped */
|
||||
#define IN_WFI __vcpu_single_flag(sflags, BIT(7))
|
||||
#define IN_WFI __vcpu_single_flag(sflags, BIT(6))
|
||||
|
||||
|
||||
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
|
||||
@@ -1307,6 +1303,13 @@ DECLARE_KVM_HYP_PER_CPU(struct kvm_host_data, kvm_host_data);
|
||||
&this_cpu_ptr_hyp_sym(kvm_host_data)->f)
|
||||
#endif
|
||||
|
||||
#define host_data_test_flag(flag) \
|
||||
(test_bit(KVM_HOST_DATA_FLAG_##flag, host_data_ptr(flags)))
|
||||
#define host_data_set_flag(flag) \
|
||||
set_bit(KVM_HOST_DATA_FLAG_##flag, host_data_ptr(flags))
|
||||
#define host_data_clear_flag(flag) \
|
||||
clear_bit(KVM_HOST_DATA_FLAG_##flag, host_data_ptr(flags))
|
||||
|
||||
/* Check whether the FP regs are owned by the guest */
|
||||
static inline bool guest_owns_fp_regs(void)
|
||||
{
|
||||
@@ -1332,15 +1335,22 @@ static inline bool kvm_system_needs_idmapped_vectors(void)
|
||||
|
||||
static inline void kvm_arch_sync_events(struct kvm *kvm) {}
|
||||
|
||||
void kvm_arm_init_debug(void);
|
||||
void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_arm_clear_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu);
|
||||
void kvm_init_host_debug_data(void);
|
||||
void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_vcpu_put_debug(struct kvm_vcpu *vcpu);
|
||||
void kvm_debug_set_guest_ownership(struct kvm_vcpu *vcpu);
|
||||
void kvm_debug_handle_oslar(struct kvm_vcpu *vcpu, u64 val);
|
||||
|
||||
#define kvm_vcpu_os_lock_enabled(vcpu) \
|
||||
(!!(__vcpu_sys_reg(vcpu, OSLSR_EL1) & OSLSR_EL1_OSLK))
|
||||
|
||||
#define kvm_debug_regs_in_use(vcpu) \
|
||||
((vcpu)->arch.debug_owner != VCPU_DEBUG_FREE)
|
||||
#define kvm_host_owns_debug_regs(vcpu) \
|
||||
((vcpu)->arch.debug_owner == VCPU_DEBUG_HOST_OWNED)
|
||||
#define kvm_guest_owns_debug_regs(vcpu) \
|
||||
((vcpu)->arch.debug_owner == VCPU_DEBUG_GUEST_OWNED)
|
||||
|
||||
int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
|
||||
struct kvm_device_attr *attr);
|
||||
int kvm_arm_vcpu_arch_get_attr(struct kvm_vcpu *vcpu,
|
||||
@@ -1367,10 +1377,6 @@ static inline bool kvm_pmu_counter_deferred(struct perf_event_attr *attr)
|
||||
return (!has_vhe() && attr->exclude_host);
|
||||
}
|
||||
|
||||
/* Flags for host debug state */
|
||||
void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu);
|
||||
void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu);
|
||||
|
||||
#ifdef CONFIG_KVM
|
||||
void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr);
|
||||
void kvm_clr_pmu_events(u64 clr);
|
||||
|
||||
@@ -64,6 +64,7 @@ static inline u64 translate_ttbr0_el2_to_ttbr0_el1(u64 ttbr0)
|
||||
}
|
||||
|
||||
extern bool forward_smc_trap(struct kvm_vcpu *vcpu);
|
||||
extern bool forward_debug_exception(struct kvm_vcpu *vcpu);
|
||||
extern void kvm_init_nested(struct kvm *kvm);
|
||||
extern int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu);
|
||||
extern void kvm_init_nested_s2_mmu(struct kvm_s2_mmu *mmu);
|
||||
|
||||
@@ -476,8 +476,6 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
||||
|
||||
kvm_pmu_vcpu_init(vcpu);
|
||||
|
||||
kvm_arm_reset_debug_ptr(vcpu);
|
||||
|
||||
kvm_arm_pvtime_vcpu_init(&vcpu->arch);
|
||||
|
||||
vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu;
|
||||
@@ -598,6 +596,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
|
||||
kvm_vgic_load(vcpu);
|
||||
kvm_timer_vcpu_load(vcpu);
|
||||
kvm_vcpu_load_debug(vcpu);
|
||||
if (has_vhe())
|
||||
kvm_vcpu_load_vhe(vcpu);
|
||||
kvm_arch_vcpu_load_fp(vcpu);
|
||||
@@ -617,15 +616,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
|
||||
|
||||
vcpu_set_pauth_traps(vcpu);
|
||||
|
||||
kvm_arch_vcpu_load_debug_state_flags(vcpu);
|
||||
|
||||
if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus))
|
||||
vcpu_set_on_unsupported_cpu(vcpu);
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
kvm_arch_vcpu_put_debug_state_flags(vcpu);
|
||||
kvm_vcpu_put_debug(vcpu);
|
||||
kvm_arch_vcpu_put_fp(vcpu);
|
||||
if (has_vhe())
|
||||
kvm_vcpu_put_vhe(vcpu);
|
||||
@@ -808,8 +805,6 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
|
||||
|
||||
kvm_init_mpidr_data(kvm);
|
||||
|
||||
kvm_arm_vcpu_init_debug(vcpu);
|
||||
|
||||
if (likely(irqchip_in_kernel(kvm))) {
|
||||
/*
|
||||
* Map the VGIC hardware resources before running a vcpu the
|
||||
@@ -1187,7 +1182,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
||||
continue;
|
||||
}
|
||||
|
||||
kvm_arm_setup_debug(vcpu);
|
||||
kvm_arch_vcpu_ctxflush_fp(vcpu);
|
||||
|
||||
/**************************************************************
|
||||
@@ -1204,8 +1198,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
||||
* Back from guest
|
||||
*************************************************************/
|
||||
|
||||
kvm_arm_clear_debug(vcpu);
|
||||
|
||||
/*
|
||||
* We must sync the PMU state before the vgic state so
|
||||
* that the vgic can properly sample the updated state of the
|
||||
@@ -2109,6 +2101,7 @@ static void cpu_set_hyp_vector(void)
|
||||
static void cpu_hyp_init_context(void)
|
||||
{
|
||||
kvm_init_host_cpu_context(host_data_ptr(host_ctxt));
|
||||
kvm_init_host_debug_data();
|
||||
|
||||
if (!is_kernel_in_hyp_mode())
|
||||
cpu_init_hyp_mode();
|
||||
@@ -2117,7 +2110,6 @@ static void cpu_hyp_init_context(void)
|
||||
static void cpu_hyp_init_features(void)
|
||||
{
|
||||
cpu_set_hyp_vector();
|
||||
kvm_arm_init_debug();
|
||||
|
||||
if (is_kernel_in_hyp_mode())
|
||||
kvm_timer_init_vhe();
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Debug and Guest Debug support
|
||||
*
|
||||
* Copyright (C) 2015 - Linaro Ltd
|
||||
* Author: Alex Bennée <alex.bennee@linaro.org>
|
||||
* Authors: Alex Bennée <alex.bennee@linaro.org>
|
||||
* Oliver Upton <oliver.upton@linux.dev>
|
||||
*/
|
||||
|
||||
#include <linux/kvm_host.h>
|
||||
@@ -14,72 +15,6 @@
|
||||
#include <asm/kvm_arm.h>
|
||||
#include <asm/kvm_emulate.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
/* These are the bits of MDSCR_EL1 we may manipulate */
|
||||
#define MDSCR_EL1_DEBUG_MASK (DBG_MDSCR_SS | \
|
||||
DBG_MDSCR_KDE | \
|
||||
DBG_MDSCR_MDE)
|
||||
|
||||
static DEFINE_PER_CPU(u64, mdcr_el2);
|
||||
|
||||
/*
|
||||
* save/restore_guest_debug_regs
|
||||
*
|
||||
* For some debug operations we need to tweak some guest registers. As
|
||||
* a result we need to save the state of those registers before we
|
||||
* make those modifications.
|
||||
*
|
||||
* Guest access to MDSCR_EL1 is trapped by the hypervisor and handled
|
||||
* after we have restored the preserved value to the main context.
|
||||
*
|
||||
* When single-step is enabled by userspace, we tweak PSTATE.SS on every
|
||||
* guest entry. Preserve PSTATE.SS so we can restore the original value
|
||||
* for the vcpu after the single-step is disabled.
|
||||
*/
|
||||
static void save_guest_debug_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
|
||||
vcpu->arch.guest_debug_preserved.mdscr_el1 = val;
|
||||
|
||||
trace_kvm_arm_set_dreg32("Saved MDSCR_EL1",
|
||||
vcpu->arch.guest_debug_preserved.mdscr_el1);
|
||||
|
||||
vcpu->arch.guest_debug_preserved.pstate_ss =
|
||||
(*vcpu_cpsr(vcpu) & DBG_SPSR_SS);
|
||||
}
|
||||
|
||||
static void restore_guest_debug_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 val = vcpu->arch.guest_debug_preserved.mdscr_el1;
|
||||
|
||||
vcpu_write_sys_reg(vcpu, val, MDSCR_EL1);
|
||||
|
||||
trace_kvm_arm_set_dreg32("Restored MDSCR_EL1",
|
||||
vcpu_read_sys_reg(vcpu, MDSCR_EL1));
|
||||
|
||||
if (vcpu->arch.guest_debug_preserved.pstate_ss)
|
||||
*vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
||||
else
|
||||
*vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arm_init_debug - grab what we need for debug
|
||||
*
|
||||
* Currently the sole task of this function is to retrieve the initial
|
||||
* value of mdcr_el2 so we can preserve MDCR_EL2.HPMN which has
|
||||
* presumably been set-up by some knowledgeable bootcode.
|
||||
*
|
||||
* It is called once per-cpu during CPU hyp initialisation.
|
||||
*/
|
||||
|
||||
void kvm_arm_init_debug(void)
|
||||
{
|
||||
__this_cpu_write(mdcr_el2, kvm_call_hyp_ret(__kvm_get_mdcr_el2));
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arm_setup_mdcr_el2 - configure vcpu mdcr_el2 value
|
||||
*
|
||||
@@ -95,11 +30,14 @@ void kvm_arm_init_debug(void)
|
||||
*/
|
||||
static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
preempt_disable();
|
||||
|
||||
/*
|
||||
* This also clears MDCR_EL2_E2PB_MASK and MDCR_EL2_E2TB_MASK
|
||||
* to disable guest access to the profiling and trace buffers
|
||||
*/
|
||||
vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
|
||||
vcpu->arch.mdcr_el2 = FIELD_PREP(MDCR_EL2_HPMN,
|
||||
*host_data_ptr(nr_event_counters));
|
||||
vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
|
||||
MDCR_EL2_TPMS |
|
||||
MDCR_EL2_TTRF |
|
||||
@@ -113,233 +51,171 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu)
|
||||
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
|
||||
|
||||
/*
|
||||
* Trap debug register access when one of the following is true:
|
||||
* - Userspace is using the hardware to debug the guest
|
||||
* (KVM_GUESTDBG_USE_HW is set).
|
||||
* - The guest is not using debug (DEBUG_DIRTY clear).
|
||||
* - The guest has enabled the OS Lock (debug exceptions are blocked).
|
||||
* Trap debug registers if the guest doesn't have ownership of them.
|
||||
*/
|
||||
if ((vcpu->guest_debug & KVM_GUESTDBG_USE_HW) ||
|
||||
!vcpu_get_flag(vcpu, DEBUG_DIRTY) ||
|
||||
kvm_vcpu_os_lock_enabled(vcpu))
|
||||
if (!kvm_guest_owns_debug_regs(vcpu))
|
||||
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
|
||||
|
||||
trace_kvm_arm_set_dreg32("MDCR_EL2", vcpu->arch.mdcr_el2);
|
||||
}
|
||||
/* Write MDCR_EL2 directly if we're already at EL2 */
|
||||
if (has_vhe())
|
||||
write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
|
||||
|
||||
/**
|
||||
* kvm_arm_vcpu_init_debug - setup vcpu debug traps
|
||||
*
|
||||
* @vcpu: the vcpu pointer
|
||||
*
|
||||
* Set vcpu initial mdcr_el2 value.
|
||||
*/
|
||||
void kvm_arm_vcpu_init_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
preempt_disable();
|
||||
kvm_arm_setup_mdcr_el2(vcpu);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_arm_reset_debug_ptr - reset the debug ptr to point to the vcpu state
|
||||
* @vcpu: the vcpu pointer
|
||||
*/
|
||||
|
||||
void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu)
|
||||
void kvm_init_host_debug_data(void)
|
||||
{
|
||||
vcpu->arch.debug_ptr = &vcpu->arch.vcpu_debug_state;
|
||||
}
|
||||
u64 dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
|
||||
/**
|
||||
* kvm_arm_setup_debug - set up debug related stuff
|
||||
*
|
||||
* @vcpu: the vcpu pointer
|
||||
*
|
||||
* This is called before each entry into the hypervisor to setup any
|
||||
* debug related registers.
|
||||
*
|
||||
* Additionally, KVM only traps guest accesses to the debug registers if
|
||||
* the guest is not actively using them (see the DEBUG_DIRTY
|
||||
* flag on vcpu->arch.iflags). Since the guest must not interfere
|
||||
* with the hardware state when debugging the guest, we must ensure that
|
||||
* trapping is enabled whenever we are debugging the guest using the
|
||||
* debug registers.
|
||||
*/
|
||||
if (cpuid_feature_extract_signed_field(dfr0, ID_AA64DFR0_EL1_PMUVer_SHIFT) > 0)
|
||||
*host_data_ptr(nr_event_counters) = FIELD_GET(ARMV8_PMU_PMCR_N,
|
||||
read_sysreg(pmcr_el0));
|
||||
|
||||
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long mdscr, orig_mdcr_el2 = vcpu->arch.mdcr_el2;
|
||||
*host_data_ptr(debug_brps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, BRPs, dfr0);
|
||||
*host_data_ptr(debug_wrps) = SYS_FIELD_GET(ID_AA64DFR0_EL1, WRPs, dfr0);
|
||||
|
||||
trace_kvm_arm_setup_debug(vcpu, vcpu->guest_debug);
|
||||
|
||||
kvm_arm_setup_mdcr_el2(vcpu);
|
||||
|
||||
/* Check if we need to use the debug registers. */
|
||||
if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
/* Save guest debug state */
|
||||
save_guest_debug_regs(vcpu);
|
||||
|
||||
/*
|
||||
* Single Step (ARM ARM D2.12.3 The software step state
|
||||
* machine)
|
||||
*
|
||||
* If we are doing Single Step we need to manipulate
|
||||
* the guest's MDSCR_EL1.SS and PSTATE.SS. Once the
|
||||
* step has occurred the hypervisor will trap the
|
||||
* debug exception and we return to userspace.
|
||||
*
|
||||
* If the guest attempts to single step its userspace
|
||||
* we would have to deal with a trapped exception
|
||||
* while in the guest kernel. Because this would be
|
||||
* hard to unwind we suppress the guest's ability to
|
||||
* do so by masking MDSCR_EL.SS.
|
||||
*
|
||||
* This confuses guest debuggers which use
|
||||
* single-step behind the scenes but everything
|
||||
* returns to normal once the host is no longer
|
||||
* debugging the system.
|
||||
*/
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
||||
/*
|
||||
* If the software step state at the last guest exit
|
||||
* was Active-pending, we don't set DBG_SPSR_SS so
|
||||
* that the state is maintained (to not run another
|
||||
* single-step until the pending Software Step
|
||||
* exception is taken).
|
||||
*/
|
||||
if (!vcpu_get_flag(vcpu, DBG_SS_ACTIVE_PENDING))
|
||||
*vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
||||
else
|
||||
*vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
||||
|
||||
mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
mdscr |= DBG_MDSCR_SS;
|
||||
vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
|
||||
} else {
|
||||
mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
mdscr &= ~DBG_MDSCR_SS;
|
||||
vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
|
||||
}
|
||||
|
||||
trace_kvm_arm_set_dreg32("SPSR_EL2", *vcpu_cpsr(vcpu));
|
||||
|
||||
/*
|
||||
* HW Breakpoints and watchpoints
|
||||
*
|
||||
* We simply switch the debug_ptr to point to our new
|
||||
* external_debug_state which has been populated by the
|
||||
* debug ioctl. The existing DEBUG_DIRTY mechanism ensures
|
||||
* the registers are updated on the world switch.
|
||||
*/
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW) {
|
||||
/* Enable breakpoints/watchpoints */
|
||||
mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
mdscr |= DBG_MDSCR_MDE;
|
||||
vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
|
||||
|
||||
vcpu->arch.debug_ptr = &vcpu->arch.external_debug_state;
|
||||
vcpu_set_flag(vcpu, DEBUG_DIRTY);
|
||||
|
||||
trace_kvm_arm_set_regset("BKPTS", get_num_brps(),
|
||||
&vcpu->arch.debug_ptr->dbg_bcr[0],
|
||||
&vcpu->arch.debug_ptr->dbg_bvr[0]);
|
||||
|
||||
trace_kvm_arm_set_regset("WAPTS", get_num_wrps(),
|
||||
&vcpu->arch.debug_ptr->dbg_wcr[0],
|
||||
&vcpu->arch.debug_ptr->dbg_wvr[0]);
|
||||
|
||||
/*
|
||||
* The OS Lock blocks debug exceptions in all ELs when it is
|
||||
* enabled. If the guest has enabled the OS Lock, constrain its
|
||||
* effects to the guest. Emulate the behavior by clearing
|
||||
* MDSCR_EL1.MDE. In so doing, we ensure that host debug
|
||||
* exceptions are unaffected by guest configuration of the OS
|
||||
* Lock.
|
||||
*/
|
||||
} else if (kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
mdscr &= ~DBG_MDSCR_MDE;
|
||||
vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1);
|
||||
}
|
||||
}
|
||||
|
||||
BUG_ON(!vcpu->guest_debug &&
|
||||
vcpu->arch.debug_ptr != &vcpu->arch.vcpu_debug_state);
|
||||
|
||||
/* If KDE or MDE are set, perform a full save/restore cycle. */
|
||||
if (vcpu_read_sys_reg(vcpu, MDSCR_EL1) & (DBG_MDSCR_KDE | DBG_MDSCR_MDE))
|
||||
vcpu_set_flag(vcpu, DEBUG_DIRTY);
|
||||
|
||||
/* Write mdcr_el2 changes since vcpu_load on VHE systems */
|
||||
if (has_vhe() && orig_mdcr_el2 != vcpu->arch.mdcr_el2)
|
||||
write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
|
||||
|
||||
trace_kvm_arm_set_dreg32("MDSCR_EL1", vcpu_read_sys_reg(vcpu, MDSCR_EL1));
|
||||
}
|
||||
|
||||
void kvm_arm_clear_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
trace_kvm_arm_clear_debug(vcpu->guest_debug);
|
||||
|
||||
/*
|
||||
* Restore the guest's debug registers if we were using them.
|
||||
*/
|
||||
if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
||||
if (!(*vcpu_cpsr(vcpu) & DBG_SPSR_SS))
|
||||
/*
|
||||
* Mark the vcpu as ACTIVE_PENDING
|
||||
* until Software Step exception is taken.
|
||||
*/
|
||||
vcpu_set_flag(vcpu, DBG_SS_ACTIVE_PENDING);
|
||||
}
|
||||
|
||||
restore_guest_debug_regs(vcpu);
|
||||
|
||||
/*
|
||||
* If we were using HW debug we need to restore the
|
||||
* debug_ptr to the guest debug state.
|
||||
*/
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW) {
|
||||
kvm_arm_reset_debug_ptr(vcpu);
|
||||
|
||||
trace_kvm_arm_set_regset("BKPTS", get_num_brps(),
|
||||
&vcpu->arch.debug_ptr->dbg_bcr[0],
|
||||
&vcpu->arch.debug_ptr->dbg_bvr[0]);
|
||||
|
||||
trace_kvm_arm_set_regset("WAPTS", get_num_wrps(),
|
||||
&vcpu->arch.debug_ptr->dbg_wcr[0],
|
||||
&vcpu->arch.debug_ptr->dbg_wvr[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 dfr0;
|
||||
|
||||
/* For VHE, there is nothing to do */
|
||||
if (has_vhe())
|
||||
return;
|
||||
|
||||
dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
/*
|
||||
* If SPE is present on this CPU and is available at current EL,
|
||||
* we may need to check if the host state needs to be saved.
|
||||
*/
|
||||
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
|
||||
!(read_sysreg_s(SYS_PMBIDR_EL1) & BIT(PMBIDR_EL1_P_SHIFT)))
|
||||
vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_SPE);
|
||||
!(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P))
|
||||
host_data_set_flag(HAS_SPE);
|
||||
|
||||
/* Check if we have TRBE implemented and available at the host */
|
||||
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
|
||||
!(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
|
||||
vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
|
||||
host_data_set_flag(HAS_TRBE);
|
||||
}
|
||||
|
||||
void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
|
||||
/*
|
||||
* Configures the 'external' MDSCR_EL1 value for the guest, i.e. when the host
|
||||
* has taken over MDSCR_EL1.
|
||||
*
|
||||
* - Userspace is single-stepping the guest, and MDSCR_EL1.SS is forced to 1.
|
||||
*
|
||||
* - Userspace is using the breakpoint/watchpoint registers to debug the
|
||||
* guest, and MDSCR_EL1.MDE is forced to 1.
|
||||
*
|
||||
* - The guest has enabled the OS Lock, and KVM is forcing MDSCR_EL1.MDE to 0,
|
||||
* masking all debug exceptions affected by the OS Lock.
|
||||
*/
|
||||
static void setup_external_mdscr(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
|
||||
vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
|
||||
/*
|
||||
* Use the guest's MDSCR_EL1 as a starting point, since there are
|
||||
* several other features controlled by MDSCR_EL1 that are not relevant
|
||||
* to the host.
|
||||
*
|
||||
* Clear the bits that KVM may use which also satisfies emulation of
|
||||
* the OS Lock as MDSCR_EL1.MDE is cleared.
|
||||
*/
|
||||
u64 mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1) & ~(MDSCR_EL1_SS |
|
||||
MDSCR_EL1_MDE |
|
||||
MDSCR_EL1_KDE);
|
||||
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
|
||||
mdscr |= MDSCR_EL1_SS;
|
||||
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW)
|
||||
mdscr |= MDSCR_EL1_MDE | MDSCR_EL1_KDE;
|
||||
|
||||
vcpu->arch.external_mdscr_el1 = mdscr;
|
||||
}
|
||||
|
||||
void kvm_vcpu_load_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 mdscr;
|
||||
|
||||
/* Must be called before kvm_vcpu_load_vhe() */
|
||||
KVM_BUG_ON(vcpu_get_flag(vcpu, SYSREGS_ON_CPU), vcpu->kvm);
|
||||
|
||||
/*
|
||||
* Determine which of the possible debug states we're in:
|
||||
*
|
||||
* - VCPU_DEBUG_HOST_OWNED: KVM has taken ownership of the guest's
|
||||
* breakpoint/watchpoint registers, or needs to use MDSCR_EL1 to do
|
||||
* software step or emulate the effects of the OS Lock being enabled.
|
||||
*
|
||||
* - VCPU_DEBUG_GUEST_OWNED: The guest has debug exceptions enabled, and
|
||||
* the breakpoint/watchpoint registers need to be loaded eagerly.
|
||||
*
|
||||
* - VCPU_DEBUG_FREE: Neither of the above apply, no breakpoint/watchpoint
|
||||
* context needs to be loaded on the CPU.
|
||||
*/
|
||||
if (vcpu->guest_debug || kvm_vcpu_os_lock_enabled(vcpu)) {
|
||||
vcpu->arch.debug_owner = VCPU_DEBUG_HOST_OWNED;
|
||||
setup_external_mdscr(vcpu);
|
||||
|
||||
/*
|
||||
* Steal the guest's single-step state machine if userspace wants
|
||||
* single-step the guest.
|
||||
*/
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
||||
if (*vcpu_cpsr(vcpu) & DBG_SPSR_SS)
|
||||
vcpu_clear_flag(vcpu, GUEST_SS_ACTIVE_PENDING);
|
||||
else
|
||||
vcpu_set_flag(vcpu, GUEST_SS_ACTIVE_PENDING);
|
||||
|
||||
if (!vcpu_get_flag(vcpu, HOST_SS_ACTIVE_PENDING))
|
||||
*vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
||||
else
|
||||
*vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
||||
}
|
||||
} else {
|
||||
mdscr = vcpu_read_sys_reg(vcpu, MDSCR_EL1);
|
||||
|
||||
if (mdscr & (MDSCR_EL1_KDE | MDSCR_EL1_MDE))
|
||||
vcpu->arch.debug_owner = VCPU_DEBUG_GUEST_OWNED;
|
||||
else
|
||||
vcpu->arch.debug_owner = VCPU_DEBUG_FREE;
|
||||
}
|
||||
|
||||
kvm_arm_setup_mdcr_el2(vcpu);
|
||||
}
|
||||
|
||||
void kvm_vcpu_put_debug(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (likely(!(vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Save the host's software step state and restore the guest's before
|
||||
* potentially returning to userspace.
|
||||
*/
|
||||
if (!(*vcpu_cpsr(vcpu) & DBG_SPSR_SS))
|
||||
vcpu_set_flag(vcpu, HOST_SS_ACTIVE_PENDING);
|
||||
else
|
||||
vcpu_clear_flag(vcpu, HOST_SS_ACTIVE_PENDING);
|
||||
|
||||
if (vcpu_get_flag(vcpu, GUEST_SS_ACTIVE_PENDING))
|
||||
*vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
|
||||
else
|
||||
*vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates ownership of the debug registers after a trapped guest access to a
|
||||
* breakpoint/watchpoint register. Host ownership of the debug registers is of
|
||||
* strictly higher priority, and it is the responsibility of the VMM to emulate
|
||||
* guest debug exceptions in this configuration.
|
||||
*/
|
||||
void kvm_debug_set_guest_ownership(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (kvm_host_owns_debug_regs(vcpu))
|
||||
return;
|
||||
|
||||
vcpu->arch.debug_owner = VCPU_DEBUG_GUEST_OWNED;
|
||||
kvm_arm_setup_mdcr_el2(vcpu);
|
||||
}
|
||||
|
||||
void kvm_debug_handle_oslar(struct kvm_vcpu *vcpu, u64 val)
|
||||
{
|
||||
if (val & OSLAR_EL1_OSLK)
|
||||
__vcpu_sys_reg(vcpu, OSLSR_EL1) |= OSLSR_EL1_OSLK;
|
||||
else
|
||||
__vcpu_sys_reg(vcpu, OSLSR_EL1) &= ~OSLSR_EL1_OSLK;
|
||||
|
||||
preempt_disable();
|
||||
kvm_arch_vcpu_put(vcpu);
|
||||
kvm_arch_vcpu_load(vcpu, smp_processor_id());
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
@@ -2345,14 +2345,14 @@ bool triage_sysreg_trap(struct kvm_vcpu *vcpu, int *sr_index)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool forward_traps(struct kvm_vcpu *vcpu, u64 control_bit)
|
||||
static bool __forward_traps(struct kvm_vcpu *vcpu, unsigned int reg, u64 control_bit)
|
||||
{
|
||||
bool control_bit_set;
|
||||
|
||||
if (!vcpu_has_nv(vcpu))
|
||||
return false;
|
||||
|
||||
control_bit_set = __vcpu_sys_reg(vcpu, HCR_EL2) & control_bit;
|
||||
control_bit_set = __vcpu_sys_reg(vcpu, reg) & control_bit;
|
||||
if (!is_hyp_ctxt(vcpu) && control_bit_set) {
|
||||
kvm_inject_nested_sync(vcpu, kvm_vcpu_get_esr(vcpu));
|
||||
return true;
|
||||
@@ -2360,9 +2360,24 @@ static bool forward_traps(struct kvm_vcpu *vcpu, u64 control_bit)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool forward_hcr_traps(struct kvm_vcpu *vcpu, u64 control_bit)
|
||||
{
|
||||
return __forward_traps(vcpu, HCR_EL2, control_bit);
|
||||
}
|
||||
|
||||
bool forward_smc_trap(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return forward_traps(vcpu, HCR_TSC);
|
||||
return forward_hcr_traps(vcpu, HCR_TSC);
|
||||
}
|
||||
|
||||
static bool forward_mdcr_traps(struct kvm_vcpu *vcpu, u64 control_bit)
|
||||
{
|
||||
return __forward_traps(vcpu, MDCR_EL2, control_bit);
|
||||
}
|
||||
|
||||
bool forward_debug_exception(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return forward_mdcr_traps(vcpu, MDCR_EL2_TDE);
|
||||
}
|
||||
|
||||
static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr)
|
||||
@@ -2406,7 +2421,7 @@ void kvm_emulate_nested_eret(struct kvm_vcpu *vcpu)
|
||||
* Forward this trap to the virtual EL2 if the virtual
|
||||
* HCR_EL2.NV bit is set and this is coming from !EL2.
|
||||
*/
|
||||
if (forward_traps(vcpu, HCR_NV))
|
||||
if (forward_hcr_traps(vcpu, HCR_NV))
|
||||
return;
|
||||
|
||||
spsr = vcpu_read_sys_reg(vcpu, SPSR_EL2);
|
||||
|
||||
@@ -65,14 +65,14 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu)
|
||||
*host_data_ptr(fpsimd_state) = kern_hyp_va(¤t->thread.uw.fpsimd_state);
|
||||
*host_data_ptr(fpmr_ptr) = kern_hyp_va(¤t->thread.uw.fpmr);
|
||||
|
||||
vcpu_clear_flag(vcpu, HOST_SVE_ENABLED);
|
||||
host_data_clear_flag(HOST_SVE_ENABLED);
|
||||
if (read_sysreg(cpacr_el1) & CPACR_EL1_ZEN_EL0EN)
|
||||
vcpu_set_flag(vcpu, HOST_SVE_ENABLED);
|
||||
host_data_set_flag(HOST_SVE_ENABLED);
|
||||
|
||||
if (system_supports_sme()) {
|
||||
vcpu_clear_flag(vcpu, HOST_SME_ENABLED);
|
||||
host_data_clear_flag(HOST_SME_ENABLED);
|
||||
if (read_sysreg(cpacr_el1) & CPACR_EL1_SMEN_EL0EN)
|
||||
vcpu_set_flag(vcpu, HOST_SME_ENABLED);
|
||||
host_data_set_flag(HOST_SME_ENABLED);
|
||||
|
||||
/*
|
||||
* If PSTATE.SM is enabled then save any pending FP
|
||||
@@ -168,7 +168,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu)
|
||||
*/
|
||||
if (has_vhe() && system_supports_sme()) {
|
||||
/* Also restore EL0 state seen on entry */
|
||||
if (vcpu_get_flag(vcpu, HOST_SME_ENABLED))
|
||||
if (host_data_test_flag(HOST_SME_ENABLED))
|
||||
sysreg_clear_set(CPACR_EL1, 0, CPACR_ELx_SMEN);
|
||||
else
|
||||
sysreg_clear_set(CPACR_EL1,
|
||||
@@ -227,7 +227,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu)
|
||||
* for EL0. To avoid spurious traps, restore the trap state
|
||||
* seen by kvm_arch_vcpu_load_fp():
|
||||
*/
|
||||
if (vcpu_get_flag(vcpu, HOST_SVE_ENABLED))
|
||||
if (host_data_test_flag(HOST_SVE_ENABLED))
|
||||
sysreg_clear_set(CPACR_EL1, 0, CPACR_EL1_ZEN_EL0EN);
|
||||
else
|
||||
sysreg_clear_set(CPACR_EL1, CPACR_EL1_ZEN_EL0EN, 0);
|
||||
|
||||
@@ -917,31 +917,24 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
|
||||
int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
||||
struct kvm_guest_debug *dbg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
trace_kvm_set_guest_debug(vcpu, dbg->control);
|
||||
|
||||
if (dbg->control & ~KVM_GUESTDBG_VALID_MASK) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (dbg->control & ~KVM_GUESTDBG_VALID_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
if (dbg->control & KVM_GUESTDBG_ENABLE) {
|
||||
vcpu->guest_debug = dbg->control;
|
||||
|
||||
/* Hardware assisted Break and Watch points */
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW) {
|
||||
vcpu->arch.external_debug_state = dbg->arch;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* If not enabled clear all flags */
|
||||
if (!(dbg->control & KVM_GUESTDBG_ENABLE)) {
|
||||
vcpu->guest_debug = 0;
|
||||
vcpu_clear_flag(vcpu, DBG_SS_ACTIVE_PENDING);
|
||||
vcpu_clear_flag(vcpu, HOST_SS_ACTIVE_PENDING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
vcpu->guest_debug = dbg->control;
|
||||
|
||||
/* Hardware assisted Break and Watch points */
|
||||
if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW)
|
||||
vcpu->arch.external_debug_state = dbg->arch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arm_vcpu_arch_set_attr(struct kvm_vcpu *vcpu,
|
||||
|
||||
@@ -183,6 +183,9 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
|
||||
struct kvm_run *run = vcpu->run;
|
||||
u64 esr = kvm_vcpu_get_esr(vcpu);
|
||||
|
||||
if (!vcpu->guest_debug && forward_debug_exception(vcpu))
|
||||
return 1;
|
||||
|
||||
run->exit_reason = KVM_EXIT_DEBUG;
|
||||
run->debug.arch.hsr = lower_32_bits(esr);
|
||||
run->debug.arch.hsr_high = upper_32_bits(esr);
|
||||
@@ -193,7 +196,7 @@ static int kvm_handle_guest_debug(struct kvm_vcpu *vcpu)
|
||||
run->debug.arch.far = vcpu->arch.fault.far_el2;
|
||||
break;
|
||||
case ESR_ELx_EC_SOFTSTP_LOW:
|
||||
vcpu_clear_flag(vcpu, DBG_SS_ACTIVE_PENDING);
|
||||
*vcpu_cpsr(vcpu) |= DBG_SPSR_SS;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,15 +88,26 @@
|
||||
default: write_debug(ptr[0], reg, 0); \
|
||||
}
|
||||
|
||||
static struct kvm_guest_debug_arch *__vcpu_debug_regs(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
switch (vcpu->arch.debug_owner) {
|
||||
case VCPU_DEBUG_FREE:
|
||||
WARN_ON_ONCE(1);
|
||||
fallthrough;
|
||||
case VCPU_DEBUG_GUEST_OWNED:
|
||||
return &vcpu->arch.vcpu_debug_state;
|
||||
case VCPU_DEBUG_HOST_OWNED:
|
||||
return &vcpu->arch.external_debug_state;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void __debug_save_state(struct kvm_guest_debug_arch *dbg,
|
||||
struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
u64 aa64dfr0;
|
||||
int brps, wrps;
|
||||
|
||||
aa64dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
brps = (aa64dfr0 >> 12) & 0xf;
|
||||
wrps = (aa64dfr0 >> 20) & 0xf;
|
||||
int brps = *host_data_ptr(debug_brps);
|
||||
int wrps = *host_data_ptr(debug_wrps);
|
||||
|
||||
save_debug(dbg->dbg_bcr, dbgbcr, brps);
|
||||
save_debug(dbg->dbg_bvr, dbgbvr, brps);
|
||||
@@ -109,13 +120,8 @@ static void __debug_save_state(struct kvm_guest_debug_arch *dbg,
|
||||
static void __debug_restore_state(struct kvm_guest_debug_arch *dbg,
|
||||
struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
u64 aa64dfr0;
|
||||
int brps, wrps;
|
||||
|
||||
aa64dfr0 = read_sysreg(id_aa64dfr0_el1);
|
||||
|
||||
brps = (aa64dfr0 >> 12) & 0xf;
|
||||
wrps = (aa64dfr0 >> 20) & 0xf;
|
||||
int brps = *host_data_ptr(debug_brps);
|
||||
int wrps = *host_data_ptr(debug_wrps);
|
||||
|
||||
restore_debug(dbg->dbg_bcr, dbgbcr, brps);
|
||||
restore_debug(dbg->dbg_bvr, dbgbvr, brps);
|
||||
@@ -132,13 +138,13 @@ static inline void __debug_switch_to_guest_common(struct kvm_vcpu *vcpu)
|
||||
struct kvm_guest_debug_arch *host_dbg;
|
||||
struct kvm_guest_debug_arch *guest_dbg;
|
||||
|
||||
if (!vcpu_get_flag(vcpu, DEBUG_DIRTY))
|
||||
if (!kvm_debug_regs_in_use(vcpu))
|
||||
return;
|
||||
|
||||
host_ctxt = host_data_ptr(host_ctxt);
|
||||
guest_ctxt = &vcpu->arch.ctxt;
|
||||
host_dbg = host_data_ptr(host_debug_state.regs);
|
||||
guest_dbg = kern_hyp_va(vcpu->arch.debug_ptr);
|
||||
guest_dbg = __vcpu_debug_regs(vcpu);
|
||||
|
||||
__debug_save_state(host_dbg, host_ctxt);
|
||||
__debug_restore_state(guest_dbg, guest_ctxt);
|
||||
@@ -151,18 +157,16 @@ static inline void __debug_switch_to_host_common(struct kvm_vcpu *vcpu)
|
||||
struct kvm_guest_debug_arch *host_dbg;
|
||||
struct kvm_guest_debug_arch *guest_dbg;
|
||||
|
||||
if (!vcpu_get_flag(vcpu, DEBUG_DIRTY))
|
||||
if (!kvm_debug_regs_in_use(vcpu))
|
||||
return;
|
||||
|
||||
host_ctxt = host_data_ptr(host_ctxt);
|
||||
guest_ctxt = &vcpu->arch.ctxt;
|
||||
host_dbg = host_data_ptr(host_debug_state.regs);
|
||||
guest_dbg = kern_hyp_va(vcpu->arch.debug_ptr);
|
||||
guest_dbg = __vcpu_debug_regs(vcpu);
|
||||
|
||||
__debug_save_state(guest_dbg, guest_ctxt);
|
||||
__debug_restore_state(host_dbg, host_ctxt);
|
||||
|
||||
vcpu_clear_flag(vcpu, DEBUG_DIRTY);
|
||||
}
|
||||
|
||||
#endif /* __ARM64_KVM_HYP_DEBUG_SR_H__ */
|
||||
|
||||
@@ -18,9 +18,34 @@
|
||||
|
||||
static inline bool ctxt_has_s1poe(struct kvm_cpu_context *ctxt);
|
||||
|
||||
static inline struct kvm_vcpu *ctxt_to_vcpu(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->__hyp_running_vcpu;
|
||||
|
||||
if (!vcpu)
|
||||
vcpu = container_of(ctxt, struct kvm_vcpu, arch.ctxt);
|
||||
|
||||
return vcpu;
|
||||
}
|
||||
|
||||
static inline bool ctxt_is_guest(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
return host_data_ptr(host_ctxt) != ctxt;
|
||||
}
|
||||
|
||||
static inline u64 *ctxt_mdscr_el1(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt_to_vcpu(ctxt);
|
||||
|
||||
if (ctxt_is_guest(ctxt) && kvm_host_owns_debug_regs(vcpu))
|
||||
return &vcpu->arch.external_mdscr_el1;
|
||||
|
||||
return &ctxt_sys_reg(ctxt, MDSCR_EL1);
|
||||
}
|
||||
|
||||
static inline void __sysreg_save_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
ctxt_sys_reg(ctxt, MDSCR_EL1) = read_sysreg(mdscr_el1);
|
||||
*ctxt_mdscr_el1(ctxt) = read_sysreg(mdscr_el1);
|
||||
|
||||
// POR_EL0 can affect uaccess, so must be saved/restored early.
|
||||
if (ctxt_has_s1poe(ctxt))
|
||||
@@ -33,16 +58,6 @@ static inline void __sysreg_save_user_state(struct kvm_cpu_context *ctxt)
|
||||
ctxt_sys_reg(ctxt, TPIDRRO_EL0) = read_sysreg(tpidrro_el0);
|
||||
}
|
||||
|
||||
static inline struct kvm_vcpu *ctxt_to_vcpu(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt->__hyp_running_vcpu;
|
||||
|
||||
if (!vcpu)
|
||||
vcpu = container_of(ctxt, struct kvm_vcpu, arch.ctxt);
|
||||
|
||||
return vcpu;
|
||||
}
|
||||
|
||||
static inline bool ctxt_has_mte(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
struct kvm_vcpu *vcpu = ctxt_to_vcpu(ctxt);
|
||||
@@ -139,7 +154,7 @@ static inline void __sysreg_save_el2_return_state(struct kvm_cpu_context *ctxt)
|
||||
|
||||
static inline void __sysreg_restore_common_state(struct kvm_cpu_context *ctxt)
|
||||
{
|
||||
write_sysreg(ctxt_sys_reg(ctxt, MDSCR_EL1), mdscr_el1);
|
||||
write_sysreg(*ctxt_mdscr_el1(ctxt), mdscr_el1);
|
||||
|
||||
// POR_EL0 can affect uaccess, so must be saved/restored early.
|
||||
if (ctxt_has_s1poe(ctxt))
|
||||
@@ -283,7 +298,7 @@ static inline void __sysreg32_save_state(struct kvm_vcpu *vcpu)
|
||||
__vcpu_sys_reg(vcpu, DACR32_EL2) = read_sysreg(dacr32_el2);
|
||||
__vcpu_sys_reg(vcpu, IFSR32_EL2) = read_sysreg(ifsr32_el2);
|
||||
|
||||
if (has_vhe() || vcpu_get_flag(vcpu, DEBUG_DIRTY))
|
||||
if (has_vhe() || kvm_debug_regs_in_use(vcpu))
|
||||
__vcpu_sys_reg(vcpu, DBGVCR32_EL2) = read_sysreg(dbgvcr32_el2);
|
||||
}
|
||||
|
||||
@@ -300,7 +315,7 @@ static inline void __sysreg32_restore_state(struct kvm_vcpu *vcpu)
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, DACR32_EL2), dacr32_el2);
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, IFSR32_EL2), ifsr32_el2);
|
||||
|
||||
if (has_vhe() || vcpu_get_flag(vcpu, DEBUG_DIRTY))
|
||||
if (has_vhe() || kvm_debug_regs_in_use(vcpu))
|
||||
write_sysreg(__vcpu_sys_reg(vcpu, DBGVCR32_EL2), dbgvcr32_el2);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,10 +82,10 @@ static void __debug_restore_trace(u64 trfcr_el1)
|
||||
void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* Disable and flush SPE data generation */
|
||||
if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE))
|
||||
if (host_data_test_flag(HAS_SPE))
|
||||
__debug_save_spe(host_data_ptr(host_debug_state.pmscr_el1));
|
||||
/* Disable and flush Self-Hosted Trace generation */
|
||||
if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
|
||||
if (host_data_test_flag(HAS_TRBE))
|
||||
__debug_save_trace(host_data_ptr(host_debug_state.trfcr_el1));
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ void __debug_switch_to_guest(struct kvm_vcpu *vcpu)
|
||||
|
||||
void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE))
|
||||
if (host_data_test_flag(HAS_SPE))
|
||||
__debug_restore_spe(*host_data_ptr(host_debug_state.pmscr_el1));
|
||||
if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE))
|
||||
if (host_data_test_flag(HAS_TRBE))
|
||||
__debug_restore_trace(*host_data_ptr(host_debug_state.trfcr_el1));
|
||||
}
|
||||
|
||||
@@ -106,8 +106,3 @@ void __debug_switch_to_host(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__debug_switch_to_host_common(vcpu);
|
||||
}
|
||||
|
||||
u64 __kvm_get_mdcr_el2(void)
|
||||
{
|
||||
return read_sysreg(mdcr_el2);
|
||||
}
|
||||
|
||||
@@ -112,8 +112,6 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
|
||||
|
||||
hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags;
|
||||
|
||||
hyp_vcpu->vcpu.arch.debug_ptr = kern_hyp_va(host_vcpu->arch.debug_ptr);
|
||||
|
||||
hyp_vcpu->vcpu.arch.vsesr_el2 = host_vcpu->arch.vsesr_el2;
|
||||
|
||||
hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3;
|
||||
@@ -264,11 +262,6 @@ static void handle___vgic_v3_init_lrs(struct kvm_cpu_context *host_ctxt)
|
||||
__vgic_v3_init_lrs();
|
||||
}
|
||||
|
||||
static void handle___kvm_get_mdcr_el2(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
cpu_reg(host_ctxt, 1) = __kvm_get_mdcr_el2();
|
||||
}
|
||||
|
||||
static void handle___vgic_v3_save_vmcr_aprs(struct kvm_cpu_context *host_ctxt)
|
||||
{
|
||||
DECLARE_REG(struct vgic_v3_cpu_if *, cpu_if, host_ctxt, 1);
|
||||
@@ -384,7 +377,6 @@ typedef void (*hcall_t)(struct kvm_cpu_context *);
|
||||
|
||||
static const hcall_t host_hcall[] = {
|
||||
/* ___kvm_hyp_init */
|
||||
HANDLE_FUNC(__kvm_get_mdcr_el2),
|
||||
HANDLE_FUNC(__pkvm_init),
|
||||
HANDLE_FUNC(__pkvm_create_private_mapping),
|
||||
HANDLE_FUNC(__pkvm_cpu_set_vector),
|
||||
|
||||
@@ -19,8 +19,3 @@ void __debug_switch_to_host(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
__debug_switch_to_host_common(vcpu);
|
||||
}
|
||||
|
||||
u64 __kvm_get_mdcr_el2(void)
|
||||
{
|
||||
return read_sysreg(mdcr_el2);
|
||||
}
|
||||
|
||||
@@ -570,17 +570,10 @@ static bool trap_oslar_el1(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
u64 oslsr;
|
||||
|
||||
if (!p->is_write)
|
||||
return read_from_write_only(vcpu, p, r);
|
||||
|
||||
/* Forward the OSLK bit to OSLSR */
|
||||
oslsr = __vcpu_sys_reg(vcpu, OSLSR_EL1) & ~OSLSR_EL1_OSLK;
|
||||
if (p->regval & OSLAR_EL1_OSLK)
|
||||
oslsr |= OSLSR_EL1_OSLK;
|
||||
|
||||
__vcpu_sys_reg(vcpu, OSLSR_EL1) = oslsr;
|
||||
kvm_debug_handle_oslar(vcpu, p->regval);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -621,43 +614,13 @@ static bool trap_dbgauthstatus_el1(struct kvm_vcpu *vcpu,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to avoid world-switching all the DBG registers all the
|
||||
* time:
|
||||
*
|
||||
* - If we've touched any debug register, it is likely that we're
|
||||
* going to touch more of them. It then makes sense to disable the
|
||||
* traps and start doing the save/restore dance
|
||||
* - If debug is active (DBG_MDSCR_KDE or DBG_MDSCR_MDE set), it is
|
||||
* then mandatory to save/restore the registers, as the guest
|
||||
* depends on them.
|
||||
*
|
||||
* For this, we use a DIRTY bit, indicating the guest has modified the
|
||||
* debug registers, used as follow:
|
||||
*
|
||||
* On guest entry:
|
||||
* - If the dirty bit is set (because we're coming back from trapping),
|
||||
* disable the traps, save host registers, restore guest registers.
|
||||
* - If debug is actively in use (DBG_MDSCR_KDE or DBG_MDSCR_MDE set),
|
||||
* set the dirty bit, disable the traps, save host registers,
|
||||
* restore guest registers.
|
||||
* - Otherwise, enable the traps
|
||||
*
|
||||
* On guest exit:
|
||||
* - If the dirty bit is set, save guest registers, restore host
|
||||
* registers and clear the dirty bit. This ensure that the host can
|
||||
* now use the debug registers.
|
||||
*/
|
||||
static bool trap_debug_regs(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *r)
|
||||
{
|
||||
access_rw(vcpu, p, r);
|
||||
if (p->is_write)
|
||||
vcpu_set_flag(vcpu, DEBUG_DIRTY);
|
||||
|
||||
trace_trap_reg(__func__, r->reg, p->is_write, p->regval);
|
||||
|
||||
kvm_debug_set_guest_ownership(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -666,9 +629,6 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu,
|
||||
*
|
||||
* A 32 bit write to a debug register leave top bits alone
|
||||
* A 32 bit read from a debug register only returns the bottom bits
|
||||
*
|
||||
* All writes will set the DEBUG_DIRTY flag to ensure the hyp code
|
||||
* switches between host and guest values in future.
|
||||
*/
|
||||
static void reg_to_dbg(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
@@ -683,8 +643,6 @@ static void reg_to_dbg(struct kvm_vcpu *vcpu,
|
||||
val &= ~mask;
|
||||
val |= (p->regval & (mask >> shift)) << shift;
|
||||
*dbg_reg = val;
|
||||
|
||||
vcpu_set_flag(vcpu, DEBUG_DIRTY);
|
||||
}
|
||||
|
||||
static void dbg_to_reg(struct kvm_vcpu *vcpu,
|
||||
@@ -698,152 +656,79 @@ static void dbg_to_reg(struct kvm_vcpu *vcpu,
|
||||
p->regval = (*dbg_reg & mask) >> shift;
|
||||
}
|
||||
|
||||
static bool trap_bvr(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *rd)
|
||||
static u64 *demux_wb_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
|
||||
{
|
||||
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm];
|
||||
struct kvm_guest_debug_arch *dbg = &vcpu->arch.vcpu_debug_state;
|
||||
|
||||
switch (rd->Op2) {
|
||||
case 0b100:
|
||||
return &dbg->dbg_bvr[rd->CRm];
|
||||
case 0b101:
|
||||
return &dbg->dbg_bcr[rd->CRm];
|
||||
case 0b110:
|
||||
return &dbg->dbg_wvr[rd->CRm];
|
||||
case 0b111:
|
||||
return &dbg->dbg_wcr[rd->CRm];
|
||||
default:
|
||||
KVM_BUG_ON(1, vcpu->kvm);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool trap_dbg_wb_reg(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
u64 *reg = demux_wb_reg(vcpu, rd);
|
||||
|
||||
if (!reg)
|
||||
return false;
|
||||
|
||||
if (p->is_write)
|
||||
reg_to_dbg(vcpu, p, rd, dbg_reg);
|
||||
reg_to_dbg(vcpu, p, rd, reg);
|
||||
else
|
||||
dbg_to_reg(vcpu, p, rd, dbg_reg);
|
||||
|
||||
trace_trap_reg(__func__, rd->CRm, p->is_write, *dbg_reg);
|
||||
dbg_to_reg(vcpu, p, rd, reg);
|
||||
|
||||
kvm_debug_set_guest_ownership(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int set_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
static int set_dbg_wb_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm] = val;
|
||||
u64 *reg = demux_wb_reg(vcpu, rd);
|
||||
|
||||
if (!reg)
|
||||
return -EINVAL;
|
||||
|
||||
*reg = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 *val)
|
||||
static int get_dbg_wb_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 *val)
|
||||
{
|
||||
*val = vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm];
|
||||
u64 *reg = demux_wb_reg(vcpu, rd);
|
||||
|
||||
if (!reg)
|
||||
return -EINVAL;
|
||||
|
||||
*val = *reg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 reset_bvr(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
static u64 reset_dbg_wb_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm] = rd->val;
|
||||
return rd->val;
|
||||
}
|
||||
u64 *reg = demux_wb_reg(vcpu, rd);
|
||||
|
||||
static bool trap_bcr(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm];
|
||||
/*
|
||||
* Bail early if we couldn't find storage for the register, the
|
||||
* KVM_BUG_ON() in demux_wb_reg() will prevent this VM from ever
|
||||
* being run.
|
||||
*/
|
||||
if (!reg)
|
||||
return 0;
|
||||
|
||||
if (p->is_write)
|
||||
reg_to_dbg(vcpu, p, rd, dbg_reg);
|
||||
else
|
||||
dbg_to_reg(vcpu, p, rd, dbg_reg);
|
||||
|
||||
trace_trap_reg(__func__, rd->CRm, p->is_write, *dbg_reg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int set_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 *val)
|
||||
{
|
||||
*val = vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 reset_bcr(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm] = rd->val;
|
||||
return rd->val;
|
||||
}
|
||||
|
||||
static bool trap_wvr(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm];
|
||||
|
||||
if (p->is_write)
|
||||
reg_to_dbg(vcpu, p, rd, dbg_reg);
|
||||
else
|
||||
dbg_to_reg(vcpu, p, rd, dbg_reg);
|
||||
|
||||
trace_trap_reg(__func__, rd->CRm, p->is_write,
|
||||
vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int set_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 *val)
|
||||
{
|
||||
*val = vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 reset_wvr(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm] = rd->val;
|
||||
return rd->val;
|
||||
}
|
||||
|
||||
static bool trap_wcr(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *p,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
u64 *dbg_reg = &vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm];
|
||||
|
||||
if (p->is_write)
|
||||
reg_to_dbg(vcpu, p, rd, dbg_reg);
|
||||
else
|
||||
dbg_to_reg(vcpu, p, rd, dbg_reg);
|
||||
|
||||
trace_trap_reg(__func__, rd->CRm, p->is_write, *dbg_reg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int set_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 val)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm] = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd,
|
||||
u64 *val)
|
||||
{
|
||||
*val = vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 reset_wcr(struct kvm_vcpu *vcpu,
|
||||
const struct sys_reg_desc *rd)
|
||||
{
|
||||
vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm] = rd->val;
|
||||
*reg = rd->val;
|
||||
return rd->val;
|
||||
}
|
||||
|
||||
@@ -1352,13 +1237,17 @@ static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
|
||||
/* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
|
||||
#define DBG_BCR_BVR_WCR_WVR_EL1(n) \
|
||||
{ SYS_DESC(SYS_DBGBVRn_EL1(n)), \
|
||||
trap_bvr, reset_bvr, 0, 0, get_bvr, set_bvr }, \
|
||||
trap_dbg_wb_reg, reset_dbg_wb_reg, 0, 0, \
|
||||
get_dbg_wb_reg, set_dbg_wb_reg }, \
|
||||
{ SYS_DESC(SYS_DBGBCRn_EL1(n)), \
|
||||
trap_bcr, reset_bcr, 0, 0, get_bcr, set_bcr }, \
|
||||
trap_dbg_wb_reg, reset_dbg_wb_reg, 0, 0, \
|
||||
get_dbg_wb_reg, set_dbg_wb_reg }, \
|
||||
{ SYS_DESC(SYS_DBGWVRn_EL1(n)), \
|
||||
trap_wvr, reset_wvr, 0, 0, get_wvr, set_wvr }, \
|
||||
trap_dbg_wb_reg, reset_dbg_wb_reg, 0, 0, \
|
||||
get_dbg_wb_reg, set_dbg_wb_reg }, \
|
||||
{ SYS_DESC(SYS_DBGWCRn_EL1(n)), \
|
||||
trap_wcr, reset_wcr, 0, 0, get_wcr, set_wcr }
|
||||
trap_dbg_wb_reg, reset_dbg_wb_reg, 0, 0, \
|
||||
get_dbg_wb_reg, set_dbg_wb_reg }
|
||||
|
||||
#define PMU_SYS_REG(name) \
|
||||
SYS_DESC(SYS_##name), .reset = reset_pmu_reg, \
|
||||
@@ -3572,18 +3461,20 @@ static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
|
||||
* None of the other registers share their location, so treat them as
|
||||
* if they were 64bit.
|
||||
*/
|
||||
#define DBG_BCR_BVR_WCR_WVR(n) \
|
||||
/* DBGBVRn */ \
|
||||
{ AA32(LO), Op1( 0), CRn( 0), CRm((n)), Op2( 4), trap_bvr, NULL, n }, \
|
||||
/* DBGBCRn */ \
|
||||
{ Op1( 0), CRn( 0), CRm((n)), Op2( 5), trap_bcr, NULL, n }, \
|
||||
/* DBGWVRn */ \
|
||||
{ Op1( 0), CRn( 0), CRm((n)), Op2( 6), trap_wvr, NULL, n }, \
|
||||
/* DBGWCRn */ \
|
||||
{ Op1( 0), CRn( 0), CRm((n)), Op2( 7), trap_wcr, NULL, n }
|
||||
#define DBG_BCR_BVR_WCR_WVR(n) \
|
||||
/* DBGBVRn */ \
|
||||
{ AA32(LO), Op1( 0), CRn( 0), CRm((n)), Op2( 4), \
|
||||
trap_dbg_wb_reg, NULL, n }, \
|
||||
/* DBGBCRn */ \
|
||||
{ Op1( 0), CRn( 0), CRm((n)), Op2( 5), trap_dbg_wb_reg, NULL, n }, \
|
||||
/* DBGWVRn */ \
|
||||
{ Op1( 0), CRn( 0), CRm((n)), Op2( 6), trap_dbg_wb_reg, NULL, n }, \
|
||||
/* DBGWCRn */ \
|
||||
{ Op1( 0), CRn( 0), CRm((n)), Op2( 7), trap_dbg_wb_reg, NULL, n }
|
||||
|
||||
#define DBGBXVR(n) \
|
||||
{ AA32(HI), Op1( 0), CRn( 1), CRm((n)), Op2( 1), trap_bvr, NULL, n }
|
||||
#define DBGBXVR(n) \
|
||||
{ AA32(HI), Op1( 0), CRn( 1), CRm((n)), Op2( 1), \
|
||||
trap_dbg_wb_reg, NULL, n }
|
||||
|
||||
/*
|
||||
* Trapped cp14 registers. We generally ignore most of the external
|
||||
|
||||
@@ -46,38 +46,6 @@ TRACE_EVENT(kvm_hvc_arm64,
|
||||
__entry->vcpu_pc, __entry->r0, __entry->imm)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_arm_setup_debug,
|
||||
TP_PROTO(struct kvm_vcpu *vcpu, __u32 guest_debug),
|
||||
TP_ARGS(vcpu, guest_debug),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(struct kvm_vcpu *, vcpu)
|
||||
__field(__u32, guest_debug)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->vcpu = vcpu;
|
||||
__entry->guest_debug = guest_debug;
|
||||
),
|
||||
|
||||
TP_printk("vcpu: %p, flags: 0x%08x", __entry->vcpu, __entry->guest_debug)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_arm_clear_debug,
|
||||
TP_PROTO(__u32 guest_debug),
|
||||
TP_ARGS(guest_debug),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(__u32, guest_debug)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->guest_debug = guest_debug;
|
||||
),
|
||||
|
||||
TP_printk("flags: 0x%08x", __entry->guest_debug)
|
||||
);
|
||||
|
||||
/*
|
||||
* The dreg32 name is a leftover from a distant past. This will really
|
||||
* output a 64bit value...
|
||||
@@ -99,49 +67,6 @@ TRACE_EVENT(kvm_arm_set_dreg32,
|
||||
TP_printk("%s: 0x%llx", __entry->name, __entry->value)
|
||||
);
|
||||
|
||||
TRACE_DEFINE_SIZEOF(__u64);
|
||||
|
||||
TRACE_EVENT(kvm_arm_set_regset,
|
||||
TP_PROTO(const char *type, int len, __u64 *control, __u64 *value),
|
||||
TP_ARGS(type, len, control, value),
|
||||
TP_STRUCT__entry(
|
||||
__field(const char *, name)
|
||||
__field(int, len)
|
||||
__array(u64, ctrls, 16)
|
||||
__array(u64, values, 16)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->name = type;
|
||||
__entry->len = len;
|
||||
memcpy(__entry->ctrls, control, len << 3);
|
||||
memcpy(__entry->values, value, len << 3);
|
||||
),
|
||||
TP_printk("%d %s CTRL:%s VALUE:%s", __entry->len, __entry->name,
|
||||
__print_array(__entry->ctrls, __entry->len, sizeof(__u64)),
|
||||
__print_array(__entry->values, __entry->len, sizeof(__u64)))
|
||||
);
|
||||
|
||||
TRACE_EVENT(trap_reg,
|
||||
TP_PROTO(const char *fn, int reg, bool is_write, u64 write_value),
|
||||
TP_ARGS(fn, reg, is_write, write_value),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(const char *, fn)
|
||||
__field(int, reg)
|
||||
__field(bool, is_write)
|
||||
__field(u64, write_value)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->fn = fn;
|
||||
__entry->reg = reg;
|
||||
__entry->is_write = is_write;
|
||||
__entry->write_value = write_value;
|
||||
),
|
||||
|
||||
TP_printk("%s %s reg %d (0x%016llx)", __entry->fn, __entry->is_write?"write to":"read from", __entry->reg, __entry->write_value)
|
||||
);
|
||||
|
||||
TRACE_EVENT(kvm_handle_sys_reg,
|
||||
TP_PROTO(unsigned long hsr),
|
||||
TP_ARGS(hsr),
|
||||
|
||||
Reference in New Issue
Block a user