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.


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