Path: blob/master/tools/testing/selftests/kvm/x86/nested_emulation_test.c
38235 views
// SPDX-License-Identifier: GPL-2.0-only1#include "test_util.h"2#include "kvm_util.h"3#include "processor.h"4#include "vmx.h"5#include "svm_util.h"67enum {8SVM_F,9VMX_F,10NR_VIRTUALIZATION_FLAVORS,11};1213struct emulated_instruction {14const char name[32];15uint8_t opcode[15];16uint32_t exit_reason[NR_VIRTUALIZATION_FLAVORS];17};1819static struct emulated_instruction instructions[] = {20{21.name = "pause",22.opcode = { 0xf3, 0x90 },23.exit_reason = { SVM_EXIT_PAUSE,24EXIT_REASON_PAUSE_INSTRUCTION, }25},26{27.name = "hlt",28.opcode = { 0xf4 },29.exit_reason = { SVM_EXIT_HLT,30EXIT_REASON_HLT, }31},32};3334static uint8_t kvm_fep[] = { 0x0f, 0x0b, 0x6b, 0x76, 0x6d }; /* ud2 ; .ascii "kvm" */35static uint8_t l2_guest_code[sizeof(kvm_fep) + 15];36static uint8_t *l2_instruction = &l2_guest_code[sizeof(kvm_fep)];3738static uint32_t get_instruction_length(struct emulated_instruction *insn)39{40uint32_t i;4142for (i = 0; i < ARRAY_SIZE(insn->opcode) && insn->opcode[i]; i++)43;4445return i;46}4748static void guest_code(void *test_data)49{50int f = this_cpu_has(X86_FEATURE_SVM) ? SVM_F : VMX_F;51int i;5253memcpy(l2_guest_code, kvm_fep, sizeof(kvm_fep));5455if (f == SVM_F) {56struct svm_test_data *svm = test_data;57struct vmcb *vmcb = svm->vmcb;5859generic_svm_setup(svm, NULL, NULL);60vmcb->save.idtr.limit = 0;61vmcb->save.rip = (u64)l2_guest_code;6263vmcb->control.intercept |= BIT_ULL(INTERCEPT_SHUTDOWN) |64BIT_ULL(INTERCEPT_PAUSE) |65BIT_ULL(INTERCEPT_HLT);66vmcb->control.intercept_exceptions = 0;67} else {68GUEST_ASSERT(prepare_for_vmx_operation(test_data));69GUEST_ASSERT(load_vmcs(test_data));7071prepare_vmcs(test_data, NULL, NULL);72GUEST_ASSERT(!vmwrite(GUEST_IDTR_LIMIT, 0));73GUEST_ASSERT(!vmwrite(GUEST_RIP, (u64)l2_guest_code));74GUEST_ASSERT(!vmwrite(EXCEPTION_BITMAP, 0));7576vmwrite(CPU_BASED_VM_EXEC_CONTROL, vmreadz(CPU_BASED_VM_EXEC_CONTROL) |77CPU_BASED_PAUSE_EXITING |78CPU_BASED_HLT_EXITING);79}8081for (i = 0; i < ARRAY_SIZE(instructions); i++) {82struct emulated_instruction *insn = &instructions[i];83uint32_t insn_len = get_instruction_length(insn);84uint32_t exit_insn_len;85u32 exit_reason;8687/*88* Copy the target instruction to the L2 code stream, and fill89* the remaining bytes with INT3s so that a missed intercept90* results in a consistent failure mode (SHUTDOWN).91*/92memcpy(l2_instruction, insn->opcode, insn_len);93memset(l2_instruction + insn_len, 0xcc, sizeof(insn->opcode) - insn_len);9495if (f == SVM_F) {96struct svm_test_data *svm = test_data;97struct vmcb *vmcb = svm->vmcb;9899run_guest(vmcb, svm->vmcb_gpa);100exit_reason = vmcb->control.exit_code;101exit_insn_len = vmcb->control.next_rip - vmcb->save.rip;102GUEST_ASSERT_EQ(vmcb->save.rip, (u64)l2_instruction);103} else {104GUEST_ASSERT_EQ(i ? vmresume() : vmlaunch(), 0);105exit_reason = vmreadz(VM_EXIT_REASON);106exit_insn_len = vmreadz(VM_EXIT_INSTRUCTION_LEN);107GUEST_ASSERT_EQ(vmreadz(GUEST_RIP), (u64)l2_instruction);108}109110__GUEST_ASSERT(exit_reason == insn->exit_reason[f],111"Wanted exit_reason '0x%x' for '%s', got '0x%x'",112insn->exit_reason[f], insn->name, exit_reason);113114__GUEST_ASSERT(exit_insn_len == insn_len,115"Wanted insn_len '%u' for '%s', got '%u'",116insn_len, insn->name, exit_insn_len);117}118119GUEST_DONE();120}121122int main(int argc, char *argv[])123{124vm_vaddr_t nested_test_data_gva;125struct kvm_vcpu *vcpu;126struct kvm_vm *vm;127128TEST_REQUIRE(is_forced_emulation_enabled);129TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX));130131vm = vm_create_with_one_vcpu(&vcpu, guest_code);132vm_enable_cap(vm, KVM_CAP_EXCEPTION_PAYLOAD, -2ul);133134if (kvm_cpu_has(X86_FEATURE_SVM))135vcpu_alloc_svm(vm, &nested_test_data_gva);136else137vcpu_alloc_vmx(vm, &nested_test_data_gva);138139vcpu_args_set(vcpu, 1, nested_test_data_gva);140141vcpu_run(vcpu);142TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE);143144kvm_vm_free(vm);145}146147148