Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14c47b75 authored by Andrew Jones's avatar Andrew Jones Committed by Paolo Bonzini
Browse files

kvm: selftests: introduce ucall



Rework the guest exit to userspace code to generalize the concept
into what it is, a "hypercall to userspace", and provide two
implementations of it: the PortIO version currently used, but only
useable by x86, and an MMIO version that other architectures (except
s390) can use.

Signed-off-by: default avatarAndrew Jones <drjones@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 6c930268
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ all:
top_srcdir = ../../../../
UNAME_M := $(shell uname -m)

LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/ucall.c lib/sparsebit.c
LIBKVM_x86_64 = lib/x86.c lib/vmx.c

TEST_GEN_PROGS_x86_64 = platform_info_test
+6 −6
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ int main(int argc, char *argv[])
	struct kvm_vm *vm;
	struct kvm_sregs sregs;
	struct kvm_cpuid_entry2 *entry;
	struct ucall uc;
	int rc;

	entry = kvm_get_supported_cpuid_entry(1);
@@ -87,21 +88,20 @@ int main(int argc, char *argv[])
		rc = _vcpu_run(vm, VCPU_ID);

		if (run->exit_reason == KVM_EXIT_IO) {
			switch (run->io.port) {
			case GUEST_PORT_SYNC:
			switch (get_ucall(vm, VCPU_ID, &uc)) {
			case UCALL_SYNC:
				/* emulate hypervisor clearing CR4.OSXSAVE */
				vcpu_sregs_get(vm, VCPU_ID, &sregs);
				sregs.cr4 &= ~X86_CR4_OSXSAVE;
				vcpu_sregs_set(vm, VCPU_ID, &sregs);
				break;
			case GUEST_PORT_ABORT:
			case UCALL_ABORT:
				TEST_ASSERT(false, "Guest CR4 bit (OSXSAVE) unsynchronized with CPUID bit.");
				break;
			case GUEST_PORT_DONE:
			case UCALL_DONE:
				goto done;
			default:
				TEST_ASSERT(false, "Unknown port 0x%x.",
					    run->io.port);
				TEST_ASSERT(false, "Unknown ucall 0x%x.", uc.cmd);
			}
		}
	}
+2 −3
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ void *vcpu_worker(void *data)
	uint64_t loops, *guest_array, pages_count = 0;
	struct kvm_vm *vm = data;
	struct kvm_run *run;
	struct guest_args args;
	struct ucall uc;

	run = vcpu_state(vm, VCPU_ID);

@@ -124,9 +124,8 @@ void *vcpu_worker(void *data)
	while (!READ_ONCE(host_quit)) {
		/* Let the guest to dirty these random pages */
		ret = _vcpu_run(vm, VCPU_ID);
		guest_args_read(vm, VCPU_ID, &args);
		if (run->exit_reason == KVM_EXIT_IO &&
		    args.port == GUEST_PORT_SYNC) {
		    get_ucall(vm, VCPU_ID, &uc) == UCALL_SYNC) {
			pages_count += TEST_PAGES_PER_LOOP;
			generate_random_array(guest_array, TEST_PAGES_PER_LOOP);
		} else {
+41 −35
Original line number Diff line number Diff line
@@ -152,43 +152,49 @@ allocate_kvm_dirty_log(struct kvm_userspace_memory_region *region);

int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd);

#define GUEST_PORT_SYNC         0x1000
#define GUEST_PORT_ABORT        0x1001
#define GUEST_PORT_DONE         0x1002
#define sync_global_to_guest(vm, g) ({				\
	typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g));	\
	memcpy(_p, &(g), sizeof(g));				\
})

#define sync_global_from_guest(vm, g) ({			\
	typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g));	\
	memcpy(&(g), _p, sizeof(g));				\
})

/* ucall implementation types */
typedef enum {
	UCALL_PIO,
	UCALL_MMIO,
} ucall_type_t;

/* Common ucalls */
enum {
	UCALL_NONE,
	UCALL_SYNC,
	UCALL_ABORT,
	UCALL_DONE,
};

static inline void __exit_to_l0(uint16_t port, uint64_t arg0, uint64_t arg1)
{
	__asm__ __volatile__("in %[port], %%al"
			     :
			     : [port]"d"(port), "D"(arg0), "S"(arg1)
			     : "rax");
}
#define UCALL_MAX_ARGS 6

/*
 * Allows to pass three arguments to the host: port is 16bit wide,
 * arg0 & arg1 are 64bit wide
 */
#define GUEST_SYNC_ARGS(_port, _arg0, _arg1) \
	__exit_to_l0(_port, (uint64_t) (_arg0), (uint64_t) (_arg1))
struct ucall {
	uint64_t cmd;
	uint64_t args[UCALL_MAX_ARGS];
};

void ucall_init(struct kvm_vm *vm, ucall_type_t type, void *arg);
void ucall_uninit(struct kvm_vm *vm);
void ucall(uint64_t cmd, int nargs, ...);
uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc);

#define GUEST_SYNC(stage)	ucall(UCALL_SYNC, 2, "hello", stage)
#define GUEST_DONE()		ucall(UCALL_DONE, 0)
#define GUEST_ASSERT(_condition) do {			\
	if (!(_condition))				\
			GUEST_SYNC_ARGS(GUEST_PORT_ABORT,	\
		ucall(UCALL_ABORT, 2,			\
			"Failed guest assert: "		\
			#_condition, __LINE__);		\
} while (0)

#define GUEST_SYNC(stage)  GUEST_SYNC_ARGS(GUEST_PORT_SYNC, "hello", stage)

#define GUEST_DONE()  GUEST_SYNC_ARGS(GUEST_PORT_DONE, 0, 0)

struct guest_args {
	uint64_t arg0;
	uint64_t arg1;
	uint16_t port;
} __attribute__ ((packed));

void guest_args_read(struct kvm_vm *vm, uint32_t vcpu_id,
		     struct guest_args *args);

#endif /* SELFTEST_KVM_UTIL_H */
+1 −14
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)
	case VM_MODE_FLAT48PG:
		vm->page_size = 0x1000;
		vm->page_shift = 12;
		vm->va_bits = 48;

		/* Limit to 48-bit canonical virtual addresses. */
		vm->vpages_valid = sparsebit_alloc();
@@ -1669,17 +1670,3 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva)
{
	return addr_gpa2hva(vm, addr_gva2gpa(vm, gva));
}

void guest_args_read(struct kvm_vm *vm, uint32_t vcpu_id,
		     struct guest_args *args)
{
	struct kvm_run *run = vcpu_state(vm, vcpu_id);
	struct kvm_regs regs;

	memset(&regs, 0, sizeof(regs));
	vcpu_regs_get(vm, vcpu_id, &regs);

	args->port = run->io.port;
	args->arg0 = regs.rdi;
	args->arg1 = regs.rsi;
}
Loading