]> rtime.felk.cvut.cz Git - jailhouse.git/blob - inmates/lib/x86/timing.c
inmates: x86: Allow to bypass TSC and APIC timer calibration
[jailhouse.git] / inmates / lib / x86 / timing.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2013-2016
5  *
6  * Authors:
7  *  Jan Kiszka <jan.kiszka@siemens.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <inmate.h>
14
15 #define PM_TIMER_HZ             3579545
16 #define PM_TIMER_OVERFLOW      ((0x1000000 * NS_PER_SEC) / PM_TIMER_HZ)
17
18 #define X2APIC_LVTT             0x832
19 #define X2APIC_TMICT            0x838
20 #define X2APIC_TMCCT            0x839
21 #define X2APIC_TDCR             0x83e
22
23 static unsigned long divided_apic_freq;
24 static unsigned long pm_timer_last[SMP_MAX_CPUS];
25 static unsigned long pm_timer_overflows[SMP_MAX_CPUS];
26 static unsigned long tsc_freq, tsc_overflow;
27 static unsigned long tsc_last[SMP_MAX_CPUS];
28 static unsigned long tsc_overflows[SMP_MAX_CPUS];
29
30 static u64 rdtsc(void)
31 {
32 #ifdef __x86_64__
33         u32 lo, hi;
34
35         asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
36         return (u64)lo | (((u64)hi) << 32);
37 #else
38         u64 v;
39
40         asm volatile("rdtsc" : "=A" (v));
41         return v;
42 #endif
43 }
44
45 unsigned long tsc_read(void)
46 {
47         unsigned int cpu = cpu_id();
48         unsigned long tmr;
49
50         tmr = ((rdtsc() & 0xffffffffLL) * NS_PER_SEC) / tsc_freq;
51         if (tmr < tsc_last[cpu])
52                 tsc_overflows[cpu] += tsc_overflow;
53         tsc_last[cpu] = tmr;
54         return tmr + tsc_overflows[cpu];
55 }
56
57 unsigned long tsc_init(void)
58 {
59         unsigned long start_pm, end_pm;
60         u64 start_tsc, end_tsc;
61
62         tsc_freq = cmdline_parse_int("tsc_freq", 0);
63
64         if (tsc_freq == 0) {
65                 start_pm = pm_timer_read();
66                 start_tsc = rdtsc();
67                 asm volatile("mfence" : : : "memory");
68
69                 while (pm_timer_read() - start_pm < 100 * NS_PER_MSEC)
70                         cpu_relax();
71
72                 end_pm = pm_timer_read();
73                 end_tsc = rdtsc();
74                 asm volatile("mfence" : : : "memory");
75
76                 tsc_freq = (end_tsc - start_tsc) * NS_PER_SEC / (end_pm - start_pm);
77         }
78
79         tsc_overflow = (0x100000000L * NS_PER_SEC) / tsc_freq;
80
81         return tsc_freq;
82 }
83
84 unsigned long pm_timer_read(void)
85 {
86         unsigned int cpu = cpu_id();
87         unsigned long tmr;
88
89         tmr = ((inl(comm_region->pm_timer_address) & 0x00ffffff) * NS_PER_SEC)
90                 / PM_TIMER_HZ;
91         if (tmr < pm_timer_last[cpu])
92                 pm_timer_overflows[cpu] += PM_TIMER_OVERFLOW;
93         pm_timer_last[cpu] = tmr;
94         return tmr + pm_timer_overflows[cpu];
95 }
96
97 void delay_us(unsigned long microsecs)
98 {
99         unsigned long timeout = pm_timer_read() + microsecs * NS_PER_USEC;
100
101         while ((long)(timeout - pm_timer_read()) > 0)
102                 cpu_relax();
103 }
104
105 unsigned long apic_timer_init(unsigned int vector)
106 {
107         unsigned long long apic_freq;
108         unsigned long start, end;
109         unsigned long tmr;
110
111         apic_freq = cmdline_parse_int("apic_freq", 0);
112
113         if (apic_freq == 0) {
114                 write_msr(X2APIC_TDCR, 3);
115
116                 start = pm_timer_read();
117                 write_msr(X2APIC_TMICT, 0xffffffff);
118
119                 while (pm_timer_read() - start < 100 * NS_PER_MSEC)
120                         cpu_relax();
121
122                 end = pm_timer_read();
123                 tmr = read_msr(X2APIC_TMCCT);
124
125                 write_msr(X2APIC_TMICT, 0);
126
127                 divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
128                 apic_freq = (divided_apic_freq * 16 + 500) / 1000;
129         } else {
130                 divided_apic_freq = apic_freq / 16;
131                 apic_freq /= 1000;
132         }
133
134         write_msr(X2APIC_LVTT, vector);
135
136         return apic_freq;
137 }
138
139 void apic_timer_set(unsigned long timeout_ns)
140 {
141         unsigned long long ticks =
142                 (unsigned long long)timeout_ns * divided_apic_freq;
143         write_msr(X2APIC_TMICT, ticks / NS_PER_SEC);
144 }