19ed24f4bSMarc Zyngier // SPDX-License-Identifier: GPL-2.0
29ed24f4bSMarc Zyngier // Copyright (C) 2019 Arm Ltd.
39ed24f4bSMarc Zyngier
49ed24f4bSMarc Zyngier #include <linux/arm-smccc.h>
59ed24f4bSMarc Zyngier #include <linux/kvm_host.h>
6a25e9102SAndrew Jones #include <linux/sched/stat.h>
79ed24f4bSMarc Zyngier
89ed24f4bSMarc Zyngier #include <asm/kvm_mmu.h>
99ed24f4bSMarc Zyngier #include <asm/pvclock-abi.h>
109ed24f4bSMarc Zyngier
119ed24f4bSMarc Zyngier #include <kvm/arm_hypercalls.h>
129ed24f4bSMarc Zyngier
kvm_update_stolen_time(struct kvm_vcpu * vcpu)139ed24f4bSMarc Zyngier void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
149ed24f4bSMarc Zyngier {
159ed24f4bSMarc Zyngier struct kvm *kvm = vcpu->kvm;
169ed24f4bSMarc Zyngier u64 base = vcpu->arch.steal.base;
1753f98558SAndrew Jones u64 last_steal = vcpu->arch.steal.last_steal;
1853f98558SAndrew Jones u64 offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
1953f98558SAndrew Jones u64 steal = 0;
2053f98558SAndrew Jones int idx;
219ed24f4bSMarc Zyngier
22*cecafc0aSYu Zhang if (base == INVALID_GPA)
239ed24f4bSMarc Zyngier return;
249ed24f4bSMarc Zyngier
2553f98558SAndrew Jones idx = srcu_read_lock(&kvm->srcu);
2653f98558SAndrew Jones if (!kvm_get_guest(kvm, base + offset, steal)) {
2753f98558SAndrew Jones steal = le64_to_cpu(steal);
282dbd780eSAndrew Jones vcpu->arch.steal.last_steal = READ_ONCE(current->sched_info.run_delay);
292dbd780eSAndrew Jones steal += vcpu->arch.steal.last_steal - last_steal;
3053f98558SAndrew Jones kvm_put_guest(kvm, base + offset, cpu_to_le64(steal));
3153f98558SAndrew Jones }
329ed24f4bSMarc Zyngier srcu_read_unlock(&kvm->srcu, idx);
339ed24f4bSMarc Zyngier }
349ed24f4bSMarc Zyngier
kvm_hypercall_pv_features(struct kvm_vcpu * vcpu)359ed24f4bSMarc Zyngier long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
369ed24f4bSMarc Zyngier {
379ed24f4bSMarc Zyngier u32 feature = smccc_get_arg1(vcpu);
389ed24f4bSMarc Zyngier long val = SMCCC_RET_NOT_SUPPORTED;
399ed24f4bSMarc Zyngier
409ed24f4bSMarc Zyngier switch (feature) {
419ed24f4bSMarc Zyngier case ARM_SMCCC_HV_PV_TIME_FEATURES:
429ed24f4bSMarc Zyngier case ARM_SMCCC_HV_PV_TIME_ST:
43*cecafc0aSYu Zhang if (vcpu->arch.steal.base != INVALID_GPA)
449ed24f4bSMarc Zyngier val = SMCCC_RET_SUCCESS;
459ed24f4bSMarc Zyngier break;
469ed24f4bSMarc Zyngier }
479ed24f4bSMarc Zyngier
489ed24f4bSMarc Zyngier return val;
499ed24f4bSMarc Zyngier }
509ed24f4bSMarc Zyngier
kvm_init_stolen_time(struct kvm_vcpu * vcpu)519ed24f4bSMarc Zyngier gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)
529ed24f4bSMarc Zyngier {
539ed24f4bSMarc Zyngier struct pvclock_vcpu_stolen_time init_values = {};
549ed24f4bSMarc Zyngier struct kvm *kvm = vcpu->kvm;
559ed24f4bSMarc Zyngier u64 base = vcpu->arch.steal.base;
569ed24f4bSMarc Zyngier
57*cecafc0aSYu Zhang if (base == INVALID_GPA)
589ed24f4bSMarc Zyngier return base;
599ed24f4bSMarc Zyngier
609ed24f4bSMarc Zyngier /*
619ed24f4bSMarc Zyngier * Start counting stolen time from the time the guest requests
629ed24f4bSMarc Zyngier * the feature enabled.
639ed24f4bSMarc Zyngier */
649ed24f4bSMarc Zyngier vcpu->arch.steal.last_steal = current->sched_info.run_delay;
65652d0b70SKeqian Zhu kvm_write_guest_lock(kvm, base, &init_values, sizeof(init_values));
669ed24f4bSMarc Zyngier
679ed24f4bSMarc Zyngier return base;
689ed24f4bSMarc Zyngier }
699ed24f4bSMarc Zyngier
kvm_arm_pvtime_supported(void)70004a0124SAndrew Jones bool kvm_arm_pvtime_supported(void)
71a25e9102SAndrew Jones {
72a25e9102SAndrew Jones return !!sched_info_on();
73a25e9102SAndrew Jones }
74a25e9102SAndrew Jones
kvm_arm_pvtime_set_attr(struct kvm_vcpu * vcpu,struct kvm_device_attr * attr)759ed24f4bSMarc Zyngier int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
769ed24f4bSMarc Zyngier struct kvm_device_attr *attr)
779ed24f4bSMarc Zyngier {
789ed24f4bSMarc Zyngier u64 __user *user = (u64 __user *)attr->addr;
799ed24f4bSMarc Zyngier struct kvm *kvm = vcpu->kvm;
809ed24f4bSMarc Zyngier u64 ipa;
819ed24f4bSMarc Zyngier int ret = 0;
829ed24f4bSMarc Zyngier int idx;
839ed24f4bSMarc Zyngier
84a25e9102SAndrew Jones if (!kvm_arm_pvtime_supported() ||
85a25e9102SAndrew Jones attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
869ed24f4bSMarc Zyngier return -ENXIO;
879ed24f4bSMarc Zyngier
889ed24f4bSMarc Zyngier if (get_user(ipa, user))
899ed24f4bSMarc Zyngier return -EFAULT;
909ed24f4bSMarc Zyngier if (!IS_ALIGNED(ipa, 64))
919ed24f4bSMarc Zyngier return -EINVAL;
92*cecafc0aSYu Zhang if (vcpu->arch.steal.base != INVALID_GPA)
939ed24f4bSMarc Zyngier return -EEXIST;
949ed24f4bSMarc Zyngier
959ed24f4bSMarc Zyngier /* Check the address is in a valid memslot */
969ed24f4bSMarc Zyngier idx = srcu_read_lock(&kvm->srcu);
979ed24f4bSMarc Zyngier if (kvm_is_error_hva(gfn_to_hva(kvm, ipa >> PAGE_SHIFT)))
989ed24f4bSMarc Zyngier ret = -EINVAL;
999ed24f4bSMarc Zyngier srcu_read_unlock(&kvm->srcu, idx);
1009ed24f4bSMarc Zyngier
1019ed24f4bSMarc Zyngier if (!ret)
1029ed24f4bSMarc Zyngier vcpu->arch.steal.base = ipa;
1039ed24f4bSMarc Zyngier
1049ed24f4bSMarc Zyngier return ret;
1059ed24f4bSMarc Zyngier }
1069ed24f4bSMarc Zyngier
kvm_arm_pvtime_get_attr(struct kvm_vcpu * vcpu,struct kvm_device_attr * attr)1079ed24f4bSMarc Zyngier int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
1089ed24f4bSMarc Zyngier struct kvm_device_attr *attr)
1099ed24f4bSMarc Zyngier {
1109ed24f4bSMarc Zyngier u64 __user *user = (u64 __user *)attr->addr;
1119ed24f4bSMarc Zyngier u64 ipa;
1129ed24f4bSMarc Zyngier
113a25e9102SAndrew Jones if (!kvm_arm_pvtime_supported() ||
114a25e9102SAndrew Jones attr->attr != KVM_ARM_VCPU_PVTIME_IPA)
1159ed24f4bSMarc Zyngier return -ENXIO;
1169ed24f4bSMarc Zyngier
1179ed24f4bSMarc Zyngier ipa = vcpu->arch.steal.base;
1189ed24f4bSMarc Zyngier
1199ed24f4bSMarc Zyngier if (put_user(ipa, user))
1209ed24f4bSMarc Zyngier return -EFAULT;
1219ed24f4bSMarc Zyngier return 0;
1229ed24f4bSMarc Zyngier }
1239ed24f4bSMarc Zyngier
kvm_arm_pvtime_has_attr(struct kvm_vcpu * vcpu,struct kvm_device_attr * attr)1249ed24f4bSMarc Zyngier int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
1259ed24f4bSMarc Zyngier struct kvm_device_attr *attr)
1269ed24f4bSMarc Zyngier {
1279ed24f4bSMarc Zyngier switch (attr->attr) {
1289ed24f4bSMarc Zyngier case KVM_ARM_VCPU_PVTIME_IPA:
129a25e9102SAndrew Jones if (kvm_arm_pvtime_supported())
1309ed24f4bSMarc Zyngier return 0;
1319ed24f4bSMarc Zyngier }
1329ed24f4bSMarc Zyngier return -ENXIO;
1339ed24f4bSMarc Zyngier }
134