xref: /openbmc/linux/arch/arm64/kvm/pvtime.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
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