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

