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.


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