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
1 /*
2  * GeekOS timer interrupt support
3  * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
4  * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
5  * (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
6  * (c) 2008, The V3VEE Project <http://www.v3vee.org> 
7  * $Revision: 1.11 $
8  * 
9  * This is free software.  You are permitted to use,
10  * redistribute, and modify it as specified in the file "COPYING".
11  */
12
13 #include <limits.h>
14 #include <geekos/io.h>
15 #include <geekos/int.h>
16 #include <geekos/irq.h>
17 #include <geekos/kthread.h>
18 #include <geekos/timer.h>
19
20 #include <geekos/serial.h>
21 #include <geekos/debug.h>
22
23 #include <geekos/io_defs.h>
24
25 /* PAD this currently is in nvram.c */
26 extern void deliver_timer_interrupt_to_vmm(uint_t period_us);
27
28 /* JRL Add a cpu frequency measurement */
29 uint_t cpu_khz_freq;
30
31
32
33
34
35
36
37
38
39
40 #if defined(__i386__)
41
42 #define rdtscll(val)                            \
43      __asm__ __volatile__("rdtsc" : "=A" (val))
44
45 #elif defined(__x86_64__)
46
47 #define rdtscll(val) do {                                   \
48     unsigned int a,d;                                       \
49     asm volatile("rdtsc" : "=a" (a), "=d" (d));             \
50     (val) = ((unsigned long)a) | (((unsigned long)d)<<32);  \
51   } while(0)
52
53 #endif
54
55 #define do_div(n,base) ({                                    \
56       unsigned long __upper, __low, __high, __mod, __base;   \
57       __base = (base);                                       \
58       asm("":"=a" (__low), "=d" (__high):"A" (n));           \
59       __upper = __high;                                      \
60       if (__high) {                                          \
61         __upper = __high % (__base);                         \
62         __high = __high / (__base);                          \
63       }                                                                 \
64       asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
65       asm("":"=A" (n):"a" (__low),"d" (__high));                        \
66       __mod;                                                            \
67     })
68
69
70 /**
71  * This uses the Programmable Interval Timer that is standard on all
72  * PC-compatible systems to determine the time stamp counter frequency.
73  *
74  * This uses the speaker output (channel 2) of the PIT. This is better than
75  * using the timer interrupt output because we can read the value of the
76  * speaker with just one inb(), where we need three i/o operations for the
77  * interrupt channel. We count how many ticks the TSC does in 50 ms.
78  *
79  * Returns the detected time stamp counter frequency in KHz.
80  */
81
82
83 static unsigned int 
84 pit_calibrate_tsc(void)
85 {
86   uint_t khz = 0;
87   ullong_t start, end;
88   //        unsigned long flags;
89   unsigned long pit_tick_rate = 1193182UL;  /* 1.193182 MHz */
90   
91   //        spin_lock_irqsave(&pit_lock, flags);
92   
93   outb((inb(0x61) & ~0x02) | 0x01, 0x61);
94   
95   outb(0xb0, 0x43);
96   outb((pit_tick_rate / (1000 / 50)) & 0xff, 0x42);
97   outb((pit_tick_rate / (1000 / 50)) >> 8, 0x42);
98   //        start = get_cycles_sync();
99   rdtscll(start);
100   while ((inb(0x61) & 0x20) == 0);
101   rdtscll(end);
102   //   end = get_cycles_sync();
103   
104   //        spin_unlock_irqrestore(&pit_lock, flags);
105   
106   
107   //  return (end - start) / 50;
108   khz = end - start;
109 ;
110
111   return khz / 50;
112 }
113
114
115
116 /* END JRL */
117
118
119
120 #define MAX_TIMER_EVENTS        100
121
122 static int timerDebug = 0;
123 static int timeEventCount;
124 static int nextEventID;
125 timerEvent pendingTimerEvents[MAX_TIMER_EVENTS];
126
127
128
129 /*
130  * Global tick counter
131  */
132 volatile ulong_t g_numTicks;
133
134 ulong_t clock_time(void){
135         return g_numTicks;
136 }
137
138
139 /*
140  * Number of times the spin loop can execute during one timer tick
141  */
142 static int s_spinCountPerTick;
143
144 /*
145  * Number of ticks to wait before calibrating the delay loop.
146  */
147 #define CALIBRATE_NUM_TICKS     3
148
149 /*
150  * The default quantum; maximum number of ticks a thread can use before
151  * we suspend it and choose another.
152  */
153 #define DEFAULT_MAX_TICKS 4
154
155 /*
156  * Settable quantum.
157  */
158 int g_Quantum = DEFAULT_MAX_TICKS;
159
160 /*
161  * Ticks per second.
162  * FIXME: should set this to something more reasonable, like 100.
163  */
164 #define HZ 100
165 //#define TICKS_PER_SEC 18
166
167 /*#define DEBUG_TIMER */
168 #ifdef DEBUG_TIMER
169 #  define Debug(args...) Print(args)
170 #else
171 #  define Debug(args...)
172 #endif
173
174 /* ----------------------------------------------------------------------
175  * Private functions
176  * ---------------------------------------------------------------------- */
177
178 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
179 {
180   int i;
181   struct Kernel_Thread* current = g_currentThread;
182
183   Begin_IRQ(state);
184
185   /* Update global and per-thread number of ticks */
186   ++g_numTicks;
187   ++current->numTicks;
188   
189
190   /* update timer events */
191   for (i=0; i < timeEventCount; i++) {
192     if (pendingTimerEvents[i].ticks == 0) {
193       if (timerDebug) Print("timer: event %d expired (%d ticks)\n", 
194                             pendingTimerEvents[i].id, pendingTimerEvents[i].origTicks);
195       (pendingTimerEvents[i].callBack)(pendingTimerEvents[i].id);
196       pendingTimerEvents[i].ticks = pendingTimerEvents[i].origTicks;
197     } else {
198       pendingTimerEvents[i].ticks--;
199     }
200   }
201
202   /*
203    * If thread has been running for an entire quantum,
204    * inform the interrupt return code that we want
205    * to choose a new thread.
206    */
207   if (current->numTicks >= g_Quantum) {
208     g_needReschedule = true;
209     /*
210      * The current process is moved to a lower priority queue,
211      * since it consumed a full quantum.
212      */
213     //if (current->currentReadyQueue < (MAX_QUEUE_LEVEL - 1)) {
214       /*Print("process %d moved to ready queue %d\n", current->pid, current->currentReadyQueue); */
215       //current->currentReadyQueue++;
216     //}
217     
218   }
219   
220   
221   deliver_timer_interrupt_to_vmm(1000000/HZ);
222   
223   End_IRQ(state);
224 }
225
226 /*
227  * Temporary timer interrupt handler used to calibrate
228  * the delay loop.
229  */
230 static void Timer_Calibrate(struct Interrupt_State* state)
231 {
232     Begin_IRQ(state);
233     if (g_numTicks < CALIBRATE_NUM_TICKS)
234         ++g_numTicks;
235     else {
236         /*
237          * Now we can look at EAX, which reflects how many times
238          * the loop has executed
239          */
240         /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
241         s_spinCountPerTick = INT_MAX  - state->eax;
242         state->eax = 0;  /* make the loop terminate */
243     }
244     End_IRQ(state);
245 }
246
247 /*
248  * Delay loop; spins for given number of iterations.
249  */
250 static void Spin(int count)
251 {
252     /*
253      * The assembly code is the logical equivalent of
254      *      while (count-- > 0) { // waste some time }
255      * We rely on EAX being used as the counter
256      * variable.
257      */
258
259     int result;
260     __asm__ __volatile__ (
261         "1: decl %%eax\n\t"
262         "cmpl $0, %%eax\n\t"
263         "nop; nop; nop; nop; nop; nop\n\t"
264         "nop; nop; nop; nop; nop; nop\n\t"
265         "jg 1b"
266         : "=a" (result)
267         : "a" (count)
268     );
269 }
270
271 /*
272  * Calibrate the delay loop.
273  * This will initialize s_spinCountPerTick, which indicates
274  * how many iterations of the loop are executed per timer tick.
275  */
276 static void Calibrate_Delay(void)
277 {
278     Disable_Interrupts();
279
280     /* Install temporarily interrupt handler */
281     Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
282     Enable_IRQ(TIMER_IRQ);
283
284     Enable_Interrupts();
285
286     /* Wait a few ticks */
287     while (g_numTicks < CALIBRATE_NUM_TICKS)
288         ;
289
290     /*
291      * Execute the spin loop.xs
292      * The temporary interrupt handler will overwrite the
293      * loop counter when the next tick occurs.
294      */
295
296
297     Spin(INT_MAX);
298
299
300
301     Disable_Interrupts();
302
303     /*
304      * Mask out the timer IRQ again,
305      * since we will be installing a real timer interrupt handler.
306      */
307     Disable_IRQ(TIMER_IRQ);
308     Enable_Interrupts();
309 }
310
311 /* ----------------------------------------------------------------------
312  * Public functions
313  * ---------------------------------------------------------------------- */
314
315 void Init_Timer(void)
316 {
317   ushort_t foo = 1193182L / HZ;
318   
319   cpu_khz_freq = pit_calibrate_tsc();
320   PrintBoth("CPU KHZ=%lu\n", (ulong_t)cpu_khz_freq);
321
322   PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
323   
324   /* Calibrate for delay loop */
325   Calibrate_Delay();
326   PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
327   
328   // Set Timer to HZ
329
330   Out_Byte(0x43,0x36);          // channel 0, LSB/MSB, mode 3, binary
331   Out_Byte(0x40, foo & 0xff);   // LSB
332   Out_Byte(0x40, foo >>8);      // MSB
333     
334   /* Install an interrupt handler for the timer IRQ */
335
336   Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
337   Enable_IRQ(TIMER_IRQ);
338 }
339
340
341 int Start_Timer(int ticks, timerCallback cb)
342 {
343     int ret;
344
345     KASSERT(!Interrupts_Enabled());
346
347     if (timeEventCount == MAX_TIMER_EVENTS) {
348         return -1;
349     } else {
350         ret = nextEventID++;
351         pendingTimerEvents[timeEventCount].id = ret;
352         pendingTimerEvents[timeEventCount].callBack = cb;
353         pendingTimerEvents[timeEventCount].ticks = ticks;
354         pendingTimerEvents[timeEventCount].origTicks = ticks;
355         timeEventCount++;
356
357         return ret;
358     }
359 }
360
361 int Get_Remaing_Timer_Ticks(int id)
362 {
363     int i;
364
365     KASSERT(!Interrupts_Enabled());
366     for (i=0; i < timeEventCount; i++) {
367         if (pendingTimerEvents[i].id == id) {
368             return pendingTimerEvents[i].ticks;
369         }
370     }
371
372     return -1;
373 }
374
375 int Cancel_Timer(int id)
376 {
377     int i;
378     KASSERT(!Interrupts_Enabled());
379     for (i=0; i < timeEventCount; i++) {
380         if (pendingTimerEvents[i].id == id) {
381             pendingTimerEvents[i] = pendingTimerEvents[timeEventCount-1];
382             timeEventCount--;
383             return 0;
384         }
385     }
386
387     Print("timer: unable to find timer id %d to cancel it\n", id);
388     return -1;
389 }
390
391
392 #define US_PER_TICK (HZ * 1000000)
393
394 /*
395  * Spin for at least given number of microseconds.
396  * FIXME: I'm sure this implementation leaves a lot to
397  * be desired.
398  */
399 void Micro_Delay(int us)
400 {
401     int num = us * s_spinCountPerTick;
402     int denom = US_PER_TICK;
403
404     int numSpins = num / denom;
405     int rem = num % denom;
406
407     if (rem > 0)
408         ++numSpins;
409
410     Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
411
412     Spin(numSpins);
413 }