[PATCH 3/3] arm64: kvm: Fix single step for guest skipped instructions

Julien Thierry julien.thierry at arm.com
Wed Aug 30 02:40:39 PDT 2017


Hi Marc,

On 30/08/17 10:19, Marc Zyngier wrote:
> Hi Julien,
> 
> On 30/08/17 10:01, Julien Thierry wrote:
>> Software Step exception is missing after trapping instruction from the guest.
>>
>> We need to set the PSR.SS to 0 for the guest vcpu before resuming guest
>> execution.
>>
>> Signed-off-by: Julien Thierry <julien.thierry at arm.com>
>> Cc: Christoffer Dall <christoffer.dall at linaro.org>
>> Cc: Marc Zyngier <marc.zyngier at arm.com>
>> Cc: Catalin Marinas <catalin.marinas at arm.com>
>> Cc: Will Deacon <will.deacon at arm.com>
>>
>> ---
>>   arch/arm64/include/asm/kvm_asm.h     |  2 ++
>>   arch/arm64/include/asm/kvm_emulate.h |  2 ++
>>   arch/arm64/kvm/debug.c               | 12 +++++++++++-
>>   arch/arm64/kvm/hyp/switch.c          | 12 ++++++++++++
>>   4 files changed, 27 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
>> index 26a64d0..398bbaa 100644
>> --- a/arch/arm64/include/asm/kvm_asm.h
>> +++ b/arch/arm64/include/asm/kvm_asm.h
>> @@ -32,6 +32,8 @@
>>
>>   #define KVM_ARM64_DEBUG_DIRTY_SHIFT	0
>>   #define KVM_ARM64_DEBUG_DIRTY		(1 << KVM_ARM64_DEBUG_DIRTY_SHIFT)
>> +#define KVM_ARM64_DEBUG_INST_SKIP_SHIFT	1
>> +#define KVM_ARM64_DEBUG_INST_SKIP	(1 << KVM_ARM64_DEBUG_INST_SKIP_SHIFT)
>>
>>   #define kvm_ksym_ref(sym)						\
>>   	({								\
>> diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
>> index fe39e68..d401c64 100644
>> --- a/arch/arm64/include/asm/kvm_emulate.h
>> +++ b/arch/arm64/include/asm/kvm_emulate.h
>> @@ -95,6 +95,8 @@ static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr)
>>   		kvm_skip_instr32(vcpu, is_wide_instr);
>>   	else
>>   		*vcpu_pc(vcpu) += 4;
>> +	/* Let debug engine know we skipped an instruction */
>> +	vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_INST_SKIP;
>>   }
>>
>>   static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu)
>> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
>> index dbadfaf..4806e6b 100644
>> --- a/arch/arm64/kvm/debug.c
>> +++ b/arch/arm64/kvm/debug.c
>> @@ -151,12 +151,22 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu)
>>   		 * debugging the system.
>>   		 */
>>   		if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
>> -			*vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
>> +			/*
>> +			 * If we skipped an instruction while single stepping,
>> +			 * spsr.ss needs to be 0 in order to trigger SS
>> +			 * exception on return.
>> +			 */
>> +			if (!(vcpu->arch.debug_flags & KVM_ARM64_DEBUG_INST_SKIP))
>> +				*vcpu_cpsr(vcpu) |=  DBG_SPSR_SS;
>> +			else
>> +				*vcpu_cpsr(vcpu) &=  ~DBG_SPSR_SS;
>>   			vcpu_sys_reg(vcpu, MDSCR_EL1) |= DBG_MDSCR_SS;
>>   		} else {
>>   			vcpu_sys_reg(vcpu, MDSCR_EL1) &= ~DBG_MDSCR_SS;
>>   		}
>>
>> +		vcpu->arch.debug_flags &= ~KVM_ARM64_DEBUG_INST_SKIP;
>> +
>>   		trace_kvm_arm_set_dreg32("SPSR_EL2", *vcpu_cpsr(vcpu));
>>
>>   		/*
>> diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
>> index 945e79c..6030acd 100644
>> --- a/arch/arm64/kvm/hyp/switch.c
>> +++ b/arch/arm64/kvm/hyp/switch.c
>> @@ -22,6 +22,7 @@
>>   #include <asm/kvm_emulate.h>
>>   #include <asm/kvm_hyp.h>
>>   #include <asm/fpsimd.h>
>> +#include <asm/debug-monitors.h>
>>
>>   static bool __hyp_text __fpsimd_enabled_nvhe(void)
>>   {
>> @@ -276,6 +277,10 @@ static void __hyp_text __skip_instr(struct kvm_vcpu *vcpu)
>>   	}
>>
>>   	write_sysreg_el2(*vcpu_pc(vcpu), elr);
>> +
>> +	*vcpu_cpsr(vcpu) = read_sysreg_el2(spsr);
>> +	*vcpu_cpsr(vcpu) &= ~DBG_SPSR_SS;
>> +	write_sysreg_el2(*vcpu_cpsr(vcpu), spsr);
>>   }
>>
>>   int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
>> @@ -343,6 +348,13 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
>>   			if (ret == -1) {
>>   				/* Promote an illegal access to an SError */
>>   				__skip_instr(vcpu);
>> +
>> +				/*
>> +				 * We're not jumping back, let debug setup know
>> +				 * we skipped an instruction.
>> +				 */
>> +				vcpu->arch.debug_flags |= KVM_ARM64_DEBUG_INST_SKIP;
>> +
> 
> This feels odd. You've already cleared the SS bit from the SPSR, and yet
> you're setting that flag. What is the semantic of this?

The thing is that whenever you get out of __kvm_vcpu_run, 
kvm_arm_setup_debug will get called before running a guest again. So my 
idea is that kvm_arm_setup_debug needs to be aware whether SPSR.SS must 
be set or not. Otherwise kvm_arm_setup_debug will always set the SPSR.SS 
flag. So KVM_ARM64_DEBUG_INST_SKIP is to tell the debug setup that we 
skipped an instruction (here as well as in kvm_skip_instr).

The fact that SPSR.SS is clear in the illegal access case is because it 
uses __skip_instr instead of kvm_skip_instr because we have not saved 
the guest state at that point. But the clearing of SPSR.SS in 
__skip_instr is more targeted at cases that directly jump back into the 
guest (trapped GICv2/3 CPUIF accesses).

Let me know if you feel there is a cleaner way to do this or something 
that would make this clearer.

Thanks,

-- 
Julien Thierry



More information about the linux-arm-kernel mailing list