From: Sean Christopherson <seanjc@google.com>
To: Kevin Cheng <chengkev@google.com>
Cc: pbonzini@redhat.com, kvm@vger.kernel.org,
linux-kernel@vger.kernel.org, yosry.ahmed@linux.dev
Subject: Re: [PATCH 3/3] KVM: selftests: Add nested NPF injection test for SVM
Date: Thu, 22 Jan 2026 17:13:48 -0800 [thread overview]
Message-ID: <aXLLTA5MtOEYf8k2@google.com> (raw)
In-Reply-To: <20260121004906.2373989-4-chengkev@google.com>
On Wed, Jan 21, 2026, Kevin Cheng wrote:
> ---
> tools/testing/selftests/kvm/Makefile.kvm | 1 +
> .../selftests/kvm/x86/svm_nested_npf_test.c | 154 ++++++++++++++++++
> 2 files changed, 155 insertions(+)
> create mode 100644 tools/testing/selftests/kvm/x86/svm_nested_npf_test.c
>
> diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
> index e88699e227ddf..8babe6e228e11 100644
> --- a/tools/testing/selftests/kvm/Makefile.kvm
> +++ b/tools/testing/selftests/kvm/Makefile.kvm
> @@ -112,6 +112,7 @@ TEST_GEN_PROGS_x86 += x86/svm_vmcall_test
> TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test
> TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test
> TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test
> +TEST_GEN_PROGS_x86 += x86/svm_nested_npf_test
a, b, c, d, e, f, g, h, i, j, k, l, m, N, o, p, q, r, S, t, u, v, w, x, y, z
> TEST_GEN_PROGS_x86 += x86/tsc_scaling_sync
> TEST_GEN_PROGS_x86 += x86/sync_regs_test
> TEST_GEN_PROGS_x86 += x86/ucna_injection_test
> diff --git a/tools/testing/selftests/kvm/x86/svm_nested_npf_test.c b/tools/testing/selftests/kvm/x86/svm_nested_npf_test.c
> new file mode 100644
> index 0000000000000..c0a894acbc483
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/x86/svm_nested_npf_test.c
> @@ -0,0 +1,154 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * svm_nested_npf_test
> + *
> + * Test nested NPF injection when the original VM exit was not an NPF.
> + * This exercises nested_svm_inject_npf_exit() with exit_code != SVM_EXIT_NPF.
> + *
> + * L2 executes OUTS with the source address mapped in L2's page tables but
> + * not in L1's NPT. KVM emulates the string I/O instruction, and when it
> + * tries to read the source operand, the GPA->HPA translation fails. KVM
> + * then injects an NPF to L1 even though the original exit was IOIO.
> + *
> + * Test 1: Final data page GPA not in NPT (PFERR_GUEST_FINAL_MASK)
> + * Test 2: Page table page GPA not in NPT (PFERR_GUEST_PAGE_MASK)
Please don't add file-level comments (the Copyright is fine), because things like
the name of the test/file inevitably become stale, and they're useless, and the
description of _what_ the test is doing is almost always more helpful if it's
the comment is closer to the code it's documenting.
> + *
> + * Copyright (C) 2025, Google, Inc.
> + */
> +
> +#include "test_util.h"
> +#include "kvm_util.h"
> +#include "processor.h"
> +#include "svm_util.h"
> +
> +#define L2_GUEST_STACK_SIZE 64
> +
> +enum test_type {
> + TEST_FINAL_PAGE_UNMAPPED, /* Final data page GPA not in NPT */
> + TEST_PT_PAGE_UNMAPPED, /* Page table page GPA not in NPT */
> +};
> +
> +static void *l2_test_page;
Why store it as a "void *"? Just track a vm_addr_t and avoid a bunch of casts.
> +
> +#define TEST_IO_PORT 0x80
> +#define TEST1_VADDR 0x8000000ULL
> +#define TEST2_VADDR 0x10000000ULL
> +
> +/*
> + * L2 executes OUTS with source at l2_test_page, triggering a nested NPF.
> + * The address is mapped in L2's page tables, but either the data page or
> + * a PT page is unmapped from L1's NPT, causing the fault.
> + */
> +static void l2_guest_code(void *unused)
> +{
> + asm volatile("outsb" ::"S"(l2_test_page), "d"(TEST_IO_PORT) : "memory");
> + GUEST_ASSERT(0);
GUEST_FAIL
> +}
> +
...
> +static void run_test(enum test_type type)
> +{
> + vm_paddr_t expected_fault_gpa;
> + uint64_t exit_info_1_mask;
> + vm_vaddr_t svm_gva;
> +
> + struct kvm_vcpu *vcpu;
> + struct kvm_vm *vm;
> + struct ucall uc;
> +
> + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
> + vm_enable_npt(vm);
> + vcpu_alloc_svm(vm, &svm_gva);
> +
> + if (type == TEST_FINAL_PAGE_UNMAPPED) {
> + /*
> + * Test 1: Unmap the final data page from NPT. The page table
> + * walk succeeds, but the final GPA->HPA translation fails.
> + */
> + l2_test_page =
> + (void *)vm_vaddr_alloc(vm, vm->page_size, TEST1_VADDR);
> + expected_fault_gpa = addr_gva2gpa(vm, (vm_vaddr_t)l2_test_page);
> + exit_info_1_mask = PFERR_GUEST_FINAL_MASK;
> + } else {
> + /*
> + * Test 2: Unmap a PT page from NPT. The hardware page table
> + * walk fails when translating the PT page's GPA through NPT.
> + */
> + l2_test_page =
> + (void *)vm_vaddr_alloc(vm, vm->page_size, TEST2_VADDR);
> + expected_fault_gpa =
> + get_pt_gpa_for_vaddr(vm, (vm_vaddr_t)l2_test_page);
> + exit_info_1_mask = PFERR_GUEST_PAGE_MASK;
> + }
> +
> + tdp_identity_map_default_memslots(vm);
> + tdp_unmap(vm, expected_fault_gpa, vm->page_size);
Hrm. This should really be a vendor agnostic test. There exactly results are
vendor specific, but thye core concept and pretty much all of the configuration
is nearly identical.
It'd also be nice to support more than just !PRESENT, e.g. to verify protection
violations and other things that set PFERR/EXIT_QUAL bits.
prev parent reply other threads:[~2026-01-23 1:13 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-21 0:49 [PATCH 0/3] KVM: SVM: Set PFERR_GUEST_{PAGE,FINAL}_MASK for nested NPF and add selftest Kevin Cheng
2026-01-21 0:49 ` [PATCH 1/3] KVM: SVM: Fix nested NPF injection to set PFERR_GUEST_{PAGE,FINAL}_MASK Kevin Cheng
2026-01-21 22:07 ` Sean Christopherson
2026-01-22 0:45 ` Yosry Ahmed
2026-01-28 15:48 ` Sean Christopherson
2026-02-04 16:22 ` Kevin Cheng
2026-02-06 0:21 ` Sean Christopherson
2026-01-21 0:49 ` [PATCH 2/3] KVM: selftests: Add TDP unmap helpers Kevin Cheng
2026-01-21 22:21 ` Sean Christopherson
2026-01-21 0:49 ` [PATCH 3/3] KVM: selftests: Add nested NPF injection test for SVM Kevin Cheng
2026-01-23 1:13 ` Sean Christopherson [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aXLLTA5MtOEYf8k2@google.com \
--to=seanjc@google.com \
--cc=chengkev@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=pbonzini@redhat.com \
--cc=yosry.ahmed@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox