X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=blobdiff_plain;f=geekos%2Fsrc%2Fgeekos%2Ftimer.c;fp=geekos%2Fsrc%2Fgeekos%2Ftimer.c;h=8409fd704ea608cc5dd3e499fce26ff9c6843a35;hp=0000000000000000000000000000000000000000;hb=ddc16b0737cf58f7aa90a69c6652cdf4090aec51;hpb=626595465a2c6987606a6bc697df65130ad8c2d3 diff --git a/geekos/src/geekos/timer.c b/geekos/src/geekos/timer.c new file mode 100644 index 0000000..8409fd7 --- /dev/null +++ b/geekos/src/geekos/timer.c @@ -0,0 +1,449 @@ +/* + * GeekOS timer interrupt support + * Copyright (c) 2001,2003 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * (c) 2008, Jack Lange + * (c) 2008, The V3VEE Project + * $Revision: 1.11 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* PAD this currently is in nvram.c */ +/* JRL: This is completely broken +extern void deliver_timer_interrupt_to_vmm(uint_t period_us); +*/ + +/* JRL Add a cpu frequency measurement */ +uint_t cpu_khz_freq; + + + + + + + + + + +#if defined(__i386__) + +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + +#elif defined(__x86_64__) + +#define rdtscll(val) do { \ + unsigned int a,d; \ + asm volatile("rdtsc" : "=a" (a), "=d" (d)); \ + (val) = ((unsigned long)a) | (((unsigned long)d)<<32); \ + } while(0) + +#endif + +#define do_div(n,base) ({ \ + unsigned long __upper, __low, __high, __mod, __base; \ + __base = (base); \ + asm("":"=a" (__low), "=d" (__high):"A" (n)); \ + __upper = __high; \ + if (__high) { \ + __upper = __high % (__base); \ + __high = __high / (__base); \ + } \ + asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \ + asm("":"=A" (n):"a" (__low),"d" (__high)); \ + __mod; \ + }) + + +/** + * This uses the Programmable Interval Timer that is standard on all + * PC-compatible systems to determine the time stamp counter frequency. + * + * This uses the speaker output (channel 2) of the PIT. This is better than + * using the timer interrupt output because we can read the value of the + * speaker with just one inb(), where we need three i/o operations for the + * interrupt channel. We count how many ticks the TSC does in 50 ms. + * + * Returns the detected time stamp counter frequency in KHz. + */ + + +static unsigned int +pit_calibrate_tsc(void) +{ + uint_t khz = 0; + ullong_t start, end; + // unsigned long flags; + unsigned long pit_tick_rate = 1193182UL; /* 1.193182 MHz */ + + // spin_lock_irqsave(&pit_lock, flags); + + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + outb(0xb0, 0x43); + outb((pit_tick_rate / (1000 / 50)) & 0xff, 0x42); + outb((pit_tick_rate / (1000 / 50)) >> 8, 0x42); + // start = get_cycles_sync(); + rdtscll(start); + while ((inb(0x61) & 0x20) == 0); + rdtscll(end); + // end = get_cycles_sync(); + + // spin_unlock_irqrestore(&pit_lock, flags); + + + // return (end - start) / 50; + khz = end - start; +; + + return khz / 50; +} + + + +/* END JRL */ + + + +#define MAX_TIMER_EVENTS 100 + +static int timerDebug = 0; +static int timeEventCount; +static int nextEventID; +timerEvent pendingTimerEvents[MAX_TIMER_EVENTS]; + + + +/* + * Global tick counter + */ +volatile ulong_t g_numTicks; + +/* + * Number of times the spin loop can execute during one timer tick + */ +static int s_spinCountPerTick; + +/* + * Number of ticks to wait before calibrating the delay loop. + */ +#define CALIBRATE_NUM_TICKS 3 + +/* + * The default quantum; maximum number of ticks a thread can use before + * we suspend it and choose another. + */ +#define DEFAULT_MAX_TICKS 4 + +/* + * Settable quantum. + */ +int g_Quantum = DEFAULT_MAX_TICKS; + +/* + * Ticks per second. + * FIXME: should set this to something more reasonable, like 100. + */ +#define HZ 100 +//#define TICKS_PER_SEC 18 + +/*#define DEBUG_TIMER */ +#ifdef DEBUG_TIMER +# define Debug(args...) Print(args) +#else +# define Debug(args...) +#endif + +ulong_t clock_time(void){//in millisec + return g_numTicks * (1000/HZ); +} + +/* ---------------------------------------------------------------------- + * Private functions + * ---------------------------------------------------------------------- */ + +static void Timer_Interrupt_Handler(struct Interrupt_State* state) +{ + int i; + struct Kernel_Thread* current = g_currentThread; + + Begin_IRQ(state); + + /* Update global and per-thread number of ticks */ + ++g_numTicks; + ++current->numTicks; + + + /* update timer events */ + for (i=0; i < timeEventCount; i++) { + if (pendingTimerEvents[i].ticks == 0) { + if (timerDebug) Print("timer: event %d expired (%d ticks)\n", + pendingTimerEvents[i].id, pendingTimerEvents[i].origTicks); + (pendingTimerEvents[i].callBack)(pendingTimerEvents[i].id, pendingTimerEvents[i].cb_arg); + pendingTimerEvents[i].ticks = pendingTimerEvents[i].origTicks; + } else { + pendingTimerEvents[i].ticks--; + } + } + + /* + * If thread has been running for an entire quantum, + * inform the interrupt return code that we want + * to choose a new thread. + */ + if (current->numTicks >= g_Quantum) { + g_needReschedule = true; + /* + * The current process is moved to a lower priority queue, + * since it consumed a full quantum. + */ + //if (current->currentReadyQueue < (MAX_QUEUE_LEVEL - 1)) { + /*Print("process %d moved to ready queue %d\n", current->pid, current->currentReadyQueue); */ + //current->currentReadyQueue++; + //} + + } + + + send_tick_to_vmm(1000000/HZ); + + End_IRQ(state); +} + +/* + * Temporary timer interrupt handler used to calibrate + * the delay loop. + */ +static void Timer_Calibrate(struct Interrupt_State* state) +{ + Begin_IRQ(state); + if (g_numTicks < CALIBRATE_NUM_TICKS) + ++g_numTicks; + else { + /* + * Now we can look at EAX, which reflects how many times + * the loop has executed + */ + /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/ + s_spinCountPerTick = INT_MAX - state->eax; + state->eax = 0; /* make the loop terminate */ + } + End_IRQ(state); +} + +/* + * Delay loop; spins for given number of iterations. + */ +static void Spin(int count) +{ + /* + * The assembly code is the logical equivalent of + * while (count-- > 0) { // waste some time } + * We rely on EAX being used as the counter + * variable. + */ + + int result; + __asm__ __volatile__ ( + "1: decl %%eax\n\t" + "cmpl $0, %%eax\n\t" + "nop; nop; nop; nop; nop; nop\n\t" + "nop; nop; nop; nop; nop; nop\n\t" + "jg 1b" + : "=a" (result) + : "a" (count) + ); +} + +/* + * Calibrate the delay loop. + * This will initialize s_spinCountPerTick, which indicates + * how many iterations of the loop are executed per timer tick. + */ +static void Calibrate_Delay(void) +{ + Disable_Interrupts(); + + /* Install temporarily interrupt handler */ + Install_IRQ(TIMER_IRQ, &Timer_Calibrate); + Enable_IRQ(TIMER_IRQ); + + Enable_Interrupts(); + + /* Wait a few ticks */ + while (g_numTicks < CALIBRATE_NUM_TICKS) + ; + + /* + * Execute the spin loop.xs + * The temporary interrupt handler will overwrite the + * loop counter when the next tick occurs. + */ + + + Spin(INT_MAX); + + + + Disable_Interrupts(); + + /* + * Mask out the timer IRQ again, + * since we will be installing a real timer interrupt handler. + */ + Disable_IRQ(TIMER_IRQ); + Enable_Interrupts(); +} + +/* ---------------------------------------------------------------------- + * Public functions + * ---------------------------------------------------------------------- */ + +void Init_Timer(void) +{ + ushort_t foo = 1193182L / HZ; + + cpu_khz_freq = pit_calibrate_tsc(); + PrintBoth("CPU KHZ=%lu\n", (ulong_t)cpu_khz_freq); + + PrintBoth("Initializing timer and setting to %d Hz...\n",HZ); + + /* Calibrate for delay loop */ + Calibrate_Delay(); + PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick); + + // Set Timer to HZ + + Out_Byte(0x43,0x36); // channel 0, LSB/MSB, mode 3, binary + Out_Byte(0x40, foo & 0xff); // LSB + Out_Byte(0x40, foo >>8); // MSB + + /* Install an interrupt handler for the timer IRQ */ + + Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler); + Enable_IRQ(TIMER_IRQ); +} + + +int Start_Timer_Secs(int seconds, timerCallback cb, void * arg) { + return Start_Timer(seconds * HZ, cb, arg); +} + + +int Start_Timer_MSecs(int msecs, timerCallback cb, void * arg) { + msecs += 10 - (msecs % 10); + + return Start_Timer(msecs * (HZ / 1000), cb, arg); +} + + + +int Start_Timer(int ticks, timerCallback cb, void * arg) +{ + int ret; + + KASSERT(!Interrupts_Enabled()); + + PrintBoth ("there\n"); + + if (timeEventCount == MAX_TIMER_EVENTS) { + return -1; + } else { + ret = nextEventID++; + pendingTimerEvents[timeEventCount].id = ret; + pendingTimerEvents[timeEventCount].callBack = cb; + pendingTimerEvents[timeEventCount].cb_arg = arg; + pendingTimerEvents[timeEventCount].ticks = ticks; + pendingTimerEvents[timeEventCount].origTicks = ticks; + timeEventCount++; + + return ret; + } +} + + +int Get_Remaining_Timer_Ticks(int id) +{ + int i; + + KASSERT(!Interrupts_Enabled()); + for (i=0; i < timeEventCount; i++) { + if (pendingTimerEvents[i].id == id) { + return pendingTimerEvents[i].ticks; + } + } + + return -1; +} + + + +double Get_Remaining_Timer_Secs(int id) { + return (Get_Remaining_Timer_Ticks(id) / HZ); +} + + +int Get_Remaining_Timer_MSecs(int id) { + return ((Get_Remaining_Timer_Ticks(id) * 1000) / HZ); +} + + + +int Cancel_Timer(int id) +{ + int i; + KASSERT(!Interrupts_Enabled()); + for (i=0; i < timeEventCount; i++) { + if (pendingTimerEvents[i].id == id) { + pendingTimerEvents[i] = pendingTimerEvents[timeEventCount-1]; + timeEventCount--; + return 0; + } + } + + Print("timer: unable to find timer id %d to cancel it\n", id); + return -1; +} + + +#define US_PER_TICK (HZ * 1000000) + +/* + * Spin for at least given number of microseconds. + * FIXME: I'm sure this implementation leaves a lot to + * be desired. + */ +void Micro_Delay(int us) +{ + int num = us * s_spinCountPerTick; + int denom = US_PER_TICK; + + int numSpins = num / denom; + int rem = num % denom; + + if (rem > 0) + ++numSpins; + + Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins); + + Spin(numSpins); +} + + +