From nobody Mon Dec 1 22:32:12 2025 Received: from out-170.mta0.migadu.com (out-170.mta0.migadu.com [91.218.175.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 74A5E21578F for ; Thu, 27 Nov 2025 01:35:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207312; cv=none; b=BzkC0Xhpzkx7Q+cA1fXyygOi9R/hX3WwOPXJcTcf+Yp5SJNQ5NGzuiH9ybuaPNagcI2XvmFf2hGiZn9F11CtpQJwB9ed4ghWxD9FFjkiTYOBb9LzThIsHaGaptliX7K+4L4E7jONqvrL0PL5rNZnxb83hSK0ugBHLya8q/YLXTk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207312; c=relaxed/simple; bh=vuv7bbxrOvBKQ1IUI9rgXryPW8TxCWii3teACFVtGLE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IGa4SJFvQmLS+d4SE2H3fFrUlUZfTzYvsqeEwxac0uXmxXJZ+AL2GF+hB3xvYa2X1drtFQeU1DjcPGsijCNvoX4u/1+B0334q67kKIVTaiS8V7Sw/Z4Gner+/6RvttLtaHVv3AGeIcNVC9TS04lLaAVR/EuQf3CntMDJ40VYCgw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=U7FqDzLF; arc=none smtp.client-ip=91.218.175.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="U7FqDzLF" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1764207308; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gYInxveGKbklo3Ptic2jmegqmlW4BlcGcNBMNHcuHrk=; b=U7FqDzLFmWMhwTptUCer8z9mpc1HboYrnPGOjqKuQhpKR/96YWMA9w0jC9TPxcA6om1Z8U kKxt2OH39BNcuGoyvN5bW6PuLtz5qHgrITT3ACF6cbM/0huGGg/0VDZUDVxOdZ9tXVkIDL ACObJ3espX2p6zyTSMhVIotkYjABllA= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 07/16] KVM: selftests: Move PTE bitmasks to kvm_mmu Date: Thu, 27 Nov 2025 01:34:31 +0000 Message-ID: <20251127013440.3324671-8-yosry.ahmed@linux.dev> In-Reply-To: <20251127013440.3324671-1-yosry.ahmed@linux.dev> References: <20251127013440.3324671-1-yosry.ahmed@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Move the PTE bitmasks into kvm_mmu to parameterize them for virt mapping functions. Introduce helpers to read/write different PTE bits given a kvm_mmu. Drop the 'global' bit definition as it's currently unused, but leave the 'user' bit as it will be used in coming changes. Opportunisitcally rename 'large' to 'huge' as it's more consistent with the kernel naming. Leave PHYSICAL_PAGE_MASK alone, it's fixed in all page table formats and a lot of other macros depend on it. It's tempting to move all the other macros to be per-struct instead, but it would be too much noise for little benefit. Keep c_bit and s_bit in vm->arch as they used before the MMU is initialized, through __vmcreate() -> vm_userspace_mem_region_add() -> vm_mem_add() -> vm_arch_has_protected_memory(). No functional change intended. Signed-off-by: Yosry Ahmed --- .../selftests/kvm/include/x86/processor.h | 43 +++++++++--- .../testing/selftests/kvm/lib/x86/processor.c | 68 +++++++++++-------- 2 files changed, 74 insertions(+), 37 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index 0c295097c714..3a1a82fd42b2 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -362,16 +362,6 @@ static inline unsigned int x86_model(unsigned int eax) return ((eax >> 12) & 0xf0) | ((eax >> 4) & 0x0f); } =20 -/* Page table bitfield declarations */ -#define PTE_PRESENT_MASK BIT_ULL(0) -#define PTE_WRITABLE_MASK BIT_ULL(1) -#define PTE_USER_MASK BIT_ULL(2) -#define PTE_ACCESSED_MASK BIT_ULL(5) -#define PTE_DIRTY_MASK BIT_ULL(6) -#define PTE_LARGE_MASK BIT_ULL(7) -#define PTE_GLOBAL_MASK BIT_ULL(8) -#define PTE_NX_MASK BIT_ULL(63) - #define PHYSICAL_PAGE_MASK GENMASK_ULL(51, 12) =20 #define PAGE_SHIFT 12 @@ -1449,11 +1439,44 @@ enum pg_level { #define PG_SIZE_2M PG_LEVEL_SIZE(PG_LEVEL_2M) #define PG_SIZE_1G PG_LEVEL_SIZE(PG_LEVEL_1G) =20 +struct pte_masks { + uint64_t present; + uint64_t writable; + uint64_t user; + uint64_t accessed; + uint64_t dirty; + uint64_t huge; + uint64_t nx; + uint64_t c; + uint64_t s; +}; + struct kvm_mmu { uint64_t root_gpa; int pgtable_levels; + struct pte_masks pte_masks; }; =20 +#define PTE_PRESENT_MASK(mmu) ((mmu)->pte_masks.present) +#define PTE_WRITABLE_MASK(mmu) ((mmu)->pte_masks.writable) +#define PTE_USER_MASK(mmu) ((mmu)->pte_masks.user) +#define PTE_ACCESSED_MASK(mmu) ((mmu)->pte_masks.accessed) +#define PTE_DIRTY_MASK(mmu) ((mmu)->pte_masks.dirty) +#define PTE_HUGE_MASK(mmu) ((mmu)->pte_masks.huge) +#define PTE_NX_MASK(mmu) ((mmu)->pte_masks.nx) +#define PTE_C_MASK(mmu) ((mmu)->pte_masks.c) +#define PTE_S_MASK(mmu) ((mmu)->pte_masks.s) + +#define pte_present(mmu, pte) (!!(*(pte) & PTE_PRESENT_MASK(mmu))) +#define pte_writable(mmu, pte) (!!(*(pte) & PTE_WRITABLE_MASK(mmu))) +#define pte_user(mmu, pte) (!!(*(pte) & PTE_USER_MASK(mmu))) +#define pte_accessed(mmu, pte) (!!(*(pte) & PTE_ACCESSED_MASK(mmu))) +#define pte_dirty(mmu, pte) (!!(*(pte) & PTE_DIRTY_MASK(mmu))) +#define pte_huge(mmu, pte) (!!(*(pte) & PTE_HUGE_MASK(mmu))) +#define pte_nx(mmu, pte) (!!(*(pte) & PTE_NX_MASK(mmu))) +#define pte_c(mmu, pte) (!!(*(pte) & PTE_C_MASK(mmu))) +#define pte_s(mmu, pte) (!!(*(pte) & PTE_S_MASK(mmu))) + void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, uint64_t vaddr, uint64_t paddr, int level); void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index 871de49c35ee..dc568d70f9d6 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -157,11 +157,13 @@ bool kvm_is_tdp_enabled(void) } =20 static struct kvm_mmu *mmu_create(struct kvm_vm *vm, - int pgtable_levels) + int pgtable_levels, + struct pte_masks *pte_masks) { struct kvm_mmu *mmu =3D calloc(1, sizeof(*mmu)); =20 TEST_ASSERT(mmu, "-ENOMEM when allocating MMU"); + mmu->pte_masks =3D *pte_masks; mmu->root_gpa =3D vm_alloc_page_table(vm); mmu->pgtable_levels =3D pgtable_levels; return mmu; @@ -169,7 +171,19 @@ static struct kvm_mmu *mmu_create(struct kvm_vm *vm, =20 static void mmu_init(struct kvm_vm *vm) { - vm->arch.mmu =3D mmu_create(vm, vm->pgtable_levels); + struct pte_masks pte_masks =3D (struct pte_masks){ + .present =3D BIT_ULL(0), + .writable =3D BIT_ULL(1), + .user =3D BIT_ULL(2), + .accessed =3D BIT_ULL(5), + .dirty =3D BIT_ULL(6), + .huge =3D BIT_ULL(7), + .nx =3D BIT_ULL(63), + .c =3D vm->arch.c_bit, + .s =3D vm->arch.s_bit, + }; + + vm->arch.mmu =3D mmu_create(vm, vm->pgtable_levels, &pte_masks); vm->pgd =3D vm->arch.mmu->root_gpa; } =20 @@ -177,7 +191,6 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) { TEST_ASSERT(vm->mode =3D=3D VM_MODE_PXXVYY_4K, "Unknown or unsupported guest mode: 0x%x", vm->mode); - /* If needed, create the top-level page table. */ if (!vm->pgd_created) { mmu_init(vm); @@ -192,7 +205,7 @@ static void *virt_get_pte(struct kvm_vm *vm, struct kvm= _mmu *mmu, uint64_t *page_table =3D addr_gpa2hva(vm, pt_gpa); int index =3D (vaddr >> PG_LEVEL_SHIFT(level)) & 0x1ffu; =20 - TEST_ASSERT((*parent_pte =3D=3D mmu->root_gpa) || (*parent_pte & PTE_PRES= ENT_MASK), + TEST_ASSERT((*parent_pte =3D=3D mmu->root_gpa) || pte_present(mmu, parent= _pte), "Parent PTE (level %d) not PRESENT for gva: 0x%08lx", level + 1, vaddr); =20 @@ -211,10 +224,10 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm = *vm, =20 paddr =3D vm_untag_gpa(vm, paddr); =20 - if (!(*pte & PTE_PRESENT_MASK)) { - *pte =3D PTE_PRESENT_MASK | PTE_WRITABLE_MASK; + if (!pte_present(mmu, pte)) { + *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu); if (current_level =3D=3D target_level) - *pte |=3D PTE_LARGE_MASK | (paddr & PHYSICAL_PAGE_MASK); + *pte |=3D PTE_HUGE_MASK(mmu) | (paddr & PHYSICAL_PAGE_MASK); else *pte |=3D vm_alloc_page_table(vm) & PHYSICAL_PAGE_MASK; } else { @@ -226,7 +239,7 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *v= m, TEST_ASSERT(current_level !=3D target_level, "Cannot create hugepage at level: %u, vaddr: 0x%lx", current_level, vaddr); - TEST_ASSERT(!(*pte & PTE_LARGE_MASK), + TEST_ASSERT(!pte_huge(mmu, pte), "Cannot create page table at level: %u, vaddr: 0x%lx", current_level, vaddr); } @@ -267,24 +280,24 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu = *mmu, uint64_t vaddr, current_level--) { pte =3D virt_create_upper_pte(vm, mmu, pte, vaddr, paddr, current_level, level); - if (*pte & PTE_LARGE_MASK) + if (pte_huge(mmu, pte)) return; } =20 /* Fill in page table entry. */ pte =3D virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); - TEST_ASSERT(!(*pte & PTE_PRESENT_MASK), + TEST_ASSERT(!pte_present(mmu, pte), "PTE already present for 4k page at vaddr: 0x%lx", vaddr); - *pte =3D PTE_PRESENT_MASK | PTE_WRITABLE_MASK | (paddr & PHYSICAL_PAGE_MA= SK); + *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | (paddr & PHYSIC= AL_PAGE_MASK); =20 /* * Neither SEV nor TDX supports shared page tables, so only the final * leaf PTE needs manually set the C/S-bit. */ if (vm_is_gpa_protected(vm, paddr)) - *pte |=3D vm->arch.c_bit; + *pte |=3D PTE_C_MASK(mmu); else - *pte |=3D vm->arch.s_bit; + *pte |=3D PTE_S_MASK(mmu); } =20 void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) @@ -316,7 +329,7 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, = uint64_t paddr, static bool vm_is_target_pte(struct kvm_mmu *mmu, uint64_t *pte, int *level, int current_level) { - if (*pte & PTE_LARGE_MASK) { + if (pte_huge(mmu, pte)) { TEST_ASSERT(*level =3D=3D PG_LEVEL_NONE || *level =3D=3D current_level, "Unexpected hugepage at level %d", current_level); @@ -374,6 +387,7 @@ uint64_t *vm_get_page_table_entry(struct kvm_vm *vm, ui= nt64_t vaddr) =20 void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) { + struct kvm_mmu *mmu =3D vm->arch.mmu; uint64_t *pml4e, *pml4e_start; uint64_t *pdpe, *pdpe_start; uint64_t *pde, *pde_start; @@ -390,44 +404,44 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, = uint8_t indent) pml4e_start =3D (uint64_t *) addr_gpa2hva(vm, vm->pgd); for (uint16_t n1 =3D 0; n1 <=3D 0x1ffu; n1++) { pml4e =3D &pml4e_start[n1]; - if (!(*pml4e & PTE_PRESENT_MASK)) + if (!pte_present(mmu, pml4e)) continue; fprintf(stream, "%*spml4e 0x%-3zx %p 0x%-12lx 0x%-10llx %u " " %u\n", indent, "", pml4e - pml4e_start, pml4e, addr_hva2gpa(vm, pml4e), PTE_GET_PFN(*pml4e), - !!(*pml4e & PTE_WRITABLE_MASK), !!(*pml4e & PTE_NX_MASK)); + pte_writable(mmu, pml4e), pte_nx(mmu, pml4e)); =20 pdpe_start =3D addr_gpa2hva(vm, *pml4e & PHYSICAL_PAGE_MASK); for (uint16_t n2 =3D 0; n2 <=3D 0x1ffu; n2++) { pdpe =3D &pdpe_start[n2]; - if (!(*pdpe & PTE_PRESENT_MASK)) + if (!pte_present(mmu, pdpe)) continue; fprintf(stream, "%*spdpe 0x%-3zx %p 0x%-12lx 0x%-10llx " "%u %u\n", indent, "", pdpe - pdpe_start, pdpe, addr_hva2gpa(vm, pdpe), - PTE_GET_PFN(*pdpe), !!(*pdpe & PTE_WRITABLE_MASK), - !!(*pdpe & PTE_NX_MASK)); + PTE_GET_PFN(*pdpe), pte_writable(mmu, pdpe), + pte_nx(mmu, pdpe)); =20 pde_start =3D addr_gpa2hva(vm, *pdpe & PHYSICAL_PAGE_MASK); for (uint16_t n3 =3D 0; n3 <=3D 0x1ffu; n3++) { pde =3D &pde_start[n3]; - if (!(*pde & PTE_PRESENT_MASK)) + if (!pte_present(mmu, pde)) continue; fprintf(stream, "%*spde 0x%-3zx %p " "0x%-12lx 0x%-10llx %u %u\n", indent, "", pde - pde_start, pde, addr_hva2gpa(vm, pde), - PTE_GET_PFN(*pde), !!(*pde & PTE_WRITABLE_MASK), - !!(*pde & PTE_NX_MASK)); + PTE_GET_PFN(*pde), pte_writable(mmu, pde), + pte_nx(mmu, pde)); =20 pte_start =3D addr_gpa2hva(vm, *pde & PHYSICAL_PAGE_MASK); for (uint16_t n4 =3D 0; n4 <=3D 0x1ffu; n4++) { pte =3D &pte_start[n4]; - if (!(*pte & PTE_PRESENT_MASK)) + if (!pte_present(mmu, pte)) continue; fprintf(stream, "%*spte 0x%-3zx %p " "0x%-12lx 0x%-10llx %u %u " @@ -436,9 +450,9 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, ui= nt8_t indent) pte - pte_start, pte, addr_hva2gpa(vm, pte), PTE_GET_PFN(*pte), - !!(*pte & PTE_WRITABLE_MASK), - !!(*pte & PTE_NX_MASK), - !!(*pte & PTE_DIRTY_MASK), + pte_writable(mmu, pte), + pte_nx(mmu, pte), + pte_dirty(mmu, pte), ((uint64_t) n1 << 27) | ((uint64_t) n2 << 18) | ((uint64_t) n3 << 9) @@ -522,7 +536,7 @@ vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vadd= r_t gva) struct kvm_mmu *mmu =3D vm->arch.mmu; uint64_t *pte =3D __vm_get_page_table_entry(vm, mmu, gva, &level); =20 - TEST_ASSERT(*pte & PTE_PRESENT_MASK, + TEST_ASSERT(pte_present(mmu, pte), "Leaf PTE not PRESENT for gva: 0x%08lx", gva); =20 /* --=20 2.52.0.158.g65b55ccf14-goog