1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2022, Red Hat, Inc.
5 * Tests for Hyper-V extensions to SVM.
11 #include <sys/ioctl.h>
12 #include <linux/bitmap.h>
14 #include "test_util.h"
17 #include "processor.h"
21 #define L2_GUEST_STACK_SIZE 256
23 /* Exit to L1 from L2 with RDMSR instruction */
24 static inline void rdmsr_from_l2(uint32_t msr
)
26 /* Currently, L1 doesn't preserve GPRs during vmexits. */
27 __asm__
__volatile__ ("rdmsr" : : "c"(msr
) :
28 "rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9",
29 "r10", "r11", "r12", "r13", "r14", "r15");
32 void l2_guest_code(void)
40 /* MSR-Bitmap tests */
41 rdmsr_from_l2(MSR_FS_BASE
); /* intercepted */
42 rdmsr_from_l2(MSR_FS_BASE
); /* intercepted */
43 rdmsr_from_l2(MSR_GS_BASE
); /* not intercepted */
45 rdmsr_from_l2(MSR_GS_BASE
); /* intercepted */
49 /* L2 TLB flush tests */
50 hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE
|
51 HV_HYPERCALL_FAST_BIT
, 0x0,
52 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES
|
53 HV_FLUSH_ALL_PROCESSORS
);
54 rdmsr_from_l2(MSR_FS_BASE
);
56 * Note: hypercall status (RAX) is not preserved correctly by L1 after
57 * synthetic vmexit, use unchecked version.
59 __hyperv_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE
|
60 HV_HYPERCALL_FAST_BIT
, 0x0,
61 HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES
|
62 HV_FLUSH_ALL_PROCESSORS
, &unused
);
64 /* Done, exit to L1 and never come back. */
68 static void __attribute__((__flatten__
)) guest_code(struct svm_test_data
*svm
,
69 struct hyperv_test_pages
*hv_pages
,
72 unsigned long l2_guest_stack
[L2_GUEST_STACK_SIZE
];
73 struct vmcb
*vmcb
= svm
->vmcb
;
74 struct hv_vmcb_enlightenments
*hve
= &vmcb
->control
.hv_enlightenments
;
78 wrmsr(HV_X64_MSR_GUEST_OS_ID
, HYPERV_LINUX_OS_ID
);
79 wrmsr(HV_X64_MSR_HYPERCALL
, pgs_gpa
);
80 enable_vp_assist(hv_pages
->vp_assist_gpa
, hv_pages
->vp_assist
);
82 GUEST_ASSERT(svm
->vmcb_gpa
);
83 /* Prepare for L2 execution. */
84 generic_svm_setup(svm
, l2_guest_code
,
85 &l2_guest_stack
[L2_GUEST_STACK_SIZE
]);
87 /* L2 TLB flush setup */
88 hve
->partition_assist_page
= hv_pages
->partition_assist_gpa
;
89 hve
->hv_enlightenments_control
.nested_flush_hypercall
= 1;
92 current_vp_assist
->nested_control
.features
.directhypercall
= 1;
93 *(u32
*)(hv_pages
->partition_assist
) = 0;
96 run_guest(vmcb
, svm
->vmcb_gpa
);
97 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_VMMCALL
);
101 /* Intercept RDMSR 0xc0000100 */
102 vmcb
->control
.intercept
|= 1ULL << INTERCEPT_MSR_PROT
;
103 __set_bit(2 * (MSR_FS_BASE
& 0x1fff), svm
->msr
+ 0x800);
104 run_guest(vmcb
, svm
->vmcb_gpa
);
105 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_MSR
);
106 vmcb
->save
.rip
+= 2; /* rdmsr */
108 /* Enable enlightened MSR bitmap */
109 hve
->hv_enlightenments_control
.msr_bitmap
= 1;
110 run_guest(vmcb
, svm
->vmcb_gpa
);
111 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_MSR
);
112 vmcb
->save
.rip
+= 2; /* rdmsr */
114 /* Intercept RDMSR 0xc0000101 without telling KVM about it */
115 __set_bit(2 * (MSR_GS_BASE
& 0x1fff), svm
->msr
+ 0x800);
116 /* Make sure HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP is set */
117 vmcb
->control
.clean
|= HV_VMCB_NESTED_ENLIGHTENMENTS
;
118 run_guest(vmcb
, svm
->vmcb_gpa
);
119 /* Make sure we don't see SVM_EXIT_MSR here so eMSR bitmap works */
120 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_VMMCALL
);
121 vmcb
->save
.rip
+= 3; /* vmcall */
123 /* Now tell KVM we've changed MSR-Bitmap */
124 vmcb
->control
.clean
&= ~HV_VMCB_NESTED_ENLIGHTENMENTS
;
125 run_guest(vmcb
, svm
->vmcb_gpa
);
126 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_MSR
);
127 vmcb
->save
.rip
+= 2; /* rdmsr */
131 * L2 TLB flush test. First VMCALL should be handled directly by L0,
132 * no VMCALL exit expected.
134 run_guest(vmcb
, svm
->vmcb_gpa
);
135 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_MSR
);
136 vmcb
->save
.rip
+= 2; /* rdmsr */
137 /* Enable synthetic vmexit */
138 *(u32
*)(hv_pages
->partition_assist
) = 1;
139 run_guest(vmcb
, svm
->vmcb_gpa
);
140 GUEST_ASSERT(vmcb
->control
.exit_code
== HV_SVM_EXITCODE_ENL
);
141 GUEST_ASSERT(vmcb
->control
.exit_info_1
== HV_SVM_ENL_EXITCODE_TRAP_AFTER_FLUSH
);
143 run_guest(vmcb
, svm
->vmcb_gpa
);
144 GUEST_ASSERT(vmcb
->control
.exit_code
== SVM_EXIT_VMMCALL
);
150 int main(int argc
, char *argv
[])
152 vm_vaddr_t nested_gva
= 0, hv_pages_gva
= 0;
153 vm_vaddr_t hcall_page
;
154 struct kvm_vcpu
*vcpu
;
159 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM
));
160 TEST_REQUIRE(kvm_hv_cpu_has(HV_X64_NESTED_DIRECT_FLUSH
));
163 vm
= vm_create_with_one_vcpu(&vcpu
, guest_code
);
164 vcpu_set_hv_cpuid(vcpu
);
165 vcpu_alloc_svm(vm
, &nested_gva
);
166 vcpu_alloc_hyperv_test_pages(vm
, &hv_pages_gva
);
168 hcall_page
= vm_vaddr_alloc_pages(vm
, 1);
169 memset(addr_gva2hva(vm
, hcall_page
), 0x0, getpagesize());
171 vcpu_args_set(vcpu
, 3, nested_gva
, hv_pages_gva
, addr_gva2gpa(vm
, hcall_page
));
172 vcpu_set_msr(vcpu
, HV_X64_MSR_VP_INDEX
, vcpu
->id
);
174 for (stage
= 1;; stage
++) {
176 TEST_ASSERT_KVM_EXIT_REASON(vcpu
, KVM_EXIT_IO
);
178 switch (get_ucall(vcpu
, &uc
)) {
180 REPORT_GUEST_ASSERT(uc
);
187 TEST_FAIL("Unknown ucall %lu", uc
.cmd
);
190 /* UCALL_SYNC is handled here. */
191 TEST_ASSERT(!strcmp((const char *)uc
.args
[0], "hello") &&
192 uc
.args
[1] == stage
, "Stage %d: Unexpected register values vmexit, got %lx",
193 stage
, (ulong
)uc
.args
[1]);