Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 142 lines 3.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2020, Google LLC. 4 * 5 * Tests for KVM paravirtual feature disablement 6 */ 7#include <asm/kvm_para.h> 8#include <linux/kvm_para.h> 9#include <linux/stringify.h> 10#include <stdint.h> 11 12#include "kvm_test_harness.h" 13#include "apic.h" 14#include "test_util.h" 15#include "kvm_util.h" 16#include "processor.h" 17 18/* VMCALL and VMMCALL are both 3-byte opcodes. */ 19#define HYPERCALL_INSN_SIZE 3 20 21static bool quirk_disabled; 22 23static void guest_ud_handler(struct ex_regs *regs) 24{ 25 regs->rax = -EFAULT; 26 regs->rip += HYPERCALL_INSN_SIZE; 27} 28 29static const u8 vmx_vmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xc1 }; 30static const u8 svm_vmmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xd9 }; 31 32extern u8 hypercall_insn[HYPERCALL_INSN_SIZE]; 33static u64 do_sched_yield(u8 apic_id) 34{ 35 u64 ret; 36 37 asm volatile("hypercall_insn:\n\t" 38 ".byte 0xcc,0xcc,0xcc\n\t" 39 : "=a"(ret) 40 : "a"((u64)KVM_HC_SCHED_YIELD), "b"((u64)apic_id) 41 : "memory"); 42 43 return ret; 44} 45 46static void guest_main(void) 47{ 48 const u8 *native_hypercall_insn; 49 const u8 *other_hypercall_insn; 50 u64 ret; 51 52 if (host_cpu_is_intel) { 53 native_hypercall_insn = vmx_vmcall; 54 other_hypercall_insn = svm_vmmcall; 55 } else if (host_cpu_is_amd_compatible) { 56 native_hypercall_insn = svm_vmmcall; 57 other_hypercall_insn = vmx_vmcall; 58 } else { 59 GUEST_ASSERT(0); 60 /* unreachable */ 61 return; 62 } 63 64 memcpy(hypercall_insn, other_hypercall_insn, HYPERCALL_INSN_SIZE); 65 66 ret = do_sched_yield(GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID))); 67 68 /* 69 * If the quirk is disabled, verify that guest_ud_handler() "returned" 70 * -EFAULT and that KVM did NOT patch the hypercall. If the quirk is 71 * enabled, verify that the hypercall succeeded and that KVM patched in 72 * the "right" hypercall. 73 */ 74 if (quirk_disabled) { 75 GUEST_ASSERT(ret == (u64)-EFAULT); 76 GUEST_ASSERT(!memcmp(other_hypercall_insn, hypercall_insn, 77 HYPERCALL_INSN_SIZE)); 78 } else { 79 GUEST_ASSERT(!ret); 80 GUEST_ASSERT(!memcmp(native_hypercall_insn, hypercall_insn, 81 HYPERCALL_INSN_SIZE)); 82 } 83 84 GUEST_DONE(); 85} 86 87KVM_ONE_VCPU_TEST_SUITE(fix_hypercall); 88 89static void enter_guest(struct kvm_vcpu *vcpu) 90{ 91 struct kvm_run *run = vcpu->run; 92 struct ucall uc; 93 94 vcpu_run(vcpu); 95 switch (get_ucall(vcpu, &uc)) { 96 case UCALL_SYNC: 97 pr_info("%s: %016lx\n", (const char *)uc.args[2], uc.args[3]); 98 break; 99 case UCALL_DONE: 100 return; 101 case UCALL_ABORT: 102 REPORT_GUEST_ASSERT(uc); 103 default: 104 TEST_FAIL("Unhandled ucall: %ld\nexit_reason: %u (%s)", 105 uc.cmd, run->exit_reason, exit_reason_str(run->exit_reason)); 106 } 107} 108 109static void test_fix_hypercall(struct kvm_vcpu *vcpu, bool disable_quirk) 110{ 111 struct kvm_vm *vm = vcpu->vm; 112 113 vm_install_exception_handler(vcpu->vm, UD_VECTOR, guest_ud_handler); 114 115 if (disable_quirk) 116 vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, 117 KVM_X86_QUIRK_FIX_HYPERCALL_INSN); 118 119 quirk_disabled = disable_quirk; 120 sync_global_to_guest(vm, quirk_disabled); 121 122 virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); 123 124 enter_guest(vcpu); 125} 126 127KVM_ONE_VCPU_TEST(fix_hypercall, enable_quirk, guest_main) 128{ 129 test_fix_hypercall(vcpu, false); 130} 131 132KVM_ONE_VCPU_TEST(fix_hypercall, disable_quirk, guest_main) 133{ 134 test_fix_hypercall(vcpu, true); 135} 136 137int main(int argc, char *argv[]) 138{ 139 TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) & KVM_X86_QUIRK_FIX_HYPERCALL_INSN); 140 141 return test_harness_run(argc, argv); 142}