kvm: selftests: add API testing infrastructure
Testsuite contributed by Google and cleaned up by myself for inclusion in Linux. Signed-off-by: Ken Hofsass <hofsass@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
		
							parent
							
								
									3140c156e9
								
							
						
					
					
						commit
						783e9e5126
					
				| @ -14,6 +14,7 @@ TARGETS += gpio | ||||
| TARGETS += intel_pstate | ||||
| TARGETS += ipc | ||||
| TARGETS += kcmp | ||||
| TARGETS += kvm | ||||
| TARGETS += lib | ||||
| TARGETS += membarrier | ||||
| TARGETS += memfd | ||||
|  | ||||
							
								
								
									
										38
									
								
								tools/testing/selftests/kvm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tools/testing/selftests/kvm/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| all: | ||||
| 
 | ||||
| top_srcdir = ../../../../ | ||||
| UNAME_M := $(shell uname -m) | ||||
| 
 | ||||
| LIBKVM = lib/assert.c lib/kvm_util.c lib/sparsebit.c | ||||
| LIBKVM_x86_64 = lib/x86.c | ||||
| 
 | ||||
| TEST_GEN_PROGS_x86_64 = set_sregs_test | ||||
| 
 | ||||
| TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) | ||||
| LIBKVM += $(LIBKVM_$(UNAME_M)) | ||||
| 
 | ||||
| INSTALL_HDR_PATH = $(top_srcdir)/usr | ||||
| LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/ | ||||
| CFLAGS += -O2 -g -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) | ||||
| 
 | ||||
| # After inclusion, $(OUTPUT) is defined and
 | ||||
| # $(TEST_GEN_PROGS) starts with $(OUTPUT)/
 | ||||
| include ../lib.mk | ||||
| 
 | ||||
| STATIC_LIBS := $(OUTPUT)/libkvm.a | ||||
| LIBKVM_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM)) | ||||
| EXTRA_CLEAN += $(LIBKVM_OBJ) $(STATIC_LIBS) | ||||
| 
 | ||||
| x := $(shell mkdir -p $(sort $(dir $(LIBKVM_OBJ)))) | ||||
| $(LIBKVM_OBJ): $(OUTPUT)/%.o: %.c | ||||
| 	$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ | ||||
| 
 | ||||
| $(OUTPUT)/libkvm.a: $(LIBKVM_OBJ) | ||||
| 	$(AR) crs $@ $^ | ||||
| 
 | ||||
| $(LINUX_HDR_PATH): | ||||
| 	make -C $(top_srcdir) headers_install | ||||
| 
 | ||||
| all: $(STATIC_LIBS) $(LINUX_HDR_PATH) | ||||
| $(TEST_GEN_PROGS): $(STATIC_LIBS) | ||||
| $(TEST_GEN_PROGS) $(LIBKVM_OBJ): | $(LINUX_HDR_PATH) | ||||
							
								
								
									
										139
									
								
								tools/testing/selftests/kvm/include/kvm_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								tools/testing/selftests/kvm/include/kvm_util.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,139 @@ | ||||
| /*
 | ||||
|  * tools/testing/selftests/kvm/include/kvm_util.h | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  * | ||||
|  */ | ||||
| #ifndef SELFTEST_KVM_UTIL_H | ||||
| #define SELFTEST_KVM_UTIL_H 1 | ||||
| 
 | ||||
| #include "test_util.h" | ||||
| 
 | ||||
| #include "asm/kvm.h" | ||||
| #include "linux/kvm.h" | ||||
| #include <sys/ioctl.h> | ||||
| 
 | ||||
| #include "sparsebit.h" | ||||
| 
 | ||||
| /*
 | ||||
|  * Memslots can't cover the gfn starting at this gpa otherwise vCPUs can't be | ||||
|  * created. Only applies to VMs using EPT. | ||||
|  */ | ||||
| #define KVM_DEFAULT_IDENTITY_MAP_ADDRESS 0xfffbc000ul | ||||
| 
 | ||||
| 
 | ||||
| /* Callers of kvm_util only have an incomplete/opaque description of the
 | ||||
|  * structure kvm_util is using to maintain the state of a VM. | ||||
|  */ | ||||
| struct kvm_vm; | ||||
| 
 | ||||
| typedef uint64_t vm_paddr_t; /* Virtual Machine (Guest) physical address */ | ||||
| typedef uint64_t vm_vaddr_t; /* Virtual Machine (Guest) virtual address */ | ||||
| 
 | ||||
| /* Minimum allocated guest virtual and physical addresses */ | ||||
| #define KVM_UTIL_MIN_VADDR 0x2000 | ||||
| 
 | ||||
| #define DEFAULT_GUEST_PHY_PAGES		512 | ||||
| #define DEFAULT_GUEST_STACK_VADDR_MIN	0xab6000 | ||||
| #define DEFAULT_STACK_PGS               5 | ||||
| 
 | ||||
| enum vm_guest_mode { | ||||
| 	VM_MODE_FLAT48PG, | ||||
| }; | ||||
| 
 | ||||
| enum vm_mem_backing_src_type { | ||||
| 	VM_MEM_SRC_ANONYMOUS, | ||||
| 	VM_MEM_SRC_ANONYMOUS_THP, | ||||
| 	VM_MEM_SRC_ANONYMOUS_HUGETLB, | ||||
| }; | ||||
| 
 | ||||
| int kvm_check_cap(long cap); | ||||
| 
 | ||||
| struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm); | ||||
| void kvm_vm_free(struct kvm_vm *vmp); | ||||
| 
 | ||||
| int kvm_memcmp_hva_gva(void *hva, | ||||
| 	struct kvm_vm *vm, const vm_vaddr_t gva, size_t len); | ||||
| 
 | ||||
| void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); | ||||
| void vcpu_dump(FILE *stream, struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, uint8_t indent); | ||||
| 
 | ||||
| void vm_create_irqchip(struct kvm_vm *vm); | ||||
| 
 | ||||
| void vm_userspace_mem_region_add(struct kvm_vm *vm, | ||||
| 	enum vm_mem_backing_src_type src_type, | ||||
| 	uint64_t guest_paddr, uint32_t slot, uint64_t npages, | ||||
| 	uint32_t flags); | ||||
| 
 | ||||
| void vcpu_ioctl(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, unsigned long ioctl, void *arg); | ||||
| void vm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg); | ||||
| void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags); | ||||
| void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid); | ||||
| vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, | ||||
| 	uint32_t data_memslot, uint32_t pgd_memslot); | ||||
| void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa); | ||||
| void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva); | ||||
| vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva); | ||||
| vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva); | ||||
| 
 | ||||
| struct kvm_run *vcpu_state(struct kvm_vm *vm, uint32_t vcpuid); | ||||
| void vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); | ||||
| int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); | ||||
| void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid, | ||||
| 	struct kvm_mp_state *mp_state); | ||||
| void vcpu_regs_get(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, struct kvm_regs *regs); | ||||
| void vcpu_regs_set(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, struct kvm_regs *regs); | ||||
| void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...); | ||||
| void vcpu_sregs_get(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, struct kvm_sregs *sregs); | ||||
| void vcpu_sregs_set(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, struct kvm_sregs *sregs); | ||||
| int _vcpu_sregs_set(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid, struct kvm_sregs *sregs); | ||||
| void vcpu_events_get(struct kvm_vm *vm, uint32_t vcpuid, | ||||
| 			  struct kvm_vcpu_events *events); | ||||
| void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid, | ||||
| 			  struct kvm_vcpu_events *events); | ||||
| 
 | ||||
| const char *exit_reason_str(unsigned int exit_reason); | ||||
| 
 | ||||
| void virt_pgd_alloc(struct kvm_vm *vm, uint32_t pgd_memslot); | ||||
| void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, | ||||
| 	uint32_t pgd_memslot); | ||||
| vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, | ||||
| 	vm_paddr_t paddr_min, uint32_t memslot); | ||||
| 
 | ||||
| void kvm_get_supported_cpuid(struct kvm_cpuid2 *cpuid); | ||||
| void vcpu_set_cpuid( | ||||
| 	struct kvm_vm *vm, uint32_t vcpuid, struct kvm_cpuid2 *cpuid); | ||||
| 
 | ||||
| struct kvm_cpuid2 *allocate_kvm_cpuid2(void); | ||||
| struct kvm_cpuid_entry2 * | ||||
| find_cpuid_index_entry(struct kvm_cpuid2 *cpuid, uint32_t function, | ||||
| 		       uint32_t index); | ||||
| 
 | ||||
| static inline struct kvm_cpuid_entry2 * | ||||
| find_cpuid_entry(struct kvm_cpuid2 *cpuid, uint32_t function) | ||||
| { | ||||
| 	return find_cpuid_index_entry(cpuid, function, 0); | ||||
| } | ||||
| 
 | ||||
| struct kvm_vm *vm_create_default(uint32_t vcpuid, void *guest_code); | ||||
| void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code); | ||||
| 
 | ||||
| struct kvm_userspace_memory_region * | ||||
| kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start, | ||||
| 				 uint64_t end); | ||||
| 
 | ||||
| struct kvm_dirty_log * | ||||
| allocate_kvm_dirty_log(struct kvm_userspace_memory_region *region); | ||||
| 
 | ||||
| int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd); | ||||
| 
 | ||||
| #endif /* SELFTEST_KVM_UTIL_H */ | ||||
							
								
								
									
										75
									
								
								tools/testing/selftests/kvm/include/sparsebit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								tools/testing/selftests/kvm/include/sparsebit.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| /*
 | ||||
|  * tools/testing/selftests/kvm/include/sparsebit.h | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  * | ||||
|  * | ||||
|  * Header file that describes API to the sparsebit library. | ||||
|  * This library provides a memory efficient means of storing | ||||
|  * the settings of bits indexed via a uint64_t.  Memory usage | ||||
|  * is reasonable, significantly less than (2^64 / 8) bytes, as | ||||
|  * long as bits that are mostly set or mostly cleared are close | ||||
|  * to each other.  This library is efficient in memory usage | ||||
|  * even in the case where most bits are set. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TEST_SPARSEBIT_H_ | ||||
| #define _TEST_SPARSEBIT_H_ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| struct sparsebit; | ||||
| typedef uint64_t sparsebit_idx_t; | ||||
| typedef uint64_t sparsebit_num_t; | ||||
| 
 | ||||
| struct sparsebit *sparsebit_alloc(void); | ||||
| void sparsebit_free(struct sparsebit **sbitp); | ||||
| void sparsebit_copy(struct sparsebit *dstp, struct sparsebit *src); | ||||
| 
 | ||||
| bool sparsebit_is_set(struct sparsebit *sbit, sparsebit_idx_t idx); | ||||
| bool sparsebit_is_set_num(struct sparsebit *sbit, | ||||
| 			  sparsebit_idx_t idx, sparsebit_num_t num); | ||||
| bool sparsebit_is_clear(struct sparsebit *sbit, sparsebit_idx_t idx); | ||||
| bool sparsebit_is_clear_num(struct sparsebit *sbit, | ||||
| 			    sparsebit_idx_t idx, sparsebit_num_t num); | ||||
| sparsebit_num_t sparsebit_num_set(struct sparsebit *sbit); | ||||
| bool sparsebit_any_set(struct sparsebit *sbit); | ||||
| bool sparsebit_any_clear(struct sparsebit *sbit); | ||||
| bool sparsebit_all_set(struct sparsebit *sbit); | ||||
| bool sparsebit_all_clear(struct sparsebit *sbit); | ||||
| sparsebit_idx_t sparsebit_first_set(struct sparsebit *sbit); | ||||
| sparsebit_idx_t sparsebit_first_clear(struct sparsebit *sbit); | ||||
| sparsebit_idx_t sparsebit_next_set(struct sparsebit *sbit, sparsebit_idx_t prev); | ||||
| sparsebit_idx_t sparsebit_next_clear(struct sparsebit *sbit, sparsebit_idx_t prev); | ||||
| sparsebit_idx_t sparsebit_next_set_num(struct sparsebit *sbit, | ||||
| 				       sparsebit_idx_t start, sparsebit_num_t num); | ||||
| sparsebit_idx_t sparsebit_next_clear_num(struct sparsebit *sbit, | ||||
| 					 sparsebit_idx_t start, sparsebit_num_t num); | ||||
| 
 | ||||
| void sparsebit_set(struct sparsebit *sbitp, sparsebit_idx_t idx); | ||||
| void sparsebit_set_num(struct sparsebit *sbitp, sparsebit_idx_t start, | ||||
| 		       sparsebit_num_t num); | ||||
| void sparsebit_set_all(struct sparsebit *sbitp); | ||||
| 
 | ||||
| void sparsebit_clear(struct sparsebit *sbitp, sparsebit_idx_t idx); | ||||
| void sparsebit_clear_num(struct sparsebit *sbitp, | ||||
| 			 sparsebit_idx_t start, sparsebit_num_t num); | ||||
| void sparsebit_clear_all(struct sparsebit *sbitp); | ||||
| 
 | ||||
| void sparsebit_dump(FILE *stream, struct sparsebit *sbit, | ||||
| 		    unsigned int indent); | ||||
| void sparsebit_validate_internal(struct sparsebit *sbit); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* _TEST_SPARSEBIT_H_ */ | ||||
							
								
								
									
										45
									
								
								tools/testing/selftests/kvm/include/test_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tools/testing/selftests/kvm/include/test_util.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| /*
 | ||||
|  * tools/testing/selftests/kvm/include/test_util.h | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef TEST_UTIL_H | ||||
| #define TEST_UTIL_H 1 | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
| #include <errno.h> | ||||
| #include <unistd.h> | ||||
| #include <fcntl.h> | ||||
| 
 | ||||
| ssize_t test_write(int fd, const void *buf, size_t count); | ||||
| ssize_t test_read(int fd, void *buf, size_t count); | ||||
| int test_seq_read(const char *path, char **bufp, size_t *sizep); | ||||
| 
 | ||||
| void test_assert(bool exp, const char *exp_str, | ||||
| 		 const char *file, unsigned int line, const char *fmt, ...); | ||||
| 
 | ||||
| #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) | ||||
| 
 | ||||
| #define TEST_ASSERT(e, fmt, ...) \ | ||||
| 	test_assert((e), #e, __FILE__, __LINE__, fmt, ##__VA_ARGS__) | ||||
| 
 | ||||
| #define ASSERT_EQ(a, b) do { \ | ||||
| 	typeof(a) __a = (a); \ | ||||
| 	typeof(b) __b = (b); \ | ||||
| 	TEST_ASSERT(__a == __b, \ | ||||
| 		    "ASSERT_EQ(%s, %s) failed.\n" \ | ||||
| 		    "\t%s is %#lx\n" \ | ||||
| 		    "\t%s is %#lx", \ | ||||
| 		    #a, #b, #a, (unsigned long) __a, #b, (unsigned long) __b); \ | ||||
| } while (0) | ||||
| 
 | ||||
| #endif /* TEST_UTIL_H */ | ||||
							
								
								
									
										1043
									
								
								tools/testing/selftests/kvm/include/x86.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1043
									
								
								tools/testing/selftests/kvm/include/x86.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										87
									
								
								tools/testing/selftests/kvm/lib/assert.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								tools/testing/selftests/kvm/lib/assert.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| /*
 | ||||
|  * tools/testing/selftests/kvm/lib/assert.c | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE /* for getline(3) and strchrnul(3)*/ | ||||
| 
 | ||||
| #include "test_util.h" | ||||
| 
 | ||||
| #include <execinfo.h> | ||||
| #include <sys/syscall.h> | ||||
| 
 | ||||
| /* Dumps the current stack trace to stderr. */ | ||||
| static void __attribute__((noinline)) test_dump_stack(void); | ||||
| static void test_dump_stack(void) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Build and run this command: | ||||
| 	 * | ||||
| 	 *	addr2line -s -e /proc/$PPID/exe -fpai {backtrace addresses} | \ | ||||
| 	 *		grep -v test_dump_stack | cat -n 1>&2 | ||||
| 	 * | ||||
| 	 * Note that the spacing is different and there's no newline. | ||||
| 	 */ | ||||
| 	size_t i; | ||||
| 	size_t n = 20; | ||||
| 	void *stack[n]; | ||||
| 	const char *addr2line = "addr2line -s -e /proc/$PPID/exe -fpai"; | ||||
| 	const char *pipeline = "|cat -n 1>&2"; | ||||
| 	char cmd[strlen(addr2line) + strlen(pipeline) + | ||||
| 		 /* N bytes per addr * 2 digits per byte + 1 space per addr: */ | ||||
| 		 n * (((sizeof(void *)) * 2) + 1) + | ||||
| 		 /* Null terminator: */ | ||||
| 		 1]; | ||||
| 	char *c; | ||||
| 
 | ||||
| 	n = backtrace(stack, n); | ||||
| 	c = &cmd[0]; | ||||
| 	c += sprintf(c, "%s", addr2line); | ||||
| 	/*
 | ||||
| 	 * Skip the first 3 frames: backtrace, test_dump_stack, and | ||||
| 	 * test_assert. We hope that backtrace isn't inlined and the other two | ||||
| 	 * we've declared noinline. | ||||
| 	 */ | ||||
| 	for (i = 2; i < n; i++) | ||||
| 		c += sprintf(c, " %lx", ((unsigned long) stack[i]) - 1); | ||||
| 	c += sprintf(c, "%s", pipeline); | ||||
| #pragma GCC diagnostic push | ||||
| #pragma GCC diagnostic ignored "-Wunused-result" | ||||
| 	system(cmd); | ||||
| #pragma GCC diagnostic pop | ||||
| } | ||||
| 
 | ||||
| static pid_t gettid(void) | ||||
| { | ||||
| 	return syscall(SYS_gettid); | ||||
| } | ||||
| 
 | ||||
| void __attribute__((noinline)) | ||||
| test_assert(bool exp, const char *exp_str, | ||||
| 	const char *file, unsigned int line, const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 
 | ||||
| 	if (!(exp)) { | ||||
| 		va_start(ap, fmt); | ||||
| 
 | ||||
| 		fprintf(stderr, "==== Test Assertion Failure ====\n" | ||||
| 			"  %s:%u: %s\n" | ||||
| 			"  pid=%d tid=%d\n", | ||||
| 			file, line, exp_str, getpid(), gettid()); | ||||
| 		test_dump_stack(); | ||||
| 		if (fmt) { | ||||
| 			fputs("  ", stderr); | ||||
| 			vfprintf(stderr, fmt, ap); | ||||
| 			fputs("\n", stderr); | ||||
| 		} | ||||
| 		va_end(ap); | ||||
| 
 | ||||
| 		exit(254); | ||||
| 	} | ||||
| 
 | ||||
| 	return; | ||||
| } | ||||
							
								
								
									
										1480
									
								
								tools/testing/selftests/kvm/lib/kvm_util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1480
									
								
								tools/testing/selftests/kvm/lib/kvm_util.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										67
									
								
								tools/testing/selftests/kvm/lib/kvm_util_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								tools/testing/selftests/kvm/lib/kvm_util_internal.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| /*
 | ||||
|  * tools/testing/selftests/kvm/lib/kvm_util.c | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef KVM_UTIL_INTERNAL_H | ||||
| #define KVM_UTIL_INTERNAL_H 1 | ||||
| 
 | ||||
| #include "sparsebit.h" | ||||
| 
 | ||||
| #ifndef BITS_PER_BYTE | ||||
| #define BITS_PER_BYTE           8 | ||||
| #endif | ||||
| 
 | ||||
| #ifndef BITS_PER_LONG | ||||
| #define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long)) | ||||
| #endif | ||||
| 
 | ||||
| #define DIV_ROUND_UP(n, d)	(((n) + (d) - 1) / (d)) | ||||
| #define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_LONG) | ||||
| 
 | ||||
| /* Concrete definition of struct kvm_vm. */ | ||||
| struct userspace_mem_region { | ||||
| 	struct userspace_mem_region *next, *prev; | ||||
| 	struct kvm_userspace_memory_region region; | ||||
| 	struct sparsebit *unused_phy_pages; | ||||
| 	int fd; | ||||
| 	off_t offset; | ||||
| 	void *host_mem; | ||||
| 	void *mmap_start; | ||||
| 	size_t mmap_size; | ||||
| }; | ||||
| 
 | ||||
| struct vcpu { | ||||
| 	struct vcpu *next, *prev; | ||||
| 	uint32_t id; | ||||
| 	int fd; | ||||
| 	struct kvm_run *state; | ||||
| }; | ||||
| 
 | ||||
| struct kvm_vm { | ||||
| 	int mode; | ||||
| 	int fd; | ||||
| 	unsigned int page_size; | ||||
| 	unsigned int page_shift; | ||||
| 	uint64_t max_gfn; | ||||
| 	struct vcpu *vcpu_head; | ||||
| 	struct userspace_mem_region *userspace_mem_region_head; | ||||
| 	struct sparsebit *vpages_valid; | ||||
| 	struct sparsebit *vpages_mapped; | ||||
| 	bool pgd_created; | ||||
| 	vm_paddr_t pgd; | ||||
| }; | ||||
| 
 | ||||
| struct vcpu *vcpu_find(struct kvm_vm *vm, | ||||
| 	uint32_t vcpuid); | ||||
| void vcpu_setup(struct kvm_vm *vm, int vcpuid); | ||||
| void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); | ||||
| void regs_dump(FILE *stream, struct kvm_regs *regs, | ||||
| 	uint8_t indent); | ||||
| void sregs_dump(FILE *stream, struct kvm_sregs *sregs, | ||||
| 	uint8_t indent); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										2087
									
								
								tools/testing/selftests/kvm/lib/sparsebit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2087
									
								
								tools/testing/selftests/kvm/lib/sparsebit.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										697
									
								
								tools/testing/selftests/kvm/lib/x86.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								tools/testing/selftests/kvm/lib/x86.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,697 @@ | ||||
| /*
 | ||||
|  * tools/testing/selftests/kvm/lib/x86.c | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  */ | ||||
| 
 | ||||
| #define _GNU_SOURCE /* for program_invocation_name */ | ||||
| 
 | ||||
| #include "test_util.h" | ||||
| #include "kvm_util.h" | ||||
| #include "kvm_util_internal.h" | ||||
| #include "x86.h" | ||||
| 
 | ||||
| /* Minimum physical address used for virtual translation tables. */ | ||||
| #define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000 | ||||
| 
 | ||||
| /* Virtual translation table structure declarations */ | ||||
| struct pageMapL4Entry { | ||||
| 	uint64_t present:1; | ||||
| 	uint64_t writable:1; | ||||
| 	uint64_t user:1; | ||||
| 	uint64_t write_through:1; | ||||
| 	uint64_t cache_disable:1; | ||||
| 	uint64_t accessed:1; | ||||
| 	uint64_t ignored_06:1; | ||||
| 	uint64_t page_size:1; | ||||
| 	uint64_t ignored_11_08:4; | ||||
| 	uint64_t address:40; | ||||
| 	uint64_t ignored_62_52:11; | ||||
| 	uint64_t execute_disable:1; | ||||
| }; | ||||
| 
 | ||||
| struct pageDirectoryPointerEntry { | ||||
| 	uint64_t present:1; | ||||
| 	uint64_t writable:1; | ||||
| 	uint64_t user:1; | ||||
| 	uint64_t write_through:1; | ||||
| 	uint64_t cache_disable:1; | ||||
| 	uint64_t accessed:1; | ||||
| 	uint64_t ignored_06:1; | ||||
| 	uint64_t page_size:1; | ||||
| 	uint64_t ignored_11_08:4; | ||||
| 	uint64_t address:40; | ||||
| 	uint64_t ignored_62_52:11; | ||||
| 	uint64_t execute_disable:1; | ||||
| }; | ||||
| 
 | ||||
| struct pageDirectoryEntry { | ||||
| 	uint64_t present:1; | ||||
| 	uint64_t writable:1; | ||||
| 	uint64_t user:1; | ||||
| 	uint64_t write_through:1; | ||||
| 	uint64_t cache_disable:1; | ||||
| 	uint64_t accessed:1; | ||||
| 	uint64_t ignored_06:1; | ||||
| 	uint64_t page_size:1; | ||||
| 	uint64_t ignored_11_08:4; | ||||
| 	uint64_t address:40; | ||||
| 	uint64_t ignored_62_52:11; | ||||
| 	uint64_t execute_disable:1; | ||||
| }; | ||||
| 
 | ||||
| struct pageTableEntry { | ||||
| 	uint64_t present:1; | ||||
| 	uint64_t writable:1; | ||||
| 	uint64_t user:1; | ||||
| 	uint64_t write_through:1; | ||||
| 	uint64_t cache_disable:1; | ||||
| 	uint64_t accessed:1; | ||||
| 	uint64_t dirty:1; | ||||
| 	uint64_t reserved_07:1; | ||||
| 	uint64_t global:1; | ||||
| 	uint64_t ignored_11_09:3; | ||||
| 	uint64_t address:40; | ||||
| 	uint64_t ignored_62_52:11; | ||||
| 	uint64_t execute_disable:1; | ||||
| }; | ||||
| 
 | ||||
| /* Register Dump
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   indent - Left margin indent amount | ||||
|  *   regs - register | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   stream - Output FILE stream | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Dumps the state of the registers given by regs, to the FILE stream | ||||
|  * given by steam. | ||||
|  */ | ||||
| void regs_dump(FILE *stream, struct kvm_regs *regs, | ||||
| 	       uint8_t indent) | ||||
| { | ||||
| 	fprintf(stream, "%*srax: 0x%.16llx rbx: 0x%.16llx " | ||||
| 		"rcx: 0x%.16llx rdx: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		regs->rax, regs->rbx, regs->rcx, regs->rdx); | ||||
| 	fprintf(stream, "%*srsi: 0x%.16llx rdi: 0x%.16llx " | ||||
| 		"rsp: 0x%.16llx rbp: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		regs->rsi, regs->rdi, regs->rsp, regs->rbp); | ||||
| 	fprintf(stream, "%*sr8:  0x%.16llx r9:  0x%.16llx " | ||||
| 		"r10: 0x%.16llx r11: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		regs->r8, regs->r9, regs->r10, regs->r11); | ||||
| 	fprintf(stream, "%*sr12: 0x%.16llx r13: 0x%.16llx " | ||||
| 		"r14: 0x%.16llx r15: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		regs->r12, regs->r13, regs->r14, regs->r15); | ||||
| 	fprintf(stream, "%*srip: 0x%.16llx rfl: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		regs->rip, regs->rflags); | ||||
| } | ||||
| 
 | ||||
| /* Segment Dump
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   indent - Left margin indent amount | ||||
|  *   segment - KVM segment | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   stream - Output FILE stream | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Dumps the state of the KVM segment given by segment, to the FILE stream | ||||
|  * given by steam. | ||||
|  */ | ||||
| static void segment_dump(FILE *stream, struct kvm_segment *segment, | ||||
| 			 uint8_t indent) | ||||
| { | ||||
| 	fprintf(stream, "%*sbase: 0x%.16llx limit: 0x%.8x " | ||||
| 		"selector: 0x%.4x type: 0x%.2x\n", | ||||
| 		indent, "", segment->base, segment->limit, | ||||
| 		segment->selector, segment->type); | ||||
| 	fprintf(stream, "%*spresent: 0x%.2x dpl: 0x%.2x " | ||||
| 		"db: 0x%.2x s: 0x%.2x l: 0x%.2x\n", | ||||
| 		indent, "", segment->present, segment->dpl, | ||||
| 		segment->db, segment->s, segment->l); | ||||
| 	fprintf(stream, "%*sg: 0x%.2x avl: 0x%.2x " | ||||
| 		"unusable: 0x%.2x padding: 0x%.2x\n", | ||||
| 		indent, "", segment->g, segment->avl, | ||||
| 		segment->unusable, segment->padding); | ||||
| } | ||||
| 
 | ||||
| /* dtable Dump
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   indent - Left margin indent amount | ||||
|  *   dtable - KVM dtable | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   stream - Output FILE stream | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Dumps the state of the KVM dtable given by dtable, to the FILE stream | ||||
|  * given by steam. | ||||
|  */ | ||||
| static void dtable_dump(FILE *stream, struct kvm_dtable *dtable, | ||||
| 			uint8_t indent) | ||||
| { | ||||
| 	fprintf(stream, "%*sbase: 0x%.16llx limit: 0x%.4x " | ||||
| 		"padding: 0x%.4x 0x%.4x 0x%.4x\n", | ||||
| 		indent, "", dtable->base, dtable->limit, | ||||
| 		dtable->padding[0], dtable->padding[1], dtable->padding[2]); | ||||
| } | ||||
| 
 | ||||
| /* System Register Dump
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   indent - Left margin indent amount | ||||
|  *   sregs - System registers | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   stream - Output FILE stream | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Dumps the state of the system registers given by sregs, to the FILE stream | ||||
|  * given by steam. | ||||
|  */ | ||||
| void sregs_dump(FILE *stream, struct kvm_sregs *sregs, | ||||
| 		uint8_t indent) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	fprintf(stream, "%*scs:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->cs, indent + 2); | ||||
| 	fprintf(stream, "%*sds:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->ds, indent + 2); | ||||
| 	fprintf(stream, "%*ses:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->es, indent + 2); | ||||
| 	fprintf(stream, "%*sfs:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->fs, indent + 2); | ||||
| 	fprintf(stream, "%*sgs:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->gs, indent + 2); | ||||
| 	fprintf(stream, "%*sss:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->ss, indent + 2); | ||||
| 	fprintf(stream, "%*str:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->tr, indent + 2); | ||||
| 	fprintf(stream, "%*sldt:\n", indent, ""); | ||||
| 	segment_dump(stream, &sregs->ldt, indent + 2); | ||||
| 
 | ||||
| 	fprintf(stream, "%*sgdt:\n", indent, ""); | ||||
| 	dtable_dump(stream, &sregs->gdt, indent + 2); | ||||
| 	fprintf(stream, "%*sidt:\n", indent, ""); | ||||
| 	dtable_dump(stream, &sregs->idt, indent + 2); | ||||
| 
 | ||||
| 	fprintf(stream, "%*scr0: 0x%.16llx cr2: 0x%.16llx " | ||||
| 		"cr3: 0x%.16llx cr4: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		sregs->cr0, sregs->cr2, sregs->cr3, sregs->cr4); | ||||
| 	fprintf(stream, "%*scr8: 0x%.16llx efer: 0x%.16llx " | ||||
| 		"apic_base: 0x%.16llx\n", | ||||
| 		indent, "", | ||||
| 		sregs->cr8, sregs->efer, sregs->apic_base); | ||||
| 
 | ||||
| 	fprintf(stream, "%*sinterrupt_bitmap:\n", indent, ""); | ||||
| 	for (i = 0; i < (KVM_NR_INTERRUPTS + 63) / 64; i++) { | ||||
| 		fprintf(stream, "%*s%.16llx\n", indent + 2, "", | ||||
| 			sregs->interrupt_bitmap[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void virt_pgd_alloc(struct kvm_vm *vm, uint32_t pgd_memslot) | ||||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	TEST_ASSERT(vm->mode == VM_MODE_FLAT48PG, "Attempt to use " | ||||
| 		"unknown or unsupported guest mode, mode: 0x%x", vm->mode); | ||||
| 
 | ||||
| 	/* If needed, create page map l4 table. */ | ||||
| 	if (!vm->pgd_created) { | ||||
| 		vm_paddr_t paddr = vm_phy_page_alloc(vm, | ||||
| 			KVM_GUEST_PAGE_TABLE_MIN_PADDR, pgd_memslot); | ||||
| 		vm->pgd = paddr; | ||||
| 
 | ||||
| 		/* Set pointer to pgd tables in all the VCPUs that
 | ||||
| 		 * have already been created.  Future VCPUs will have | ||||
| 		 * the value set as each one is created. | ||||
| 		 */ | ||||
| 		for (struct vcpu *vcpu = vm->vcpu_head; vcpu; | ||||
| 			vcpu = vcpu->next) { | ||||
| 			struct kvm_sregs sregs; | ||||
| 
 | ||||
| 			/* Obtain the current system register settings */ | ||||
| 			vcpu_sregs_get(vm, vcpu->id, &sregs); | ||||
| 
 | ||||
| 			/* Set and store the pointer to the start of the
 | ||||
| 			 * pgd tables. | ||||
| 			 */ | ||||
| 			sregs.cr3 = vm->pgd; | ||||
| 			vcpu_sregs_set(vm, vcpu->id, &sregs); | ||||
| 		} | ||||
| 
 | ||||
| 		vm->pgd_created = true; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* VM Virtual Page Map
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   vm - Virtual Machine | ||||
|  *   vaddr - VM Virtual Address | ||||
|  *   paddr - VM Physical Address | ||||
|  *   pgd_memslot - Memory region slot for new virtual translation tables | ||||
|  * | ||||
|  * Output Args: None | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Within the VM given by vm, creates a virtual translation for the page | ||||
|  * starting at vaddr to the page starting at paddr. | ||||
|  */ | ||||
| void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, | ||||
| 	uint32_t pgd_memslot) | ||||
| { | ||||
| 	uint16_t index[4]; | ||||
| 	struct pageMapL4Entry *pml4e; | ||||
| 
 | ||||
| 	TEST_ASSERT(vm->mode == VM_MODE_FLAT48PG, "Attempt to use " | ||||
| 		"unknown or unsupported guest mode, mode: 0x%x", vm->mode); | ||||
| 
 | ||||
| 	TEST_ASSERT((vaddr % vm->page_size) == 0, | ||||
| 		"Virtual address not on page boundary,\n" | ||||
| 		"  vaddr: 0x%lx vm->page_size: 0x%x", | ||||
| 		vaddr, vm->page_size); | ||||
| 	TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, | ||||
| 		(vaddr >> vm->page_shift)), | ||||
| 		"Invalid virtual address, vaddr: 0x%lx", | ||||
| 		vaddr); | ||||
| 	TEST_ASSERT((paddr % vm->page_size) == 0, | ||||
| 		"Physical address not on page boundary,\n" | ||||
| 		"  paddr: 0x%lx vm->page_size: 0x%x", | ||||
| 		paddr, vm->page_size); | ||||
| 	TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn, | ||||
| 		"Physical address beyond beyond maximum supported,\n" | ||||
| 		"  paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x", | ||||
| 		paddr, vm->max_gfn, vm->page_size); | ||||
| 
 | ||||
| 	index[0] = (vaddr >> 12) & 0x1ffu; | ||||
| 	index[1] = (vaddr >> 21) & 0x1ffu; | ||||
| 	index[2] = (vaddr >> 30) & 0x1ffu; | ||||
| 	index[3] = (vaddr >> 39) & 0x1ffu; | ||||
| 
 | ||||
| 	/* Allocate page directory pointer table if not present. */ | ||||
| 	pml4e = addr_gpa2hva(vm, vm->pgd); | ||||
| 	if (!pml4e[index[3]].present) { | ||||
| 		pml4e[index[3]].address = vm_phy_page_alloc(vm, | ||||
| 			KVM_GUEST_PAGE_TABLE_MIN_PADDR, pgd_memslot) | ||||
| 			>> vm->page_shift; | ||||
| 		pml4e[index[3]].writable = true; | ||||
| 		pml4e[index[3]].present = true; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Allocate page directory table if not present. */ | ||||
| 	struct pageDirectoryPointerEntry *pdpe; | ||||
| 	pdpe = addr_gpa2hva(vm, pml4e[index[3]].address * vm->page_size); | ||||
| 	if (!pdpe[index[2]].present) { | ||||
| 		pdpe[index[2]].address = vm_phy_page_alloc(vm, | ||||
| 			KVM_GUEST_PAGE_TABLE_MIN_PADDR, pgd_memslot) | ||||
| 			>> vm->page_shift; | ||||
| 		pdpe[index[2]].writable = true; | ||||
| 		pdpe[index[2]].present = true; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Allocate page table if not present. */ | ||||
| 	struct pageDirectoryEntry *pde; | ||||
| 	pde = addr_gpa2hva(vm, pdpe[index[2]].address * vm->page_size); | ||||
| 	if (!pde[index[1]].present) { | ||||
| 		pde[index[1]].address = vm_phy_page_alloc(vm, | ||||
| 			KVM_GUEST_PAGE_TABLE_MIN_PADDR, pgd_memslot) | ||||
| 			>> vm->page_shift; | ||||
| 		pde[index[1]].writable = true; | ||||
| 		pde[index[1]].present = true; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Fill in page table entry. */ | ||||
| 	struct pageTableEntry *pte; | ||||
| 	pte = addr_gpa2hva(vm, pde[index[1]].address * vm->page_size); | ||||
| 	pte[index[0]].address = paddr >> vm->page_shift; | ||||
| 	pte[index[0]].writable = true; | ||||
| 	pte[index[0]].present = 1; | ||||
| } | ||||
| 
 | ||||
| /* Virtual Translation Tables Dump
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   vm - Virtual Machine | ||||
|  *   indent - Left margin indent amount | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   stream - Output FILE stream | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Dumps to the FILE stream given by stream, the contents of all the | ||||
|  * virtual translation tables for the VM given by vm. | ||||
|  */ | ||||
| void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) | ||||
| { | ||||
| 	struct pageMapL4Entry *pml4e, *pml4e_start; | ||||
| 	struct pageDirectoryPointerEntry *pdpe, *pdpe_start; | ||||
| 	struct pageDirectoryEntry *pde, *pde_start; | ||||
| 	struct pageTableEntry *pte, *pte_start; | ||||
| 
 | ||||
| 	if (!vm->pgd_created) | ||||
| 		return; | ||||
| 
 | ||||
| 	fprintf(stream, "%*s                                          " | ||||
| 		"                no\n", indent, ""); | ||||
| 	fprintf(stream, "%*s      index hvaddr         gpaddr         " | ||||
| 		"addr         w exec dirty\n", | ||||
| 		indent, ""); | ||||
| 	pml4e_start = (struct pageMapL4Entry *) addr_gpa2hva(vm, | ||||
| 		vm->pgd); | ||||
| 	for (uint16_t n1 = 0; n1 <= 0x1ffu; n1++) { | ||||
| 		pml4e = &pml4e_start[n1]; | ||||
| 		if (!pml4e->present) | ||||
| 			continue; | ||||
| 		fprintf(stream, "%*spml4e 0x%-3zx %p 0x%-12lx 0x%-10lx %u " | ||||
| 			" %u\n", | ||||
| 			indent, "", | ||||
| 			pml4e - pml4e_start, pml4e, | ||||
| 			addr_hva2gpa(vm, pml4e), (uint64_t) pml4e->address, | ||||
| 			pml4e->writable, pml4e->execute_disable); | ||||
| 
 | ||||
| 		pdpe_start = addr_gpa2hva(vm, pml4e->address | ||||
| 			* vm->page_size); | ||||
| 		for (uint16_t n2 = 0; n2 <= 0x1ffu; n2++) { | ||||
| 			pdpe = &pdpe_start[n2]; | ||||
| 			if (!pdpe->present) | ||||
| 				continue; | ||||
| 			fprintf(stream, "%*spdpe  0x%-3zx %p 0x%-12lx 0x%-10lx " | ||||
| 				"%u  %u\n", | ||||
| 				indent, "", | ||||
| 				pdpe - pdpe_start, pdpe, | ||||
| 				addr_hva2gpa(vm, pdpe), | ||||
| 				(uint64_t) pdpe->address, pdpe->writable, | ||||
| 				pdpe->execute_disable); | ||||
| 
 | ||||
| 			pde_start = addr_gpa2hva(vm, | ||||
| 				pdpe->address * vm->page_size); | ||||
| 			for (uint16_t n3 = 0; n3 <= 0x1ffu; n3++) { | ||||
| 				pde = &pde_start[n3]; | ||||
| 				if (!pde->present) | ||||
| 					continue; | ||||
| 				fprintf(stream, "%*spde   0x%-3zx %p " | ||||
| 					"0x%-12lx 0x%-10lx %u  %u\n", | ||||
| 					indent, "", pde - pde_start, pde, | ||||
| 					addr_hva2gpa(vm, pde), | ||||
| 					(uint64_t) pde->address, pde->writable, | ||||
| 					pde->execute_disable); | ||||
| 
 | ||||
| 				pte_start = addr_gpa2hva(vm, | ||||
| 					pde->address * vm->page_size); | ||||
| 				for (uint16_t n4 = 0; n4 <= 0x1ffu; n4++) { | ||||
| 					pte = &pte_start[n4]; | ||||
| 					if (!pte->present) | ||||
| 						continue; | ||||
| 					fprintf(stream, "%*spte   0x%-3zx %p " | ||||
| 						"0x%-12lx 0x%-10lx %u  %u " | ||||
| 						"    %u    0x%-10lx\n", | ||||
| 						indent, "", | ||||
| 						pte - pte_start, pte, | ||||
| 						addr_hva2gpa(vm, pte), | ||||
| 						(uint64_t) pte->address, | ||||
| 						pte->writable, | ||||
| 						pte->execute_disable, | ||||
| 						pte->dirty, | ||||
| 						((uint64_t) n1 << 27) | ||||
| 							| ((uint64_t) n2 << 18) | ||||
| 							| ((uint64_t) n3 << 9) | ||||
| 							| ((uint64_t) n4)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* Set Unusable Segment
 | ||||
|  * | ||||
|  * Input Args: None | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   segp - Pointer to segment register | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Sets the segment register pointed to by segp to an unusable state. | ||||
|  */ | ||||
| static void kvm_seg_set_unusable(struct kvm_segment *segp) | ||||
| { | ||||
| 	memset(segp, 0, sizeof(*segp)); | ||||
| 	segp->unusable = true; | ||||
| } | ||||
| 
 | ||||
| /* Set Long Mode Flat Kernel Code Segment
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   selector - selector value | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   segp - Pointer to KVM segment | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Sets up the KVM segment pointed to by segp, to be a code segment | ||||
|  * with the selector value given by selector. | ||||
|  */ | ||||
| static void kvm_seg_set_kernel_code_64bit(uint16_t selector, | ||||
| 	struct kvm_segment *segp) | ||||
| { | ||||
| 	memset(segp, 0, sizeof(*segp)); | ||||
| 	segp->selector = selector; | ||||
| 	segp->limit = 0xFFFFFFFFu; | ||||
| 	segp->s = 0x1; /* kTypeCodeData */ | ||||
| 	segp->type = 0x08 | 0x01 | 0x02; /* kFlagCode | kFlagCodeAccessed
 | ||||
| 					  * | kFlagCodeReadable | ||||
| 					  */ | ||||
| 	segp->g = true; | ||||
| 	segp->l = true; | ||||
| 	segp->present = 1; | ||||
| } | ||||
| 
 | ||||
| /* Set Long Mode Flat Kernel Data Segment
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   selector - selector value | ||||
|  * | ||||
|  * Output Args: | ||||
|  *   segp - Pointer to KVM segment | ||||
|  * | ||||
|  * Return: None | ||||
|  * | ||||
|  * Sets up the KVM segment pointed to by segp, to be a data segment | ||||
|  * with the selector value given by selector. | ||||
|  */ | ||||
| static void kvm_seg_set_kernel_data_64bit(uint16_t selector, | ||||
| 	struct kvm_segment *segp) | ||||
| { | ||||
| 	memset(segp, 0, sizeof(*segp)); | ||||
| 	segp->selector = selector; | ||||
| 	segp->limit = 0xFFFFFFFFu; | ||||
| 	segp->s = 0x1; /* kTypeCodeData */ | ||||
| 	segp->type = 0x00 | 0x01 | 0x02; /* kFlagData | kFlagDataAccessed
 | ||||
| 					  * | kFlagDataWritable | ||||
| 					  */ | ||||
| 	segp->g = true; | ||||
| 	segp->present = true; | ||||
| } | ||||
| 
 | ||||
| /* Address Guest Virtual to Guest Physical
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   vm - Virtual Machine | ||||
|  *   gpa - VM virtual address | ||||
|  * | ||||
|  * Output Args: None | ||||
|  * | ||||
|  * Return: | ||||
|  *   Equivalent VM physical address | ||||
|  * | ||||
|  * Translates the VM virtual address given by gva to a VM physical | ||||
|  * address and then locates the memory region containing the VM | ||||
|  * physical address, within the VM given by vm.  When found, the host | ||||
|  * virtual address providing the memory to the vm physical address is returned. | ||||
|  * A TEST_ASSERT failure occurs if no region containing translated | ||||
|  * VM virtual address exists. | ||||
|  */ | ||||
| vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) | ||||
| { | ||||
| 	uint16_t index[4]; | ||||
| 	struct pageMapL4Entry *pml4e; | ||||
| 	struct pageDirectoryPointerEntry *pdpe; | ||||
| 	struct pageDirectoryEntry *pde; | ||||
| 	struct pageTableEntry *pte; | ||||
| 	void *hva; | ||||
| 
 | ||||
| 	TEST_ASSERT(vm->mode == VM_MODE_FLAT48PG, "Attempt to use " | ||||
| 		"unknown or unsupported guest mode, mode: 0x%x", vm->mode); | ||||
| 
 | ||||
| 	index[0] = (gva >> 12) & 0x1ffu; | ||||
| 	index[1] = (gva >> 21) & 0x1ffu; | ||||
| 	index[2] = (gva >> 30) & 0x1ffu; | ||||
| 	index[3] = (gva >> 39) & 0x1ffu; | ||||
| 
 | ||||
| 	if (!vm->pgd_created) | ||||
| 		goto unmapped_gva; | ||||
| 	pml4e = addr_gpa2hva(vm, vm->pgd); | ||||
| 	if (!pml4e[index[3]].present) | ||||
| 		goto unmapped_gva; | ||||
| 
 | ||||
| 	pdpe = addr_gpa2hva(vm, pml4e[index[3]].address * vm->page_size); | ||||
| 	if (!pdpe[index[2]].present) | ||||
| 		goto unmapped_gva; | ||||
| 
 | ||||
| 	pde = addr_gpa2hva(vm, pdpe[index[2]].address * vm->page_size); | ||||
| 	if (!pde[index[1]].present) | ||||
| 		goto unmapped_gva; | ||||
| 
 | ||||
| 	pte = addr_gpa2hva(vm, pde[index[1]].address * vm->page_size); | ||||
| 	if (!pte[index[0]].present) | ||||
| 		goto unmapped_gva; | ||||
| 
 | ||||
| 	return (pte[index[0]].address * vm->page_size) + (gva & 0xfffu); | ||||
| 
 | ||||
| unmapped_gva: | ||||
| 	TEST_ASSERT(false, "No mapping for vm virtual address, " | ||||
| 		    "gva: 0x%lx", gva); | ||||
| } | ||||
| 
 | ||||
| void vcpu_setup(struct kvm_vm *vm, int vcpuid) | ||||
| { | ||||
| 	struct kvm_sregs sregs; | ||||
| 
 | ||||
| 	/* Set mode specific system register values. */ | ||||
| 	vcpu_sregs_get(vm, vcpuid, &sregs); | ||||
| 
 | ||||
| 	switch (vm->mode) { | ||||
| 	case VM_MODE_FLAT48PG: | ||||
| 		sregs.cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG; | ||||
| 		sregs.cr4 |= X86_CR4_PAE; | ||||
| 		sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX); | ||||
| 
 | ||||
| 		kvm_seg_set_unusable(&sregs.ldt); | ||||
| 		kvm_seg_set_kernel_code_64bit(0x8, &sregs.cs); | ||||
| 		kvm_seg_set_kernel_data_64bit(0x10, &sregs.ds); | ||||
| 		kvm_seg_set_kernel_data_64bit(0x10, &sregs.es); | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		TEST_ASSERT(false, "Unknown guest mode, mode: 0x%x", vm->mode); | ||||
| 	} | ||||
| 	vcpu_sregs_set(vm, vcpuid, &sregs); | ||||
| 
 | ||||
| 	/* If virtual translation table have been setup, set system register
 | ||||
| 	 * to point to the tables.  It's okay if they haven't been setup yet, | ||||
| 	 * in that the code that sets up the virtual translation tables, will | ||||
| 	 * go back through any VCPUs that have already been created and set | ||||
| 	 * their values. | ||||
| 	 */ | ||||
| 	if (vm->pgd_created) { | ||||
| 		struct kvm_sregs sregs; | ||||
| 
 | ||||
| 		vcpu_sregs_get(vm, vcpuid, &sregs); | ||||
| 
 | ||||
| 		sregs.cr3 = vm->pgd; | ||||
| 		vcpu_sregs_set(vm, vcpuid, &sregs); | ||||
| 	} | ||||
| } | ||||
| /* Adds a vCPU with reasonable defaults (i.e., a stack)
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   vcpuid - The id of the VCPU to add to the VM. | ||||
|  *   guest_code - The vCPU's entry point | ||||
|  */ | ||||
| void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) | ||||
| { | ||||
| 	struct kvm_mp_state mp_state; | ||||
| 	struct kvm_regs regs; | ||||
| 	vm_vaddr_t stack_vaddr; | ||||
| 	stack_vaddr = vm_vaddr_alloc(vm, DEFAULT_STACK_PGS * getpagesize(), | ||||
| 				     DEFAULT_GUEST_STACK_VADDR_MIN, 0, 0); | ||||
| 
 | ||||
| 	/* Create VCPU */ | ||||
| 	vm_vcpu_add(vm, vcpuid); | ||||
| 
 | ||||
| 	/* Setup guest general purpose registers */ | ||||
| 	vcpu_regs_get(vm, vcpuid, ®s); | ||||
| 	regs.rflags = regs.rflags | 0x2; | ||||
| 	regs.rsp = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize()); | ||||
| 	regs.rip = (unsigned long) guest_code; | ||||
| 	vcpu_regs_set(vm, vcpuid, ®s); | ||||
| 
 | ||||
| 	/* Setup the MP state */ | ||||
| 	mp_state.mp_state = 0; | ||||
| 	vcpu_set_mp_state(vm, vcpuid, &mp_state); | ||||
| } | ||||
| 
 | ||||
| /* VM VCPU CPUID Set
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   vm - Virtual Machine | ||||
|  *   vcpuid - VCPU id | ||||
|  *   cpuid - The CPUID values to set. | ||||
|  * | ||||
|  * Output Args: None | ||||
|  * | ||||
|  * Return: void | ||||
|  * | ||||
|  * Set the VCPU's CPUID. | ||||
|  */ | ||||
| void vcpu_set_cpuid(struct kvm_vm *vm, | ||||
| 		uint32_t vcpuid, struct kvm_cpuid2 *cpuid) | ||||
| { | ||||
| 	struct vcpu *vcpu = vcpu_find(vm, vcpuid); | ||||
| 	int rc; | ||||
| 
 | ||||
| 	TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); | ||||
| 
 | ||||
| 	rc = ioctl(vcpu->fd, KVM_SET_CPUID2, cpuid); | ||||
| 	TEST_ASSERT(rc == 0, "KVM_SET_CPUID2 failed, rc: %i errno: %i", | ||||
| 		    rc, errno); | ||||
| 
 | ||||
| } | ||||
| /* Create a VM with reasonable defaults
 | ||||
|  * | ||||
|  * Input Args: | ||||
|  *   vcpuid - The id of the single VCPU to add to the VM. | ||||
|  *   guest_code - The vCPU's entry point | ||||
|  * | ||||
|  * Output Args: None | ||||
|  * | ||||
|  * Return: | ||||
|  *   Pointer to opaque structure that describes the created VM. | ||||
|  */ | ||||
| struct kvm_vm *vm_create_default(uint32_t vcpuid, void *guest_code) | ||||
| { | ||||
| 	struct kvm_vm *vm; | ||||
| 
 | ||||
| 	/* Create VM */ | ||||
| 	vm = vm_create(VM_MODE_FLAT48PG, DEFAULT_GUEST_PHY_PAGES, O_RDWR); | ||||
| 
 | ||||
| 	/* Setup IRQ Chip */ | ||||
| 	vm_create_irqchip(vm); | ||||
| 
 | ||||
| 	/* Add the first vCPU. */ | ||||
| 	vm_vcpu_add_default(vm, vcpuid, guest_code); | ||||
| 
 | ||||
| 	return vm; | ||||
| } | ||||
							
								
								
									
										54
									
								
								tools/testing/selftests/kvm/set_sregs_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								tools/testing/selftests/kvm/set_sregs_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| /*
 | ||||
|  * KVM_SET_SREGS tests | ||||
|  * | ||||
|  * Copyright (C) 2018, Google LLC. | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. | ||||
|  * | ||||
|  * This is a regression test for the bug fixed by the following commit: | ||||
|  * d3802286fa0f ("kvm: x86: Disallow illegal IA32_APIC_BASE MSR values") | ||||
|  * | ||||
|  * That bug allowed a user-mode program that called the KVM_SET_SREGS | ||||
|  * ioctl to put a VCPU's local APIC into an invalid state. | ||||
|  * | ||||
|  */ | ||||
| #define _GNU_SOURCE /* for program_invocation_short_name */ | ||||
| #include <fcntl.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/ioctl.h> | ||||
| 
 | ||||
| #include "test_util.h" | ||||
| 
 | ||||
| #include "kvm_util.h" | ||||
| #include "x86.h" | ||||
| 
 | ||||
| #define VCPU_ID                  5 | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	struct kvm_sregs sregs; | ||||
| 	struct kvm_vm *vm; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	/* Tell stdout not to buffer its content */ | ||||
| 	setbuf(stdout, NULL); | ||||
| 
 | ||||
| 	/* Create VM */ | ||||
| 	vm = vm_create_default(VCPU_ID, NULL); | ||||
| 
 | ||||
| 	vcpu_sregs_get(vm, VCPU_ID, &sregs); | ||||
| 	sregs.apic_base = 1 << 10; | ||||
| 	rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); | ||||
| 	TEST_ASSERT(rc, "Set IA32_APIC_BASE to %llx (invalid)", | ||||
| 		    sregs.apic_base); | ||||
| 	sregs.apic_base = 1 << 11; | ||||
| 	rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); | ||||
| 	TEST_ASSERT(!rc, "Couldn't set IA32_APIC_BASE to %llx (valid)", | ||||
| 		    sregs.apic_base); | ||||
| 
 | ||||
| 	kvm_vm_free(vm); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user