History

kvm: vmx: Defer setting of DR6 until #DB delivery

When exception payloads are enabled by userspace (which is not yet possible) and a #DB is raised in L2, defer the setting of DR6 until later. Under VMX, this allows the L1 hypervisor to intercept the fault before DR6 is modified. Under SVM, DR6 is modified before L1 can intercept the fault (as has always been the case with DR7). Note that the payload associated with a #DB exception includes only the "new DR6 bits." When the payload is delievered, DR6.B0-B3 will be cleared and DR6.RTM will be set prior to merging in the new DR6 bits. Also note that bit 16 in the "new DR6 bits" is set to indicate that a debug exception (#DB) or a breakpoint exception (#BP) occurred inside an RTM region while advanced debugging of RTM transactional regions was enabled. Though the reverse of DR6.RTM, this makes the #DB payload field compatible with both the pending debug exceptions field under VMX and the exit qualification for #DB exceptions under VMX.

Jim Mattson committed 8 years ago

Commit f10c7291 parent da998b42 files · +62 −31Browse files

Unified · Split

2 files
  1. arch/x86/kvm/vmx.c +10 −14
  2. arch/x86/kvm/x86.c +52 −17
arch/x86/kvm/vmx.c+10−14
@@ -3305,21 +3305,17 @@ static int nested_vmx_check_exception(struct kvm_vcpu *vcpu, unsigned long *exit · 3304 unmodified lines
3305 *exit_qual = has_payload ? payload : vcpu->arch.cr2;3305 *exit_qual = has_payload ? payload : vcpu->arch.cr2;
3306 return 1;3306 return 1;
3307 }3307 }
3308 } else {3308 } else if (vmcs12->exception_bitmap & (1u << nr)) {
3309 /*3309 if (nr == DB_VECTOR) {
3310 * FIXME: we must not write DR6 when L1 intercepts an3310 if (!has_payload) {
3311 * L2 #DB exception.3311 payload = vcpu->arch.dr6;
3312 */3312 payload &= ~(DR6_FIXED_1 | DR6_BT);
3313 if (vmcs12->exception_bitmap & (1u << nr)) {3313 payload ^= DR6_RTM;
3314 if (nr == DB_VECTOR) {
3315 *exit_qual = vcpu->arch.dr6;
3316 *exit_qual &= ~(DR6_FIXED_1 | DR6_BT);
3317 *exit_qual ^= DR6_RTM;
3318 } else {
3319 *exit_qual = 0;
3320 }3314 }
3321 return 1;3315 *exit_qual = payload;
3322 }3316 } else
3317 *exit_qual = 0;
3318 return 1;
3323 }3319 }
3324 3320
3325 return 0;3321 return 0;
arch/x86/kvm/x86.c+52−17
@@ -410,6 +410,28 @@ void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu) · 409 unmodified lines
410 return;410 return;
411 411
412 switch (nr) {412 switch (nr) {
413 case DB_VECTOR:
414 /*
415 * "Certain debug exceptions may clear bit 0-3. The
416 * remaining contents of the DR6 register are never
417 * cleared by the processor".
418 */
419 vcpu->arch.dr6 &= ~DR_TRAP_BITS;
420 /*
421 * DR6.RTM is set by all #DB exceptions that don't clear it.
422 */
423 vcpu->arch.dr6 |= DR6_RTM;
424 vcpu->arch.dr6 |= payload;
425 /*
426 * Bit 16 should be set in the payload whenever the #DB
427 * exception should clear DR6.RTM. This makes the payload
428 * compatible with the pending debug exceptions under VMX.
429 * Though not currently documented in the SDM, this also
430 * makes the payload compatible with the exit qualification
431 * for #DB exceptions under VMX.
432 */
433 vcpu->arch.dr6 ^= payload & DR6_RTM;
434 break;
413 case PF_VECTOR:435 case PF_VECTOR:
414 vcpu->arch.cr2 = payload;436 vcpu->arch.cr2 = payload;
415 break;437 break;
@@ -464,11 +486,13 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, · 48 unmodified lines
464 /*486 /*
465 * In guest mode, payload delivery should be deferred,487 * In guest mode, payload delivery should be deferred,
466 * so that the L1 hypervisor can intercept #PF before488 * so that the L1 hypervisor can intercept #PF before
467 * CR2 is modified. However, for ABI compatibility489 * CR2 is modified (or intercept #DB before DR6 is
468 * with KVM_GET_VCPU_EVENTS and KVM_SET_VCPU_EVENTS,490 * modified under nVMX). However, for ABI
469 * we can't delay payload delivery unless userspace491 * compatibility with KVM_GET_VCPU_EVENTS and
470 * has enabled this functionality via the per-VM492 * KVM_SET_VCPU_EVENTS, we can't delay payload
471 * capability, KVM_CAP_EXCEPTION_PAYLOAD.493 * delivery unless userspace has enabled this
494 * functionality via the per-VM capability,
495 * KVM_CAP_EXCEPTION_PAYLOAD.
472 */496 */
473 if (!vcpu->kvm->arch.exception_payload_enabled ||497 if (!vcpu->kvm->arch.exception_payload_enabled ||
474 !is_guest_mode(vcpu))498 !is_guest_mode(vcpu))
@@ -518,6 +542,12 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned nr) · 43 unmodified lines
518}542}
519EXPORT_SYMBOL_GPL(kvm_requeue_exception);543EXPORT_SYMBOL_GPL(kvm_requeue_exception);
520 544
545static void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr,
546 unsigned long payload)
547{
548 kvm_multiple_exception(vcpu, nr, false, 0, true, payload, false);
549}
550
521static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr,551static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr,
522 u32 error_code, unsigned long payload)552 u32 error_code, unsigned long payload)
523{553{
@@ -6156,14 +6186,7 @@ static void kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu, int *r) · 5632 unmodified lines
6156 kvm_run->exit_reason = KVM_EXIT_DEBUG;6186 kvm_run->exit_reason = KVM_EXIT_DEBUG;
6157 *r = EMULATE_USER_EXIT;6187 *r = EMULATE_USER_EXIT;
6158 } else {6188 } else {
6159 /*6189 kvm_queue_exception_p(vcpu, DB_VECTOR, DR6_BS);
6160 * "Certain debug exceptions may clear bit 0-3. The
6161 * remaining contents of the DR6 register are never
6162 * cleared by the processor".
6163 */
6164 vcpu->arch.dr6 &= ~15;
6165 vcpu->arch.dr6 |= DR6_BS | DR6_RTM;
6166 kvm_queue_exception(vcpu, DB_VECTOR);
6167 }6190 }
6168}6191}
6169 6192
@@ -7102,10 +7125,22 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool req_int_win) · 932 unmodified lines
7102 __kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) |7125 __kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) |
7103 X86_EFLAGS_RF);7126 X86_EFLAGS_RF);
7104 7127
7105 if (vcpu->arch.exception.nr == DB_VECTOR &&7128 if (vcpu->arch.exception.nr == DB_VECTOR) {
7106 (vcpu->arch.dr7 & DR7_GD)) {7129 /*
7107 vcpu->arch.dr7 &= ~DR7_GD;7130 * This code assumes that nSVM doesn't use
7108 kvm_update_dr7(vcpu);7131 * check_nested_events(). If it does, the
7132 * DR6/DR7 changes should happen before L1
7133 * gets a #VMEXIT for an intercepted #DB in
7134 * L2. (Under VMX, on the other hand, the
7135 * DR6/DR7 changes should not happen in the
7136 * event of a VM-exit to L1 for an intercepted
7137 * #DB in L2.)
7138 */
7139 kvm_deliver_exception_payload(vcpu);
7140 if (vcpu->arch.dr7 & DR7_GD) {
7141 vcpu->arch.dr7 &= ~DR7_GD;
7142 kvm_update_dr7(vcpu);
7143 }
7109 }7144 }
7110 7145
7111 kvm_x86_ops->queue_exception(vcpu);7146 kvm_x86_ops->queue_exception(vcpu);