AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
clock.c
Go to the documentation of this file.
1
24
25#include <core/system.h>
26#include <core/arch.h>
27#include <core/kargs.h>
28#include <core/string.h>
29#include <platform/misc.h>
30
31#define CALIBRATE_US 50000 /* 50 ms */
32
34
35static const char *names[] = {"pit", "hpet", "tsc"};
36
37static int source = SOURCE_PIT;
38static uint64_t mult; /* the ns per count, << 32 */
40
41/* the timer's ticks: their period, and how many there were */
44
46static inline uint64_t rdtsc(void)
47{
48 uint32_t lo, hi;
49 asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
50 return (uint64_t) hi << 32 | lo;
51}
52
54static inline void cpuid(uint32_t leaf, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
55{
56 asm volatile("cpuid" : "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d) : "a"(leaf), "c"(0));
57}
58
61static int tsc_invariant(void)
62{
63 uint32_t a, b, c, d;
64
65 cpuid(0x80000000, &a, &b, &c, &d);
66
67 if (a < 0x80000007)
68 return 0;
69
70 cpuid(0x80000007, &a, &b, &c, &d);
71 return !!(d & (1 << 8));
72}
73
77{
78 uint64_t ah = a >> 32, al = a & 0xffffffff;
79 uint64_t bh = b >> 32, bl = b & 0xffffffff;
80
81 return ((ah * bh) << 32) + ah * bl + al * bh + ((al * bl) >> 32);
82}
83
85static uint64_t counter(void)
86{
87 return source == SOURCE_TSC ? rdtsc() : x86_hpet_read32();
88}
89
92{
93 return source == SOURCE_TSC ? c - base_count : (uint32_t) (c - base_count);
94}
95
96/*
97 * Reads the clock.
98 */
100{
101 if (source == SOURCE_PIT)
102 return ticks * tick_period;
103
105
106 if (ns < last_ns)
107 ns = last_ns;
108
109 last_ns = ns;
110 return ns;
111}
112
113/*
114 * Reads the clock, in microseconds.
115 */
117{
118 return arch_rtime_ns() / 1000ULL;
119}
120
121/*
122 * Reads the clock, in milliseconds.
123 */
125{
126 return arch_rtime_ns() / 1000000ULL;
127}
128
131{
132 ticks++;
133
134 if (source == SOURCE_PIT)
135 return;
136
137 uint64_t c = counter();
138
140 base_count = c;
141}
142
146static uint64_t tsc_calibrate(int hpet, const char **by)
147{
148 uint64_t t0, t1, ns;
149
150 if (hpet) {
151 uint64_t period = x86_hpet_period_fs();
152 uint64_t h0 = x86_hpet_read(), h1;
153 uint64_t want = CALIBRATE_US * 1000000000ULL / period;
154
155 t0 = rdtsc();
156 while ((h1 = x86_hpet_read()) - h0 < want)
157 ;
158 t1 = rdtsc();
159
160 ns = (h1 - h0) * period / 1000000ULL;
161 *by = "hpet";
162 } else {
163 t0 = rdtsc();
165 t1 = rdtsc();
166
167 ns = CALIBRATE_US * 1000ULL;
168 *by = "pit";
169 }
170
171 return t1 > t0 ? (ns << 32) / (t1 - t0) : 0;
172}
173
181{
182 tick_period = period_ns;
183
184 if (source == SOURCE_PIT)
185 printk("clock: timer ticks, %d ns\n", period_ns);
186}
187
194{
195 return source != SOURCE_PIT;
196}
197
201{
202 const char *want = NULL;
203 int hpet = x86_hpet_init && !x86_hpet_init();
204
205 kargs_get("clock", &want);
206
207 int forced = want ? !strcmp(want, "tsc") ? SOURCE_TSC : !strcmp(want, "hpet") ? SOURCE_HPET :
208 !strcmp(want, "pit") ? SOURCE_PIT : -1 : -1;
209
210 if (want && forced < 0)
211 printk("clock: clock=%s: not tsc, hpet or pit\n", want);
212
213 int choice = forced >= 0 ? forced : tsc_invariant() ? SOURCE_TSC : hpet ? SOURCE_HPET : SOURCE_PIT;
214
215 if (choice == SOURCE_HPET && !hpet) {
216 printk("clock: no HPET\n");
217 choice = tsc_invariant() ? SOURCE_TSC : SOURCE_PIT;
218 }
219
220 if (choice == SOURCE_TSC && forced != SOURCE_TSC && !tsc_invariant())
221 choice = SOURCE_PIT;
222
223 if (choice == SOURCE_TSC) {
224 const char *by;
225
226 if (forced == SOURCE_TSC && !tsc_invariant())
227 printk("clock: the TSC isn't invariant (its rate may change)\n");
228
229 mult = tsc_calibrate(hpet, &by);
230
231 if (mult) {
232 uint64_t mhz = (1000ULL << 32) / mult;
233 printk("clock: tsc, %ld MHz (calibrated against the %s)\n", mhz, by);
234 } else {
235 choice = hpet ? SOURCE_HPET : SOURCE_PIT;
236 }
237 }
238
239 if (choice == SOURCE_HPET) {
240 mult = (x86_hpet_period_fs() << 32) / 1000000ULL;
241 printk("clock: hpet, %ld fs a count\n", x86_hpet_period_fs());
242 }
243
244 source = choice;
245
246 if (source != SOURCE_PIT) {
248 base_ns = last_ns = 0;
249 }
250}
251
257const char *x86_clock_source(void)
258{
259 return names[source];
260}
static const char * names[]
Definition clock.c:35
static int source
Definition clock.c:37
static uint64_t mult
Definition clock.c:38
static void cpuid(uint32_t leaf, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d)
Runs CPUID for leaf.
Definition clock.c:54
void x86_clock_ticks(uint32_t period_ns)
Says how often the timer ticks, from now on: with neither TSC nor HPET, the ticks are the clock.
Definition clock.c:180
static uint64_t mul_shift32(uint64_t a, uint64_t b)
Computes (a * b) >> 32 in 32-bit halves, without needing the 128-bit product.
Definition clock.c:76
static uint64_t rdtsc(void)
Reads the TSC.
Definition clock.c:46
static uint64_t counter(void)
Reads the counter the clock uses.
Definition clock.c:85
const char * x86_clock_source(void)
Returns the name of the clock's counter.
Definition clock.c:257
void x86_clock_init(void)
Chooses the clock's counter and calibrates it, at boot, before the timer ticks.
Definition clock.c:200
void x86_clock_tick(void)
Counts a tick of the timer, and moves the clock's base on to now.
Definition clock.c:130
@ SOURCE_TSC
Definition clock.c:33
@ SOURCE_PIT
Definition clock.c:33
@ SOURCE_HPET
Definition clock.c:33
static uint64_t ticks
Definition clock.c:43
static uint64_t base_ns
Definition clock.c:39
#define CALIBRATE_US
Definition clock.c:31
static int tsc_invariant(void)
Returns whether the TSC counts at a constant rate (CPUID 0x80000007, EDX bit 8).
Definition clock.c:61
static uint64_t tsc_calibrate(int hpet, const char **by)
Calibrates the TSC: counts it over CALIBRATE_US, timed by the HPET or the PIT, and returns its mult (...
Definition clock.c:146
static uint64_t since(uint64_t c)
Returns the counts since the base, from the counter's value c.
Definition clock.c:91
static uint64_t last_ns
Definition clock.c:39
static uint64_t base_count
Definition clock.c:39
static uint32_t tick_period
Definition clock.c:42
int kargs_get(const char *key, const char **value)
Looks up an argument of the kernel's command line.
Definition kargs.c:73
int x86_clock_precise(void)
Says whether the clock counts finer than the ticks.
Definition clock.c:193
int x86_hpet_init(void)
Finds the HPET and starts its counter.
Definition hpet.c:90
uint64_t x86_hpet_read(void)
Reads the HPET's 64-bit counter.
Definition hpet.c:51
uint32_t x86_hpet_read32(void)
Reads the low half of the HPET's counter only: a single read, which matters because each read is slow...
Definition hpet.c:69
uint64_t x86_hpet_period_fs(void)
Returns the HPET counter's period.
Definition hpet.c:79
void x86_pit_delay(uint32_t us)
Waits us microseconds, at most 54 ms, busy.
Definition pit.c:123
int printk(const char *fmt,...)
Writes a kernel message, to the message buffer and the consoles.
Definition printk.c:249
#define NULL
Definition stddef.h:27
unsigned int uint32_t
Definition stddef.h:22
unsigned long long int uint64_t
Definition stddef.h:23
uint64_t arch_rtime_ns(void)
Reads the monotonic clock, which starts at boot.
Definition timer.c:56
uint64_t arch_rtime_us(void)
Reads the monotonic clock, in microseconds.
Definition timer.c:65
uint64_t arch_rtime_ms(void)
Reads the monotonic clock, in milliseconds.
Definition timer.c:73