All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
@ 2017-04-25 14:42 Ladi Prosek
  2017-04-25 16:38 ` David Hildenbrand
  2017-05-17  7:05 ` Wanpeng Li
  0 siblings, 2 replies; 7+ messages in thread
From: Ladi Prosek @ 2017-04-25 14:42 UTC (permalink / raw)
  To: kvm; +Cc: pbonzini

On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
on hflags is reverted later on in x86_emulate_instruction where hflags are
overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.

Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
an instruction is emulated, this commit deletes emul_flags altogether and
makes the emulator access vcpu->arch.hflags using two new accessors. This
way all changes, on the emulator side as well as in functions called from
the emulator and accessing vcpu state with emul_to_vcpu, are preserved.

More details on the bug and its manifestation with Windows and OVMF:

  It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
  I believe that the SMM part explains why we started seeing this only with
  OVMF.

  KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
  the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
  later on in x86_emulate_instruction we overwrite arch.hflags with
  ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
  The AMD-specific hflag of interest here is HF_NMI_MASK.

  When rebooting the system, Windows sends an NMI IPI to all but the current
  cpu to shut them down. Only after all of them are parked in HLT will the
  initiating cpu finish the restart. If NMI is masked, other cpus never get
  the memo and the initiating cpu spins forever, waiting for
  hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.

Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
Signed-off-by: Ladi Prosek <lprosek@redhat.com>
---
 arch/x86/include/asm/kvm_emulate.h |  4 +++-
 arch/x86/kvm/emulate.c             | 17 ++++++++++-------
 arch/x86/kvm/x86.c                 | 15 ++++++++++++---
 3 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index 3e8c287..0559626 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -221,6 +221,9 @@ struct x86_emulate_ops {
 	void (*get_cpuid)(struct x86_emulate_ctxt *ctxt,
 			  u32 *eax, u32 *ebx, u32 *ecx, u32 *edx);
 	void (*set_nmi_mask)(struct x86_emulate_ctxt *ctxt, bool masked);
+
+	unsigned (*get_hflags)(struct x86_emulate_ctxt *ctxt);
+	void (*set_hflags)(struct x86_emulate_ctxt *ctxt, unsigned hflags);
 };
 
 typedef u32 __attribute__((vector_size(16))) sse128_t;
@@ -290,7 +293,6 @@ struct x86_emulate_ctxt {
 
 	/* interruptibility state, as a result of execution of STI or MOV SS */
 	int interruptibility;
-	int emul_flags;
 
 	bool perm_ok; /* do not check permissions if true */
 	bool ud;	/* inject an #UD if host doesn't support insn */
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 45c7306..6a61936 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -2547,7 +2547,7 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt)
 	u64 smbase;
 	int ret;
 
-	if ((ctxt->emul_flags & X86EMUL_SMM_MASK) == 0)
+	if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_MASK) == 0)
 		return emulate_ud(ctxt);
 
 	/*
@@ -2596,11 +2596,12 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt)
 		return X86EMUL_UNHANDLEABLE;
 	}
 
-	if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
+	if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
 		ctxt->ops->set_nmi_mask(ctxt, false);
 
-	ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
-	ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
+	ctxt->ops->set_hflags(ctxt, ctxt->ops->get_hflags(ctxt) &
+		~X86EMUL_SMM_INSIDE_NMI_MASK &
+		~X86EMUL_SMM_MASK);
 	return X86EMUL_CONTINUE;
 }
 
@@ -5316,6 +5317,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 	const struct x86_emulate_ops *ops = ctxt->ops;
 	int rc = X86EMUL_CONTINUE;
 	int saved_dst_type = ctxt->dst.type;
+	unsigned emul_flags;
 
 	ctxt->mem_read.pos = 0;
 
@@ -5330,6 +5332,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 		goto done;
 	}
 
+	emul_flags = ctxt->ops->get_hflags(ctxt);
 	if (unlikely(ctxt->d &
 		     (No64|Undefined|Sse|Mmx|Intercept|CheckPerm|Priv|Prot|String))) {
 		if ((ctxt->mode == X86EMUL_MODE_PROT64 && (ctxt->d & No64)) ||
@@ -5363,7 +5366,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 				fetch_possible_mmx_operand(ctxt, &ctxt->dst);
 		}
 
-		if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && ctxt->intercept) {
+		if (unlikely(emul_flags & X86EMUL_GUEST_MASK) && ctxt->intercept) {
 			rc = emulator_check_intercept(ctxt, ctxt->intercept,
 						      X86_ICPT_PRE_EXCEPT);
 			if (rc != X86EMUL_CONTINUE)
@@ -5392,7 +5395,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 				goto done;
 		}
 
-		if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
+		if (unlikely(emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
 			rc = emulator_check_intercept(ctxt, ctxt->intercept,
 						      X86_ICPT_POST_EXCEPT);
 			if (rc != X86EMUL_CONTINUE)
@@ -5446,7 +5449,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 
 special_insn:
 
-	if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
+	if (unlikely(emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
 		rc = emulator_check_intercept(ctxt, ctxt->intercept,
 					      X86_ICPT_POST_MEMACCESS);
 		if (rc != X86EMUL_CONTINUE)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ccbd45e..642223d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5223,6 +5223,16 @@ static void emulator_set_nmi_mask(struct x86_emulate_ctxt *ctxt, bool masked)
 	kvm_x86_ops->set_nmi_mask(emul_to_vcpu(ctxt), masked);
 }
 
+static unsigned emulator_get_hflags(struct x86_emulate_ctxt *ctxt)
+{
+	return emul_to_vcpu(ctxt)->arch.hflags;
+}
+
+static void emulator_set_hflags(struct x86_emulate_ctxt *ctxt, unsigned emul_flags)
+{
+	kvm_set_hflags(emul_to_vcpu(ctxt), emul_flags);
+}
+
 static const struct x86_emulate_ops emulate_ops = {
 	.read_gpr            = emulator_read_gpr,
 	.write_gpr           = emulator_write_gpr,
@@ -5262,6 +5272,8 @@ static const struct x86_emulate_ops emulate_ops = {
 	.intercept           = emulator_intercept,
 	.get_cpuid           = emulator_get_cpuid,
 	.set_nmi_mask        = emulator_set_nmi_mask,
+	.get_hflags          = emulator_get_hflags,
+	.set_hflags          = emulator_set_hflags,
 };
 
 static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
@@ -5314,7 +5326,6 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
 	BUILD_BUG_ON(HF_GUEST_MASK != X86EMUL_GUEST_MASK);
 	BUILD_BUG_ON(HF_SMM_MASK != X86EMUL_SMM_MASK);
 	BUILD_BUG_ON(HF_SMM_INSIDE_NMI_MASK != X86EMUL_SMM_INSIDE_NMI_MASK);
-	ctxt->emul_flags = vcpu->arch.hflags;
 
 	init_decode_cache(ctxt);
 	vcpu->arch.emulate_regs_need_sync_from_vcpu = false;
@@ -5718,8 +5729,6 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
 		unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
 		toggle_interruptibility(vcpu, ctxt->interruptibility);
 		vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
-		if (vcpu->arch.hflags != ctxt->emul_flags)
-			kvm_set_hflags(vcpu, ctxt->emul_flags);
 		kvm_rip_write(vcpu, ctxt->eip);
 		if (r == EMULATE_DONE)
 			kvm_vcpu_check_singlestep(vcpu, rflags, &r);
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
  2017-04-25 14:42 [PATCH] KVM: x86: fix emulation of RSM and IRET instructions Ladi Prosek
@ 2017-04-25 16:38 ` David Hildenbrand
  2017-04-26  6:34   ` Ladi Prosek
  2017-05-17  7:05 ` Wanpeng Li
  1 sibling, 1 reply; 7+ messages in thread
From: David Hildenbrand @ 2017-04-25 16:38 UTC (permalink / raw)
  To: Ladi Prosek, kvm; +Cc: pbonzini

On 25.04.2017 16:42, Ladi Prosek wrote:
> On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
> on hflags is reverted later on in x86_emulate_instruction where hflags are
> overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
> as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.
> 
> Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
> an instruction is emulated, this commit deletes emul_flags altogether and
> makes the emulator access vcpu->arch.hflags using two new accessors. This
> way all changes, on the emulator side as well as in functions called from
> the emulator and accessing vcpu state with emul_to_vcpu, are preserved.
> 
> More details on the bug and its manifestation with Windows and OVMF:
> 
>   It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
>   I believe that the SMM part explains why we started seeing this only with
>   OVMF.
> 
>   KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
>   the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
>   later on in x86_emulate_instruction we overwrite arch.hflags with
>   ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
>   The AMD-specific hflag of interest here is HF_NMI_MASK.
> 
>   When rebooting the system, Windows sends an NMI IPI to all but the current
>   cpu to shut them down. Only after all of them are parked in HLT will the
>   initiating cpu finish the restart. If NMI is masked, other cpus never get
>   the memo and the initiating cpu spins forever, waiting for
>   hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.
> 
> Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
> Signed-off-by: Ladi Prosek <lprosek@redhat.com>
[...]
>  
> -	if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
> +	if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>  		ctxt->ops->set_nmi_mask(ctxt, false);
>  
> -	ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
> -	ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
> +	ctxt->ops->set_hflags(ctxt, ctxt->ops->get_hflags(ctxt) &
> +		~X86EMUL_SMM_INSIDE_NMI_MASK &
> +		~X86EMUL_SMM_MASK);

Wonder if that would look better with
	& ~(X86EMUL_SMM_INSIDE_NMI_MASK | X86EMUL_SMM_MASK)


[...]
>  
>  static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
> @@ -5314,7 +5326,6 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
>  	BUILD_BUG_ON(HF_GUEST_MASK != X86EMUL_GUEST_MASK);
>  	BUILD_BUG_ON(HF_SMM_MASK != X86EMUL_SMM_MASK);
>  	BUILD_BUG_ON(HF_SMM_INSIDE_NMI_MASK != X86EMUL_SMM_INSIDE_NMI_MASK);
> -	ctxt->emul_flags = vcpu->arch.hflags;
>  
>  	init_decode_cache(ctxt);
>  	vcpu->arch.emulate_regs_need_sync_from_vcpu = false;
> @@ -5718,8 +5729,6 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
>  		unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
>  		toggle_interruptibility(vcpu, ctxt->interruptibility);
>  		vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
> -		if (vcpu->arch.hflags != ctxt->emul_flags)
> -			kvm_set_hflags(vcpu, ctxt->emul_flags);

I like to see that go.

>  		kvm_rip_write(vcpu, ctxt->eip);
>  		if (r == EMULATE_DONE)
>  			kvm_vcpu_check_singlestep(vcpu, rflags, &r);
> 

Looks very good to me!

Reviewed-by: David Hildenbrand <david@redhat.com>

-- 

Thanks,

David

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
  2017-04-25 16:38 ` David Hildenbrand
@ 2017-04-26  6:34   ` Ladi Prosek
  2017-04-27 15:01     ` Paolo Bonzini
  0 siblings, 1 reply; 7+ messages in thread
From: Ladi Prosek @ 2017-04-26  6:34 UTC (permalink / raw)
  To: David Hildenbrand; +Cc: KVM list, Paolo Bonzini

On Tue, Apr 25, 2017 at 6:38 PM, David Hildenbrand <david@redhat.com> wrote:
> On 25.04.2017 16:42, Ladi Prosek wrote:
>> On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
>> on hflags is reverted later on in x86_emulate_instruction where hflags are
>> overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
>> as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.
>>
>> Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
>> an instruction is emulated, this commit deletes emul_flags altogether and
>> makes the emulator access vcpu->arch.hflags using two new accessors. This
>> way all changes, on the emulator side as well as in functions called from
>> the emulator and accessing vcpu state with emul_to_vcpu, are preserved.
>>
>> More details on the bug and its manifestation with Windows and OVMF:
>>
>>   It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
>>   I believe that the SMM part explains why we started seeing this only with
>>   OVMF.
>>
>>   KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
>>   the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
>>   later on in x86_emulate_instruction we overwrite arch.hflags with
>>   ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
>>   The AMD-specific hflag of interest here is HF_NMI_MASK.
>>
>>   When rebooting the system, Windows sends an NMI IPI to all but the current
>>   cpu to shut them down. Only after all of them are parked in HLT will the
>>   initiating cpu finish the restart. If NMI is masked, other cpus never get
>>   the memo and the initiating cpu spins forever, waiting for
>>   hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.
>>
>> Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
>> Signed-off-by: Ladi Prosek <lprosek@redhat.com>
> [...]
>>
>> -     if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>> +     if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>>               ctxt->ops->set_nmi_mask(ctxt, false);
>>
>> -     ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
>> -     ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
>> +     ctxt->ops->set_hflags(ctxt, ctxt->ops->get_hflags(ctxt) &
>> +             ~X86EMUL_SMM_INSIDE_NMI_MASK &
>> +             ~X86EMUL_SMM_MASK);
>
> Wonder if that would look better with
>         & ~(X86EMUL_SMM_INSIDE_NMI_MASK | X86EMUL_SMM_MASK)

Yes, it definitely would.

> [...]
>>
>>  static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
>> @@ -5314,7 +5326,6 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
>>       BUILD_BUG_ON(HF_GUEST_MASK != X86EMUL_GUEST_MASK);
>>       BUILD_BUG_ON(HF_SMM_MASK != X86EMUL_SMM_MASK);
>>       BUILD_BUG_ON(HF_SMM_INSIDE_NMI_MASK != X86EMUL_SMM_INSIDE_NMI_MASK);
>> -     ctxt->emul_flags = vcpu->arch.hflags;
>>
>>       init_decode_cache(ctxt);
>>       vcpu->arch.emulate_regs_need_sync_from_vcpu = false;
>> @@ -5718,8 +5729,6 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
>>               unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
>>               toggle_interruptibility(vcpu, ctxt->interruptibility);
>>               vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
>> -             if (vcpu->arch.hflags != ctxt->emul_flags)
>> -                     kvm_set_hflags(vcpu, ctxt->emul_flags);
>
> I like to see that go.
>
>>               kvm_rip_write(vcpu, ctxt->eip);
>>               if (r == EMULATE_DONE)
>>                       kvm_vcpu_check_singlestep(vcpu, rflags, &r);
>>
>
> Looks very good to me!

Thank you for the review!

> Reviewed-by: David Hildenbrand <david@redhat.com>
>
> --
>
> Thanks,
>
> David

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
  2017-04-26  6:34   ` Ladi Prosek
@ 2017-04-27 15:01     ` Paolo Bonzini
  0 siblings, 0 replies; 7+ messages in thread
From: Paolo Bonzini @ 2017-04-27 15:01 UTC (permalink / raw)
  To: Ladi Prosek, David Hildenbrand; +Cc: KVM list



On 26/04/2017 08:34, Ladi Prosek wrote:
> On Tue, Apr 25, 2017 at 6:38 PM, David Hildenbrand <david@redhat.com> wrote:
>> On 25.04.2017 16:42, Ladi Prosek wrote:
>>> On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
>>> on hflags is reverted later on in x86_emulate_instruction where hflags are
>>> overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
>>> as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.
>>>
>>> Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
>>> an instruction is emulated, this commit deletes emul_flags altogether and
>>> makes the emulator access vcpu->arch.hflags using two new accessors. This
>>> way all changes, on the emulator side as well as in functions called from
>>> the emulator and accessing vcpu state with emul_to_vcpu, are preserved.
>>>
>>> More details on the bug and its manifestation with Windows and OVMF:
>>>
>>>   It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
>>>   I believe that the SMM part explains why we started seeing this only with
>>>   OVMF.
>>>
>>>   KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
>>>   the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
>>>   later on in x86_emulate_instruction we overwrite arch.hflags with
>>>   ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
>>>   The AMD-specific hflag of interest here is HF_NMI_MASK.
>>>
>>>   When rebooting the system, Windows sends an NMI IPI to all but the current
>>>   cpu to shut them down. Only after all of them are parked in HLT will the
>>>   initiating cpu finish the restart. If NMI is masked, other cpus never get
>>>   the memo and the initiating cpu spins forever, waiting for
>>>   hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.
>>>
>>> Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
>>> Signed-off-by: Ladi Prosek <lprosek@redhat.com>
>> [...]
>>>
>>> -     if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>>> +     if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>>>               ctxt->ops->set_nmi_mask(ctxt, false);
>>>
>>> -     ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
>>> -     ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
>>> +     ctxt->ops->set_hflags(ctxt, ctxt->ops->get_hflags(ctxt) &
>>> +             ~X86EMUL_SMM_INSIDE_NMI_MASK &
>>> +             ~X86EMUL_SMM_MASK);
>>
>> Wonder if that would look better with
>>         & ~(X86EMUL_SMM_INSIDE_NMI_MASK | X86EMUL_SMM_MASK)
> 
> Yes, it definitely would.
> 
>> [...]
>>>
>>>  static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
>>> @@ -5314,7 +5326,6 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
>>>       BUILD_BUG_ON(HF_GUEST_MASK != X86EMUL_GUEST_MASK);
>>>       BUILD_BUG_ON(HF_SMM_MASK != X86EMUL_SMM_MASK);
>>>       BUILD_BUG_ON(HF_SMM_INSIDE_NMI_MASK != X86EMUL_SMM_INSIDE_NMI_MASK);
>>> -     ctxt->emul_flags = vcpu->arch.hflags;
>>>
>>>       init_decode_cache(ctxt);
>>>       vcpu->arch.emulate_regs_need_sync_from_vcpu = false;
>>> @@ -5718,8 +5729,6 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
>>>               unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
>>>               toggle_interruptibility(vcpu, ctxt->interruptibility);
>>>               vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
>>> -             if (vcpu->arch.hflags != ctxt->emul_flags)
>>> -                     kvm_set_hflags(vcpu, ctxt->emul_flags);
>>
>> I like to see that go.
>>
>>>               kvm_rip_write(vcpu, ctxt->eip);
>>>               if (r == EMULATE_DONE)
>>>                       kvm_vcpu_check_singlestep(vcpu, rflags, &r);
>>>
>>
>> Looks very good to me!
> 
> Thank you for the review!

Fixed and queued.

Paolo

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
  2017-04-25 14:42 [PATCH] KVM: x86: fix emulation of RSM and IRET instructions Ladi Prosek
  2017-04-25 16:38 ` David Hildenbrand
@ 2017-05-17  7:05 ` Wanpeng Li
  2017-05-18  7:17   ` Ladi Prosek
  1 sibling, 1 reply; 7+ messages in thread
From: Wanpeng Li @ 2017-05-17  7:05 UTC (permalink / raw)
  To: Ladi Prosek; +Cc: kvm, Paolo Bonzini

Hi Ladi,
2017-04-25 22:42 GMT+08:00 Ladi Prosek <lprosek@redhat.com>:
> On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
> on hflags is reverted later on in x86_emulate_instruction where hflags are
> overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
> as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.
>
> Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
> an instruction is emulated, this commit deletes emul_flags altogether and
> makes the emulator access vcpu->arch.hflags using two new accessors. This
> way all changes, on the emulator side as well as in functions called from
> the emulator and accessing vcpu state with emul_to_vcpu, are preserved.
>
> More details on the bug and its manifestation with Windows and OVMF:
>
>   It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
>   I believe that the SMM part explains why we started seeing this only with
>   OVMF.
>
>   KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
>   the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
>   later on in x86_emulate_instruction we overwrite arch.hflags with
>   ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
>   The AMD-specific hflag of interest here is HF_NMI_MASK.
>
>   When rebooting the system, Windows sends an NMI IPI to all but the current
>   cpu to shut them down. Only after all of them are parked in HLT will the
>   initiating cpu finish the restart. If NMI is masked, other cpus never get
>   the memo and the initiating cpu spins forever, waiting for
>   hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.
>
> Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
> Signed-off-by: Ladi Prosek <lprosek@redhat.com>

[snip]

>
> -       if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
> +       if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>                 ctxt->ops->set_nmi_mask(ctxt, false);
>
> -       ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
> -       ctxt->emul_flags &= ~X86EMUL_SMM_MASK;

This will clear SMM related flags for ctxt->emul_flags, so why
overwrites arch.hflags with ctxt->emul_flags matters in
x86_emulate_instruction? In addition, nmi mask is cleared in the above
codes, I didn't find where can set nmi mask again due to SMM unless
enter SMM.

Regards,
Wanpeng Li

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
  2017-05-17  7:05 ` Wanpeng Li
@ 2017-05-18  7:17   ` Ladi Prosek
  2017-05-18  7:37     ` Wanpeng Li
  0 siblings, 1 reply; 7+ messages in thread
From: Ladi Prosek @ 2017-05-18  7:17 UTC (permalink / raw)
  To: Wanpeng Li; +Cc: kvm, Paolo Bonzini

Hi Wanpeng,

On Wed, May 17, 2017 at 9:05 AM, Wanpeng Li <kernellwp@gmail.com> wrote:
> Hi Ladi,
> 2017-04-25 22:42 GMT+08:00 Ladi Prosek <lprosek@redhat.com>:
>> On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
>> on hflags is reverted later on in x86_emulate_instruction where hflags are
>> overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
>> as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.
>>
>> Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
>> an instruction is emulated, this commit deletes emul_flags altogether and
>> makes the emulator access vcpu->arch.hflags using two new accessors. This
>> way all changes, on the emulator side as well as in functions called from
>> the emulator and accessing vcpu state with emul_to_vcpu, are preserved.
>>
>> More details on the bug and its manifestation with Windows and OVMF:
>>
>>   It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
>>   I believe that the SMM part explains why we started seeing this only with
>>   OVMF.
>>
>>   KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
>>   the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
>>   later on in x86_emulate_instruction we overwrite arch.hflags with
>>   ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
>>   The AMD-specific hflag of interest here is HF_NMI_MASK.
>>
>>   When rebooting the system, Windows sends an NMI IPI to all but the current
>>   cpu to shut them down. Only after all of them are parked in HLT will the
>>   initiating cpu finish the restart. If NMI is masked, other cpus never get
>>   the memo and the initiating cpu spins forever, waiting for
>>   hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.
>>
>> Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
>> Signed-off-by: Ladi Prosek <lprosek@redhat.com>
>
> [snip]
>
>>
>> -       if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>> +       if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>>                 ctxt->ops->set_nmi_mask(ctxt, false);
>>
>> -       ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
>> -       ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
>
> This will clear SMM related flags for ctxt->emul_flags, so why
> overwrites arch.hflags with ctxt->emul_flags matters in
> x86_emulate_instruction? In addition, nmi mask is cleared in the above
> codes, I didn't find where can set nmi mask again due to SMM unless
> enter SMM.

The flag that gets overwritten is HF_NMI_MASK.

em_rsm calls ctxt->ops->set_nmi_mask which points to svm_set_nmi_mask on AMD.
svm_set_nmi_mask modifies vcpu.arch.hflags, but
x86_emulate_instruction then called kvm_set_hflags(vcpu,
ctxt->emul_flags) and vcpu.arch.hflags was overwritten with its
original value (as saved in init_emulate_ctxt) plus whatever changes
to ctxt->emul_flags were made as part of emulating the instruction.
The effect of svm_set_nmi_mask on vcpu.arch.hflags was lost.

Thanks,
Ladi

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] KVM: x86: fix emulation of RSM and IRET instructions
  2017-05-18  7:17   ` Ladi Prosek
@ 2017-05-18  7:37     ` Wanpeng Li
  0 siblings, 0 replies; 7+ messages in thread
From: Wanpeng Li @ 2017-05-18  7:37 UTC (permalink / raw)
  To: Ladi Prosek; +Cc: kvm, Paolo Bonzini

2017-05-18 15:17 GMT+08:00 Ladi Prosek <lprosek@redhat.com>:
> Hi Wanpeng,
>
> On Wed, May 17, 2017 at 9:05 AM, Wanpeng Li <kernellwp@gmail.com> wrote:
>> Hi Ladi,
>> 2017-04-25 22:42 GMT+08:00 Ladi Prosek <lprosek@redhat.com>:
>>> On AMD, the effect of set_nmi_mask called by emulate_iret_real and em_rsm
>>> on hflags is reverted later on in x86_emulate_instruction where hflags are
>>> overwritten with ctxt->emul_flags (the kvm_set_hflags call). This manifests
>>> as a hang when rebooting Windows VMs with QEMU, OVMF, and >1 vcpu.
>>>
>>> Instead of trying to merge ctxt->emul_flags into vcpu->arch.hflags after
>>> an instruction is emulated, this commit deletes emul_flags altogether and
>>> makes the emulator access vcpu->arch.hflags using two new accessors. This
>>> way all changes, on the emulator side as well as in functions called from
>>> the emulator and accessing vcpu state with emul_to_vcpu, are preserved.
>>>
>>> More details on the bug and its manifestation with Windows and OVMF:
>>>
>>>   It's a KVM bug in the interaction between SMI/SMM and NMI, specific to AMD.
>>>   I believe that the SMM part explains why we started seeing this only with
>>>   OVMF.
>>>
>>>   KVM masks and unmasks NMI when entering and leaving SMM. When KVM emulates
>>>   the RSM instruction in em_rsm, the set_nmi_mask call doesn't stick because
>>>   later on in x86_emulate_instruction we overwrite arch.hflags with
>>>   ctxt->emul_flags, effectively reverting the effect of the set_nmi_mask call.
>>>   The AMD-specific hflag of interest here is HF_NMI_MASK.
>>>
>>>   When rebooting the system, Windows sends an NMI IPI to all but the current
>>>   cpu to shut them down. Only after all of them are parked in HLT will the
>>>   initiating cpu finish the restart. If NMI is masked, other cpus never get
>>>   the memo and the initiating cpu spins forever, waiting for
>>>   hal!HalpInterruptProcessorsStarted to drop. That's the symptom we observe.
>>>
>>> Fixes: a584539b24b8 ("KVM: x86: pass the whole hflags field to emulator and back")
>>> Signed-off-by: Ladi Prosek <lprosek@redhat.com>
>>
>> [snip]
>>
>>>
>>> -       if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>>> +       if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
>>>                 ctxt->ops->set_nmi_mask(ctxt, false);
>>>
>>> -       ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
>>> -       ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
>>
>> This will clear SMM related flags for ctxt->emul_flags, so why
>> overwrites arch.hflags with ctxt->emul_flags matters in
>> x86_emulate_instruction? In addition, nmi mask is cleared in the above
>> codes, I didn't find where can set nmi mask again due to SMM unless
>> enter SMM.
>
> The flag that gets overwritten is HF_NMI_MASK.
>
> em_rsm calls ctxt->ops->set_nmi_mask which points to svm_set_nmi_mask on AMD.
> svm_set_nmi_mask modifies vcpu.arch.hflags, but
> x86_emulate_instruction then called kvm_set_hflags(vcpu,
> ctxt->emul_flags) and vcpu.arch.hflags was overwritten with its
> original value (as saved in init_emulate_ctxt) plus whatever changes
> to ctxt->emul_flags were made as part of emulating the instruction.
> The effect of svm_set_nmi_mask on vcpu.arch.hflags was lost.

I see, vmx keeps the nmi mask information by vmx->nmi_known_unmasked
which will not be influenced by this overwritten, thanks for the
explanation.

Regards,
Wanpeng Li

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-05-18  7:37 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-04-25 14:42 [PATCH] KVM: x86: fix emulation of RSM and IRET instructions Ladi Prosek
2017-04-25 16:38 ` David Hildenbrand
2017-04-26  6:34   ` Ladi Prosek
2017-04-27 15:01     ` Paolo Bonzini
2017-05-17  7:05 ` Wanpeng Li
2017-05-18  7:17   ` Ladi Prosek
2017-05-18  7:37     ` Wanpeng Li

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.