18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2019 Arm Ltd. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 58c2ecf20Sopenharmony_ci#include <linux/kvm_host.h> 68c2ecf20Sopenharmony_ci#include <linux/sched/stat.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/kvm_mmu.h> 98c2ecf20Sopenharmony_ci#include <asm/pvclock-abi.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <kvm/arm_hypercalls.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_civoid kvm_update_stolen_time(struct kvm_vcpu *vcpu) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 168c2ecf20Sopenharmony_ci u64 base = vcpu->arch.steal.base; 178c2ecf20Sopenharmony_ci u64 last_steal = vcpu->arch.steal.last_steal; 188c2ecf20Sopenharmony_ci u64 offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time); 198c2ecf20Sopenharmony_ci u64 steal = 0; 208c2ecf20Sopenharmony_ci int idx; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci if (base == GPA_INVALID) 238c2ecf20Sopenharmony_ci return; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci idx = srcu_read_lock(&kvm->srcu); 268c2ecf20Sopenharmony_ci if (!kvm_get_guest(kvm, base + offset, steal)) { 278c2ecf20Sopenharmony_ci steal = le64_to_cpu(steal); 288c2ecf20Sopenharmony_ci vcpu->arch.steal.last_steal = READ_ONCE(current->sched_info.run_delay); 298c2ecf20Sopenharmony_ci steal += vcpu->arch.steal.last_steal - last_steal; 308c2ecf20Sopenharmony_ci kvm_put_guest(kvm, base + offset, cpu_to_le64(steal)); 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci srcu_read_unlock(&kvm->srcu, idx); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cilong kvm_hypercall_pv_features(struct kvm_vcpu *vcpu) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci u32 feature = smccc_get_arg1(vcpu); 388c2ecf20Sopenharmony_ci long val = SMCCC_RET_NOT_SUPPORTED; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci switch (feature) { 418c2ecf20Sopenharmony_ci case ARM_SMCCC_HV_PV_TIME_FEATURES: 428c2ecf20Sopenharmony_ci case ARM_SMCCC_HV_PV_TIME_ST: 438c2ecf20Sopenharmony_ci if (vcpu->arch.steal.base != GPA_INVALID) 448c2ecf20Sopenharmony_ci val = SMCCC_RET_SUCCESS; 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return val; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cigpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct pvclock_vcpu_stolen_time init_values = {}; 548c2ecf20Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 558c2ecf20Sopenharmony_ci u64 base = vcpu->arch.steal.base; 568c2ecf20Sopenharmony_ci int idx; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (base == GPA_INVALID) 598c2ecf20Sopenharmony_ci return base; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* 628c2ecf20Sopenharmony_ci * Start counting stolen time from the time the guest requests 638c2ecf20Sopenharmony_ci * the feature enabled. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci vcpu->arch.steal.last_steal = current->sched_info.run_delay; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci idx = srcu_read_lock(&kvm->srcu); 688c2ecf20Sopenharmony_ci kvm_write_guest(kvm, base, &init_values, sizeof(init_values)); 698c2ecf20Sopenharmony_ci srcu_read_unlock(&kvm->srcu, idx); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return base; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cibool kvm_arm_pvtime_supported(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci return !!sched_info_on(); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciint kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu, 808c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci u64 __user *user = (u64 __user *)attr->addr; 838c2ecf20Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 848c2ecf20Sopenharmony_ci u64 ipa; 858c2ecf20Sopenharmony_ci int ret = 0; 868c2ecf20Sopenharmony_ci int idx; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!kvm_arm_pvtime_supported() || 898c2ecf20Sopenharmony_ci attr->attr != KVM_ARM_VCPU_PVTIME_IPA) 908c2ecf20Sopenharmony_ci return -ENXIO; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (get_user(ipa, user)) 938c2ecf20Sopenharmony_ci return -EFAULT; 948c2ecf20Sopenharmony_ci if (!IS_ALIGNED(ipa, 64)) 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci if (vcpu->arch.steal.base != GPA_INVALID) 978c2ecf20Sopenharmony_ci return -EEXIST; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Check the address is in a valid memslot */ 1008c2ecf20Sopenharmony_ci idx = srcu_read_lock(&kvm->srcu); 1018c2ecf20Sopenharmony_ci if (kvm_is_error_hva(gfn_to_hva(kvm, ipa >> PAGE_SHIFT))) 1028c2ecf20Sopenharmony_ci ret = -EINVAL; 1038c2ecf20Sopenharmony_ci srcu_read_unlock(&kvm->srcu, idx); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!ret) 1068c2ecf20Sopenharmony_ci vcpu->arch.steal.base = ipa; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciint kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu, 1128c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci u64 __user *user = (u64 __user *)attr->addr; 1158c2ecf20Sopenharmony_ci u64 ipa; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (!kvm_arm_pvtime_supported() || 1188c2ecf20Sopenharmony_ci attr->attr != KVM_ARM_VCPU_PVTIME_IPA) 1198c2ecf20Sopenharmony_ci return -ENXIO; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ipa = vcpu->arch.steal.base; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (put_user(ipa, user)) 1248c2ecf20Sopenharmony_ci return -EFAULT; 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu, 1298c2ecf20Sopenharmony_ci struct kvm_device_attr *attr) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci switch (attr->attr) { 1328c2ecf20Sopenharmony_ci case KVM_ARM_VCPU_PVTIME_IPA: 1338c2ecf20Sopenharmony_ci if (kvm_arm_pvtime_supported()) 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci return -ENXIO; 1378c2ecf20Sopenharmony_ci} 138