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.


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