Message ID | 20240507155817.3951344-8-pbonzini@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | KVM: x86/mmu: Page fault and MMIO cleanups | expand |
On 5/7/2024 11:58 PM, Paolo Bonzini wrote: > From: Sean Christopherson <seanjc@google.com> > > Add and use a synthetic, KVM-defined page fault error code to indicate > whether a fault is to private vs. shared memory. TDX and SNP have > different mechanisms for reporting private vs. shared, and KVM's > software-protected VMs have no mechanism at all. Usurp an error code > flag to avoid having to plumb another parameter to kvm_mmu_page_fault() > and friends. > > Alternatively, KVM could borrow AMD's PFERR_GUEST_ENC_MASK, i.e. set it > for TDX and software-protected VMs as appropriate, but that would require > *clearing* the flag for SEV and SEV-ES VMs, which support encrypted > memory at the hardware layer, but don't utilize private memory at the > KVM layer. > > Opportunistically add a comment to call out that the logic for software- > protected VMs is (and was before this commit) broken for nested MMUs, i.e. > for nested TDP, as the GPA is an L2 GPA. Punt on trying to play nice with > nested MMUs as there is a _lot_ of functionality that simply doesn't work > for software-protected VMs, e.g. all of the paths where KVM accesses guest > memory need to be updated to be aware of private vs. shared memory. > > Signed-off-by: Sean Christopherson <seanjc@google.com> > Message-Id: <20240228024147.41573-6-seanjc@google.com> > Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com> > --- > arch/x86/include/asm/kvm_host.h | 7 ++++++- > arch/x86/kvm/mmu/mmu.c | 14 ++++++++++++++ > arch/x86/kvm/mmu/mmu_internal.h | 2 +- > 3 files changed, 21 insertions(+), 2 deletions(-) > > diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h > index 12e727301262..0dc755a6dc0c 100644 > --- a/arch/x86/include/asm/kvm_host.h > +++ b/arch/x86/include/asm/kvm_host.h > @@ -273,7 +273,12 @@ enum x86_intercept_stage; > * when emulating instructions that triggers implicit access. > */ > #define PFERR_IMPLICIT_ACCESS BIT_ULL(48) > -#define PFERR_SYNTHETIC_MASK (PFERR_IMPLICIT_ACCESS) > +/* > + * PRIVATE_ACCESS is a KVM-defined flag us to indicate that a fault occurred > + * when the guest was accessing private memory. > + */ > +#define PFERR_PRIVATE_ACCESS BIT_ULL(49) > +#define PFERR_SYNTHETIC_MASK (PFERR_IMPLICIT_ACCESS | PFERR_PRIVATE_ACCESS) > > #define PFERR_NESTED_GUEST_PAGE (PFERR_GUEST_PAGE_MASK | \ > PFERR_WRITE_MASK | \ > diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c > index 3609167ba30e..eb041acec2dc 100644 > --- a/arch/x86/kvm/mmu/mmu.c > +++ b/arch/x86/kvm/mmu/mmu.c > @@ -5799,6 +5799,20 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err > if (WARN_ON_ONCE(!VALID_PAGE(vcpu->arch.mmu->root.hpa))) > return RET_PF_RETRY; > > + /* > + * Except for reserved faults (emulated MMIO is shared-only), set the > + * PFERR_PRIVATE_ACCESS flag for software-protected VMs based on the gfn's > + * current attributes, which are the source of truth for such VMs. Note, > + * this wrong for nested MMUs as the GPA is an L2 GPA, but KVM doesn't > + * currently supported nested virtualization (among many other things) > + * for software-protected VMs. > + */ > + if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && > + !(error_code & PFERR_RSVD_MASK) && > + vcpu->kvm->arch.vm_type == KVM_X86_SW_PROTECTED_VM && > + kvm_mem_is_private(vcpu->kvm, gpa_to_gfn(cr2_or_gpa))) > + error_code |= PFERR_PRIVATE_ACCESS; > + > r = RET_PF_INVALID; > if (unlikely(error_code & PFERR_RSVD_MASK)) { > r = handle_mmio_page_fault(vcpu, cr2_or_gpa, direct); > diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h > index 797b80f996a7..dfd9ff383663 100644 > --- a/arch/x86/kvm/mmu/mmu_internal.h > +++ b/arch/x86/kvm/mmu/mmu_internal.h > @@ -306,7 +306,7 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, > .max_level = KVM_MAX_HUGEPAGE_LEVEL, > .req_level = PG_LEVEL_4K, > .goal_level = PG_LEVEL_4K, > - .is_private = kvm_mem_is_private(vcpu->kvm, cr2_or_gpa >> PAGE_SHIFT), > + .is_private = err & PFERR_PRIVATE_ACCESS, > }; > int r; >
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 12e727301262..0dc755a6dc0c 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -273,7 +273,12 @@ enum x86_intercept_stage; * when emulating instructions that triggers implicit access. */ #define PFERR_IMPLICIT_ACCESS BIT_ULL(48) -#define PFERR_SYNTHETIC_MASK (PFERR_IMPLICIT_ACCESS) +/* + * PRIVATE_ACCESS is a KVM-defined flag us to indicate that a fault occurred + * when the guest was accessing private memory. + */ +#define PFERR_PRIVATE_ACCESS BIT_ULL(49) +#define PFERR_SYNTHETIC_MASK (PFERR_IMPLICIT_ACCESS | PFERR_PRIVATE_ACCESS) #define PFERR_NESTED_GUEST_PAGE (PFERR_GUEST_PAGE_MASK | \ PFERR_WRITE_MASK | \ diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 3609167ba30e..eb041acec2dc 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5799,6 +5799,20 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err if (WARN_ON_ONCE(!VALID_PAGE(vcpu->arch.mmu->root.hpa))) return RET_PF_RETRY; + /* + * Except for reserved faults (emulated MMIO is shared-only), set the + * PFERR_PRIVATE_ACCESS flag for software-protected VMs based on the gfn's + * current attributes, which are the source of truth for such VMs. Note, + * this wrong for nested MMUs as the GPA is an L2 GPA, but KVM doesn't + * currently supported nested virtualization (among many other things) + * for software-protected VMs. + */ + if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && + !(error_code & PFERR_RSVD_MASK) && + vcpu->kvm->arch.vm_type == KVM_X86_SW_PROTECTED_VM && + kvm_mem_is_private(vcpu->kvm, gpa_to_gfn(cr2_or_gpa))) + error_code |= PFERR_PRIVATE_ACCESS; + r = RET_PF_INVALID; if (unlikely(error_code & PFERR_RSVD_MASK)) { r = handle_mmio_page_fault(vcpu, cr2_or_gpa, direct); diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index 797b80f996a7..dfd9ff383663 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -306,7 +306,7 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, .max_level = KVM_MAX_HUGEPAGE_LEVEL, .req_level = PG_LEVEL_4K, .goal_level = PG_LEVEL_4K, - .is_private = kvm_mem_is_private(vcpu->kvm, cr2_or_gpa >> PAGE_SHIFT), + .is_private = err & PFERR_PRIVATE_ACCESS, }; int r;