Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * KVM_GET/SET_* tests
0004  *
0005  * Copyright (C) 2022, Red Hat, Inc.
0006  *
0007  * Tests for Hyper-V extensions to SVM.
0008  */
0009 #define _GNU_SOURCE /* for program_invocation_short_name */
0010 #include <fcntl.h>
0011 #include <stdio.h>
0012 #include <stdlib.h>
0013 #include <string.h>
0014 #include <sys/ioctl.h>
0015 #include <linux/bitmap.h>
0016 
0017 #include "test_util.h"
0018 
0019 #include "kvm_util.h"
0020 #include "processor.h"
0021 #include "svm_util.h"
0022 #include "hyperv.h"
0023 
0024 #define L2_GUEST_STACK_SIZE 256
0025 
0026 struct hv_enlightenments {
0027     struct __packed hv_enlightenments_control {
0028         u32 nested_flush_hypercall:1;
0029         u32 msr_bitmap:1;
0030         u32 enlightened_npt_tlb: 1;
0031         u32 reserved:29;
0032     } __packed hv_enlightenments_control;
0033     u32 hv_vp_id;
0034     u64 hv_vm_id;
0035     u64 partition_assist_page;
0036     u64 reserved;
0037 } __packed;
0038 
0039 /*
0040  * Hyper-V uses the software reserved clean bit in VMCB
0041  */
0042 #define VMCB_HV_NESTED_ENLIGHTENMENTS (1U << 31)
0043 
0044 void l2_guest_code(void)
0045 {
0046     GUEST_SYNC(3);
0047     /* Exit to L1 */
0048     vmmcall();
0049 
0050     /* MSR-Bitmap tests */
0051     rdmsr(MSR_FS_BASE); /* intercepted */
0052     rdmsr(MSR_FS_BASE); /* intercepted */
0053     rdmsr(MSR_GS_BASE); /* not intercepted */
0054     vmmcall();
0055     rdmsr(MSR_GS_BASE); /* intercepted */
0056 
0057     GUEST_SYNC(5);
0058 
0059     /* Done, exit to L1 and never come back.  */
0060     vmmcall();
0061 }
0062 
0063 static void __attribute__((__flatten__)) guest_code(struct svm_test_data *svm)
0064 {
0065     unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE];
0066     struct vmcb *vmcb = svm->vmcb;
0067     struct hv_enlightenments *hve =
0068         (struct hv_enlightenments *)vmcb->control.reserved_sw;
0069 
0070     GUEST_SYNC(1);
0071 
0072     wrmsr(HV_X64_MSR_GUEST_OS_ID, (u64)0x8100 << 48);
0073 
0074     GUEST_ASSERT(svm->vmcb_gpa);
0075     /* Prepare for L2 execution. */
0076     generic_svm_setup(svm, l2_guest_code,
0077               &l2_guest_stack[L2_GUEST_STACK_SIZE]);
0078 
0079     GUEST_SYNC(2);
0080     run_guest(vmcb, svm->vmcb_gpa);
0081     GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);
0082     GUEST_SYNC(4);
0083     vmcb->save.rip += 3;
0084 
0085     /* Intercept RDMSR 0xc0000100 */
0086     vmcb->control.intercept |= 1ULL << INTERCEPT_MSR_PROT;
0087     set_bit(2 * (MSR_FS_BASE & 0x1fff), svm->msr + 0x800);
0088     run_guest(vmcb, svm->vmcb_gpa);
0089     GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);
0090     vmcb->save.rip += 2; /* rdmsr */
0091 
0092     /* Enable enlightened MSR bitmap */
0093     hve->hv_enlightenments_control.msr_bitmap = 1;
0094     run_guest(vmcb, svm->vmcb_gpa);
0095     GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);
0096     vmcb->save.rip += 2; /* rdmsr */
0097 
0098     /* Intercept RDMSR 0xc0000101 without telling KVM about it */
0099     set_bit(2 * (MSR_GS_BASE & 0x1fff), svm->msr + 0x800);
0100     /* Make sure HV_VMX_ENLIGHTENED_CLEAN_FIELD_MSR_BITMAP is set */
0101     vmcb->control.clean |= VMCB_HV_NESTED_ENLIGHTENMENTS;
0102     run_guest(vmcb, svm->vmcb_gpa);
0103     /* Make sure we don't see SVM_EXIT_MSR here so eMSR bitmap works */
0104     GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);
0105     vmcb->save.rip += 3; /* vmcall */
0106 
0107     /* Now tell KVM we've changed MSR-Bitmap */
0108     vmcb->control.clean &= ~VMCB_HV_NESTED_ENLIGHTENMENTS;
0109     run_guest(vmcb, svm->vmcb_gpa);
0110     GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_MSR);
0111     vmcb->save.rip += 2; /* rdmsr */
0112 
0113     run_guest(vmcb, svm->vmcb_gpa);
0114     GUEST_ASSERT(vmcb->control.exit_code == SVM_EXIT_VMMCALL);
0115     GUEST_SYNC(6);
0116 
0117     GUEST_DONE();
0118 }
0119 
0120 int main(int argc, char *argv[])
0121 {
0122     vm_vaddr_t nested_gva = 0;
0123 
0124     struct kvm_vcpu *vcpu;
0125     struct kvm_vm *vm;
0126     struct kvm_run *run;
0127     struct ucall uc;
0128     int stage;
0129 
0130     TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
0131 
0132     /* Create VM */
0133     vm = vm_create_with_one_vcpu(&vcpu, guest_code);
0134     vcpu_set_hv_cpuid(vcpu);
0135     run = vcpu->run;
0136     vcpu_alloc_svm(vm, &nested_gva);
0137     vcpu_args_set(vcpu, 1, nested_gva);
0138 
0139     for (stage = 1;; stage++) {
0140         vcpu_run(vcpu);
0141         TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0142                 "Stage %d: unexpected exit reason: %u (%s),\n",
0143                 stage, run->exit_reason,
0144                 exit_reason_str(run->exit_reason));
0145 
0146         switch (get_ucall(vcpu, &uc)) {
0147         case UCALL_ABORT:
0148             REPORT_GUEST_ASSERT(uc);
0149             /* NOT REACHED */
0150         case UCALL_SYNC:
0151             break;
0152         case UCALL_DONE:
0153             goto done;
0154         default:
0155             TEST_FAIL("Unknown ucall %lu", uc.cmd);
0156         }
0157 
0158         /* UCALL_SYNC is handled here.  */
0159         TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") &&
0160                 uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx",
0161                 stage, (ulong)uc.args[1]);
0162 
0163     }
0164 
0165 done:
0166     kvm_vm_free(vm);
0167 }