]> rtime.felk.cvut.cz Git - jailhouse.git/blobdiff - inmates/lib/x86/timing.c
Merge remote-tracking branch 'kiszka/master'
[jailhouse.git] / inmates / lib / x86 / timing.c
index 5f12e0336335f40432e12b7d5a220356ba2ffa63..4f3890bd1c4c1d4165dded909b8b05d04bf45ba5 100644 (file)
@@ -1,12 +1,10 @@
 /*
  * Jailhouse, a Linux-based partitioning hypervisor
  *
- * Copyright (c) Siemens AG, 2013, 2014
- * Copyright (c) Valentine Sinitsyn, 2014
+ * Copyright (c) Siemens AG, 2013-2016
  *
  * Authors:
  *  Jan Kiszka <jan.kiszka@siemens.com>
- *  Valentine Sinitsyn <valentine.sinitsyn@gmail.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -15,6 +13,7 @@
 #include <inmate.h>
 
 #define PM_TIMER_HZ            3579545
+#define PM_TIMER_OVERFLOW      ((0x1000000 * NS_PER_SEC) / PM_TIMER_HZ)
 
 #define X2APIC_LVTT            0x832
 #define X2APIC_TMICT           0x838
 #define X2APIC_TDCR            0x83e
 
 static unsigned long divided_apic_freq;
-static unsigned long pm_timer_overflow;
+static unsigned long pm_timer_last[SMP_MAX_CPUS];
+static unsigned long pm_timer_overflows[SMP_MAX_CPUS];
+static unsigned long tsc_freq, tsc_overflow;
+static unsigned long tsc_last[SMP_MAX_CPUS];
+static unsigned long tsc_overflows[SMP_MAX_CPUS];
 
-void pm_timer_init(void)
+static u64 rdtsc(void)
 {
-       /* Initialize pm_timer overflow value, as per ACPI 5.0, Sect. 5.2.9. */
-       if (comm_region->pm_timer_val_ext)
-               pm_timer_overflow =
-                       ((0x100000000 * 1000000000ULL) / PM_TIMER_HZ);
-       else
-               pm_timer_overflow =
-                       ((0x1000000 * 1000000000ULL) / PM_TIMER_HZ);
+#ifdef __x86_64__
+       u32 lo, hi;
+
+       asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
+       return (u64)lo | (((u64)hi) << 32);
+#else
+       u64 v;
+
+       asm volatile("rdtsc" : "=A" (v));
+       return v;
+#endif
+}
+
+unsigned long tsc_read(void)
+{
+       unsigned int cpu = cpu_id();
+       unsigned long tmr;
+
+       tmr = ((rdtsc() & 0xffffffffLL) * NS_PER_SEC) / tsc_freq;
+       if (tmr < tsc_last[cpu])
+               tsc_overflows[cpu] += tsc_overflow;
+       tsc_last[cpu] = tmr;
+       return tmr + tsc_overflows[cpu];
+}
+
+unsigned long tsc_init(void)
+{
+       unsigned long start_pm, end_pm;
+       u64 start_tsc, end_tsc;
+
+       tsc_freq = cmdline_parse_int("tsc_freq", 0);
+
+       if (tsc_freq == 0) {
+               start_pm = pm_timer_read();
+               start_tsc = rdtsc();
+               asm volatile("mfence" : : : "memory");
+
+               while (pm_timer_read() - start_pm < 100 * NS_PER_MSEC)
+                       cpu_relax();
+
+               end_pm = pm_timer_read();
+               end_tsc = rdtsc();
+               asm volatile("mfence" : : : "memory");
+
+               tsc_freq = (end_tsc - start_tsc) * NS_PER_SEC / (end_pm - start_pm);
+       }
+
+       tsc_overflow = (0x100000000L * NS_PER_SEC) / tsc_freq;
+
+       return tsc_freq;
 }
 
 unsigned long pm_timer_read(void)
 {
-       static unsigned long last, overflows;
+       unsigned int cpu = cpu_id();
        unsigned long tmr;
 
-       tmr = (inl(comm_region->pm_timer_address) * NS_PER_SEC) / PM_TIMER_HZ;
-       if (tmr < last)
-               overflows += pm_timer_overflow;
-       last = tmr;
-       return tmr + overflows;
+       tmr = ((inl(comm_region->pm_timer_address) & 0x00ffffff) * NS_PER_SEC)
+               / PM_TIMER_HZ;
+       if (tmr < pm_timer_last[cpu])
+               pm_timer_overflows[cpu] += PM_TIMER_OVERFLOW;
+       pm_timer_last[cpu] = tmr;
+       return tmr + pm_timer_overflows[cpu];
 }
 
 void delay_us(unsigned long microsecs)
@@ -57,26 +104,36 @@ void delay_us(unsigned long microsecs)
 
 unsigned long apic_timer_init(unsigned int vector)
 {
+       unsigned long long apic_freq;
        unsigned long start, end;
        unsigned long tmr;
 
-       write_msr(X2APIC_TDCR, 3);
+       apic_freq = cmdline_parse_int("apic_freq", 0);
 
-       start = pm_timer_read();
-       write_msr(X2APIC_TMICT, 0xffffffff);
+       if (apic_freq == 0) {
+               write_msr(X2APIC_TDCR, 3);
 
-       while (pm_timer_read() - start < 100 * NS_PER_MSEC)
-               cpu_relax();
+               start = pm_timer_read();
+               write_msr(X2APIC_TMICT, 0xffffffff);
+
+               while (pm_timer_read() - start < 100 * NS_PER_MSEC)
+                       cpu_relax();
+
+               end = pm_timer_read();
+               tmr = read_msr(X2APIC_TMCCT);
 
-       end = pm_timer_read();
-       tmr = read_msr(X2APIC_TMCCT);
+               write_msr(X2APIC_TMICT, 0);
 
-       divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
+               divided_apic_freq = (0xffffffffULL - tmr) * NS_PER_SEC / (end - start);
+               apic_freq = (divided_apic_freq * 16 + 500) / 1000;
+       } else {
+               divided_apic_freq = apic_freq / 16;
+               apic_freq /= 1000;
+       }
 
-       write_msr(X2APIC_TMICT, 0);
        write_msr(X2APIC_LVTT, vector);
 
-       return (divided_apic_freq * 16 + 500) / 1000;
+       return apic_freq;
 }
 
 void apic_timer_set(unsigned long timeout_ns)