Added some function to read the tsc with start/stop semantics
[akaros.git] / kern / apic.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * See LICENSE for details.
4  */
5
6 #include <inc/mmu.h>
7 #include <inc/x86.h>
8 #include <inc/assert.h>
9
10 #include <kern/apic.h>
11
12 system_timing_t system_timing = {0, 0, 0xffff, 0};
13 uint64_t timing_overhead = 0;
14
15 /*
16  * Remaps the Programmable Interrupt Controller to use IRQs 32-47
17  * http://wiki.osdev.org/PIC
18  * Not 100% on this stuff, after looking over 
19  * http://bochs.sourceforge.net/techspec/PORTS.LST  The cascading and other 
20  * stuff might need to be in one command, and after that all we are doing
21  * is toggling masks.
22  */
23 void pic_remap() 
24 {
25         // start initialization
26         outb(PIC1_CMD, 0x11);
27         outb(PIC2_CMD, 0x11);
28         // set new offsets
29         outb(PIC1_DATA, PIC1_OFFSET);
30         outb(PIC2_DATA, PIC2_OFFSET);
31         // set up cascading
32         outb(PIC1_DATA, 0x04);
33         outb(PIC2_DATA, 0x02);
34         // other stuff (put in 8086/88 mode, or whatever)
35         outb(PIC1_DATA, 0x01);
36         outb(PIC2_DATA, 0x01);
37         // set masks, defaulting to all masked for now
38         outb(PIC1_DATA, 0xff);
39         outb(PIC2_DATA, 0xff);
40 }
41
42 void pic_mask_irq(uint8_t irq)
43 {
44         if (irq > 7)
45                 outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
46         else
47                 outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
48 }
49
50 void pic_unmask_irq(uint8_t irq)
51 {
52         if (irq > 7) {
53                 outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
54                 outb(PIC1_DATA, inb(PIC1_DATA) & 0xfd); // make sure irq2 is unmasked
55         } else
56                 outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
57 }
58
59
60 /*
61  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
62  * clock freq is actually the bus clock, which we figure out during timer_init
63  * Unmasking is implied.  Ref SDM, 3A, 9.6.4
64  */
65 void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
66 {
67         // clears bottom bit and then set divider
68         write_mmreg32(LAPIC_TIMER_DIVIDE, (read_mmreg32(LAPIC_TIMER_DIVIDE) &~0xf) |
69                       (div & 0xf));
70         // set LVT with interrupt handling information
71         write_mmreg32(LAPIC_LVT_TIMER, vec | (periodic << 17));
72         write_mmreg32(LAPIC_TIMER_INIT, ticks);
73         // For debugging when we expand this
74         //cprintf("LAPIC LVT Timer: 0x%08x\n", read_mmreg32(LAPIC_LVT_TIMER));
75         //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT));
76         //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT));
77 }
78
79 void lapic_set_timer(uint32_t usec, bool periodic)
80 {
81         // divide the bus clock by 128, which is the max.
82         uint32_t ticks = (usec * system_timing.bus_freq / 128) / 1000000;
83         __lapic_set_timer(ticks, LAPIC_TIMER_DEFAULT_VECTOR, periodic,
84                           LAPIC_TIMER_DEFAULT_DIVISOR);
85 }
86
87 uint32_t lapic_get_default_id(void)
88 {
89         uint32_t ebx;
90         cpuid(1, 0, &ebx, 0, 0);
91         // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
92         return (ebx & 0xFF000000) >> 24;
93 }
94
95 uint64_t read_tsc_serialized() __attribute__((noinline)) 
96 {
97         cpuid(0, 0, 0, 0, 0);
98         return read_tsc();
99 }
100
101 uint64_t start_timing() __attribute__((noinline)) 
102 {
103         return read_tsc_serialized();
104 }
105
106 uint64_t stop_timing(uint64_t val) __attribute__((noinline)) 
107 {
108         return (read_tsc_serialized() - val - timing_overhead);
109 }
110
111 void train_timing() 
112 {
113         uint16_t num_runs = 0;
114         uint16_t num_times_zero = 0;
115         uint64_t cum_overhead = 0;
116         
117         //Do this while 1/3 of all runs have not equaled 0 yet
118         do {
119                 /* Run 100 times, counting how many out of that 100 
120                  * were equal to 0, adjusting the global timing_overhead
121                  * in the process.
122                  */
123                 for(int i=0; i<100; i++) {
124                         uint64_t time = start_timing();
125                         uint64_t diff = stop_timing(time);
126                         
127                         /* In case diff was negative, I want to add its absolute value
128                          * to the cumulative error, otherwise, just diff itself
129                          */
130                         if((int64_t)diff < 0)
131                                 diff = (uint64_t)(~0) - diff; 
132                         cum_overhead += diff;
133
134                         //TODO: Consider using diff < 2  instead of 
135                         // strictly == 0 in case forcing a 0 restriction is too strict 
136                         if(diff == 0) num_times_zero++;
137                         num_runs++;
138                         timing_overhead = (cum_overhead/num_runs);
139                         printk("Timing Differences: %llu\n", diff);
140                 }
141         } while(num_runs/num_times_zero > 2);
142 }
143
144 // timer init calibrates both tsc timer and lapic timer using PIT
145 void timer_init(void){
146         uint64_t tscval[2];
147         long timercount[2];
148         pit_set_timer(0xffff, TIMER_RATEGEN);
149         // assume tsc exist
150         tscval[0] = read_tsc();
151         udelay_pit(1000000);
152         tscval[1] = read_tsc();
153         system_timing.tsc_freq = tscval[1] - tscval[0];
154         
155         cprintf("TSC Frequency: %llu\n", system_timing.tsc_freq);
156
157         __lapic_set_timer(0xffffffff, LAPIC_TIMER_DEFAULT_VECTOR, FALSE,
158                           LAPIC_TIMER_DEFAULT_DIVISOR);
159         // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
160         mask_lapic_lvt(LAPIC_LVT_TIMER);
161         timercount[0] = read_mmreg32(LAPIC_TIMER_CURRENT);
162         udelay_pit(1000000);
163         timercount[1] = read_mmreg32(LAPIC_TIMER_CURRENT);
164         system_timing.bus_freq = (timercount[0] - timercount[1])*128;
165                 
166         cprintf("Bus Frequency: %llu\n", system_timing.bus_freq);
167         train_timing();
168 }
169
170 void pit_set_timer(uint32_t divisor, uint32_t mode)
171 {
172         if (divisor & 0xffff0000)
173                 warn("Divisor too large!");
174         mode = TIMER_SEL0|TIMER_16BIT|mode;
175         outb(TIMER_MODE, mode); 
176         outb(TIMER_CNTR0, divisor & 0xff);
177         outb(TIMER_CNTR0, (divisor >> 8) );
178         system_timing.pit_mode = mode;
179         system_timing.pit_divisor = divisor;
180         // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
181 }
182
183 static int getpit()
184 {
185     int high, low;
186         // TODO: need a lock to protect access to PIT
187
188     /* Select timer0 and latch counter value. */
189     outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
190     
191     low = inb(TIMER_CNTR0);
192     high = inb(TIMER_CNTR0);
193
194     return ((high << 8) | low);
195 }
196
197 // forces cpu to relax for usec miliseconds
198 void udelay(uint64_t usec)
199 {
200         #if !defined(__BOCHS__)
201         if (system_timing.tsc_freq != 0)
202         {
203                 uint64_t start, end, now;
204
205                 start = read_tsc();
206         end = start + (system_timing.tsc_freq * usec) / 1000000;
207         //cprintf("start %llu, end %llu\n", start, end);
208                 if (end == 0) cprintf("This is terribly wrong \n");
209                 do {
210             cpu_relax();
211             now = read_tsc();
212                         //cprintf("now %llu\n", now);
213                 } while (now < end || (now > start && end < start));
214         return;
215
216         } else
217         #endif
218         {
219                 udelay_pit(usec);
220         }
221 }
222
223 void udelay_pit(uint64_t usec)
224 {
225         
226         int64_t delta, prev_tick, tick, ticks_left;
227         prev_tick = getpit();
228         /*
229          * Calculate (n * (i8254_freq / 1e6)) without using floating point
230          * and without any avoidable overflows.
231          */
232         if (usec <= 0)
233                 ticks_left = 0;
234         // some optimization from bsd code
235         else if (usec < 256)
236                 /*
237                  * Use fixed point to avoid a slow division by 1000000.
238                  * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
239                  * 2^15 is the first power of 2 that gives exact results
240                  * for n between 0 and 256.
241                  */
242                 ticks_left = ((uint64_t)usec * 39099 + (1 << 15) - 1) >> 15;
243         else
244                 // round up the ticks left
245                 ticks_left = ((uint64_t)usec * (long long)PIT_FREQ+ 999999)
246                              / 1000000; 
247         while (ticks_left > 0) {
248                 tick = getpit();
249                 delta = prev_tick - tick;
250                 prev_tick = tick;
251                 if (delta < 0) {
252                         // counter looped around during the delta time period
253                         delta += system_timing.pit_divisor; // maximum count 
254                         if (delta < 0)
255                                 delta = 0;
256                 }
257                 ticks_left -= delta;
258         }
259 }
260
261 uint64_t gettimer(void)
262 {
263         return read_tsc();      
264 }
265
266 uint64_t getfreq(void)
267 {
268         return system_timing.tsc_freq;
269 }
270