xref: /linux/tools/testing/selftests/kvm/lib/x86/memstress.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * x86-specific extensions to memstress.c.
4  *
5  * Copyright (C) 2022, Google, Inc.
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <linux/bitmap.h>
10 #include <linux/bitops.h>
11 
12 #include "test_util.h"
13 #include "kvm_util.h"
14 #include "memstress.h"
15 #include "processor.h"
16 #include "svm_util.h"
17 #include "vmx.h"
18 
19 void memstress_l2_guest_code(uint64_t vcpu_id)
20 {
21 	memstress_guest_code(vcpu_id);
22 	vmcall();
23 }
24 
25 extern char memstress_l2_guest_entry[];
26 __asm__(
27 "memstress_l2_guest_entry:"
28 "	mov (%rsp), %rdi;"
29 "	call memstress_l2_guest_code;"
30 "	ud2;"
31 );
32 
33 #define L2_GUEST_STACK_SIZE 64
34 
35 static void l1_vmx_code(struct vmx_pages *vmx, uint64_t vcpu_id)
36 {
37 	unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
38 	unsigned long *rsp;
39 
40 	GUEST_ASSERT(vmx->vmcs_gpa);
41 	GUEST_ASSERT(prepare_for_vmx_operation(vmx));
42 	GUEST_ASSERT(load_vmcs(vmx));
43 	GUEST_ASSERT(ept_1g_pages_supported());
44 
45 	rsp = &l2_guest_stack[L2_GUEST_STACK_SIZE - 1];
46 	*rsp = vcpu_id;
47 	prepare_vmcs(vmx, memstress_l2_guest_entry, rsp);
48 
49 	GUEST_ASSERT(!vmlaunch());
50 	GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_VMCALL);
51 	GUEST_DONE();
52 }
53 
54 static void l1_svm_code(struct svm_test_data *svm, uint64_t vcpu_id)
55 {
56 	unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
57 	unsigned long *rsp;
58 
59 
60 	rsp = &l2_guest_stack[L2_GUEST_STACK_SIZE - 1];
61 	*rsp = vcpu_id;
62 	generic_svm_setup(svm, memstress_l2_guest_entry, rsp);
63 
64 	run_guest(svm->vmcb, svm->vmcb_gpa);
65 	GUEST_ASSERT_EQ(svm->vmcb->control.exit_code, SVM_EXIT_VMMCALL);
66 	GUEST_DONE();
67 }
68 
69 
70 static void memstress_l1_guest_code(void *data, uint64_t vcpu_id)
71 {
72 	if (this_cpu_has(X86_FEATURE_VMX))
73 		l1_vmx_code(data, vcpu_id);
74 	else
75 		l1_svm_code(data, vcpu_id);
76 }
77 
78 uint64_t memstress_nested_pages(int nr_vcpus)
79 {
80 	/*
81 	 * 513 page tables is enough to identity-map 256 TiB of L2 with 1G
82 	 * pages and 4-level paging, plus a few pages per-vCPU for data
83 	 * structures such as the VMCS.
84 	 */
85 	return 513 + 10 * nr_vcpus;
86 }
87 
88 static void memstress_setup_ept_mappings(struct kvm_vm *vm)
89 {
90 	uint64_t start, end;
91 
92 	/*
93 	 * Identity map the first 4G and the test region with 1G pages so that
94 	 * KVM can shadow the EPT12 with the maximum huge page size supported
95 	 * by the backing source.
96 	 */
97 	tdp_identity_map_1g(vm, 0, 0x100000000ULL);
98 
99 	start = align_down(memstress_args.gpa, PG_SIZE_1G);
100 	end = align_up(memstress_args.gpa + memstress_args.size, PG_SIZE_1G);
101 	tdp_identity_map_1g(vm, start, end - start);
102 }
103 
104 void memstress_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[])
105 {
106 	struct kvm_regs regs;
107 	vm_vaddr_t nested_gva;
108 	int vcpu_id;
109 
110 	TEST_REQUIRE(kvm_cpu_has_tdp());
111 
112 	vm_enable_tdp(vm);
113 	memstress_setup_ept_mappings(vm);
114 	for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) {
115 		if (kvm_cpu_has(X86_FEATURE_VMX))
116 			vcpu_alloc_vmx(vm, &nested_gva);
117 		else
118 			vcpu_alloc_svm(vm, &nested_gva);
119 
120 		/*
121 		 * Override the vCPU to run memstress_l1_guest_code() which will
122 		 * bounce it into L2 before calling memstress_guest_code().
123 		 */
124 		vcpu_regs_get(vcpus[vcpu_id], &regs);
125 		regs.rip = (unsigned long) memstress_l1_guest_code;
126 		vcpu_regs_set(vcpus[vcpu_id], &regs);
127 		vcpu_args_set(vcpus[vcpu_id], 2, nested_gva, vcpu_id);
128 	}
129 }
130