~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright (C) 2022, Red Hat, Inc.
  4  *
  5  * Tests for Hyper-V extensions to SVM.
  6  */
  7 #include <fcntl.h>
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <string.h>
 11 #include <sys/ioctl.h>
 12 #include <linux/bitmap.h>
 13 
 14 #include "test_util.h"
 15 
 16 #include "kvm_util.h"
 17 #include "processor.h"
 18 #include "svm_util.h"
 19 #include "hyperv.h"
 20 
 21 #define L2_GUEST_STACK_SIZE 256
 22 
 23 /* Exit to L1 from L2 with RDMSR instruction */
 24 static inline void rdmsr_from_l2(uint32_t msr)
 25 {
 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");
 30 }
 31 
 32 void l2_guest_code(void)
 33 {
 34         u64 unused;
 35 
 36         GUEST_SYNC(3);
 37         /* Exit to L1 */
 38         vmmcall();
 39 
 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 */
 44         vmmcall();
 45         rdmsr_from_l2(MSR_GS_BASE); /* intercepted */
 46 
 47         GUEST_SYNC(5);
 48 
 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);
 55         /*
 56          * Note: hypercall status (RAX) is not preserved correctly by L1 after
 57          * synthetic vmexit, use unchecked version.
 58          */
 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);
 63 
 64         /* Done, exit to L1 and never come back.  */
 65         vmmcall();
 66 }
 67 
 68 static void __attribute__((__flatten__)) guest_code(struct svm_test_data *svm,
 69                                                     struct hyperv_test_pages *hv_pages,
 70                                                     vm_vaddr_t pgs_gpa)
 71 {
 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;
 75 
 76         GUEST_SYNC(1);
 77 
 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);
 81 
 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]);
 86 
 87         /* L2 TLB flush setup */
 88         hve->partition_assist_page = hv_pages->partition_assist_gpa;
 89         hve->hv_enlightenments_control.nested_flush_hypercall = 1;
 90         hve->hv_vm_id = 1;
 91         hve->hv_vp_id = 1;
 92         current_vp_assist->nested_control.features.directhypercall = 1;
 93         *(u32 *)(hv_pages->partition_assist) = 0;
 94 
 95         GUEST_SYNC(2);
 96         run_guest(vmcb, svm->vmcb_gpa);
 97         GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);
 98         GUEST_SYNC(4);
 99         vmcb->save.rip += 3;
100 
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 */
107 
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 */
113 
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 */
122 
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 */
128 
129 
130         /*
131          * L2 TLB flush test. First VMCALL should be handled directly by L0,
132          * no VMCALL exit expected.
133          */
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);
142 
143         run_guest(vmcb, svm->vmcb_gpa);
144         GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);
145         GUEST_SYNC(6);
146 
147         GUEST_DONE();
148 }
149 
150 int main(int argc, char *argv[])
151 {
152         vm_vaddr_t nested_gva = 0, hv_pages_gva = 0;
153         vm_vaddr_t hcall_page;
154         struct kvm_vcpu *vcpu;
155         struct kvm_vm *vm;
156         struct ucall uc;
157         int stage;
158 
159         TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
160         TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_DIRECT_TLBFLUSH));
161 
162         /* Create VM */
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);
167 
168         hcall_page = vm_vaddr_alloc_pages(vm, 1);
169         memset(addr_gva2hva(vm, hcall_page), 0x0,  getpagesize());
170 
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);
173 
174         for (stage = 1;; stage++) {
175                 vcpu_run(vcpu);
176                 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
177 
178                 switch (get_ucall(vcpu, &uc)) {
179                 case UCALL_ABORT:
180                         REPORT_GUEST_ASSERT(uc);
181                         /* NOT REACHED */
182                 case UCALL_SYNC:
183                         break;
184                 case UCALL_DONE:
185                         goto done;
186                 default:
187                         TEST_FAIL("Unknown ucall %lu", uc.cmd);
188                 }
189 
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]);
194 
195         }
196 
197 done:
198         kvm_vm_free(vm);
199 }
200 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php