gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/vdso/vdso_time.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "vdso/vdso_time.h" 16 17 #include <stdint.h> 18 #include <sys/time.h> 19 #include <time.h> 20 21 #include "vdso/cycle_clock.h" 22 #include "vdso/seqlock.h" 23 #include "vdso/syscalls.h" 24 25 // struct params defines the layout of the parameter page maintained by the 26 // kernel (i.e., sentry). 27 // 28 // This is similar to the VVAR page maintained by the normal Linux kernel for 29 // its VDSO, but it has a different layout. 30 // 31 // It must be kept in sync with VDSOParamPage in pkg/sentry/kernel/vdso.go. 32 struct params { 33 uint64_t seq_count; 34 35 uint64_t monotonic_ready; 36 int64_t monotonic_base_cycles; 37 int64_t monotonic_base_ref; 38 uint64_t monotonic_frequency; 39 40 uint64_t realtime_ready; 41 int64_t realtime_base_cycles; 42 int64_t realtime_base_ref; 43 uint64_t realtime_frequency; 44 }; 45 46 // Returns a pointer to the global parameter page. 47 // 48 // This page lives in the page just before the VDSO binary itself. The linker 49 // defines _params as the page before the VDSO. 50 // 51 // Ideally, we'd simply declare _params as an extern struct params. 52 // Unfortunately various combinations of old/new versions of gcc/clang and 53 // gold/bfd struggle to generate references to such a global without generating 54 // relocations. 55 // 56 // So instead, we use inline assembly with a construct that seems to have wide 57 // compatibility across many toolchains. 58 #if __x86_64__ 59 60 inline struct params* get_params() { 61 struct params* p = nullptr; 62 asm("leaq _params(%%rip), %0" : "=r"(p) : :); 63 return p; 64 } 65 66 #elif __aarch64__ 67 68 inline struct params* get_params() { 69 struct params* p = nullptr; 70 asm("adr %0, _params" : "=r"(p) : :); 71 return p; 72 } 73 74 #else 75 #error "unsupported architecture" 76 #endif 77 78 namespace vdso { 79 80 const uint64_t kNsecsPerSec = 1000000000UL; 81 82 inline struct timespec ns_to_timespec(uint64_t ns) { 83 struct timespec ts; 84 ts.tv_sec = ns / kNsecsPerSec; 85 ts.tv_nsec = ns % kNsecsPerSec; 86 return ts; 87 } 88 89 inline uint64_t cycles_to_ns(uint64_t frequency, uint64_t cycles) { 90 uint64_t mult = (kNsecsPerSec << 32) / frequency; 91 return ((unsigned __int128)cycles * mult) >> 32; 92 } 93 94 // ClockRealtime() is the VDSO implementation of clock_gettime(CLOCK_REALTIME). 95 int ClockRealtime(struct timespec* ts) { 96 struct params* params = get_params(); 97 uint64_t seq; 98 uint64_t ready; 99 int64_t base_ref; 100 int64_t base_cycles; 101 uint64_t frequency; 102 int64_t now_cycles; 103 104 do { 105 seq = read_seqcount_begin(¶ms->seq_count); 106 ready = params->realtime_ready; 107 base_ref = params->realtime_base_ref; 108 base_cycles = params->realtime_base_cycles; 109 frequency = params->realtime_frequency; 110 now_cycles = cycle_clock(); 111 } while (read_seqcount_retry(¶ms->seq_count, seq)); 112 113 if (!ready) { 114 // The sandbox kernel ensures that we won't compute a time later than this 115 // once the params are ready. 116 return sys_clock_gettime(CLOCK_REALTIME, ts); 117 } 118 119 int64_t delta_cycles = 120 (now_cycles < base_cycles) ? 0 : now_cycles - base_cycles; 121 int64_t now_ns = base_ref + cycles_to_ns(frequency, delta_cycles); 122 *ts = ns_to_timespec(now_ns); 123 return 0; 124 } 125 126 // ClockMonotonic() is the VDSO implementation of 127 // clock_gettime(CLOCK_MONOTONIC). 128 int ClockMonotonic(struct timespec* ts) { 129 struct params* params = get_params(); 130 uint64_t seq; 131 uint64_t ready; 132 int64_t base_ref; 133 int64_t base_cycles; 134 uint64_t frequency; 135 int64_t now_cycles; 136 137 do { 138 seq = read_seqcount_begin(¶ms->seq_count); 139 ready = params->monotonic_ready; 140 base_ref = params->monotonic_base_ref; 141 base_cycles = params->monotonic_base_cycles; 142 frequency = params->monotonic_frequency; 143 now_cycles = cycle_clock(); 144 } while (read_seqcount_retry(¶ms->seq_count, seq)); 145 146 if (!ready) { 147 // The sandbox kernel ensures that we won't compute a time later than this 148 // once the params are ready. 149 return sys_clock_gettime(CLOCK_MONOTONIC, ts); 150 } 151 152 int64_t delta_cycles = 153 (now_cycles < base_cycles) ? 0 : now_cycles - base_cycles; 154 int64_t now_ns = base_ref + cycles_to_ns(frequency, delta_cycles); 155 *ts = ns_to_timespec(now_ns); 156 return 0; 157 } 158 159 } // namespace vdso