Real mode interrupt injection emulation
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
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
@@ -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
PATCH 2/4 KVM: Add kvm_inject_realmode_interrupt() wrapper
In reply to Avi KivityFrom: 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
@@ -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
@@ -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
PATCH 3/4 KVM: VMX: Emulated real mode interrupt injection
In reply to Avi KivityFrom: 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
@@ -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
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.
Signed-off-by Mohammed Gamal <[email protected]>Signed-off-by Avi Kivity <[email protected]>--- arch/x86/kvm/vmx.c | 65 ++++----------------------------------------------- 1 files changed, 6 insertions(+), 59 deletions(-)arch/x86/kvm/vmx.c +6
-56@@ -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
-0idtv_info_valid = idt_vectoring_info &VECTORING_INFO_VALID_MASK ; vmx->vcpu.arch.nmi_injected = false;Signature · 1 line
1.7.2.3
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,
u32idt_vectoring_info,
intinstr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(structvcpu_vmx*vmx,
int type;
boolidtv_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_interruptif 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.
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,
u32idt_vectoring_info,
intinstr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(structvcpu_vmx*vmx,
int type;
boolidtv_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_interruptif 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_REQthat 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...
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,
u32idt_vectoring_info,
intinstr_len_field,
@@ -3864,9 +3814,6 @@ static void __vmx_complete_interrupts(structvcpu_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_interruptif 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_REQthat 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
PATCH 4/4 KVM: VMX: Respect interrupt window in big real mode
In reply to Avi KivityIf 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
@@ -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
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
Marcelo Tosatti
AppliedQuoted 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 modeMohammed Gamal (3):
KVM: x86 emulator: Exposeemulate_int_real()
KVM: Addkvm_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.