Skip to content

Commit

Permalink
kvm: x86/mmu: Support MMIO in the TDP MMU
Browse files Browse the repository at this point in the history
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: Ben Gardon <bgardon@google.com>
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Message-Id: <20201014182700.2888246-19-bgardon@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
Ben Gardon authored and Paolo Bonzini committed Oct 23, 2020
1 parent 46044f7 commit 95fb5b0
Showing 3 changed files with 72 additions and 21 deletions.
70 changes: 49 additions & 21 deletions arch/x86/kvm/mmu/mmu.c
Original file line number Diff line number Diff line change
@@ -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 true if reserved bit is detected on spte. */
static bool
walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
/*
* Return the level of the lowest level SPTE added to sptes.
* That SPTE may be non-present.
*/
static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
{
struct kvm_shadow_walk_iterator iterator;
u64 sptes[PT64_ROOT_MAX_LEVEL], spte = 0ull;
struct rsvd_bits_validate *rsvd_check;
int root, leaf;
bool reserved = false;
int leaf = vcpu->arch.mmu->root_level;
u64 spte;

rsvd_check = &vcpu->arch.mmu->shadow_zero_check;

walk_shadow_page_lockless_begin(vcpu);

for (shadow_walk_init(&iterator, vcpu, addr),
leaf = root = iterator.level;
for (shadow_walk_init(&iterator, vcpu, addr);
shadow_walk_okay(&iterator);
__shadow_walk_next(&iterator, spte)) {
leaf = iterator.level;
spte = mmu_spte_get_lockless(iterator.sptep);

sptes[leaf - 1] = spte;
leaf--;

if (!is_shadow_present_pte(spte))
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
* reserved bit and EPT's invalid memtype/XWR checks to avoid
* adding a Jcc in the loop.
*/
reserved |= __is_bad_mt_xwr(rsvd_check, spte) |
__is_rsvd_bits_set(rsvd_check, spte, iterator.level);
reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level - 1]) |
__is_rsvd_bits_set(rsvd_check, sptes[level - 1],
level);
}

walk_shadow_page_lockless_end(vcpu);

if (reserved) {
pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n",
__func__, addr);
while (root > leaf) {
for (level = root; level >= leaf; level--)
pr_err("------ spte 0x%llx level %d.\n",
sptes[root - 1], root);
root--;
}
sptes[level - 1], level);
}

*sptep = spte;
*sptep = sptes[leaf - 1];

return reserved;
}

@@ -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))
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))
return -EINVAL;

21 changes: 21 additions & 0 deletions arch/x86/kvm/mmu/tdp_mmu.c
Original file line number Diff line number Diff line change
@@ -7,7 +7,10 @@
#include "tdp_mmu.h"
#include "spte.h"

#ifdef CONFIG_X86_64
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)
{
@@ -1128,3 +1131,21 @@ bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
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;
}
2 changes: 2 additions & 0 deletions arch/x86/kvm/mmu/tdp_mmu.h
Original file line number Diff line number Diff line change
@@ -43,4 +43,6 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,

bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
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 */

0 comments on commit 95fb5b0

Please sign in to comment.