@@ -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 },
};
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@google.com> --- .../selftests/kvm/aarch64/page_fault_test.c | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+)