Path: blob/master/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2020, Google LLC.3*4* Tests for KVM paravirtual feature disablement5*/6#include <asm/kvm_para.h>7#include <linux/kvm_para.h>8#include <linux/stringify.h>9#include <stdint.h>1011#include "kvm_test_harness.h"12#include "apic.h"13#include "test_util.h"14#include "kvm_util.h"15#include "processor.h"1617/* VMCALL and VMMCALL are both 3-byte opcodes. */18#define HYPERCALL_INSN_SIZE 31920static bool quirk_disabled;2122static void guest_ud_handler(struct ex_regs *regs)23{24regs->rax = -EFAULT;25regs->rip += HYPERCALL_INSN_SIZE;26}2728static const uint8_t vmx_vmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xc1 };29static const uint8_t svm_vmmcall[HYPERCALL_INSN_SIZE] = { 0x0f, 0x01, 0xd9 };3031extern uint8_t hypercall_insn[HYPERCALL_INSN_SIZE];32static uint64_t do_sched_yield(uint8_t apic_id)33{34uint64_t ret;3536asm volatile("hypercall_insn:\n\t"37".byte 0xcc,0xcc,0xcc\n\t"38: "=a"(ret)39: "a"((uint64_t)KVM_HC_SCHED_YIELD), "b"((uint64_t)apic_id)40: "memory");4142return ret;43}4445static void guest_main(void)46{47const uint8_t *native_hypercall_insn;48const uint8_t *other_hypercall_insn;49uint64_t ret;5051if (host_cpu_is_intel) {52native_hypercall_insn = vmx_vmcall;53other_hypercall_insn = svm_vmmcall;54} else if (host_cpu_is_amd) {55native_hypercall_insn = svm_vmmcall;56other_hypercall_insn = vmx_vmcall;57} else {58GUEST_ASSERT(0);59/* unreachable */60return;61}6263memcpy(hypercall_insn, other_hypercall_insn, HYPERCALL_INSN_SIZE);6465ret = do_sched_yield(GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID)));6667/*68* If the quirk is disabled, verify that guest_ud_handler() "returned"69* -EFAULT and that KVM did NOT patch the hypercall. If the quirk is70* enabled, verify that the hypercall succeeded and that KVM patched in71* the "right" hypercall.72*/73if (quirk_disabled) {74GUEST_ASSERT(ret == (uint64_t)-EFAULT);75GUEST_ASSERT(!memcmp(other_hypercall_insn, hypercall_insn,76HYPERCALL_INSN_SIZE));77} else {78GUEST_ASSERT(!ret);79GUEST_ASSERT(!memcmp(native_hypercall_insn, hypercall_insn,80HYPERCALL_INSN_SIZE));81}8283GUEST_DONE();84}8586KVM_ONE_VCPU_TEST_SUITE(fix_hypercall);8788static void enter_guest(struct kvm_vcpu *vcpu)89{90struct kvm_run *run = vcpu->run;91struct ucall uc;9293vcpu_run(vcpu);94switch (get_ucall(vcpu, &uc)) {95case UCALL_SYNC:96pr_info("%s: %016lx\n", (const char *)uc.args[2], uc.args[3]);97break;98case UCALL_DONE:99return;100case UCALL_ABORT:101REPORT_GUEST_ASSERT(uc);102default:103TEST_FAIL("Unhandled ucall: %ld\nexit_reason: %u (%s)",104uc.cmd, run->exit_reason, exit_reason_str(run->exit_reason));105}106}107108static void test_fix_hypercall(struct kvm_vcpu *vcpu, bool disable_quirk)109{110struct kvm_vm *vm = vcpu->vm;111112vm_install_exception_handler(vcpu->vm, UD_VECTOR, guest_ud_handler);113114if (disable_quirk)115vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2,116KVM_X86_QUIRK_FIX_HYPERCALL_INSN);117118quirk_disabled = disable_quirk;119sync_global_to_guest(vm, quirk_disabled);120121virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);122123enter_guest(vcpu);124}125126KVM_ONE_VCPU_TEST(fix_hypercall, enable_quirk, guest_main)127{128test_fix_hypercall(vcpu, false);129}130131KVM_ONE_VCPU_TEST(fix_hypercall, disable_quirk, guest_main)132{133test_fix_hypercall(vcpu, true);134}135136int main(int argc, char *argv[])137{138TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) & KVM_X86_QUIRK_FIX_HYPERCALL_INSN);139140return test_harness_run(argc, argv);141}142143144