xref: /linux/tools/testing/selftests/kvm/x86/save_restore_pf_stress_test.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <sys/types.h>
7 #include <time.h>
8 #include <pthread.h>
9 #include <signal.h>
10 #include <unistd.h>
11 #include <getopt.h>
12 
13 #include "test_util.h"
14 #include "kvm_util.h"
15 #include "processor.h"
16 #include "svm_util.h"
17 #include "vmx.h"
18 
19 #define NR_ITERATIONS		500
20 
21 #define PTRS_PER_PTE		512
22 #define PXD_INDEX(vaddr, level)	(((vaddr) >> PG_LEVEL_SHIFT(level)) & (PTRS_PER_PTE - 1))
23 
24 #define TEST_MEM_BASE_GVA	0xc0000000ULL
25 #define TEST_PGTABLE_GVA_OFFSET	0xd0000000ULL
26 #define PATTERN			0xabcdefabcdefabcdULL
27 
28 static u64 expected_vaddr;
29 static u64 guest_faults;
30 
31 static u64 *guest_get_pte(u64 vaddr)
32 {
33 	u64 pgtable_pa, pte;
34 	u64 *pgtable;
35 	int level;
36 
37 	level = (get_cr4() & X86_CR4_LA57) ? PG_LEVEL_256T : PG_LEVEL_512G;
38 
39 	pgtable_pa = get_cr3() & PHYSICAL_PAGE_MASK;
40 	for (; level > PG_LEVEL_4K; level--) {
41 		pgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);
42 		pte = pgtable[PXD_INDEX(vaddr, level)];
43 		GUEST_ASSERT(pte & PTE_PRESENT_MASK(&guest_mmu));
44 		GUEST_ASSERT(!(pte & PTE_HUGE_MASK(&guest_mmu)));
45 		pgtable_pa = PTE_GET_PA(pte);
46 	}
47 
48 	pgtable = (u64 *)(pgtable_pa + TEST_PGTABLE_GVA_OFFSET);
49 	return &pgtable[PXD_INDEX(vaddr, PG_LEVEL_4K)];
50 }
51 
52 static void guest_pf_handler(struct ex_regs *regs)
53 {
54 	u64 fault_addr;
55 	u64 *ptep;
56 
57 	fault_addr = get_cr2();
58 	GUEST_ASSERT_EQ(fault_addr, READ_ONCE(expected_vaddr));
59 
60 	ptep = guest_get_pte(fault_addr);
61 	GUEST_ASSERT(ptep);
62 	GUEST_ASSERT(!(*ptep & PTE_PRESENT_MASK(&guest_mmu)));
63 
64 	*ptep |= PTE_PRESENT_MASK(&guest_mmu);
65 	guest_faults++;
66 }
67 
68 static void guest_access_memory(void *arg)
69 {
70 	u64 vaddr, val;
71 	int i;
72 
73 	for (i = 0; ; i++) {
74 		vaddr = TEST_MEM_BASE_GVA + (i % PTRS_PER_PTE) * PAGE_SIZE;
75 		WRITE_ONCE(expected_vaddr, vaddr);
76 
77 		/* Read to trigger #PF */
78 		val = READ_ONCE(*(u64 *)vaddr);
79 		GUEST_ASSERT_EQ(val, PATTERN);
80 
81 		/* Clear the present bit again so it faults next time */
82 		*guest_get_pte(vaddr) &= ~PTE_PRESENT_MASK(&guest_mmu);
83 		invlpg(vaddr);
84 	}
85 }
86 
87 static void l1_svm_code(struct svm_test_data *svm)
88 {
89 	generic_svm_setup(svm, guest_access_memory);
90 	svm->vmcb->control.intercept_exceptions |= BIT(UD_VECTOR);
91 
92 	while (1) {
93 		run_guest(svm->vmcb, svm->vmcb_gpa);
94 		GUEST_ASSERT_EQ(svm->vmcb->control.exit_code,
95 				(SVM_EXIT_EXCP_BASE + UD_VECTOR));
96 	}
97 }
98 
99 static void l1_vmx_code(struct vmx_pages *vmx)
100 {
101 	GUEST_ASSERT(prepare_for_vmx_operation(vmx));
102 	GUEST_ASSERT(load_vmcs(vmx));
103 	prepare_vmcs(vmx, guest_access_memory);
104 
105 	GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, BIT(UD_VECTOR)));
106 
107 	GUEST_ASSERT(!vmlaunch());
108 	while (1) {
109 		GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_EXCEPTION_NMI);
110 		GUEST_ASSERT_EQ(vmreadz(VM_EXIT_INTR_INFO) & 0xff, UD_VECTOR);
111 		GUEST_ASSERT(!vmresume());
112 	}
113 }
114 
115 static void l1_guest_code(void *test_data)
116 {
117 	if (this_cpu_has(X86_FEATURE_SVM))
118 		l1_svm_code(test_data);
119 	else
120 		l1_vmx_code(test_data);
121 }
122 
123 static void *sigusr_thread_fn(void *arg)
124 {
125 	pthread_t vcpu_thread = (pthread_t)arg;
126 
127 	for (;;) {
128 		pthread_testcancel();
129 		pthread_kill(vcpu_thread, SIGUSR1);
130 		usleep(msecs_to_usecs(1));
131 	}
132 	return NULL;
133 }
134 
135 static void dummy_signal_handler(int signo) {}
136 static struct sigaction sa;
137 
138 static void vcpu_sigusr_listen(void)
139 {
140 	sa.sa_handler = dummy_signal_handler;
141 	sigaction(SIGUSR1, &sa, NULL);
142 }
143 
144 static void vcpu_sigusr_ignore(void)
145 {
146 	sa.sa_handler = SIG_IGN;
147 	sigaction(SIGUSR1, &sa, NULL);
148 }
149 
150 static void kvm_x86_state_queue_ud(struct kvm_x86_state *state)
151 {
152 	if (state->events.exception.pending || state->events.exception.injected)
153 		return;
154 
155 	state->events.flags |= KVM_VCPUEVENT_VALID_PAYLOAD;
156 	state->events.exception.pending = true;
157 	state->events.exception.injected = false;
158 	state->events.exception.nr = UD_VECTOR;
159 	state->events.exception.has_error_code = false;
160 	state->events.exception_has_payload = false;
161 }
162 
163 static void run_test(bool nested)
164 {
165 	struct kvm_x86_state *state;
166 	int r, i, level;
167 	pthread_t sigusr_thread;
168 	gpa_t gpa, pgtable_gpa;
169 	struct kvm_vcpu *vcpu;
170 	struct kvm_vm *vm;
171 	struct ucall uc;
172 	u64 *pgtable;
173 	gva_t gva;
174 	u64 pte;
175 
176 	vm = vm_create_with_one_vcpu(&vcpu, nested ? l1_guest_code : guest_access_memory);
177 	vm_install_exception_handler(vm, PF_VECTOR, guest_pf_handler);
178 
179 	if (nested) {
180 		vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
181 		if (kvm_cpu_has(X86_FEATURE_SVM))
182 			vcpu_alloc_svm(vm, &gva);
183 		else
184 			vcpu_alloc_vmx(vm, &gva);
185 		vcpu_args_set(vcpu, 1, gva);
186 	}
187 
188 	/* Allocate a page and write the pattern to it */
189 	gva = vm_alloc_page(vm);
190 	*(u64 *)addr_gva2hva(vm, gva) = PATTERN;
191 	gpa = addr_gva2gpa(vm, gva);
192 
193 	/*
194 	 * Map all virtual addresses to the pattern page and clear the present
195 	 * bit such that guest accesses will cause a #PF.
196 	 */
197 	for (i = 0; i < PTRS_PER_PTE; i++) {
198 		gva = TEST_MEM_BASE_GVA + i * getpagesize();
199 		virt_pg_map(vm, gva, gpa);
200 		*vm_get_pte(vm, gva) &= ~PTE_PRESENT_MASK(&vm->mmu);
201 	}
202 
203 	/*
204 	 * Now create mappings for the page tables created above so that the
205 	 * guest #PF handler can walk them. All PTEs for test virtual addresses
206 	 * should lie on the same PTE page, so one page is mapped for each page
207 	 * table level.
208 	 *
209 	 * Use an offset for the GVA instead of creating identity mappings to
210 	 * avoid collision with existing mappings at low GVAs (e.g. ELF).
211 	 */
212 	pgtable_gpa = vm->mmu.pgd;
213 	for (level = vm->mmu.pgtable_levels; level >= PG_LEVEL_4K; level--) {
214 		virt_map(vm, pgtable_gpa + TEST_PGTABLE_GVA_OFFSET, pgtable_gpa, 1);
215 		pgtable = addr_gpa2hva(vm, pgtable_gpa);
216 		pte = pgtable[PXD_INDEX(TEST_MEM_BASE_GVA, level)];
217 		pgtable_gpa = PTE_GET_PA(pte);
218 	}
219 
220 	/* Initialize the thread sending SIGUSR and install the handler */
221 	vcpu_sigusr_ignore();
222 	r = pthread_create(&sigusr_thread, NULL, sigusr_thread_fn,
223 			   (void *)pthread_self());
224 	TEST_ASSERT(!r, "pthread_create() failed: %d", r);
225 
226 	for (i = 1; i <= NR_ITERATIONS; i++) {
227 		/*
228 		 * Only handle SIGUSR while the vCPU is running, otherwise
229 		 * ignore it to avoid interrupting other ioctls/syscalls.
230 		 */
231 		vcpu_sigusr_listen();
232 		r = __vcpu_run(vcpu);
233 		TEST_ASSERT(!r || errno == EINTR, "Expected success or SIGUSR1");
234 		vcpu_sigusr_ignore();
235 
236 		/* The guest only exits due to a signal or failed assertion */
237 		if (!r) {
238 			TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
239 			TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_ABORT);
240 			REPORT_GUEST_ASSERT(uc);
241 			break;
242 		}
243 
244 		state = vcpu_save_state(vcpu);
245 
246 		/*
247 		 * If the vCPU is in guest mode, inject a #UD to trigger an
248 		 * L2->L1 VM-Exit every other iteration.
249 		 */
250 		if (kvm_x86_state_is_guest_mode(state) && i % 2 == 0)
251 			kvm_x86_state_queue_ud(state);
252 
253 		kvm_vm_release(vm);
254 		vcpu = vm_recreate_with_one_vcpu(vm);
255 		if (nested)
256 			vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);
257 		vcpu_load_state(vcpu, state);
258 		kvm_x86_state_cleanup(state);
259 
260 		pr_info("\rSave+restore iterations: %d", i);
261 	}
262 	pr_info("\n");
263 
264 	sync_global_from_guest(vm, guest_faults);
265 	TEST_ASSERT(guest_faults, "No guest page faults triggered");
266 	pr_info("Guest page faults%s: %lu\n", nested ? " (in L2)" : "", guest_faults);
267 
268 	pthread_cancel(sigusr_thread);
269 	pthread_join(sigusr_thread, NULL);
270 	kvm_vm_free(vm);
271 }
272 
273 int main(int argc, char *argv[])
274 {
275 	pr_info("Running save+restore stress test...\n");
276 	run_test(/*nested=*/false);
277 
278 	if (!kvm_has_cap(KVM_CAP_EXCEPTION_PAYLOAD) ||
279 	    !kvm_has_cap(KVM_CAP_NESTED_STATE) ||
280 	    (!kvm_cpu_has(X86_FEATURE_SVM) && !kvm_cpu_has(X86_FEATURE_VMX))) {
281 		pr_info("Nested virtualization not supported, skipping nested test\n");
282 		return 0;
283 	}
284 
285 	pr_info("Running save+restore stress test with a nested guest...\n");
286 	run_test(/*nested=*/true);
287 	return 0;
288 }
289