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.


17d6f1c30bfe58bfa52a5c6a34e5e16a64581f24
[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  * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
6  * $Revision: 1.11 $
7  * 
8  * This is free software.  You are permitted to use,
9  * redistribute, and modify it as specified in the file "COPYING".
10  */
11
12 #include <limits.h>
13 #include <geekos/io.h>
14 #include <geekos/int.h>
15 #include <geekos/irq.h>
16 #include <geekos/kthread.h>
17 #include <geekos/timer.h>
18
19 #include <geekos/serial.h>
20 #include <geekos/debug.h>
21
22 #include <geekos/io_defs.h>
23
24 /* PAD this currently is in nvram.c */
25 extern void deliver_timer_interrupt_to_vmm(uint_t period_us);
26
27 /* JRL Add a cpu frequency measurement */
28 uint_t cpu_khz_freq;
29
30
31
32
33
34
35
36
37
38
39 #if defined(__i386__)
40
41 #define rdtscll(val)                            \
42      __asm__ __volatile__("rdtsc" : "=A" (val))
43
44 #elif defined(__x86_64__)
45
46 #define rdtscll(val) do {                                   \
47     unsigned int a,d;                                       \
48     asm volatile("rdtsc" : "=a" (a), "=d" (d));             \
49     (val) = ((unsigned long)a) | (((unsigned long)d)<<32);  \
50   } while(0)
51
52 #endif
53
54 #define do_div(n,base) ({                                    \
55       unsigned long __upper, __low, __high, __mod, __base;   \
56       __base = (base);                                       \
57       asm("":"=a" (__low), "=d" (__high):"A" (n));           \
58       __upper = __high;                                      \
59       if (__high) {                                          \
60         __upper = __high % (__base);                         \
61         __high = __high / (__base);                          \
62       }                                                                 \
63       asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
64       asm("":"=A" (n):"a" (__low),"d" (__high));                        \
65       __mod;                                                            \
66     })
67
68
69 /**
70  * This uses the Programmable Interval Timer that is standard on all
71  * PC-compatible systems to determine the time stamp counter frequency.
72  *
73  * This uses the speaker output (channel 2) of the PIT. This is better than
74  * using the timer interrupt output because we can read the value of the
75  * speaker with just one inb(), where we need three i/o operations for the
76  * interrupt channel. We count how many ticks the TSC does in 50 ms.
77  *
78  * Returns the detected time stamp counter frequency in KHz.
79  */
80
81
82 static unsigned int 
83 pit_calibrate_tsc(void)
84 {
85   uint_t khz = 0;
86   ullong_t start, end;
87   //        unsigned long flags;
88   unsigned long pit_tick_rate = 1193182UL;  /* 1.193182 MHz */
89   
90   //        spin_lock_irqsave(&pit_lock, flags);
91   
92   outb((inb(0x61) & ~0x02) | 0x01, 0x61);
93   
94   outb(0xb0, 0x43);
95   outb((pit_tick_rate / (1000 / 50)) & 0xff, 0x42);
96   outb((pit_tick_rate / (1000 / 50)) >> 8, 0x42);
97   //        start = get_cycles_sync();
98   rdtscll(start);
99   while ((inb(0x61) & 0x20) == 0);
100   rdtscll(end);
101   //   end = get_cycles_sync();
102   
103   //        spin_unlock_irqrestore(&pit_lock, flags);
104   
105   
106   //  return (end - start) / 50;
107   khz = end - start;
108 ;
109
110   return khz / 50;
111 }
112
113
114
115 /* END JRL */
116
117
118
119 #define MAX_TIMER_EVENTS        100
120
121 static int timerDebug = 0;
122 static int timeEventCount;
123 static int nextEventID;
124 timerEvent pendingTimerEvents[MAX_TIMER_EVENTS];
125
126
127
128 /*
129  * Global tick counter
130  */
131 volatile ulong_t g_numTicks;
132
133 ulong_t clock_time(void){
134         return g_numTicks;
135 }
136
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 /* ----------------------------------------------------------------------
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);
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(int ticks, timerCallback cb)
341 {
342     int ret;
343
344     KASSERT(!Interrupts_Enabled());
345
346     if (timeEventCount == MAX_TIMER_EVENTS) {
347         return -1;
348     } else {
349         ret = nextEventID++;
350         pendingTimerEvents[timeEventCount].id = ret;
351         pendingTimerEvents[timeEventCount].callBack = cb;
352         pendingTimerEvents[timeEventCount].ticks = ticks;
353         pendingTimerEvents[timeEventCount].origTicks = ticks;
354         timeEventCount++;
355
356         return ret;
357     }
358 }
359
360 int Get_Remaing_Timer_Ticks(int id)
361 {
362     int i;
363
364     KASSERT(!Interrupts_Enabled());
365     for (i=0; i < timeEventCount; i++) {
366         if (pendingTimerEvents[i].id == id) {
367             return pendingTimerEvents[i].ticks;
368         }
369     }
370
371     return -1;
372 }
373
374 int Cancel_Timer(int id)
375 {
376     int i;
377     KASSERT(!Interrupts_Enabled());
378     for (i=0; i < timeEventCount; i++) {
379         if (pendingTimerEvents[i].id == id) {
380             pendingTimerEvents[i] = pendingTimerEvents[timeEventCount-1];
381             timeEventCount--;
382             return 0;
383         }
384     }
385
386     Print("timer: unable to find timer id %d to cancel it\n", id);
387     return -1;
388 }
389
390
391 #define US_PER_TICK (HZ * 1000000)
392
393 /*
394  * Spin for at least given number of microseconds.
395  * FIXME: I'm sure this implementation leaves a lot to
396  * be desired.
397  */
398 void Micro_Delay(int us)
399 {
400     int num = us * s_spinCountPerTick;
401     int denom = US_PER_TICK;
402
403     int numSpins = num / denom;
404     int rem = num % denom;
405
406     if (rem > 0)
407         ++numSpins;
408
409     Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
410
411     Spin(numSpins);
412 }