akaros/kern/arch/x86/time.c
<<
>>
Prefs
   1/* Copyright (c) 2009 The Regents of the University of California
   2 * David (Yu) Zhu <yuzhu@cs.berkeley.edu>
   3 * Barret Rhoden <brho@cs.berkeley.edu>
   4 *
   5 * See LICENSE for details. */
   6
   7#include <arch/x86.h>
   8#include <arch/arch.h>
   9#include <arch/pic.h>
  10#include <arch/apic.h>
  11#include <time.h>
  12#include <trap.h>
  13#include <assert.h>
  14#include <stdio.h>
  15#include <ros/procinfo.h>
  16#include <arch/uaccess.h>
  17
  18static uint16_t pit_divisor;
  19static uint8_t pit_mode;
  20
  21static uint64_t compute_tsc_freq(void)
  22{
  23        uint64_t tscval[2];
  24
  25        /* some boards have this unmasked early on. */
  26        pic_mask_irq(0, 0 + PIC1_OFFSET);
  27        pit_set_timer(0xffff, TIMER_RATEGEN);
  28        tscval[0] = read_tsc();
  29        udelay_pit(1000000);
  30        tscval[1] = read_tsc();
  31        return tscval[1] - tscval[0];
  32}
  33
  34static void set_tsc_freq(void)
  35{
  36        uint64_t msr_val, tsc_freq = 0;
  37        bool computed = FALSE;
  38
  39        if (!read_msr_safe(MSR_PLATFORM_INFO, &msr_val))
  40                tsc_freq = __proc_global_info.bus_freq
  41                           * ((msr_val >> 8) & 0xff);
  42        /* Even if we have the MSR, it might have given us 0. (QEMU). */
  43        if (!tsc_freq) {
  44                tsc_freq = compute_tsc_freq();
  45                computed = TRUE;
  46        }
  47        __proc_global_info.tsc_freq = tsc_freq;
  48        printk("TSC Frequency: %llu%s\n", tsc_freq,
  49               computed ? " (computed)" : "");
  50}
  51
  52static uint64_t compute_bus_freq(void)
  53{
  54        uint32_t timercount[2];
  55
  56        __lapic_set_timer(0xffffffff, IdtLAPIC_TIMER, FALSE,
  57                          LAPIC_TIMER_DIVISOR_BITS);
  58        // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
  59        mask_lapic_lvt(MSR_LAPIC_LVT_TIMER);
  60        timercount[0] = apicrget(MSR_LAPIC_CURRENT_COUNT);
  61        udelay_pit(1000000);
  62        timercount[1] = apicrget(MSR_LAPIC_CURRENT_COUNT);
  63        /* The time base for the timer is derived from the processor's bus
  64         * clock, divided by the value specified in the divide configuration
  65         * register.  Note we mult and div by the divisor, saving the actual
  66         * freq (even though we don't use it yet). */
  67        return (timercount[0] - timercount[1]) * LAPIC_TIMER_DIVISOR_VAL;
  68}
  69
  70static uint64_t lookup_bus_freq(void)
  71{
  72        /* Got these from the good book for any model supporting
  73         * MSR_PLATFORM_INFO.  If they don't support that MSR, we're going to
  74         * compute the TSC anyways.
  75         *
  76         * A couple models weren't in the book, but were reported at:
  77         * http://a4lg.com/tech/x86/database/x86-families-and-models.en.html.
  78         * Feel free to add more.  If we fail here, we'll compute it manually
  79         * and be off slightly. */
  80        switch ((x86_family << 16) | x86_model) {
  81        case 0x6001a:
  82        case 0x6001e:
  83        case 0x6001f:
  84        case 0x6002e:
  85                /* Nehalem */
  86                return 133333333;
  87        case 0x60025:
  88        case 0x6002c:
  89        case 0x6002f:   /* from a4lg.com */
  90                /* Westmere */
  91                return 133333333;
  92        case 0x6002a:
  93        case 0x6002d:
  94                /* Sandy Bridge */
  95                return 100000000;
  96        case 0x6003a:   /* from a4lg.com */
  97        case 0x6003e:
  98                /* Ivy Bridge */
  99                return 100000000;
 100        case 0x6003c:
 101        case 0x6003f:
 102        case 0x60045:
 103        case 0x60046:
 104                /* Haswell */
 105                return 100000000;
 106        case 0x6003d:
 107        case 0x6004f:
 108        case 0x60056:
 109                /* Broadwell */
 110                return 100000000;
 111        case 0x6004d:
 112                /* Sky Lake */
 113                return 100000000;
 114        case 0x60057:
 115                /* Knights Landing */
 116                return 100000000;
 117        }
 118        return 0;
 119}
 120
 121static void set_bus_freq(void)
 122{
 123        uint64_t bus_freq;
 124        bool computed = FALSE;
 125
 126        bus_freq = lookup_bus_freq();
 127        if (!bus_freq) {
 128                bus_freq = compute_bus_freq();
 129                computed = TRUE;
 130        }
 131        __proc_global_info.bus_freq = bus_freq;
 132        printk("Bus Frequency: %llu%s\n", bus_freq,
 133               computed ? " (computed)" : "");
 134}
 135
 136void timer_init(void)
 137{
 138        set_bus_freq();
 139        assert(__proc_global_info.bus_freq);
 140        set_tsc_freq();
 141}
 142
 143void pit_set_timer(uint32_t divisor, uint32_t mode)
 144{
 145        if (divisor & 0xffff0000)
 146                warn("Divisor too large!");
 147        mode = TIMER_SEL0|TIMER_16BIT|mode;
 148        outb(TIMER_MODE, mode);
 149        outb(TIMER_CNTR0, divisor & 0xff);
 150        outb(TIMER_CNTR0, (divisor >> 8) );
 151        pit_mode = mode;
 152        pit_divisor = divisor;
 153        // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
 154}
 155
 156static int getpit(void)
 157{
 158        int high, low;
 159        // TODO: need a lock to protect access to PIT
 160        
 161        /* Select counter 0 and latch counter value. */
 162        outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
 163        
 164        low = inb(TIMER_CNTR0);
 165        high = inb(TIMER_CNTR0);
 166        
 167        return ((high << 8) | low);
 168}
 169
 170// forces cpu to relax for usec miliseconds.  declared in kern/include/time.h
 171void udelay(uint64_t usec)
 172{
 173        #if !defined(__BOCHS__)
 174        if (__proc_global_info.tsc_freq != 0)
 175        {
 176                uint64_t start, end, now;
 177
 178                start = read_tsc();
 179        end = start + usec2tsc(usec);
 180        //cprintf("start %llu, end %llu\n", start, end);
 181                if (end == 0) cprintf("This is terribly wrong \n");
 182                do {
 183            cpu_relax();
 184            now = read_tsc();
 185                        //cprintf("now %llu\n", now);
 186                } while (now < end || (now > start && end < start));
 187        return;
 188
 189        } else
 190        #endif
 191        {
 192                udelay_pit(usec);
 193        }
 194}
 195
 196void udelay_pit(uint64_t usec)
 197{
 198        int64_t delta, prev_tick, tick, ticks_left;
 199
 200        if (usec <= 0)
 201                return;
 202
 203        prev_tick = getpit();
 204        /*
 205         * Calculate ticks as (usec * (i8254_freq / 1e6)) rounded up
 206         * without using floating point and without any avoidable overflows.
 207         */
 208        ticks_left = ((usec * PIT_FREQ) + 999999) / 1000000;
 209        while (ticks_left > 0) {
 210                tick = getpit();
 211                delta = prev_tick - tick;
 212                prev_tick = tick;
 213                if (delta < 0) {
 214                        // counter looped around during the delta time period
 215                        delta += pit_divisor; // maximum count
 216                        if (delta < 0)
 217                                delta = 0;
 218                }
 219                ticks_left -= delta;
 220        }
 221}
 222
 223uint64_t gettimer(void)
 224{
 225        return read_tsc();
 226}
 227
 228uint64_t getfreq(void)
 229{
 230        return __proc_global_info.tsc_freq;
 231}
 232
 233void set_core_timer(uint32_t usec, bool periodic)
 234{
 235        if (usec)
 236                lapic_set_timer(usec, periodic);
 237        else
 238                lapic_disable_timer();
 239}
 240