All of lore.kernel.org
 help / color / mirror / Atom feed
From: Fuad Tabba <fuad.tabba@linux.dev>
To: Marc Zyngier <maz@kernel.org>, Oliver Upton <oupton@kernel.org>
Cc: Will Deacon <will@kernel.org>,
	Paolo Bonzini <pbonzini@redhat.com>,
	Sean Christopherson <seanjc@google.com>,
	Shuah Khan <shuah@kernel.org>, Joey Gouly <joey.gouly@arm.com>,
	Steffen Eiden <seiden@linux.ibm.com>,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Zenghui Yu <yuzenghui@huawei.com>,
	Vincent Donnefort <vdonnefort@google.com>,
	Quentin Perret <qperret@google.com>,
	Fuad Tabba <tabba@google.com>,
	kvmarm@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	kvm@vger.kernel.org, linux-kselftest@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 07/16] KVM: arm64: selftests: Add pkvm_host_abi host-interface test
Date: Wed,  4 Nov 2026 08:48:33 +0000	[thread overview]
Message-ID: <20261104084842.132944-8-fuad.tabba@linux.dev> (raw)
In-Reply-To: <20261104084842.132944-1-fuad.tabba@linux.dev>

The host isn't trusted with a protected guest's state, so once a
protected vCPU has run, KVM rejects the ioctls that would read or change
that state. The host can still inject a virtual SError and read back
whether one is pending. The VM gets only the capabilities and vCPU
features pKVM supports.

Check what the host's KVM interface allows and rejects for a protected
VM, before and after the first run, and that the host accounts the VM's
memory as locked until the VM is freed.

Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../selftests/kvm/arm64/pkvm_host_abi.c       | 428 ++++++++++++++++++
 2 files changed, 429 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/arm64/pkvm_host_abi.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index e173a9adad3f2..42e530e0e573b 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -190,6 +190,7 @@ TEST_GEN_PROGS_arm64 += arm64/nv_pre_fault_memory_test
 TEST_GEN_PROGS_arm64 += arm64/external_aborts
 TEST_GEN_PROGS_arm64 += arm64/mmio_sign_ext
 TEST_GEN_PROGS_arm64 += arm64/page_fault_test
+TEST_GEN_PROGS_arm64 += arm64/pkvm_host_abi
 TEST_GEN_PROGS_arm64 += arm64/psci_test
 TEST_GEN_PROGS_arm64 += arm64/sea_to_user
 TEST_GEN_PROGS_arm64 += arm64/set_id_regs
diff --git a/tools/testing/selftests/kvm/arm64/pkvm_host_abi.c b/tools/testing/selftests/kvm/arm64/pkvm_host_abi.c
new file mode 100644
index 0000000000000..4e048c6c2752d
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/pkvm_host_abi.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pkvm_host_abi - what the host's KVM interface allows a protected VM.
+ *
+ * Copyright (C) 2025 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ */
+
+#include <errno.h>
+#include <sys/mman.h>
+
+#include <linux/bits.h>
+#include <linux/psci.h>
+
+#include "kvm_util.h"
+#include "pkvm.h"
+#include "processor.h"
+#include "test_util.h"
+
+#define TEST_MEMSLOT	(PKVM_IDMAP_MEMSLOT + 1)
+#define TEST_GPA	BIT_ULL(30)
+#define TEST_PAGES	16
+
+/* Every probe sets PSCI_0_2, so that VCPU_INIT fails only on the feature under test. */
+#define PVM_INIT_FEATURES	BIT(KVM_ARM_VCPU_PSCI_0_2)
+
+#define CAP(c)	{ KVM_CAP_##c, "KVM_CAP_" #c }
+
+/* Caps a protected VM has on any pKVM host. */
+static const struct {
+	long cap;
+	const char *name;
+} allowed_caps[] = {
+	CAP(ONE_REG),
+	CAP(ARM_PSCI),
+	CAP(ARM_PSCI_0_2),
+	CAP(NR_VCPUS),
+	CAP(MAX_VCPUS),
+	CAP(MAX_VCPU_ID),
+	CAP(ARM_VM_IPA_SIZE),
+	CAP(MP_STATE),
+	CAP(VCPU_EVENTS),
+	CAP(VCPU_ATTRIBUTES),
+	CAP(IMMEDIATE_EXIT),
+	CAP(IOEVENTFD),
+	CAP(IRQFD_RESAMPLE),
+	CAP(ARM_IRQ_LINE_LAYOUT_2),
+};
+
+/* Set in the guest's copy by the host before the first run. */
+static u64 test_size;
+
+static void guest_finalize(void)
+{
+	pkvm_guest_init();
+	GUEST_DONE();
+}
+
+/* One write per MIN_PAGE_SIZE faults in every page of the test memslot. */
+static void guest_touch_memory(void)
+{
+	volatile u64 *end = (u64 *)(TEST_GPA + test_size);
+	volatile u64 *p;
+
+	pkvm_guest_init();
+
+	for (p = (u64 *)TEST_GPA; p < end; p += MIN_PAGE_SIZE / sizeof(*p))
+		*p = 1;
+
+	GUEST_DONE();
+}
+
+/* A protected VM with one vCPU and no VCPU_INIT yet. */
+static struct kvm_vcpu *create_uninitialized_vcpu(struct kvm_vm **vm_out)
+{
+	struct vm_shape shape = VM_SHAPE_DEFAULT;
+
+	shape.type = KVM_VM_TYPE_ARM_PROTECTED;
+	*vm_out = __vm_create(shape, 1, 0);
+	return __vm_vcpu_add(*vm_out, 0);
+}
+
+/* The errno of KVM_ARM_VCPU_INIT with @features on a protected vCPU, or 0. */
+static int vcpu_init_errno(u32 features)
+{
+	struct kvm_vcpu_init init;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	int err;
+
+	vcpu = create_uninitialized_vcpu(&vm);
+	kvm_get_default_vcpu_target(vm, &init);
+	init.features[0] |= features;
+
+	err = __vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, &init) ? errno : 0;
+
+	kvm_vm_free(vm);
+	return err;
+}
+
+static void assert_feature_accepted(u32 feature, const char *name)
+{
+	int err = vcpu_init_errno(PVM_INIT_FEATURES | feature);
+
+	TEST_ASSERT(!err, "%s on a protected vCPU: errno %d", name, err);
+	ksft_test_result_pass("%s accepted at VCPU_INIT\n", name);
+}
+
+static void assert_feature_rejected(u32 feature, const char *name)
+{
+	int err = vcpu_init_errno(PVM_INIT_FEATURES | feature);
+
+	TEST_ASSERT(err == EINVAL, "%s on a protected vCPU: errno %d", name, err);
+	ksft_test_result_pass("%s rejected at VCPU_INIT\n", name);
+}
+
+static void test_vcpu_init_conditional_features(void)
+{
+	int err = vcpu_init_errno(0);
+
+	TEST_ASSERT(err == EINVAL, "no PSCI_0_2 on a protected vCPU: errno %d", err);
+	assert_feature_accepted(BIT(KVM_ARM_VCPU_PSCI_0_2), "PSCI_0_2");
+
+	if (kvm_has_cap(KVM_CAP_ARM_PMU_V3))
+		assert_feature_rejected(BIT(KVM_ARM_VCPU_PMU_V3), "PMU_V3");
+	else
+		ksft_test_result_skip("PMU_V3 rejected at VCPU_INIT: PMUv3 unavailable\n");
+
+	if (kvm_has_cap(KVM_CAP_ARM_SVE))
+		assert_feature_rejected(BIT(KVM_ARM_VCPU_SVE), "SVE");
+	else
+		ksft_test_result_skip("SVE rejected at VCPU_INIT: SVE unavailable\n");
+
+	if (kvm_has_cap(KVM_CAP_ARM_PTRAUTH_ADDRESS) && kvm_has_cap(KVM_CAP_ARM_PTRAUTH_GENERIC))
+		assert_feature_accepted(BIT(KVM_ARM_VCPU_PTRAUTH_ADDRESS) |
+					BIT(KVM_ARM_VCPU_PTRAUTH_GENERIC), "PtrAuth");
+	else
+		ksft_test_result_skip("PtrAuth accepted at VCPU_INIT: PtrAuth unavailable\n");
+}
+
+/* KVM_ARM_VCPU_INIT can be repeated until the vCPU's first run. */
+static void test_vcpu_init_repeated(void)
+{
+	struct kvm_vcpu_init init;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+
+	vcpu = create_uninitialized_vcpu(&vm);
+	kvm_get_default_vcpu_target(vm, &init);
+	init.features[0] |= PVM_INIT_FEATURES;
+	vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, &init);
+	vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, &init);
+	kvm_vm_free(vm);
+
+	ksft_test_result_pass("VCPU_INIT repeated before the first run\n");
+}
+
+static void assert_ext(struct kvm_vm *vm, long ext, bool want_nonzero, const char *name)
+{
+	int r = __vm_ioctl(vm, KVM_CHECK_EXTENSION, (void *)ext);
+
+	if (want_nonzero)
+		TEST_ASSERT(r > 0, "%s: expected nonzero, got %d", name, r);
+	else
+		TEST_ASSERT(r == 0, "%s: expected 0, got %d", name, r);
+}
+
+static void test_caps(void)
+{
+	struct kvm_vm *vm = vm_create_barebones_type(KVM_VM_TYPE_ARM_PROTECTED);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(allowed_caps); i++) {
+		assert_ext(vm, allowed_caps[i].cap, true, allowed_caps[i].name);
+		ksft_test_result_pass("%s allowed\n", allowed_caps[i].name);
+	}
+
+	if (kvm_has_cap(KVM_CAP_ARM_PTRAUTH_ADDRESS) && kvm_has_cap(KVM_CAP_ARM_PTRAUTH_GENERIC)) {
+		assert_ext(vm, KVM_CAP_ARM_PTRAUTH_ADDRESS, true, "KVM_CAP_ARM_PTRAUTH_ADDRESS");
+		assert_ext(vm, KVM_CAP_ARM_PTRAUTH_GENERIC, true, "KVM_CAP_ARM_PTRAUTH_GENERIC");
+		ksft_test_result_pass("KVM_CAP_ARM_PTRAUTH_* allowed\n");
+	} else {
+		ksft_test_result_skip("KVM_CAP_ARM_PTRAUTH_* allowed: PtrAuth unavailable\n");
+	}
+
+	if (kvm_has_cap(KVM_CAP_ARM_SVE)) {
+		assert_ext(vm, KVM_CAP_ARM_SVE, false, "KVM_CAP_ARM_SVE");
+		ksft_test_result_pass("KVM_CAP_ARM_SVE blocked\n");
+	} else {
+		ksft_test_result_skip("KVM_CAP_ARM_SVE blocked: SVE unavailable\n");
+	}
+
+	if (kvm_has_cap(KVM_CAP_ARM_PMU_V3)) {
+		assert_ext(vm, KVM_CAP_ARM_PMU_V3, false, "KVM_CAP_ARM_PMU_V3");
+		ksft_test_result_pass("KVM_CAP_ARM_PMU_V3 blocked\n");
+	} else {
+		ksft_test_result_skip("KVM_CAP_ARM_PMU_V3 blocked: PMUv3 unavailable\n");
+	}
+
+	assert_ext(vm, KVM_CAP_READONLY_MEM, false, "KVM_CAP_READONLY_MEM");
+	ksft_test_result_pass("KVM_CAP_READONLY_MEM blocked\n");
+
+	assert_ext(vm, KVM_CAP_SET_GUEST_DEBUG, false, "KVM_CAP_SET_GUEST_DEBUG");
+	ksft_test_result_pass("KVM_CAP_SET_GUEST_DEBUG blocked\n");
+
+	assert_ext(vm, KVM_CAP_ARM_INJECT_EXT_DABT, false, "KVM_CAP_ARM_INJECT_EXT_DABT");
+	ksft_test_result_pass("KVM_CAP_ARM_INJECT_EXT_DABT blocked\n");
+
+	/* Blocked explicitly, and reads 0 anyway on a host without MTE. */
+	assert_ext(vm, KVM_CAP_ARM_MTE, false, "KVM_CAP_ARM_MTE");
+	ksft_test_result_pass("KVM_CAP_ARM_MTE blocked\n");
+
+	kvm_vm_free(vm);
+}
+
+/* VM ioctls whose capability a protected VM doesn't get fail with EINVAL. */
+static void test_vm_ioctls_rejected(void)
+{
+	struct kvm_vm *vm = vm_create_barebones_type(KVM_VM_TYPE_ARM_PROTECTED);
+	struct kvm_arm_counter_offset offset = {};
+	struct reg_mask_range range = {};
+	int ret;
+
+	ret = __kvm_has_device_attr(vm->fd, KVM_ARM_VM_SMCCC_CTRL, KVM_ARM_VM_SMCCC_FILTER);
+	TEST_ASSERT(ret == -1 && errno == EINVAL, "SMCCC_CTRL: rc %d errno %d", ret, errno);
+	ret = __vm_ioctl(vm, KVM_ARM_SET_COUNTER_OFFSET, &offset);
+	TEST_ASSERT(ret == -1 && errno == EINVAL, "SET_COUNTER_OFFSET: rc %d errno %d", ret, errno);
+	ret = __vm_ioctl(vm, KVM_ARM_GET_REG_WRITABLE_MASKS, &range);
+	TEST_ASSERT(ret == -1 && errno == EINVAL, "GET_REG_WRITABLE_MASKS: rc %d errno %d",
+		    ret, errno);
+
+	kvm_vm_free(vm);
+	ksft_test_result_pass("VM ioctls without their capability rejected\n");
+}
+
+/* The host allows every ONE_REG until a protected vCPU's first KVM_RUN. */
+static void test_one_reg_gate(void)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	u64 val;
+	int ret;
+
+	vm = pkvm_vm_create(1, 0, &vcpu);
+
+	/*
+	 * pkvm_vcpu_setup_boot() snapshots regs[0..7] and SCTLR_EL1 for the
+	 * trampoline, so write back SCTLR_EL1 unchanged and probe regs[10].
+	 */
+	ret = __vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &val);
+	TEST_ASSERT_EQ(ret, 0);
+	ret = __vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), val);
+	TEST_ASSERT_EQ(ret, 0);
+	ret = __vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &val);
+	TEST_ASSERT_EQ(ret, 0);
+	ret = __vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.regs[10]), 0x1234);
+	TEST_ASSERT_EQ(ret, 0);
+
+	pkvm_vcpu_setup_boot(vcpu, guest_finalize);
+	pkvm_run_loop(vcpu, NULL);
+
+	ret = __vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), 0);
+	TEST_ASSERT(ret == -1 && errno == EPERM, "non-CORE post-run: rc %d errno %d", ret, errno);
+	ret = __vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &val);
+	TEST_ASSERT(ret == -1 && errno == EPERM,
+		    "non-CORE get post-run: rc %d errno %d", ret, errno);
+
+	ret = __vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &val);
+	TEST_ASSERT(ret == -1 && errno == EPERM, "CORE pc post-run: rc %d errno %d", ret, errno);
+	ret = __vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.regs[4]), 0);
+	TEST_ASSERT(ret == -1 && errno == EPERM,
+		    "CORE regs[4] post-run: rc %d errno %d", ret, errno);
+	ksft_test_result_pass("ONE_REG gate\n");
+
+	kvm_vm_free(vm);
+}
+
+static void test_vcpu_ioctl_gates(void)
+{
+	struct kvm_guest_debug dbg = { .control = KVM_GUESTDBG_ENABLE };
+	struct kvm_vcpu_events events = {};
+	struct kvm_vcpu_init init;
+	struct kvm_vcpu *vcpu;
+	u64 ipa = TEST_GPA;
+	struct kvm_vm *vm;
+	int ret;
+
+	vm = pkvm_vm_create(1, 0, &vcpu);
+
+	ret = __vcpu_ioctl(vcpu, KVM_SET_GUEST_DEBUG, &dbg);
+	TEST_ASSERT(ret == -1 && errno == EPERM, "pre-run SET_GUEST_DEBUG: rc %d errno %d",
+		    ret, errno);
+	/* EL2 implements PSCI 1.1, so the host can't set an older version. */
+	ret = __vcpu_set_reg(vcpu, KVM_REG_ARM_PSCI_VERSION, PSCI_VERSION(1, 0));
+	TEST_ASSERT(ret == -1 && errno == EINVAL, "PSCI_VERSION 1.0: rc %d errno %d", ret, errno);
+	vcpu_set_reg(vcpu, KVM_REG_ARM_PSCI_VERSION, PSCI_VERSION(1, 1));
+	ksft_test_result_pass("pre-run: SET_GUEST_DEBUG refused, PSCI_VERSION 1.1 at least\n");
+
+	pkvm_vcpu_setup_boot(vcpu, guest_finalize);
+	pkvm_run_loop(vcpu, NULL);
+
+	ret = __vcpu_ioctl(vcpu, KVM_SET_GUEST_DEBUG, &dbg);
+	TEST_ASSERT(ret == -1 && errno == EPERM, "post-run SET_GUEST_DEBUG: rc %d errno %d",
+		    ret, errno);
+	kvm_get_default_vcpu_target(vm, &init);
+	init.features[0] |= PVM_INIT_FEATURES;
+	ret = __vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, &init);
+	TEST_ASSERT(ret == -1 && errno == EPERM, "post-run VCPU_INIT: rc %d errno %d", ret, errno);
+
+	events.exception.ext_dabt_pending = 1;
+	ret = __vcpu_ioctl(vcpu, KVM_SET_VCPU_EVENTS, &events);
+	TEST_ASSERT(ret == -1 && errno == EPERM, "ext_dabt_pending: rc %d errno %d", ret, errno);
+	events.exception.ext_dabt_pending = 0;
+	events.exception.serror_pending = 1;
+	vcpu_events_set(vcpu, &events);
+	memset(&events, 0, sizeof(events));
+	vcpu_events_get(vcpu, &events);
+	TEST_ASSERT(events.exception.serror_pending, "post-run SError not read back");
+
+	ret = __vcpu_has_device_attr(vcpu, KVM_ARM_VCPU_PVTIME_CTRL, KVM_ARM_VCPU_PVTIME_IPA);
+	TEST_ASSERT(ret == -1 && errno == ENXIO, "HAS PVTIME_IPA: rc %d errno %d", ret, errno);
+	ret = __vcpu_device_attr_get(vcpu, KVM_ARM_VCPU_PVTIME_CTRL, KVM_ARM_VCPU_PVTIME_IPA, &ipa);
+	TEST_ASSERT(ret == -1 && errno == ENXIO, "GET PVTIME_IPA: rc %d errno %d", ret, errno);
+	ret = __vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PVTIME_CTRL, KVM_ARM_VCPU_PVTIME_IPA, &ipa);
+	TEST_ASSERT(ret == -1 && errno == ENXIO, "SET PVTIME_IPA: rc %d errno %d", ret, errno);
+
+	kvm_vm_free(vm);
+	ksft_test_result_pass("post-run: debug, VCPU_INIT, abort injection and PVTIME refused\n");
+}
+
+/* GET_REG_LIST is gated on VCPU_INIT, not on the vCPU having run. */
+static void test_get_reg_list_gate(void)
+{
+	struct kvm_reg_list reg_list = { .n = 0 };
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	int ret;
+
+	vcpu = create_uninitialized_vcpu(&vm);
+	ret = __vcpu_ioctl(vcpu, KVM_GET_REG_LIST, &reg_list);
+	TEST_ASSERT(ret == -1 && errno == ENOEXEC, "uninitialized: rc %d errno %d", ret, errno);
+	kvm_vm_free(vm);
+
+	vm = pkvm_vm_create(1, 0, &vcpu);
+	free(vcpu_get_reg_list(vcpu));
+	kvm_vm_free(vm);
+	ksft_test_result_pass("GET_REG_LIST gate\n");
+}
+
+static void test_memslot_flags_rejected(void)
+{
+	size_t size = getpagesize();
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	void *hva;
+	int ret;
+
+	vm = pkvm_vm_create(1, 0, &vcpu);
+
+	hva = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	TEST_ASSERT(hva != MAP_FAILED, "mmap failed: %d", errno);
+
+	ret = __vm_set_user_memory_region2(vm, TEST_MEMSLOT, KVM_MEM_READONLY, TEST_GPA,
+					   size, hva, -1, 0);
+	TEST_ASSERT(ret == -1 && errno == EPERM,
+		    "read-only memslot on a protected VM: rc %d errno %d", ret, errno);
+	ksft_test_result_pass("read-only memslot rejected at creation\n");
+
+	ret = __vm_set_user_memory_region2(vm, TEST_MEMSLOT, KVM_MEM_LOG_DIRTY_PAGES, TEST_GPA,
+					   size, hva, -1, 0);
+	TEST_ASSERT(ret == -1 && errno == EPERM,
+		    "dirty-logging memslot on a protected VM: rc %d errno %d", ret, errno);
+
+	vm_set_user_memory_region2(vm, TEST_MEMSLOT, 0, TEST_GPA, size, hva, -1, 0);
+	ret = __vm_set_user_memory_region2(vm, TEST_MEMSLOT, KVM_MEM_LOG_DIRTY_PAGES, TEST_GPA,
+					   size, hva, -1, 0);
+	TEST_ASSERT(ret == -1 && errno == EPERM,
+		    "dirty logging enabled on a protected VM: rc %d errno %d", ret, errno);
+	ksft_test_result_pass("dirty logging rejected at creation and when enabled\n");
+
+	munmap(hva, size);
+	kvm_vm_free(vm);
+}
+
+/* The host pins and accounts a protected VM's memory until the VM is freed. */
+static void test_memory_locked(void)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	size_t locked;
+
+	vm = pkvm_vm_create_with_one_vcpu(&vcpu, guest_touch_memory);
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, TEST_GPA, TEST_MEMSLOT,
+				    TEST_PAGES, 0);
+	virt_map(vm, TEST_GPA, TEST_GPA, TEST_PAGES);
+	write_guest_global(vm, test_size, TEST_PAGES * vm->page_size);
+
+	pkvm_run_loop(vcpu, NULL);
+	locked = pkvm_host_locked_vm();
+	TEST_ASSERT(locked > TEST_PAGES * vm->page_size,
+		    "guest memory not locked while the VM is alive: %zu bytes", locked);
+
+	kvm_vm_free(vm);
+	TEST_ASSERT_EQ(pkvm_host_locked_vm(), 0);
+
+	ksft_test_result_pass("memory locked while running\n");
+}
+
+int main(void)
+{
+	TEST_REQUIRE(pkvm_supported());
+
+	ksft_print_header();
+	ksft_set_plan(ARRAY_SIZE(allowed_caps) + 21);
+
+	assert_feature_rejected(BIT(KVM_ARM_VCPU_EL1_32BIT), "EL1_32BIT");
+	test_vcpu_init_conditional_features();
+	test_vcpu_init_repeated();
+	test_caps();
+	test_vm_ioctls_rejected();
+	test_one_reg_gate();
+	test_vcpu_ioctl_gates();
+	test_get_reg_list_gate();
+	test_memslot_flags_rejected();
+	test_memory_locked();
+
+	ksft_finished();
+}
-- 
2.47.3


  parent reply	other threads:[~2026-11-04  8:49 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-04  8:48 [PATCH 00/16] KVM: arm64: selftests: Tests for protected VMs Fuad Tabba
2026-11-04  8:48 ` [PATCH 01/16] KVM: arm64: selftests: Inject an abort before a vCPU's first run Fuad Tabba
2026-11-04  8:48 ` [PATCH 02/16] KVM: selftests: Allow arm64 tests to create protected VMs Fuad Tabba
2026-11-05 12:16   ` Joey Gouly
2026-11-04  8:48 ` [PATCH 03/16] tools: arm-smccc: Add the pKVM memory sharing function IDs Fuad Tabba
2026-11-04  8:48 ` [PATCH 04/16] KVM: selftests: Export the ucall pool's guest physical address Fuad Tabba
2026-11-04  8:48 ` [PATCH 05/16] KVM: arm64: selftests: Add protected-VM boot trampoline Fuad Tabba
2026-11-05 13:34   ` Joey Gouly
2026-11-04  8:48 ` [PATCH 06/16] KVM: arm64: selftests: Add protected-VM library helpers Fuad Tabba
2026-11-06 14:50   ` Joey Gouly
2026-11-06 15:35     ` Fuad Tabba
2026-11-04  8:48 ` Fuad Tabba [this message]
2026-11-04  8:48 ` [PATCH 08/16] KVM: arm64: selftests: Add pkvm_psci multi-vCPU PSCI test Fuad Tabba
2026-11-04  8:48 ` [PATCH 09/16] KVM: arm64: selftests: Add pkvm_mem_share hypercall test Fuad Tabba
2026-11-04  8:48 ` [PATCH 10/16] KVM: arm64: selftests: Add pkvm_smccc " Fuad Tabba
2026-11-04  8:48 ` [PATCH 11/16] KVM: arm64: selftests: Add pkvm_id_regs register and trap test Fuad Tabba
2026-11-04  8:48 ` [PATCH 12/16] KVM: arm64: selftests: Add pkvm_vcpu_regs register-state test Fuad Tabba
2026-11-04  8:48 ` [PATCH 13/16] KVM: arm64: selftests: Add pkvm_irq interrupt test Fuad Tabba
2026-11-04  8:48 ` [PATCH 14/16] KVM: arm64: selftests: Add pkvm_isolation host-access test Fuad Tabba
2026-11-04  8:48 ` [PATCH 15/16] KVM: arm64: selftests: Add pkvm_lifecycle teardown test Fuad Tabba
2026-11-04  8:48 ` [PATCH 16/16] KVM: arm64: selftests: Add pkvm_exceptions debug and SError test Fuad Tabba
2026-11-04 14:10 ` [PATCH 00/16] KVM: arm64: selftests: Tests for protected VMs Joey Gouly
2026-11-04 19:38   ` Fuad Tabba

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=20261104084842.132944-8-fuad.tabba@linux.dev \
    --to=fuad.tabba@linux.dev \
    --cc=joey.gouly@arm.com \
    --cc=kvm@vger.kernel.org \
    --cc=kvmarm@lists.linux.dev \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=maz@kernel.org \
    --cc=oupton@kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=qperret@google.com \
    --cc=seanjc@google.com \
    --cc=seiden@linux.ibm.com \
    --cc=shuah@kernel.org \
    --cc=suzuki.poulose@arm.com \
    --cc=tabba@google.com \
    --cc=vdonnefort@google.com \
    --cc=will@kernel.org \
    --cc=yuzenghui@huawei.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.