Skip to content

Commit b9b3eb5

Browse files
ladiprogregkh
authored andcommitted
KVM: x86: fix emulation of RSM and IRET instructions
commit 6ed071f051e12cf7baa1b69d3becb8f232fdfb7b upstream. 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: a584539 ("KVM: x86: pass the whole hflags field to emulator and back") Signed-off-by: Ladi Prosek <lprosek@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 3491a0b commit b9b3eb5

3 files changed

Lines changed: 24 additions & 11 deletions

File tree

arch/x86/include/asm/kvm_emulate.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ struct x86_emulate_ops {
221221
void (*get_cpuid)(struct x86_emulate_ctxt *ctxt,
222222
u32 *eax, u32 *ebx, u32 *ecx, u32 *edx);
223223
void (*set_nmi_mask)(struct x86_emulate_ctxt *ctxt, bool masked);
224+
225+
unsigned (*get_hflags)(struct x86_emulate_ctxt *ctxt);
226+
void (*set_hflags)(struct x86_emulate_ctxt *ctxt, unsigned hflags);
224227
};
225228

226229
typedef u32 __attribute__((vector_size(16))) sse128_t;
@@ -290,7 +293,6 @@ struct x86_emulate_ctxt {
290293

291294
/* interruptibility state, as a result of execution of STI or MOV SS */
292295
int interruptibility;
293-
int emul_flags;
294296

295297
bool perm_ok; /* do not check permissions if true */
296298
bool ud; /* inject an #UD if host doesn't support insn */

arch/x86/kvm/emulate.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2531,7 +2531,7 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt)
25312531
u64 smbase;
25322532
int ret;
25332533

2534-
if ((ctxt->emul_flags & X86EMUL_SMM_MASK) == 0)
2534+
if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_MASK) == 0)
25352535
return emulate_ud(ctxt);
25362536

25372537
/*
@@ -2580,11 +2580,11 @@ static int em_rsm(struct x86_emulate_ctxt *ctxt)
25802580
return X86EMUL_UNHANDLEABLE;
25812581
}
25822582

2583-
if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
2583+
if ((ctxt->ops->get_hflags(ctxt) & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
25842584
ctxt->ops->set_nmi_mask(ctxt, false);
25852585

2586-
ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
2587-
ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
2586+
ctxt->ops->set_hflags(ctxt, ctxt->ops->get_hflags(ctxt) &
2587+
~(X86EMUL_SMM_INSIDE_NMI_MASK | X86EMUL_SMM_MASK));
25882588
return X86EMUL_CONTINUE;
25892589
}
25902590

@@ -5296,6 +5296,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
52965296
const struct x86_emulate_ops *ops = ctxt->ops;
52975297
int rc = X86EMUL_CONTINUE;
52985298
int saved_dst_type = ctxt->dst.type;
5299+
unsigned emul_flags;
52995300

53005301
ctxt->mem_read.pos = 0;
53015302

@@ -5310,6 +5311,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
53105311
goto done;
53115312
}
53125313

5314+
emul_flags = ctxt->ops->get_hflags(ctxt);
53135315
if (unlikely(ctxt->d &
53145316
(No64|Undefined|Sse|Mmx|Intercept|CheckPerm|Priv|Prot|String))) {
53155317
if ((ctxt->mode == X86EMUL_MODE_PROT64 && (ctxt->d & No64)) ||
@@ -5343,7 +5345,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
53435345
fetch_possible_mmx_operand(ctxt, &ctxt->dst);
53445346
}
53455347

5346-
if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && ctxt->intercept) {
5348+
if (unlikely(emul_flags & X86EMUL_GUEST_MASK) && ctxt->intercept) {
53475349
rc = emulator_check_intercept(ctxt, ctxt->intercept,
53485350
X86_ICPT_PRE_EXCEPT);
53495351
if (rc != X86EMUL_CONTINUE)
@@ -5372,7 +5374,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
53725374
goto done;
53735375
}
53745376

5375-
if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
5377+
if (unlikely(emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
53765378
rc = emulator_check_intercept(ctxt, ctxt->intercept,
53775379
X86_ICPT_POST_EXCEPT);
53785380
if (rc != X86EMUL_CONTINUE)
@@ -5426,7 +5428,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
54265428

54275429
special_insn:
54285430

5429-
if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
5431+
if (unlikely(emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
54305432
rc = emulator_check_intercept(ctxt, ctxt->intercept,
54315433
X86_ICPT_POST_MEMACCESS);
54325434
if (rc != X86EMUL_CONTINUE)

arch/x86/kvm/x86.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4999,6 +4999,16 @@ static void emulator_set_nmi_mask(struct x86_emulate_ctxt *ctxt, bool masked)
49994999
kvm_x86_ops->set_nmi_mask(emul_to_vcpu(ctxt), masked);
50005000
}
50015001

5002+
static unsigned emulator_get_hflags(struct x86_emulate_ctxt *ctxt)
5003+
{
5004+
return emul_to_vcpu(ctxt)->arch.hflags;
5005+
}
5006+
5007+
static void emulator_set_hflags(struct x86_emulate_ctxt *ctxt, unsigned emul_flags)
5008+
{
5009+
kvm_set_hflags(emul_to_vcpu(ctxt), emul_flags);
5010+
}
5011+
50025012
static const struct x86_emulate_ops emulate_ops = {
50035013
.read_gpr = emulator_read_gpr,
50045014
.write_gpr = emulator_write_gpr,
@@ -5038,6 +5048,8 @@ static const struct x86_emulate_ops emulate_ops = {
50385048
.intercept = emulator_intercept,
50395049
.get_cpuid = emulator_get_cpuid,
50405050
.set_nmi_mask = emulator_set_nmi_mask,
5051+
.get_hflags = emulator_get_hflags,
5052+
.set_hflags = emulator_set_hflags,
50415053
};
50425054

50435055
static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
@@ -5090,7 +5102,6 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
50905102
BUILD_BUG_ON(HF_GUEST_MASK != X86EMUL_GUEST_MASK);
50915103
BUILD_BUG_ON(HF_SMM_MASK != X86EMUL_SMM_MASK);
50925104
BUILD_BUG_ON(HF_SMM_INSIDE_NMI_MASK != X86EMUL_SMM_INSIDE_NMI_MASK);
5093-
ctxt->emul_flags = vcpu->arch.hflags;
50945105

50955106
init_decode_cache(ctxt);
50965107
vcpu->arch.emulate_regs_need_sync_from_vcpu = false;
@@ -5486,8 +5497,6 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
54865497
unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
54875498
toggle_interruptibility(vcpu, ctxt->interruptibility);
54885499
vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
5489-
if (vcpu->arch.hflags != ctxt->emul_flags)
5490-
kvm_set_hflags(vcpu, ctxt->emul_flags);
54915500
kvm_rip_write(vcpu, ctxt->eip);
54925501
if (r == EMULATE_DONE)
54935502
kvm_vcpu_check_singlestep(vcpu, rflags, &r);

0 commit comments

Comments
 (0)