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.


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