Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


modified copyright tags
[palacios.git] / palacios / src / geekos / timer.c
index cd89f86..d3d4a6a 100644 (file)
@@ -2,7 +2,9 @@
  * GeekOS timer interrupt support
  * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
  * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
- * $Revision: 1.1 $
+ * (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
+ * (c) 2008, The V3VEE Project <http://www.v3vee.org> 
+ * $Revision: 1.11 $
  * 
  * This is free software.  You are permitted to use,
  * redistribute, and modify it as specified in the file "COPYING".
 #include <geekos/timer.h>
 
 #include <geekos/serial.h>
+#include <geekos/debug.h>
+
+#include <geekos/io_defs.h>
+
+/* PAD this currently is in nvram.c */
+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];
 
-#define HZ 100
 
 
 /*
  */
 volatile ulong_t g_numTicks;
 
+ulong_t clock_time(void){
+       return g_numTicks;
+}
+
+
 /*
  * Number of times the spin loop can execute during one timer tick
  */
@@ -50,7 +161,8 @@ int g_Quantum = DEFAULT_MAX_TICKS;
  * Ticks per second.
  * FIXME: should set this to something more reasonable, like 100.
  */
-#define TICKS_PER_SEC 18
+#define HZ 100
+//#define TICKS_PER_SEC 18
 
 /*#define DEBUG_TIMER */
 #ifdef DEBUG_TIMER
@@ -65,28 +177,50 @@ int g_Quantum = DEFAULT_MAX_TICKS;
 
 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
 {
-    struct Kernel_Thread* current = g_currentThread;
-
-    Begin_IRQ(state);
+  int i;
+  struct Kernel_Thread* current = g_currentThread;
 
-    SerialPrintLevel(10,"Host Timer Interrupt Handler Running\n");
-
-    /* Update global and per-thread number of ticks */
-    ++g_numTicks;
-    ++current->numTicks;
+  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].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;
     /*
-     * If thread has been running for an entire quantum,
-     * inform the interrupt return code that we want
-     * to choose a new thread.
+     * The current process is moved to a lower priority queue,
+     * since it consumed a full quantum.
      */
-    if (current->numTicks >= g_Quantum) {
-       g_needReschedule = true;
-    }
-
-
-    End_IRQ(state);
+    //if (current->currentReadyQueue < (MAX_QUEUE_LEVEL - 1)) {
+      /*Print("process %d moved to ready queue %d\n", current->pid, current->currentReadyQueue); */
+      //current->currentReadyQueue++;
+    //}
+    
+  }
+  
+  
+  deliver_timer_interrupt_to_vmm(1000000/HZ);
+  
+  End_IRQ(state);
 }
 
 /*
@@ -154,12 +288,16 @@ static void Calibrate_Delay(void)
        ;
 
     /*
-     * Execute the spin loop.
+     * Execute the spin loop.xs
      * The temporary interrupt handler will overwrite the
      * loop counter when the next tick occurs.
      */
+
+
     Spin(INT_MAX);
 
+
+
     Disable_Interrupts();
 
     /*
@@ -178,6 +316,9 @@ 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 */
@@ -197,7 +338,58 @@ void Init_Timer(void)
 }
 
 
-#define US_PER_TICK (TICKS_PER_SEC * 1000000)
+int Start_Timer(int ticks, timerCallback cb)
+{
+    int ret;
+
+    KASSERT(!Interrupts_Enabled());
+
+    if (timeEventCount == MAX_TIMER_EVENTS) {
+       return -1;
+    } else {
+       ret = nextEventID++;
+       pendingTimerEvents[timeEventCount].id = ret;
+       pendingTimerEvents[timeEventCount].callBack = cb;
+       pendingTimerEvents[timeEventCount].ticks = ticks;
+       pendingTimerEvents[timeEventCount].origTicks = ticks;
+       timeEventCount++;
+
+       return ret;
+    }
+}
+
+int Get_Remaing_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;
+}
+
+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.