x86: Use MSRs and model info for timer freqs (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 24 Oct 2017 20:00:32 +0000 (16:00 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 26 Oct 2017 18:33:29 +0000 (14:33 -0400)
We should be able to know from cpuid family/model what the bus frequency
is.  It's a minor pain.

On some machine (notably not my Qemu), you can read MSR_PLATFORM_INFO and
get the invariant TSC freq.  With that and the bus freq, we can get an
exact TSC frequency.

This helps FTQ slightly.  With slight errors in the invariant TSC freq,
our measurements would drift.  FTQ has an override for get_tsc_freq(), but
we can do the right thing on Akaros.

Plus this speeds up everyone's boot times!

Reinstall your kernel header, though it shouldn't matter.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/arch.h
kern/arch/x86/cpuinfo.c
kern/arch/x86/ros/msr-index.h
kern/arch/x86/time.c

index e20997e..68db46f 100644 (file)
@@ -33,6 +33,7 @@ __reset_stack_pointer(void *arg, uintptr_t sp, void (*f)(void *));
 /* in trap.c */
 void send_ipi(uint32_t os_coreid, uint8_t vector);
 /* in cpuinfo.c */
+int x86_family, x86_model, x86_stepping;
 void print_cpuinfo(void);
 void show_mapping(pgdir_t pgdir, uintptr_t start, size_t size);
 int vendor_id(char *);
index c3c3bec..0e16f75 100644 (file)
@@ -15,6 +15,8 @@
 #include <string.h>
 #include <cpu_feat.h>
 
+int x86_family, x86_model, x86_stepping;
+
 /* Check Intel's SDM 2a for Table 3-17 for the cpuid leaves */
 void print_cpuinfo(void)
 {
@@ -69,9 +71,12 @@ void print_cpuinfo(void)
                model += ext_model << 4;
        if (family == 15)
                family += ext_family;
-       cprintf("Family: %d\n", family);
-       cprintf("Model: %d\n", model);
-       cprintf("Stepping: %d\n", eax & 0x0000000F);
+       x86_family = family;
+       x86_model = model;
+       x86_stepping = eax & 0xf;
+       printk("Family: %d\n", x86_family);
+       printk("Model: %d\n", x86_model);
+       printk("Stepping: %d\n", x86_stepping);
        // eventually can fill this out with SDM Vol3B App B info, or
        // better yet with stepping info.  or cpuid 8000_000{2,3,4}
        switch ( family << 8 | model ) {
index 69456d4..deaad47 100644 (file)
@@ -49,7 +49,7 @@
 #define ARCH_PERFMON_EVENTSEL_CMASK                    0xFF000000ULL
 
 #define MSR_FSB_FREQ                   0x000000cd
-#define MSR_NHM_PLATFORM_INFO          0x000000ce
+#define MSR_PLATFORM_INFO              0x000000ce
 
 #define MSR_NHM_SNB_PKG_CST_CFG_CTL    0x000000e2
 #define NHM_C3_AUTO_DEMOTE             (1UL << 25)
index 2c3d506..fa04816 100644 (file)
 #include <assert.h>
 #include <stdio.h>
 #include <ros/procinfo.h>
+#include <arch/uaccess.h>
 
 static uint16_t pit_divisor;
 static uint8_t pit_mode;
 
-// timer init calibrates both tsc timer and lapic timer using PIT
-void timer_init(void){
+static uint64_t compute_tsc_freq(void)
+{
+       uint64_t tscval[2];
+
        /* some boards have this unmasked early on. */
        pic_mask_irq(0, 0 + PIC1_OFFSET);
-       uint64_t tscval[2];
-       long timercount[2];
        pit_set_timer(0xffff, TIMER_RATEGEN);
-       // assume tsc exist
        tscval[0] = read_tsc();
        udelay_pit(1000000);
        tscval[1] = read_tsc();
-       __proc_global_info.tsc_freq = tscval[1] - tscval[0];
-       cprintf("TSC Frequency: %llu\n", __proc_global_info.tsc_freq);
+       return tscval[1] - tscval[0];
+}
+
+static void set_tsc_freq(void)
+{
+       uint64_t msr_val, tsc_freq;
+       bool computed = FALSE;
+
+       if (read_msr_safe(MSR_PLATFORM_INFO, &msr_val)) {
+               tsc_freq = compute_tsc_freq();
+               computed = TRUE;
+       } else {
+               tsc_freq = __proc_global_info.bus_freq * ((msr_val >> 8) & 0xff);
+       }
+       __proc_global_info.tsc_freq = tsc_freq;
+       printk("TSC Frequency: %llu%s\n", tsc_freq, computed ? " (computed)" : "");
+}
+
+static uint64_t compute_bus_freq(void)
+{
+       uint32_t timercount[2];
+
        __lapic_set_timer(0xffffffff, IdtLAPIC_TIMER, FALSE,
                          LAPIC_TIMER_DIVISOR_BITS);
        // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
@@ -37,14 +57,82 @@ void timer_init(void){
        timercount[0] = apicrget(MSR_LAPIC_CURRENT_COUNT);
        udelay_pit(1000000);
        timercount[1] = apicrget(MSR_LAPIC_CURRENT_COUNT);
-       __proc_global_info.bus_freq = (timercount[0] - timercount[1])
-                                * LAPIC_TIMER_DIVISOR_VAL;
-       assert(__proc_global_info.bus_freq);
        /* The time base for the timer is derived from the processor's bus clock,
         * divided by the value specified in the divide configuration register.
         * Note we mult and div by the divisor, saving the actual freq (even though
         * we don't use it yet). */
-       cprintf("Bus Frequency: %llu\n", __proc_global_info.bus_freq);
+       return (timercount[0] - timercount[1]) * LAPIC_TIMER_DIVISOR_VAL;
+}
+
+static uint64_t lookup_bus_freq(void)
+{
+       /* Got these from the good book for any model supporting MSR_PLATFORM_INFO.
+        * If they don't support that MSR, we're going to compute the TSC anyways.
+        *
+        * A couple models weren't in the book, but were reported at:
+        * http://a4lg.com/tech/x86/database/x86-families-and-models.en.html.
+        * Feel free to add more.  If we fail here, we'll compute it manually and be
+        * off slightly. */
+       switch ((x86_family << 16) | x86_model) {
+       case 0x6001a:
+       case 0x6001e:
+       case 0x6001f:
+       case 0x6002e:
+               /* Nehalem */
+               return 133333333;
+       case 0x60025:
+       case 0x6002c:
+       case 0x6002f:   /* from a4lg.com */
+               /* Westmere */
+               return 133333333;
+       case 0x6002a:
+       case 0x6002d:
+               /* Sandy Bridge */
+               return 100000000;
+       case 0x6003a:   /* from a4lg.com */
+       case 0x6003e:
+               /* Ivy Bridge */
+               return 100000000;
+       case 0x6003c:
+       case 0x6003f:
+       case 0x60045:
+       case 0x60046:
+               /* Haswell */
+               return 100000000;
+       case 0x6003d:
+       case 0x6004f:
+       case 0x60056:
+               /* Broadwell */
+               return 100000000;
+       case 0x6004d:
+               /* Sky Lake */
+               return 100000000;
+       case 0x60057:
+               /* Knights Landing */
+               return 100000000;
+       }
+       return 0;
+}
+
+static void set_bus_freq(void)
+{
+       uint64_t bus_freq;
+       bool computed = FALSE;
+
+       bus_freq = lookup_bus_freq();
+       if (!bus_freq) {
+               bus_freq = compute_bus_freq();
+               computed = TRUE;
+       }
+       __proc_global_info.bus_freq = bus_freq;
+       printk("Bus Frequency: %llu%s\n", bus_freq, computed ? " (computed)" : "");
+}
+
+void timer_init(void)
+{
+       set_bus_freq();
+       assert(__proc_global_info.bus_freq);
+       set_tsc_freq();
 }
 
 void pit_set_timer(uint32_t divisor, uint32_t mode)