Commit 95fb5b02 authored by Ben Gardon's avatar Ben Gardon Committed by Paolo Bonzini

kvm: x86/mmu: Support MMIO in the TDP MMU

In order to support MMIO, KVM must be able to walk the TDP paging
structures to find mappings for a given GFN. Support this walk for
the TDP MMU.

Tested by running kvm-unit-tests and KVM selftests on an Intel Haswell
machine. This series introduced no new failures.

This series can be viewed in Gerrit at:
	https://linux-review.googlesource.com/c/virt/kvm/kvm/+/2538

v2: Thanks to Dan Carpenter and kernel test robot for finding that root
was used uninitialized in get_mmio_spte.
Signed-off-by: default avatarBen Gardon <bgardon@google.com>
Reported-by: default avatarkernel test robot <lkp@intel.com>
Reported-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Message-Id: <20201014182700.2888246-19-bgardon@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 46044f72
...@@ -3479,54 +3479,82 @@ static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct) ...@@ -3479,54 +3479,82 @@ static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct)
return vcpu_match_mmio_gva(vcpu, addr); return vcpu_match_mmio_gva(vcpu, addr);
} }
/* return true if reserved bit is detected on spte. */ /*
static bool * Return the level of the lowest level SPTE added to sptes.
walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) * That SPTE may be non-present.
*/
static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
{ {
struct kvm_shadow_walk_iterator iterator; struct kvm_shadow_walk_iterator iterator;
u64 sptes[PT64_ROOT_MAX_LEVEL], spte = 0ull; int leaf = vcpu->arch.mmu->root_level;
struct rsvd_bits_validate *rsvd_check; u64 spte;
int root, leaf;
bool reserved = false;
rsvd_check = &vcpu->arch.mmu->shadow_zero_check;
walk_shadow_page_lockless_begin(vcpu); walk_shadow_page_lockless_begin(vcpu);
for (shadow_walk_init(&iterator, vcpu, addr), for (shadow_walk_init(&iterator, vcpu, addr);
leaf = root = iterator.level;
shadow_walk_okay(&iterator); shadow_walk_okay(&iterator);
__shadow_walk_next(&iterator, spte)) { __shadow_walk_next(&iterator, spte)) {
leaf = iterator.level;
spte = mmu_spte_get_lockless(iterator.sptep); spte = mmu_spte_get_lockless(iterator.sptep);
sptes[leaf - 1] = spte; sptes[leaf - 1] = spte;
leaf--;
if (!is_shadow_present_pte(spte)) if (!is_shadow_present_pte(spte))
break; break;
}
walk_shadow_page_lockless_end(vcpu);
return leaf;
}
/* return true if reserved bit is detected on spte. */
static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
{
u64 sptes[PT64_ROOT_MAX_LEVEL];
struct rsvd_bits_validate *rsvd_check;
int root = vcpu->arch.mmu->root_level;
int leaf;
int level;
bool reserved = false;
if (!VALID_PAGE(vcpu->arch.mmu->root_hpa)) {
*sptep = 0ull;
return reserved;
}
if (is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa))
leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes);
else
leaf = get_walk(vcpu, addr, sptes);
rsvd_check = &vcpu->arch.mmu->shadow_zero_check;
for (level = root; level >= leaf; level--) {
if (!is_shadow_present_pte(sptes[level - 1]))
break;
/* /*
* Use a bitwise-OR instead of a logical-OR to aggregate the * Use a bitwise-OR instead of a logical-OR to aggregate the
* reserved bit and EPT's invalid memtype/XWR checks to avoid * reserved bit and EPT's invalid memtype/XWR checks to avoid
* adding a Jcc in the loop. * adding a Jcc in the loop.
*/ */
reserved |= __is_bad_mt_xwr(rsvd_check, spte) | reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level - 1]) |
__is_rsvd_bits_set(rsvd_check, spte, iterator.level); __is_rsvd_bits_set(rsvd_check, sptes[level - 1],
level);
} }
walk_shadow_page_lockless_end(vcpu);
if (reserved) { if (reserved) {
pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n", pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n",
__func__, addr); __func__, addr);
while (root > leaf) { for (level = root; level >= leaf; level--)
pr_err("------ spte 0x%llx level %d.\n", pr_err("------ spte 0x%llx level %d.\n",
sptes[root - 1], root); sptes[level - 1], level);
root--;
}
} }
*sptep = spte; *sptep = sptes[leaf - 1];
return reserved; return reserved;
} }
...@@ -3538,7 +3566,7 @@ static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct) ...@@ -3538,7 +3566,7 @@ static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct)
if (mmio_info_in_cache(vcpu, addr, direct)) if (mmio_info_in_cache(vcpu, addr, direct))
return RET_PF_EMULATE; return RET_PF_EMULATE;
reserved = walk_shadow_page_get_mmio_spte(vcpu, addr, &spte); reserved = get_mmio_spte(vcpu, addr, &spte);
if (WARN_ON(reserved)) if (WARN_ON(reserved))
return -EINVAL; return -EINVAL;
......
...@@ -7,7 +7,10 @@ ...@@ -7,7 +7,10 @@
#include "tdp_mmu.h" #include "tdp_mmu.h"
#include "spte.h" #include "spte.h"
#ifdef CONFIG_X86_64
static bool __read_mostly tdp_mmu_enabled = false; static bool __read_mostly tdp_mmu_enabled = false;
module_param_named(tdp_mmu, tdp_mmu_enabled, bool, 0644);
#endif
static bool is_tdp_mmu_enabled(void) static bool is_tdp_mmu_enabled(void)
{ {
...@@ -1128,3 +1131,21 @@ bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm, ...@@ -1128,3 +1131,21 @@ bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
return spte_set; return spte_set;
} }
/*
* Return the level of the lowest level SPTE added to sptes.
* That SPTE may be non-present.
*/
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
{
struct tdp_iter iter;
struct kvm_mmu *mmu = vcpu->arch.mmu;
int leaf = vcpu->arch.mmu->shadow_root_level;
gfn_t gfn = addr >> PAGE_SHIFT;
tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) {
leaf = iter.level;
sptes[leaf - 1] = iter.old_spte;
}
return leaf;
}
...@@ -43,4 +43,6 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm, ...@@ -43,4 +43,6 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm, bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
struct kvm_memory_slot *slot, gfn_t gfn); struct kvm_memory_slot *slot, gfn_t gfn);
int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes);
#endif /* __KVM_X86_MMU_TDP_MMU_H */ #endif /* __KVM_X86_MMU_TDP_MMU_H */
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