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.


Added RTC functionality (including interrupt injection) to nvram
[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.9 $
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 /*
168  * Number of times the spin loop can execute during one timer tick
169  */
170 static int s_spinCountPerTick;
171
172 /*
173  * Number of ticks to wait before calibrating the delay loop.
174  */
175 #define CALIBRATE_NUM_TICKS     3
176
177 /*
178  * The default quantum; maximum number of ticks a thread can use before
179  * we suspend it and choose another.
180  */
181 #define DEFAULT_MAX_TICKS 4
182
183 /*
184  * Settable quantum.
185  */
186 int g_Quantum = DEFAULT_MAX_TICKS;
187
188 /*
189  * Ticks per second.
190  * FIXME: should set this to something more reasonable, like 100.
191  */
192 #define HZ 100
193 //#define TICKS_PER_SEC 18
194
195 /*#define DEBUG_TIMER */
196 #ifdef DEBUG_TIMER
197 #  define Debug(args...) Print(args)
198 #else
199 #  define Debug(args...)
200 #endif
201
202 /* ----------------------------------------------------------------------
203  * Private functions
204  * ---------------------------------------------------------------------- */
205
206 static void Timer_Interrupt_Handler(struct Interrupt_State* state)
207 {
208    struct Kernel_Thread* current = g_currentThread;
209
210     Begin_IRQ(state);
211
212
213
214 #if 0
215 #define STACK_LEN 256
216
217     SerialPrint("Host Timer Interrupt Handler running\n");
218     SerialPrint("Timer====\n");
219     Dump_Interrupt_State(state);
220     //    SerialMemDump((unsigned char*)(&current),STACK_LEN);
221     SerialPrint("Timer done===\n");
222
223 #endif
224     /* Update global and per-thread number of ticks */
225     ++g_numTicks;
226     ++current->numTicks;
227
228     /*
229      * If thread has been running for an entire quantum,
230      * inform the interrupt return code that we want
231      * to choose a new thread.
232      */
233     if (current->numTicks >= g_Quantum) {
234         g_needReschedule = true;
235     }
236
237
238     deliver_timer_interrupt_to_vmm(1000000/HZ);
239
240     End_IRQ(state);
241 }
242
243 /*
244  * Temporary timer interrupt handler used to calibrate
245  * the delay loop.
246  */
247 static void Timer_Calibrate(struct Interrupt_State* state)
248 {
249     Begin_IRQ(state);
250     if (g_numTicks < CALIBRATE_NUM_TICKS)
251         ++g_numTicks;
252     else {
253         /*
254          * Now we can look at EAX, which reflects how many times
255          * the loop has executed
256          */
257         /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
258         s_spinCountPerTick = INT_MAX  - state->eax;
259         state->eax = 0;  /* make the loop terminate */
260     }
261     End_IRQ(state);
262 }
263
264 /*
265  * Delay loop; spins for given number of iterations.
266  */
267 static void Spin(int count)
268 {
269     /*
270      * The assembly code is the logical equivalent of
271      *      while (count-- > 0) { // waste some time }
272      * We rely on EAX being used as the counter
273      * variable.
274      */
275
276     int result;
277     __asm__ __volatile__ (
278         "1: decl %%eax\n\t"
279         "cmpl $0, %%eax\n\t"
280         "nop; nop; nop; nop; nop; nop\n\t"
281         "nop; nop; nop; nop; nop; nop\n\t"
282         "jg 1b"
283         : "=a" (result)
284         : "a" (count)
285     );
286 }
287
288 /*
289  * Calibrate the delay loop.
290  * This will initialize s_spinCountPerTick, which indicates
291  * how many iterations of the loop are executed per timer tick.
292  */
293 static void Calibrate_Delay(void)
294 {
295     Disable_Interrupts();
296
297     /* Install temporarily interrupt handler */
298     Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
299     Enable_IRQ(TIMER_IRQ);
300
301     Enable_Interrupts();
302
303     /* Wait a few ticks */
304     while (g_numTicks < CALIBRATE_NUM_TICKS)
305         ;
306
307     /*
308      * Execute the spin loop.xs
309      * The temporary interrupt handler will overwrite the
310      * loop counter when the next tick occurs.
311      */
312
313
314     Spin(INT_MAX);
315
316
317
318     Disable_Interrupts();
319
320     /*
321      * Mask out the timer IRQ again,
322      * since we will be installing a real timer interrupt handler.
323      */
324     Disable_IRQ(TIMER_IRQ);
325     Enable_Interrupts();
326 }
327
328 /* ----------------------------------------------------------------------
329  * Public functions
330  * ---------------------------------------------------------------------- */
331
332 void Init_Timer(void)
333 {
334   ushort_t foo = 1193182L / HZ;
335   
336   cpu_khz_freq = pit_calibrate_tsc();
337   PrintBoth("CPU KHZ=%lu\n", (ulong_t)cpu_khz_freq);
338
339   PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
340   
341   /* Calibrate for delay loop */
342   Calibrate_Delay();
343   PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
344   
345   // Set Timer to HZ
346
347   Out_Byte(0x43,0x36);          // channel 0, LSB/MSB, mode 3, binary
348   Out_Byte(0x40, foo & 0xff);   // LSB
349   Out_Byte(0x40, foo >>8);      // MSB
350     
351   /* Install an interrupt handler for the timer IRQ */
352
353   Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
354   Enable_IRQ(TIMER_IRQ);
355 }
356
357
358 #define US_PER_TICK (HZ * 1000000)
359
360 /*
361  * Spin for at least given number of microseconds.
362  * FIXME: I'm sure this implementation leaves a lot to
363  * be desired.
364  */
365 void Micro_Delay(int us)
366 {
367     int num = us * s_spinCountPerTick;
368     int denom = US_PER_TICK;
369
370     int numSpins = num / denom;
371     int rem = num % denom;
372
373     if (rem > 0)
374         ++numSpins;
375
376     Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
377
378     Spin(numSpins);
379 }