diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2018-03-28 03:45:34 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2018-04-04 13:11:00 -0400 |
commit | 6089ae0bd5e15fc150adce5bc694e87e00513825 (patch) | |
tree | 68ef0918d9b8680cd4b9de9109a090eef63a5849 | |
parent | 783e9e51266ebb7f78c606a53cb0fa41bb7c31a0 (diff) |
kvm: selftests: add sync_regs_test
This includes the infrastructure to map the test into the guest and
run code from the test program inside a VM.
Signed-off-by: Ken Hofsass <hofsass@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | tools/testing/selftests/kvm/Makefile | 3 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/include/kvm_util.h | 3 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/lib/elf.c | 197 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/lib/io.c | 158 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/lib/x86.c | 3 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/sync_regs_test.c | 232 |
6 files changed, 595 insertions, 1 deletions
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 6aade26e9ca2..dc44de904797 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile | |||
@@ -3,10 +3,11 @@ all: | |||
3 | top_srcdir = ../../../../ | 3 | top_srcdir = ../../../../ |
4 | UNAME_M := $(shell uname -m) | 4 | UNAME_M := $(shell uname -m) |
5 | 5 | ||
6 | LIBKVM = lib/assert.c lib/kvm_util.c lib/sparsebit.c | 6 | LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c |
7 | LIBKVM_x86_64 = lib/x86.c | 7 | LIBKVM_x86_64 = lib/x86.c |
8 | 8 | ||
9 | TEST_GEN_PROGS_x86_64 = set_sregs_test | 9 | TEST_GEN_PROGS_x86_64 = set_sregs_test |
10 | TEST_GEN_PROGS_x86_64 += sync_regs_test | ||
10 | 11 | ||
11 | TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) | 12 | TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) |
12 | LIBKVM += $(LIBKVM_$(UNAME_M)) | 13 | LIBKVM += $(LIBKVM_$(UNAME_M)) |
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index d2e3e23bfbd3..57974ad46373 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h | |||
@@ -57,6 +57,9 @@ void kvm_vm_free(struct kvm_vm *vmp); | |||
57 | int kvm_memcmp_hva_gva(void *hva, | 57 | int kvm_memcmp_hva_gva(void *hva, |
58 | struct kvm_vm *vm, const vm_vaddr_t gva, size_t len); | 58 | struct kvm_vm *vm, const vm_vaddr_t gva, size_t len); |
59 | 59 | ||
60 | void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename, | ||
61 | uint32_t data_memslot, uint32_t pgd_memslot); | ||
62 | |||
60 | void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); | 63 | void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); |
61 | void vcpu_dump(FILE *stream, struct kvm_vm *vm, | 64 | void vcpu_dump(FILE *stream, struct kvm_vm *vm, |
62 | uint32_t vcpuid, uint8_t indent); | 65 | uint32_t vcpuid, uint8_t indent); |
diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c new file mode 100644 index 000000000000..5eb857584aa3 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/elf.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | * tools/testing/selftests/kvm/lib/elf.c | ||
3 | * | ||
4 | * Copyright (C) 2018, Google LLC. | ||
5 | * | ||
6 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
7 | */ | ||
8 | |||
9 | #include "test_util.h" | ||
10 | |||
11 | #include <bits/endian.h> | ||
12 | #include <linux/elf.h> | ||
13 | |||
14 | #include "kvm_util.h" | ||
15 | #include "kvm_util_internal.h" | ||
16 | |||
17 | static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp) | ||
18 | { | ||
19 | off_t offset_rv; | ||
20 | |||
21 | /* Open the ELF file. */ | ||
22 | int fd; | ||
23 | fd = open(filename, O_RDONLY); | ||
24 | TEST_ASSERT(fd >= 0, "Failed to open ELF file,\n" | ||
25 | " filename: %s\n" | ||
26 | " rv: %i errno: %i", filename, fd, errno); | ||
27 | |||
28 | /* Read in and validate ELF Identification Record. | ||
29 | * The ELF Identification record is the first 16 (EI_NIDENT) bytes | ||
30 | * of the ELF header, which is at the beginning of the ELF file. | ||
31 | * For now it is only safe to read the first EI_NIDENT bytes. Once | ||
32 | * read and validated, the value of e_ehsize can be used to determine | ||
33 | * the real size of the ELF header. | ||
34 | */ | ||
35 | unsigned char ident[EI_NIDENT]; | ||
36 | test_read(fd, ident, sizeof(ident)); | ||
37 | TEST_ASSERT((ident[EI_MAG0] == ELFMAG0) && (ident[EI_MAG1] == ELFMAG1) | ||
38 | && (ident[EI_MAG2] == ELFMAG2) && (ident[EI_MAG3] == ELFMAG3), | ||
39 | "ELF MAGIC Mismatch,\n" | ||
40 | " filename: %s\n" | ||
41 | " ident[EI_MAG0 - EI_MAG3]: %02x %02x %02x %02x\n" | ||
42 | " Expected: %02x %02x %02x %02x", | ||
43 | filename, | ||
44 | ident[EI_MAG0], ident[EI_MAG1], ident[EI_MAG2], ident[EI_MAG3], | ||
45 | ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3); | ||
46 | TEST_ASSERT(ident[EI_CLASS] == ELFCLASS64, | ||
47 | "Current implementation only able to handle ELFCLASS64,\n" | ||
48 | " filename: %s\n" | ||
49 | " ident[EI_CLASS]: %02x\n" | ||
50 | " expected: %02x", | ||
51 | filename, | ||
52 | ident[EI_CLASS], ELFCLASS64); | ||
53 | TEST_ASSERT(((BYTE_ORDER == LITTLE_ENDIAN) | ||
54 | && (ident[EI_DATA] == ELFDATA2LSB)) | ||
55 | || ((BYTE_ORDER == BIG_ENDIAN) | ||
56 | && (ident[EI_DATA] == ELFDATA2MSB)), "Current " | ||
57 | "implementation only able to handle\n" | ||
58 | "cases where the host and ELF file endianness\n" | ||
59 | "is the same:\n" | ||
60 | " host BYTE_ORDER: %u\n" | ||
61 | " host LITTLE_ENDIAN: %u\n" | ||
62 | " host BIG_ENDIAN: %u\n" | ||
63 | " ident[EI_DATA]: %u\n" | ||
64 | " ELFDATA2LSB: %u\n" | ||
65 | " ELFDATA2MSB: %u", | ||
66 | BYTE_ORDER, LITTLE_ENDIAN, BIG_ENDIAN, | ||
67 | ident[EI_DATA], ELFDATA2LSB, ELFDATA2MSB); | ||
68 | TEST_ASSERT(ident[EI_VERSION] == EV_CURRENT, | ||
69 | "Current implementation only able to handle current " | ||
70 | "ELF version,\n" | ||
71 | " filename: %s\n" | ||
72 | " ident[EI_VERSION]: %02x\n" | ||
73 | " expected: %02x", | ||
74 | filename, ident[EI_VERSION], EV_CURRENT); | ||
75 | |||
76 | /* Read in the ELF header. | ||
77 | * With the ELF Identification portion of the ELF header | ||
78 | * validated, especially that the value at EI_VERSION is | ||
79 | * as expected, it is now safe to read the entire ELF header. | ||
80 | */ | ||
81 | offset_rv = lseek(fd, 0, SEEK_SET); | ||
82 | TEST_ASSERT(offset_rv == 0, "Seek to ELF header failed,\n" | ||
83 | " rv: %zi expected: %i", offset_rv, 0); | ||
84 | test_read(fd, hdrp, sizeof(*hdrp)); | ||
85 | TEST_ASSERT(hdrp->e_phentsize == sizeof(Elf64_Phdr), | ||
86 | "Unexpected physical header size,\n" | ||
87 | " hdrp->e_phentsize: %x\n" | ||
88 | " expected: %zx", | ||
89 | hdrp->e_phentsize, sizeof(Elf64_Phdr)); | ||
90 | TEST_ASSERT(hdrp->e_shentsize == sizeof(Elf64_Shdr), | ||
91 | "Unexpected section header size,\n" | ||
92 | " hdrp->e_shentsize: %x\n" | ||
93 | " expected: %zx", | ||
94 | hdrp->e_shentsize, sizeof(Elf64_Shdr)); | ||
95 | } | ||
96 | |||
97 | /* VM ELF Load | ||
98 | * | ||
99 | * Input Args: | ||
100 | * filename - Path to ELF file | ||
101 | * | ||
102 | * Output Args: None | ||
103 | * | ||
104 | * Input/Output Args: | ||
105 | * vm - Pointer to opaque type that describes the VM. | ||
106 | * | ||
107 | * Return: None, TEST_ASSERT failures for all error conditions | ||
108 | * | ||
109 | * Loads the program image of the ELF file specified by filename, | ||
110 | * into the virtual address space of the VM pointed to by vm. On entry | ||
111 | * the VM needs to not be using any of the virtual address space used | ||
112 | * by the image and it needs to have sufficient available physical pages, to | ||
113 | * back the virtual pages used to load the image. | ||
114 | */ | ||
115 | void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename, | ||
116 | uint32_t data_memslot, uint32_t pgd_memslot) | ||
117 | { | ||
118 | off_t offset, offset_rv; | ||
119 | Elf64_Ehdr hdr; | ||
120 | |||
121 | /* Open the ELF file. */ | ||
122 | int fd; | ||
123 | fd = open(filename, O_RDONLY); | ||
124 | TEST_ASSERT(fd >= 0, "Failed to open ELF file,\n" | ||
125 | " filename: %s\n" | ||
126 | " rv: %i errno: %i", filename, fd, errno); | ||
127 | |||
128 | /* Read in the ELF header. */ | ||
129 | elfhdr_get(filename, &hdr); | ||
130 | |||
131 | /* For each program header. | ||
132 | * The following ELF header members specify the location | ||
133 | * and size of the program headers: | ||
134 | * | ||
135 | * e_phoff - File offset to start of program headers | ||
136 | * e_phentsize - Size of each program header | ||
137 | * e_phnum - Number of program header entries | ||
138 | */ | ||
139 | for (unsigned int n1 = 0; n1 < hdr.e_phnum; n1++) { | ||
140 | /* Seek to the beginning of the program header. */ | ||
141 | offset = hdr.e_phoff + (n1 * hdr.e_phentsize); | ||
142 | offset_rv = lseek(fd, offset, SEEK_SET); | ||
143 | TEST_ASSERT(offset_rv == offset, | ||
144 | "Failed to seek to begining of program header %u,\n" | ||
145 | " filename: %s\n" | ||
146 | " rv: %jd errno: %i", | ||
147 | n1, filename, (intmax_t) offset_rv, errno); | ||
148 | |||
149 | /* Read in the program header. */ | ||
150 | Elf64_Phdr phdr; | ||
151 | test_read(fd, &phdr, sizeof(phdr)); | ||
152 | |||
153 | /* Skip if this header doesn't describe a loadable segment. */ | ||
154 | if (phdr.p_type != PT_LOAD) | ||
155 | continue; | ||
156 | |||
157 | /* Allocate memory for this segment within the VM. */ | ||
158 | TEST_ASSERT(phdr.p_memsz > 0, "Unexpected loadable segment " | ||
159 | "memsize of 0,\n" | ||
160 | " phdr index: %u p_memsz: 0x%" PRIx64, | ||
161 | n1, (uint64_t) phdr.p_memsz); | ||
162 | vm_vaddr_t seg_vstart = phdr.p_vaddr; | ||
163 | seg_vstart &= ~(vm_vaddr_t)(vm->page_size - 1); | ||
164 | vm_vaddr_t seg_vend = phdr.p_vaddr + phdr.p_memsz - 1; | ||
165 | seg_vend |= vm->page_size - 1; | ||
166 | size_t seg_size = seg_vend - seg_vstart + 1; | ||
167 | |||
168 | vm_vaddr_t vaddr = vm_vaddr_alloc(vm, seg_size, seg_vstart, | ||
169 | data_memslot, pgd_memslot); | ||
170 | TEST_ASSERT(vaddr == seg_vstart, "Unable to allocate " | ||
171 | "virtual memory for segment at requested min addr,\n" | ||
172 | " segment idx: %u\n" | ||
173 | " seg_vstart: 0x%lx\n" | ||
174 | " vaddr: 0x%lx", | ||
175 | n1, seg_vstart, vaddr); | ||
176 | memset(addr_gva2hva(vm, vaddr), 0, seg_size); | ||
177 | /* TODO(lhuemill): Set permissions of each memory segment | ||
178 | * based on the least-significant 3 bits of phdr.p_flags. | ||
179 | */ | ||
180 | |||
181 | /* Load portion of initial state that is contained within | ||
182 | * the ELF file. | ||
183 | */ | ||
184 | if (phdr.p_filesz) { | ||
185 | offset_rv = lseek(fd, phdr.p_offset, SEEK_SET); | ||
186 | TEST_ASSERT(offset_rv == phdr.p_offset, | ||
187 | "Seek to program segment offset failed,\n" | ||
188 | " program header idx: %u errno: %i\n" | ||
189 | " offset_rv: 0x%jx\n" | ||
190 | " expected: 0x%jx\n", | ||
191 | n1, errno, (intmax_t) offset_rv, | ||
192 | (intmax_t) phdr.p_offset); | ||
193 | test_read(fd, addr_gva2hva(vm, phdr.p_vaddr), | ||
194 | phdr.p_filesz); | ||
195 | } | ||
196 | } | ||
197 | } | ||
diff --git a/tools/testing/selftests/kvm/lib/io.c b/tools/testing/selftests/kvm/lib/io.c new file mode 100644 index 000000000000..cff869ffe6ee --- /dev/null +++ b/tools/testing/selftests/kvm/lib/io.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * tools/testing/selftests/kvm/lib/io.c | ||
3 | * | ||
4 | * Copyright (C) 2018, Google LLC. | ||
5 | * | ||
6 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
7 | */ | ||
8 | |||
9 | #include "test_util.h" | ||
10 | |||
11 | /* Test Write | ||
12 | * | ||
13 | * A wrapper for write(2), that automatically handles the following | ||
14 | * special conditions: | ||
15 | * | ||
16 | * + Interrupted system call (EINTR) | ||
17 | * + Write of less than requested amount | ||
18 | * + Non-block return (EAGAIN) | ||
19 | * | ||
20 | * For each of the above, an additional write is performed to automatically | ||
21 | * continue writing the requested data. | ||
22 | * There are also many cases where write(2) can return an unexpected | ||
23 | * error (e.g. EIO). Such errors cause a TEST_ASSERT failure. | ||
24 | * | ||
25 | * Note, for function signature compatibility with write(2), this function | ||
26 | * returns the number of bytes written, but that value will always be equal | ||
27 | * to the number of requested bytes. All other conditions in this and | ||
28 | * future enhancements to this function either automatically issue another | ||
29 | * write(2) or cause a TEST_ASSERT failure. | ||
30 | * | ||
31 | * Args: | ||
32 | * fd - Opened file descriptor to file to be written. | ||
33 | * count - Number of bytes to write. | ||
34 | * | ||
35 | * Output: | ||
36 | * buf - Starting address of data to be written. | ||
37 | * | ||
38 | * Return: | ||
39 | * On success, number of bytes written. | ||
40 | * On failure, a TEST_ASSERT failure is caused. | ||
41 | */ | ||
42 | ssize_t test_write(int fd, const void *buf, size_t count) | ||
43 | { | ||
44 | ssize_t rc; | ||
45 | ssize_t num_written = 0; | ||
46 | size_t num_left = count; | ||
47 | const char *ptr = buf; | ||
48 | |||
49 | /* Note: Count of zero is allowed (see "RETURN VALUE" portion of | ||
50 | * write(2) manpage for details. | ||
51 | */ | ||
52 | TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count); | ||
53 | |||
54 | do { | ||
55 | rc = write(fd, ptr, num_left); | ||
56 | |||
57 | switch (rc) { | ||
58 | case -1: | ||
59 | TEST_ASSERT(errno == EAGAIN || errno == EINTR, | ||
60 | "Unexpected write failure,\n" | ||
61 | " rc: %zi errno: %i", rc, errno); | ||
62 | continue; | ||
63 | |||
64 | case 0: | ||
65 | TEST_ASSERT(false, "Unexpected EOF,\n" | ||
66 | " rc: %zi num_written: %zi num_left: %zu", | ||
67 | rc, num_written, num_left); | ||
68 | break; | ||
69 | |||
70 | default: | ||
71 | TEST_ASSERT(rc >= 0, "Unexpected ret from write,\n" | ||
72 | " rc: %zi errno: %i", rc, errno); | ||
73 | num_written += rc; | ||
74 | num_left -= rc; | ||
75 | ptr += rc; | ||
76 | break; | ||
77 | } | ||
78 | } while (num_written < count); | ||
79 | |||
80 | return num_written; | ||
81 | } | ||
82 | |||
83 | /* Test Read | ||
84 | * | ||
85 | * A wrapper for read(2), that automatically handles the following | ||
86 | * special conditions: | ||
87 | * | ||
88 | * + Interrupted system call (EINTR) | ||
89 | * + Read of less than requested amount | ||
90 | * + Non-block return (EAGAIN) | ||
91 | * | ||
92 | * For each of the above, an additional read is performed to automatically | ||
93 | * continue reading the requested data. | ||
94 | * There are also many cases where read(2) can return an unexpected | ||
95 | * error (e.g. EIO). Such errors cause a TEST_ASSERT failure. Note, | ||
96 | * it is expected that the file opened by fd at the current file position | ||
97 | * contains at least the number of requested bytes to be read. A TEST_ASSERT | ||
98 | * failure is produced if an End-Of-File condition occurs, before all the | ||
99 | * data is read. It is the callers responsibility to assure that sufficient | ||
100 | * data exists. | ||
101 | * | ||
102 | * Note, for function signature compatibility with read(2), this function | ||
103 | * returns the number of bytes read, but that value will always be equal | ||
104 | * to the number of requested bytes. All other conditions in this and | ||
105 | * future enhancements to this function either automatically issue another | ||
106 | * read(2) or cause a TEST_ASSERT failure. | ||
107 | * | ||
108 | * Args: | ||
109 | * fd - Opened file descriptor to file to be read. | ||
110 | * count - Number of bytes to read. | ||
111 | * | ||
112 | * Output: | ||
113 | * buf - Starting address of where to write the bytes read. | ||
114 | * | ||
115 | * Return: | ||
116 | * On success, number of bytes read. | ||
117 | * On failure, a TEST_ASSERT failure is caused. | ||
118 | */ | ||
119 | ssize_t test_read(int fd, void *buf, size_t count) | ||
120 | { | ||
121 | ssize_t rc; | ||
122 | ssize_t num_read = 0; | ||
123 | size_t num_left = count; | ||
124 | char *ptr = buf; | ||
125 | |||
126 | /* Note: Count of zero is allowed (see "If count is zero" portion of | ||
127 | * read(2) manpage for details. | ||
128 | */ | ||
129 | TEST_ASSERT(count >= 0, "Unexpected count, count: %li", count); | ||
130 | |||
131 | do { | ||
132 | rc = read(fd, ptr, num_left); | ||
133 | |||
134 | switch (rc) { | ||
135 | case -1: | ||
136 | TEST_ASSERT(errno == EAGAIN || errno == EINTR, | ||
137 | "Unexpected read failure,\n" | ||
138 | " rc: %zi errno: %i", rc, errno); | ||
139 | break; | ||
140 | |||
141 | case 0: | ||
142 | TEST_ASSERT(false, "Unexpected EOF,\n" | ||
143 | " rc: %zi num_read: %zi num_left: %zu", | ||
144 | rc, num_read, num_left); | ||
145 | break; | ||
146 | |||
147 | default: | ||
148 | TEST_ASSERT(rc > 0, "Unexpected ret from read,\n" | ||
149 | " rc: %zi errno: %i", rc, errno); | ||
150 | num_read += rc; | ||
151 | num_left -= rc; | ||
152 | ptr += rc; | ||
153 | break; | ||
154 | } | ||
155 | } while (num_read < count); | ||
156 | |||
157 | return num_read; | ||
158 | } | ||
diff --git a/tools/testing/selftests/kvm/lib/x86.c b/tools/testing/selftests/kvm/lib/x86.c index 12df46280b23..2f17675f4275 100644 --- a/tools/testing/selftests/kvm/lib/x86.c +++ b/tools/testing/selftests/kvm/lib/x86.c | |||
@@ -687,6 +687,9 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, void *guest_code) | |||
687 | /* Create VM */ | 687 | /* Create VM */ |
688 | vm = vm_create(VM_MODE_FLAT48PG, DEFAULT_GUEST_PHY_PAGES, O_RDWR); | 688 | vm = vm_create(VM_MODE_FLAT48PG, DEFAULT_GUEST_PHY_PAGES, O_RDWR); |
689 | 689 | ||
690 | /* Setup guest code */ | ||
691 | kvm_vm_elf_load(vm, program_invocation_name, 0, 0); | ||
692 | |||
690 | /* Setup IRQ Chip */ | 693 | /* Setup IRQ Chip */ |
691 | vm_create_irqchip(vm); | 694 | vm_create_irqchip(vm); |
692 | 695 | ||
diff --git a/tools/testing/selftests/kvm/sync_regs_test.c b/tools/testing/selftests/kvm/sync_regs_test.c new file mode 100644 index 000000000000..428e9473f5e2 --- /dev/null +++ b/tools/testing/selftests/kvm/sync_regs_test.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * Test for x86 KVM_CAP_SYNC_REGS | ||
3 | * | ||
4 | * Copyright (C) 2018, Google LLC. | ||
5 | * | ||
6 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
7 | * | ||
8 | * Verifies expected behavior of x86 KVM_CAP_SYNC_REGS functionality, | ||
9 | * including requesting an invalid register set, updates to/from values | ||
10 | * in kvm_run.s.regs when kvm_valid_regs and kvm_dirty_regs are toggled. | ||
11 | */ | ||
12 | |||
13 | #define _GNU_SOURCE /* for program_invocation_short_name */ | ||
14 | #include <fcntl.h> | ||
15 | #include <stdio.h> | ||
16 | #include <stdlib.h> | ||
17 | #include <string.h> | ||
18 | #include <sys/ioctl.h> | ||
19 | |||
20 | #include "test_util.h" | ||
21 | #include "kvm_util.h" | ||
22 | #include "x86.h" | ||
23 | |||
24 | #define VCPU_ID 5 | ||
25 | #define PORT_HOST_SYNC 0x1000 | ||
26 | |||
27 | static void __exit_to_l0(uint16_t port, uint64_t arg0, uint64_t arg1) | ||
28 | { | ||
29 | __asm__ __volatile__("in %[port], %%al" | ||
30 | : | ||
31 | : [port]"d"(port), "D"(arg0), "S"(arg1) | ||
32 | : "rax"); | ||
33 | } | ||
34 | |||
35 | #define exit_to_l0(_port, _arg0, _arg1) \ | ||
36 | __exit_to_l0(_port, (uint64_t) (_arg0), (uint64_t) (_arg1)) | ||
37 | |||
38 | #define GUEST_ASSERT(_condition) do { \ | ||
39 | if (!(_condition)) \ | ||
40 | exit_to_l0(PORT_ABORT, "Failed guest assert: " #_condition, 0);\ | ||
41 | } while (0) | ||
42 | |||
43 | void guest_code(void) | ||
44 | { | ||
45 | for (;;) { | ||
46 | exit_to_l0(PORT_HOST_SYNC, "hello", 0); | ||
47 | asm volatile ("inc %r11"); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | static void compare_regs(struct kvm_regs *left, struct kvm_regs *right) | ||
52 | { | ||
53 | #define REG_COMPARE(reg) \ | ||
54 | TEST_ASSERT(left->reg == right->reg, \ | ||
55 | "Register " #reg \ | ||
56 | " values did not match: 0x%llx, 0x%llx\n", \ | ||
57 | left->reg, right->reg) | ||
58 | REG_COMPARE(rax); | ||
59 | REG_COMPARE(rbx); | ||
60 | REG_COMPARE(rcx); | ||
61 | REG_COMPARE(rdx); | ||
62 | REG_COMPARE(rsi); | ||
63 | REG_COMPARE(rdi); | ||
64 | REG_COMPARE(rsp); | ||
65 | REG_COMPARE(rbp); | ||
66 | REG_COMPARE(r8); | ||
67 | REG_COMPARE(r9); | ||
68 | REG_COMPARE(r10); | ||
69 | REG_COMPARE(r11); | ||
70 | REG_COMPARE(r12); | ||
71 | REG_COMPARE(r13); | ||
72 | REG_COMPARE(r14); | ||
73 | REG_COMPARE(r15); | ||
74 | REG_COMPARE(rip); | ||
75 | REG_COMPARE(rflags); | ||
76 | #undef REG_COMPARE | ||
77 | } | ||
78 | |||
79 | static void compare_sregs(struct kvm_sregs *left, struct kvm_sregs *right) | ||
80 | { | ||
81 | } | ||
82 | |||
83 | static void compare_vcpu_events(struct kvm_vcpu_events *left, | ||
84 | struct kvm_vcpu_events *right) | ||
85 | { | ||
86 | } | ||
87 | |||
88 | int main(int argc, char *argv[]) | ||
89 | { | ||
90 | struct kvm_vm *vm; | ||
91 | struct kvm_run *run; | ||
92 | struct kvm_regs regs; | ||
93 | struct kvm_sregs sregs; | ||
94 | struct kvm_vcpu_events events; | ||
95 | int rv, cap; | ||
96 | |||
97 | /* Tell stdout not to buffer its content */ | ||
98 | setbuf(stdout, NULL); | ||
99 | |||
100 | cap = kvm_check_cap(KVM_CAP_SYNC_REGS); | ||
101 | TEST_ASSERT((unsigned long)cap == KVM_SYNC_X86_VALID_FIELDS, | ||
102 | "KVM_CAP_SYNC_REGS (0x%x) != KVM_SYNC_X86_VALID_FIELDS (0x%lx)\n", | ||
103 | cap, KVM_SYNC_X86_VALID_FIELDS); | ||
104 | |||
105 | /* Create VM */ | ||
106 | vm = vm_create_default(VCPU_ID, guest_code); | ||
107 | |||
108 | run = vcpu_state(vm, VCPU_ID); | ||
109 | |||
110 | /* Request reading invalid register set from VCPU. */ | ||
111 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS << 1; | ||
112 | rv = _vcpu_run(vm, VCPU_ID); | ||
113 | TEST_ASSERT(rv < 0 && errno == EINVAL, | ||
114 | "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n", | ||
115 | rv); | ||
116 | vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0; | ||
117 | |||
118 | /* Request setting invalid register set into VCPU. */ | ||
119 | run->kvm_dirty_regs = KVM_SYNC_X86_VALID_FIELDS << 1; | ||
120 | rv = _vcpu_run(vm, VCPU_ID); | ||
121 | TEST_ASSERT(rv < 0 && errno == EINVAL, | ||
122 | "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n", | ||
123 | rv); | ||
124 | vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0; | ||
125 | |||
126 | /* Request and verify all valid register sets. */ | ||
127 | /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */ | ||
128 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS; | ||
129 | rv = _vcpu_run(vm, VCPU_ID); | ||
130 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, | ||
131 | "Unexpected exit reason: %u (%s),\n", | ||
132 | run->exit_reason, | ||
133 | exit_reason_str(run->exit_reason)); | ||
134 | |||
135 | vcpu_regs_get(vm, VCPU_ID, ®s); | ||
136 | compare_regs(®s, &run->s.regs.regs); | ||
137 | |||
138 | vcpu_sregs_get(vm, VCPU_ID, &sregs); | ||
139 | compare_sregs(&sregs, &run->s.regs.sregs); | ||
140 | |||
141 | vcpu_events_get(vm, VCPU_ID, &events); | ||
142 | compare_vcpu_events(&events, &run->s.regs.events); | ||
143 | |||
144 | /* Set and verify various register values. */ | ||
145 | run->s.regs.regs.r11 = 0xBAD1DEA; | ||
146 | run->s.regs.sregs.apic_base = 1 << 11; | ||
147 | /* TODO run->s.regs.events.XYZ = ABC; */ | ||
148 | |||
149 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS; | ||
150 | run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS; | ||
151 | rv = _vcpu_run(vm, VCPU_ID); | ||
152 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, | ||
153 | "Unexpected exit reason: %u (%s),\n", | ||
154 | run->exit_reason, | ||
155 | exit_reason_str(run->exit_reason)); | ||
156 | TEST_ASSERT(run->s.regs.regs.r11 == 0xBAD1DEA + 1, | ||
157 | "r11 sync regs value incorrect 0x%llx.", | ||
158 | run->s.regs.regs.r11); | ||
159 | TEST_ASSERT(run->s.regs.sregs.apic_base == 1 << 11, | ||
160 | "apic_base sync regs value incorrect 0x%llx.", | ||
161 | run->s.regs.sregs.apic_base); | ||
162 | |||
163 | vcpu_regs_get(vm, VCPU_ID, ®s); | ||
164 | compare_regs(®s, &run->s.regs.regs); | ||
165 | |||
166 | vcpu_sregs_get(vm, VCPU_ID, &sregs); | ||
167 | compare_sregs(&sregs, &run->s.regs.sregs); | ||
168 | |||
169 | vcpu_events_get(vm, VCPU_ID, &events); | ||
170 | compare_vcpu_events(&events, &run->s.regs.events); | ||
171 | |||
172 | /* Clear kvm_dirty_regs bits, verify new s.regs values are | ||
173 | * overwritten with existing guest values. | ||
174 | */ | ||
175 | run->kvm_valid_regs = KVM_SYNC_X86_VALID_FIELDS; | ||
176 | run->kvm_dirty_regs = 0; | ||
177 | run->s.regs.regs.r11 = 0xDEADBEEF; | ||
178 | rv = _vcpu_run(vm, VCPU_ID); | ||
179 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, | ||
180 | "Unexpected exit reason: %u (%s),\n", | ||
181 | run->exit_reason, | ||
182 | exit_reason_str(run->exit_reason)); | ||
183 | TEST_ASSERT(run->s.regs.regs.r11 != 0xDEADBEEF, | ||
184 | "r11 sync regs value incorrect 0x%llx.", | ||
185 | run->s.regs.regs.r11); | ||
186 | |||
187 | /* Clear kvm_valid_regs bits and kvm_dirty_bits. | ||
188 | * Verify s.regs values are not overwritten with existing guest values | ||
189 | * and that guest values are not overwritten with kvm_sync_regs values. | ||
190 | */ | ||
191 | run->kvm_valid_regs = 0; | ||
192 | run->kvm_dirty_regs = 0; | ||
193 | run->s.regs.regs.r11 = 0xAAAA; | ||
194 | regs.r11 = 0xBAC0; | ||
195 | vcpu_regs_set(vm, VCPU_ID, ®s); | ||
196 | rv = _vcpu_run(vm, VCPU_ID); | ||
197 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, | ||
198 | "Unexpected exit reason: %u (%s),\n", | ||
199 | run->exit_reason, | ||
200 | exit_reason_str(run->exit_reason)); | ||
201 | TEST_ASSERT(run->s.regs.regs.r11 == 0xAAAA, | ||
202 | "r11 sync regs value incorrect 0x%llx.", | ||
203 | run->s.regs.regs.r11); | ||
204 | vcpu_regs_get(vm, VCPU_ID, ®s); | ||
205 | TEST_ASSERT(regs.r11 == 0xBAC0 + 1, | ||
206 | "r11 guest value incorrect 0x%llx.", | ||
207 | regs.r11); | ||
208 | |||
209 | /* Clear kvm_valid_regs bits. Verify s.regs values are not overwritten | ||
210 | * with existing guest values but that guest values are overwritten | ||
211 | * with kvm_sync_regs values. | ||
212 | */ | ||
213 | run->kvm_valid_regs = 0; | ||
214 | run->kvm_dirty_regs = KVM_SYNC_X86_VALID_FIELDS; | ||
215 | run->s.regs.regs.r11 = 0xBBBB; | ||
216 | rv = _vcpu_run(vm, VCPU_ID); | ||
217 | TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, | ||
218 | "Unexpected exit reason: %u (%s),\n", | ||
219 | run->exit_reason, | ||
220 | exit_reason_str(run->exit_reason)); | ||
221 | TEST_ASSERT(run->s.regs.regs.r11 == 0xBBBB, | ||
222 | "r11 sync regs value incorrect 0x%llx.", | ||
223 | run->s.regs.regs.r11); | ||
224 | vcpu_regs_get(vm, VCPU_ID, ®s); | ||
225 | TEST_ASSERT(regs.r11 == 0xBBBB + 1, | ||
226 | "r11 guest value incorrect 0x%llx.", | ||
227 | regs.r11); | ||
228 | |||
229 | kvm_vm_free(vm); | ||
230 | |||
231 | return 0; | ||
232 | } | ||