xref: /kernel/linux/linux-6.6/arch/arm64/kvm/pvtime.c (revision 62306a36)
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