0001
0002
0003
0004
0005
0006
0007
0008 #define _GNU_SOURCE
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
0031
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
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
0045 GET_RM(insn_bytes[1]) != 0x4 &&
0046
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
0083
0084
0085
0086 vcpu_regs_get(vcpu, ®s);
0087 regs.rip += 2;
0088 vcpu_regs_set(vcpu, ®s);
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
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 }