162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (C) 2019 Arm Ltd. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/arm-smccc.h> 562306a36Sopenharmony_ci#include <linux/kvm_host.h> 662306a36Sopenharmony_ci#include <linux/sched/stat.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/kvm_mmu.h> 962306a36Sopenharmony_ci#include <asm/pvclock-abi.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <kvm/arm_hypercalls.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_civoid kvm_update_stolen_time(struct kvm_vcpu *vcpu) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 1662306a36Sopenharmony_ci u64 base = vcpu->arch.steal.base; 1762306a36Sopenharmony_ci u64 last_steal = vcpu->arch.steal.last_steal; 1862306a36Sopenharmony_ci u64 offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time); 1962306a36Sopenharmony_ci u64 steal = 0; 2062306a36Sopenharmony_ci int idx; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci if (base == INVALID_GPA) 2362306a36Sopenharmony_ci return; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci idx = srcu_read_lock(&kvm->srcu); 2662306a36Sopenharmony_ci if (!kvm_get_guest(kvm, base + offset, steal)) { 2762306a36Sopenharmony_ci steal = le64_to_cpu(steal); 2862306a36Sopenharmony_ci vcpu->arch.steal.last_steal = READ_ONCE(current->sched_info.run_delay); 2962306a36Sopenharmony_ci steal += vcpu->arch.steal.last_steal - last_steal; 3062306a36Sopenharmony_ci kvm_put_guest(kvm, base + offset, cpu_to_le64(steal)); 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci srcu_read_unlock(&kvm->srcu, idx); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cilong kvm_hypercall_pv_features(struct kvm_vcpu *vcpu) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci u32 feature = smccc_get_arg1(vcpu); 3862306a36Sopenharmony_ci long val = SMCCC_RET_NOT_SUPPORTED; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci switch (feature) { 4162306a36Sopenharmony_ci case ARM_SMCCC_HV_PV_TIME_FEATURES: 4262306a36Sopenharmony_ci case ARM_SMCCC_HV_PV_TIME_ST: 4362306a36Sopenharmony_ci if (vcpu->arch.steal.base != INVALID_GPA) 4462306a36Sopenharmony_ci val = SMCCC_RET_SUCCESS; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return val; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cigpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct pvclock_vcpu_stolen_time init_values = {}; 5462306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 5562306a36Sopenharmony_ci u64 base = vcpu->arch.steal.base; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (base == INVALID_GPA) 5862306a36Sopenharmony_ci return base; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * Start counting stolen time from the time the guest requests 6262306a36Sopenharmony_ci * the feature enabled. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci vcpu->arch.steal.last_steal = current->sched_info.run_delay; 6562306a36Sopenharmony_ci kvm_write_guest_lock(kvm, base, &init_values, sizeof(init_values)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return base; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cibool kvm_arm_pvtime_supported(void) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return !!sched_info_on(); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciint kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu, 7662306a36Sopenharmony_ci struct kvm_device_attr *attr) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u64 __user *user = (u64 __user *)attr->addr; 7962306a36Sopenharmony_ci struct kvm *kvm = vcpu->kvm; 8062306a36Sopenharmony_ci u64 ipa; 8162306a36Sopenharmony_ci int ret = 0; 8262306a36Sopenharmony_ci int idx; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!kvm_arm_pvtime_supported() || 8562306a36Sopenharmony_ci attr->attr != KVM_ARM_VCPU_PVTIME_IPA) 8662306a36Sopenharmony_ci return -ENXIO; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (get_user(ipa, user)) 8962306a36Sopenharmony_ci return -EFAULT; 9062306a36Sopenharmony_ci if (!IS_ALIGNED(ipa, 64)) 9162306a36Sopenharmony_ci return -EINVAL; 9262306a36Sopenharmony_ci if (vcpu->arch.steal.base != INVALID_GPA) 9362306a36Sopenharmony_ci return -EEXIST; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Check the address is in a valid memslot */ 9662306a36Sopenharmony_ci idx = srcu_read_lock(&kvm->srcu); 9762306a36Sopenharmony_ci if (kvm_is_error_hva(gfn_to_hva(kvm, ipa >> PAGE_SHIFT))) 9862306a36Sopenharmony_ci ret = -EINVAL; 9962306a36Sopenharmony_ci srcu_read_unlock(&kvm->srcu, idx); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!ret) 10262306a36Sopenharmony_ci vcpu->arch.steal.base = ipa; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciint kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu, 10862306a36Sopenharmony_ci struct kvm_device_attr *attr) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci u64 __user *user = (u64 __user *)attr->addr; 11162306a36Sopenharmony_ci u64 ipa; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!kvm_arm_pvtime_supported() || 11462306a36Sopenharmony_ci attr->attr != KVM_ARM_VCPU_PVTIME_IPA) 11562306a36Sopenharmony_ci return -ENXIO; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci ipa = vcpu->arch.steal.base; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (put_user(ipa, user)) 12062306a36Sopenharmony_ci return -EFAULT; 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciint kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu, 12562306a36Sopenharmony_ci struct kvm_device_attr *attr) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci switch (attr->attr) { 12862306a36Sopenharmony_ci case KVM_ARM_VCPU_PVTIME_IPA: 12962306a36Sopenharmony_ci if (kvm_arm_pvtime_supported()) 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci return -ENXIO; 13362306a36Sopenharmony_ci} 134