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.


lot of changes
[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.4 $
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     SerialPrintLevel(1,"Host Timer Interrupt Handler Running\n");
210
211     /* Update global and per-thread number of ticks */
212     ++g_numTicks;
213     ++current->numTicks;
214
215
216     /*
217      * If thread has been running for an entire quantum,
218      * inform the interrupt return code that we want
219      * to choose a new thread.
220      */
221     if (current->numTicks >= g_Quantum) {
222         g_needReschedule = true;
223     }
224
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 #define US_PER_TICK (HZ * 1000000)
345
346 /*
347  * Spin for at least given number of microseconds.
348  * FIXME: I'm sure this implementation leaves a lot to
349  * be desired.
350  */
351 void Micro_Delay(int us)
352 {
353     int num = us * s_spinCountPerTick;
354     int denom = US_PER_TICK;
355
356     int numSpins = num / denom;
357     int rem = num % denom;
358
359     if (rem > 0)
360         ++numSpins;
361
362     Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
363
364     Spin(numSpins);
365 }