diff options
author | Aaron Lewis <aaronlewis@google.com> | 2019-05-02 14:31:41 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2019-05-08 08:12:09 -0400 |
commit | da1e3071d53d79b00b07b34a5853c8e42f35d745 (patch) | |
tree | fd7b638659579b86d2870afb0564872170285d5c | |
parent | 332d079735f5add26f4443cec2991ee03ed2ae19 (diff) |
tests: kvm: Add tests for KVM_SET_NESTED_STATE
Add tests for KVM_SET_NESTED_STATE and for various code paths in its implementation in vmx_set_nested_state().
Signed-off-by: Aaron Lewis <aaronlewis@google.com>
Reviewed-by: Marc Orr <marcorr@google.com>
Reviewed-by: Peter Shier <pshier@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | tools/testing/selftests/kvm/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/Makefile | 1 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/include/kvm_util.h | 4 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/lib/kvm_util.c | 32 | ||||
-rw-r--r-- | tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c | 280 |
5 files changed, 318 insertions, 0 deletions
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 2a9209d18684..df1bf9230a74 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore | |||
@@ -8,6 +8,7 @@ | |||
8 | /x86_64/state_test | 8 | /x86_64/state_test |
9 | /x86_64/sync_regs_test | 9 | /x86_64/sync_regs_test |
10 | /x86_64/vmx_close_while_nested_test | 10 | /x86_64/vmx_close_while_nested_test |
11 | /x86_64/vmx_set_nested_state_test | ||
11 | /x86_64/vmx_tsc_adjust_test | 12 | /x86_64/vmx_tsc_adjust_test |
12 | /clear_dirty_log_test | 13 | /clear_dirty_log_test |
13 | /dirty_log_test | 14 | /dirty_log_test |
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 6b7b3617d25c..79c524395ebe 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile | |||
@@ -21,6 +21,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid | |||
21 | TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test | 21 | TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test |
22 | TEST_GEN_PROGS_x86_64 += x86_64/smm_test | 22 | TEST_GEN_PROGS_x86_64 += x86_64/smm_test |
23 | TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus | 23 | TEST_GEN_PROGS_x86_64 += x86_64/kvm_create_max_vcpus |
24 | TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test | ||
24 | TEST_GEN_PROGS_x86_64 += dirty_log_test | 25 | TEST_GEN_PROGS_x86_64 += dirty_log_test |
25 | TEST_GEN_PROGS_x86_64 += clear_dirty_log_test | 26 | TEST_GEN_PROGS_x86_64 += clear_dirty_log_test |
26 | 27 | ||
diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h index 07b71ad9734a..8c6b9619797d 100644 --- a/tools/testing/selftests/kvm/include/kvm_util.h +++ b/tools/testing/selftests/kvm/include/kvm_util.h | |||
@@ -118,6 +118,10 @@ void vcpu_events_get(struct kvm_vm *vm, uint32_t vcpuid, | |||
118 | struct kvm_vcpu_events *events); | 118 | struct kvm_vcpu_events *events); |
119 | void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid, | 119 | void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid, |
120 | struct kvm_vcpu_events *events); | 120 | struct kvm_vcpu_events *events); |
121 | void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid, | ||
122 | struct kvm_nested_state *state); | ||
123 | int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid, | ||
124 | struct kvm_nested_state *state, bool ignore_error); | ||
121 | 125 | ||
122 | const char *exit_reason_str(unsigned int exit_reason); | 126 | const char *exit_reason_str(unsigned int exit_reason); |
123 | 127 | ||
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 4ca96b228e46..e9113857f44e 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c | |||
@@ -1250,6 +1250,38 @@ void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid, | |||
1250 | ret, errno); | 1250 | ret, errno); |
1251 | } | 1251 | } |
1252 | 1252 | ||
1253 | void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid, | ||
1254 | struct kvm_nested_state *state) | ||
1255 | { | ||
1256 | struct vcpu *vcpu = vcpu_find(vm, vcpuid); | ||
1257 | int ret; | ||
1258 | |||
1259 | TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); | ||
1260 | |||
1261 | ret = ioctl(vcpu->fd, KVM_GET_NESTED_STATE, state); | ||
1262 | TEST_ASSERT(ret == 0, | ||
1263 | "KVM_SET_NESTED_STATE failed, ret: %i errno: %i", | ||
1264 | ret, errno); | ||
1265 | } | ||
1266 | |||
1267 | int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid, | ||
1268 | struct kvm_nested_state *state, bool ignore_error) | ||
1269 | { | ||
1270 | struct vcpu *vcpu = vcpu_find(vm, vcpuid); | ||
1271 | int ret; | ||
1272 | |||
1273 | TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); | ||
1274 | |||
1275 | ret = ioctl(vcpu->fd, KVM_SET_NESTED_STATE, state); | ||
1276 | if (!ignore_error) { | ||
1277 | TEST_ASSERT(ret == 0, | ||
1278 | "KVM_SET_NESTED_STATE failed, ret: %i errno: %i", | ||
1279 | ret, errno); | ||
1280 | } | ||
1281 | |||
1282 | return ret; | ||
1283 | } | ||
1284 | |||
1253 | /* | 1285 | /* |
1254 | * VM VCPU System Regs Get | 1286 | * VM VCPU System Regs Get |
1255 | * | 1287 | * |
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c b/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c new file mode 100644 index 000000000000..61a2163cf9f1 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c | |||
@@ -0,0 +1,280 @@ | |||
1 | /* | ||
2 | * vmx_set_nested_state_test | ||
3 | * | ||
4 | * Copyright (C) 2019, Google LLC. | ||
5 | * | ||
6 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
7 | * | ||
8 | * This test verifies the integrity of calling the ioctl KVM_SET_NESTED_STATE. | ||
9 | */ | ||
10 | |||
11 | #include "test_util.h" | ||
12 | #include "kvm_util.h" | ||
13 | #include "processor.h" | ||
14 | #include "vmx.h" | ||
15 | |||
16 | #include <errno.h> | ||
17 | #include <linux/kvm.h> | ||
18 | #include <string.h> | ||
19 | #include <sys/ioctl.h> | ||
20 | #include <unistd.h> | ||
21 | |||
22 | /* | ||
23 | * Mirror of VMCS12_REVISION in arch/x86/kvm/vmx/vmcs12.h. If that value | ||
24 | * changes this should be updated. | ||
25 | */ | ||
26 | #define VMCS12_REVISION 0x11e57ed0 | ||
27 | #define VCPU_ID 5 | ||
28 | |||
29 | void test_nested_state(struct kvm_vm *vm, struct kvm_nested_state *state) | ||
30 | { | ||
31 | volatile struct kvm_run *run; | ||
32 | |||
33 | vcpu_nested_state_set(vm, VCPU_ID, state, false); | ||
34 | run = vcpu_state(vm, VCPU_ID); | ||
35 | vcpu_run(vm, VCPU_ID); | ||
36 | TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN, | ||
37 | "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s),\n", | ||
38 | run->exit_reason, | ||
39 | exit_reason_str(run->exit_reason)); | ||
40 | } | ||
41 | |||
42 | void test_nested_state_expect_errno(struct kvm_vm *vm, | ||
43 | struct kvm_nested_state *state, | ||
44 | int expected_errno) | ||
45 | { | ||
46 | volatile struct kvm_run *run; | ||
47 | int rv; | ||
48 | |||
49 | rv = vcpu_nested_state_set(vm, VCPU_ID, state, true); | ||
50 | TEST_ASSERT(rv == -1 && errno == expected_errno, | ||
51 | "Expected %s (%d) from vcpu_nested_state_set but got rv: %i errno: %s (%d)", | ||
52 | strerror(expected_errno), expected_errno, rv, strerror(errno), | ||
53 | errno); | ||
54 | run = vcpu_state(vm, VCPU_ID); | ||
55 | vcpu_run(vm, VCPU_ID); | ||
56 | TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN, | ||
57 | "Got exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s),\n", | ||
58 | run->exit_reason, | ||
59 | exit_reason_str(run->exit_reason)); | ||
60 | } | ||
61 | |||
62 | void test_nested_state_expect_einval(struct kvm_vm *vm, | ||
63 | struct kvm_nested_state *state) | ||
64 | { | ||
65 | test_nested_state_expect_errno(vm, state, EINVAL); | ||
66 | } | ||
67 | |||
68 | void test_nested_state_expect_efault(struct kvm_vm *vm, | ||
69 | struct kvm_nested_state *state) | ||
70 | { | ||
71 | test_nested_state_expect_errno(vm, state, EFAULT); | ||
72 | } | ||
73 | |||
74 | void set_revision_id_for_vmcs12(struct kvm_nested_state *state, | ||
75 | u32 vmcs12_revision) | ||
76 | { | ||
77 | /* Set revision_id in vmcs12 to vmcs12_revision. */ | ||
78 | *(u32 *)(state->data) = vmcs12_revision; | ||
79 | } | ||
80 | |||
81 | void set_default_state(struct kvm_nested_state *state) | ||
82 | { | ||
83 | memset(state, 0, sizeof(*state)); | ||
84 | state->flags = KVM_STATE_NESTED_RUN_PENDING | | ||
85 | KVM_STATE_NESTED_GUEST_MODE; | ||
86 | state->format = 0; | ||
87 | state->size = sizeof(*state); | ||
88 | } | ||
89 | |||
90 | void set_default_vmx_state(struct kvm_nested_state *state, int size) | ||
91 | { | ||
92 | memset(state, 0, size); | ||
93 | state->flags = KVM_STATE_NESTED_GUEST_MODE | | ||
94 | KVM_STATE_NESTED_RUN_PENDING | | ||
95 | KVM_STATE_NESTED_EVMCS; | ||
96 | state->format = 0; | ||
97 | state->size = size; | ||
98 | state->vmx.vmxon_pa = 0x1000; | ||
99 | state->vmx.vmcs_pa = 0x2000; | ||
100 | state->vmx.smm.flags = 0; | ||
101 | set_revision_id_for_vmcs12(state, VMCS12_REVISION); | ||
102 | } | ||
103 | |||
104 | void test_vmx_nested_state(struct kvm_vm *vm) | ||
105 | { | ||
106 | /* Add a page for VMCS12. */ | ||
107 | const int state_sz = sizeof(struct kvm_nested_state) + getpagesize(); | ||
108 | struct kvm_nested_state *state = | ||
109 | (struct kvm_nested_state *)malloc(state_sz); | ||
110 | |||
111 | /* The format must be set to 0. 0 for VMX, 1 for SVM. */ | ||
112 | set_default_vmx_state(state, state_sz); | ||
113 | state->format = 1; | ||
114 | test_nested_state_expect_einval(vm, state); | ||
115 | |||
116 | /* | ||
117 | * We cannot virtualize anything if the guest does not have VMX | ||
118 | * enabled. | ||
119 | */ | ||
120 | set_default_vmx_state(state, state_sz); | ||
121 | test_nested_state_expect_einval(vm, state); | ||
122 | |||
123 | /* | ||
124 | * We cannot virtualize anything if the guest does not have VMX | ||
125 | * enabled. We expect KVM_SET_NESTED_STATE to return 0 if vmxon_pa | ||
126 | * is set to -1ull. | ||
127 | */ | ||
128 | set_default_vmx_state(state, state_sz); | ||
129 | state->vmx.vmxon_pa = -1ull; | ||
130 | test_nested_state(vm, state); | ||
131 | |||
132 | /* Enable VMX in the guest CPUID. */ | ||
133 | vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); | ||
134 | |||
135 | /* It is invalid to have vmxon_pa == -1ull and SMM flags non-zero. */ | ||
136 | set_default_vmx_state(state, state_sz); | ||
137 | state->vmx.vmxon_pa = -1ull; | ||
138 | state->vmx.smm.flags = 1; | ||
139 | test_nested_state_expect_einval(vm, state); | ||
140 | |||
141 | /* It is invalid to have vmxon_pa == -1ull and vmcs_pa != -1ull. */ | ||
142 | set_default_vmx_state(state, state_sz); | ||
143 | state->vmx.vmxon_pa = -1ull; | ||
144 | state->vmx.vmcs_pa = 0; | ||
145 | test_nested_state_expect_einval(vm, state); | ||
146 | |||
147 | /* | ||
148 | * Setting vmxon_pa == -1ull and vmcs_pa == -1ull exits early without | ||
149 | * setting the nested state. | ||
150 | */ | ||
151 | set_default_vmx_state(state, state_sz); | ||
152 | state->vmx.vmxon_pa = -1ull; | ||
153 | state->vmx.vmcs_pa = -1ull; | ||
154 | test_nested_state(vm, state); | ||
155 | |||
156 | /* It is invalid to have vmxon_pa set to a non-page aligned address. */ | ||
157 | set_default_vmx_state(state, state_sz); | ||
158 | state->vmx.vmxon_pa = 1; | ||
159 | test_nested_state_expect_einval(vm, state); | ||
160 | |||
161 | /* | ||
162 | * It is invalid to have KVM_STATE_NESTED_SMM_GUEST_MODE and | ||
163 | * KVM_STATE_NESTED_GUEST_MODE set together. | ||
164 | */ | ||
165 | set_default_vmx_state(state, state_sz); | ||
166 | state->flags = KVM_STATE_NESTED_GUEST_MODE | | ||
167 | KVM_STATE_NESTED_RUN_PENDING; | ||
168 | state->vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE; | ||
169 | test_nested_state_expect_einval(vm, state); | ||
170 | |||
171 | /* | ||
172 | * It is invalid to have any of the SMM flags set besides: | ||
173 | * KVM_STATE_NESTED_SMM_GUEST_MODE | ||
174 | * KVM_STATE_NESTED_SMM_VMXON | ||
175 | */ | ||
176 | set_default_vmx_state(state, state_sz); | ||
177 | state->vmx.smm.flags = ~(KVM_STATE_NESTED_SMM_GUEST_MODE | | ||
178 | KVM_STATE_NESTED_SMM_VMXON); | ||
179 | test_nested_state_expect_einval(vm, state); | ||
180 | |||
181 | /* Outside SMM, SMM flags must be zero. */ | ||
182 | set_default_vmx_state(state, state_sz); | ||
183 | state->flags = 0; | ||
184 | state->vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE; | ||
185 | test_nested_state_expect_einval(vm, state); | ||
186 | |||
187 | /* Size must be large enough to fit kvm_nested_state and vmcs12. */ | ||
188 | set_default_vmx_state(state, state_sz); | ||
189 | state->size = sizeof(*state); | ||
190 | test_nested_state(vm, state); | ||
191 | |||
192 | /* vmxon_pa cannot be the same address as vmcs_pa. */ | ||
193 | set_default_vmx_state(state, state_sz); | ||
194 | state->vmx.vmxon_pa = 0; | ||
195 | state->vmx.vmcs_pa = 0; | ||
196 | test_nested_state_expect_einval(vm, state); | ||
197 | |||
198 | /* The revision id for vmcs12 must be VMCS12_REVISION. */ | ||
199 | set_default_vmx_state(state, state_sz); | ||
200 | set_revision_id_for_vmcs12(state, 0); | ||
201 | test_nested_state_expect_einval(vm, state); | ||
202 | |||
203 | /* | ||
204 | * Test that if we leave nesting the state reflects that when we get | ||
205 | * it again. | ||
206 | */ | ||
207 | set_default_vmx_state(state, state_sz); | ||
208 | state->vmx.vmxon_pa = -1ull; | ||
209 | state->vmx.vmcs_pa = -1ull; | ||
210 | state->flags = 0; | ||
211 | test_nested_state(vm, state); | ||
212 | vcpu_nested_state_get(vm, VCPU_ID, state); | ||
213 | TEST_ASSERT(state->size >= sizeof(*state) && state->size <= state_sz, | ||
214 | "Size must be between %d and %d. The size returned was %d.", | ||
215 | sizeof(*state), state_sz, state->size); | ||
216 | TEST_ASSERT(state->vmx.vmxon_pa == -1ull, "vmxon_pa must be -1ull."); | ||
217 | TEST_ASSERT(state->vmx.vmcs_pa == -1ull, "vmcs_pa must be -1ull."); | ||
218 | |||
219 | free(state); | ||
220 | } | ||
221 | |||
222 | int main(int argc, char *argv[]) | ||
223 | { | ||
224 | struct kvm_vm *vm; | ||
225 | struct kvm_nested_state state; | ||
226 | struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1); | ||
227 | |||
228 | if (!kvm_check_cap(KVM_CAP_NESTED_STATE)) { | ||
229 | printf("KVM_CAP_NESTED_STATE not available, skipping test\n"); | ||
230 | exit(KSFT_SKIP); | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * AMD currently does not implement set_nested_state, so for now we | ||
235 | * just early out. | ||
236 | */ | ||
237 | if (!(entry->ecx & CPUID_VMX)) { | ||
238 | fprintf(stderr, "nested VMX not enabled, skipping test\n"); | ||
239 | exit(KSFT_SKIP); | ||
240 | } | ||
241 | |||
242 | vm = vm_create_default(VCPU_ID, 0, 0); | ||
243 | |||
244 | /* Passing a NULL kvm_nested_state causes a EFAULT. */ | ||
245 | test_nested_state_expect_efault(vm, NULL); | ||
246 | |||
247 | /* 'size' cannot be smaller than sizeof(kvm_nested_state). */ | ||
248 | set_default_state(&state); | ||
249 | state.size = 0; | ||
250 | test_nested_state_expect_einval(vm, &state); | ||
251 | |||
252 | /* | ||
253 | * Setting the flags 0xf fails the flags check. The only flags that | ||
254 | * can be used are: | ||
255 | * KVM_STATE_NESTED_GUEST_MODE | ||
256 | * KVM_STATE_NESTED_RUN_PENDING | ||
257 | * KVM_STATE_NESTED_EVMCS | ||
258 | */ | ||
259 | set_default_state(&state); | ||
260 | state.flags = 0xf; | ||
261 | test_nested_state_expect_einval(vm, &state); | ||
262 | |||
263 | /* | ||
264 | * If KVM_STATE_NESTED_RUN_PENDING is set then | ||
265 | * KVM_STATE_NESTED_GUEST_MODE has to be set as well. | ||
266 | */ | ||
267 | set_default_state(&state); | ||
268 | state.flags = KVM_STATE_NESTED_RUN_PENDING; | ||
269 | test_nested_state_expect_einval(vm, &state); | ||
270 | |||
271 | /* | ||
272 | * TODO: When SVM support is added for KVM_SET_NESTED_STATE | ||
273 | * add tests here to support it like VMX. | ||
274 | */ | ||
275 | if (entry->ecx & CPUID_VMX) | ||
276 | test_vmx_nested_state(vm); | ||
277 | |||
278 | kvm_vm_free(vm); | ||
279 | return 0; | ||
280 | } | ||