162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The test validates the basic hypercall functionalities that are exposed 662306a36Sopenharmony_ci * via the psuedo-firmware bitmap register. This includes the registers' 762306a36Sopenharmony_ci * read/write behavior before and after the VM has started, and if the 862306a36Sopenharmony_ci * hypercalls are properly masked or unmasked to the guest when disabled or 962306a36Sopenharmony_ci * enabled from the KVM userspace, respectively. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <errno.h> 1262306a36Sopenharmony_ci#include <linux/arm-smccc.h> 1362306a36Sopenharmony_ci#include <asm/kvm.h> 1462306a36Sopenharmony_ci#include <kvm_util.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "processor.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK(max_feat_bit, 0)) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Last valid bits of the bitmapped firmware registers */ 2162306a36Sopenharmony_ci#define KVM_REG_ARM_STD_BMAP_BIT_MAX 0 2262306a36Sopenharmony_ci#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX 0 2362306a36Sopenharmony_ci#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX 1 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct kvm_fw_reg_info { 2662306a36Sopenharmony_ci uint64_t reg; /* Register definition */ 2762306a36Sopenharmony_ci uint64_t max_feat_bit; /* Bit that represents the upper limit of the feature-map */ 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define FW_REG_INFO(r) \ 3162306a36Sopenharmony_ci { \ 3262306a36Sopenharmony_ci .reg = r, \ 3362306a36Sopenharmony_ci .max_feat_bit = r##_BIT_MAX, \ 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const struct kvm_fw_reg_info fw_reg_info[] = { 3762306a36Sopenharmony_ci FW_REG_INFO(KVM_REG_ARM_STD_BMAP), 3862306a36Sopenharmony_ci FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP), 3962306a36Sopenharmony_ci FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP), 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cienum test_stage { 4362306a36Sopenharmony_ci TEST_STAGE_REG_IFACE, 4462306a36Sopenharmony_ci TEST_STAGE_HVC_IFACE_FEAT_DISABLED, 4562306a36Sopenharmony_ci TEST_STAGE_HVC_IFACE_FEAT_ENABLED, 4662306a36Sopenharmony_ci TEST_STAGE_HVC_IFACE_FALSE_INFO, 4762306a36Sopenharmony_ci TEST_STAGE_END, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int stage = TEST_STAGE_REG_IFACE; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct test_hvc_info { 5362306a36Sopenharmony_ci uint32_t func_id; 5462306a36Sopenharmony_ci uint64_t arg1; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define TEST_HVC_INFO(f, a1) \ 5862306a36Sopenharmony_ci { \ 5962306a36Sopenharmony_ci .func_id = f, \ 6062306a36Sopenharmony_ci .arg1 = a1, \ 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const struct test_hvc_info hvc_info[] = { 6462306a36Sopenharmony_ci /* KVM_REG_ARM_STD_BMAP */ 6562306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0), 6662306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64), 6762306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0), 6862306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0), 6962306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0), 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* KVM_REG_ARM_STD_HYP_BMAP */ 7262306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES), 7362306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST), 7462306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0), 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* KVM_REG_ARM_VENDOR_HYP_BMAP */ 7762306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID, 7862306a36Sopenharmony_ci ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID), 7962306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, 0), 8062306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER), 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Feed false hypercall info to test the KVM behavior */ 8462306a36Sopenharmony_cistatic const struct test_hvc_info false_hvc_info[] = { 8562306a36Sopenharmony_ci /* Feature support check against a different family of hypercalls */ 8662306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID), 8762306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64), 8862306a36Sopenharmony_ci TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64), 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void guest_test_hvc(const struct test_hvc_info *hc_info) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned int i; 9462306a36Sopenharmony_ci struct arm_smccc_res res; 9562306a36Sopenharmony_ci unsigned int hvc_info_arr_sz; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci hvc_info_arr_sz = 9862306a36Sopenharmony_ci hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) { 10162306a36Sopenharmony_ci memset(&res, 0, sizeof(res)); 10262306a36Sopenharmony_ci smccc_hvc(hc_info->func_id, hc_info->arg1, 0, 0, 0, 0, 0, 0, &res); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci switch (stage) { 10562306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FEAT_DISABLED: 10662306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FALSE_INFO: 10762306a36Sopenharmony_ci __GUEST_ASSERT(res.a0 == SMCCC_RET_NOT_SUPPORTED, 10862306a36Sopenharmony_ci "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%llx, stage = %u", 10962306a36Sopenharmony_ci res.a0, hc_info->func_id, hc_info->arg1, stage); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FEAT_ENABLED: 11262306a36Sopenharmony_ci __GUEST_ASSERT(res.a0 != SMCCC_RET_NOT_SUPPORTED, 11362306a36Sopenharmony_ci "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%llx, stage = %u", 11462306a36Sopenharmony_ci res.a0, hc_info->func_id, hc_info->arg1, stage); 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci default: 11762306a36Sopenharmony_ci GUEST_FAIL("Unexpected stage = %u", stage); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void guest_code(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci while (stage != TEST_STAGE_END) { 12562306a36Sopenharmony_ci switch (stage) { 12662306a36Sopenharmony_ci case TEST_STAGE_REG_IFACE: 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FEAT_DISABLED: 12962306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FEAT_ENABLED: 13062306a36Sopenharmony_ci guest_test_hvc(hvc_info); 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FALSE_INFO: 13362306a36Sopenharmony_ci guest_test_hvc(false_hvc_info); 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci default: 13662306a36Sopenharmony_ci GUEST_FAIL("Unexpected stage = %u", stage); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci GUEST_SYNC(stage); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci GUEST_DONE(); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistruct st_time { 14662306a36Sopenharmony_ci uint32_t rev; 14762306a36Sopenharmony_ci uint32_t attr; 14862306a36Sopenharmony_ci uint64_t st_time; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define STEAL_TIME_SIZE ((sizeof(struct st_time) + 63) & ~63) 15262306a36Sopenharmony_ci#define ST_GPA_BASE (1 << 30) 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void steal_time_init(struct kvm_vcpu *vcpu) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci uint64_t st_ipa = (ulong)ST_GPA_BASE; 15762306a36Sopenharmony_ci unsigned int gpages; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE); 16062306a36Sopenharmony_ci vm_userspace_mem_region_add(vcpu->vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PVTIME_CTRL, 16362306a36Sopenharmony_ci KVM_ARM_VCPU_PVTIME_IPA, &st_ipa); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void test_fw_regs_before_vm_start(struct kvm_vcpu *vcpu) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci uint64_t val; 16962306a36Sopenharmony_ci unsigned int i; 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) { 17362306a36Sopenharmony_ci const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i]; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* First 'read' should be an upper limit of the features supported */ 17662306a36Sopenharmony_ci vcpu_get_reg(vcpu, reg_info->reg, &val); 17762306a36Sopenharmony_ci TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit), 17862306a36Sopenharmony_ci "Expected all the features to be set for reg: 0x%lx; expected: 0x%lx; read: 0x%lx\n", 17962306a36Sopenharmony_ci reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit), val); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Test a 'write' by disabling all the features of the register map */ 18262306a36Sopenharmony_ci ret = __vcpu_set_reg(vcpu, reg_info->reg, 0); 18362306a36Sopenharmony_ci TEST_ASSERT(ret == 0, 18462306a36Sopenharmony_ci "Failed to clear all the features of reg: 0x%lx; ret: %d\n", 18562306a36Sopenharmony_ci reg_info->reg, errno); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci vcpu_get_reg(vcpu, reg_info->reg, &val); 18862306a36Sopenharmony_ci TEST_ASSERT(val == 0, 18962306a36Sopenharmony_ci "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * Test enabling a feature that's not supported. 19362306a36Sopenharmony_ci * Avoid this check if all the bits are occupied. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci if (reg_info->max_feat_bit < 63) { 19662306a36Sopenharmony_ci ret = __vcpu_set_reg(vcpu, reg_info->reg, BIT(reg_info->max_feat_bit + 1)); 19762306a36Sopenharmony_ci TEST_ASSERT(ret != 0 && errno == EINVAL, 19862306a36Sopenharmony_ci "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n", 19962306a36Sopenharmony_ci errno, reg_info->reg); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void test_fw_regs_after_vm_start(struct kvm_vcpu *vcpu) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci uint64_t val; 20762306a36Sopenharmony_ci unsigned int i; 20862306a36Sopenharmony_ci int ret; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) { 21162306a36Sopenharmony_ci const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i]; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Before starting the VM, the test clears all the bits. 21562306a36Sopenharmony_ci * Check if that's still the case. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci vcpu_get_reg(vcpu, reg_info->reg, &val); 21862306a36Sopenharmony_ci TEST_ASSERT(val == 0, 21962306a36Sopenharmony_ci "Expected all the features to be cleared for reg: 0x%lx\n", 22062306a36Sopenharmony_ci reg_info->reg); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Since the VM has run at least once, KVM shouldn't allow modification of 22462306a36Sopenharmony_ci * the registers and should return EBUSY. Set the registers and check for 22562306a36Sopenharmony_ci * the expected errno. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci ret = __vcpu_set_reg(vcpu, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit)); 22862306a36Sopenharmony_ci TEST_ASSERT(ret != 0 && errno == EBUSY, 22962306a36Sopenharmony_ci "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n", 23062306a36Sopenharmony_ci errno, reg_info->reg); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic struct kvm_vm *test_vm_create(struct kvm_vcpu **vcpu) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct kvm_vm *vm; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci vm = vm_create_with_one_vcpu(vcpu, guest_code); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci steal_time_init(*vcpu); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return vm; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void test_guest_stage(struct kvm_vm **vm, struct kvm_vcpu **vcpu) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci int prev_stage = stage; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci pr_debug("Stage: %d\n", prev_stage); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Sync the stage early, the VM might be freed below. */ 25262306a36Sopenharmony_ci stage++; 25362306a36Sopenharmony_ci sync_global_to_guest(*vm, stage); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci switch (prev_stage) { 25662306a36Sopenharmony_ci case TEST_STAGE_REG_IFACE: 25762306a36Sopenharmony_ci test_fw_regs_after_vm_start(*vcpu); 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FEAT_DISABLED: 26062306a36Sopenharmony_ci /* Start a new VM so that all the features are now enabled by default */ 26162306a36Sopenharmony_ci kvm_vm_free(*vm); 26262306a36Sopenharmony_ci *vm = test_vm_create(vcpu); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FEAT_ENABLED: 26562306a36Sopenharmony_ci case TEST_STAGE_HVC_IFACE_FALSE_INFO: 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci default: 26862306a36Sopenharmony_ci TEST_FAIL("Unknown test stage: %d\n", prev_stage); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void test_run(void) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 27562306a36Sopenharmony_ci struct kvm_vm *vm; 27662306a36Sopenharmony_ci struct ucall uc; 27762306a36Sopenharmony_ci bool guest_done = false; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci vm = test_vm_create(&vcpu); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci test_fw_regs_before_vm_start(vcpu); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci while (!guest_done) { 28462306a36Sopenharmony_ci vcpu_run(vcpu); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (get_ucall(vcpu, &uc)) { 28762306a36Sopenharmony_ci case UCALL_SYNC: 28862306a36Sopenharmony_ci test_guest_stage(&vm, &vcpu); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case UCALL_DONE: 29162306a36Sopenharmony_ci guest_done = true; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case UCALL_ABORT: 29462306a36Sopenharmony_ci REPORT_GUEST_ASSERT(uc); 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci default: 29762306a36Sopenharmony_ci TEST_FAIL("Unexpected guest exit\n"); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci kvm_vm_free(vm); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint main(void) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci test_run(); 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 309