Re: [PATCH 04/22] KVM: x86/mmu: shuffle high bits of SPTEs in preparation for MBEC

From: Huang, Kai

Date: Wed Mar 25 2026 - 00:37:11 EST


On Sat, 2026-03-21 at 01:09 +0100, Paolo Bonzini wrote:
> Access tracking will need to save bit 10 when MBEC is enabled.
> Right now it is simply shifting the R and X bits into bits 54 and 56,
> but bit 10 would not fit with the same scheme. Reorganize the
> high bits so that access tracking will use bits 52, 54 and 62.

So that we can continue to do a simple:

spte |= (spte & SHADOW_ACC_TRACK_SAVED_BITS_MASK) <<
SHADOW_ACC_TRACK_SAVED_BITS_SHIFT;
?

> As a side effect, the free bits are compacted slightly, with
> 56-59 still unused.
>
> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> ---
> arch/x86/kvm/mmu/spte.h | 20 +++++++++++++++-----
> 1 file changed, 15 insertions(+), 5 deletions(-)
>
> diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h
> index b60666778f61..7223a61b1260 100644
> --- a/arch/x86/kvm/mmu/spte.h
> +++ b/arch/x86/kvm/mmu/spte.h
> @@ -17,10 +17,20 @@
> */
> #define SPTE_MMU_PRESENT_MASK BIT_ULL(11)
>
> +/*
> + * The ignored high bits are allocated as follows:
> + * - bits 52, 54: saved X-R bits for access tracking when EPT does not have A/D
> + * - bits 53 (EPT only): host writable
> + * - bits 55 (EPT only): MMU-writable
> + * - bits 56-59: unused
> + * - bits 60-61: type of A/D tracking
> + * - bits 62: unused
> + */
> +
> /*
> * TDP SPTES (more specifically, EPT SPTEs) may not have A/D bits, and may also
> * be restricted to using write-protection (for L2 when CPU dirty logging, i.e.
> - * PML, is enabled). Use bits 52 and 53 to hold the type of A/D tracking that
> + * PML, is enabled). Use bits 60 and 61 to hold the type of A/D tracking that
> * is must be employed for a given TDP SPTE.
> *
> * Note, the "enabled" mask must be '0', as bits 62:52 are _reserved_ for PAE
> @@ -29,7 +39,7 @@
> * TDP with CPU dirty logging (PML). If NPT ever gains PML-like support, it
> * must be restricted to 64-bit KVM.
> */
> -#define SPTE_TDP_AD_SHIFT 52
> +#define SPTE_TDP_AD_SHIFT 60
> #define SPTE_TDP_AD_MASK (3ULL << SPTE_TDP_AD_SHIFT)
> #define SPTE_TDP_AD_ENABLED (0ULL << SPTE_TDP_AD_SHIFT)
> #define SPTE_TDP_AD_DISABLED (1ULL << SPTE_TDP_AD_SHIFT)
> @@ -65,7 +75,7 @@ static_assert(SPTE_TDP_AD_ENABLED == 0);
> */
> #define SHADOW_ACC_TRACK_SAVED_BITS_MASK (SPTE_EPT_READABLE_MASK | \
> SPTE_EPT_EXECUTABLE_MASK)
> -#define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 54
> +#define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 52
> #define SHADOW_ACC_TRACK_SAVED_MASK (SHADOW_ACC_TRACK_SAVED_BITS_MASK << \
> SHADOW_ACC_TRACK_SAVED_BITS_SHIFT)
> static_assert(!(SPTE_TDP_AD_MASK & SHADOW_ACC_TRACK_SAVED_MASK));
> @@ -84,8 +94,8 @@ static_assert(!(SPTE_TDP_AD_MASK & SHADOW_ACC_TRACK_SAVED_MASK));
> * to not overlap the A/D type mask or the saved access bits of access-tracked
> * SPTEs when A/D bits are disabled.
> */
> -#define EPT_SPTE_HOST_WRITABLE BIT_ULL(57)
> -#define EPT_SPTE_MMU_WRITABLE BIT_ULL(58)
> +#define EPT_SPTE_HOST_WRITABLE BIT_ULL(53)
> +#define EPT_SPTE_MMU_WRITABLE BIT_ULL(55)

It's a bit dangerous to put HOST_WRITABLE bit between the R-X bits, but we
don't keep the W bit of the to-be-tracked SPTE and we are going to do a
simple shift to preserve the R, X and XU bits anyway, so should be fine.

Acked-by: Kai Huang <kai.huang@xxxxxxxxx>