]>
Commit | Line | Data |
---|---|---|
1e66a233 LP |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
1e66a233 | 3 | #include "ticks.h" |
bafc5945 | 4 | #include "util.h" |
ba279392 | 5 | #include "vmm.h" |
3bcc999f | 6 | |
09614b35 | 7 | #if defined(__i386__) || defined(__x86_64__) |
2a3ae5fa | 8 | # include <cpuid.h> |
706fd67e JJ |
9 | |
10 | static uint64_t ticks_read_arch(void) { | |
bafc5945 JJ |
11 | /* The TSC might or might not be virtualized in VMs (and thus might not be accurate or start at zero |
12 | * at boot), depending on hypervisor and CPU functionality. If it's not virtualized it's not useful | |
13 | * for keeping time, hence don't attempt to use it. */ | |
3bcc999f LP |
14 | if (in_hypervisor()) |
15 | return 0; | |
16 | ||
09614b35 | 17 | return __builtin_ia32_rdtsc(); |
1e66a233 | 18 | } |
706fd67e JJ |
19 | |
20 | static uint64_t ticks_freq_arch(void) { | |
2a3ae5fa JJ |
21 | /* Detect TSC frequency from CPUID information if available. */ |
22 | ||
23 | unsigned max_leaf, ebx, ecx, edx; | |
24 | if (__get_cpuid(0, &max_leaf, &ebx, &ecx, &edx) == 0) | |
25 | return 0; | |
26 | ||
27 | /* Leaf 0x15 is Intel only. */ | |
28 | if (max_leaf < 0x15 || ebx != signature_INTEL_ebx || ecx != signature_INTEL_ecx || | |
29 | edx != signature_INTEL_edx) | |
30 | return 0; | |
31 | ||
32 | unsigned denominator, numerator, crystal_hz; | |
33 | __cpuid(0x15, denominator, numerator, crystal_hz, edx); | |
34 | if (denominator == 0 || numerator == 0) | |
35 | return 0; | |
36 | ||
37 | uint64_t freq = crystal_hz; | |
38 | if (crystal_hz == 0) { | |
fcdd21ec | 39 | /* If the crystal frequency is not available, try to deduce it from |
2a3ae5fa JJ |
40 | * the processor frequency leaf if available. */ |
41 | if (max_leaf < 0x16) | |
42 | return 0; | |
43 | ||
44 | unsigned core_mhz; | |
45 | __cpuid(0x16, core_mhz, ebx, ecx, edx); | |
46 | freq = core_mhz * 1000ULL * 1000ULL * denominator / numerator; | |
47 | } | |
48 | ||
49 | return freq * numerator / denominator; | |
706fd67e JJ |
50 | } |
51 | ||
1e66a233 | 52 | #elif defined(__aarch64__) |
706fd67e JJ |
53 | |
54 | static uint64_t ticks_read_arch(void) { | |
db4122d1 | 55 | uint64_t val; |
2df8574a | 56 | asm volatile("mrs %0, cntvct_el0" : "=r"(val)); |
1e66a233 LP |
57 | return val; |
58 | } | |
1e66a233 | 59 | |
706fd67e | 60 | static uint64_t ticks_freq_arch(void) { |
db4122d1 | 61 | uint64_t freq; |
2df8574a | 62 | asm volatile("mrs %0, cntfrq_el0" : "=r"(freq)); |
1e66a233 LP |
63 | return freq; |
64 | } | |
706fd67e | 65 | |
1e66a233 | 66 | #else |
706fd67e JJ |
67 | |
68 | static uint64_t ticks_read_arch(void) { | |
69 | return 0; | |
70 | } | |
71 | ||
72 | static uint64_t ticks_freq_arch(void) { | |
73 | return 0; | |
74 | } | |
75 | ||
76 | #endif | |
77 | ||
db4122d1 | 78 | static uint64_t ticks_freq(void) { |
db4122d1 | 79 | static uint64_t cache = 0; |
fb63526f LP |
80 | |
81 | if (cache != 0) | |
82 | return cache; | |
1e66a233 | 83 | |
706fd67e JJ |
84 | cache = ticks_freq_arch(); |
85 | if (cache != 0) | |
86 | return cache; | |
87 | ||
88 | /* As a fallback, count ticks during a millisecond delay. */ | |
89 | uint64_t ticks_start = ticks_read_arch(); | |
1e66a233 | 90 | BS->Stall(1000); |
706fd67e | 91 | uint64_t ticks_end = ticks_read_arch(); |
1e66a233 | 92 | |
476c0e96 | 93 | if (ticks_end < ticks_start) /* Check for an overflow (which is not that unlikely, given on some |
da890466 | 94 | * archs the value is 32-bit) */ |
476c0e96 LP |
95 | return 0; |
96 | ||
fb63526f LP |
97 | cache = (ticks_end - ticks_start) * 1000UL; |
98 | return cache; | |
1e66a233 | 99 | } |
1e66a233 | 100 | |
db4122d1 | 101 | uint64_t time_usec(void) { |
706fd67e | 102 | uint64_t ticks = ticks_read_arch(); |
1e66a233 LP |
103 | if (ticks == 0) |
104 | return 0; | |
105 | ||
706fd67e | 106 | uint64_t freq = ticks_freq(); |
fb63526f LP |
107 | if (freq == 0) |
108 | return 0; | |
1e66a233 LP |
109 | |
110 | return 1000UL * 1000UL * ticks / freq; | |
111 | } |