1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018, Red Hat, Inc. 4 * 5 * Tests for SMM. 6 */ 7 #include <fcntl.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <stdint.h> 11 #include <string.h> 12 #include <sys/ioctl.h> 13 14 #include "test_util.h" 15 16 #include "kvm_util.h" 17 #include "smm.h" 18 19 #include "vmx.h" 20 #include "svm_util.h" 21 22 #define SMRAM_GPA 0x1000000 23 #define SMRAM_STAGE 0xfe 24 25 #define SYNC_PORT 0xe 26 #define DONE 0xff 27 28 /* 29 * This is compiled as normal 64-bit code, however, SMI handler is executed 30 * in real-address mode. To stay simple we're limiting ourselves to a mode 31 * independent subset of asm here. 32 * SMI handler always report back fixed stage SMRAM_STAGE. 33 */ 34 u8 smi_handler[] = { 35 0xb0, SMRAM_STAGE, /* mov $SMRAM_STAGE, %al */ 36 0xe4, SYNC_PORT, /* in $SYNC_PORT, %al */ 37 0x0f, 0xaa, /* rsm */ 38 }; 39 40 static inline void sync_with_host(u64 phase) 41 { 42 asm volatile("in $" __stringify(SYNC_PORT)", %%al \n" 43 : "+a" (phase)); 44 } 45 46 static void self_smi(void) 47 { 48 x2apic_write_reg(APIC_ICR, 49 APIC_DEST_SELF | APIC_INT_ASSERT | APIC_DM_SMI); 50 } 51 52 static void l2_guest_code(void) 53 { 54 sync_with_host(8); 55 56 sync_with_host(10); 57 58 vmcall(); 59 } 60 61 static void guest_code(void *arg) 62 { 63 u64 apicbase = rdmsr(MSR_IA32_APICBASE); 64 struct svm_test_data *svm = arg; 65 struct vmx_pages *vmx_pages = arg; 66 67 sync_with_host(1); 68 69 wrmsr(MSR_IA32_APICBASE, apicbase | X2APIC_ENABLE); 70 71 sync_with_host(2); 72 73 self_smi(); 74 75 sync_with_host(4); 76 77 if (arg) { 78 if (this_cpu_has(X86_FEATURE_SVM)) { 79 generic_svm_setup(svm, l2_guest_code); 80 } else { 81 GUEST_ASSERT(prepare_for_vmx_operation(vmx_pages)); 82 GUEST_ASSERT(load_vmcs(vmx_pages)); 83 prepare_vmcs(vmx_pages, l2_guest_code); 84 } 85 86 sync_with_host(5); 87 88 self_smi(); 89 90 sync_with_host(7); 91 92 if (this_cpu_has(X86_FEATURE_SVM)) { 93 run_guest(svm->vmcb, svm->vmcb_gpa); 94 run_guest(svm->vmcb, svm->vmcb_gpa); 95 } else { 96 vmlaunch(); 97 vmresume(); 98 } 99 100 /* Stages 8-11 are eaten by SMM (SMRAM_STAGE reported instead) */ 101 sync_with_host(12); 102 } 103 104 sync_with_host(DONE); 105 } 106 107 int main(int argc, char *argv[]) 108 { 109 gva_t nested_gva = 0; 110 111 struct kvm_vcpu *vcpu; 112 struct kvm_regs regs; 113 struct kvm_vm *vm; 114 struct kvm_x86_state *state; 115 int stage, stage_reported; 116 117 TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_SMM)); 118 119 /* Create VM */ 120 vm = vm_create_with_one_vcpu(&vcpu, guest_code); 121 122 setup_smram(vm, vcpu, SMRAM_GPA, smi_handler, sizeof(smi_handler)); 123 124 if (kvm_has_cap(KVM_CAP_NESTED_STATE)) { 125 if (kvm_cpu_has(X86_FEATURE_SVM)) 126 vcpu_alloc_svm(vm, &nested_gva); 127 else if (kvm_cpu_has(X86_FEATURE_VMX)) 128 vcpu_alloc_vmx(vm, &nested_gva); 129 } 130 131 if (!nested_gva) 132 pr_info("will skip SMM test with VMX enabled\n"); 133 134 vcpu_args_set(vcpu, 1, nested_gva); 135 136 for (stage = 1;; stage++) { 137 vcpu_run(vcpu); 138 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 139 140 memset(®s, 0, sizeof(regs)); 141 vcpu_regs_get(vcpu, ®s); 142 143 stage_reported = regs.rax & 0xff; 144 145 if (stage_reported == DONE) 146 goto done; 147 148 TEST_ASSERT(stage_reported == stage || 149 stage_reported == SMRAM_STAGE, 150 "Unexpected stage: #%x, got %x", 151 stage, stage_reported); 152 153 /* 154 * Enter SMM during L2 execution and check that we correctly 155 * return from it. Do not perform save/restore while in SMM yet. 156 */ 157 if (stage == 8) { 158 inject_smi(vcpu); 159 continue; 160 } 161 162 /* 163 * Perform save/restore while the guest is in SMM triggered 164 * during L2 execution. 165 */ 166 if (stage == 10) 167 inject_smi(vcpu); 168 169 state = vcpu_save_state(vcpu); 170 kvm_vm_release(vm); 171 172 vcpu = vm_recreate_with_one_vcpu(vm); 173 vcpu_load_state(vcpu, state); 174 kvm_x86_state_cleanup(state); 175 } 176 177 done: 178 kvm_vm_free(vm); 179 } 180