Browse Source

KVM: nSVM: propagate the NPF EXITINFO to the guest

This is similar to what the EPT code does with the exit qualification.
This allows the guest to see a valid value for bits 33:32.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini 11 years ago
parent
commit
5e35251951
2 changed files with 32 additions and 4 deletions
  1. 12 0
      arch/x86/kvm/paging_tmpl.h
  2. 20 4
      arch/x86/kvm/svm.c

+ 12 - 0
arch/x86/kvm/paging_tmpl.h

@@ -322,8 +322,20 @@ retry_walk:
 
 
 		real_gfn = mmu->translate_gpa(vcpu, gfn_to_gpa(table_gfn),
 		real_gfn = mmu->translate_gpa(vcpu, gfn_to_gpa(table_gfn),
 					      PFERR_USER_MASK|PFERR_WRITE_MASK);
 					      PFERR_USER_MASK|PFERR_WRITE_MASK);
+
+		/*
+		 * FIXME: This can happen if emulation (for of an INS/OUTS
+		 * instruction) triggers a nested page fault.  The exit
+		 * qualification / exit info field will incorrectly have
+		 * "guest page access" as the nested page fault's cause,
+		 * instead of "guest page structure access".  To fix this,
+		 * the x86_exception struct should be augmented with enough
+		 * information to fix the exit_qualification or exit_info_1
+		 * fields.
+		 */
 		if (unlikely(real_gfn == UNMAPPED_GVA))
 		if (unlikely(real_gfn == UNMAPPED_GVA))
 			goto error;
 			goto error;
+
 		real_gfn = gpa_to_gfn(real_gfn);
 		real_gfn = gpa_to_gfn(real_gfn);
 
 
 		host_addr = gfn_to_hva_prot(vcpu->kvm, real_gfn,
 		host_addr = gfn_to_hva_prot(vcpu->kvm, real_gfn,

+ 20 - 4
arch/x86/kvm/svm.c

@@ -1974,10 +1974,26 @@ static void nested_svm_inject_npf_exit(struct kvm_vcpu *vcpu,
 {
 {
 	struct vcpu_svm *svm = to_svm(vcpu);
 	struct vcpu_svm *svm = to_svm(vcpu);
 
 
-	svm->vmcb->control.exit_code = SVM_EXIT_NPF;
-	svm->vmcb->control.exit_code_hi = 0;
-	svm->vmcb->control.exit_info_1 = fault->error_code;
-	svm->vmcb->control.exit_info_2 = fault->address;
+	if (svm->vmcb->control.exit_code != SVM_EXIT_NPF) {
+		/*
+		 * TODO: track the cause of the nested page fault, and
+		 * correctly fill in the high bits of exit_info_1.
+		 */
+		svm->vmcb->control.exit_code = SVM_EXIT_NPF;
+		svm->vmcb->control.exit_code_hi = 0;
+		svm->vmcb->control.exit_info_1 = (1ULL << 32);
+		svm->vmcb->control.exit_info_2 = fault->address;
+	}
+
+	svm->vmcb->control.exit_info_1 &= ~0xffffffffULL;
+	svm->vmcb->control.exit_info_1 |= fault->error_code;
+
+	/*
+	 * The present bit is always zero for page structure faults on real
+	 * hardware.
+	 */
+	if (svm->vmcb->control.exit_info_1 & (2ULL << 32))
+		svm->vmcb->control.exit_info_1 &= ~1;
 
 
 	nested_svm_vmexit(svm);
 	nested_svm_vmexit(svm);
 }
 }