Add some readonly memslot tests into page_fault_test. Mark the data and/or
page-table memory regions as readonly, perform some accesses, and check
that the right fault is triggered when expected (e.g., a store with no
write-back should lead to an mmio exit).

Signed-off-by: Ricardo Koller <ricar...@google.com>
---
 .../selftests/kvm/aarch64/page_fault_test.c   | 102 +++++++++++++++++-
 1 file changed, 101 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/kvm/aarch64/page_fault_test.c 
b/tools/testing/selftests/kvm/aarch64/page_fault_test.c
index a36001143aff..727f4f2b6cc4 100644
--- a/tools/testing/selftests/kvm/aarch64/page_fault_test.c
+++ b/tools/testing/selftests/kvm/aarch64/page_fault_test.c
@@ -41,6 +41,8 @@ static uint64_t *guest_test_memory = (uint64_t *)TEST_GVA;
 #define CHECK_FN_NR                            10
 
 static struct event_cnt {
+       int mmio_exits;
+       int fail_vcpu_runs;
        int uffd_faults;
        /* uffd_faults is incremented from multiple threads. */
        pthread_mutex_t uffd_faults_mutex;
@@ -57,6 +59,8 @@ struct test_desc {
        uffd_handler_t uffd_data_handler;
        void (*dabt_handler)(struct ex_regs *regs);
        void (*iabt_handler)(struct ex_regs *regs);
+       void (*mmio_handler)(struct kvm_vm *vm, struct kvm_run *run);
+       void (*fail_vcpu_run_handler)(int ret);
        uint32_t pt_memslot_flags;
        uint32_t data_memslot_flags;
        bool skip;
@@ -415,6 +419,31 @@ static bool punch_hole_in_backing_store(struct kvm_vm *vm,
        return true;
 }
 
+static void mmio_on_test_gpa_handler(struct kvm_vm *vm, struct kvm_run *run)
+{
+       struct userspace_mem_region *region;
+       void *hva;
+
+       region = vm_get_mem_region(vm, MEM_REGION_TEST_DATA);
+       hva = (void *)region->region.userspace_addr;
+
+       ASSERT_EQ(run->mmio.phys_addr, region->region.guest_phys_addr);
+
+       memcpy(hva, run->mmio.data, run->mmio.len);
+       events.mmio_exits += 1;
+}
+
+static void mmio_no_handler(struct kvm_vm *vm, struct kvm_run *run)
+{
+       uint64_t data;
+
+       memcpy(&data, run->mmio.data, sizeof(data));
+       pr_debug("addr=%lld len=%d w=%d data=%lx\n",
+                run->mmio.phys_addr, run->mmio.len,
+                run->mmio.is_write, data);
+       TEST_FAIL("There was no MMIO exit expected.");
+}
+
 static bool check_write_in_dirty_log(struct kvm_vm *vm,
                                     struct userspace_mem_region *region,
                                     uint64_t host_pg_nr)
@@ -463,6 +492,18 @@ static bool handle_cmd(struct kvm_vm *vm, int cmd)
        return continue_test;
 }
 
+void fail_vcpu_run_no_handler(int ret)
+{
+       TEST_FAIL("Unexpected vcpu run failure\n");
+}
+
+void fail_vcpu_run_mmio_no_syndrome_handler(int ret)
+{
+       TEST_ASSERT(errno == ENOSYS,
+                   "The mmio handler should have returned not implemented.");
+       events.fail_vcpu_runs += 1;
+}
+
 typedef uint32_t aarch64_insn_t;
 extern aarch64_insn_t __exec_test[2];
 
@@ -564,9 +605,20 @@ static void setup_memslots(struct kvm_vm *vm, struct 
test_params *p)
        vm->memslots[MEM_REGION_TEST_DATA] = TEST_DATA_MEMSLOT;
 }
 
+static void setup_default_handlers(struct test_desc *test)
+{
+       if (!test->mmio_handler)
+               test->mmio_handler = mmio_no_handler;
+
+       if (!test->fail_vcpu_run_handler)
+               test->fail_vcpu_run_handler = fail_vcpu_run_no_handler;
+}
+
 static void check_event_counts(struct test_desc *test)
 {
        ASSERT_EQ(test->expected_events.uffd_faults, events.uffd_faults);
+       ASSERT_EQ(test->expected_events.mmio_exits, events.mmio_exits);
+       ASSERT_EQ(test->expected_events.fail_vcpu_runs, events.fail_vcpu_runs);
 }
 
 static void print_test_banner(enum vm_guest_mode mode, struct test_params *p)
@@ -591,10 +643,18 @@ static void reset_event_counts(void)
 static void vcpu_run_loop(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
                          struct test_desc *test)
 {
+       struct kvm_run *run;
        struct ucall uc;
+       int ret;
+
+       run = vcpu->run;
 
        for (;;) {
-               vcpu_run(vcpu);
+               ret = _vcpu_run(vcpu);
+               if (ret) {
+                       test->fail_vcpu_run_handler(ret);
+                       goto done;
+               }
 
                switch (get_ucall(vcpu, &uc)) {
                case UCALL_SYNC:
@@ -608,6 +668,10 @@ static void vcpu_run_loop(struct kvm_vm *vm, struct 
kvm_vcpu *vcpu,
                        break;
                case UCALL_DONE:
                        goto done;
+               case UCALL_NONE:
+                       if (run->exit_reason == KVM_EXIT_MMIO)
+                               test->mmio_handler(vm, run);
+                       break;
                default:
                        TEST_FAIL("Unknown ucall %lu", uc.cmd);
                }
@@ -647,6 +711,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
        load_exec_code_for_test(vm);
        setup_uffd(vm, p, &pt_uffd, &data_uffd);
        setup_abort_handlers(vm, vcpu, test);
+       setup_default_handlers(test);
        vcpu_args_set(vcpu, 1, test);
 
        vcpu_run_loop(vm, vcpu, test);
@@ -734,6 +799,25 @@ static void help(char *name)
        .expected_events        = { 0 },                                        
\
 }
 
+#define TEST_RO_MEMSLOT(_access, _mmio_handler, _mmio_exits)                   
\
+{                                                                              
\
+       .name                   = SCAT3(ro_memslot, _access, _with_af),         
\
+       .data_memslot_flags     = KVM_MEM_READONLY,                             
\
+       .guest_prepare          = { _PREPARE(_access) },                        
\
+       .guest_test             = _access,                                      
\
+       .mmio_handler           = _mmio_handler,                                
\
+       .expected_events        = { .mmio_exits = _mmio_exits },                
\
+}
+
+#define TEST_RO_MEMSLOT_NO_SYNDROME(_access)                                   
\
+{                                                                              
\
+       .name                   = SCAT2(ro_memslot_no_syndrome, _access),       
\
+       .data_memslot_flags     = KVM_MEM_READONLY,                             
\
+       .guest_test             = _access,                                      
\
+       .fail_vcpu_run_handler  = fail_vcpu_run_mmio_no_syndrome_handler,       
\
+       .expected_events        = { .fail_vcpu_runs = 1 },                      
\
+}
+
 static struct test_desc tests[] = {
 
        /* Check that HW is setting the Access Flag (AF) (sanity checks). */
@@ -808,6 +892,22 @@ static struct test_desc tests[] = {
        TEST_DIRTY_LOG(guest_dc_zva, with_af, guest_check_write_in_dirty_log),
        TEST_DIRTY_LOG(guest_st_preidx, with_af, 
guest_check_write_in_dirty_log),
 
+       /*
+        * Try accesses when the data memory region is marked read-only
+        * (with KVM_MEM_READONLY). Writes with a syndrome result in an
+        * MMIO exit, writes with no syndrome (e.g., CAS) result in a
+        * failed vcpu run, and reads/execs with and without syndroms do
+        * not fault.
+        */
+       TEST_RO_MEMSLOT(guest_read64, 0, 0),
+       TEST_RO_MEMSLOT(guest_ld_preidx, 0, 0),
+       TEST_RO_MEMSLOT(guest_at, 0, 0),
+       TEST_RO_MEMSLOT(guest_exec, 0, 0),
+       TEST_RO_MEMSLOT(guest_write64, mmio_on_test_gpa_handler, 1),
+       TEST_RO_MEMSLOT_NO_SYNDROME(guest_dc_zva),
+       TEST_RO_MEMSLOT_NO_SYNDROME(guest_cas),
+       TEST_RO_MEMSLOT_NO_SYNDROME(guest_st_preidx),
+
        { 0 }
 };
 
-- 
2.38.0.413.g74048e4d9e-goog

_______________________________________________
kvmarm mailing list
kvmarm@lists.cs.columbia.edu
https://lists.cs.columbia.edu/mailman/listinfo/kvmarm

Reply via email to