Skip to content

Commit

Permalink
KVM: SVM: Emulate nRIP feature when reinjecting INT3
Browse files Browse the repository at this point in the history
When in guest debugging mode, we have to reinject those #BP software
exceptions that are caused by guest-injected INT3. As older AMD
processors do not support the required nRIP VMCB field, try to emulate
it by moving RIP past the instruction on exception injection. Fix it up
again in case the injection failed and we were able to catch this. This
does not work for unintercepted faults, but it is better than doing
nothing.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
  • Loading branch information
Jan Kiszka authored and Avi Kivity committed Apr 25, 2010
1 parent f92653e commit 66b7138
Showing 1 changed file with 35 additions and 3 deletions.
38 changes: 35 additions & 3 deletions arch/x86/kvm/svm.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ MODULE_LICENSE("GPL");
#define SVM_FEATURE_NPT (1 << 0)
#define SVM_FEATURE_LBRV (1 << 1)
#define SVM_FEATURE_SVML (1 << 2)
#define SVM_FEATURE_NRIP (1 << 3)
#define SVM_FEATURE_PAUSE_FILTER (1 << 10)

#define NESTED_EXIT_HOST 0 /* Exit handled on host level */
Expand Down Expand Up @@ -110,6 +111,9 @@ struct vcpu_svm {
struct nested_state nested;

bool nmi_singlestep;

unsigned int3_injected;
unsigned long int3_rip;
};

/* enable NPT for AMD64 and X86 with PAE */
Expand Down Expand Up @@ -291,6 +295,22 @@ static void svm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
if (nested_svm_check_exception(svm, nr, has_error_code, error_code))
return;

if (nr == BP_VECTOR && !svm_has(SVM_FEATURE_NRIP)) {
unsigned long rip, old_rip = kvm_rip_read(&svm->vcpu);

/*
* For guest debugging where we have to reinject #BP if some
* INT3 is guest-owned:
* Emulate nRIP by moving RIP forward. Will fail if injection
* raises a fault that is not intercepted. Still better than
* failing in all cases.
*/
skip_emulated_instruction(&svm->vcpu);
rip = kvm_rip_read(&svm->vcpu);
svm->int3_rip = rip + svm->vmcb->save.cs.base;
svm->int3_injected = rip - old_rip;
}

svm->vmcb->control.event_inj = nr
| SVM_EVTINJ_VALID
| (has_error_code ? SVM_EVTINJ_VALID_ERR : 0)
Expand Down Expand Up @@ -2701,6 +2721,9 @@ static void svm_complete_interrupts(struct vcpu_svm *svm)
u8 vector;
int type;
u32 exitintinfo = svm->vmcb->control.exit_int_info;
unsigned int3_injected = svm->int3_injected;

svm->int3_injected = 0;

if (svm->vcpu.arch.hflags & HF_IRET_MASK)
svm->vcpu.arch.hflags &= ~(HF_NMI_MASK | HF_IRET_MASK);
Expand All @@ -2720,12 +2743,21 @@ static void svm_complete_interrupts(struct vcpu_svm *svm)
svm->vcpu.arch.nmi_injected = true;
break;
case SVM_EXITINTINFO_TYPE_EXEPT:
/* In case of software exception do not reinject an exception
vector, but re-execute and instruction instead */
if (is_nested(svm))
break;
if (kvm_exception_is_soft(vector))
/*
* In case of software exceptions, do not reinject the vector,
* but re-execute the instruction instead. Rewind RIP first
* if we emulated INT3 before.
*/
if (kvm_exception_is_soft(vector)) {
if (vector == BP_VECTOR && int3_injected &&
kvm_is_linear_rip(&svm->vcpu, svm->int3_rip))
kvm_rip_write(&svm->vcpu,
kvm_rip_read(&svm->vcpu) -
int3_injected);
break;
}
if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) {
u32 err = svm->vmcb->control.exit_int_info_err;
kvm_queue_exception_e(&svm->vcpu, vector, err);
Expand Down

0 comments on commit 66b7138

Please sign in to comment.