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.


0e3804f2421e935a22dcd768caf67011411ff8b1
[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 /*
135  * Number of times the spin loop can execute during one timer tick
136  */
137 static int s_spinCountPerTick;
138
139 /*
140  * Number of ticks to wait before calibrating the delay loop.
141  */
142 #define CALIBRATE_NUM_TICKS     3
143
144 /*
145  * The default quantum; maximum number of ticks a thread can use before
146  * we suspend it and choose another.
147  */
148 #define DEFAULT_MAX_TICKS 4
149
150 /*
151  * Settable quantum.
152  */
153 int g_Quantum = DEFAULT_MAX_TICKS;
154
155 /*
156  * Ticks per second.
157  * FIXME: should set this to something more reasonable, like 100.
158  */
159 #define HZ 100
160 //#define TICKS_PER_SEC 18
161
162 /*#define DEBUG_TIMER */
163 #ifdef DEBUG_TIMER
164 #  define Debug(args...) Print(args)
165 #else
166 #  define Debug(args...)
167 #endif
168
169 ulong_t clock_time(void){//in millisec
170       return g_numTicks * (1000/HZ);
171 }
172
173 /* ----------------------------------------------------------------------
174  * Private functions
175  * ---------------------------------------------------------------------- */
176
177 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
178 {
179   int i;
180   struct Kernel_Thread* current = g_currentThread;
181
182   Begin_IRQ(state);
183
184   /* Update global and per-thread number of ticks */
185   ++g_numTicks;
186   ++current->numTicks;
187   
188
189   /* update timer events */
190   for (i=0; i < timeEventCount; i++) {
191     if (pendingTimerEvents[i].ticks == 0) {
192       if (timerDebug) Print("timer: event %d expired (%d ticks)\n", 
193                             pendingTimerEvents[i].id, pendingTimerEvents[i].origTicks);
194       (pendingTimerEvents[i].callBack)(pendingTimerEvents[i].id, pendingTimerEvents[i].cb_arg);
195       pendingTimerEvents[i].ticks = pendingTimerEvents[i].origTicks;
196     } else {
197       pendingTimerEvents[i].ticks--;
198     }
199   }
200
201   /*
202    * If thread has been running for an entire quantum,
203    * inform the interrupt return code that we want
204    * to choose a new thread.
205    */
206   if (current->numTicks >= g_Quantum) {
207     g_needReschedule = true;
208     /*
209      * The current process is moved to a lower priority queue,
210      * since it consumed a full quantum.
211      */
212     //if (current->currentReadyQueue < (MAX_QUEUE_LEVEL - 1)) {
213       /*Print("process %d moved to ready queue %d\n", current->pid, current->currentReadyQueue); */
214       //current->currentReadyQueue++;
215     //}
216     
217   }
218   
219   
220   deliver_timer_interrupt_to_vmm(1000000/HZ);
221   
222   End_IRQ(state);
223 }
224
225 /*
226  * Temporary timer interrupt handler used to calibrate
227  * the delay loop.
228  */
229 static void Timer_Calibrate(struct Interrupt_State* state)
230 {
231     Begin_IRQ(state);
232     if (g_numTicks < CALIBRATE_NUM_TICKS)
233         ++g_numTicks;
234     else {
235         /*
236          * Now we can look at EAX, which reflects how many times
237          * the loop has executed
238          */
239         /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
240         s_spinCountPerTick = INT_MAX  - state->eax;
241         state->eax = 0;  /* make the loop terminate */
242     }
243     End_IRQ(state);
244 }
245
246 /*
247  * Delay loop; spins for given number of iterations.
248  */
249 static void Spin(int count)
250 {
251     /*
252      * The assembly code is the logical equivalent of
253      *      while (count-- > 0) { // waste some time }
254      * We rely on EAX being used as the counter
255      * variable.
256      */
257
258     int result;
259     __asm__ __volatile__ (
260         "1: decl %%eax\n\t"
261         "cmpl $0, %%eax\n\t"
262         "nop; nop; nop; nop; nop; nop\n\t"
263         "nop; nop; nop; nop; nop; nop\n\t"
264         "jg 1b"
265         : "=a" (result)
266         : "a" (count)
267     );
268 }
269
270 /*
271  * Calibrate the delay loop.
272  * This will initialize s_spinCountPerTick, which indicates
273  * how many iterations of the loop are executed per timer tick.
274  */
275 static void Calibrate_Delay(void)
276 {
277     Disable_Interrupts();
278
279     /* Install temporarily interrupt handler */
280     Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
281     Enable_IRQ(TIMER_IRQ);
282
283     Enable_Interrupts();
284
285     /* Wait a few ticks */
286     while (g_numTicks < CALIBRATE_NUM_TICKS)
287         ;
288
289     /*
290      * Execute the spin loop.xs
291      * The temporary interrupt handler will overwrite the
292      * loop counter when the next tick occurs.
293      */
294
295
296     Spin(INT_MAX);
297
298
299
300     Disable_Interrupts();
301
302     /*
303      * Mask out the timer IRQ again,
304      * since we will be installing a real timer interrupt handler.
305      */
306     Disable_IRQ(TIMER_IRQ);
307     Enable_Interrupts();
308 }
309
310 /* ----------------------------------------------------------------------
311  * Public functions
312  * ---------------------------------------------------------------------- */
313
314 void Init_Timer(void)
315 {
316   ushort_t foo = 1193182L / HZ;
317   
318   cpu_khz_freq = pit_calibrate_tsc();
319   PrintBoth("CPU KHZ=%lu\n", (ulong_t)cpu_khz_freq);
320
321   PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
322   
323   /* Calibrate for delay loop */
324   Calibrate_Delay();
325   PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
326   
327   // Set Timer to HZ
328
329   Out_Byte(0x43,0x36);          // channel 0, LSB/MSB, mode 3, binary
330   Out_Byte(0x40, foo & 0xff);   // LSB
331   Out_Byte(0x40, foo >>8);      // MSB
332     
333   /* Install an interrupt handler for the timer IRQ */
334
335   Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
336   Enable_IRQ(TIMER_IRQ);
337 }
338
339
340 int Start_Timer_Secs(int seconds, timerCallback cb, void * arg) {
341   return Start_Timer(seconds * HZ, cb, arg);
342 }
343
344
345 int Start_Timer_MSecs(int msecs, timerCallback cb, void * arg) {
346   msecs += 10 - (msecs % 10);
347
348   return Start_Timer(msecs * (HZ / 1000), cb, arg);
349 }
350
351
352
353 int Start_Timer(int ticks, timerCallback cb, void * arg)
354 {
355     int ret;
356
357     KASSERT(!Interrupts_Enabled());
358
359     PrintBoth ("there\n");
360
361     if (timeEventCount == MAX_TIMER_EVENTS) {
362         return -1;
363     } else {
364         ret = nextEventID++;
365         pendingTimerEvents[timeEventCount].id = ret;
366         pendingTimerEvents[timeEventCount].callBack = cb;
367         pendingTimerEvents[timeEventCount].cb_arg = arg;
368         pendingTimerEvents[timeEventCount].ticks = ticks;
369         pendingTimerEvents[timeEventCount].origTicks = ticks;
370         timeEventCount++;
371
372         return ret;
373     }
374 }
375
376
377 int Get_Remaining_Timer_Ticks(int id)
378 {
379     int i;
380
381     KASSERT(!Interrupts_Enabled());
382     for (i=0; i < timeEventCount; i++) {
383         if (pendingTimerEvents[i].id == id) {
384             return pendingTimerEvents[i].ticks;
385         }
386     }
387
388     return -1;
389 }
390
391
392
393 double Get_Remaining_Timer_Secs(int id) {
394   return (Get_Remaining_Timer_Ticks(id) / HZ);
395 }
396
397
398 int Get_Remaining_Timer_MSecs(int id) {
399   return ((Get_Remaining_Timer_Ticks(id) * 1000) / HZ);
400 }
401
402
403
404 int Cancel_Timer(int id)
405 {
406     int i;
407     KASSERT(!Interrupts_Enabled());
408     for (i=0; i < timeEventCount; i++) {
409         if (pendingTimerEvents[i].id == id) {
410             pendingTimerEvents[i] = pendingTimerEvents[timeEventCount-1];
411             timeEventCount--;
412             return 0;
413         }
414     }
415
416     Print("timer: unable to find timer id %d to cancel it\n", id);
417     return -1;
418 }
419
420
421 #define US_PER_TICK (HZ * 1000000)
422
423 /*
424  * Spin for at least given number of microseconds.
425  * FIXME: I'm sure this implementation leaves a lot to
426  * be desired.
427  */
428 void Micro_Delay(int us)
429 {
430     int num = us * s_spinCountPerTick;
431     int denom = US_PER_TICK;
432
433     int numSpins = num / denom;
434     int rem = num % denom;
435
436     if (rem > 0)
437         ++numSpins;
438
439     Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
440
441     Spin(numSpins);
442 }
443
444
445