1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7
8 #include "mp.h"
9
10 enum { /* Local APIC registers */
11 LapicID = 0x0020, /* ID */
12 LapicVER = 0x0030, /* Version */
13 LapicTPR = 0x0080, /* Task Priority */
14 LapicAPR = 0x0090, /* Arbitration Priority */
15 LapicPPR = 0x00A0, /* Processor Priority */
16 LapicEOI = 0x00B0, /* EOI */
17 LapicLDR = 0x00D0, /* Logical Destination */
18 LapicDFR = 0x00E0, /* Destination Format */
19 LapicSVR = 0x00F0, /* Spurious Interrupt Vector */
20 LapicISR = 0x0100, /* Interrupt Status (8 registers) */
21 LapicTMR = 0x0180, /* Trigger Mode (8 registers) */
22 LapicIRR = 0x0200, /* Interrupt Request (8 registers) */
23 LapicESR = 0x0280, /* Error Status */
24 LapicICRLO = 0x0300, /* Interrupt Command */
25 LapicICRHI = 0x0310, /* Interrupt Command [63:32] */
26 LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */
27 LapicPCINT = 0x0340, /* Performance Counter LVT */
28 LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */
29 LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */
30 LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */
31 LapicTICR = 0x0380, /* Timer Initial Count */
32 LapicTCCR = 0x0390, /* Timer Current Count */
33 LapicTDCR = 0x03E0, /* Timer Divide Configuration */
34 };
35
36 enum { /* LapicSVR */
37 LapicENABLE = 0x00000100, /* Unit Enable */
38 LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */
39 };
40
41 enum { /* LapicICRLO */
42 /* [14] IPI Trigger Mode Level (RW) */
43 LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */
44 LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */
45
46 /* [17:16] Remote Read Status */
47 LapicINVALID = 0x00000000, /* Invalid */
48 LapicWAIT = 0x00010000, /* In-Progress */
49 LapicVALID = 0x00020000, /* Valid */
50
51 /* [19:18] Destination Shorthand */
52 LapicFIELD = 0x00000000, /* No shorthand */
53 LapicSELF = 0x00040000, /* Self is single destination */
54 LapicALLINC = 0x00080000, /* All including self */
55 LapicALLEXC = 0x000C0000, /* All Excluding self */
56 };
57
58 enum { /* LapicESR */
59 LapicSENDCS = 0x00000001, /* Send CS Error */
60 LapicRCVCS = 0x00000002, /* Receive CS Error */
61 LapicSENDACCEPT = 0x00000004, /* Send Accept Error */
62 LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */
63 LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */
64 LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */
65 LapicREGISTER = 0x00000080, /* Illegal Register Address */
66 };
67
68 enum { /* LapicTIMER */
69 /* [17] Timer Mode (RW) */
70 LapicONESHOT = 0x00000000, /* One-shot */
71 LapicPERIODIC = 0x00020000, /* Periodic */
72
73 /* [19:18] Timer Base (RW) */
74 LapicCLKIN = 0x00000000, /* use CLKIN as input */
75 LapicTMBASE = 0x00040000, /* use TMBASE */
76 LapicDIVIDER = 0x00080000, /* use output of the divider */
77 };
78
79 enum { /* LapicTDCR */
80 LapicX2 = 0x00000000, /* divide by 2 */
81 LapicX4 = 0x00000001, /* divide by 4 */
82 LapicX8 = 0x00000002, /* divide by 8 */
83 LapicX16 = 0x00000003, /* divide by 16 */
84 LapicX32 = 0x00000008, /* divide by 32 */
85 LapicX64 = 0x00000009, /* divide by 64 */
86 LapicX128 = 0x0000000A, /* divide by 128 */
87 LapicX1 = 0x0000000B, /* divide by 1 */
88 };
89
90 static ulong* lapicbase;
91
92 struct
93 {
94 uvlong hz;
95 ulong max;
96 ulong min;
97 ulong div;
98 } lapictimer;
99
100 static ulong
lapicr(int r)101 lapicr(int r)
102 {
103 if(lapicbase == 0)
104 panic("lapicr: no lapic");
105 return *(lapicbase+(r/sizeof(*lapicbase)));
106 }
107
108 static void
lapicw(int r,ulong data)109 lapicw(int r, ulong data)
110 {
111 if(lapicbase == 0)
112 panic("lapicw: no lapic");
113 *(lapicbase+(r/sizeof(*lapicbase))) = data;
114 data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
115 USED(data);
116 }
117
118 void
lapiconline(void)119 lapiconline(void)
120 {
121 /*
122 * Reload the timer to de-synchronise the processors,
123 * then lower the task priority to allow interrupts to be
124 * accepted by the APIC.
125 */
126 microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
127 lapicw(LapicTICR, lapictimer.max);
128 lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
129
130 lapicw(LapicTPR, 0);
131 }
132
133 /*
134 * use the i8253 clock to figure out our lapic timer rate.
135 */
136 static void
lapictimerinit(void)137 lapictimerinit(void)
138 {
139 uvlong x, v, hz;
140
141 v = m->cpuhz/1000;
142 lapicw(LapicTDCR, LapicX1);
143 lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
144
145 if(lapictimer.hz == 0ULL){
146 x = fastticks(&hz);
147 x += hz/10;
148 lapicw(LapicTICR, 0xffffffff);
149 do{
150 v = fastticks(nil);
151 }while(v < x);
152
153 lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10;
154 lapictimer.max = lapictimer.hz/HZ;
155 lapictimer.min = lapictimer.hz/(100*HZ);
156
157 if(lapictimer.hz > hz-(hz/10)){
158 if(lapictimer.hz > hz+(hz/10))
159 panic("lapic clock %lld > cpu clock > %lld\n",
160 lapictimer.hz, hz);
161 lapictimer.hz = hz;
162 }
163 assert(lapictimer.hz != 0);
164 lapictimer.div = hz/lapictimer.hz;
165 }
166 }
167
168 void
lapicinit(Apic * apic)169 lapicinit(Apic* apic)
170 {
171 ulong dfr, ldr, lvt;
172
173 if(lapicbase == 0)
174 lapicbase = apic->addr;
175 if(lapicbase == 0) {
176 print("lapicinit: no lapic\n");
177 return;
178 }
179
180 /*
181 * These don't really matter in Physical mode;
182 * set the defaults anyway.
183 */
184 if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
185 dfr = 0xf0000000;
186 else
187 dfr = 0xffffffff;
188 ldr = 0x00000000;
189
190 lapicw(LapicDFR, dfr);
191 lapicw(LapicLDR, ldr);
192 lapicw(LapicTPR, 0xff);
193 lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
194
195 lapictimerinit();
196
197 /*
198 * Some Pentium revisions have a bug whereby spurious
199 * interrupts are generated in the through-local mode.
200 */
201 switch(m->cpuidax & 0xFFF){
202 case 0x526: /* stepping cB1 */
203 case 0x52B: /* stepping E0 */
204 case 0x52C: /* stepping cC0 */
205 wrmsr(0x0E, 1<<14); /* TR12 */
206 break;
207 }
208
209 /*
210 * Set the local interrupts. It's likely these should just be
211 * masked off for SMP mode as some Pentium Pros have problems if
212 * LINT[01] are set to ExtINT.
213 * Acknowledge any outstanding interrupts.
214 lapicw(LapicLINT0, apic->lintr[0]);
215 lapicw(LapicLINT1, apic->lintr[1]);
216 */
217 lapiceoi(0);
218
219 lvt = (lapicr(LapicVER)>>16) & 0xFF;
220 if(lvt >= 4)
221 lapicw(LapicPCINT, ApicIMASK|(VectorPIC+IrqPCINT));
222 lapicw(LapicERROR, VectorPIC+IrqERROR);
223 lapicw(LapicESR, 0);
224 lapicr(LapicESR);
225
226 /*
227 * Issue an INIT Level De-Assert to synchronise arbitration ID's.
228 */
229 lapicw(LapicICRHI, 0);
230 lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
231 while(lapicr(LapicICRLO) & ApicDELIVS)
232 ;
233
234 /*
235 * Do not allow acceptance of interrupts until all initialisation
236 * for this processor is done. For the bootstrap processor this can be
237 * early during initialisation. For the application processors this should
238 * be after the bootstrap processor has lowered priority and is accepting
239 * interrupts.
240 lapicw(LapicTPR, 0);
241 */
242 }
243
244 void
lapicstartap(Apic * apic,int v)245 lapicstartap(Apic* apic, int v)
246 {
247 int i;
248 ulong crhi;
249
250 /* make apic's processor do a warm reset */
251 crhi = apic->apicno<<24;
252 lapicw(LapicICRHI, crhi);
253 lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
254 microdelay(200);
255 lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
256 delay(10);
257
258 /* assumes apic is not an 82489dx */
259 for(i = 0; i < 2; i++){
260 lapicw(LapicICRHI, crhi);
261 /* make apic's processor start at v in real mode */
262 lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
263 microdelay(200);
264 }
265 }
266
267 void
lapicerror(Ureg *,void *)268 lapicerror(Ureg*, void*)
269 {
270 ulong esr;
271
272 lapicw(LapicESR, 0);
273 esr = lapicr(LapicESR);
274 switch(m->cpuidax & 0xFFF){
275 case 0x526: /* stepping cB1 */
276 case 0x52B: /* stepping E0 */
277 case 0x52C: /* stepping cC0 */
278 return;
279 }
280 print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
281 }
282
283 void
lapicspurious(Ureg *,void *)284 lapicspurious(Ureg*, void*)
285 {
286 print("cpu%d: lapicspurious\n", m->machno);
287 }
288
289 int
lapicisr(int v)290 lapicisr(int v)
291 {
292 ulong isr;
293
294 isr = lapicr(LapicISR + (v/32));
295
296 return isr & (1<<(v%32));
297 }
298
299 int
lapiceoi(int v)300 lapiceoi(int v)
301 {
302 lapicw(LapicEOI, 0);
303
304 return v;
305 }
306
307 void
lapicicrw(ulong hi,ulong lo)308 lapicicrw(ulong hi, ulong lo)
309 {
310 lapicw(LapicICRHI, hi);
311 lapicw(LapicICRLO, lo);
312 }
313
314 void
ioapicrdtr(Apic * apic,int sel,int * hi,int * lo)315 ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
316 {
317 ulong *iowin;
318
319 iowin = apic->addr+(0x10/sizeof(ulong));
320 sel = IoapicRDT + 2*sel;
321
322 lock(apic);
323 *apic->addr = sel+1;
324 if(hi)
325 *hi = *iowin;
326 *apic->addr = sel;
327 if(lo)
328 *lo = *iowin;
329 unlock(apic);
330 }
331
332 void
ioapicrdtw(Apic * apic,int sel,int hi,int lo)333 ioapicrdtw(Apic* apic, int sel, int hi, int lo)
334 {
335 ulong *iowin;
336
337 iowin = apic->addr+(0x10/sizeof(ulong));
338 sel = IoapicRDT + 2*sel;
339
340 lock(apic);
341 *apic->addr = sel+1;
342 *iowin = hi;
343 *apic->addr = sel;
344 *iowin = lo;
345 unlock(apic);
346 }
347
348 void
ioapicinit(Apic * apic,int apicno)349 ioapicinit(Apic* apic, int apicno)
350 {
351 int hi, lo, v;
352 ulong *iowin;
353
354 /*
355 * Initialise the I/O APIC.
356 * The MultiProcessor Specification says it is the responsibility
357 * of the O/S to set the APIC id.
358 * Make sure interrupts are all masked off for now.
359 */
360 iowin = apic->addr+(0x10/sizeof(ulong));
361 lock(apic);
362 *apic->addr = IoapicVER;
363 apic->mre = (*iowin>>16) & 0xFF;
364
365 *apic->addr = IoapicID;
366 *iowin = apicno<<24;
367 unlock(apic);
368
369 hi = 0;
370 lo = ApicIMASK;
371 for(v = 0; v <= apic->mre; v++)
372 ioapicrdtw(apic, v, hi, lo);
373 }
374
375 void
lapictimerset(uvlong next)376 lapictimerset(uvlong next)
377 {
378 vlong period;
379 int x;
380
381 x = splhi();
382 lock(&m->apictimerlock);
383
384 period = lapictimer.max;
385 if(next != 0){
386 period = next - fastticks(nil);
387 if (lapictimer.div == 0)
388 panic("lapictimerset: zero lapictimer.div");
389 period /= lapictimer.div;
390
391 if(period < lapictimer.min)
392 period = lapictimer.min;
393 else if(period > lapictimer.max - lapictimer.min)
394 period = lapictimer.max;
395 }
396 lapicw(LapicTICR, period);
397
398 unlock(&m->apictimerlock);
399 splx(x);
400 }
401
402 void
lapicclock(Ureg * u,void *)403 lapicclock(Ureg *u, void*)
404 {
405 /*
406 * since the MTRR updates need to be synchronized across processors,
407 * we want to do this within the clock tick.
408 */
409 mtrrclock();
410 timerintr(u, 0);
411 }
412
413 void
lapicintron(void)414 lapicintron(void)
415 {
416 lapicw(LapicTPR, 0);
417 }
418
419 void
lapicintroff(void)420 lapicintroff(void)
421 {
422 lapicw(LapicTPR, 0xFF);
423 }
424
425 void
lapicnmienable(void)426 lapicnmienable(void)
427 {
428 /*
429 * On the one hand the manual says the vector information
430 * is ignored if the delivery mode is NMI, and on the other
431 * a "Receive Illegal Vector" should be generated for a
432 * vector in the range 0 through 15.
433 * Some implementations generate the error interrupt if the
434 * NMI vector is invalid, so always give a valid value.
435 */
436 if (lapicbase)
437 lapicw(LapicPCINT, ApicNMI|(VectorPIC+IrqPCINT));
438 else
439 print("lapicnmienable: no lapic\n");
440 }
441
442 void
lapicnmidisable(void)443 lapicnmidisable(void)
444 {
445 if (lapicbase)
446 lapicw(LapicPCINT, ApicIMASK|(VectorPIC+IrqPCINT));
447 else
448 print("lapicnmidisable: no lapic\n");
449 }
450