mirror of
https://github.com/torvalds/linux.git
synced 2024-12-03 17:41:22 +00:00
fa89d31c53
In struct vcpu_arch, the debug_flags field is used to store
debug-related flags about the vcpu state.
Since we are about to add some more flags related to FPSIMD and
SVE, it makes sense to add them to the existing flags field rather
than adding new fields. Since there is only one debug_flags flag
defined so far, there is plenty of free space for expansion.
In preparation for adding more flags, this patch renames the
debug_flags field to simply "flags", and updates comments
appropriately.
The flag definitions are also moved to <asm/kvm_host.h>, since
their presence in <asm/kvm_asm.h> was for purely historical
reasons: these definitions are not used from asm any more, and not
very likely to be as more Hyp asm is migrated to C.
KVM_ARM64_DEBUG_DIRTY_SHIFT has not been used since commit
1ea66d27e7
("arm64: KVM: Move away from the assembly version of
the world switch"), so this patch gets rid of that too.
No functional change.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Acked-by: Christoffer Dall <christoffer.dall@arm.com>
[maz: fixed minor conflict]
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
260 lines
7.8 KiB
C
260 lines
7.8 KiB
C
/*
|
|
* Debug and Guest Debug support
|
|
*
|
|
* Copyright (C) 2015 - Linaro Ltd
|
|
* Author: Alex Bennée <alex.bennee@linaro.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/hw_breakpoint.h>
|
|
|
|
#include <asm/debug-monitors.h>
|
|
#include <asm/kvm_asm.h>
|
|
#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(u32, 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.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
/**
|
|
* 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(__kvm_get_mdcr_el2));
|
|
}
|
|
|
|
/**
|
|
* kvm_arm_reset_debug_ptr - reset the debug ptr to point to the vcpu state
|
|
*/
|
|
|
|
void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu)
|
|
{
|
|
vcpu->arch.debug_ptr = &vcpu->arch.vcpu_debug_state;
|
|
}
|
|
|
|
/**
|
|
* 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. Currently this just 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)
|
|
*
|
|
* Additionally, KVM only traps guest accesses to the debug registers if
|
|
* the guest is not actively using them (see the KVM_ARM64_DEBUG_DIRTY
|
|
* flag on vcpu->arch.flags). 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.
|
|
*/
|
|
|
|
void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
|
|
{
|
|
bool trap_debug = !(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY);
|
|
unsigned long mdscr;
|
|
|
|
trace_kvm_arm_setup_debug(vcpu, vcpu->guest_debug);
|
|
|
|
/*
|
|
* This also clears MDCR_EL2_E2PB_MASK to disable guest access
|
|
* to the profiling buffer.
|
|
*/
|
|
vcpu->arch.mdcr_el2 = __this_cpu_read(mdcr_el2) & MDCR_EL2_HPMN_MASK;
|
|
vcpu->arch.mdcr_el2 |= (MDCR_EL2_TPM |
|
|
MDCR_EL2_TPMS |
|
|
MDCR_EL2_TPMCR |
|
|
MDCR_EL2_TDRA |
|
|
MDCR_EL2_TDOSA);
|
|
|
|
/* Is Guest debugging in effect? */
|
|
if (vcpu->guest_debug) {
|
|
/* Route all software debug exceptions to EL2 */
|
|
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDE;
|
|
|
|
/* 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) {
|
|
*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 KVM_ARM64_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->arch.flags |= KVM_ARM64_DEBUG_DIRTY;
|
|
trap_debug = true;
|
|
|
|
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]);
|
|
}
|
|
}
|
|
|
|
BUG_ON(!vcpu->guest_debug &&
|
|
vcpu->arch.debug_ptr != &vcpu->arch.vcpu_debug_state);
|
|
|
|
/* Trap debug register access */
|
|
if (trap_debug)
|
|
vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA;
|
|
|
|
/* 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->arch.flags |= KVM_ARM64_DEBUG_DIRTY;
|
|
|
|
trace_kvm_arm_set_dreg32("MDCR_EL2", vcpu->arch.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);
|
|
|
|
if (vcpu->guest_debug) {
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* After successfully emulating an instruction, we might want to
|
|
* return to user space with a KVM_EXIT_DEBUG. We can only do this
|
|
* once the emulation is complete, though, so for userspace emulations
|
|
* we have to wait until we have re-entered KVM before calling this
|
|
* helper.
|
|
*
|
|
* Return true (and set exit_reason) to return to userspace or false
|
|
* if no further action is required.
|
|
*/
|
|
bool kvm_arm_handle_step_debug(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
|
{
|
|
if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
|
|
run->exit_reason = KVM_EXIT_DEBUG;
|
|
run->debug.arch.hsr = ESR_ELx_EC_SOFTSTP_LOW << ESR_ELx_EC_SHIFT;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|