KVM: arm64: Add support for the KVM PTP service
[linux-2.6-microblaze.git] / arch / arm64 / kvm / hypercalls.c
index ead21b9..30da78f 100644 (file)
@@ -9,16 +9,65 @@
 #include <kvm/arm_hypercalls.h>
 #include <kvm/arm_psci.h>
 
+static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
+{
+       struct system_time_snapshot systime_snapshot;
+       u64 cycles = ~0UL;
+       u32 feature;
+
+       /*
+        * system time and counter value must captured at the same
+        * time to keep consistency and precision.
+        */
+       ktime_get_snapshot(&systime_snapshot);
+
+       /*
+        * This is only valid if the current clocksource is the
+        * architected counter, as this is the only one the guest
+        * can see.
+        */
+       if (systime_snapshot.cs_id != CSID_ARM_ARCH_COUNTER)
+               return;
+
+       /*
+        * The guest selects one of the two reference counters
+        * (virtual or physical) with the first argument of the SMCCC
+        * call. In case the identifier is not supported, error out.
+        */
+       feature = smccc_get_arg1(vcpu);
+       switch (feature) {
+       case KVM_PTP_VIRT_COUNTER:
+               cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2);
+               break;
+       case KVM_PTP_PHYS_COUNTER:
+               cycles = systime_snapshot.cycles;
+               break;
+       default:
+               return;
+       }
+
+       /*
+        * This relies on the top bit of val[0] never being set for
+        * valid values of system time, because that is *really* far
+        * in the future (about 292 years from 1970, and at that stage
+        * nobody will give a damn about it).
+        */
+       val[0] = upper_32_bits(systime_snapshot.real);
+       val[1] = lower_32_bits(systime_snapshot.real);
+       val[2] = upper_32_bits(cycles);
+       val[3] = lower_32_bits(cycles);
+}
+
 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
 {
        u32 func_id = smccc_get_function(vcpu);
-       long val = SMCCC_RET_NOT_SUPPORTED;
+       u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
        u32 feature;
        gpa_t gpa;
 
        switch (func_id) {
        case ARM_SMCCC_VERSION_FUNC_ID:
-               val = ARM_SMCCC_VERSION_1_1;
+               val[0] = ARM_SMCCC_VERSION_1_1;
                break;
        case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
                feature = smccc_get_arg1(vcpu);
@@ -28,10 +77,10 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
                        case SPECTRE_VULNERABLE:
                                break;
                        case SPECTRE_MITIGATED:
-                               val = SMCCC_RET_SUCCESS;
+                               val[0] = SMCCC_RET_SUCCESS;
                                break;
                        case SPECTRE_UNAFFECTED:
-                               val = SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED;
+                               val[0] = SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED;
                                break;
                        }
                        break;
@@ -54,22 +103,35 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
                                        break;
                                fallthrough;
                        case SPECTRE_UNAFFECTED:
-                               val = SMCCC_RET_NOT_REQUIRED;
+                               val[0] = SMCCC_RET_NOT_REQUIRED;
                                break;
                        }
                        break;
                case ARM_SMCCC_HV_PV_TIME_FEATURES:
-                       val = SMCCC_RET_SUCCESS;
+                       val[0] = SMCCC_RET_SUCCESS;
                        break;
                }
                break;
        case ARM_SMCCC_HV_PV_TIME_FEATURES:
-               val = kvm_hypercall_pv_features(vcpu);
+               val[0] = kvm_hypercall_pv_features(vcpu);
                break;
        case ARM_SMCCC_HV_PV_TIME_ST:
                gpa = kvm_init_stolen_time(vcpu);
                if (gpa != GPA_INVALID)
-                       val = gpa;
+                       val[0] = gpa;
+               break;
+       case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID:
+               val[0] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0;
+               val[1] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1;
+               val[2] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2;
+               val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
+               break;
+       case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
+               val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
+               val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
+               break;
+       case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
+               kvm_ptp_get_time(vcpu, val);
                break;
        case ARM_SMCCC_TRNG_VERSION:
        case ARM_SMCCC_TRNG_FEATURES:
@@ -81,6 +143,6 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
                return kvm_psci_call(vcpu);
        }
 
-       smccc_set_retval(vcpu, val, 0, 0, 0);
+       smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
        return 1;
 }