xref: /plan9/sys/src/9/pc/apic.c (revision afaac077351c6c0d3d2dfc98c64b1a45ead81394)
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