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 "pvclock_priv.h" 8#include <arch/hypervisor.h> 9#include <arch/x86/pvclock.h> 10#include <bits.h> 11#include <hypervisor/guest_physical_address_space.h> 12#include <platform.h> 13#include <vm/physmap.h> 14#include <zircon/types.h> 15 16namespace { 17 18void calculate_scale_factor(uint64_t tsc_freq, uint32_t* mul, int8_t* shift) { 19 // Guests converts TSC ticks to nanoseconds using this formula: 20 // ns = #TSCticks * mul * 2^(shift - 32). 21 // mul * 2^(shift - 32) is a fractional number used as a scale factor in conversion. 22 // It's very similar to how floating point numbers are usually represented in memory. 23 static const uint64_t target_freq = 1000000000ul; 24 25 DEBUG_ASSERT(tsc_freq != 0); 26 27 // We maintain the folowing invariant: 2^(exponent - 32) * x/y ~ target_freq / tsc_freq, 28 int8_t exponent = 32; 29 uint64_t x = target_freq; 30 uint64_t y = tsc_freq; 31 32 // First make y small enough so that (y << 31) doesn't overflow in the next step. Adjust 33 // exponent along the way to maintain invariant. 34 while (y >= (1ull << 31)) { 35 y >>= 1; 36 exponent--; 37 } 38 39 // We scale x/y multiplying x by 2 until it gets big enough or we run out of bits. 40 while (x < (y << 31) && BIT(x, 63) == 0) { 41 x <<= 1; 42 exponent--; 43 } 44 45 // Though it's very unlikely lets also consider a situation when x/y is still too small. 46 while (x < y) { 47 y >>= 1; 48 exponent++; 49 } 50 51 // Finally make sure that x/y fits within 32 bits. 52 while (x >= (y << 32)) { 53 x >>= 1; 54 exponent++; 55 } 56 57 *shift = static_cast<int8_t>(exponent); 58 *mul = static_cast<uint32_t>(x / y); 59} 60 61} // namespace 62 63extern fbl::atomic<int64_t> utc_offset; 64 65zx_status_t pvclock_update_boot_time(hypervisor::GuestPhysicalAddressSpace* gpas, 66 zx_vaddr_t guest_paddr) { 67 // KVM doesn't provide any protection against concurrent wall time requests from different 68 // VCPUs, but documentation doesn't mention that it cannot happen and moreover it properly 69 // protects per VCPU system time. Therefore to be on the safer side we use one global mutex 70 // for protection. 71 static fbl::Mutex mutex; 72 static uint32_t version __TA_GUARDED(mutex); 73 74 hypervisor::GuestPtr guest_ptr; 75 zx_status_t status = gpas->CreateGuestPtr(guest_paddr, sizeof(pvclock_boot_time), 76 "pvclock-boot-time-guest-mapping", &guest_ptr); 77 if (status != ZX_OK) { 78 return status; 79 } 80 auto boot_time = guest_ptr.as<pvclock_boot_time>(); 81 ZX_DEBUG_ASSERT(boot_time != nullptr); 82 memset(boot_time, 0, sizeof(*boot_time)); 83 84 fbl::AutoLock lock(&mutex); 85 zx_time_t time = utc_offset.load(); 86 // See the comment for pvclock_boot_time structure in arch/x86/pvclock.h 87 atomic_store_relaxed_u32(&boot_time->version, version + 1); 88 atomic_fence(); 89 boot_time->seconds = static_cast<uint32_t>(time / ZX_SEC(1)); 90 boot_time->nseconds = static_cast<uint32_t>(time % ZX_SEC(1)); 91 atomic_fence(); 92 atomic_store_relaxed_u32(&boot_time->version, version + 2); 93 version += 2; 94 return ZX_OK; 95} 96 97zx_status_t pvclock_reset_clock(PvClockState* pvclock, hypervisor::GuestPhysicalAddressSpace* gpas, 98 zx_vaddr_t guest_paddr) { 99 zx_status_t status = 100 gpas->CreateGuestPtr(guest_paddr, sizeof(pvclock_system_time), 101 "pvclock-system-time-guest-mapping", &pvclock->guest_ptr); 102 if (status != ZX_OK) { 103 return status; 104 } 105 pvclock->system_time = pvclock->guest_ptr.as<pvclock_system_time>(); 106 ZX_DEBUG_ASSERT(pvclock->system_time != nullptr); 107 memset(pvclock->system_time, 0, sizeof(*pvclock->system_time)); 108 return ZX_OK; 109} 110 111void pvclock_update_system_time(PvClockState* pvclock, 112 hypervisor::GuestPhysicalAddressSpace* gpas) { 113 if (!pvclock->system_time) { 114 return; 115 } 116 117 uint32_t tsc_mul; 118 int8_t tsc_shift; 119 calculate_scale_factor(ticks_per_second(), &tsc_mul, &tsc_shift); 120 121 // See the comment for pvclock_boot_time structure in arch/x86/pvclock.h 122 pvclock_system_time* system_time = pvclock->system_time; 123 atomic_store_relaxed_u32(&system_time->version, pvclock->version + 1); 124 atomic_fence(); 125 system_time->tsc_mul = tsc_mul; 126 system_time->tsc_shift = tsc_shift; 127 system_time->system_time = current_time(); 128 system_time->tsc_timestamp = rdtsc(); 129 system_time->flags = pvclock->is_stable ? kKvmSystemTimeStable : 0; 130 atomic_fence(); 131 atomic_store_relaxed_u32(&system_time->version, pvclock->version + 2); 132 pvclock->version += 2; 133} 134 135void pvclock_stop_clock(PvClockState* pvclock) { 136 pvclock->system_time = nullptr; 137 pvclock->guest_ptr.reset(); 138} 139 140zx_status_t pvclock_populate_offset(hypervisor::GuestPhysicalAddressSpace* gpas, 141 zx_vaddr_t guest_paddr) { 142 hypervisor::GuestPtr guest_ptr; 143 zx_status_t status = 144 gpas->CreateGuestPtr(guest_paddr, sizeof(PvClockOffset), 145 "pvclock-offset-guest-mapping", &guest_ptr); 146 if (status != ZX_OK) { 147 return status; 148 } 149 auto offset = guest_ptr.as<PvClockOffset>(); 150 ZX_DEBUG_ASSERT(offset != nullptr); 151 memset(offset, 0, sizeof(*offset)); 152 zx_time_t time = utc_offset.load() + current_time(); 153 uint64_t tsc = rdtsc(); 154 offset->sec = time / ZX_SEC(1); 155 offset->nsec = time % ZX_SEC(1); 156 offset->tsc = tsc; 157 return ZX_OK; 158} 159