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.


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