1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2019 Arm Ltd.
4 #include <linux/arm-smccc.h>
5 #include <linux/kvm_host.h>
6 #include <linux/sched/stat.h>
8 #include <asm/kvm_mmu.h>
9 #include <asm/pvclock-abi.h>
11 #include <kvm/arm_hypercalls.h>
13 void kvm_update_stolen_time(struct kvm_vcpu
*vcpu
)
15 struct kvm
*kvm
= vcpu
->kvm
;
16 u64 base
= vcpu
->arch
.steal
.base
;
17 u64 last_steal
= vcpu
->arch
.steal
.last_steal
;
18 u64 offset
= offsetof(struct pvclock_vcpu_stolen_time
, stolen_time
);
22 if (base
== GPA_INVALID
)
25 idx
= srcu_read_lock(&kvm
->srcu
);
26 if (!kvm_get_guest(kvm
, base
+ offset
, steal
)) {
27 steal
= le64_to_cpu(steal
);
28 vcpu
->arch
.steal
.last_steal
= READ_ONCE(current
->sched_info
.run_delay
);
29 steal
+= vcpu
->arch
.steal
.last_steal
- last_steal
;
30 kvm_put_guest(kvm
, base
+ offset
, cpu_to_le64(steal
));
32 srcu_read_unlock(&kvm
->srcu
, idx
);
35 long kvm_hypercall_pv_features(struct kvm_vcpu
*vcpu
)
37 u32 feature
= smccc_get_arg1(vcpu
);
38 long val
= SMCCC_RET_NOT_SUPPORTED
;
41 case ARM_SMCCC_HV_PV_TIME_FEATURES
:
42 case ARM_SMCCC_HV_PV_TIME_ST
:
43 if (vcpu
->arch
.steal
.base
!= GPA_INVALID
)
44 val
= SMCCC_RET_SUCCESS
;
51 gpa_t
kvm_init_stolen_time(struct kvm_vcpu
*vcpu
)
53 struct pvclock_vcpu_stolen_time init_values
= {};
54 struct kvm
*kvm
= vcpu
->kvm
;
55 u64 base
= vcpu
->arch
.steal
.base
;
57 if (base
== GPA_INVALID
)
61 * Start counting stolen time from the time the guest requests
62 * the feature enabled.
64 vcpu
->arch
.steal
.last_steal
= current
->sched_info
.run_delay
;
65 kvm_write_guest_lock(kvm
, base
, &init_values
, sizeof(init_values
));
70 bool kvm_arm_pvtime_supported(void)
72 return !!sched_info_on();
75 int kvm_arm_pvtime_set_attr(struct kvm_vcpu
*vcpu
,
76 struct kvm_device_attr
*attr
)
78 u64 __user
*user
= (u64 __user
*)attr
->addr
;
79 struct kvm
*kvm
= vcpu
->kvm
;
84 if (!kvm_arm_pvtime_supported() ||
85 attr
->attr
!= KVM_ARM_VCPU_PVTIME_IPA
)
88 if (get_user(ipa
, user
))
90 if (!IS_ALIGNED(ipa
, 64))
92 if (vcpu
->arch
.steal
.base
!= GPA_INVALID
)
95 /* Check the address is in a valid memslot */
96 idx
= srcu_read_lock(&kvm
->srcu
);
97 if (kvm_is_error_hva(gfn_to_hva(kvm
, ipa
>> PAGE_SHIFT
)))
99 srcu_read_unlock(&kvm
->srcu
, idx
);
102 vcpu
->arch
.steal
.base
= ipa
;
107 int kvm_arm_pvtime_get_attr(struct kvm_vcpu
*vcpu
,
108 struct kvm_device_attr
*attr
)
110 u64 __user
*user
= (u64 __user
*)attr
->addr
;
113 if (!kvm_arm_pvtime_supported() ||
114 attr
->attr
!= KVM_ARM_VCPU_PVTIME_IPA
)
117 ipa
= vcpu
->arch
.steal
.base
;
119 if (put_user(ipa
, user
))
124 int kvm_arm_pvtime_has_attr(struct kvm_vcpu
*vcpu
,
125 struct kvm_device_attr
*attr
)
127 switch (attr
->attr
) {
128 case KVM_ARM_VCPU_PVTIME_IPA
:
129 if (kvm_arm_pvtime_supported())