1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020, Google LLC. 4 * 5 * Test that KVM emulates instructions in response to EPT violations when 6 * allow_smaller_maxphyaddr is enabled and guest.MAXPHYADDR < host.MAXPHYADDR. 7 */ 8 9#define _GNU_SOURCE /* for program_invocation_short_name */ 10 11#include "flds_emulation.h" 12 13#include "test_util.h" 14#include "kvm_util.h" 15#include "vmx.h" 16 17#define MAXPHYADDR 36 18 19#define MEM_REGION_GVA 0x0000123456789000 20#define MEM_REGION_GPA 0x0000000700000000 21#define MEM_REGION_SLOT 10 22#define MEM_REGION_SIZE PAGE_SIZE 23 24static void guest_code(bool tdp_enabled) 25{ 26 uint64_t error_code; 27 uint64_t vector; 28 29 vector = kvm_asm_safe_ec(FLDS_MEM_EAX, error_code, "a"(MEM_REGION_GVA)); 30 31 /* 32 * When TDP is enabled, flds will trigger an emulation failure, exit to 33 * userspace, and then the selftest host "VMM" skips the instruction. 34 * 35 * When TDP is disabled, no instruction emulation is required so flds 36 * should generate #PF(RSVD). 37 */ 38 if (tdp_enabled) { 39 GUEST_ASSERT(!vector); 40 } else { 41 GUEST_ASSERT_EQ(vector, PF_VECTOR); 42 GUEST_ASSERT(error_code & PFERR_RSVD_MASK); 43 } 44 45 GUEST_DONE(); 46} 47 48int main(int argc, char *argv[]) 49{ 50 struct kvm_vcpu *vcpu; 51 struct kvm_vm *vm; 52 struct ucall uc; 53 uint64_t *pte; 54 uint64_t *hva; 55 uint64_t gpa; 56 int rc; 57 58 TEST_REQUIRE(kvm_has_cap(KVM_CAP_SMALLER_MAXPHYADDR)); 59 60 vm = vm_create_with_one_vcpu(&vcpu, guest_code); 61 vcpu_args_set(vcpu, 1, kvm_is_tdp_enabled()); 62 63 vm_init_descriptor_tables(vm); 64 vcpu_init_descriptor_tables(vcpu); 65 66 vcpu_set_cpuid_property(vcpu, X86_PROPERTY_MAX_PHY_ADDR, MAXPHYADDR); 67 68 rc = kvm_check_cap(KVM_CAP_EXIT_ON_EMULATION_FAILURE); 69 TEST_ASSERT(rc, "KVM_CAP_EXIT_ON_EMULATION_FAILURE is unavailable"); 70 vm_enable_cap(vm, KVM_CAP_EXIT_ON_EMULATION_FAILURE, 1); 71 72 vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 73 MEM_REGION_GPA, MEM_REGION_SLOT, 74 MEM_REGION_SIZE / PAGE_SIZE, 0); 75 gpa = vm_phy_pages_alloc(vm, MEM_REGION_SIZE / PAGE_SIZE, 76 MEM_REGION_GPA, MEM_REGION_SLOT); 77 TEST_ASSERT(gpa == MEM_REGION_GPA, "Failed vm_phy_pages_alloc"); 78 virt_map(vm, MEM_REGION_GVA, MEM_REGION_GPA, 1); 79 hva = addr_gpa2hva(vm, MEM_REGION_GPA); 80 memset(hva, 0, PAGE_SIZE); 81 82 pte = vm_get_page_table_entry(vm, MEM_REGION_GVA); 83 *pte |= BIT_ULL(MAXPHYADDR); 84 85 vcpu_run(vcpu); 86 87 /* 88 * When TDP is enabled, KVM must emulate in response the guest physical 89 * address that is illegal from the guest's perspective, but is legal 90 * from hardware's perspeective. This should result in an emulation 91 * failure exit to userspace since KVM doesn't support emulating flds. 92 */ 93 if (kvm_is_tdp_enabled()) { 94 handle_flds_emulation_failure_exit(vcpu); 95 vcpu_run(vcpu); 96 } 97 98 switch (get_ucall(vcpu, &uc)) { 99 case UCALL_ABORT: 100 REPORT_GUEST_ASSERT(uc); 101 break; 102 case UCALL_DONE: 103 break; 104 default: 105 TEST_FAIL("Unrecognized ucall: %lu", uc.cmd); 106 } 107 108 kvm_vm_free(vm); 109 110 return 0; 111} 112