Add some mix of tests into page_fault_test, like stage 2 faults on memslots marked for both userfaultfd and dirty-logging. Signed-off-by: Ricardo Koller <ricarkol@xxxxxxxxxx> --- .../selftests/kvm/aarch64/page_fault_test.c | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/tools/testing/selftests/kvm/aarch64/page_fault_test.c b/tools/testing/selftests/kvm/aarch64/page_fault_test.c index e6607f903bc1..f1a5bf081a5b 100644 --- a/tools/testing/selftests/kvm/aarch64/page_fault_test.c +++ b/tools/testing/selftests/kvm/aarch64/page_fault_test.c @@ -399,6 +399,12 @@ static int uffd_test_read_handler(int mode, int uffd, struct uffd_msg *msg) return uffd_generic_handler(mode, uffd, msg, &memslot[TEST], false); } +static int uffd_no_handler(int mode, int uffd, struct uffd_msg *msg) +{ + TEST_FAIL("There was no UFFD fault expected."); + return -1; +} + static void punch_hole_in_memslot(struct kvm_vm *vm, struct memslot_desc *memslot) { @@ -912,6 +918,30 @@ int main(int argc, char *argv[]) #define TEST_S1PTW_ON_HOLE_UFFD_AF(__a, __uffd_handler) \ TEST_S1PTW_ON_HOLE_UFFD(__a, __uffd_handler, __AF_TEST_ARGS) +#define __DIRTY_LOG_TEST \ + .test_memslot_flags = KVM_MEM_LOG_DIRTY_PAGES, \ + .guest_test_check = { guest_check_write_in_dirty_log, }, \ + +#define __DIRTY_LOG_S1PTW_TEST \ + .pt_memslot_flags = KVM_MEM_LOG_DIRTY_PAGES, \ + .guest_test_check = { guest_check_s1ptw_wr_in_dirty_log, }, \ + +#define TEST_WRITE_DIRTY_LOG_AND_S1PTW_ON_UFFD(__a, __uffd_handler, ...) \ + TEST_S1PTW_ON_HOLE_UFFD(__a, __uffd_handler, \ + __DIRTY_LOG_TEST __VA_ARGS__) + +#define TEST_WRITE_ON_DIRTY_LOG_AND_UFFD(__a, __uffd_handler, ...) \ + TEST_ACCESS_ON_HOLE_UFFD(__a, __uffd_handler, \ + __DIRTY_LOG_TEST __VA_ARGS__) + +#define TEST_WRITE_UFFD_AND_S1PTW_ON_DIRTY_LOG(__a, __uffd_handler, ...) \ + TEST_ACCESS_ON_HOLE_UFFD(__a, __uffd_handler, \ + __DIRTY_LOG_S1PTW_TEST __VA_ARGS__) + +#define TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(__a, __uffd_handler, ...) \ + TEST_S1PTW_ON_HOLE_UFFD(__a, __uffd_handler, \ + __DIRTY_LOG_S1PTW_TEST __VA_ARGS__) + #define TEST_ACCESS_AND_S1PTW_ON_HOLE_UFFD(__a, __th, __ph, ...) \ { \ .name = SNAME(ACCESS_S1PTW_ON_HOLE_UFFD ## _ ## __a), \ @@ -1015,6 +1045,10 @@ int main(int argc, char *argv[]) .guest_prepare = { guest_set_ha, guest_check_lse, }, \ .guest_test_check = { guest_check_pte_af, } +#define __NULL_UFFD_HANDLERS \ + .uffd_test_handler = uffd_no_handler, \ + .uffd_pt_handler = uffd_no_handler + #define TEST_WRITE_ON_RO_MEMSLOT_AF(__a) \ TEST_WRITE_ON_RO_MEMSLOT(__a, __AF_TEST_IN_RO_MEMSLOT_ARGS) @@ -1105,6 +1139,37 @@ int main(int argc, char *argv[]) #define TEST_EXEC_AND_S1PTW_AF_ON_RO_MEMSLOT(__a) \ TEST_EXEC_AND_S1PTW_ON_RO_MEMSLOT(__a, __AF_TEST_IN_RO_MEMSLOT_ARGS) +#define TEST_WRITE_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(__a) \ + TEST_WRITE_AND_S1PTW_ON_RO_MEMSLOT(__a, __NULL_UFFD_HANDLERS) +#define TEST_READ_AND_S1PTW_ON_RO_MEMSLOT_WITH_UFFD(__a) \ + TEST_READ_AND_S1PTW_ON_RO_MEMSLOT(__a, __NULL_UFFD_HANDLERS) +#define TEST_CM_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(__a) \ + TEST_CM_AND_S1PTW_ON_RO_MEMSLOT(__a, __NULL_UFFD_HANDLERS) +#define TEST_EXEC_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(__a) \ + TEST_EXEC_AND_S1PTW_ON_RO_MEMSLOT(__a, __NULL_UFFD_HANDLERS) + +#define TEST_WRITE_ON_RO_DIRTY_LOG_MEMSLOT(__a, ...) \ +{ \ + .name = SNAME(WRITE_ON_RO_MEMSLOT ## _ ## __a), \ + .test_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \ + .guest_test = __a, \ + .guest_test_check = { guest_check_no_write_in_dirty_log, }, \ + .mmio_handler = mmio_on_test_gpa_handler, \ + .expected_events = { .mmio_exits = 1, }, \ + __VA_ARGS__ \ +} + +#define TEST_CM_ON_RO_DIRTY_LOG_MEMSLOT(__a, ...) \ +{ \ + .name = SNAME(WRITE_ON_RO_MEMSLOT ## _ ## __a), \ + .test_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \ + .guest_test = __a, \ + .guest_test_check = { guest_check_no_write_in_dirty_log, }, \ + .fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \ + .expected_events = { .fail_vcpu_runs = 1, }, \ + __VA_ARGS__ \ +} + static struct test_desc tests[] = { /* Check that HW is setting the AF (sanity checks). */ TEST_HW_ACCESS_FLAG(guest_test_read64), @@ -1223,6 +1288,65 @@ static struct test_desc tests[] = { TEST_ACCESS_AND_S1PTW_ON_HOLE_UFFD_AF(guest_test_exec, uffd_test_read_handler, uffd_pt_write_handler), + /* Write into a memslot marked for both dirty logging and UFFD. */ + TEST_WRITE_ON_DIRTY_LOG_AND_UFFD(guest_test_write64, + uffd_test_write_handler), + /* Note that the cas uffd handler is for a read. */ + TEST_WRITE_ON_DIRTY_LOG_AND_UFFD(guest_test_cas, + uffd_test_read_handler, __PREPARE_LSE_TEST_ARGS), + TEST_WRITE_ON_DIRTY_LOG_AND_UFFD(guest_test_dc_zva, + uffd_test_write_handler), + TEST_WRITE_ON_DIRTY_LOG_AND_UFFD(guest_test_st_preidx, + uffd_test_write_handler), + + /* + * Access whose s1ptw faults on a hole that's marked for both dirty + * logging and UFFD. + */ + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_read64, + uffd_pt_write_handler), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_cas, + uffd_pt_write_handler, __PREPARE_LSE_TEST_ARGS), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_ld_preidx, + uffd_pt_write_handler), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_exec, + uffd_pt_write_handler), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_write64, + uffd_pt_write_handler), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_st_preidx, + uffd_pt_write_handler), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_dc_zva, + uffd_pt_write_handler), + TEST_S1PTW_ON_DIRTY_LOG_AND_UFFD(guest_test_at, + uffd_pt_write_handler), + + /* + * Write on a memslot marked for dirty logging whose related s1ptw + * is on a hole marked with UFFD. + */ + TEST_WRITE_DIRTY_LOG_AND_S1PTW_ON_UFFD(guest_test_write64, + uffd_pt_write_handler), + TEST_WRITE_DIRTY_LOG_AND_S1PTW_ON_UFFD(guest_test_cas, + uffd_pt_write_handler, __PREPARE_LSE_TEST_ARGS), + TEST_WRITE_DIRTY_LOG_AND_S1PTW_ON_UFFD(guest_test_dc_zva, + uffd_pt_write_handler), + TEST_WRITE_DIRTY_LOG_AND_S1PTW_ON_UFFD(guest_test_st_preidx, + uffd_pt_write_handler), + + /* + * Write on a memslot that's on a hole marked with UFFD, whose related + * sp1ptw is on a memslot marked for dirty logging. + */ + TEST_WRITE_UFFD_AND_S1PTW_ON_DIRTY_LOG(guest_test_write64, + uffd_test_write_handler), + /* Note that the uffd handler is for a read. */ + TEST_WRITE_UFFD_AND_S1PTW_ON_DIRTY_LOG(guest_test_cas, + uffd_test_read_handler, __PREPARE_LSE_TEST_ARGS), + TEST_WRITE_UFFD_AND_S1PTW_ON_DIRTY_LOG(guest_test_dc_zva, + uffd_test_write_handler), + TEST_WRITE_UFFD_AND_S1PTW_ON_DIRTY_LOG(guest_test_st_preidx, + uffd_test_write_handler), + /* Access on readonly memslot (sanity check). */ TEST_WRITE_ON_RO_MEMSLOT(guest_test_write64), TEST_READ_ON_RO_MEMSLOT(guest_test_read64), @@ -1290,6 +1414,30 @@ static struct test_desc tests[] = { TEST_CM_AND_S1PTW_AF_ON_RO_MEMSLOT(guest_test_st_preidx), TEST_EXEC_AND_S1PTW_AF_ON_RO_MEMSLOT(guest_test_exec), + /* + * Access on a memslot marked as readonly with also dirty log tracking. + * There should be no write in the dirty log. + */ + TEST_WRITE_ON_RO_DIRTY_LOG_MEMSLOT(guest_test_write64), + TEST_CM_ON_RO_DIRTY_LOG_MEMSLOT(guest_test_cas, + __PREPARE_LSE_TEST_ARGS), + TEST_CM_ON_RO_DIRTY_LOG_MEMSLOT(guest_test_dc_zva), + TEST_CM_ON_RO_DIRTY_LOG_MEMSLOT(guest_test_st_preidx), + + /* + * Access on a RO memslot with S1PTW also on a RO memslot, while also + * having those memslot regions marked for UFFD fault handling. The + * result is that UFFD fault handlers should not be called. + */ + TEST_WRITE_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(guest_test_write64), + TEST_READ_AND_S1PTW_ON_RO_MEMSLOT_WITH_UFFD(guest_test_read64), + TEST_READ_AND_S1PTW_ON_RO_MEMSLOT_WITH_UFFD(guest_test_ld_preidx), + TEST_CM_AND_S1PTW_ON_RO_MEMSLOT(guest_test_cas, + __PREPARE_LSE_TEST_ARGS __NULL_UFFD_HANDLERS), + TEST_CM_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(guest_test_dc_zva), + TEST_CM_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(guest_test_st_preidx), + TEST_EXEC_AND_S1PTW_AF_ON_RO_MEMSLOT_WITH_UFFD(guest_test_exec), + { 0 }, }; -- 2.35.1.723.g4982287a31-goog