From: David Woodhouse The TLFS leaves DR6 sharing between VTLs processor-dependent and tells the guest which way it went in HvRegisterVsmCapabilities.Dr6Shared. KVM cannot know what the VMM advertises there, so make it a share bit and let the VMM keep the two consistent: KVM_PLANE_SHARE_DR6. The twins keep their own arch.dr6, kept equal whenever no plane is in the guest: - after each plane's run in the KVM_RUN plane loop, its DR6 is copied to the twins. By then the exit has synced any DR6 the guest owned in hardware back into arch.dr6 (sync_dirty_debug_regs() on VMX and SVM, under KVM_DEBUGREG_WONT_EXIT); otherwise DR accesses are intercepted and arch.dr6 is the only copy. A VTL switch done in the kernel returns through the same loop; - KVM_SET_DEBUGREGS copies the new value to the twins at once; - a twin created later starts with plane 0's DR6, as for XCR0. Converting every arch.dr6 user to a shared location instead would touch the vendor, nested, SMM and exception code for no gain. Writers audited for the copy-after-run rule: - MOV to DR6 (emulated, or direct under KVM_DEBUGREG_WONT_EXIT), #DB payload delivery (kvm_deliver_exception_payload()), RSM, INIT and nested VMRUN on SVM all write the DR6 of the plane that is running, so the next copy carries them; - a #DB still pending on a plane that is switched away from updates DR6 when that plane next takes it, which matches a debug exception that has not been delivered yet; - host-side debugging (KVM_SET_GUEST_DEBUG) reports in kvm_run->debug.arch.dr6 and leaves arch.dr6 alone; - vCPU ioctls on twins take the shared vCPU mutex, so none of them can observe a plane mid-run. DR0-DR3 and DR7 stay per plane. Signed-off-by: David Woodhouse --- Documentation/virt/kvm/api.rst | 5 +++++ arch/x86/include/uapi/asm/kvm.h | 1 + arch/x86/kvm/regs.c | 1 + arch/x86/kvm/x86.c | 25 +++++++++++++++++++++++++ arch/x86/kvm/x86.h | 4 +++- 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 22dc3c793bc4..351cc7448fe1 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -9353,6 +9353,11 @@ is set once. The bits are: - KVM_PLANE_SHARE_XCR0: XCR0, whether written by the guest with XSETBV or with KVM_SET_XCRS on any plane's vCPU fd. A plane vCPU created later starts with plane 0's value. +- KVM_PLANE_SHARE_DR6: DR6. A guest write, or a debug exception, in one + plane is seen by the others once that plane returns from KVM_RUN or + switches to another plane; a KVM_SET_DEBUGREGS on any plane's vCPU fd is + seen at once. A plane vCPU created later starts with plane 0's value. + DR0-DR3 and DR7 stay per plane. 8. Other capabilities. ====================== diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index edec00979acf..dbfdc1188c20 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -924,6 +924,7 @@ struct kvm_sev_snp_launch_finish { /* KVM_CAP_PLANES args[1]: state the planes of a vCPU share */ #define KVM_PLANE_SHARE_XCR0 _BITULL(0) +#define KVM_PLANE_SHARE_DR6 _BITULL(1) struct kvm_hyperv_eventfd { __u32 conn_id; diff --git a/arch/x86/kvm/regs.c b/arch/x86/kvm/regs.c index 3621f1a47d37..8a856e478095 100644 --- a/arch/x86/kvm/regs.c +++ b/arch/x86/kvm/regs.c @@ -876,6 +876,7 @@ int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu, kvm_update_dr0123(vcpu); vcpu->arch.dr6 = dbgregs->dr6; + kvm_plane_share_dr6(vcpu); vcpu->arch.dr7 = dbgregs->dr7; kvm_update_dr7(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ceec10d116a9..7206ee889e9b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9362,6 +9362,26 @@ static int __kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, bool held) return r; } +/* + * With KVM_PLANE_SHARE_DR6, every twin holds the same DR6 whenever no plane + * is in the guest. Copy it from @vcpu to its twins after @vcpu changed it: + * after an ioctl write, or after @vcpu ran, by which time its exit has + * synced any guest-owned hardware DR6 back into arch.dr6 + * (sync_dirty_debug_regs() on both vendors). + */ +void kvm_plane_share_dr6(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu *twin; + int i; + + if (!kvm_plane_shares(vcpu->kvm, KVM_PLANE_SHARE_DR6) || + vcpu->common->vcpus[vcpu->plane_level] != vcpu) + return; + + vcpu_for_each_plane(vcpu->common, i, twin) + twin->arch.dr6 = vcpu->arch.dr6; +} + int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu_plane0) { struct kvm_vcpu_common *common = vcpu_plane0->common; @@ -9374,6 +9394,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu_plane0) common->plane_switch = false; ret = __kvm_arch_vcpu_ioctl_run(vcpu, held); + kvm_plane_share_dr6(vcpu); if (ret) break; } while (vcpu_plane0->common->plane_switch); @@ -9854,6 +9875,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) if (kvm_plane_shares(vcpu->kvm, KVM_PLANE_SHARE_XCR0) && vcpu->plane_level && vcpu->common->vcpus[0]) vcpu->arch.xcr0 = vcpu->common->vcpus[0]->arch.xcr0; + /* Likewise for a shared DR6. */ + if (kvm_plane_shares(vcpu->kvm, KVM_PLANE_SHARE_DR6) && + vcpu->plane_level && vcpu->common->vcpus[0]) + vcpu->arch.dr6 = vcpu->common->vcpus[0]->arch.dr6; kvm_init_mmu(vcpu); vcpu_put(vcpu); return 0; diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index aa8abd219507..4da224eb1745 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -927,11 +927,13 @@ unsigned kvm_x86_default_max_planes(struct kvm *kvm); * The per-vCPU state that userspace may ask the planes of a vCPU to share, * in args[1] of KVM_ENABLE_CAP(KVM_CAP_PLANES). */ -#define KVM_PLANE_SHARE_VALID KVM_PLANE_SHARE_XCR0 +#define KVM_PLANE_SHARE_VALID (KVM_PLANE_SHARE_XCR0 | KVM_PLANE_SHARE_DR6) static inline bool kvm_plane_shares(struct kvm *kvm, u64 state) { return READ_ONCE(kvm->arch.plane_share) & state; } +void kvm_plane_share_dr6(struct kvm_vcpu *vcpu); + #endif -- 2.50.1