Test that
+ memory failure handling results in unmapping of bad memory from stage
  2 page tables, hence requiring faulting on next guest access
+ when the guest tries to fault a poisoned page from guest_memfd, the
  userspace VMM informed with EHWPOISON

Co-developed-by: Ackerley Tng <[email protected]>
Signed-off-by: Ackerley Tng <[email protected]>
Signed-off-by: Lisa Wang <[email protected]>
---
 .../kvm/guest_memfd_memory_failure_test.c          | 66 ++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c 
b/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c
index 6c8032d390ae..e6f4c327bd5a 100644
--- a/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_memory_failure_test.c
@@ -24,6 +24,7 @@
 #include "kvm_util.h"
 #include "test_util.h"
 #include "kselftest_harness.h"
+#include "ucall_common.h"
 
 static size_t page_size, total_size;
 
@@ -313,6 +314,71 @@ TEST_F(guest_memfd_failure, test_memory_failure)
        }
 }
 
+static void __guest_code_read(uint64_t gpa)
+{
+       uint8_t *mem = (uint8_t *)gpa;
+
+       READ_ONCE(*mem);
+       GUEST_SYNC(0);
+       READ_ONCE(*mem);
+       GUEST_DONE();
+}
+
+static void guest_read(struct kvm_vcpu *vcpu, int expected_errno)
+{
+       if (expected_errno) {
+               TEST_ASSERT_EQ(_vcpu_run(vcpu), -1);
+               TEST_ASSERT_EQ(errno, expected_errno);
+       } else {
+               vcpu_run(vcpu);
+               TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_SYNC);
+       }
+}
+
+TEST_F(guest_memfd_failure, test_memory_failure_guest)
+{
+       const uint64_t gpa = SZ_4G;
+       const int slot = 1;
+
+       unsigned long memory_failure_pfn;
+       struct kvm_vcpu *vcpu;
+       uint8_t *mem;
+
+       /* Limit guest test execution to a single variant to avoid redundant 
runs. */
+       if (variant->method != MF_INJECT_DEBUGFS ||
+           variant->kill_config != PR_MCE_KILL_EARLY ||
+           !variant->map_page || !variant->dirty_page)
+               return;
+
+       self->vm = __vm_create_shape_with_one_vcpu(VM_SHAPE_DEFAULT, &vcpu, 1, 
__guest_code_read);
+       vcpu_args_set(vcpu, 1, gpa);
+
+       self->fd = vm_create_guest_memfd(self->vm, self->vm->page_size,
+                                        GUEST_MEMFD_FLAG_MMAP |
+                                        GUEST_MEMFD_FLAG_INIT_SHARED);
+       vm_set_user_memory_region2(self->vm, slot, KVM_MEM_GUEST_MEMFD, gpa,
+                                  self->vm->page_size, NULL, self->fd, 0);
+
+       mem = mmap(NULL, self->vm->page_size, PROT_READ | PROT_WRITE,
+                  MAP_SHARED, self->fd, 0);
+       TEST_ASSERT(mem != MAP_FAILED, "mmap() for guest_memfd should 
succeed.");
+       virt_pg_map(self->vm, gpa, gpa);
+
+       /* Fault in page to read pfn, then unmap page for testing. */
+       READ_ONCE(*mem);
+
+       memory_failure_pfn = addr_to_pfn(mem);
+       munmap(mem, self->vm->page_size);
+
+       /* Fault page into stage2 page tables. */
+       guest_read(vcpu, 0);
+
+       self->poisoned_pfn = memory_failure_pfn;
+       mark_memory_failure(memory_failure_pfn, 0);
+
+       guest_read(vcpu, EHWPOISON);
+}
+
 static bool can_inject_memory_failure(void)
 {
        int fd;

-- 
2.54.0.1013.g208068f2d8-goog


Reply via email to