diff options
| author | Mark Brown <broonie@kernel.org> | 2020-12-11 17:49:01 +0000 | 
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2020-12-11 17:49:01 +0000 | 
| commit | 3e98a021cc85e7d52acdd1eae8a988e975ec5bf9 (patch) | |
| tree | cecfac58b9550a602555a02a9d583ed0f3378b38 /tools/testing/selftests/kvm/lib | |
| parent | 58f7553fa424fd0fd74e8b796d50c66014cebebe (diff) | |
| parent | 2fee9583198eb97b5351feda7bd825e0f778385c (diff) | |
Merge remote-tracking branch 'spi/for-5.11' into spi-next
Diffstat (limited to 'tools/testing/selftests/kvm/lib')
| -rw-r--r-- | tools/testing/selftests/kvm/lib/aarch64/processor.c | 4 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/aarch64/ucall.c | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/kvm_util.c | 90 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/kvm_util_internal.h | 2 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/s390x/processor.c | 4 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/s390x/ucall.c | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/test_util.c | 22 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/x86_64/handlers.S | 81 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/x86_64/processor.c | 146 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/x86_64/ucall.c | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/kvm/lib/x86_64/vmx.c | 9 | 
11 files changed, 348 insertions, 19 deletions
| diff --git a/tools/testing/selftests/kvm/lib/aarch64/processor.c b/tools/testing/selftests/kvm/lib/aarch64/processor.c index 2afa6618b396..d6c32c328e9a 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/processor.c +++ b/tools/testing/selftests/kvm/lib/aarch64/processor.c @@ -350,3 +350,7 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...)  	va_end(ap);  } + +void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +{ +} diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c index c8e0ec20d3bf..2f37b90ee1a9 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c @@ -94,6 +94,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)  	struct kvm_run *run = vcpu_state(vm, vcpu_id);  	struct ucall ucall = {}; +	if (uc) +		memset(uc, 0, sizeof(*uc)); +  	if (run->exit_reason == KVM_EXIT_MMIO &&  	    run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {  		vm_vaddr_t gva; diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 74776ee228f2..126c6727a6b0 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -14,6 +14,7 @@  #include <sys/mman.h>  #include <sys/types.h>  #include <sys/stat.h> +#include <unistd.h>  #include <linux/kernel.h>  #define KVM_UTIL_PGS_PER_HUGEPG 512 @@ -85,6 +86,34 @@ int vm_enable_cap(struct kvm_vm *vm, struct kvm_enable_cap *cap)  	return ret;  } +/* VCPU Enable Capability + * + * Input Args: + *   vm - Virtual Machine + *   vcpu_id - VCPU + *   cap - Capability + * + * Output Args: None + * + * Return: On success, 0. On failure a TEST_ASSERT failure is produced. + * + * Enables a capability (KVM_CAP_*) on the VCPU. + */ +int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id, +		    struct kvm_enable_cap *cap) +{ +	struct vcpu *vcpu = vcpu_find(vm, vcpu_id); +	int r; + +	TEST_ASSERT(vcpu, "cannot find vcpu %d", vcpu_id); + +	r = ioctl(vcpu->fd, KVM_ENABLE_CAP, cap); +	TEST_ASSERT(!r, "KVM_ENABLE_CAP vCPU ioctl failed,\n" +			"  rc: %i, errno: %i", r, errno); + +	return r; +} +  static void vm_open(struct kvm_vm *vm, int perm)  {  	vm->kvm_fd = open(KVM_DEV_PATH, perm); @@ -151,7 +180,7 @@ _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params)   * descriptor to control the created VM is created with the permissions   * given by perm (e.g. O_RDWR).   */ -struct kvm_vm *_vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm) +struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)  {  	struct kvm_vm *vm; @@ -242,11 +271,6 @@ struct kvm_vm *_vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm)  	return vm;  } -struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm) -{ -	return _vm_create(mode, phy_pages, perm); -} -  /*   * VM Restart   * @@ -664,13 +688,21 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,  	/* As needed perform madvise */  	if (src_type == VM_MEM_SRC_ANONYMOUS || src_type == VM_MEM_SRC_ANONYMOUS_THP) { -		ret = madvise(region->host_mem, npages * vm->page_size, -			     src_type == VM_MEM_SRC_ANONYMOUS ? MADV_NOHUGEPAGE : MADV_HUGEPAGE); -		TEST_ASSERT(ret == 0, "madvise failed,\n" -			    "  addr: %p\n" -			    "  length: 0x%lx\n" -			    "  src_type: %x", -			    region->host_mem, npages * vm->page_size, src_type); +		struct stat statbuf; + +		ret = stat("/sys/kernel/mm/transparent_hugepage", &statbuf); +		TEST_ASSERT(ret == 0 || (ret == -1 && errno == ENOENT), +			    "stat /sys/kernel/mm/transparent_hugepage"); + +		TEST_ASSERT(ret == 0 || src_type != VM_MEM_SRC_ANONYMOUS_THP, +			    "VM_MEM_SRC_ANONYMOUS_THP requires THP to be configured in the host kernel"); + +		if (ret == 0) { +			ret = madvise(region->host_mem, npages * vm->page_size, +				      src_type == VM_MEM_SRC_ANONYMOUS ? MADV_NOHUGEPAGE : MADV_HUGEPAGE); +			TEST_ASSERT(ret == 0, "madvise failed, addr: %p length: 0x%lx src_type: %x", +				    region->host_mem, npages * vm->page_size, src_type); +		}  	}  	region->unused_phy_pages = sparsebit_alloc(); @@ -1195,6 +1227,9 @@ int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid)  	do {  		rc = ioctl(vcpu->fd, KVM_RUN, NULL);  	} while (rc == -1 && errno == EINTR); + +	assert_on_unhandled_exception(vm, vcpuid); +  	return rc;  } @@ -1252,6 +1287,35 @@ void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid,  }  /* + * VM VCPU Get Reg List + * + * Input Args: + *   vm - Virtual Machine + *   vcpuid - VCPU ID + * + * Output Args: + *   None + * + * Return: + *   A pointer to an allocated struct kvm_reg_list + * + * Get the list of guest registers which are supported for + * KVM_GET_ONE_REG/KVM_SET_ONE_REG calls + */ +struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vm *vm, uint32_t vcpuid) +{ +	struct kvm_reg_list reg_list_n = { .n = 0 }, *reg_list; +	int ret; + +	ret = _vcpu_ioctl(vm, vcpuid, KVM_GET_REG_LIST, ®_list_n); +	TEST_ASSERT(ret == -1 && errno == E2BIG, "KVM_GET_REG_LIST n=0"); +	reg_list = calloc(1, sizeof(*reg_list) + reg_list_n.n * sizeof(__u64)); +	reg_list->n = reg_list_n.n; +	vcpu_ioctl(vm, vcpuid, KVM_GET_REG_LIST, reg_list); +	return reg_list; +} + +/*   * VM VCPU Regs Get   *   * Input Args: diff --git a/tools/testing/selftests/kvm/lib/kvm_util_internal.h b/tools/testing/selftests/kvm/lib/kvm_util_internal.h index 2ef446520748..f07d383d03a1 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util_internal.h +++ b/tools/testing/selftests/kvm/lib/kvm_util_internal.h @@ -50,6 +50,8 @@ struct kvm_vm {  	vm_paddr_t pgd;  	vm_vaddr_t gdt;  	vm_vaddr_t tss; +	vm_vaddr_t idt; +	vm_vaddr_t handlers;  };  struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid); diff --git a/tools/testing/selftests/kvm/lib/s390x/processor.c b/tools/testing/selftests/kvm/lib/s390x/processor.c index a88c5d665725..7349bb2e1a24 100644 --- a/tools/testing/selftests/kvm/lib/s390x/processor.c +++ b/tools/testing/selftests/kvm/lib/s390x/processor.c @@ -241,3 +241,7 @@ void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent)  	fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n",  		indent, "", vcpu->state->psw_mask, vcpu->state->psw_addr);  } + +void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +{ +} diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c index fd589dc9bfab..9d3b0f15249a 100644 --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c @@ -38,6 +38,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)  	struct kvm_run *run = vcpu_state(vm, vcpu_id);  	struct ucall ucall = {}; +	if (uc) +		memset(uc, 0, sizeof(*uc)); +  	if (run->exit_reason == KVM_EXIT_S390_SIEIC &&  	    run->s390_sieic.icptcode == 4 &&  	    (run->s390_sieic.ipa >> 8) == 0x83 &&    /* 0x83 means DIAGNOSE */ diff --git a/tools/testing/selftests/kvm/lib/test_util.c b/tools/testing/selftests/kvm/lib/test_util.c index 689e97c27ee2..8e04c0b1608e 100644 --- a/tools/testing/selftests/kvm/lib/test_util.c +++ b/tools/testing/selftests/kvm/lib/test_util.c @@ -4,10 +4,13 @@   *   * Copyright (C) 2020, Google LLC.   */ -#include <stdlib.h> + +#include <assert.h>  #include <ctype.h>  #include <limits.h> -#include <assert.h> +#include <stdlib.h> +#include <time.h> +  #include "test_util.h"  /* @@ -81,6 +84,21 @@ struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)  	return timespec_add_ns((struct timespec){0}, ns1 - ns2);  } +struct timespec timespec_diff_now(struct timespec start) +{ +	struct timespec end; + +	clock_gettime(CLOCK_MONOTONIC, &end); +	return timespec_sub(end, start); +} + +struct timespec timespec_div(struct timespec ts, int divisor) +{ +	int64_t ns = timespec_to_ns(ts) / divisor; + +	return timespec_add_ns((struct timespec){0}, ns); +} +  void print_skip(const char *fmt, ...)  {  	va_list ap; diff --git a/tools/testing/selftests/kvm/lib/x86_64/handlers.S b/tools/testing/selftests/kvm/lib/x86_64/handlers.S new file mode 100644 index 000000000000..aaf7bc7d2ce1 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/x86_64/handlers.S @@ -0,0 +1,81 @@ +handle_exception: +	push %r15 +	push %r14 +	push %r13 +	push %r12 +	push %r11 +	push %r10 +	push %r9 +	push %r8 + +	push %rdi +	push %rsi +	push %rbp +	push %rbx +	push %rdx +	push %rcx +	push %rax +	mov %rsp, %rdi + +	call route_exception + +	pop %rax +	pop %rcx +	pop %rdx +	pop %rbx +	pop %rbp +	pop %rsi +	pop %rdi +	pop %r8 +	pop %r9 +	pop %r10 +	pop %r11 +	pop %r12 +	pop %r13 +	pop %r14 +	pop %r15 + +	/* Discard vector and error code. */ +	add $16, %rsp +	iretq + +/* + * Build the handle_exception wrappers which push the vector/error code on the + * stack and an array of pointers to those wrappers. + */ +.pushsection .rodata +.globl idt_handlers +idt_handlers: +.popsection + +.macro HANDLERS has_error from to +	vector = \from +	.rept \to - \from + 1 +	.align 8 + +	/* Fetch current address and append it to idt_handlers. */ +	current_handler = . +.pushsection .rodata +.quad current_handler +.popsection + +	.if ! \has_error +	pushq $0 +	.endif +	pushq $vector +	jmp handle_exception +	vector = vector + 1 +	.endr +.endm + +.global idt_handler_code +idt_handler_code: +	HANDLERS has_error=0 from=0  to=7 +	HANDLERS has_error=1 from=8  to=8 +	HANDLERS has_error=0 from=9  to=9 +	HANDLERS has_error=1 from=10 to=14 +	HANDLERS has_error=0 from=15 to=16 +	HANDLERS has_error=1 from=17 to=17 +	HANDLERS has_error=0 from=18 to=255 + +.section        .note.GNU-stack, "", %progbits diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index f6eb34eaa0d2..d10c5c05bdf0 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -12,9 +12,18 @@  #include "../kvm_util_internal.h"  #include "processor.h" +#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif + +#define DEFAULT_CODE_SELECTOR 0x8 +#define DEFAULT_DATA_SELECTOR 0x10 +  /* Minimum physical address used for virtual translation tables. */  #define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000 +vm_vaddr_t exception_handlers; +  /* Virtual translation table structure declarations */  struct pageMapL4Entry {  	uint64_t present:1; @@ -392,11 +401,12 @@ static void kvm_seg_fill_gdt_64bit(struct kvm_vm *vm, struct kvm_segment *segp)  	desc->limit0 = segp->limit & 0xFFFF;  	desc->base0 = segp->base & 0xFFFF;  	desc->base1 = segp->base >> 16; -	desc->s = segp->s;  	desc->type = segp->type; +	desc->s = segp->s;  	desc->dpl = segp->dpl;  	desc->p = segp->present;  	desc->limit1 = segp->limit >> 16; +	desc->avl = segp->avl;  	desc->l = segp->l;  	desc->db = segp->db;  	desc->g = segp->g; @@ -556,9 +566,9 @@ static void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_m  		sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX);  		kvm_seg_set_unusable(&sregs.ldt); -		kvm_seg_set_kernel_code_64bit(vm, 0x8, &sregs.cs); -		kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.ds); -		kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.es); +		kvm_seg_set_kernel_code_64bit(vm, DEFAULT_CODE_SELECTOR, &sregs.cs); +		kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.ds); +		kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.es);  		kvm_setup_tss_64bit(vm, &sregs.tr, 0x18, gdt_memslot, pgd_memslot);  		break; @@ -1118,3 +1128,131 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)  		*va_bits = (entry->eax >> 8) & 0xff;  	}  } + +struct idt_entry { +	uint16_t offset0; +	uint16_t selector; +	uint16_t ist : 3; +	uint16_t : 5; +	uint16_t type : 4; +	uint16_t : 1; +	uint16_t dpl : 2; +	uint16_t p : 1; +	uint16_t offset1; +	uint32_t offset2; uint32_t reserved; +}; + +static void set_idt_entry(struct kvm_vm *vm, int vector, unsigned long addr, +			  int dpl, unsigned short selector) +{ +	struct idt_entry *base = +		(struct idt_entry *)addr_gva2hva(vm, vm->idt); +	struct idt_entry *e = &base[vector]; + +	memset(e, 0, sizeof(*e)); +	e->offset0 = addr; +	e->selector = selector; +	e->ist = 0; +	e->type = 14; +	e->dpl = dpl; +	e->p = 1; +	e->offset1 = addr >> 16; +	e->offset2 = addr >> 32; +} + +void kvm_exit_unexpected_vector(uint32_t value) +{ +	outl(UNEXPECTED_VECTOR_PORT, value); +} + +void route_exception(struct ex_regs *regs) +{ +	typedef void(*handler)(struct ex_regs *); +	handler *handlers = (handler *)exception_handlers; + +	if (handlers && handlers[regs->vector]) { +		handlers[regs->vector](regs); +		return; +	} + +	kvm_exit_unexpected_vector(regs->vector); +} + +void vm_init_descriptor_tables(struct kvm_vm *vm) +{ +	extern void *idt_handlers; +	int i; + +	vm->idt = vm_vaddr_alloc(vm, getpagesize(), 0x2000, 0, 0); +	vm->handlers = vm_vaddr_alloc(vm, 256 * sizeof(void *), 0x2000, 0, 0); +	/* Handlers have the same address in both address spaces.*/ +	for (i = 0; i < NUM_INTERRUPTS; i++) +		set_idt_entry(vm, i, (unsigned long)(&idt_handlers)[i], 0, +			DEFAULT_CODE_SELECTOR); +} + +void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid) +{ +	struct kvm_sregs sregs; + +	vcpu_sregs_get(vm, vcpuid, &sregs); +	sregs.idt.base = vm->idt; +	sregs.idt.limit = NUM_INTERRUPTS * sizeof(struct idt_entry) - 1; +	sregs.gdt.base = vm->gdt; +	sregs.gdt.limit = getpagesize() - 1; +	kvm_seg_set_kernel_data_64bit(NULL, DEFAULT_DATA_SELECTOR, &sregs.gs); +	vcpu_sregs_set(vm, vcpuid, &sregs); +	*(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers; +} + +void vm_handle_exception(struct kvm_vm *vm, int vector, +			 void (*handler)(struct ex_regs *)) +{ +	vm_vaddr_t *handlers = (vm_vaddr_t *)addr_gva2hva(vm, vm->handlers); + +	handlers[vector] = (vm_vaddr_t)handler; +} + +void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +{ +	if (vcpu_state(vm, vcpuid)->exit_reason == KVM_EXIT_IO +		&& vcpu_state(vm, vcpuid)->io.port == UNEXPECTED_VECTOR_PORT +		&& vcpu_state(vm, vcpuid)->io.size == 4) { +		/* Grab pointer to io data */ +		uint32_t *data = (void *)vcpu_state(vm, vcpuid) +			+ vcpu_state(vm, vcpuid)->io.data_offset; + +		TEST_ASSERT(false, +			    "Unexpected vectored event in guest (vector:0x%x)", +			    *data); +	} +} + +bool set_cpuid(struct kvm_cpuid2 *cpuid, +	       struct kvm_cpuid_entry2 *ent) +{ +	int i; + +	for (i = 0; i < cpuid->nent; i++) { +		struct kvm_cpuid_entry2 *cur = &cpuid->entries[i]; + +		if (cur->function != ent->function || cur->index != ent->index) +			continue; + +		memcpy(cur, ent, sizeof(struct kvm_cpuid_entry2)); +		return true; +	} + +	return false; +} + +uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, +		       uint64_t a3) +{ +	uint64_t r; + +	asm volatile("vmcall" +		     : "=a"(r) +		     : "b"(a0), "c"(a1), "d"(a2), "S"(a3)); +	return r; +} diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c index da4d89ad5419..a3489973e290 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c @@ -40,6 +40,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)  	struct kvm_run *run = vcpu_state(vm, vcpu_id);  	struct ucall ucall = {}; +	if (uc) +		memset(uc, 0, sizeof(*uc)); +  	if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {  		struct kvm_regs regs; diff --git a/tools/testing/selftests/kvm/lib/x86_64/vmx.c b/tools/testing/selftests/kvm/lib/x86_64/vmx.c index f1e00d43eea2..2448b30e8efa 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86_64/vmx.c @@ -542,3 +542,12 @@ void prepare_eptp(struct vmx_pages *vmx, struct kvm_vm *vm,  	vmx->eptp_hva = addr_gva2hva(vm, (uintptr_t)vmx->eptp);  	vmx->eptp_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->eptp);  } + +void prepare_virtualize_apic_accesses(struct vmx_pages *vmx, struct kvm_vm *vm, +				      uint32_t eptp_memslot) +{ +	vmx->apic_access = (void *)vm_vaddr_alloc(vm, getpagesize(), +						  0x10000, 0, 0); +	vmx->apic_access_hva = addr_gva2hva(vm, (uintptr_t)vmx->apic_access); +	vmx->apic_access_gpa = addr_gva2gpa(vm, (uintptr_t)vmx->apic_access); +} | 
