Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Yosry Ahmed <yosry@kernel.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Jim Mattson <jmattson@google.com>,
	kvm@vger.kernel.org,  linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3] KVM: selftests: Add a test for gPAT handling in L2
Date: Wed, 27 May 2026 18:01:48 -0700	[thread overview]
Message-ID: <aheT_AUgWYJxZJlR@google.com> (raw)
In-Reply-To: <20260521175948.522757-1-yosry@kernel.org>

On Thu, May 21, 2026, Yosry Ahmed wrote:
> +static void l1_guest_code_invalid_gpat(struct svm_test_data *svm)
> +{
> +	unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
> +	struct vmcb *vmcb = svm->vmcb;
> +
> +	/* VMRUN should fail without running L2 */
> +	generic_svm_setup(svm, NULL, &l2_guest_stack[L2_GUEST_STACK_SIZE]);
> +
> +	vmcb->save.g_pat = INVALID_PAT_VALUE;
> +	run_guest(vmcb, svm->vmcb_gpa);
> +
> +	GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_ERR);
> +	GUEST_DONE();
> +}
> +
> +static void run_test(const char *test_name, void *guest_code, bool do_save_restore)
> +{
> +	struct kvm_x86_state *state;
> +	struct kvm_vcpu *vcpu;
> +	struct kvm_vm *vm;
> +	struct ucall uc;
> +	gva_t svm_gva;
> +
> +	pr_info("Testing: %s\n", test_name);
> +
> +	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
> +	vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2,
> +		      KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT);
> +
> +	if (npt_enabled) {
> +		vm_enable_npt(vm);
> +		tdp_identity_map_default_memslots(vm);
> +	}
> +
> +	vcpu_alloc_svm(vm, &svm_gva);
> +	vcpu_args_set(vcpu, 1, svm_gva);
> +	sync_global_to_guest(vm, npt_enabled);
> +	sync_global_to_guest(vm, nr_iterations);
> +
> +	for (;;) {
> +		vcpu_run(vcpu);
> +		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
> +
> +		switch (get_ucall(vcpu, &uc)) {
> +		case UCALL_ABORT:
> +			REPORT_GUEST_ASSERT(uc);
> +			/* NOT REACHED */
> +		case UCALL_SYNC:
> +			if (do_save_restore) {
> +				pr_info("  Save/restore at sync point %ld\n",
> +					uc.args[1]);

Meh, this is noise to me.  Maybe it's marginally useful if there's a failure, but
I don't see how it can possibly provide enough information to have to avoid
hacking on the test.

> +				state = vcpu_save_state(vcpu);
> +				kvm_vm_release(vm);
> +				vcpu = vm_recreate_with_one_vcpu(vm);
> +				vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2,
> +					      KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT);
> +				vcpu_load_state(vcpu, state);
> +				kvm_x86_state_cleanup(state);
> +			}
> +			break;
> +		case UCALL_DONE:
> +			pr_info("  PASSED\n");

This is useless and noisy.  The test runs in ~.25 seconds, and most of that is
the printks.  If there's a failure, I'll see.  Otherwise it's a pretty safe
assumption the test passed.

> +			kvm_vm_free(vm);
> +			return;
> +		default:
> +			TEST_FAIL("Unknown ucall %lu", uc.cmd);
> +		}
> +	}
> +}
> +
> +#define NPT_DISABLED 0
> +#define NPT_ENABLED 1
> +
> +#define NO_SAVE_RESTORE 0
> +#define DO_SAVE_RESTORE 1

Eww.

> +
> +#define NESTED_PAT_TEST(test_name, guest_code, npt, sr, iter)	\
> +({								\

Unless you need to "return" a value, do-while (0) is preferred over ({}).  There's
a technical reason, but I can't remember it off the top of my head.

> +	npt_enabled = npt;					\
> +	nr_iterations = iter;					\
> +	run_test(test_name, guest_code, sr);			\

LOL, all of this, and the one thing that _doesn't_ use macro shenanigans is
string concatenation.

> +})
> +
> +int main(int argc, char *argv[])
> +{
> +	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
> +	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_NPT));
> +	TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE));
> +	TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) &
> +		     KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT);
> +
> +	NESTED_PAT_TEST("Invalid gPAT", l1_guest_code_invalid_gpat,
> +			NPT_ENABLED, NO_SAVE_RESTORE, 1);
> +
> +	NESTED_PAT_TEST("Nested NPT disabled", l1_guest_code,
> +			NPT_DISABLED, NO_SAVE_RESTORE, 1);
> +
> +	NESTED_PAT_TEST("Nested NPT enabled", l1_guest_code,
> +			NPT_ENABLED, NO_SAVE_RESTORE, 1);
> +
> +	NESTED_PAT_TEST("Save/Restore", l1_guest_code,
> +			NPT_ENABLED, DO_SAVE_RESTORE, 1);
> +
> +	NESTED_PAT_TEST("Multiple VM-Entries", l1_guest_code,
> +			NPT_ENABLED, NO_SAVE_RESTORE, 10);

I appreciated trying to avoid true/false literals, but this is harder to read.
At least "true" and "false" are visually different, {N,D}O_SAVE_RESTORE are one
char, and NPT_{DIS,EN}ABLED are two.

And explicitly defining the number of iterations and whether or not to do save/restore
is silly.  It requires _more_ code to provide _less_ coverage.  Just run the combos
for npt_enabled=false.

If we're going to use macro shenanigans, let's actually use them!  I've got this
working, I'll post a v4.

#define gpat_test(test_name, guest_code, npt_input)			\
do {									\
	npt_input;							\
									\
	pr_info("Testing: " test_name "\n");				\
	run_test(guest_code, false, 1);					\
									\
	if (guest_code == l1_guest_code) {				\
		pr_info("Testing: " test_name " Save/Restore\n");	\
		run_test(guest_code, true, 1);				\
									\
		pr_info("Testing: " test_name " Multiple VMRUNs\n");	\
		run_test(guest_code, false, 10);			\
	}								\
} while (0)

int main(int argc, char *argv[])
{
	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_NPT));
	TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE));
	TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) &
		     KVM_X86_QUIRK_NESTED_SVM_SHARED_PAT);

	gpat_test("Invalid gPAT", l1_guest_code_invalid_gpat, npt_enabled = true);
	gpat_test("Nested NPT disabled", l1_guest_code, npt_enabled = false);
	gpat_test("Nested NPT enabled", l1_guest_code, npt_enabled = true);

	return 0;
}

> +
> +	return 0;
> +}
> 
> base-commit: 4f256d5770febb9d61f9b57a4c79c491bf4987f1
> -- 
> 2.54.0.794.g4f17f83d09-goog
> 

  reply	other threads:[~2026-05-28  1:01 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-21 17:59 [PATCH v3] KVM: selftests: Add a test for gPAT handling in L2 Yosry Ahmed
2026-05-28  1:01 ` Sean Christopherson [this message]
2026-05-28 17:47   ` Yosry Ahmed
2026-05-28 17:58     ` Sean Christopherson

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=aheT_AUgWYJxZJlR@google.com \
    --to=seanjc@google.com \
    --cc=jmattson@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=yosry@kernel.org \
    /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