From nobody Mon Dec 1 22:02:20 2025 Received: from out-172.mta0.migadu.com (out-172.mta0.migadu.com [91.218.175.172]) (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 5257A1FC110 for ; Thu, 27 Nov 2025 01:35:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207304; cv=none; b=Gpv2eNJIghgRub7+JzVKnX9l1VA8iaEmvcGwlmjnCSFSdAcYakkh7VjAAApiXJiWCh8agSledin7pYC0mDz9GZOkOMl4n3DBgyKC5BCpyFPhJPrBI+uJeeNLG3PY+cztCde3UxN/bgbncrZXsAJ4H0W2PkqPUVo6iBkA+0hjJv4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207304; c=relaxed/simple; bh=8dSwIsMf5WkZ+GB47q/HjDY4MVtZOBN0iPzHxaa8MB4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MNWW1yBTQf8nNM0NRO5rhaN4Q1rB3HXz4yKFJ9syKpBt5Dx9uJgVzD4hLhlRlkcl8s3HX2+QlxVyZYQDRfEPrpy0csMyBFfMIUVtLjxgAfm4mJzNWGmmvBT/zRiMuiDb26q2S2tUvxuC2nTd37Pphrg4Tf0pfMiiiId9hkgASwM= 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=iobWqrUf; arc=none smtp.client-ip=91.218.175.172 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="iobWqrUf" 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=1764207299; 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=kgL39qdbTnUxdJP/NSbd0jALF1Kl9kFpUI0uKGh2kK0=; b=iobWqrUfhNMTYtVgI+psJKay/AbEG7uSqkZPrQiBLifDsO1wGIu7Ol6R5AB2o90ZNityDU GG23b3J9OhUbS5OdEA/rbMVKW2Ic0tE8AjjMKR3jdru1zrBpZ46ilK4y6rOABnve9binZW muan9ZZVixPOjDbEvZH4zP2gs441tp0= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 01/16] KVM: selftests: Make __vm_get_page_table_entry() static Date: Thu, 27 Nov 2025 01:34:25 +0000 Message-ID: <20251127013440.3324671-2-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" The function is only used in processor.c, drop the declaration in processor.h and make it static. No functional change intended. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/include/x86/processor.h | 2 -- tools/testing/selftests/kvm/lib/x86/processor.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index 57d62a425109..c00c0fbe62cd 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1367,8 +1367,6 @@ static inline bool kvm_is_ignore_msrs(void) return get_kvm_param_bool("ignore_msrs"); } =20 -uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, - int *level); uint64_t *vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr); =20 uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index 36104d27f3d9..c14bf2b5f28f 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -306,8 +306,8 @@ static bool vm_is_target_pte(uint64_t *pte, int *level,= int current_level) return *level =3D=3D current_level; } =20 -uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, - int *level) +static uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vad= dr, + int *level) { int va_width =3D 12 + (vm->pgtable_levels) * 9; uint64_t *pte =3D &vm->pgd; --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-188.mta0.migadu.com (out-188.mta0.migadu.com [91.218.175.188]) (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 E48B8208994 for ; Thu, 27 Nov 2025 01:35:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.188 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207305; cv=none; b=IGytJMJUuf+JlnhCa1GMUczVuTJarGNmRc10nSNsz5rGh5NPLcM9VVzgZ6M8wjvLbquNojcnpd3MyOqWTdCVLiIPxJh8okVJ/BgE3YTownIoPyhV1fKONxaGoHDw8xPQkURKDEhCHiMkYxOPq9Q0iBvReWlVDhuGjOc/n0nLWkc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207305; c=relaxed/simple; bh=6WtAfRiH26crlvJct2IW2aooUxpguTYbw3yNk1UnbK0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=puULCWbz16pKVTMtRQNRrX8PPvLPCBM9CPA1VMzy7/asJjH3qsuojoHe16Tp6t6nY4bOol0ib3l27EbmvuamcJElG/AT+GDRjA8LArgD/UUpDXJINTXWdPuAnsBBED8nJxbNahWJ3d3nmQ7JB9WFT3WMlioFw3Fati6OpUV/X6Q= 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=HZqGIS5W; arc=none smtp.client-ip=91.218.175.188 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="HZqGIS5W" 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=1764207301; 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=Ngn58Ujgt1/h2MxgJUJ7H8sJngcgIdx0fAFNYR8+FNQ=; b=HZqGIS5WYnwJB1vmmTbNwtGulu+CJE5a0t3b1vzCoLDeYIEzgKWfkxdOumuPiVpki5LE9C XPGE9knHa3rzBZ8+cvukqP18J1vHxlMkONcWWzr1hnD9y31cs2HFud51XtjSpqBStjCGXA xd84KwjQiq7paq09G69YVIiQltpYrok= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 02/16] KVM: selftests: Stop passing a memslot to nested_map_memslot() Date: Thu, 27 Nov 2025 01:34:26 +0000 Message-ID: <20251127013440.3324671-3-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" On x86, KVM selftests use memslot 0 for all the default regions used by the test infrastructure. This is an implementation detail. nested_map_memslot() is currently used to map the default regions by explicitly passing slot 0, which leaks the library implementation into the caller. Rename the function to a very verbose nested_identity_map_default_memslots() to reflect what it actually does. Add an assertion that only memslot 0 is being used so that the implementation does not change from under us. No functional change intended. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/include/x86/vmx.h | 4 ++-- tools/testing/selftests/kvm/lib/x86/vmx.c | 12 ++++++++---- tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/= selftests/kvm/include/x86/vmx.h index 96e2b4c630a9..91916b8aa94b 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -563,8 +563,8 @@ void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm= *vm, uint64_t nested_paddr, uint64_t paddr); void nested_map(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uint64_t size); -void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm, - uint32_t memslot); +void nested_identity_map_default_memslots(struct vmx_pages *vmx, + struct kvm_vm *vm); void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t addr, uint64_t size); bool kvm_cpu_has_ept(void); diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index 29b082a58daa..eec33ec63811 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -494,12 +494,16 @@ void nested_map(struct vmx_pages *vmx, struct kvm_vm = *vm, /* Prepare an identity extended page table that maps all the * physical pages in VM. */ -void nested_map_memslot(struct vmx_pages *vmx, struct kvm_vm *vm, - uint32_t memslot) +void nested_identity_map_default_memslots(struct vmx_pages *vmx, + struct kvm_vm *vm) { + uint32_t s, memslot =3D 0; sparsebit_idx_t i, last; - struct userspace_mem_region *region =3D - memslot2region(vm, memslot); + struct userspace_mem_region *region =3D memslot2region(vm, memslot); + + /* Only memslot 0 is mapped here, ensure it's the only one being used */ + for (s =3D 0; s < NR_MEM_REGIONS; s++) + TEST_ASSERT_EQ(vm->memslots[s], 0); =20 i =3D (region->region.guest_phys_addr >> vm->page_shift) - 1; last =3D i + (region->region.memory_size >> vm->page_shift); diff --git a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c b/tools/t= esting/selftests/kvm/x86/vmx_dirty_log_test.c index 98cb6bdab3e6..aab7333aaef0 100644 --- a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c @@ -121,7 +121,7 @@ static void test_vmx_dirty_log(bool enable_ept) */ if (enable_ept) { prepare_eptp(vmx, vm); - nested_map_memslot(vmx, vm, 0); + nested_identity_map_default_memslots(vmx, vm); nested_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); nested_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); } --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 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 701222248AF for ; Thu, 27 Nov 2025 01:35:04 +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=1764207307; cv=none; b=kLufxsbrMwkoiTlDFuZCXd+RH1FDHpbWmaOGHdnt9gQkJlmrBOtPelxrnTpqOy09LLI47JDSxPmlPfYQJcPOXhkGCGZ/aFBisP6rW6QUZK/DS0MjEDQW4mTefMR1d6E8BRUZ0QhJMP0SHWLHxum2yYbY5WI+bynhAaV6axsbYf4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207307; c=relaxed/simple; bh=y7Kre+DkX1MTboGKKc7mVSU+QK1cL/kp3LN3RjGq57M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WKWkt9WlbmDe8IyUIw/J7mPTzC4L53JnAnCtMzaSqta6oW0JthnLlIYlODfEXVS0yqhnQJ9W6IbrZncrUWrR+afZc3Ky9hk6JohUlnGTPIzGMBPad0ipjsq3XQAfZTal0inUZFbDE0hmhIsokmwbWIo7n89C0ai8lFbW8+9E0eA= 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=vSC0cKDL; 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="vSC0cKDL" 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=1764207302; 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=CXRqDLnp/0k6cTO8+Ttea2aY14lC4UMEoybi0vihUAc=; b=vSC0cKDLQJBB6EHKpXmfioKRXJRNkWqIiMMeuwP0TxPZe34CRxW9PuduMtG/JsH+q0NBdT prWI7IBvimRaeVK8FdEevPu70zO/7TepxOGJxBgDKtpEnLIt4TZFtHedEqsWmxd25rLTrm QS6+WJik2VB5PjJkJwbAuc2Cqri+mic= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 03/16] KVM: selftests: Rename nested TDP mapping functions Date: Thu, 27 Nov 2025 01:34:27 +0000 Message-ID: <20251127013440.3324671-4-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" Rename the functions from nested_* to tdp_* to make their purpose clearer. No functional change intended. Suggested-by: Sean Christopherson Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/include/x86/vmx.h | 16 +++--- .../testing/selftests/kvm/lib/x86/memstress.c | 4 +- tools/testing/selftests/kvm/lib/x86/vmx.c | 50 +++++++++---------- .../selftests/kvm/x86/vmx_dirty_log_test.c | 6 +-- 4 files changed, 37 insertions(+), 39 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/= selftests/kvm/include/x86/vmx.h index 91916b8aa94b..04b8231d032a 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -559,14 +559,14 @@ bool load_vmcs(struct vmx_pages *vmx); =20 bool ept_1g_pages_supported(void); =20 -void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr); -void nested_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, uint64_t size); -void nested_identity_map_default_memslots(struct vmx_pages *vmx, - struct kvm_vm *vm); -void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t addr, uint64_t size); +void tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t nested_= paddr, + uint64_t paddr); +void tdp_map(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t nested_pad= dr, + uint64_t paddr, uint64_t size); +void tdp_identity_map_default_memslots(struct vmx_pages *vmx, + struct kvm_vm *vm); +void tdp_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t addr, uint64_t size); bool kvm_cpu_has_ept(void); void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm); void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm= *vm); diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testin= g/selftests/kvm/lib/x86/memstress.c index 0b1f288ad556..1928b00bde51 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -70,11 +70,11 @@ void memstress_setup_ept(struct vmx_pages *vmx, struct = kvm_vm *vm) * KVM can shadow the EPT12 with the maximum huge page size supported * by the backing source. */ - nested_identity_map_1g(vmx, vm, 0, 0x100000000ULL); + tdp_identity_map_1g(vmx, vm, 0, 0x100000000ULL); =20 start =3D align_down(memstress_args.gpa, PG_SIZE_1G); end =3D align_up(memstress_args.gpa + memstress_args.size, PG_SIZE_1G); - nested_identity_map_1g(vmx, vm, start, end - start); + tdp_identity_map_1g(vmx, vm, start, end - start); } =20 void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vc= pu *vcpus[]) diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index eec33ec63811..1954ccdfc353 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -362,12 +362,12 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_= rip, void *guest_rsp) init_vmcs_guest_state(guest_rip, guest_rsp); } =20 -static void nested_create_pte(struct kvm_vm *vm, - struct eptPageTableEntry *pte, - uint64_t nested_paddr, - uint64_t paddr, - int current_level, - int target_level) +static void tdp_create_pte(struct kvm_vm *vm, + struct eptPageTableEntry *pte, + uint64_t nested_paddr, + uint64_t paddr, + int current_level, + int target_level) { if (!pte->readable) { pte->writable =3D true; @@ -394,8 +394,8 @@ static void nested_create_pte(struct kvm_vm *vm, } =20 =20 -void __nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, int target_level) +void __tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, int target_level) { const uint64_t page_size =3D PG_LEVEL_SIZE(target_level); struct eptPageTableEntry *pt =3D vmx->eptp_hva, *pte; @@ -428,7 +428,7 @@ void __nested_pg_map(struct vmx_pages *vmx, struct kvm_= vm *vm, index =3D (nested_paddr >> PG_LEVEL_SHIFT(level)) & 0x1ffu; pte =3D &pt[index]; =20 - nested_create_pte(vm, pte, nested_paddr, paddr, level, target_level); + tdp_create_pte(vm, pte, nested_paddr, paddr, level, target_level); =20 if (pte->page_size) break; @@ -445,10 +445,10 @@ void __nested_pg_map(struct vmx_pages *vmx, struct kv= m_vm *vm, =20 } =20 -void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr) +void tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr) { - __nested_pg_map(vmx, vm, nested_paddr, paddr, PG_LEVEL_4K); + __tdp_pg_map(vmx, vm, nested_paddr, paddr, PG_LEVEL_4K); } =20 /* @@ -468,8 +468,8 @@ void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm= *vm, * Within the VM given by vm, creates a nested guest translation for the * page range starting at nested_paddr to the page range starting at paddr. */ -void __nested_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, uint64_t size, +void __tdp_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, uint64_t size, int level) { size_t page_size =3D PG_LEVEL_SIZE(level); @@ -479,23 +479,23 @@ void __nested_map(struct vmx_pages *vmx, struct kvm_v= m *vm, TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); =20 while (npages--) { - __nested_pg_map(vmx, vm, nested_paddr, paddr, level); + __tdp_pg_map(vmx, vm, nested_paddr, paddr, level); nested_paddr +=3D page_size; paddr +=3D page_size; } } =20 -void nested_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, uint64_t size) +void tdp_map(struct vmx_pages *vmx, struct kvm_vm *vm, + uint64_t nested_paddr, uint64_t paddr, uint64_t size) { - __nested_map(vmx, vm, nested_paddr, paddr, size, PG_LEVEL_4K); + __tdp_map(vmx, vm, nested_paddr, paddr, size, PG_LEVEL_4K); } =20 /* Prepare an identity extended page table that maps all the * physical pages in VM. */ -void nested_identity_map_default_memslots(struct vmx_pages *vmx, - struct kvm_vm *vm) +void tdp_identity_map_default_memslots(struct vmx_pages *vmx, + struct kvm_vm *vm) { uint32_t s, memslot =3D 0; sparsebit_idx_t i, last; @@ -512,18 +512,16 @@ void nested_identity_map_default_memslots(struct vmx_= pages *vmx, if (i > last) break; =20 - nested_map(vmx, vm, - (uint64_t)i << vm->page_shift, - (uint64_t)i << vm->page_shift, - 1 << vm->page_shift); + tdp_map(vmx, vm, (uint64_t)i << vm->page_shift, + (uint64_t)i << vm->page_shift, 1 << vm->page_shift); } } =20 /* Identity map a region with 1GiB Pages. */ -void nested_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, +void tdp_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t addr, uint64_t size) { - __nested_map(vmx, vm, addr, addr, size, PG_LEVEL_1G); + __tdp_map(vmx, vm, addr, addr, size, PG_LEVEL_1G); } =20 bool kvm_cpu_has_ept(void) diff --git a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c b/tools/t= esting/selftests/kvm/x86/vmx_dirty_log_test.c index aab7333aaef0..e7d0c08ba29d 100644 --- a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c @@ -121,9 +121,9 @@ static void test_vmx_dirty_log(bool enable_ept) */ if (enable_ept) { prepare_eptp(vmx, vm); - nested_identity_map_default_memslots(vmx, vm); - nested_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); - nested_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); + tdp_identity_map_default_memslots(vmx, vm); + tdp_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); + tdp_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); } =20 bmap =3D bitmap_zalloc(TEST_MEM_PAGES); --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-171.mta0.migadu.com (out-171.mta0.migadu.com [91.218.175.171]) (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 F1C5A227563 for ; Thu, 27 Nov 2025 01:35:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207308; cv=none; b=Ewk/nEsAaA+a8q8IAkwZY8xKq31u0AnUN1an+OlwZ+eqhWFKUZ72PZSdeStFC933feQNC0pv0i4gQd2Wr1ofxBCSbcB8zqUSsnOftYiy+/oNzVT6SNe6bTxzqtKLh0YhzyFrC+m/WKzRHRV1tSS6yGu801izd1JAp3y+dUAbpMg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207308; c=relaxed/simple; bh=J3u8HvEYGkQg9tcuAt/VJZMZwXvTrd0+nmOMRkQ7ZwA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YnCVEWkXSU0AXNN2CCu58VSnvgNyqs+QFadDImjONJDdphiUByGHds5OXEGoTwIztvexn1aSJalPVMQTycWJhulxRxTwUw7lR1KMihKvQzJjSiGI5zXEGjPsuZO6nUSzh24XBS0v8IzIuIEMM5QtpC7zxIkQHxoJ4wJPhb8qquA= 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=kT6yuenh; arc=none smtp.client-ip=91.218.175.171 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="kT6yuenh" 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=1764207304; 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=L2ucYVpjEc6TsSz805pVOAkHRLs/b+8emb5igbeiiE4=; b=kT6yuenhYJqKmqopO5hXPqhWn0j6mwkgnz6KAEamfawxIVd/Osxe6mU2jsU9JuwGIhbWem fiRFpKcmj+SQnX4u0Qo1xGWkmedCaeNOVzqazoOnd40GvhNjaeKdkLGKF/YRsTtp3/pvtv LkWG6SO6cF38Kz+++0dKWGkHD+VCS8E= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 04/16] KVM: selftests: Kill eptPageTablePointer Date: Thu, 27 Nov 2025 01:34:28 +0000 Message-ID: <20251127013440.3324671-5-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" Replace the struct overlay with explicit bitmasks, which is clearer and less error-prone. See commit f18b4aebe107 ("kvm: selftests: do not use bitfields larger than 32-bits for PTEs") for an example of why bitfields are not preferrable. Remove the unused PAGE_SHIFT_4K definition while at it. No functional change intended. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/lib/x86/vmx.c | 37 +++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index 1954ccdfc353..85043bb1ec4d 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -10,10 +10,16 @@ #include "processor.h" #include "vmx.h" =20 -#define PAGE_SHIFT_4K 12 - #define KVM_EPT_PAGE_TABLE_MIN_PADDR 0x1c0000 =20 +#define EPTP_MT_SHIFT 0 /* EPTP memtype bits 2:0 */ +#define EPTP_PWL_SHIFT 3 /* EPTP page walk length bits 5:3 */ +#define EPTP_AD_ENABLED_SHIFT 6 /* EPTP AD enabled bit 6 */ + +#define EPTP_WB (X86_MEMTYPE_WB << EPTP_MT_SHIFT) +#define EPTP_PWL_4 (3ULL << EPTP_PWL_SHIFT) /* PWL is (levels - 1) */ +#define EPTP_AD_ENABLED (1ULL << EPTP_AD_ENABLED_SHIFT) + bool enable_evmcs; =20 struct hv_enlightened_vmcs *current_evmcs; @@ -34,14 +40,6 @@ struct eptPageTableEntry { uint64_t suppress_ve:1; }; =20 -struct eptPageTablePointer { - uint64_t memory_type:3; - uint64_t page_walk_length:3; - uint64_t ad_enabled:1; - uint64_t reserved_11_07:5; - uint64_t address:40; - uint64_t reserved_63_52:12; -}; int vcpu_enable_evmcs(struct kvm_vcpu *vcpu) { uint16_t evmcs_ver; @@ -196,16 +194,15 @@ static inline void init_vmcs_control_fields(struct vm= x_pages *vmx) vmwrite(PIN_BASED_VM_EXEC_CONTROL, rdmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS)= ); =20 if (vmx->eptp_gpa) { - uint64_t ept_paddr; - struct eptPageTablePointer eptp =3D { - .memory_type =3D X86_MEMTYPE_WB, - .page_walk_length =3D 3, /* + 1 */ - .ad_enabled =3D ept_vpid_cap_supported(VMX_EPT_VPID_CAP_AD_BITS), - .address =3D vmx->eptp_gpa >> PAGE_SHIFT_4K, - }; - - memcpy(&ept_paddr, &eptp, sizeof(ept_paddr)); - vmwrite(EPT_POINTER, ept_paddr); + uint64_t eptp =3D vmx->eptp_gpa | EPTP_WB | EPTP_PWL_4; + + TEST_ASSERT((vmx->eptp_gpa & ~PHYSICAL_PAGE_MASK) =3D=3D 0, + "Illegal bits set in vmx->eptp_gpa"); + + if (ept_vpid_cap_supported(VMX_EPT_VPID_CAP_AD_BITS)) + eptp |=3D EPTP_AD_ENABLED; + + vmwrite(EPT_POINTER, eptp); sec_exec_ctl |=3D SECONDARY_EXEC_ENABLE_EPT; } =20 --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-179.mta0.migadu.com (out-179.mta0.migadu.com [91.218.175.179]) (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 88EE7238D5A for ; Thu, 27 Nov 2025 01:35:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207309; cv=none; b=hv8VU5WfddeqRBIp4bZg/F9V1vnExLMgue0Nuk4cSuCWyvamRYAhED2R67dIVDL07XU+dfH1oltlRV9b+9/FjDI2YRt6cg1wvwTxTp6rt9dlGv0C1nq7yVnnxAn+kvWnBsXkYNg235a7FXLXGbyk8JtN/AxiXMO78uR6Lhf5aIY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207309; c=relaxed/simple; bh=UV0zQexRvvdY6sr4DDe35YyGuYRdEJphSxkjaenYDhA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=llKtkzPGn0DOLmIC4HyQi+Qd6Z4a/MaE+qSn5sK8Ddb/+owge/IMLZ4NPUsZ3TXVWMztqU09t3m4ic91dWEr1Km/AWJ56ZWmGlgkqeHAt9ouzhtbYPpJ////cGplZuNsHCeT753A40w3DSsBg8W7Qj5VyFIIAVBKtxz5x1p4VK0= 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=kaNhqMns; arc=none smtp.client-ip=91.218.175.179 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="kaNhqMns" 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=1764207305; 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=X+eMCiC+Ad/t5DRqhycrkMjOhIHljfy9tanTohxdWII=; b=kaNhqMnsPU0QxtPij8W3K1dQ2nrF61cQxdUMsFw7HS6H/z40sFAkXa9JeNpteXwD8LUB5Q EHM2seIuc9A9Lj8KWDOJqpYDP2tprhCfKv5eqy+s/DekBhvvhM7FakxQDmzh2oGVedLavL 6TXoVr0QXhEGamKAUzLhLUD3DvP0NbU= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 05/16] KVM: selftests: Stop setting AD bits on nested EPTs on creation Date: Thu, 27 Nov 2025 01:34:29 +0000 Message-ID: <20251127013440.3324671-6-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" When new nested EPTs are created, the AD bits are set. This was introduced by commit 094444204570 ("selftests: kvm: add test for dirty logging inside nested guests"), which introduced vmx_dirty_log_test. It's unclear why that was needed at the time, but regardless, the test seems to pass without them so probably no longer needed. dirty_log_perf_test (with -n to run in L2) also passes, and these are the only tests currently using nested EPT mappings. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/lib/x86/vmx.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index 85043bb1ec4d..a3e2eae981da 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -432,14 +432,6 @@ void __tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm= *vm, =20 pt =3D addr_gpa2hva(vm, pte->address * vm->page_size); } - - /* - * For now mark these as accessed and dirty because the only - * testcase we have needs that. Can be reconsidered later. - */ - pte->accessed =3D true; - pte->dirty =3D true; - } =20 void tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-171.mta0.migadu.com (out-171.mta0.migadu.com [91.218.175.171]) (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 BC74623F422 for ; Thu, 27 Nov 2025 01:35:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207311; cv=none; b=Qam+9OlaQEVokY6U9RxZtluJisvSktAb79CmD8RCCehbxur4gby7+/KsfyO7BJxiSS8kk6z6Z6KqKidUghqjl9yZYUB2EkNlOv/RVMNaNDapoGQnQrLHLdqLnEW9mGcVZaXrhnlWu1zGmevH+CZOH6ozNF762UO5AKn5bO1Carc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207311; c=relaxed/simple; bh=rJ8IoloP2mS8Aj9KDphh3mE3ooKXuct3r7Tjf9+L1aQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TAHlGp60VCatHsv6GC6+lu6SjTIOCUONq/sdeI3Oj5DfnEd7ARdYtnIptmJRiIXNBwkr8jQMOxIPLkm2o+t4QPeVAw4mCTSrzm9mg92QgFRmejgUeo9NsINC8SD/yKJDhah1pjSwnDsGtSWA6XNsOGnj5/tpXLgqx4DubWwR/5o= 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=gGki0PWz; arc=none smtp.client-ip=91.218.175.171 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="gGki0PWz" 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=1764207307; 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=b6AFu0tluctYDpxhADpmghqFz338Jgmap++Fi9r6Ggs=; b=gGki0PWztDUomJ33glUMRuprhXI+yoRhmi1BO77qhRd02vsnuafANITePQRUKOA5LMF2b4 mOxk+DlJRLdRnLVs+9ezIUYJDOGomkwbGKg4W4IASHZpl+M8YB9LIsUTRDUsMdTKMRGqrT L9prh8wa0DiFYV0nM0VyMDeEvuEG8Qk= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 06/16] KVM: selftests: Introduce struct kvm_mmu Date: Thu, 27 Nov 2025 01:34:30 +0000 Message-ID: <20251127013440.3324671-7-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" In preparation for generalizing the virt mapping functions to work with TDP page tables, introduce struct kvm_mmu. This struct currently only holds the root GPA and number of page table levels. Parameterize virt mapping functions by the kvm_mmu, and use the root GPA and page table levels instead of hardcoding vm->pgd and vm->pgtable_levels. There's a subtle change here, instead of checking that the parent pointer is the address of the vm->pgd, check if the value pointed at by the parent pointer is the root GPA (i.e. the value of vm->pgd in this case). No change in behavior expected. Opportunistically, switch the ordering of the checks in the assertion in virt_get_pte(), as it makes more sense to check if the parent PTE is the root (in which case, not a PTE) before checking the present flag. vm->arch.mmu is dynamically allocated to avoid a circular dependency chain if kvm_util_arch.h includes processor.h for the struct definition: kvm_util_arch.h -> processor.h -> kvm_util.h -> kvm_util_arch.h No functional change intended. Suggested-by: Sean Christopherson Signed-off-by: Yosry Ahmed --- .../selftests/kvm/include/x86/kvm_util_arch.h | 4 ++ .../selftests/kvm/include/x86/processor.h | 8 ++- .../testing/selftests/kvm/lib/x86/processor.c | 61 +++++++++++++------ 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h b/tool= s/testing/selftests/kvm/include/x86/kvm_util_arch.h index 972bb1c4ab4c..d8808fa33faa 100644 --- a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h +++ b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h @@ -10,6 +10,8 @@ =20 extern bool is_forced_emulation_enabled; =20 +struct kvm_mmu; + struct kvm_vm_arch { vm_vaddr_t gdt; vm_vaddr_t tss; @@ -19,6 +21,8 @@ struct kvm_vm_arch { uint64_t s_bit; int sev_fd; bool is_pt_protected; + + struct kvm_mmu *mmu; }; =20 static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index c00c0fbe62cd..0c295097c714 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1449,7 +1449,13 @@ enum pg_level { #define PG_SIZE_2M PG_LEVEL_SIZE(PG_LEVEL_2M) #define PG_SIZE_1G PG_LEVEL_SIZE(PG_LEVEL_1G) =20 -void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int = level); +struct kvm_mmu { + uint64_t root_gpa; + int pgtable_levels; +}; + +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, uint64_t nr_bytes, int level); =20 diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index c14bf2b5f28f..871de49c35ee 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -156,6 +156,23 @@ bool kvm_is_tdp_enabled(void) return get_kvm_amd_param_bool("npt"); } =20 +static struct kvm_mmu *mmu_create(struct kvm_vm *vm, + int pgtable_levels) +{ + struct kvm_mmu *mmu =3D calloc(1, sizeof(*mmu)); + + TEST_ASSERT(mmu, "-ENOMEM when allocating MMU"); + mmu->root_gpa =3D vm_alloc_page_table(vm); + mmu->pgtable_levels =3D pgtable_levels; + return mmu; +} + +static void mmu_init(struct kvm_vm *vm) +{ + vm->arch.mmu =3D mmu_create(vm, vm->pgtable_levels); + vm->pgd =3D vm->arch.mmu->root_gpa; +} + void virt_arch_pgd_alloc(struct kvm_vm *vm) { TEST_ASSERT(vm->mode =3D=3D VM_MODE_PXXVYY_4K, @@ -163,19 +180,19 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm) =20 /* If needed, create the top-level page table. */ if (!vm->pgd_created) { - vm->pgd =3D vm_alloc_page_table(vm); + mmu_init(vm); vm->pgd_created =3D true; } } =20 -static void *virt_get_pte(struct kvm_vm *vm, uint64_t *parent_pte, - uint64_t vaddr, int level) +static void *virt_get_pte(struct kvm_vm *vm, struct kvm_mmu *mmu, + uint64_t *parent_pte, uint64_t vaddr, int level) { uint64_t pt_gpa =3D PTE_GET_PA(*parent_pte); uint64_t *page_table =3D addr_gpa2hva(vm, pt_gpa); int index =3D (vaddr >> PG_LEVEL_SHIFT(level)) & 0x1ffu; =20 - TEST_ASSERT((*parent_pte & PTE_PRESENT_MASK) || parent_pte =3D=3D &vm->pg= d, + TEST_ASSERT((*parent_pte =3D=3D mmu->root_gpa) || (*parent_pte & PTE_PRES= ENT_MASK), "Parent PTE (level %d) not PRESENT for gva: 0x%08lx", level + 1, vaddr); =20 @@ -183,13 +200,14 @@ static void *virt_get_pte(struct kvm_vm *vm, uint64_t= *parent_pte, } =20 static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, + struct kvm_mmu *mmu, uint64_t *parent_pte, uint64_t vaddr, uint64_t paddr, int current_level, int target_level) { - uint64_t *pte =3D virt_get_pte(vm, parent_pte, vaddr, current_level); + uint64_t *pte =3D virt_get_pte(vm, mmu, parent_pte, vaddr, current_level); =20 paddr =3D vm_untag_gpa(vm, paddr); =20 @@ -215,10 +233,11 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm = *vm, return pte; } =20 -void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int = level) +void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *mmu, uint64_t vaddr, + uint64_t paddr, int level) { const uint64_t pg_size =3D PG_LEVEL_SIZE(level); - uint64_t *pte =3D &vm->pgd; + uint64_t *pte =3D &mmu->root_gpa; int current_level; =20 TEST_ASSERT(vm->mode =3D=3D VM_MODE_PXXVYY_4K, @@ -243,17 +262,17 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr,= uint64_t paddr, int level) * Allocate upper level page tables, if not already present. Return * early if a hugepage was created. */ - for (current_level =3D vm->pgtable_levels; + for (current_level =3D mmu->pgtable_levels; current_level > PG_LEVEL_4K; current_level--) { - pte =3D virt_create_upper_pte(vm, pte, vaddr, paddr, + pte =3D virt_create_upper_pte(vm, mmu, pte, vaddr, paddr, current_level, level); if (*pte & PTE_LARGE_MASK) return; } =20 /* Fill in page table entry. */ - pte =3D virt_get_pte(vm, pte, vaddr, PG_LEVEL_4K); + pte =3D virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); TEST_ASSERT(!(*pte & PTE_PRESENT_MASK), "PTE already present for 4k page at vaddr: 0x%lx", vaddr); *pte =3D PTE_PRESENT_MASK | PTE_WRITABLE_MASK | (paddr & PHYSICAL_PAGE_MA= SK); @@ -270,7 +289,7 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, u= int64_t paddr, int level) =20 void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) { - __virt_pg_map(vm, vaddr, paddr, PG_LEVEL_4K); + __virt_pg_map(vm, vm->arch.mmu, vaddr, paddr, PG_LEVEL_4K); } =20 void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, @@ -285,7 +304,7 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, = uint64_t paddr, nr_bytes, pg_size); =20 for (i =3D 0; i < nr_pages; i++) { - __virt_pg_map(vm, vaddr, paddr, level); + __virt_pg_map(vm, vm->arch.mmu, vaddr, paddr, level); sparsebit_set_num(vm->vpages_mapped, vaddr >> vm->page_shift, nr_bytes / PAGE_SIZE); =20 @@ -294,7 +313,8 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, = uint64_t paddr, } } =20 -static bool vm_is_target_pte(uint64_t *pte, int *level, int current_level) +static bool vm_is_target_pte(struct kvm_mmu *mmu, uint64_t *pte, + int *level, int current_level) { if (*pte & PTE_LARGE_MASK) { TEST_ASSERT(*level =3D=3D PG_LEVEL_NONE || @@ -306,7 +326,9 @@ static bool vm_is_target_pte(uint64_t *pte, int *level,= int current_level) return *level =3D=3D current_level; } =20 -static uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vad= dr, +static uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, + struct kvm_mmu *mmu, + uint64_t vaddr, int *level) { int va_width =3D 12 + (vm->pgtable_levels) * 9; @@ -335,19 +357,19 @@ static uint64_t *__vm_get_page_table_entry(struct kvm= _vm *vm, uint64_t vaddr, for (current_level =3D vm->pgtable_levels; current_level > PG_LEVEL_4K; current_level--) { - pte =3D virt_get_pte(vm, pte, vaddr, current_level); - if (vm_is_target_pte(pte, level, current_level)) + pte =3D virt_get_pte(vm, mmu, pte, vaddr, current_level); + if (vm_is_target_pte(mmu, pte, level, current_level)) return pte; } =20 - return virt_get_pte(vm, pte, vaddr, PG_LEVEL_4K); + return virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); } =20 uint64_t *vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr) { int level =3D PG_LEVEL_4K; =20 - return __vm_get_page_table_entry(vm, vaddr, &level); + return __vm_get_page_table_entry(vm, vm->arch.mmu, vaddr, &level); } =20 void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) @@ -497,7 +519,8 @@ static void kvm_seg_set_kernel_data_64bit(struct kvm_se= gment *segp) vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) { int level =3D PG_LEVEL_NONE; - uint64_t *pte =3D __vm_get_page_table_entry(vm, gva, &level); + 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, "Leaf PTE not PRESENT for gva: 0x%08lx", gva); --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 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 From nobody Mon Dec 1 22:02:20 2025 Received: from out-186.mta0.migadu.com (out-186.mta0.migadu.com [91.218.175.186]) (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 D588A24DD1F for ; Thu, 27 Nov 2025 01:35:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.186 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207313; cv=none; b=d1NhX4o2QlJRYCq1sIwLOk24D6jIpzTKgqjbgQxvuaoqRnAfMx5FY+NLFgh2t9eLl5wNKdXgJqlG35yKqlPorB7q+aTcO6wISqLA+6wmP2+0goJU7OvhiUKZyIOL6VPRCKkm3gokQbp+z7CQboW4YRZg06++skAqyVKbp1b2fuE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207313; c=relaxed/simple; bh=9J4gOoOLLBaaPkuAOJpUOQ38ee/Jp+i0964SqQllsE8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KEpY3jCdykw15xIjeuaIoEXF3o5VWfKk2qN7bzXLVwdJaI//w0b/LZ6QBfwPvkxQXw2kpDQNMmBM1MDTcrNX5pJxpYE+0QAC43fCnNLN5EMSPI7b3RbyzpFA9iMUgDjrUTE3NXmDrnDjgfvVFG7eCaQYwiK9rkxtG4g+DYu6Tls= 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=key3w0ui; arc=none smtp.client-ip=91.218.175.186 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="key3w0ui" 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=1764207310; 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=vUAdpq0tM8xfaKfzdDru2JHD0ntk7pfaOK0qFsQsUaM=; b=key3w0uiaIvv0OcfVmp0pxvkcNeg7M9eJgVA/4gAS9wZKfnicm8FU+HulhNFyVk6b3jxqm g351yIQcSTKC0kQD9AgBysSX6jkgRMLo1rVFr6AOfW0ErCLpfIM7XJpDmVgA5X88fvhI5y Xpp6zbsiu3Ta9/uuZuH78f5zqPm/LpM= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 08/16] KVM: selftests: Use a nested MMU to share nested EPTs between vCPUs Date: Thu, 27 Nov 2025 01:34:32 +0000 Message-ID: <20251127013440.3324671-9-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" prepare_eptp() currently allocates new EPTs for each vCPU. memstress has its own hack to share the EPTs between vCPUs. Currently, there is no reason to have separate EPTs for each vCPU, and the complexity is significant. The only reason it doesn't matter now is because memstress is the only user with multiple vCPUs. Replace prepare_eptp() with vm_enable_ept(), which enables EPT for an entire VM. It allocates a new nested MMU and uses it to keep track of the common EPT root (PTE masks are currently unused, but will be by following changes). Drop 'eptp' and 'eptp_hva' from struct vmx_pages. Only keep 'eptp_gpa' and initialize it from the nested MMU root GPA. 'eptp' is unused, and addr2_gpa2hva() is used to get the HVA in __tdp_pg_map() instead of using 'eptp_hva'. Remove the workaround in memstress to copy the EPT root between vCPUs since it's handled by prepare_eptp() now. Signed-off-by: Yosry Ahmed --- .../selftests/kvm/include/x86/kvm_util_arch.h | 3 +++ .../selftests/kvm/include/x86/processor.h | 2 ++ tools/testing/selftests/kvm/include/x86/vmx.h | 8 +++--- .../testing/selftests/kvm/lib/x86/memstress.c | 19 +++++--------- .../testing/selftests/kvm/lib/x86/processor.c | 8 +++--- tools/testing/selftests/kvm/lib/x86/vmx.c | 25 +++++++++++-------- .../selftests/kvm/x86/vmx_dirty_log_test.c | 7 +++--- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h b/tool= s/testing/selftests/kvm/include/x86/kvm_util_arch.h index d8808fa33faa..1f5308f30566 100644 --- a/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h +++ b/tools/testing/selftests/kvm/include/x86/kvm_util_arch.h @@ -23,6 +23,9 @@ struct kvm_vm_arch { bool is_pt_protected; =20 struct kvm_mmu *mmu; + struct { + struct kvm_mmu *mmu; + } nested; }; =20 static inline bool __vm_arch_has_protected_memory(struct kvm_vm_arch *arch) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index 3a1a82fd42b2..fb2b2e53d453 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1477,6 +1477,8 @@ struct kvm_mmu { #define pte_c(mmu, pte) (!!(*(pte) & PTE_C_MASK(mmu))) #define pte_s(mmu, pte) (!!(*(pte) & PTE_S_MASK(mmu))) =20 +struct kvm_mmu *mmu_create(struct kvm_vm *vm, int pgtable_levels, + struct pte_masks *pte_masks); 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/include/x86/vmx.h b/tools/testing/= selftests/kvm/include/x86/vmx.h index 04b8231d032a..1fd83c23529a 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -520,13 +520,11 @@ struct vmx_pages { uint64_t vmwrite_gpa; void *vmwrite; =20 - void *eptp_hva; - uint64_t eptp_gpa; - void *eptp; - void *apic_access_hva; uint64_t apic_access_gpa; void *apic_access; + + uint64_t eptp_gpa; }; =20 union vmx_basic { @@ -568,7 +566,7 @@ void tdp_identity_map_default_memslots(struct vmx_pages= *vmx, void tdp_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t addr, uint64_t size); bool kvm_cpu_has_ept(void); -void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm); +void vm_enable_ept(struct kvm_vm *vm); void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm= *vm); =20 #endif /* SELFTEST_KVM_VMX_H */ diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testin= g/selftests/kvm/lib/x86/memstress.c index 1928b00bde51..00f7f11e5f0e 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -59,12 +59,10 @@ uint64_t memstress_nested_pages(int nr_vcpus) return 513 + 10 * nr_vcpus; } =20 -void memstress_setup_ept(struct vmx_pages *vmx, struct kvm_vm *vm) +static void memstress_setup_ept_mappings(struct vmx_pages *vmx, struct kvm= _vm *vm) { uint64_t start, end; =20 - prepare_eptp(vmx, vm); - /* * Identity map the first 4G and the test region with 1G pages so that * KVM can shadow the EPT12 with the maximum huge page size supported @@ -79,7 +77,7 @@ void memstress_setup_ept(struct vmx_pages *vmx, struct kv= m_vm *vm) =20 void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vc= pu *vcpus[]) { - struct vmx_pages *vmx, *vmx0 =3D NULL; + struct vmx_pages *vmx; struct kvm_regs regs; vm_vaddr_t vmx_gva; int vcpu_id; @@ -87,18 +85,13 @@ void memstress_setup_nested(struct kvm_vm *vm, int nr_v= cpus, struct kvm_vcpu *vc TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); TEST_REQUIRE(kvm_cpu_has_ept()); =20 + vm_enable_ept(vm); for (vcpu_id =3D 0; vcpu_id < nr_vcpus; vcpu_id++) { vmx =3D vcpu_alloc_vmx(vm, &vmx_gva); =20 - if (vcpu_id =3D=3D 0) { - memstress_setup_ept(vmx, vm); - vmx0 =3D vmx; - } else { - /* Share the same EPT table across all vCPUs. */ - vmx->eptp =3D vmx0->eptp; - vmx->eptp_hva =3D vmx0->eptp_hva; - vmx->eptp_gpa =3D vmx0->eptp_gpa; - } + /* The EPTs are shared across vCPUs, setup the mappings once */ + if (vcpu_id =3D=3D 0) + memstress_setup_ept_mappings(vmx, vm); =20 /* * Override the vCPU to run memstress_l1_guest_code() which will diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index dc568d70f9d6..bff75ff05364 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -156,14 +156,14 @@ bool kvm_is_tdp_enabled(void) return get_kvm_amd_param_bool("npt"); } =20 -static struct kvm_mmu *mmu_create(struct kvm_vm *vm, - int pgtable_levels, - struct pte_masks *pte_masks) +struct kvm_mmu *mmu_create(struct kvm_vm *vm, 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; + if (pte_masks) + mmu->pte_masks =3D *pte_masks; mmu->root_gpa =3D vm_alloc_page_table(vm); mmu->pgtable_levels =3D pgtable_levels; return mmu; diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index a3e2eae981da..5d799ec5f7c6 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -56,6 +56,16 @@ int vcpu_enable_evmcs(struct kvm_vcpu *vcpu) return evmcs_ver; } =20 +void vm_enable_ept(struct kvm_vm *vm) +{ + TEST_ASSERT(kvm_cpu_has_ept(), "KVM doesn't support nested EPT"); + if (vm->arch.nested.mmu) + return; + + /* EPTP_PWL_4 is always used */ + vm->arch.nested.mmu =3D mmu_create(vm, 4, NULL); +} + /* Allocate memory regions for nested VMX tests. * * Input Args: @@ -105,6 +115,9 @@ vcpu_alloc_vmx(struct kvm_vm *vm, vm_vaddr_t *p_vmx_gva) vmx->vmwrite_gpa =3D addr_gva2gpa(vm, (uintptr_t)vmx->vmwrite); memset(vmx->vmwrite_hva, 0, getpagesize()); =20 + if (vm->arch.nested.mmu) + vmx->eptp_gpa =3D vm->arch.nested.mmu->root_gpa; + *p_vmx_gva =3D vmx_gva; return vmx; } @@ -395,7 +408,8 @@ void __tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm = *vm, uint64_t nested_paddr, uint64_t paddr, int target_level) { const uint64_t page_size =3D PG_LEVEL_SIZE(target_level); - struct eptPageTableEntry *pt =3D vmx->eptp_hva, *pte; + void *eptp_hva =3D addr_gpa2hva(vm, vm->arch.nested.mmu->root_gpa); + struct eptPageTableEntry *pt =3D eptp_hva, *pte; uint16_t index; =20 TEST_ASSERT(vm->mode =3D=3D VM_MODE_PXXVYY_4K, @@ -525,15 +539,6 @@ bool kvm_cpu_has_ept(void) return ctrl & SECONDARY_EXEC_ENABLE_EPT; } =20 -void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm) -{ - TEST_ASSERT(kvm_cpu_has_ept(), "KVM doesn't support nested EPT"); - - vmx->eptp =3D (void *)vm_vaddr_alloc_page(vm); - vmx->eptp_hva =3D addr_gva2hva(vm, (uintptr_t)vmx->eptp); - vmx->eptp_gpa =3D addr_gva2gpa(vm, (uintptr_t)vmx->eptp); -} - void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm= *vm) { vmx->apic_access =3D (void *)vm_vaddr_alloc_page(vm); diff --git a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c b/tools/t= esting/selftests/kvm/x86/vmx_dirty_log_test.c index e7d0c08ba29d..5c8cf8ac42a2 100644 --- a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c @@ -93,6 +93,9 @@ static void test_vmx_dirty_log(bool enable_ept) =20 /* Create VM */ vm =3D vm_create_with_one_vcpu(&vcpu, l1_guest_code); + if (enable_ept) + vm_enable_ept(vm); + vmx =3D vcpu_alloc_vmx(vm, &vmx_pages_gva); vcpu_args_set(vcpu, 1, vmx_pages_gva); =20 @@ -113,14 +116,10 @@ static void test_vmx_dirty_log(bool enable_ept) * ... pages in the L2 GPA range [0xc0001000, 0xc0003000) will map to * 0xc0000000. * - * Note that prepare_eptp should be called only L1's GPA map is done, - * meaning after the last call to virt_map. - * * When EPT is disabled, the L2 guest code will still access the same L1 * GPAs as the EPT enabled case. */ if (enable_ept) { - prepare_eptp(vmx, vm); tdp_identity_map_default_memslots(vmx, vm); tdp_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); tdp_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 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 36A6C254841 for ; Thu, 27 Nov 2025 01:35:13 +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=1764207315; cv=none; b=qGJjHf3vVQCXXdXdFcmzfdu5yMHs9OgDZA+M5Ff/AX8jNyL/J48z20lg+zGdHj2aIET7Y5NLgkpTXZtDmCZzExPP1bYyJ7ElHu+BZcegr425WvqDtg/HPviccLblqEtTudG4451B/J63s4CxZNTtzsIbzi8rZPN6sgQj5g2okJU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207315; c=relaxed/simple; bh=9fqIvL+ZjBcPwbLZpcVLWbQ/sL6oJKZiqT1+y68f5ik=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Li7PQmppVxMQzH7j1hbwfbs7B5jsUY1RYCmTlILP/JKyDbnOZ+CiomkYwdh7kiUrUeyxcg9QS/xfjiSgf/1Dq10IS4v8Hae0Mhs61bn4kwHLgxqiJVW6Q5Uoiy7X09ZSjs1zgR7lpKxiSVZctsguD69cd6KSPAf3nqfyFj+yHZc= 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=MkZMqqUA; 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="MkZMqqUA" 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=1764207311; 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=h8lR1pc6Hr1WzKrsuMNFl8jlT04mlphFfDkA3PXHYU0=; b=MkZMqqUAOp6RagIs31KJrEKAPT2WLWIa4I69EM0Jtaf0srm917wWgCrNle0tmhNw77xLKL ezkSiHAv5B1IgsoRqh2/kwPuZDG6rDb93s+4FjPo49lue4hnAD1xlo8yOXHPOfQT5Vf2AY GJ9J+k8ga37sUa+E68bph/WRHXJHIxo= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 09/16] KVM: selftests: Stop passing VMX metadata to TDP mapping functions Date: Thu, 27 Nov 2025 01:34:33 +0000 Message-ID: <20251127013440.3324671-10-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" The root GPA can now be retrieved from the nested MMU, stop passing VMX metadata. This is in preparation for making these functions work for NPTs as well. Opportunistically drop tdp_pg_map() since it's unused. No functional change intended. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/include/x86/vmx.h | 11 ++----- .../testing/selftests/kvm/lib/x86/memstress.c | 11 +++---- tools/testing/selftests/kvm/lib/x86/vmx.c | 33 +++++++------------ .../selftests/kvm/x86/vmx_dirty_log_test.c | 9 +++-- 4 files changed, 24 insertions(+), 40 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/= selftests/kvm/include/x86/vmx.h index 1fd83c23529a..4dd4c2094ee6 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -557,14 +557,9 @@ bool load_vmcs(struct vmx_pages *vmx); =20 bool ept_1g_pages_supported(void); =20 -void tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t nested_= paddr, - uint64_t paddr); -void tdp_map(struct vmx_pages *vmx, struct kvm_vm *vm, uint64_t nested_pad= dr, - uint64_t paddr, uint64_t size); -void tdp_identity_map_default_memslots(struct vmx_pages *vmx, - struct kvm_vm *vm); -void tdp_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t addr, uint64_t size); +void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uin= t64_t size); +void tdp_identity_map_default_memslots(struct kvm_vm *vm); +void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size); bool kvm_cpu_has_ept(void); void vm_enable_ept(struct kvm_vm *vm); void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm= *vm); diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testin= g/selftests/kvm/lib/x86/memstress.c index 00f7f11e5f0e..3319cb57a78d 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -59,7 +59,7 @@ uint64_t memstress_nested_pages(int nr_vcpus) return 513 + 10 * nr_vcpus; } =20 -static void memstress_setup_ept_mappings(struct vmx_pages *vmx, struct kvm= _vm *vm) +static void memstress_setup_ept_mappings(struct kvm_vm *vm) { uint64_t start, end; =20 @@ -68,16 +68,15 @@ static void memstress_setup_ept_mappings(struct vmx_pag= es *vmx, struct kvm_vm *v * KVM can shadow the EPT12 with the maximum huge page size supported * by the backing source. */ - tdp_identity_map_1g(vmx, vm, 0, 0x100000000ULL); + tdp_identity_map_1g(vm, 0, 0x100000000ULL); =20 start =3D align_down(memstress_args.gpa, PG_SIZE_1G); end =3D align_up(memstress_args.gpa + memstress_args.size, PG_SIZE_1G); - tdp_identity_map_1g(vmx, vm, start, end - start); + tdp_identity_map_1g(vm, start, end - start); } =20 void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vc= pu *vcpus[]) { - struct vmx_pages *vmx; struct kvm_regs regs; vm_vaddr_t vmx_gva; int vcpu_id; @@ -87,11 +86,11 @@ void memstress_setup_nested(struct kvm_vm *vm, int nr_v= cpus, struct kvm_vcpu *vc =20 vm_enable_ept(vm); for (vcpu_id =3D 0; vcpu_id < nr_vcpus; vcpu_id++) { - vmx =3D vcpu_alloc_vmx(vm, &vmx_gva); + vcpu_alloc_vmx(vm, &vmx_gva); =20 /* The EPTs are shared across vCPUs, setup the mappings once */ if (vcpu_id =3D=3D 0) - memstress_setup_ept_mappings(vmx, vm); + memstress_setup_ept_mappings(vm); =20 /* * Override the vCPU to run memstress_l1_guest_code() which will diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index 5d799ec5f7c6..a909fad57fd5 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -404,8 +404,8 @@ static void tdp_create_pte(struct kvm_vm *vm, } =20 =20 -void __tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, int target_level) +void __tdp_pg_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, + int target_level) { const uint64_t page_size =3D PG_LEVEL_SIZE(target_level); void *eptp_hva =3D addr_gpa2hva(vm, vm->arch.nested.mmu->root_gpa); @@ -448,12 +448,6 @@ void __tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm= *vm, } } =20 -void tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr) -{ - __tdp_pg_map(vmx, vm, nested_paddr, paddr, PG_LEVEL_4K); -} - /* * Map a range of EPT guest physical addresses to the VM's physical address * @@ -471,9 +465,8 @@ void tdp_pg_map(struct vmx_pages *vmx, struct kvm_vm *v= m, * Within the VM given by vm, creates a nested guest translation for the * page range starting at nested_paddr to the page range starting at paddr. */ -void __tdp_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, uint64_t size, - int level) +void __tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, + uint64_t size, int level) { size_t page_size =3D PG_LEVEL_SIZE(level); size_t npages =3D size / page_size; @@ -482,23 +475,22 @@ void __tdp_map(struct vmx_pages *vmx, struct kvm_vm *= vm, TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); =20 while (npages--) { - __tdp_pg_map(vmx, vm, nested_paddr, paddr, level); + __tdp_pg_map(vm, nested_paddr, paddr, level); nested_paddr +=3D page_size; paddr +=3D page_size; } } =20 -void tdp_map(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t nested_paddr, uint64_t paddr, uint64_t size) +void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, + uint64_t size) { - __tdp_map(vmx, vm, nested_paddr, paddr, size, PG_LEVEL_4K); + __tdp_map(vm, nested_paddr, paddr, size, PG_LEVEL_4K); } =20 /* Prepare an identity extended page table that maps all the * physical pages in VM. */ -void tdp_identity_map_default_memslots(struct vmx_pages *vmx, - struct kvm_vm *vm) +void tdp_identity_map_default_memslots(struct kvm_vm *vm) { uint32_t s, memslot =3D 0; sparsebit_idx_t i, last; @@ -515,16 +507,15 @@ void tdp_identity_map_default_memslots(struct vmx_pag= es *vmx, if (i > last) break; =20 - tdp_map(vmx, vm, (uint64_t)i << vm->page_shift, + tdp_map(vm, (uint64_t)i << vm->page_shift, (uint64_t)i << vm->page_shift, 1 << vm->page_shift); } } =20 /* Identity map a region with 1GiB Pages. */ -void tdp_identity_map_1g(struct vmx_pages *vmx, struct kvm_vm *vm, - uint64_t addr, uint64_t size) +void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size) { - __tdp_map(vmx, vm, addr, addr, size, PG_LEVEL_1G); + __tdp_map(vm, addr, addr, size, PG_LEVEL_1G); } =20 bool kvm_cpu_has_ept(void) diff --git a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c b/tools/t= esting/selftests/kvm/x86/vmx_dirty_log_test.c index 5c8cf8ac42a2..370f8d3117c2 100644 --- a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c @@ -80,7 +80,6 @@ void l1_guest_code(struct vmx_pages *vmx) static void test_vmx_dirty_log(bool enable_ept) { vm_vaddr_t vmx_pages_gva =3D 0; - struct vmx_pages *vmx; unsigned long *bmap; uint64_t *host_test_mem; =20 @@ -96,7 +95,7 @@ static void test_vmx_dirty_log(bool enable_ept) if (enable_ept) vm_enable_ept(vm); =20 - vmx =3D vcpu_alloc_vmx(vm, &vmx_pages_gva); + vcpu_alloc_vmx(vm, &vmx_pages_gva); vcpu_args_set(vcpu, 1, vmx_pages_gva); =20 /* Add an extra memory slot for testing dirty logging */ @@ -120,9 +119,9 @@ static void test_vmx_dirty_log(bool enable_ept) * GPAs as the EPT enabled case. */ if (enable_ept) { - tdp_identity_map_default_memslots(vmx, vm); - tdp_map(vmx, vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); - tdp_map(vmx, vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); + tdp_identity_map_default_memslots(vm); + tdp_map(vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); + tdp_map(vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); } =20 bmap =3D bitmap_zalloc(TEST_MEM_PAGES); --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-185.mta0.migadu.com (out-185.mta0.migadu.com [91.218.175.185]) (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 E42FF263F4E for ; Thu, 27 Nov 2025 01:35:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.185 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207319; cv=none; b=C2QFQZnx5vy772nBZF02VTL6I+3UuiuY2o4e0zrFz5ixUVjDao1jz7QJjwQZkqCRaC7MHyOEmcehf8/tM+mX2EM9t4dAqMlA4gIfRftXpkIZa9Qf5X8OvYbbHwXBPVtV+PJJrQsFCsphOWND4lr7MU92CQ7njqQ+4E646Y2P1FI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207319; c=relaxed/simple; bh=AVL65zE6ap9yXa02g08WlNKn7e/EuZdOWIoUxqMYqAs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MwJPMcrO59aUUc/dMmxBEe4Z9uokOM/Xle90Fv6xATzSGbU3Fi0EeJwm8OL2z426P3FYGBucwZgGltxb2giwfaN1JDJgoNRsQe1qBK6dG5VtYTQM7ypUyR/Qj0sMUJuxquP/NE61ZATGsKndhFJHCptSpz9PtuTroBek8mkR9GI= 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=VA0qzw9i; arc=none smtp.client-ip=91.218.175.185 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="VA0qzw9i" 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=1764207313; 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=heCl6z29MAL1C93kBW4Xu7OGs20kKxnbrJao+8ri5pg=; b=VA0qzw9iMAs3D2ocRyQ5XNuY/PAsNZyEk0BVwoJmkEHh8lI8RIJothTwLjyyyHX/yNEfwp Fn259pMLYb/S8pBM3qtd6o2AXt0Z2e7nTcpfB9s8jK3LFdOQNzaI9PwRtS5a9eT3waJFNc XBqmcapbioaMuLVrm6k9DLudcuV93Cs= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 10/16] KVM: selftests: Reuse virt mapping functions for nested EPTs Date: Thu, 27 Nov 2025 01:34:34 +0000 Message-ID: <20251127013440.3324671-11-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" __tdp_pg_map() bears a lot of resemblence to __virt_pg_map(). The main differences are: - It uses the EPT struct overlay instead of the PTE masks. - It always assumes 4-level EPTs. To reuse __virt_pg_map(), initialize the PTE masks in nested MMU with EPT PTE masks. EPTs have no 'present' or 'user' bits, so use the 'readable' bit instead like shadow_{present/user}_mask, ignoring the fact that entries can be present and not readable if the CPU has VMX_EPT_EXECUTE_ONLY_BIT. This is simple and sufficient for testing. Add an executable bitmask and update __virt_pg_map() and friends to set the bit on newly created entries to match the EPT behavior. It's a nop for x86 page tables. Another benefit of reusing the code is having separate handling for upper-level PTEs vs 4K PTEs, which avoids some quirks like setting the large bit on a 4K PTE in the EPTs. No functional change intended. Suggested-by: Sean Christopherson Signed-off-by: Yosry Ahmed --- .../selftests/kvm/include/x86/processor.h | 3 + .../testing/selftests/kvm/lib/x86/processor.c | 12 +- tools/testing/selftests/kvm/lib/x86/vmx.c | 115 ++++-------------- 3 files changed, 33 insertions(+), 97 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index fb2b2e53d453..62e10b296719 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1447,6 +1447,7 @@ struct pte_masks { uint64_t dirty; uint64_t huge; uint64_t nx; + uint64_t x; uint64_t c; uint64_t s; }; @@ -1464,6 +1465,7 @@ struct kvm_mmu { #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_X_MASK(mmu) ((mmu)->pte_masks.x) #define PTE_C_MASK(mmu) ((mmu)->pte_masks.c) #define PTE_S_MASK(mmu) ((mmu)->pte_masks.s) =20 @@ -1474,6 +1476,7 @@ struct kvm_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_x(mmu, pte) (!!(*(pte) & PTE_X_MASK(mmu))) #define pte_c(mmu, pte) (!!(*(pte) & PTE_C_MASK(mmu))) #define pte_s(mmu, pte) (!!(*(pte) & PTE_S_MASK(mmu))) =20 diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index bff75ff05364..8b0e17f8ca37 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -162,8 +162,7 @@ struct kvm_mmu *mmu_create(struct kvm_vm *vm, int pgtab= le_levels, struct kvm_mmu *mmu =3D calloc(1, sizeof(*mmu)); =20 TEST_ASSERT(mmu, "-ENOMEM when allocating MMU"); - if (pte_masks) - mmu->pte_masks =3D *pte_masks; + mmu->pte_masks =3D *pte_masks; mmu->root_gpa =3D vm_alloc_page_table(vm); mmu->pgtable_levels =3D pgtable_levels; return mmu; @@ -179,6 +178,7 @@ static void mmu_init(struct kvm_vm *vm) .dirty =3D BIT_ULL(6), .huge =3D BIT_ULL(7), .nx =3D BIT_ULL(63), + .x =3D 0, .c =3D vm->arch.c_bit, .s =3D vm->arch.s_bit, }; @@ -225,7 +225,7 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *v= m, paddr =3D vm_untag_gpa(vm, paddr); =20 if (!pte_present(mmu, pte)) { - *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu); + *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | PTE_X_MASK(mmu= ); if (current_level =3D=3D target_level) *pte |=3D PTE_HUGE_MASK(mmu) | (paddr & PHYSICAL_PAGE_MASK); else @@ -271,6 +271,9 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *m= mu, uint64_t vaddr, TEST_ASSERT(vm_untag_gpa(vm, paddr) =3D=3D paddr, "Unexpected bits in paddr: %lx", paddr); =20 + TEST_ASSERT(!PTE_X_MASK(mmu) || !PTE_NX_MASK(mmu), + "X and NX bit masks cannot be used simultaneously"); + /* * Allocate upper level page tables, if not already present. Return * early if a hugepage was created. @@ -288,7 +291,8 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *m= mu, uint64_t vaddr, pte =3D virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); TEST_ASSERT(!pte_present(mmu, pte), "PTE already present for 4k page at vaddr: 0x%lx", vaddr); - *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | (paddr & PHYSIC= AL_PAGE_MASK); + *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | PTE_X_MASK(mmu) + | (paddr & PHYSICAL_PAGE_MASK); =20 /* * Neither SEV nor TDX supports shared page tables, so only the final diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index a909fad57fd5..0cba31cae896 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -25,21 +25,6 @@ bool enable_evmcs; struct hv_enlightened_vmcs *current_evmcs; struct hv_vp_assist_page *current_vp_assist; =20 -struct eptPageTableEntry { - uint64_t readable:1; - uint64_t writable:1; - uint64_t executable:1; - uint64_t memory_type:3; - uint64_t ignore_pat:1; - uint64_t page_size:1; - uint64_t accessed:1; - uint64_t dirty:1; - uint64_t ignored_11_10:2; - uint64_t address:40; - uint64_t ignored_62_52:11; - uint64_t suppress_ve:1; -}; - int vcpu_enable_evmcs(struct kvm_vcpu *vcpu) { uint16_t evmcs_ver; @@ -58,12 +43,31 @@ int vcpu_enable_evmcs(struct kvm_vcpu *vcpu) =20 void vm_enable_ept(struct kvm_vm *vm) { + struct pte_masks pte_masks; + TEST_ASSERT(kvm_cpu_has_ept(), "KVM doesn't support nested EPT"); if (vm->arch.nested.mmu) return; =20 + /* + * EPTs do not have 'present' or 'user' bits, instead bit 0 is the + * 'readable' bit. In some cases, EPTs can be execute-only and an entry + * is present but not readable. However, for the purposes of testing we + * assume 'present' =3D=3D 'user' =3D=3D 'readable' for simplicity. + */ + pte_masks =3D (struct pte_masks){ + .present =3D BIT_ULL(0), + .user =3D BIT_ULL(0), + .writable =3D BIT_ULL(1), + .x =3D BIT_ULL(2), + .accessed =3D BIT_ULL(5), + .dirty =3D BIT_ULL(6), + .huge =3D BIT_ULL(7), + .nx =3D 0, + }; + /* EPTP_PWL_4 is always used */ - vm->arch.nested.mmu =3D mmu_create(vm, 4, NULL); + vm->arch.nested.mmu =3D mmu_create(vm, 4, &pte_masks); } =20 /* Allocate memory regions for nested VMX tests. @@ -372,82 +376,6 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_r= ip, void *guest_rsp) init_vmcs_guest_state(guest_rip, guest_rsp); } =20 -static void tdp_create_pte(struct kvm_vm *vm, - struct eptPageTableEntry *pte, - uint64_t nested_paddr, - uint64_t paddr, - int current_level, - int target_level) -{ - if (!pte->readable) { - pte->writable =3D true; - pte->readable =3D true; - pte->executable =3D true; - pte->page_size =3D (current_level =3D=3D target_level); - if (pte->page_size) - pte->address =3D paddr >> vm->page_shift; - else - pte->address =3D vm_alloc_page_table(vm) >> vm->page_shift; - } else { - /* - * Entry already present. Assert that the caller doesn't want - * a hugepage at this level, and that there isn't a hugepage at - * this level. - */ - TEST_ASSERT(current_level !=3D target_level, - "Cannot create hugepage at level: %u, nested_paddr: 0x%lx", - current_level, nested_paddr); - TEST_ASSERT(!pte->page_size, - "Cannot create page table at level: %u, nested_paddr: 0x%lx", - current_level, nested_paddr); - } -} - - -void __tdp_pg_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, - int target_level) -{ - const uint64_t page_size =3D PG_LEVEL_SIZE(target_level); - void *eptp_hva =3D addr_gpa2hva(vm, vm->arch.nested.mmu->root_gpa); - struct eptPageTableEntry *pt =3D eptp_hva, *pte; - uint16_t index; - - TEST_ASSERT(vm->mode =3D=3D VM_MODE_PXXVYY_4K, - "Unknown or unsupported guest mode: 0x%x", vm->mode); - - TEST_ASSERT((nested_paddr >> 48) =3D=3D 0, - "Nested physical address 0x%lx is > 48-bits and requires 5-level EPT= ", - nested_paddr); - TEST_ASSERT((nested_paddr % page_size) =3D=3D 0, - "Nested physical address not on page boundary,\n" - " nested_paddr: 0x%lx page_size: 0x%lx", - nested_paddr, page_size); - TEST_ASSERT((nested_paddr >> vm->page_shift) <=3D vm->max_gfn, - "Physical address beyond beyond maximum supported,\n" - " nested_paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", - paddr, vm->max_gfn, vm->page_size); - TEST_ASSERT((paddr % page_size) =3D=3D 0, - "Physical address not on page boundary,\n" - " paddr: 0x%lx page_size: 0x%lx", - paddr, page_size); - TEST_ASSERT((paddr >> vm->page_shift) <=3D vm->max_gfn, - "Physical address beyond beyond maximum supported,\n" - " paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", - paddr, vm->max_gfn, vm->page_size); - - for (int level =3D PG_LEVEL_512G; level >=3D PG_LEVEL_4K; level--) { - index =3D (nested_paddr >> PG_LEVEL_SHIFT(level)) & 0x1ffu; - pte =3D &pt[index]; - - tdp_create_pte(vm, pte, nested_paddr, paddr, level, target_level); - - if (pte->page_size) - break; - - pt =3D addr_gpa2hva(vm, pte->address * vm->page_size); - } -} - /* * Map a range of EPT guest physical addresses to the VM's physical address * @@ -468,6 +396,7 @@ void __tdp_pg_map(struct kvm_vm *vm, uint64_t nested_pa= ddr, uint64_t paddr, void __tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uint64_t size, int level) { + struct kvm_mmu *mmu =3D vm->arch.nested.mmu; size_t page_size =3D PG_LEVEL_SIZE(level); size_t npages =3D size / page_size; =20 @@ -475,7 +404,7 @@ void __tdp_map(struct kvm_vm *vm, uint64_t nested_paddr= , uint64_t paddr, TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); =20 while (npages--) { - __tdp_pg_map(vm, nested_paddr, paddr, level); + __virt_pg_map(vm, mmu, nested_paddr, paddr, level); nested_paddr +=3D page_size; paddr +=3D page_size; } --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-174.mta0.migadu.com (out-174.mta0.migadu.com [91.218.175.174]) (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 64601264623 for ; Thu, 27 Nov 2025 01:35:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207318; cv=none; b=DaiiKnEir7P8FFspVTtY29F8eX5mD59xlTBnWIjDmgnriYV1ZkL7o4TrDOAxX2GK8KcJAXgKZ/dARAWS2wOpMu/WqOJbMbgc+/BpOZvetOB0pvinhKuVf4wQUrwhJU8EpDPR0VGTL/cmnssfiQL/5jUcqpd/Xa1YQEx3T62gn8Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207318; c=relaxed/simple; bh=7jysPTyIv1P0d4kKSGjyAUuqEIOTtAMqKPjVlRhWESc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=S5BWgDDwCcBL71Do6HnyayDqMf/mOfI0khGXAvWE6WE2Y9xrKlNR0RNUBajlQ3oKljroV6H6uLec+tiCO/JAeHBwuNiPv9FDjJ0OpcKt5aNWYPg2O4jGJX41oD4LSW0GzhhZ/Zu9LahD6neNioJf+uwGkHqTBOGaKeUb9BoHVvQ= 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=ci8yM1TH; arc=none smtp.client-ip=91.218.175.174 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="ci8yM1TH" 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=1764207314; 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=j3foPKGFPq15EJ4a7jcZKNWZVI98HAc3xZlYw2h3wQY=; b=ci8yM1THYDgsePbAu2q9myzREAkoDertf2Y3fIZJpaSok/sUbgwiExJadhtU16O5B9liF2 o44oUJ5S5d5qJbzNoJ5bDOErXqyb/18LVPLWbbqA83MtMEYF8ZQKhrSgrcYgpNG5w0jDeo BxzdaMsYzoMxS8p2piFc4vIrY4PTHSI= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 11/16] KVM: selftests: Move TDP mapping functions outside of vmx.c Date: Thu, 27 Nov 2025 01:34:35 +0000 Message-ID: <20251127013440.3324671-12-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" Now that the functions are no longer VMX-specific, move them to processor.c. Do a minor comment tweak replacing 'EPT' with 'TDP'. No functional change intended. Signed-off-by: Yosry Ahmed --- .../selftests/kvm/include/x86/processor.h | 4 ++ tools/testing/selftests/kvm/include/x86/vmx.h | 3 - .../testing/selftests/kvm/lib/x86/processor.c | 71 +++++++++++++++++++ tools/testing/selftests/kvm/lib/x86/vmx.c | 71 ------------------- 4 files changed, 75 insertions(+), 74 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index 62e10b296719..95216b513379 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1487,6 +1487,10 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu= *mmu, uint64_t vaddr, void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, uint64_t nr_bytes, int level); =20 +void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uin= t64_t size); +void tdp_identity_map_default_memslots(struct kvm_vm *vm); +void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size); + /* * Basic CPU control in CR0 */ diff --git a/tools/testing/selftests/kvm/include/x86/vmx.h b/tools/testing/= selftests/kvm/include/x86/vmx.h index 4dd4c2094ee6..92b918700d24 100644 --- a/tools/testing/selftests/kvm/include/x86/vmx.h +++ b/tools/testing/selftests/kvm/include/x86/vmx.h @@ -557,9 +557,6 @@ bool load_vmcs(struct vmx_pages *vmx); =20 bool ept_1g_pages_supported(void); =20 -void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uin= t64_t size); -void tdp_identity_map_default_memslots(struct kvm_vm *vm); -void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size); bool kvm_cpu_has_ept(void); void vm_enable_ept(struct kvm_vm *vm); void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm= *vm); diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index 8b0e17f8ca37..517a8185eade 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -467,6 +467,77 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u= int8_t indent) } } =20 +/* + * Map a range of TDP guest physical addresses to the VM's physical address + * + * Input Args: + * vm - Virtual Machine + * nested_paddr - Nested guest physical address to map + * paddr - VM Physical Address + * size - The size of the range to map + * level - The level at which to map the range + * + * Output Args: None + * + * Return: None + * + * Within the VM given by vm, creates a nested guest translation for the + * page range starting at nested_paddr to the page range starting at paddr. + */ +void __tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, + uint64_t size, int level) +{ + struct kvm_mmu *mmu =3D vm->arch.nested.mmu; + size_t page_size =3D PG_LEVEL_SIZE(level); + size_t npages =3D size / page_size; + + TEST_ASSERT(nested_paddr + size > nested_paddr, "Vaddr overflow"); + TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); + + while (npages--) { + __virt_pg_map(vm, mmu, nested_paddr, paddr, level); + nested_paddr +=3D page_size; + paddr +=3D page_size; + } +} + +void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, + uint64_t size) +{ + __tdp_map(vm, nested_paddr, paddr, size, PG_LEVEL_4K); +} + +/* Prepare an identity extended page table that maps all the + * physical pages in VM. + */ +void tdp_identity_map_default_memslots(struct kvm_vm *vm) +{ + uint32_t s, memslot =3D 0; + sparsebit_idx_t i, last; + struct userspace_mem_region *region =3D memslot2region(vm, memslot); + + /* Only memslot 0 is mapped here, ensure it's the only one being used */ + for (s =3D 0; s < NR_MEM_REGIONS; s++) + TEST_ASSERT_EQ(vm->memslots[s], 0); + + i =3D (region->region.guest_phys_addr >> vm->page_shift) - 1; + last =3D i + (region->region.memory_size >> vm->page_shift); + for (;;) { + i =3D sparsebit_next_clear(region->unused_phy_pages, i); + if (i > last) + break; + + tdp_map(vm, (uint64_t)i << vm->page_shift, + (uint64_t)i << vm->page_shift, 1 << vm->page_shift); + } +} + +/* Identity map a region with 1GiB Pages. */ +void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size) +{ + __tdp_map(vm, addr, addr, size, PG_LEVEL_1G); +} + /* * Set Unusable Segment * diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index 0cba31cae896..4289c9eb61ff 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -376,77 +376,6 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_r= ip, void *guest_rsp) init_vmcs_guest_state(guest_rip, guest_rsp); } =20 -/* - * Map a range of EPT guest physical addresses to the VM's physical address - * - * Input Args: - * vm - Virtual Machine - * nested_paddr - Nested guest physical address to map - * paddr - VM Physical Address - * size - The size of the range to map - * level - The level at which to map the range - * - * Output Args: None - * - * Return: None - * - * Within the VM given by vm, creates a nested guest translation for the - * page range starting at nested_paddr to the page range starting at paddr. - */ -void __tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, - uint64_t size, int level) -{ - struct kvm_mmu *mmu =3D vm->arch.nested.mmu; - size_t page_size =3D PG_LEVEL_SIZE(level); - size_t npages =3D size / page_size; - - TEST_ASSERT(nested_paddr + size > nested_paddr, "Vaddr overflow"); - TEST_ASSERT(paddr + size > paddr, "Paddr overflow"); - - while (npages--) { - __virt_pg_map(vm, mmu, nested_paddr, paddr, level); - nested_paddr +=3D page_size; - paddr +=3D page_size; - } -} - -void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, - uint64_t size) -{ - __tdp_map(vm, nested_paddr, paddr, size, PG_LEVEL_4K); -} - -/* Prepare an identity extended page table that maps all the - * physical pages in VM. - */ -void tdp_identity_map_default_memslots(struct kvm_vm *vm) -{ - uint32_t s, memslot =3D 0; - sparsebit_idx_t i, last; - struct userspace_mem_region *region =3D memslot2region(vm, memslot); - - /* Only memslot 0 is mapped here, ensure it's the only one being used */ - for (s =3D 0; s < NR_MEM_REGIONS; s++) - TEST_ASSERT_EQ(vm->memslots[s], 0); - - i =3D (region->region.guest_phys_addr >> vm->page_shift) - 1; - last =3D i + (region->region.memory_size >> vm->page_shift); - for (;;) { - i =3D sparsebit_next_clear(region->unused_phy_pages, i); - if (i > last) - break; - - tdp_map(vm, (uint64_t)i << vm->page_shift, - (uint64_t)i << vm->page_shift, 1 << vm->page_shift); - } -} - -/* Identity map a region with 1GiB Pages. */ -void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size) -{ - __tdp_map(vm, addr, addr, size, PG_LEVEL_1G); -} - bool kvm_cpu_has_ept(void) { uint64_t ctrl; --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-185.mta0.migadu.com (out-185.mta0.migadu.com [91.218.175.185]) (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 1A8B1274FF5 for ; Thu, 27 Nov 2025 01:35:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.185 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207320; cv=none; b=Sit2bZFRGfMEnAz/u9FUTOi0SamTj/3gjbd671ulLExe7sEadP3ZutuBbGt7chvo0zOLkIUpDzyUHBDc7enW9eSjqX0QYDBjkbDhsTC9MDru7B40fw69rgEUpvVUUbH/J2vzmYz3muJb7ue2UjSHq8Hk3GC8uDzYz20/Xi4VfWc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207320; c=relaxed/simple; bh=iov89OnzwSVvqHY9f1/B1XYffUPu1JAJnF9bRQI4HI0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=gMxIkKXjfUaiyP8efQ/Kwq9I8OrnorqPgcXEmu3V8rJoX3eWJPcTuHEFW8RviPGa7ma6zvhOHbAQzVsLjh9Uh+xPiJxsI+qtjTToT+REVa/uPnU+Q45wmPz/MG5/1mRb4jkvvA3AplImoxXYoODikXl4lsh7quCHWTRb3bHKgDc= 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=uSvzL6h4; arc=none smtp.client-ip=91.218.175.185 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="uSvzL6h4" 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=1764207316; 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=5f4Tl1oderqSVFxuqxAn2Fzh62L9qK3Bx3fdCjBdAZY=; b=uSvzL6h4Ue7vpSUiEM2G/Sef9dyVAe0Q70LfcrdQM3Qjt8Hlca6fGvTJ3PhRqg2nGDPEkz Bgcrh1OwBiRm/3o0v/O3vLI1VGPnyVkLAV/btX+2huWSWzq2ZTDVroZpQ62RlxnYIzDRz8 crvIveyZTewlvw0zKg/Z0u2kk9UXVOI= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 12/16] KVM: selftests: Allow kvm_cpu_has_ept() to be called on AMD CPUs Date: Thu, 27 Nov 2025 01:34:36 +0000 Message-ID: <20251127013440.3324671-13-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" In preparation for generalizing the nested dirty logging test, checking if either EPT or NPT is enabled will be needed. To avoid needing to gate the kvm_cpu_has_ept() call by the CPU type, make sure the function returns false if VMX is not available instead of trying to read VMX-only MSRs. No functional change intended. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/lib/x86/vmx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/self= tests/kvm/lib/x86/vmx.c index 4289c9eb61ff..f70d0d591d5a 100644 --- a/tools/testing/selftests/kvm/lib/x86/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86/vmx.c @@ -380,6 +380,9 @@ bool kvm_cpu_has_ept(void) { uint64_t ctrl; =20 + if (!kvm_cpu_has(X86_FEATURE_VMX)) + return false; + ctrl =3D kvm_get_feature_msr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) >> 32; if (!(ctrl & CPU_BASED_ACTIVATE_SECONDARY_CONTROLS)) return false; --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-173.mta0.migadu.com (out-173.mta0.migadu.com [91.218.175.173]) (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 46E2827C84E for ; Thu, 27 Nov 2025 01:35:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207321; cv=none; b=oaxn3YACNBSTZJpfgyJxGjbD2tgqCTlidrs5I9lYzX3V2w0XPLjKhxkpeWHT7XKdlKDT7QSLPvFgzjcxBvUk4ef3XsxJj0OMtDxGjwR4MRrKOVotKjH1FDJftC5cBl1NdxcA0kGBsSS2oMH0k4sZwZqtG2qkPVzCVWtLVwJPeGE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207321; c=relaxed/simple; bh=oHdUSiYVEwzSaWXrV9eMyKLDzfN4fxnAZloLFDC3cC4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jQcSsIMFe/gZMWTmu75U70i0o7RVY189K380xgqXsJz63x7RHQ20Wn6amh/FGCmAuUm1Rw7CxF29KMZlWhM4+T5Bn2cBfpZLtrZSJaaMa6snnap6KnAHOaHTCdWyDDhjOqBZgFF+Ibe4V2rBsGrbBMdgAAsj4k8eaKcHmzqZI2E= 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=YN9vO+kX; arc=none smtp.client-ip=91.218.175.173 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="YN9vO+kX" 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=1764207317; 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=J1c/DO+GLRLRMECkRI/ad1pfHPuVmFzReD3sWFGUK6o=; b=YN9vO+kXFHy7tNmw6FflQpjVmLWG1BzADhw/pd+i664OERXSXlgcZk/PSRXCaWhUEWWEiM 5PIrqrTL2qgoncADfxphr1o8n7XwIM71f1hqdjgDKd38ezUnbWGB+Z80QRkayoJxSlqIYZ qztJaOGlwR3lnVnzT0BaBKu5uO9Y3Bg= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 13/16] KVM: selftests: Add support for nested NPTs Date: Thu, 27 Nov 2025 01:34:37 +0000 Message-ID: <20251127013440.3324671-14-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" Implement nCR3 and NPT initialization functions, similar to the EPT equivalents, and create common TDP helpers for enablement checking and initialization. Enable NPT for nested guests by default if the TDP MMU was initialized, similar to VMX. Reuse the PTE masks from the main MMU in the NPT MMU, except for the C and S bits related to confidential VMs. Signed-off-by: Yosry Ahmed --- .../selftests/kvm/include/x86/processor.h | 2 ++ .../selftests/kvm/include/x86/svm_util.h | 9 ++++++++ .../testing/selftests/kvm/lib/x86/memstress.c | 4 ++-- .../testing/selftests/kvm/lib/x86/processor.c | 15 +++++++++++++ tools/testing/selftests/kvm/lib/x86/svm.c | 22 +++++++++++++++++++ .../selftests/kvm/x86/vmx_dirty_log_test.c | 4 ++-- 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index 95216b513379..920abd14f3a6 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1487,6 +1487,8 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu = *mmu, uint64_t vaddr, void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, uint64_t nr_bytes, int level); =20 +void vm_enable_tdp(struct kvm_vm *vm); +bool kvm_cpu_has_tdp(void); void tdp_map(struct kvm_vm *vm, uint64_t nested_paddr, uint64_t paddr, uin= t64_t size); void tdp_identity_map_default_memslots(struct kvm_vm *vm); void tdp_identity_map_1g(struct kvm_vm *vm, uint64_t addr, uint64_t size); diff --git a/tools/testing/selftests/kvm/include/x86/svm_util.h b/tools/tes= ting/selftests/kvm/include/x86/svm_util.h index b74c6dcddcbd..5d7c42534bc4 100644 --- a/tools/testing/selftests/kvm/include/x86/svm_util.h +++ b/tools/testing/selftests/kvm/include/x86/svm_util.h @@ -27,6 +27,9 @@ struct svm_test_data { void *msr; /* gva */ void *msr_hva; uint64_t msr_gpa; + + /* NPT */ + uint64_t ncr3_gpa; }; =20 static inline void vmmcall(void) @@ -57,6 +60,12 @@ struct svm_test_data *vcpu_alloc_svm(struct kvm_vm *vm, = vm_vaddr_t *p_svm_gva); void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *g= uest_rsp); void run_guest(struct vmcb *vmcb, uint64_t vmcb_gpa); =20 +static inline bool kvm_cpu_has_npt(void) +{ + return kvm_cpu_has(X86_FEATURE_NPT); +} +void vm_enable_npt(struct kvm_vm *vm); + int open_sev_dev_path_or_exit(void); =20 #endif /* SELFTEST_KVM_SVM_UTILS_H */ diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testin= g/selftests/kvm/lib/x86/memstress.c index 3319cb57a78d..407abfc34909 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -82,9 +82,9 @@ void memstress_setup_nested(struct kvm_vm *vm, int nr_vcp= us, struct kvm_vcpu *vc int vcpu_id; =20 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - TEST_REQUIRE(kvm_cpu_has_ept()); + TEST_REQUIRE(kvm_cpu_has_tdp()); =20 - vm_enable_ept(vm); + vm_enable_tdp(vm); for (vcpu_id =3D 0; vcpu_id < nr_vcpus; vcpu_id++) { vcpu_alloc_vmx(vm, &vmx_gva); =20 diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index 517a8185eade..b22c8c1bfdc3 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -8,7 +8,9 @@ #include "kvm_util.h" #include "pmu.h" #include "processor.h" +#include "svm_util.h" #include "sev.h" +#include "vmx.h" =20 #ifndef NUM_INTERRUPTS #define NUM_INTERRUPTS 256 @@ -467,6 +469,19 @@ void virt_arch_dump(FILE *stream, struct kvm_vm *vm, u= int8_t indent) } } =20 +void vm_enable_tdp(struct kvm_vm *vm) +{ + if (kvm_cpu_has(X86_FEATURE_VMX)) + vm_enable_ept(vm); + else + vm_enable_npt(vm); +} + +bool kvm_cpu_has_tdp(void) +{ + return kvm_cpu_has_ept() || kvm_cpu_has_npt(); +} + /* * Map a range of TDP guest physical addresses to the VM's physical address * diff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/self= tests/kvm/lib/x86/svm.c index d239c2097391..cf3b98802164 100644 --- a/tools/testing/selftests/kvm/lib/x86/svm.c +++ b/tools/testing/selftests/kvm/lib/x86/svm.c @@ -59,6 +59,23 @@ static void vmcb_set_seg(struct vmcb_seg *seg, u16 selec= tor, seg->base =3D base; } =20 +void vm_enable_npt(struct kvm_vm *vm) +{ + struct pte_masks pte_masks; + + TEST_ASSERT(kvm_cpu_has_npt(), "KVM doesn't supported nested NPT"); + + if (vm->arch.nested.mmu) + return; + + /* NPTs use the same PTE format, except for C/S bits */ + pte_masks =3D vm->arch.mmu->pte_masks; + pte_masks.c =3D 0; + pte_masks.s =3D 0; + + vm->arch.nested.mmu =3D mmu_create(vm, vm->pgtable_levels, &pte_masks); +} + void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *g= uest_rsp) { struct vmcb *vmcb =3D svm->vmcb; @@ -102,6 +119,11 @@ void generic_svm_setup(struct svm_test_data *svm, void= *guest_rip, void *guest_r vmcb->save.rip =3D (u64)guest_rip; vmcb->save.rsp =3D (u64)guest_rsp; guest_regs.rdi =3D (u64)svm; + + if (svm->ncr3_gpa) { + ctrl->nested_ctl |=3D SVM_NESTED_CTL_NP_ENABLE; + ctrl->nested_cr3 =3D svm->ncr3_gpa; + } } =20 /* diff --git a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c b/tools/t= esting/selftests/kvm/x86/vmx_dirty_log_test.c index 370f8d3117c2..032ab8bf60a4 100644 --- a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c @@ -93,7 +93,7 @@ static void test_vmx_dirty_log(bool enable_ept) /* Create VM */ vm =3D vm_create_with_one_vcpu(&vcpu, l1_guest_code); if (enable_ept) - vm_enable_ept(vm); + vm_enable_tdp(vm); =20 vcpu_alloc_vmx(vm, &vmx_pages_gva); vcpu_args_set(vcpu, 1, vmx_pages_gva); @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) =20 test_vmx_dirty_log(/*enable_ept=3D*/false); =20 - if (kvm_cpu_has_ept()) + if (kvm_cpu_has_tdp()) test_vmx_dirty_log(/*enable_ept=3D*/true); =20 return 0; --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-189.mta0.migadu.com (out-189.mta0.migadu.com [91.218.175.189]) (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 9D94F296BA2 for ; Thu, 27 Nov 2025 01:35:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.189 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207323; cv=none; b=oqCI/yYKODH6lm+26M5pNmODkXzxt3gGgOaCP6AA3TROgmvlz7Ku5fLL/JgJjaekbWBofhRW9X7HrgWe6p5WkQHX8Y3+SVjh5GTkAPh41xKlBqtR1mXYbFEHBLJanldB/nhSrmgtxgKC+xsNz3zRyxxOJGc2ZrMWBF2hU/JEShs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207323; c=relaxed/simple; bh=1DWJ0vq5Vnbhy9GpTMm1kM3h66r8u2AM0I+qwZIpt1s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dyj79U4Fto1YpzheIthbTQIQ76CxWRU/Vv5FLRGhJow2dSHL33MvIb0JXzLMWZW2FLZRiG0ydQanEc/ewT7SPeJQkf/ZQTC/XUJ9hr15sA+8m7nAzzG5ox4nxJTazoHV/k6YwjaikvmO8wJwxf3pkQ2vK8QtT+/yQP8e3pcjrzQ= 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=iWAfUKbo; arc=none smtp.client-ip=91.218.175.189 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="iWAfUKbo" 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=1764207319; 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=fRvdalb8Nz4KrVxtn9MByTFzTbfGayUg/tC+6ctnf4M=; b=iWAfUKboKdh5xonWPG0KNDL7cViftZuXMR4LaXsDEtJQJ4eYdlJOsEGrkFSGDYZTcSDN8O pM43h/xFg9CbM2sa9WL6JS1lrm2WRexO6KcmqJDNOfkWwanNru9kLWPbqUUk/z4GCgFKnG gubWwpk7wKovSFLQ6hparqCslg1UB1Q= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 14/16] KVM: selftests: Set the user bit on nested NPT PTEs Date: Thu, 27 Nov 2025 01:34:38 +0000 Message-ID: <20251127013440.3324671-15-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" According to the APM, NPT walks are treated as user accesses. In preparation for supporting NPT mappings, set the 'user' bit on NPTs by adding a mask of bits to always be set on PTEs in kvm_mmu. Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/include/x86/processor.h | 4 ++++ tools/testing/selftests/kvm/lib/x86/processor.c | 6 ++++-- tools/testing/selftests/kvm/lib/x86/svm.c | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/te= sting/selftests/kvm/include/x86/processor.h index 920abd14f3a6..d41245e2521b 100644 --- a/tools/testing/selftests/kvm/include/x86/processor.h +++ b/tools/testing/selftests/kvm/include/x86/processor.h @@ -1450,6 +1450,8 @@ struct pte_masks { uint64_t x; uint64_t c; uint64_t s; + + uint64_t always_set; }; =20 struct kvm_mmu { @@ -1469,6 +1471,8 @@ struct kvm_mmu { #define PTE_C_MASK(mmu) ((mmu)->pte_masks.c) #define PTE_S_MASK(mmu) ((mmu)->pte_masks.s) =20 +#define PTE_ALWAYS_SET_MASK(mmu) ((mmu)->pte_masks.always_set) + #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))) diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index b22c8c1bfdc3..749ae7522473 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -227,7 +227,8 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *v= m, paddr =3D vm_untag_gpa(vm, paddr); =20 if (!pte_present(mmu, pte)) { - *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | PTE_X_MASK(mmu= ); + *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) + | PTE_X_MASK(mmu) | PTE_ALWAYS_SET_MASK(mmu); if (current_level =3D=3D target_level) *pte |=3D PTE_HUGE_MASK(mmu) | (paddr & PHYSICAL_PAGE_MASK); else @@ -293,7 +294,8 @@ void __virt_pg_map(struct kvm_vm *vm, struct kvm_mmu *m= mu, uint64_t vaddr, pte =3D virt_get_pte(vm, mmu, pte, vaddr, PG_LEVEL_4K); TEST_ASSERT(!pte_present(mmu, pte), "PTE already present for 4k page at vaddr: 0x%lx", vaddr); - *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) | PTE_X_MASK(mmu) + *pte =3D PTE_PRESENT_MASK(mmu) | PTE_WRITABLE_MASK(mmu) + | PTE_X_MASK(mmu) | PTE_ALWAYS_SET_MASK(mmu) | (paddr & PHYSICAL_PAGE_MASK); =20 /* diff --git a/tools/testing/selftests/kvm/lib/x86/svm.c b/tools/testing/self= tests/kvm/lib/x86/svm.c index cf3b98802164..838f218545af 100644 --- a/tools/testing/selftests/kvm/lib/x86/svm.c +++ b/tools/testing/selftests/kvm/lib/x86/svm.c @@ -73,6 +73,9 @@ void vm_enable_npt(struct kvm_vm *vm) pte_masks.c =3D 0; pte_masks.s =3D 0; =20 + /* NPT walks are treated as user accesses, so set the 'user' bit */ + pte_masks.always_set =3D pte_masks.user; + vm->arch.nested.mmu =3D mmu_create(vm, vm->pgtable_levels, &pte_masks); } =20 --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-183.mta0.migadu.com (out-183.mta0.migadu.com [91.218.175.183]) (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 D7B9029D27A for ; Thu, 27 Nov 2025 01:35:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.183 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207326; cv=none; b=tb3LxNefaVEmrfEUEch2193bJ06mfSPLEm55ComTeZLlkgd8wZxC8LajBLp6hBW8MvSKaKjSl72TF1zAjC7sbghYlRKDJ3Fb1IRvnwHu6f5Zj0Vfi3za5clutda1MBrI2b0c5gkhDrKmT2CEGTgk672dD1XNDbHeZh1AwEpOTZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207326; c=relaxed/simple; bh=ALNAqsDuhnVL2He7t/utahOtIzjNwUpHovIEWfitFa4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=m0kSve/mt8BsBAP08wNvsj7ntFlet6B040nbc/svSL0dzx5KUW/vaiBgBIqVgH028SF+U6nd90u91JQEonHD61YyPDme3SZ3b75MErZnr4CFvGqlIi9boKujA/zTy2kbY0eCJT997+xOawFvOob0SS65jT3ZRK89BjTa51cPHuk= 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=XmgwjNLB; arc=none smtp.client-ip=91.218.175.183 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="XmgwjNLB" 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=1764207321; 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=ctJxzwz5rT3wHcbuIHvcnCZUvktxJjr+ZoYTEWOhgnw=; b=XmgwjNLBt1uTMJO4TPZ8y9L4SBmneXZmqSCeVaWaFb1f+DQrvVxIQtifoQhd9rs7kCSNMO 8y3wo61mnMgVmWHEb3S/VW/mwXLT1XSSaeHJG4vzatg7fYfEU/sAotK380r05/BGKIx1EW PsoKLfAHdxClB0qqrRUDA2O+jnPM/Cw= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 15/16] KVM: selftests: Extend vmx_dirty_log_test to cover SVM Date: Thu, 27 Nov 2025 01:34:39 +0000 Message-ID: <20251127013440.3324671-16-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" Generalize the code in vmx_dirty_log_test.c by adding SVM-specific L1 code, doing some renaming (e.g. EPT -> TDP), and having setup code for both SVM and VMX in test_dirty_log(). Signed-off-by: Yosry Ahmed --- tools/testing/selftests/kvm/Makefile.kvm | 2 +- ...rty_log_test.c =3D> nested_dirty_log_test.c} | 73 ++++++++++++++----- 2 files changed, 54 insertions(+), 21 deletions(-) rename tools/testing/selftests/kvm/x86/{vmx_dirty_log_test.c =3D> nested_d= irty_log_test.c} (71%) diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selft= ests/kvm/Makefile.kvm index 7ebf30a87a2b..13fe403f5d82 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -89,6 +89,7 @@ TEST_GEN_PROGS_x86 +=3D x86/kvm_buslock_test TEST_GEN_PROGS_x86 +=3D x86/monitor_mwait_test TEST_GEN_PROGS_x86 +=3D x86/msrs_test TEST_GEN_PROGS_x86 +=3D x86/nested_close_kvm_test +TEST_GEN_PROGS_x86 +=3D x86/nested_dirty_log_test TEST_GEN_PROGS_x86 +=3D x86/nested_emulation_test TEST_GEN_PROGS_x86 +=3D x86/nested_exceptions_test TEST_GEN_PROGS_x86 +=3D x86/nested_invalid_cr3_test @@ -115,7 +116,6 @@ TEST_GEN_PROGS_x86 +=3D x86/ucna_injection_test TEST_GEN_PROGS_x86 +=3D x86/userspace_io_test TEST_GEN_PROGS_x86 +=3D x86/userspace_msr_exit_test TEST_GEN_PROGS_x86 +=3D x86/vmx_apic_access_test -TEST_GEN_PROGS_x86 +=3D x86/vmx_dirty_log_test TEST_GEN_PROGS_x86 +=3D x86/vmx_exception_with_invalid_guest_state TEST_GEN_PROGS_x86 +=3D x86/vmx_msrs_test TEST_GEN_PROGS_x86 +=3D x86/vmx_invalid_nested_guest_state diff --git a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c b/tools/t= esting/selftests/kvm/x86/nested_dirty_log_test.c similarity index 71% rename from tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c rename to tools/testing/selftests/kvm/x86/nested_dirty_log_test.c index 032ab8bf60a4..89d2e86a0db9 100644 --- a/tools/testing/selftests/kvm/x86/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86/nested_dirty_log_test.c @@ -12,6 +12,7 @@ #include "test_util.h" #include "kvm_util.h" #include "processor.h" +#include "svm_util.h" #include "vmx.h" =20 /* The memory slot index to track dirty pages */ @@ -25,6 +26,8 @@ #define NESTED_TEST_MEM1 0xc0001000 #define NESTED_TEST_MEM2 0xc0002000 =20 +#define L2_GUEST_STACK_SIZE 64 + static void l2_guest_code(u64 *a, u64 *b) { READ_ONCE(*a); @@ -42,20 +45,19 @@ static void l2_guest_code(u64 *a, u64 *b) vmcall(); } =20 -static void l2_guest_code_ept_enabled(void) +static void l2_guest_code_tdp_enabled(void) { l2_guest_code((u64 *)NESTED_TEST_MEM1, (u64 *)NESTED_TEST_MEM2); } =20 -static void l2_guest_code_ept_disabled(void) +static void l2_guest_code_tdp_disabled(void) { - /* Access the same L1 GPAs as l2_guest_code_ept_enabled() */ + /* Access the same L1 GPAs as l2_guest_code_tdp_enabled() */ l2_guest_code((u64 *)GUEST_TEST_MEM, (u64 *)GUEST_TEST_MEM); } =20 -void l1_guest_code(struct vmx_pages *vmx) +void l1_vmx_code(struct vmx_pages *vmx) { -#define L2_GUEST_STACK_SIZE 64 unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; void *l2_rip; =20 @@ -64,22 +66,49 @@ void l1_guest_code(struct vmx_pages *vmx) GUEST_ASSERT(load_vmcs(vmx)); =20 if (vmx->eptp_gpa) - l2_rip =3D l2_guest_code_ept_enabled; + l2_rip =3D l2_guest_code_tdp_enabled; else - l2_rip =3D l2_guest_code_ept_disabled; + l2_rip =3D l2_guest_code_tdp_disabled; =20 prepare_vmcs(vmx, l2_rip, &l2_guest_stack[L2_GUEST_STACK_SIZE]); =20 GUEST_SYNC(false); GUEST_ASSERT(!vmlaunch()); GUEST_SYNC(false); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) =3D=3D EXIT_REASON_VMCALL); + GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL); GUEST_DONE(); } =20 -static void test_vmx_dirty_log(bool enable_ept) +static void l1_svm_code(struct svm_test_data *svm) { - vm_vaddr_t vmx_pages_gva =3D 0; + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + void *l2_rip; + + if (svm->ncr3_gpa) + l2_rip =3D l2_guest_code_tdp_enabled; + else + l2_rip =3D l2_guest_code_tdp_disabled; + + generic_svm_setup(svm, l2_rip, &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + GUEST_SYNC(false); + run_guest(svm->vmcb, svm->vmcb_gpa); + GUEST_SYNC(false); + GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMMCALL); + GUEST_DONE(); +} + +static void l1_guest_code(void *data) +{ + if (this_cpu_has(X86_FEATURE_VMX)) + l1_vmx_code(data); + else + l1_svm_code(data); +} + +static void test_dirty_log(bool nested_tdp) +{ + vm_vaddr_t nested_gva =3D 0; unsigned long *bmap; uint64_t *host_test_mem; =20 @@ -88,15 +117,19 @@ static void test_vmx_dirty_log(bool enable_ept) struct ucall uc; bool done =3D false; =20 - pr_info("Nested EPT: %s\n", enable_ept ? "enabled" : "disabled"); + pr_info("Nested TDP: %s\n", nested_tdp ? "enabled" : "disabled"); =20 /* Create VM */ vm =3D vm_create_with_one_vcpu(&vcpu, l1_guest_code); - if (enable_ept) + if (nested_tdp) vm_enable_tdp(vm); =20 - vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vcpu, 1, vmx_pages_gva); + if (kvm_cpu_has(X86_FEATURE_VMX)) + vcpu_alloc_vmx(vm, &nested_gva); + else + vcpu_alloc_svm(vm, &nested_gva); + + vcpu_args_set(vcpu, 1, nested_gva); =20 /* Add an extra memory slot for testing dirty logging */ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, @@ -115,10 +148,10 @@ static void test_vmx_dirty_log(bool enable_ept) * ... pages in the L2 GPA range [0xc0001000, 0xc0003000) will map to * 0xc0000000. * - * When EPT is disabled, the L2 guest code will still access the same L1 - * GPAs as the EPT enabled case. + * When TDP is disabled, the L2 guest code will still access the same L1 + * GPAs as the TDP enabled case. */ - if (enable_ept) { + if (nested_tdp) { tdp_identity_map_default_memslots(vm); tdp_map(vm, NESTED_TEST_MEM1, GUEST_TEST_MEM, PAGE_SIZE); tdp_map(vm, NESTED_TEST_MEM2, GUEST_TEST_MEM, PAGE_SIZE); @@ -166,12 +199,12 @@ static void test_vmx_dirty_log(bool enable_ept) =20 int main(int argc, char *argv[]) { - TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX) || kvm_cpu_has(X86_FEATURE_SVM)= ); =20 - test_vmx_dirty_log(/*enable_ept=3D*/false); + test_dirty_log(/*nested_tdp=3D*/false); =20 if (kvm_cpu_has_tdp()) - test_vmx_dirty_log(/*enable_ept=3D*/true); + test_dirty_log(/*nested_tdp=3D*/true); =20 return 0; } --=20 2.52.0.158.g65b55ccf14-goog From nobody Mon Dec 1 22:02:20 2025 Received: from out-180.mta0.migadu.com (out-180.mta0.migadu.com [91.218.175.180]) (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 ACD722BE7B1 for ; Thu, 27 Nov 2025 01:35:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207326; cv=none; b=gL9rL/LrNDmaxNeCZouA+Mg72OE9/WT49UL3EEPVgXyg2dY7K+QeTVq4Ez4sFS9DnYQGM2Vk8jQXNt50RPgXH7EwgCYOsRclSelpPXsKbPf3WN5e+IPLgF2M8GtAmZDtxFwXHKuo23YXQiin4fOQcFwwuzFR0MjzFFtDE7PszZU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1764207326; c=relaxed/simple; bh=fM2U9rXbLFACC7DHpSZE7lUsaAwQh/Zi05xmZm288pI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lm6yWdc/VUTomxN5dx6KFaNJPzrVMFrXLa5Zun+7bLqWDkmL/CwU/1nz5GPFFzFG8mC7BX8d01RJRHsMX+zn6NoVI51CJpUnEUy7uAwM77z94xXoJAlsfQ7EBHKBfjFCbvvPz+EobTZI1is2qPIKGvBIIiEYv00uKEXxmYrcQl0= 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=LGyV/JDz; arc=none smtp.client-ip=91.218.175.180 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="LGyV/JDz" 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=1764207322; 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=GWwtx5jx/PEkw/2itB59dU+0w4OnN5aW9YGDJcyPCFs=; b=LGyV/JDzd6JHrZGcZwoMT68JFibeAPmZRUE2ZFRLir3XpYrGea8eeI8JGg66Bi3QFl99CE 1Ge3SxhSFHmtDucCidhXfnxe3WDe72012Em8iDezEUorqgQ0JPkv3OaP+ZNkxDhWAlS97S sAmRKtNFmH/w1VqVAKh2CYU5hwe+qAc= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [PATCH v3 16/16] KVM: selftests: Extend memstress to run on nested SVM Date: Thu, 27 Nov 2025 01:34:40 +0000 Message-ID: <20251127013440.3324671-17-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" Add L1 SVM code and generalize the setup code to work for both VMX and SVM. This allows running 'dirty_log_perf_test -n' on AMD CPUs. Signed-off-by: Yosry Ahmed --- .../testing/selftests/kvm/lib/x86/memstress.c | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/x86/memstress.c b/tools/testin= g/selftests/kvm/lib/x86/memstress.c index 407abfc34909..86f4c5e4c430 100644 --- a/tools/testing/selftests/kvm/lib/x86/memstress.c +++ b/tools/testing/selftests/kvm/lib/x86/memstress.c @@ -13,6 +13,7 @@ #include "kvm_util.h" #include "memstress.h" #include "processor.h" +#include "svm_util.h" #include "vmx.h" =20 void memstress_l2_guest_code(uint64_t vcpu_id) @@ -29,9 +30,10 @@ __asm__( " ud2;" ); =20 -static void memstress_l1_guest_code(struct vmx_pages *vmx, uint64_t vcpu_i= d) -{ #define L2_GUEST_STACK_SIZE 64 + +static void l1_vmx_code(struct vmx_pages *vmx, uint64_t vcpu_id) +{ unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; unsigned long *rsp; =20 @@ -45,10 +47,34 @@ static void memstress_l1_guest_code(struct vmx_pages *v= mx, uint64_t vcpu_id) prepare_vmcs(vmx, memstress_l2_guest_entry, rsp); =20 GUEST_ASSERT(!vmlaunch()); - GUEST_ASSERT(vmreadz(VM_EXIT_REASON) =3D=3D EXIT_REASON_VMCALL); + GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL); + GUEST_DONE(); +} + +static void l1_svm_code(struct svm_test_data *svm, uint64_t vcpu_id) +{ + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + unsigned long *rsp; + + + rsp =3D &l2_guest_stack[L2_GUEST_STACK_SIZE - 1]; + *rsp =3D vcpu_id; + generic_svm_setup(svm, memstress_l2_guest_entry, rsp); + + run_guest(svm->vmcb, svm->vmcb_gpa); + GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMMCALL); GUEST_DONE(); } =20 + +static void memstress_l1_guest_code(void *data, uint64_t vcpu_id) +{ + if (this_cpu_has(X86_FEATURE_VMX)) + l1_vmx_code(data, vcpu_id); + else + l1_svm_code(data, vcpu_id); +} + uint64_t memstress_nested_pages(int nr_vcpus) { /* @@ -78,15 +104,17 @@ static void memstress_setup_ept_mappings(struct kvm_vm= *vm) void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vc= pu *vcpus[]) { struct kvm_regs regs; - vm_vaddr_t vmx_gva; + vm_vaddr_t nested_gva; int vcpu_id; =20 - TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); TEST_REQUIRE(kvm_cpu_has_tdp()); =20 vm_enable_tdp(vm); for (vcpu_id =3D 0; vcpu_id < nr_vcpus; vcpu_id++) { - vcpu_alloc_vmx(vm, &vmx_gva); + if (kvm_cpu_has(X86_FEATURE_VMX)) + vcpu_alloc_vmx(vm, &nested_gva); + else + vcpu_alloc_svm(vm, &nested_gva); =20 /* The EPTs are shared across vCPUs, setup the mappings once */ if (vcpu_id =3D=3D 0) @@ -99,6 +127,6 @@ void memstress_setup_nested(struct kvm_vm *vm, int nr_vc= pus, struct kvm_vcpu *vc vcpu_regs_get(vcpus[vcpu_id], ®s); regs.rip =3D (unsigned long) memstress_l1_guest_code; vcpu_regs_set(vcpus[vcpu_id], ®s); - vcpu_args_set(vcpus[vcpu_id], 2, vmx_gva, vcpu_id); + vcpu_args_set(vcpus[vcpu_id], 2, nested_gva, vcpu_id); } } --=20 2.52.0.158.g65b55ccf14-goog