Commit 45acde40 authored by Ricardo Koller's avatar Ricardo Koller Committed by Marc Zyngier
Browse files

KVM: selftests: aarch64: Add readonly memslot tests into page_fault_test



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: default avatarRicardo Koller <ricarkol@google.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20221017195834.2295901-14-ricarkol@google.com
parent a4edf25b
Loading
Loading
Loading
Loading
+101 −1
Original line number Diff line number Diff line
@@ -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 }
};