123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- // SPDX-License-Identifier: GPL-2.0
- // Copyright (C) 2019 Arm Ltd.
- #include <linux/arm-smccc.h>
- #include <linux/kvm_host.h>
- #include <linux/sched/stat.h>
- #include <asm/kvm_mmu.h>
- #include <asm/pvclock-abi.h>
- #include <kvm/arm_hypercalls.h>
- void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
- {
- struct kvm *kvm = vcpu->kvm;
- u64 base = vcpu->arch.steal.base;
- u64 last_steal = vcpu->arch.steal.last_steal;
- u64 offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
- u64 steal = 0;
- int idx;
- if (base == GPA_INVALID)
- return;
- idx = srcu_read_lock(&kvm->srcu);
- if (!kvm_get_guest(kvm, base + offset, steal)) {
- steal = le64_to_cpu(steal);
- vcpu->arch.steal.last_steal = READ_ONCE(current->sched_info.run_delay);
- steal += vcpu->arch.steal.last_steal - last_steal;
- kvm_put_guest(kvm, base + offset, cpu_to_le64(steal));
- }
- srcu_read_unlock(&kvm->srcu, idx);
- }
- long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
- {
- u32 feature = smccc_get_arg1(vcpu);
- long val = SMCCC_RET_NOT_SUPPORTED;
- switch (feature) {
- case ARM_SMCCC_HV_PV_TIME_FEATURES:
- case ARM_SMCCC_HV_PV_TIME_ST:
- if (vcpu->arch.steal.base != GPA_INVALID)
- val = SMCCC_RET_SUCCESS;
- break;
- }
- return val;
- }
- gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)
- {
- struct pvclock_vcpu_stolen_time init_values = {};
- struct kvm *kvm = vcpu->kvm;
- u64 base = vcpu->arch.steal.base;
- if (base == GPA_INVALID)
- return base;
- /*
- * Start counting stolen time from the time the guest requests
- * the feature enabled.
- */
- vcpu->arch.steal.last_steal = current->sched_info.run_delay;
- kvm_write_guest_lock(kvm, base, &init_values, sizeof(init_values));
- return base;
- }
- bool kvm_arm_pvtime_supported(void)
- {
- return !!sched_info_on();
- }
- int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
- struct kvm_device_attr *attr)
- {
- u64 __user *user = (u64 __user *)attr->addr;
- struct kvm *kvm = vcpu->kvm;
- u64 ipa;
- int ret = 0;
- int idx;
- if (!kvm_arm_pvtime_supported() ||
- attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
- return -ENXIO;
- if (get_user(ipa, user))
- return -EFAULT;
- if (!IS_ALIGNED(ipa, 64))
- return -EINVAL;
- if (vcpu->arch.steal.base != GPA_INVALID)
- return -EEXIST;
- /* Check the address is in a valid memslot */
- idx = srcu_read_lock(&kvm->srcu);
- if (kvm_is_error_hva(gfn_to_hva(kvm, ipa >> PAGE_SHIFT)))
- ret = -EINVAL;
- srcu_read_unlock(&kvm->srcu, idx);
- if (!ret)
- vcpu->arch.steal.base = ipa;
- return ret;
- }
- int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
- struct kvm_device_attr *attr)
- {
- u64 __user *user = (u64 __user *)attr->addr;
- u64 ipa;
- if (!kvm_arm_pvtime_supported() ||
- attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
- return -ENXIO;
- ipa = vcpu->arch.steal.base;
- if (put_user(ipa, user))
- return -EFAULT;
- return 0;
- }
- int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
- struct kvm_device_attr *attr)
- {
- switch (attr->attr) {
- case KVM_ARM_VCPU_PVTIME_IPA:
- if (kvm_arm_pvtime_supported())
- return 0;
- }
- return -ENXIO;
- }
|