// SPDX-License-Identifier: GPL-2.0-only/* * Debug and Guest Debug support * * Copyright (C) 2015 - Linaro Ltd * Authors: Alex Bennée <alex.bennee@linaro.org> * Oliver Upton <oliver.upton@linux.dev> */#include <linux/kvm_host.h>#include <linux/hw_breakpoint.h>#include <asm/arm_pmuv3.h>#include <asm/debug-monitors.h>#include <asm/kvm_asm.h>#include <asm/kvm_arm.h>#include <asm/kvm_emulate.h>static int cpu_has_spe(u64 dfr0){ return cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) && !(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P);}/** * kvm_arm_setup_mdcr_el2 - configure vcpu mdcr_el2 value * * @vcpu: the vcpu pointer * * This ensures we will trap access to: * - Performance monitors (MDCR_EL2_TPM/MDCR_EL2_TPMCR) * - Debug ROM Address (MDCR_EL2_TDRA) * - OS related registers (MDCR_EL2_TDOSA) * - Statistical profiler (MDCR_EL2_TPMS/MDCR_EL2_E2PB) * - Self-hosted Trace Filter controls (MDCR_EL2_TTRF) * - Self-hosted Trace (MDCR_EL2_TTRF/MDCR_EL2_E2TB) */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 = FIELD_PREP(MDCR_EL2_HPMN, *host_data_ptr(nr_event_counters)); vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM | MDCR_EL2_TPMS | MDCR_EL2_TTRF | MDCR_EL2_TPMCR | MDCR_EL2_TDRA | MDCR_EL2_TDOSA); /* Is the VM being debugged by userspace? */ if (vcpu->guest_debug) /* Route all software debug exceptions to EL2 */ vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE; /* * Trap debug registers if the guest doesn't have ownership of them. */ if (!kvm_guest_owns_debug_regs(vcpu)) vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA; if (vcpu_has_nv(vcpu)) kvm_nested_setup_mdcr_el2(vcpu); /* Write MDCR_EL2 directly if we're already at EL2 */ if (has_vhe()) write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); preempt_enable();}void kvm_init_host_debug_data(void){ u64 dfr0 = read_sysreg(id_aa64dfr0_el1); unsigned int pmuver = cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMUVer_SHIFT); if (pmuv3_implemented(pmuver)) *host_data_ptr(nr_event_counters) = FIELD_GET(ARMV8_PMU_PMCR_N, read_sysreg(pmcr_el0)); *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); if (cpu_has_spe(dfr0)) host_data_set_flag(HAS_SPE); if (has_vhe()) return; /* Check if we have BRBE implemented and available at the host */ if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT)) host_data_set_flag(HAS_BRBE); if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceFilt_SHIFT)) { /* Force disable trace in protected mode in case of no TRBE */ if (is_protected_kvm_enabled()) host_data_set_flag(EL1_TRACING_CONFIGURED); if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) && !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P)) host_data_set_flag(HAS_TRBE); }}void kvm_debug_init_vhe(void){ /* Clear PMSCR_EL1.E{0,1}SPE which reset to UNKNOWN values. */ if (host_data_test_flag(HAS_SPE)) write_sysreg_el1(0, SYS_PMSCR);}/* * 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){ /* * 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); if (has_vhe()) *host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2); /* * 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 (has_vhe()) write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2); 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_rmw_sys_reg(vcpu, OSLSR_EL1, |=, OSLSR_EL1_OSLK); else __vcpu_rmw_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();}static bool skip_trbe_access(bool skip_condition){ return (WARN_ON_ONCE(preemptible()) || skip_condition || is_protected_kvm_enabled() || !is_kvm_arm_initialised());}void kvm_enable_trbe(void){ if (!skip_trbe_access(has_vhe())) host_data_set_flag(TRBE_ENABLED);}EXPORT_SYMBOL_GPL(kvm_enable_trbe);void kvm_disable_trbe(void){ if (!skip_trbe_access(has_vhe())) host_data_clear_flag(TRBE_ENABLED);}EXPORT_SYMBOL_GPL(kvm_disable_trbe);void kvm_tracing_set_el1_configuration(u64 trfcr_while_in_guest){ if (skip_trbe_access(false)) return; if (has_vhe()) { write_sysreg_s(trfcr_while_in_guest, SYS_TRFCR_EL12); return; } *host_data_ptr(trfcr_while_in_guest) = trfcr_while_in_guest; if (read_sysreg_s(SYS_TRFCR_EL1) != trfcr_while_in_guest) host_data_set_flag(EL1_TRACING_CONFIGURED); else host_data_clear_flag(EL1_TRACING_CONFIGURED);}EXPORT_SYMBOL_GPL(kvm_tracing_set_el1_configuration);
// SPDX-License-Identifier: GPL-2.0-only
/*
* Debug and Guest Debug support
*
* Copyright (C) 2015 - Linaro Ltd
* Authors: Alex Bennée <alex.bennee@linaro.org>
* Oliver Upton <oliver.upton@linux.dev>
*/
#include <linux/kvm_host.h>
#include <linux/hw_breakpoint.h>
#include <asm/arm_pmuv3.h>
#include <asm/debug-monitors.h>
#include <asm/kvm_asm.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_emulate.h>
static int cpu_has_spe(u64 dfr0)
{
return cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_PMSVer_SHIFT) &&
!(read_sysreg_s(SYS_PMBIDR_EL1) & PMBIDR_EL1_P);
}
/**
* kvm_arm_setup_mdcr_el2 - configure vcpu mdcr_el2 value
*
* @vcpu: the vcpu pointer
*
* This ensures we will trap access to:
* - Performance monitors (MDCR_EL2_TPM/MDCR_EL2_TPMCR)
* - Debug ROM Address (MDCR_EL2_TDRA)
* - OS related registers (MDCR_EL2_TDOSA)
* - Statistical profiler (MDCR_EL2_TPMS/MDCR_EL2_E2PB)
* - Self-hosted Trace Filter controls (MDCR_EL2_TTRF)
* - Self-hosted Trace (MDCR_EL2_TTRF/MDCR_EL2_E2TB)
*/
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 = FIELD_PREP(MDCR_EL2_HPMN,
*host_data_ptr(nr_event_counters));
vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
MDCR_EL2_TPMS |
MDCR_EL2_TTRF |
MDCR_EL2_TPMCR |
MDCR_EL2_TDRA |
MDCR_EL2_TDOSA);
/* Is the VM being debugged by userspace? */
if (vcpu->guest_debug)
/* Route all software debug exceptions to EL2 */
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
/*
* Trap debug registers if the guest doesn't have ownership of them.
*/
if (!kvm_guest_owns_debug_regs(vcpu))
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
if (vcpu_has_nv(vcpu))
kvm_nested_setup_mdcr_el2(vcpu);
/* Write MDCR_EL2 directly if we're already at EL2 */
if (has_vhe())
write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2);
preempt_enable();
}
void kvm_init_host_debug_data(void)
{
u64 dfr0 = read_sysreg(id_aa64dfr0_el1);
unsigned int pmuver = cpuid_feature_extract_unsigned_field(dfr0,
ID_AA64DFR0_EL1_PMUVer_SHIFT);
if (pmuv3_implemented(pmuver))
*host_data_ptr(nr_event_counters) = FIELD_GET(ARMV8_PMU_PMCR_N,
read_sysreg(pmcr_el0));
*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);
if (cpu_has_spe(dfr0))
host_data_set_flag(HAS_SPE);
if (has_vhe())
return;
/* Check if we have BRBE implemented and available at the host */
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT))
host_data_set_flag(HAS_BRBE);
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceFilt_SHIFT)) {
/* Force disable trace in protected mode in case of no TRBE */
if (is_protected_kvm_enabled())
host_data_set_flag(EL1_TRACING_CONFIGURED);
if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_TraceBuffer_SHIFT) &&
!(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_EL1_P))
host_data_set_flag(HAS_TRBE);
}
}
void kvm_debug_init_vhe(void)
{
/* Clear PMSCR_EL1.E{0,1}SPE which reset to UNKNOWN values. */
if (host_data_test_flag(HAS_SPE))
write_sysreg_el1(0, SYS_PMSCR);
}
/*
* 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)
{
/*
* 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);
if (has_vhe())
*host_data_ptr(host_debug_state.mdcr_el2) = read_sysreg(mdcr_el2);
/*
* 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 (has_vhe())
write_sysreg(*host_data_ptr(host_debug_state.mdcr_el2), mdcr_el2);
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_rmw_sys_reg(vcpu, OSLSR_EL1, |=, OSLSR_EL1_OSLK);
else
__vcpu_rmw_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();
}
static bool skip_trbe_access(bool skip_condition)
{
return (WARN_ON_ONCE(preemptible()) || skip_condition ||
is_protected_kvm_enabled() || !is_kvm_arm_initialised());
}
void kvm_enable_trbe(void)
{
if (!skip_trbe_access(has_vhe()))
host_data_set_flag(TRBE_ENABLED);
}
EXPORT_SYMBOL_GPL(kvm_enable_trbe);
void kvm_disable_trbe(void)
{
if (!skip_trbe_access(has_vhe()))
host_data_clear_flag(TRBE_ENABLED);
}
EXPORT_SYMBOL_GPL(kvm_disable_trbe);
void kvm_tracing_set_el1_configuration(u64 trfcr_while_in_guest)
{
if (skip_trbe_access(false))
return;
if (has_vhe()) {
write_sysreg_s(trfcr_while_in_guest, SYS_TRFCR_EL12);
return;
}
*host_data_ptr(trfcr_while_in_guest) = trfcr_while_in_guest;
if (read_sysreg_s(SYS_TRFCR_EL1) != trfcr_while_in_guest)
host_data_set_flag(EL1_TRACING_CONFIGURED);
else
host_data_clear_flag(EL1_TRACING_CONFIGURED);
}
EXPORT_SYMBOL_GPL(kvm_tracing_set_el1_configuration);