All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] KVM: selftests: Add test case for readonly memslots on x86
@ 2026-02-26  7:35 Yohei Kojima
  2026-02-26  7:37 ` [PATCH 1/2] KVM: selftests: Extract memslot setup code from spawn_vm() Yohei Kojima
  2026-02-26  7:37 ` [PATCH 2/2] KVM: selftests: Add test case for readonly memslots on x86 Yohei Kojima
  0 siblings, 2 replies; 3+ messages in thread
From: Yohei Kojima @ 2026-02-26  7:35 UTC (permalink / raw)
  To: Paolo Bonzini, Shuah Khan
  Cc: Yohei Kojima, kvm, linux-kselftest, linux-kernel, Daniel Palmer,
	Tim Bird

Currently, there are no x86 test cases to verify that read and execute
on RO-memslot succeeds, and write is blocked as MMIO fault. This
behavior might break if changes are made to the MMIO path, because
x86's RO-memslot relies on the fault propagation mechanism for MMIO.

The first patch adds a function vm_userspace_mem_region_add_map(), which
is essentially the memslot setup code extracted from spawn_vm(). The
second patch adds an x86 test case for read/write/exec on RO-memslot.

This series is developed based on kvm/master, although this series adds
a test case for x86. This is because the first patch changes the
spawn_vm() function, which is used in the tests for other architectures.

base-commit: 0de4a0eec25b9171f2a2abb1a820e125e6797770

Yohei Kojima (2):
  KVM: selftests: Extract memslot setup code from spawn_vm()
  KVM: selftests: Add test case for readonly memslots on x86

 .../selftests/kvm/set_memory_region_test.c    | 147 +++++++++++++++---
 1 file changed, 129 insertions(+), 18 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] KVM: selftests: Extract memslot setup code from spawn_vm()
  2026-02-26  7:35 [PATCH 0/2] KVM: selftests: Add test case for readonly memslots on x86 Yohei Kojima
@ 2026-02-26  7:37 ` Yohei Kojima
  2026-02-26  7:37 ` [PATCH 2/2] KVM: selftests: Add test case for readonly memslots on x86 Yohei Kojima
  1 sibling, 0 replies; 3+ messages in thread
From: Yohei Kojima @ 2026-02-26  7:37 UTC (permalink / raw)
  To: Paolo Bonzini, Shuah Khan
  Cc: Yohei Kojima, Daniel Palmer, Tim Bird, kvm, linux-kselftest,
	linux-kernel

Add vm_userspace_mem_region_add_map() function to set up memslot, and
update spawn_vm() to use it. This helps the test cases to create
additional memslots easily.

Signed-off-by: Yohei Kojima <yohei.kojima@sony.com>
---
 .../selftests/kvm/set_memory_region_test.c    | 47 ++++++++++++-------
 1 file changed, 29 insertions(+), 18 deletions(-)

diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c
index 7fe427ff9b38..8d4fd713347c 100644
--- a/tools/testing/selftests/kvm/set_memory_region_test.c
+++ b/tools/testing/selftests/kvm/set_memory_region_test.c
@@ -107,31 +107,42 @@ static void wait_for_vcpu(void)
 	usleep(100000);
 }
 
+static void vm_userspace_mem_region_add_map(struct kvm_vm *vm,
+		uint64_t addr, uint32_t slot, size_t size, uint32_t flags)
+{
+	uint64_t *hva;
+	uint64_t gpa;
+
+	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_THP,
+				    addr, slot,
+				    size / getpagesize(), flags);
+
+	/*
+	 * Allocate and map two pages so that the GPA accessed by guest_code()
+	 * stays valid across the memslot move.
+	 */
+	gpa = vm_phy_pages_alloc(vm, 2, addr, slot);
+	TEST_ASSERT(gpa == addr, "Failed vm_phy_pages_alloc\n");
+
+	virt_map(vm, addr, addr, 2);
+
+	/* Ditto for the host mapping so that both pages can be zeroed. */
+	hva = addr_gpa2hva(vm, MEM_REGION_GPA);
+	memset(hva, 0, 2 * 4096);
+}
+
 static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread,
 			       void *guest_code)
 {
 	struct kvm_vm *vm;
-	uint64_t *hva;
-	uint64_t gpa;
 
 	vm = vm_create_with_one_vcpu(vcpu, guest_code);
 
-	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_THP,
-				    MEM_REGION_GPA, MEM_REGION_SLOT,
-				    MEM_REGION_SIZE / getpagesize(), 0);
-
-	/*
-	 * Allocate and map two pages so that the GPA accessed by guest_code()
-	 * stays valid across the memslot move.
-	 */
-	gpa = vm_phy_pages_alloc(vm, 2, MEM_REGION_GPA, MEM_REGION_SLOT);
-	TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc\n");
-
-	virt_map(vm, MEM_REGION_GPA, MEM_REGION_GPA, 2);
-
-	/* Ditto for the host mapping so that both pages can be zeroed. */
-	hva = addr_gpa2hva(vm, MEM_REGION_GPA);
-	memset(hva, 0, 2 * 4096);
+	vm_userspace_mem_region_add_map(vm,
+					MEM_REGION_GPA,
+					MEM_REGION_SLOT,
+					MEM_REGION_SIZE,
+					0);
 
 	pthread_create(vcpu_thread, NULL, vcpu_worker, *vcpu);
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] KVM: selftests: Add test case for readonly memslots on x86
  2026-02-26  7:35 [PATCH 0/2] KVM: selftests: Add test case for readonly memslots on x86 Yohei Kojima
  2026-02-26  7:37 ` [PATCH 1/2] KVM: selftests: Extract memslot setup code from spawn_vm() Yohei Kojima
@ 2026-02-26  7:37 ` Yohei Kojima
  1 sibling, 0 replies; 3+ messages in thread
From: Yohei Kojima @ 2026-02-26  7:37 UTC (permalink / raw)
  To: Paolo Bonzini, Shuah Khan
  Cc: Yohei Kojima, Daniel Palmer, Tim Bird, kvm, linux-kselftest,
	linux-kernel

Extend set_memory_region_test to verify the following properties:

* read on RO-memslot succeeds,
* execute on RO-memslot backed by executable memory succeeds, and
* write on RO-memslot fails with mmio fault.

Signed-off-by: Yohei Kojima <yohei.kojima@sony.com>
---
 .../selftests/kvm/set_memory_region_test.c    | 100 ++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c
index 8d4fd713347c..2f21bfcbc821 100644
--- a/tools/testing/selftests/kvm/set_memory_region_test.c
+++ b/tools/testing/selftests/kvm/set_memory_region_test.c
@@ -28,7 +28,10 @@
  * Somewhat arbitrary location and slot, intended to not overlap anything.
  */
 #define MEM_REGION_GPA		0xc0000000
+#define MEM_REGION_RO_GPA	0xd0000000
+
 #define MEM_REGION_SLOT		10
+#define MEM_REGION_RO_SLOT	11
 
 static const uint64_t MMIO_VAL = 0xbeefull;
 
@@ -49,6 +52,8 @@ static inline uint64_t guest_spin_on_val(uint64_t spin_val)
 	return val;
 }
 
+static int allow_mmio_fault;
+
 static void *vcpu_worker(void *data)
 {
 	struct kvm_vcpu *vcpu = data;
@@ -76,6 +81,13 @@ static void *vcpu_worker(void *data)
 		if (run->exit_reason != KVM_EXIT_MMIO)
 			break;
 
+		if (allow_mmio_fault && run->mmio.is_write)
+			/*
+			 * in this case, skip checking mmio-related assertions
+			 * and exit status
+			 */
+			return NULL;
+
 		TEST_ASSERT(!run->mmio.is_write, "Unexpected exit mmio write");
 		TEST_ASSERT(run->mmio.len == 8,
 			    "Unexpected exit mmio size = %u", run->mmio.len);
@@ -336,6 +348,92 @@ static void test_delete_memory_region(bool disable_slot_zap_quirk)
 	kvm_vm_free(vm);
 }
 
+static void guest_code_ro_memory_region(void)
+{
+	uint64_t val;
+	unsigned char c;
+	void *instruction_addr;
+
+	GUEST_SYNC(0);
+
+	val = guest_spin_on_val(0);
+	__GUEST_ASSERT(val == 1, "Expected '1', got '%lx'", val);
+
+	/* RO memory read; should succeed if the backing memory is readable */
+	c = *(unsigned char *)MEM_REGION_RO_GPA;
+	__GUEST_ASSERT(c == 0xab, "Expected '0xab', got '0x%x'", c);
+
+	/* RO memory exec; should succeed if the backing memory is executable */
+	instruction_addr = ((unsigned char *)MEM_REGION_RO_GPA) + 8;
+	val = ((uint32_t (*)(void))instruction_addr)();
+	__GUEST_ASSERT(val == 0xbeef,
+			"Expected 0xbeef, but got '%lx'", val);
+
+	/* Spin until the mmio fault is allowed for RO-memslot write */
+	val = guest_spin_on_val(1);
+	__GUEST_ASSERT(val == 2, "Expected '2', got '%lx'", val);
+
+	/* RO memory write; should fail */
+	WRITE_ONCE(*((uint64_t *)MEM_REGION_RO_GPA), 0x12);
+	__GUEST_ASSERT(0, "RO memory write is expected to fail, but it didn't");
+}
+
+/*
+ * On x86 environment, write access to the readonly memslots are trapped as
+ * a special MMIO fault. This test verifies that write access on the readonly
+ * memslot is blocked, and read/exec access isn't.
+ */
+static void test_ro_memory_region(void)
+{
+	pthread_t vcpu_thread;
+	uint64_t *hva, *hva_ro;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+
+	/*
+	 * Equivalent C function (assuming SysV ABI):
+	 * uint32_t some_function(void) {
+	 * 	return 0xbeef;
+	 * }
+	 */
+	unsigned char inst_bytes[] = {
+		0x48, 0xc7, 0xc0, 0xef, 0xbe, 0x00, 0x00,	// mov %eax, $0xbeef
+		0xc3,						// ret
+	};
+
+	pr_info("Testing write on RO memslot\n");
+
+	vm = spawn_vm(&vcpu, &vcpu_thread, guest_code_ro_memory_region);
+
+	vm_userspace_mem_region_add_map(vm,
+					MEM_REGION_RO_GPA,
+					MEM_REGION_RO_SLOT,
+					MEM_REGION_SIZE,
+					KVM_MEM_READONLY);
+
+	hva = addr_gpa2hva(vm, MEM_REGION_GPA);
+	hva_ro = addr_gpa2hva(vm, MEM_REGION_RO_GPA);
+
+	memset(hva_ro, 0xcccccccc, 0x2000);
+	WRITE_ONCE(*hva_ro, 0xab);
+	memcpy(((unsigned char *)hva_ro) + 8, inst_bytes, sizeof(inst_bytes));
+
+	WRITE_ONCE(*hva, 1);
+
+	/* Wait the vcpu thread to complete read/exec on ro memory */
+	usleep(100000);
+
+	allow_mmio_fault = true;
+	WRITE_ONCE(*hva, 2);
+
+	wait_for_vcpu();
+
+	pthread_join(vcpu_thread, NULL);
+
+	kvm_vm_free(vm);
+	allow_mmio_fault = false;
+}
+
 static void test_zero_memory_regions(void)
 {
 	struct kvm_vcpu *vcpu;
@@ -629,6 +727,8 @@ int main(int argc, char *argv[])
 	 */
 	test_zero_memory_regions();
 	test_mmio_during_vectoring();
+
+	test_ro_memory_region();
 #endif
 
 	test_invalid_memory_region_flags();
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-02-26  7:45 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-26  7:35 [PATCH 0/2] KVM: selftests: Add test case for readonly memslots on x86 Yohei Kojima
2026-02-26  7:37 ` [PATCH 1/2] KVM: selftests: Extract memslot setup code from spawn_vm() Yohei Kojima
2026-02-26  7:37 ` [PATCH 2/2] KVM: selftests: Add test case for readonly memslots on x86 Yohei Kojima

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.