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