Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2020, Google LLC.
0004  *
0005  * Tests for KVM_CAP_EXIT_ON_EMULATION_FAILURE capability.
0006  */
0007 
0008 #define _GNU_SOURCE /* for program_invocation_short_name */
0009 
0010 #include "test_util.h"
0011 #include "kvm_util.h"
0012 #include "vmx.h"
0013 
0014 #define MAXPHYADDR 36
0015 
0016 #define MEM_REGION_GVA  0x0000123456789000
0017 #define MEM_REGION_GPA  0x0000000700000000
0018 #define MEM_REGION_SLOT 10
0019 #define MEM_REGION_SIZE PAGE_SIZE
0020 
0021 static void guest_code(void)
0022 {
0023     __asm__ __volatile__("flds (%[addr])"
0024                  :: [addr]"r"(MEM_REGION_GVA));
0025 
0026     GUEST_DONE();
0027 }
0028 
0029 /*
0030  * Accessors to get R/M, REG, and Mod bits described in the SDM vol 2,
0031  * figure 2-2 "Table Interpretation of ModR/M Byte (C8H)".
0032  */
0033 #define GET_RM(insn_byte) (insn_byte & 0x7)
0034 #define GET_REG(insn_byte) ((insn_byte & 0x38) >> 3)
0035 #define GET_MOD(insn_byte) ((insn_byte & 0xc) >> 6)
0036 
0037 /* Ensure we are dealing with a simple 2-byte flds instruction. */
0038 static bool is_flds(uint8_t *insn_bytes, uint8_t insn_size)
0039 {
0040     return insn_size >= 2 &&
0041            insn_bytes[0] == 0xd9 &&
0042            GET_REG(insn_bytes[1]) == 0x0 &&
0043            GET_MOD(insn_bytes[1]) == 0x0 &&
0044            /* Ensure there is no SIB byte. */
0045            GET_RM(insn_bytes[1]) != 0x4 &&
0046            /* Ensure there is no displacement byte. */
0047            GET_RM(insn_bytes[1]) != 0x5;
0048 }
0049 
0050 static void process_exit_on_emulation_error(struct kvm_vcpu *vcpu)
0051 {
0052     struct kvm_run *run = vcpu->run;
0053     struct kvm_regs regs;
0054     uint8_t *insn_bytes;
0055     uint8_t insn_size;
0056     uint64_t flags;
0057 
0058     TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
0059             "Unexpected exit reason: %u (%s)",
0060             run->exit_reason,
0061             exit_reason_str(run->exit_reason));
0062 
0063     TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
0064             "Unexpected suberror: %u",
0065             run->emulation_failure.suberror);
0066 
0067     if (run->emulation_failure.ndata >= 1) {
0068         flags = run->emulation_failure.flags;
0069         if ((flags & KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES) &&
0070             run->emulation_failure.ndata >= 3) {
0071             insn_size = run->emulation_failure.insn_size;
0072             insn_bytes = run->emulation_failure.insn_bytes;
0073 
0074             TEST_ASSERT(insn_size <= 15 && insn_size > 0,
0075                     "Unexpected instruction size: %u",
0076                     insn_size);
0077 
0078             TEST_ASSERT(is_flds(insn_bytes, insn_size),
0079                     "Unexpected instruction.  Expected 'flds' (0xd9 /0)");
0080 
0081             /*
0082              * If is_flds() succeeded then the instruction bytes
0083              * contained an flds instruction that is 2-bytes in
0084              * length (ie: no prefix, no SIB, no displacement).
0085              */
0086             vcpu_regs_get(vcpu, &regs);
0087             regs.rip += 2;
0088             vcpu_regs_set(vcpu, &regs);
0089         }
0090     }
0091 }
0092 
0093 static void do_guest_assert(struct ucall *uc)
0094 {
0095     REPORT_GUEST_ASSERT(*uc);
0096 }
0097 
0098 static void check_for_guest_assert(struct kvm_vcpu *vcpu)
0099 {
0100     struct ucall uc;
0101 
0102     if (vcpu->run->exit_reason == KVM_EXIT_IO &&
0103         get_ucall(vcpu, &uc) == UCALL_ABORT) {
0104         do_guest_assert(&uc);
0105     }
0106 }
0107 
0108 static void process_ucall_done(struct kvm_vcpu *vcpu)
0109 {
0110     struct kvm_run *run = vcpu->run;
0111     struct ucall uc;
0112 
0113     check_for_guest_assert(vcpu);
0114 
0115     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0116             "Unexpected exit reason: %u (%s)",
0117             run->exit_reason,
0118             exit_reason_str(run->exit_reason));
0119 
0120     TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE,
0121             "Unexpected ucall command: %lu, expected UCALL_DONE (%d)",
0122             uc.cmd, UCALL_DONE);
0123 }
0124 
0125 static uint64_t process_ucall(struct kvm_vcpu *vcpu)
0126 {
0127     struct kvm_run *run = vcpu->run;
0128     struct ucall uc;
0129 
0130     TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0131             "Unexpected exit reason: %u (%s)",
0132             run->exit_reason,
0133             exit_reason_str(run->exit_reason));
0134 
0135     switch (get_ucall(vcpu, &uc)) {
0136     case UCALL_SYNC:
0137         break;
0138     case UCALL_ABORT:
0139         do_guest_assert(&uc);
0140         break;
0141     case UCALL_DONE:
0142         process_ucall_done(vcpu);
0143         break;
0144     default:
0145         TEST_ASSERT(false, "Unexpected ucall");
0146     }
0147 
0148     return uc.cmd;
0149 }
0150 
0151 int main(int argc, char *argv[])
0152 {
0153     struct kvm_vcpu *vcpu;
0154     struct kvm_vm *vm;
0155     uint64_t gpa, pte;
0156     uint64_t *hva;
0157     int rc;
0158 
0159     /* Tell stdout not to buffer its content */
0160     setbuf(stdout, NULL);
0161 
0162     TEST_REQUIRE(kvm_has_cap(KVM_CAP_SMALLER_MAXPHYADDR));
0163 
0164     vm = vm_create_with_one_vcpu(&vcpu, guest_code);
0165 
0166     vcpu_set_cpuid_maxphyaddr(vcpu, MAXPHYADDR);
0167 
0168     rc = kvm_check_cap(KVM_CAP_EXIT_ON_EMULATION_FAILURE);
0169     TEST_ASSERT(rc, "KVM_CAP_EXIT_ON_EMULATION_FAILURE is unavailable");
0170     vm_enable_cap(vm, KVM_CAP_EXIT_ON_EMULATION_FAILURE, 1);
0171 
0172     vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
0173                     MEM_REGION_GPA, MEM_REGION_SLOT,
0174                     MEM_REGION_SIZE / PAGE_SIZE, 0);
0175     gpa = vm_phy_pages_alloc(vm, MEM_REGION_SIZE / PAGE_SIZE,
0176                  MEM_REGION_GPA, MEM_REGION_SLOT);
0177     TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc\n");
0178     virt_map(vm, MEM_REGION_GVA, MEM_REGION_GPA, 1);
0179     hva = addr_gpa2hva(vm, MEM_REGION_GPA);
0180     memset(hva, 0, PAGE_SIZE);
0181     pte = vm_get_page_table_entry(vm, vcpu, MEM_REGION_GVA);
0182     vm_set_page_table_entry(vm, vcpu, MEM_REGION_GVA, pte | (1ull << 36));
0183 
0184     vcpu_run(vcpu);
0185     process_exit_on_emulation_error(vcpu);
0186     vcpu_run(vcpu);
0187 
0188     TEST_ASSERT(process_ucall(vcpu) == UCALL_DONE, "Expected UCALL_DONE");
0189 
0190     kvm_vm_free(vm);
0191 
0192     return 0;
0193 }