Commit 47855da0 authored by David Matlack's avatar David Matlack Committed by Paolo Bonzini

KVM: x86/mmu: Extend make_huge_page_split_spte() for the shadow MMU

Currently make_huge_page_split_spte() assumes execute permissions can be
granted to any 4K SPTE when splitting huge pages. This is true for the
TDP MMU but is not necessarily true for the shadow MMU, since KVM may be
shadowing a non-executable huge page.

To fix this, pass in the role of the child shadow page where the huge
page will be split and derive the execution permission from that.  This
is correct because huge pages are always split with direct shadow page
and thus the shadow page role contains the correct access permissions.

No functional change intended.
Signed-off-by: default avatarDavid Matlack <dmatlack@google.com>
Message-Id: <20220516232138.1783324-19-dmatlack@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 6a97575d
...@@ -246,11 +246,10 @@ static u64 make_spte_executable(u64 spte) ...@@ -246,11 +246,10 @@ static u64 make_spte_executable(u64 spte)
* This is used during huge page splitting to build the SPTEs that make up the * This is used during huge page splitting to build the SPTEs that make up the
* new page table. * new page table.
*/ */
u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, int huge_level, u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, union kvm_mmu_page_role role,
int index) int index)
{ {
u64 child_spte; u64 child_spte;
int child_level;
if (WARN_ON_ONCE(!is_shadow_present_pte(huge_spte))) if (WARN_ON_ONCE(!is_shadow_present_pte(huge_spte)))
return 0; return 0;
...@@ -259,23 +258,23 @@ u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, int huge_level, ...@@ -259,23 +258,23 @@ u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, int huge_level,
return 0; return 0;
child_spte = huge_spte; child_spte = huge_spte;
child_level = huge_level - 1;
/* /*
* The child_spte already has the base address of the huge page being * The child_spte already has the base address of the huge page being
* split. So we just have to OR in the offset to the page at the next * split. So we just have to OR in the offset to the page at the next
* lower level for the given index. * lower level for the given index.
*/ */
child_spte |= (index * KVM_PAGES_PER_HPAGE(child_level)) << PAGE_SHIFT; child_spte |= (index * KVM_PAGES_PER_HPAGE(role.level)) << PAGE_SHIFT;
if (child_level == PG_LEVEL_4K) { if (role.level == PG_LEVEL_4K) {
child_spte &= ~PT_PAGE_SIZE_MASK; child_spte &= ~PT_PAGE_SIZE_MASK;
/* /*
* When splitting to a 4K page, mark the page executable as the * When splitting to a 4K page where execution is allowed, mark
* NX hugepage mitigation no longer applies. * the page executable as the NX hugepage mitigation no longer
* applies.
*/ */
if (is_nx_huge_page_enabled(kvm)) if ((role.access & ACC_EXEC_MASK) && is_nx_huge_page_enabled(kvm))
child_spte = make_spte_executable(child_spte); child_spte = make_spte_executable(child_spte);
} }
......
...@@ -421,8 +421,8 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, ...@@ -421,8 +421,8 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
unsigned int pte_access, gfn_t gfn, kvm_pfn_t pfn, unsigned int pte_access, gfn_t gfn, kvm_pfn_t pfn,
u64 old_spte, bool prefetch, bool can_unsync, u64 old_spte, bool prefetch, bool can_unsync,
bool host_writable, u64 *new_spte); bool host_writable, u64 *new_spte);
u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, int huge_level, u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte,
int index); union kvm_mmu_page_role role, int index);
u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled); u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled);
u64 make_mmio_spte(struct kvm_vcpu *vcpu, u64 gfn, unsigned int access); u64 make_mmio_spte(struct kvm_vcpu *vcpu, u64 gfn, unsigned int access);
u64 mark_spte_for_access_track(u64 spte); u64 mark_spte_for_access_track(u64 spte);
......
...@@ -1478,7 +1478,7 @@ static int tdp_mmu_split_huge_page(struct kvm *kvm, struct tdp_iter *iter, ...@@ -1478,7 +1478,7 @@ static int tdp_mmu_split_huge_page(struct kvm *kvm, struct tdp_iter *iter,
* not been linked in yet and thus is not reachable from any other CPU. * not been linked in yet and thus is not reachable from any other CPU.
*/ */
for (i = 0; i < SPTE_ENT_PER_PAGE; i++) for (i = 0; i < SPTE_ENT_PER_PAGE; i++)
sp->spt[i] = make_huge_page_split_spte(kvm, huge_spte, level, i); sp->spt[i] = make_huge_page_split_spte(kvm, huge_spte, sp->role, i);
/* /*
* Replace the huge spte with a pointer to the populated lower level * Replace the huge spte with a pointer to the populated lower level
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment