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.


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