Skip to contentSkip to conversation

Real mode interrupt injection emulation

PATCH Applied Avi Kivity 2 participants 11 messages Sep 19, 2010 – Sep 22, 2010

Subsystems (3)
lore
Share & tools
lore

Avi Kivity

author
#0 lore raw

cover Real mode interrupt injection emulation

Our current real mode interrupt injection injects external interrupts as software interrupts, which is somewhat hacky. This is problematic in big real mode (can't use vmx there) and on via processors (a cpu bug prevents this from working correctly).

Replace the current mechanism with emulation; we now inject the interrupt by looking up the vector in the IVT, updating the stack, etc. using the emulator. This is somewhat slower but works in all cases.

Avi Kivity (1):
KVM: VMX: Respect interrupt window in big real mode

Mohammed Gamal (3):
KVM: x86 emulator: Expose emulate_int_real()
KVM: Add kvm_inject_realmode_interrupt() wrapper
KVM: VMX: Emulated real mode interrupt injection

 arch/x86/include/asm/kvm_emulate.h |    3 +-
 arch/x86/kvm/vmx.c                 |   74 +++++++----------------------------
 arch/x86/kvm/x86.c                 |   29 ++++++++++++++
 arch/x86/kvm/x86.h                 |    1 +
 4 files changed, 47 insertions(+), 60 deletions(-)
Signature · 1 line
1.7.2.3
author
#1 lore raw

PATCH 1/4 KVM: x86 emulator: Expose emulate_int_real()

From: Mohammed Gamal <[email protected]>

---
 arch/x86/include/asm/kvm_emulate.h |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
arch/x86/include/asm/kvm_emulate.h +2 -1
index 5187dd8..b36c6b3 100644
@@ -260,5 +260,6 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt);
 int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
 			 u16 tss_selector, int reason,
 			 bool has_error_code, u32 error_code);
-
+int emulate_int_real(struct x86_emulate_ctxt *ctxt,
+		     struct x86_emulate_ops *ops, int irq);
 #endif /* _ASM_X86_KVM_X86_EMULATE_H */
Signature · 1 line
1.7.2.3
author
#2 lore raw

PATCH 2/4 KVM: Add kvm_inject_realmode_interrupt() wrapper

In reply to Avi Kivity

From: Mohammed Gamal <[email protected]>

This adds a wrapper function kvm_inject_realmode_interrupt() around the emulator function emulate_int_real() to allow real mode interrupt injection.

[avi: initialize operand and address sizes before emulating interrupts] [avi: initialize rip for real mode interrupt injection] [avi: clear interrupt pending flag after emulating interrupt injection]

---
 arch/x86/kvm/x86.c |   29 +++++++++++++++++++++++++++++
 arch/x86/kvm/x86.h |    1 +
 2 files changed, 30 insertions(+), 0 deletions(-)
arch/x86/kvm/x86.c +29 -0
index a51635e..a3fc151 100644
@@ -4187,6 +4187,35 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
 	memcpy(c->regs, vcpu->arch.regs, sizeof c->regs);
 }
 
+int kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq)
+{
+	struct decode_cache *c = &vcpu->arch.emulate_ctxt.decode;
+	int ret;
+
+	init_emulate_ctxt(vcpu);
+
+	vcpu->arch.emulate_ctxt.decode.op_bytes = 2;
+	vcpu->arch.emulate_ctxt.decode.ad_bytes = 2;
+	vcpu->arch.emulate_ctxt.decode.eip = vcpu->arch.emulate_ctxt.eip;
+	ret = emulate_int_real(&vcpu->arch.emulate_ctxt, &emulate_ops, irq);
+
+	if (ret != X86EMUL_CONTINUE)
+		return EMULATE_FAIL;
+
+	vcpu->arch.emulate_ctxt.eip = c->eip;
+	memcpy(vcpu->arch.regs, c->regs, sizeof c->regs);
+	kvm_rip_write(vcpu, vcpu->arch.emulate_ctxt.eip);
+	kvm_x86_ops->set_rflags(vcpu, vcpu->arch.emulate_ctxt.eflags);
+
+	if (irq == NMI_VECTOR)
+		vcpu->arch.nmi_pending = false;
+	else
+		vcpu->arch.interrupt.pending = false;
+
+	return EMULATE_DONE;
+}
+EXPORT_SYMBOL_GPL(kvm_inject_realmode_interrupt);
+
 static int handle_emulation_failure(struct kvm_vcpu *vcpu)
 {
 	++vcpu->stat.insn_emulation_fail;
arch/x86/kvm/x86.h +1 -0
index bf4dc2f..2cea414 100644
@@ -72,6 +72,7 @@ static inline int is_paging(struct kvm_vcpu *vcpu)
 
 void kvm_before_handle_nmi(struct kvm_vcpu *vcpu);
 void kvm_after_handle_nmi(struct kvm_vcpu *vcpu);
+int kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq);
 
 void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data);
 
Signature · 1 line
1.7.2.3
author
#3 lore raw

PATCH 3/4 KVM: VMX: Emulated real mode interrupt injection

In reply to Avi Kivity

From: Mohammed Gamal <[email protected]>

Replace the inject-as-software-interrupt hack we currently have with emulated injection.

---
 arch/x86/kvm/vmx.c |   65 ++++-----------------------------------------------
 1 files changed, 6 insertions(+), 59 deletions(-)
arch/x86/kvm/vmx.c +6 -59
index 8ef6199..2572153 100644
@@ -155,11 +155,6 @@ struct vcpu_vmx {
 			u32 limit;
 			u32 ar;
 		} tr, es, ds, fs, gs;
-		struct {
-			bool pending;
-			u8 vector;
-			unsigned rip;
-		} irq;
 	} rmode;
 	int vpid;
 	bool emulation_required;
@@ -1034,16 +1029,8 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
 	}
 
 	if (vmx->rmode.vm86_active) {
-		vmx->rmode.irq.pending = true;
-		vmx->rmode.irq.vector = nr;
-		vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-		if (kvm_exception_is_soft(nr))
-			vmx->rmode.irq.rip +=
-				vmx->vcpu.arch.event_exit_inst_len;
-		intr_info |= INTR_TYPE_SOFT_INTR;
-		vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
-		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-		kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
+		if (kvm_inject_realmode_interrupt(vcpu, nr) != EMULATE_DONE)
+			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
 
@@ -2822,16 +2809,8 @@ static void vmx_inject_irq(struct kvm_vcpu *vcpu)
 
 	++vcpu->stat.irq_injections;
 	if (vmx->rmode.vm86_active) {
-		vmx->rmode.irq.pending = true;
-		vmx->rmode.irq.vector = irq;
-		vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-		if (vcpu->arch.interrupt.soft)
-			vmx->rmode.irq.rip +=
-				vmx->vcpu.arch.event_exit_inst_len;
-		vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
-			     irq | INTR_TYPE_SOFT_INTR | INTR_INFO_VALID_MASK);
-		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-		kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
+		if (kvm_inject_realmode_interrupt(vcpu, irq) != EMULATE_DONE)
+			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
 	intr = irq | INTR_INFO_VALID_MASK;
@@ -2863,14 +2842,8 @@ static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
 
 	++vcpu->stat.nmi_injections;
 	if (vmx->rmode.vm86_active) {
-		vmx->rmode.irq.pending = true;
-		vmx->rmode.irq.vector = NMI_VECTOR;
-		vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-		vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
-			     NMI_VECTOR | INTR_TYPE_SOFT_INTR |
-			     INTR_INFO_VALID_MASK);
-		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-		kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
+		if (kvm_inject_realmode_interrupt(vcpu, NMI_VECTOR) != EMULATE_DONE)
+			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
@@ -3832,29 +3805,6 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx)
 			ktime_to_ns(ktime_sub(ktime_get(), vmx->entry_time));
 }
 
-/*
- * Failure to inject an interrupt should give us the information
- * in IDT_VECTORING_INFO_FIELD.  However, if the failure occurs
- * when fetching the interrupt redirection bitmap in the real-mode
- * tss, this doesn't happen.  So we do it ourselves.
- */
-static void fixup_rmode_irq(struct vcpu_vmx *vmx, u32 *idt_vectoring_info)
-{
-	vmx->rmode.irq.pending = 0;
-	if (kvm_rip_read(&vmx->vcpu) + 1 != vmx->rmode.irq.rip)
-		return;
-	kvm_rip_write(&vmx->vcpu, vmx->rmode.irq.rip);
-	if (*idt_vectoring_info & VECTORING_INFO_VALID_MASK) {
-		*idt_vectoring_info &= ~VECTORING_INFO_TYPE_MASK;
-		*idt_vectoring_info |= INTR_TYPE_EXT_INTR;
-		return;
-	}
-	*idt_vectoring_info =
-		VECTORING_INFO_VALID_MASK
-		| INTR_TYPE_EXT_INTR
-		| vmx->rmode.irq.vector;
-}
-
 static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
 				      u32 idt_vectoring_info,
 				      int instr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
 	int type;
 	bool idtv_info_valid;
 
-	if (vmx->rmode.irq.pending)
-		fixup_rmode_irq(vmx, &idt_vectoring_info);
-
 	idtv_info_valid = idt_vectoring_info & VECTORING_INFO_VALID_MASK;
 
 	vmx->vcpu.arch.nmi_injected = false;
Signature · 1 line
1.7.2.3
#4 lore raw

Re: [PATCH 3/4] KVM: VMX: Emulated real mode interrupt injection

Quoted 113 lines · Avi Kivity

From: Mohammed Gamal <[email protected]>

Replace the inject-as-software-interrupt hack we currently have with emulated injection.

---
 arch/x86/kvm/vmx.c |   65 ++++-----------------------------------------------
 1 files changed, 6 insertions(+), 59 deletions(-)
arch/x86/kvm/vmx.c +6 -56
index 8ef6199..2572153 100644
@@ -155,11 +155,6 @@ struct vcpu_vmx {
 			u32 limit;
 			u32 ar;
 		} tr, es, ds, fs, gs;
-		struct {
-			bool pending;
-			u8 vector;
-			unsigned rip;
-		} irq;
 	} rmode;
 	int vpid;
 	bool emulation_required;
@@ -1034,16 +1029,8 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
 	}
 
 	if (vmx->rmode.vm86_active) {
-		vmx->rmode.irq.pending = true;
-		vmx->rmode.irq.vector = nr;
-		vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-		if (kvm_exception_is_soft(nr))
-			vmx->rmode.irq.rip +=
-				vmx->vcpu.arch.event_exit_inst_len;
-		intr_info |= INTR_TYPE_SOFT_INTR;
-		vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
-		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-		kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
+		if (kvm_inject_realmode_interrupt(vcpu, nr) != EMULATE_DONE)
+			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
 
@@ -2822,16 +2809,8 @@ static void vmx_inject_irq(struct kvm_vcpu *vcpu)
 
 	++vcpu->stat.irq_injections;
 	if (vmx->rmode.vm86_active) {
-		vmx->rmode.irq.pending = true;
-		vmx->rmode.irq.vector = irq;
-		vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-		if (vcpu->arch.interrupt.soft)
-			vmx->rmode.irq.rip +=
-				vmx->vcpu.arch.event_exit_inst_len;
-		vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
-			     irq | INTR_TYPE_SOFT_INTR | INTR_INFO_VALID_MASK);
-		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-		kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
+		if (kvm_inject_realmode_interrupt(vcpu, irq) != EMULATE_DONE)
+			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
 	intr = irq | INTR_INFO_VALID_MASK;
@@ -2863,14 +2842,8 @@ static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
 
 	++vcpu->stat.nmi_injections;
 	if (vmx->rmode.vm86_active) {
-		vmx->rmode.irq.pending = true;
-		vmx->rmode.irq.vector = NMI_VECTOR;
-		vmx->rmode.irq.rip = kvm_rip_read(vcpu);
-		vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
-			     NMI_VECTOR | INTR_TYPE_SOFT_INTR |
-			     INTR_INFO_VALID_MASK);
-		vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
-		kvm_rip_write(vcpu, vmx->rmode.irq.rip - 1);
+		if (kvm_inject_realmode_interrupt(vcpu, NMI_VECTOR) != EMULATE_DONE)
+			kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu);
 		return;
 	}
 	vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
@@ -3832,29 +3805,6 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx)
 			ktime_to_ns(ktime_sub(ktime_get(), vmx->entry_time));
 }
 
-/*
- * Failure to inject an interrupt should give us the information
- * in IDT_VECTORING_INFO_FIELD.  However, if the failure occurs
- * when fetching the interrupt redirection bitmap in the real-mode
- * tss, this doesn't happen.  So we do it ourselves.
- */
-static void fixup_rmode_irq(struct vcpu_vmx *vmx, u32 *idt_vectoring_info)
-{
-	vmx->rmode.irq.pending = 0;
-	if (kvm_rip_read(&vmx->vcpu) + 1 != vmx->rmode.irq.rip)
-		return;
-	kvm_rip_write(&vmx->vcpu, vmx->rmode.irq.rip);
-	if (*idt_vectoring_info & VECTORING_INFO_VALID_MASK) {
-		*idt_vectoring_info &= ~VECTORING_INFO_TYPE_MASK;
-		*idt_vectoring_info |= INTR_TYPE_EXT_INTR;
-		return;
-	}
-	*idt_vectoring_info =
-		VECTORING_INFO_VALID_MASK
-		| INTR_TYPE_EXT_INTR
-		| vmx->rmode.irq.vector;
-}
-
 static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
 				      u32 idt_vectoring_info,
 				      int instr_len_field,

arch/x86/kvm/vmx.c static void __vmx_complete_interrupts(struct vcpu_vmx *vmx, · line 3869

Quoted 6 lines
arch/x86/kvm/vmx.c +0 -3
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
 	int type;
 	bool idtv_info_valid;
 
-	if (vmx->rmode.irq.pending)
-		fixup_rmode_irq(vmx, &idt_vectoring_info);
-

Don't you have to undo kvm_inject_realmode_interrupt if injection fails?

Quoted 5 lines
arch/x86/kvm/vmx.c +0 -0
 	idtv_info_valid = idt_vectoring_info & VECTORING_INFO_VALID_MASK;
 
 	vmx->vcpu.arch.nmi_injected = false;
Signature · 1 line
1.7.2.3
author
#5 lore raw

Re: [PATCH 3/4] KVM: VMX: Emulated real mode interrupt injection

Quoted 7 lines · Marcelo Tosatti
Quoted 7 lines

static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
u32 idt_vectoring_info,
int instr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
int type;
bool idtv_info_valid;

Quoted 7 lines
Quoted 3 lines

- if (vmx->rmode.irq.pending)
- fixup_rmode_irq(vmx,&idt_vectoring_info);
-

Don't you have to undo kvm_inject_realmode_interrupt if injection fails?

Injection cannot fail (at least, in the same sense as the vmx injections). It's actually not about failures, it's about guest entry being cancelled due to a signal or some KVM_REQ that needs attention. For vmx style injections, we need to undo the injection to keep things in a consistent state. To realmode emulated injection, everything is in a consistent state already, so no need to undo anything (it's also impossible, since we overwrote memory on the stack).

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#6 lore raw

Re: [PATCH 3/4] KVM: VMX: Emulated real mode interrupt injection

Quoted 16 lines · Avi Kivity
Quoted 7 lines · Marcelo Tosatti
Quoted 7 lines

static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
u32 idt_vectoring_info,
int instr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
int type;
bool idtv_info_valid;

Quoted 7 lines
Quoted 3 lines

- if (vmx->rmode.irq.pending)
- fixup_rmode_irq(vmx,&idt_vectoring_info);
-

Don't you have to undo kvm_inject_realmode_interrupt if injection fails?

Quoted 8 lines

Injection cannot fail (at least, in the same sense as the vmx injections). It's actually not about failures, it's about guest entry being cancelled due to a signal or some KVM_REQ that needs attention. For vmx style injections, we need to undo the injection to keep things in a consistent state. To realmode emulated injection, everything is in a consistent state already, so no need to undo anything (it's also impossible, since we overwrote memory on the stack).

Aren't you going to push EFLAGS,CS,EIP on the stack twice if that occurs?

Yes, can't undo it...

author
#7 lore raw

Re: [PATCH 3/4] KVM: VMX: Emulated real mode interrupt injection

Quoted 26 lines · Marcelo Tosatti
Quoted 16 lines · Avi Kivity
Quoted 7 lines · Marcelo Tosatti
Quoted 7 lines

static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,
u32 idt_vectoring_info,
int instr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(struct vcpu_vmx *vmx,

   	int type;
   	bool idtv_info_valid;
Quoted 7 lines
Quoted 3 lines
  -	if (vmx->rmode.irq.pending)
  -		fixup_rmode_irq(vmx,&idt_vectoring_info);
  -

Don't you have to undo kvm_inject_realmode_interrupt if injection fails?

Quoted 8 lines

Injection cannot fail (at least, in the same sense as the vmx injections). It's actually not about failures, it's about guest entry being cancelled due to a signal or some KVM_REQ that needs attention. For vmx style injections, we need to undo the injection to keep things in a consistent state. To realmode emulated injection, everything is in a consistent state already, so no need to undo anything (it's also impossible, since we overwrote memory on the stack).

Quoted 3 lines

Aren't you going to push EFLAGS,CS,EIP on the stack twice if that occurs?

No, since we clear the pending flag (we do that even for vmx-injected interrupts; then cancel or injection failure re-sets the flag).

Signature · 1 line
error compiling committee.c: too many arguments to function
author
#8 lore raw

PATCH 4/4 KVM: VMX: Respect interrupt window in big real mode

In reply to Avi Kivity

If an interrupt is pending, we need to stop emulation so we can inject it.

---
 arch/x86/kvm/vmx.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)
arch/x86/kvm/vmx.c +9 -0
index 2572153..1a5ecfd 100644
@@ -3588,8 +3588,17 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 	enum emulation_result err = EMULATE_DONE;
 	int ret = 1;
+	u32 cpu_exec_ctrl;
+	bool intr_window_requested;
+
+	cpu_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+	intr_window_requested = cpu_exec_ctrl & CPU_BASED_VIRTUAL_INTR_PENDING;
 
 	while (!guest_state_valid(vcpu)) {
+		if (intr_window_requested
+		    && (kvm_get_rflags(&vmx->vcpu) & X86_EFLAGS_IF))
+			return handle_interrupt_window(&vmx->vcpu);
+
 		err = emulate_instruction(vcpu, 0, 0, 0);
 
 		if (err == EMULATE_DO_MMIO) {
Signature · 1 line
1.7.2.3
author
#9 lore raw
In reply to Avi Kivity
Quoted 10 lines · Avi Kivity

Our current real mode interrupt injection injects external interrupts as software interrupts, which is somewhat hacky. This is problematic in big real mode (can't use vmx there) and on via processors (a cpu bug prevents this from working correctly).

Replace the current mechanism with emulation; we now inject the interrupt by looking up the vector in the IVT, updating the stack, etc. using the emulator. This is somewhat slower but works in all cases.

This patchset survived an autotest run.

Signature · 1 line
error compiling committee.c: too many arguments to function
#10 lore raw
In reply to Avi Kivity
Quoted 17 lines · Avi Kivity

Our current real mode interrupt injection injects external interrupts as software interrupts, which is somewhat hacky. This is problematic in big real mode (can't use vmx there) and on via processors (a cpu bug prevents this from working correctly).

Replace the current mechanism with emulation; we now inject the interrupt by looking up the vector in the IVT, updating the stack, etc. using the emulator. This is somewhat slower but works in all cases.

Avi Kivity (1):
KVM: VMX: Respect interrupt window in big real mode

Mohammed Gamal (3):
KVM: x86 emulator: Expose emulate_int_real()
KVM: Add kvm_inject_realmode_interrupt() wrapper
KVM: VMX: Emulated real mode interrupt injection

Quoted 8 lines
 arch/x86/include/asm/kvm_emulate.h |    3 +-
 arch/x86/kvm/vmx.c                 |   74 +++++++----------------------------
 arch/x86/kvm/x86.c                 |   29 ++++++++++++++
 arch/x86/kvm/x86.h                 |    1 +
 4 files changed, 47 insertions(+), 60 deletions(-)
Signature · 1 line
1.7.2.3

Applied, thanks.