Fixes bug with reading LAPIC ISR/IRR
[akaros.git] / kern / arch / i686 / apic.c
1 /*
2  * Copyright (c) 2009 The Regents of the University of California
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  * See LICENSE for details.
5  */
6
7 #ifdef __SHARC__
8 #pragma nosharc
9 #define SINIT(x) x
10 #endif
11
12 #include <arch/mmu.h>
13 #include <arch/x86.h>
14 #include <arch/arch.h>
15 #include <arch/apic.h>
16 #include <time.h>
17 #include <assert.h>
18 #include <stdio.h>
19 #include <bitmask.h>
20
21 system_timing_t RO system_timing = {0, 0, 0xffff, 0};
22
23 /* * Remaps the Programmable Interrupt Controller to use IRQs 32-47
24  * http://wiki.osdev.org/PIC
25  * Check osdev for a more thorough explanation/implementation.
26  * http://bochs.sourceforge.net/techspec/PORTS.LST  */
27 void pic_remap() 
28 {
29         /* start initialization (ICW1) */
30         outb(PIC1_CMD, 0x11);
31         outb(PIC2_CMD, 0x11);
32         /* set new offsets (ICW2) */
33         outb(PIC1_DATA, PIC1_OFFSET);
34         outb(PIC2_DATA, PIC2_OFFSET);
35         /* set up cascading (ICW3) */
36         outb(PIC1_DATA, 0x04);
37         outb(PIC2_DATA, 0x02);
38         /* other stuff (put in 8086/88 mode, or whatever) (ICW4) */
39         outb(PIC1_DATA, 0x01);
40         outb(PIC2_DATA, 0x01);
41         /* Init done, further data R/W access the interrupt mask */
42         /* set masks, defaulting to all masked for now */
43         outb(PIC1_DATA, 0xff);
44         outb(PIC2_DATA, 0xff);
45 }
46
47 void pic_mask_irq(uint8_t irq)
48 {
49         if (irq > 7)
50                 outb(PIC2_DATA, inb(PIC2_DATA) | (1 << (irq - 8)));
51         else
52                 outb(PIC1_DATA, inb(PIC1_DATA) | (1 << irq));
53 }
54
55 void pic_unmask_irq(uint8_t irq)
56 {
57         if (irq > 7) {
58                 outb(PIC2_DATA, inb(PIC2_DATA) & ~(1 << (irq - 8)));
59                 outb(PIC1_DATA, inb(PIC1_DATA) & 0xfb); // make sure irq2 is unmasked
60         } else
61                 outb(PIC1_DATA, inb(PIC1_DATA) & ~(1 << irq));
62 }
63
64 /* Aka, the IMR.  Simply reading the data port are OCW1s. */
65 uint16_t pic_get_mask(void)
66 {
67         return (inb(PIC2_DATA) << 8) | inb(PIC1_DATA);
68 }
69
70 static uint16_t __pic_get_irq_reg(int ocw3)
71 {
72         /* OCW3 to PIC CMD to get the register values.  PIC2 is chained, and
73          * represents IRQs 8-15.  PIC1 is IRQs 0-7, with 2 being the chain */
74         outb(PIC1_CMD, ocw3);
75         outb(PIC2_CMD, ocw3);
76         return (inb(PIC2_CMD) << 8) | inb(PIC1_CMD);
77 }
78
79 /* Returns the combined value of the cascaded PICs irq request register */
80 uint16_t pic_get_irr(void)
81 {
82         return __pic_get_irq_reg(PIC_READ_IRR);
83 }
84
85 /* Returns the combined value of the cascaded PICs irq service register */
86 uint16_t pic_get_isr(void)
87 {
88         return __pic_get_irq_reg(PIC_READ_ISR);
89 }
90
91 /* This works for any interrupt that goes through the LAPIC, but not things like
92  * ExtInts.  To prevent abuse, we'll use it just for IPIs for now (which only
93  * come via the APIC).
94  *
95  * Note that if you call this from an interrupt handler for 'vector' before you
96  * EOI, the ISR will show your bit as set.  It is the EOI that clears the bit
97  * from the ISR. */
98 bool ipi_is_pending(uint8_t vector)
99 {
100         /* The ISR/IRR are 256 bits long.  We want to check if 'vector' is set. */
101         return GET_BITMASK_BIT((uint8_t*)LAPIC_ISR, vector) ||
102                GET_BITMASK_BIT((uint8_t*)LAPIC_IRR, vector);
103 }
104
105 /*
106  * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
107  * clock freq is actually the bus clock, which we figure out during timer_init
108  * Unmasking is implied.  Ref SDM, 3A, 9.6.4
109  */
110 void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
111 {
112         // clears bottom bit and then set divider
113         write_mmreg32(LAPIC_TIMER_DIVIDE, (read_mmreg32(LAPIC_TIMER_DIVIDE) &~0xf) |
114                       (div & 0xf));
115         // set LVT with interrupt handling information
116         write_mmreg32(LAPIC_LVT_TIMER, vec | (periodic << 17));
117         write_mmreg32(LAPIC_TIMER_INIT, ticks);
118         // For debugging when we expand this
119         //cprintf("LAPIC LVT Timer: 0x%08x\n", read_mmreg32(LAPIC_LVT_TIMER));
120         //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT));
121         //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT));
122 }
123
124 void lapic_set_timer(uint32_t usec, bool periodic)
125 {
126         // divide the bus clock by 128, which is the max.
127         uint32_t ticks = (usec * system_timing.bus_freq / 128) / 1000000;
128         assert(ticks > 0);
129         __lapic_set_timer(ticks, LAPIC_TIMER_DEFAULT_VECTOR, periodic,
130                           LAPIC_TIMER_DEFAULT_DIVISOR);
131 }
132
133 void set_core_timer(uint32_t usec, bool periodic)
134 {
135         if (usec)
136                 lapic_set_timer(usec, periodic);
137         else
138                 lapic_disable_timer();
139 }
140
141 uint32_t lapic_get_default_id(void)
142 {
143         uint32_t ebx;
144         cpuid(0x1, 0x0, 0, &ebx, 0, 0);
145         // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
146         return (ebx & 0xFF000000) >> 24;
147 }
148
149 // timer init calibrates both tsc timer and lapic timer using PIT
150 void timer_init(void){
151         uint64_t tscval[2];
152         long timercount[2];
153         pit_set_timer(0xffff, TIMER_RATEGEN);
154         // assume tsc exist
155         tscval[0] = read_tsc();
156         udelay_pit(1000000);
157         tscval[1] = read_tsc();
158         system_timing.tsc_freq = SINIT(tscval[1] - tscval[0]);
159         
160         cprintf("TSC Frequency: %llu\n", system_timing.tsc_freq);
161
162         __lapic_set_timer(0xffffffff, LAPIC_TIMER_DEFAULT_VECTOR, FALSE,
163                           LAPIC_TIMER_DEFAULT_DIVISOR);
164         // Mask the LAPIC Timer, so we never receive this interrupt (minor race)
165         mask_lapic_lvt(LAPIC_LVT_TIMER);
166         timercount[0] = read_mmreg32(LAPIC_TIMER_CURRENT);
167         udelay_pit(1000000);
168         timercount[1] = read_mmreg32(LAPIC_TIMER_CURRENT);
169         system_timing.bus_freq = SINIT((timercount[0] - timercount[1])*128);
170                 
171         cprintf("Bus Frequency: %llu\n", system_timing.bus_freq);
172 }
173
174 void pit_set_timer(uint32_t divisor, uint32_t mode)
175 {
176         if (divisor & 0xffff0000)
177                 warn("Divisor too large!");
178         mode = TIMER_SEL0|TIMER_16BIT|mode;
179         outb(TIMER_MODE, mode); 
180         outb(TIMER_CNTR0, divisor & 0xff);
181         outb(TIMER_CNTR0, (divisor >> 8) );
182         system_timing.pit_mode = SINIT(mode);
183         system_timing.pit_divisor = SINIT(divisor);
184         // cprintf("timer mode set to %d, divisor %d\n",mode, divisor);
185 }
186
187 static int getpit()
188 {
189     int high, low;
190         // TODO: need a lock to protect access to PIT
191
192     /* Select timer0 and latch counter value. */
193     outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
194     
195     low = inb(TIMER_CNTR0);
196     high = inb(TIMER_CNTR0);
197
198     return ((high << 8) | low);
199 }
200
201 // forces cpu to relax for usec miliseconds.  declared in kern/include/time.h
202 void udelay(uint64_t usec)
203 {
204         #if !defined(__BOCHS__)
205         if (system_timing.tsc_freq != 0)
206         {
207                 uint64_t start, end, now;
208
209                 start = read_tsc();
210         end = start + usec2tsc(usec);
211         //cprintf("start %llu, end %llu\n", start, end);
212                 if (end == 0) cprintf("This is terribly wrong \n");
213                 do {
214             cpu_relax();
215             now = read_tsc();
216                         //cprintf("now %llu\n", now);
217                 } while (now < end || (now > start && end < start));
218         return;
219
220         } else
221         #endif
222         {
223                 udelay_pit(usec);
224         }
225 }
226
227 void udelay_pit(uint64_t usec)
228 {
229         
230         int64_t delta, prev_tick, tick, ticks_left;
231         prev_tick = getpit();
232         /*
233          * Calculate (n * (i8254_freq / 1e6)) without using floating point
234          * and without any avoidable overflows.
235          */
236         if (usec <= 0)
237                 ticks_left = 0;
238         // some optimization from bsd code
239         else if (usec < 256)
240                 /*
241                  * Use fixed point to avoid a slow division by 1000000.
242                  * 39099 = 1193182 * 2^15 / 10^6 rounded to nearest.
243                  * 2^15 is the first power of 2 that gives exact results
244                  * for n between 0 and 256.
245                  */
246                 ticks_left = ((uint64_t)usec * 39099 + (1 << 15) - 1) >> 15;
247         else
248                 // round up the ticks left
249                 ticks_left = ((uint64_t)usec * (long long)PIT_FREQ+ 999999)
250                              / 1000000; 
251         while (ticks_left > 0) {
252                 tick = getpit();
253                 delta = prev_tick - tick;
254                 prev_tick = tick;
255                 if (delta < 0) {
256                         // counter looped around during the delta time period
257                         delta += system_timing.pit_divisor; // maximum count 
258                         if (delta < 0)
259                                 delta = 0;
260                 }
261                 ticks_left -= delta;
262         }
263 }
264
265 uint64_t gettimer(void)
266 {
267         return read_tsc();      
268 }
269
270 uint64_t getfreq(void)
271 {
272         return system_timing.tsc_freq;
273 }