1// Copyright 2018 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <arch/ops.h>
8#include <arch/x86.h>
9#include <arch/x86/feature.h>
10#include <arch/x86/pvclock.h>
11#include <kernel/atomic.h>
12#include <vm/physmap.h>
13#include <vm/pmm.h>
14
15static volatile struct pvclock_boot_time* boot_time = nullptr;
16static volatile struct pvclock_system_time* system_time = nullptr;
17
18static constexpr uint64_t kSystemTimeEnable = 1u;
19
20zx_status_t pvclock_init(void) {
21    if (boot_time != nullptr || system_time != nullptr) {
22        return ZX_ERR_BAD_STATE;
23    }
24
25    paddr_t pa;
26    zx_status_t status = pmm_alloc_page(0, &pa);
27    if (status != ZX_OK) {
28        return status;
29    }
30    arch_zero_page(paddr_to_physmap(pa));
31    boot_time = static_cast<struct pvclock_boot_time*>(paddr_to_physmap(pa));
32    write_msr(kKvmBootTime, pa);
33
34    status = pmm_alloc_page(0, &pa);
35    if (status != ZX_OK) {
36        return status;
37    }
38    arch_zero_page(paddr_to_physmap(pa));
39    system_time = static_cast<struct pvclock_system_time*>(paddr_to_physmap(pa));
40    write_msr(kKvmSystemTimeMsr, pa | kSystemTimeEnable);
41
42    return ZX_OK;
43}
44
45bool pvclock_is_present(void) {
46    if (x86_hypervisor != X86_HYPERVISOR_KVM) {
47        return false;
48    }
49    uint32_t a, ignored;
50    cpuid(X86_CPUID_KVM_FEATURES, &a, &ignored, &ignored, &ignored);
51    if (a & kKvmFeatureClockSource) {
52        return true;
53    }
54    return false;
55}
56
57bool pvclock_is_stable() {
58    bool is_stable = (system_time->flags & kKvmSystemTimeStable) ||
59                     x86_feature_test(X86_FEATURE_KVM_PVCLOCK_STABLE);
60    printf("pvclock: Clocksource is %sstable\n", (is_stable ? "" : "not "));
61    return is_stable;
62}
63
64uint64_t pvclock_get_tsc_freq() {
65    printf("pvclock: Fetching TSC frequency\n");
66    uint32_t tsc_mul = 0;
67    int8_t tsc_shift = 0;
68    uint32_t pre_version = 0, post_version = 0;
69    do {
70        pre_version = atomic_load_u32(&system_time->version);
71        if (pre_version % 2 != 0) {
72            arch_spinloop_pause();
73            continue;
74        }
75        tsc_mul = system_time->tsc_mul;
76        tsc_shift = system_time->tsc_shift;
77        post_version = atomic_load_u32(&system_time->version);
78    } while (pre_version != post_version);
79
80    uint64_t tsc_khz = 1000000ULL << 32;
81    tsc_khz = tsc_khz / tsc_mul;
82    if (tsc_shift > 0) {
83        tsc_khz >>= tsc_shift;
84    } else {
85        tsc_khz <<= -tsc_shift;
86    }
87    return tsc_khz * 1000;
88}
89