diff mbox series

[1/4,v5] KVM: nSVM: If VMRUN is single-stepped, queue the #DB intercept in nested_svm_vmexit()

Message ID 20210323175006.73249-2-krish.sadhukhan@oracle.com (mailing list archive)
State New, archived
Headers show
Series nSVM: Test host RFLAGS.TF on VMRUN | expand

Commit Message

Krish Sadhukhan March 23, 2021, 5:50 p.m. UTC
According to APM, the #DB intercept for a single-stepped VMRUN must happen
after the completion of that instruction, when the guest does #VMEXIT to
the host. However, in the current implementation of KVM, the #DB intercept
for a single-stepped VMRUN happens after the completion of the instruction
that follows the VMRUN instruction. When the #DB intercept handler is
invoked, it shows the RIP of the instruction that follows VMRUN, instead of
of VMRUN itself. This is an incorrect RIP as far as single-stepping VMRUN
is concerned.

This patch fixes the problem by checking, in nested_svm_vmexit(), for the
condition that the VMRUN instruction is being single-stepped and if so,
queues the pending #DB intercept so that the #DB is accounted for before
we execute L1's next instruction.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oraacle.com>
---
 arch/x86/kvm/svm/nested.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

Comments

Paolo Bonzini March 26, 2021, 5:32 p.m. UTC | #1
On 23/03/21 18:50, Krish Sadhukhan wrote:
> According to APM, the #DB intercept for a single-stepped VMRUN must happen
> after the completion of that instruction, when the guest does #VMEXIT to
> the host. However, in the current implementation of KVM, the #DB intercept
> for a single-stepped VMRUN happens after the completion of the instruction
> that follows the VMRUN instruction. When the #DB intercept handler is
> invoked, it shows the RIP of the instruction that follows VMRUN, instead of
> of VMRUN itself. This is an incorrect RIP as far as single-stepping VMRUN
> is concerned.
> 
> This patch fixes the problem by checking, in nested_svm_vmexit(), for the
> condition that the VMRUN instruction is being single-stepped and if so,
> queues the pending #DB intercept so that the #DB is accounted for before
> we execute L1's next instruction.
> 
> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oraacle.com>
> ---
>   arch/x86/kvm/svm/nested.c | 10 ++++++++++
>   1 file changed, 10 insertions(+)
> 
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index 35891d9a1099..713ce5cfb0db 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -720,6 +720,16 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
>   	kvm_clear_exception_queue(&svm->vcpu);
>   	kvm_clear_interrupt_queue(&svm->vcpu);
>   
> +	/*
> +	 * If we are here following the completion of a VMRUN that
> +	 * is being single-stepped, queue the pending #DB intercept
> +	 * right now so that it an be accounted for before we execute
> +	 * L1's next instruction.
> +	 */
> +	if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_VMRUN &&
> +	    svm->vmcb->save.rflags & X86_EFLAGS_TF))
> +		kvm_queue_exception(&(svm->vcpu), DB_VECTOR);
> +
>   	return 0;
>   }

Wouldn't the exit code always be SVM_EXIT_VMRUN after the vmcb01/vmcb02 
split?  I can take care of adding a WARN_ON myself, but I wouldn't mind 
if you checked that my reasoning is true. :)

Paolo
Krish Sadhukhan March 30, 2021, 12:16 a.m. UTC | #2
On 3/26/21 10:32 AM, Paolo Bonzini wrote:
> On 23/03/21 18:50, Krish Sadhukhan wrote:
>> According to APM, the #DB intercept for a single-stepped VMRUN must 
>> happen
>> after the completion of that instruction, when the guest does #VMEXIT to
>> the host. However, in the current implementation of KVM, the #DB 
>> intercept
>> for a single-stepped VMRUN happens after the completion of the 
>> instruction
>> that follows the VMRUN instruction. When the #DB intercept handler is
>> invoked, it shows the RIP of the instruction that follows VMRUN, 
>> instead of
>> of VMRUN itself. This is an incorrect RIP as far as single-stepping 
>> VMRUN
>> is concerned.
>>
>> This patch fixes the problem by checking, in nested_svm_vmexit(), for 
>> the
>> condition that the VMRUN instruction is being single-stepped and if so,
>> queues the pending #DB intercept so that the #DB is accounted for before
>> we execute L1's next instruction.
>>
>> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
>> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@oraacle.com>
>> ---
>>   arch/x86/kvm/svm/nested.c | 10 ++++++++++
>>   1 file changed, 10 insertions(+)
>>
>> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
>> index 35891d9a1099..713ce5cfb0db 100644
>> --- a/arch/x86/kvm/svm/nested.c
>> +++ b/arch/x86/kvm/svm/nested.c
>> @@ -720,6 +720,16 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
>>       kvm_clear_exception_queue(&svm->vcpu);
>>       kvm_clear_interrupt_queue(&svm->vcpu);
>>   +    /*
>> +     * If we are here following the completion of a VMRUN that
>> +     * is being single-stepped, queue the pending #DB intercept
>> +     * right now so that it an be accounted for before we execute
>> +     * L1's next instruction.
>> +     */
>> +    if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_VMRUN &&
>> +        svm->vmcb->save.rflags & X86_EFLAGS_TF))
>> +        kvm_queue_exception(&(svm->vcpu), DB_VECTOR);
>> +
>>       return 0;
>>   }
>
> Wouldn't the exit code always be SVM_EXIT_VMRUN after the 
> vmcb01/vmcb02 split?  I can take care of adding a WARN_ON myself, but 
> I wouldn't mind if you checked that my reasoning is true. :)


Looking at the patchset on "vmcb01/vmcb02 split", I see that we are calling

         svm_switch_vmcb(svm, &svm->vmcb01)

in nested_svm_vmexiit(). So, yes, by the time we reach the above check 
we should always have an exit code of SVM_EXIT_VMRUN.

>
> Paolo
>
diff mbox series

Patch

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 35891d9a1099..713ce5cfb0db 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -720,6 +720,16 @@  int nested_svm_vmexit(struct vcpu_svm *svm)
 	kvm_clear_exception_queue(&svm->vcpu);
 	kvm_clear_interrupt_queue(&svm->vcpu);
 
+	/*
+	 * If we are here following the completion of a VMRUN that
+	 * is being single-stepped, queue the pending #DB intercept
+	 * right now so that it an be accounted for before we execute
+	 * L1's next instruction.
+	 */
+	if (unlikely(svm->vmcb->control.exit_code == SVM_EXIT_VMRUN &&
+	    svm->vmcb->save.rflags & X86_EFLAGS_TF))
+		kvm_queue_exception(&(svm->vcpu), DB_VECTOR);
+
 	return 0;
 }