KVM: x86/mmu: Document and enforce MMU-writable and Host-writable invariants
SPTEs are tagged with software-only bits to indicate if it is "MMU-writable" and "Host-writable". These bits are used to determine why KVM has marked an SPTE as read-only. Document these bits and their invariants, and enforce the invariants with new WARNs in spte_can_locklessly_be_made_writable() to ensure they are not accidentally violated in the future. Opportunistically move DEFAULT_SPTE_{MMU,HOST}_WRITABLE next to EPT_SPTE_{MMU,HOST}_WRITABLE since the new documentation applies to both. No functional change intended. Signed-off-by: David Matlack <dmatlack@google.com> Message-Id: <20220113233020.3986005-4-dmatlack@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
f082d86ea6
commit
5f16bcac6e
|
@ -60,10 +60,6 @@ static_assert(SPTE_TDP_AD_ENABLED_MASK == 0);
|
||||||
(((address) >> PT64_LEVEL_SHIFT(level)) & ((1 << PT64_LEVEL_BITS) - 1))
|
(((address) >> PT64_LEVEL_SHIFT(level)) & ((1 << PT64_LEVEL_BITS) - 1))
|
||||||
#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
|
||||||
|
|
||||||
/* Bits 9 and 10 are ignored by all non-EPT PTEs. */
|
|
||||||
#define DEFAULT_SPTE_HOST_WRITEABLE BIT_ULL(9)
|
|
||||||
#define DEFAULT_SPTE_MMU_WRITEABLE BIT_ULL(10)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The mask/shift to use for saving the original R/X bits when marking the PTE
|
* The mask/shift to use for saving the original R/X bits when marking the PTE
|
||||||
* as not-present for access tracking purposes. We do not save the W bit as the
|
* as not-present for access tracking purposes. We do not save the W bit as the
|
||||||
|
@ -78,6 +74,35 @@ static_assert(SPTE_TDP_AD_ENABLED_MASK == 0);
|
||||||
SHADOW_ACC_TRACK_SAVED_BITS_SHIFT)
|
SHADOW_ACC_TRACK_SAVED_BITS_SHIFT)
|
||||||
static_assert(!(SPTE_TDP_AD_MASK & SHADOW_ACC_TRACK_SAVED_MASK));
|
static_assert(!(SPTE_TDP_AD_MASK & SHADOW_ACC_TRACK_SAVED_MASK));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* *_SPTE_HOST_WRITEABLE (aka Host-writable) indicates whether the host permits
|
||||||
|
* writes to the guest page mapped by the SPTE. This bit is cleared on SPTEs
|
||||||
|
* that map guest pages in read-only memslots and read-only VMAs.
|
||||||
|
*
|
||||||
|
* Invariants:
|
||||||
|
* - If Host-writable is clear, PT_WRITABLE_MASK must be clear.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* *_SPTE_MMU_WRITEABLE (aka MMU-writable) indicates whether the shadow MMU
|
||||||
|
* allows writes to the guest page mapped by the SPTE. This bit is cleared when
|
||||||
|
* the guest page mapped by the SPTE contains a page table that is being
|
||||||
|
* monitored for shadow paging. In this case the SPTE can only be made writable
|
||||||
|
* by unsyncing the shadow page under the mmu_lock.
|
||||||
|
*
|
||||||
|
* Invariants:
|
||||||
|
* - If MMU-writable is clear, PT_WRITABLE_MASK must be clear.
|
||||||
|
* - If MMU-writable is set, Host-writable must be set.
|
||||||
|
*
|
||||||
|
* If MMU-writable is set, PT_WRITABLE_MASK is normally set but can be cleared
|
||||||
|
* to track writes for dirty logging. For such SPTEs, KVM will locklessly set
|
||||||
|
* PT_WRITABLE_MASK upon the next write from the guest and record the write in
|
||||||
|
* the dirty log (see fast_page_fault()).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Bits 9 and 10 are ignored by all non-EPT PTEs. */
|
||||||
|
#define DEFAULT_SPTE_HOST_WRITEABLE BIT_ULL(9)
|
||||||
|
#define DEFAULT_SPTE_MMU_WRITEABLE BIT_ULL(10)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Low ignored bits are at a premium for EPT, use high ignored bits, taking care
|
* Low ignored bits are at a premium for EPT, use high ignored bits, taking care
|
||||||
* to not overlap the A/D type mask or the saved access bits of access-tracked
|
* to not overlap the A/D type mask or the saved access bits of access-tracked
|
||||||
|
@ -316,8 +341,13 @@ static __always_inline bool is_rsvd_spte(struct rsvd_bits_validate *rsvd_check,
|
||||||
|
|
||||||
static inline bool spte_can_locklessly_be_made_writable(u64 spte)
|
static inline bool spte_can_locklessly_be_made_writable(u64 spte)
|
||||||
{
|
{
|
||||||
return (spte & shadow_host_writable_mask) &&
|
if (spte & shadow_mmu_writable_mask) {
|
||||||
(spte & shadow_mmu_writable_mask);
|
WARN_ON_ONCE(!(spte & shadow_host_writable_mask));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON_ONCE(spte & PT_WRITABLE_MASK);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u64 get_mmio_spte_generation(u64 spte)
|
static inline u64 get_mmio_spte_generation(u64 spte)
|
||||||
|
|
Loading…
Reference in New Issue