1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6
7 #include "apic.h"
8 #include "io.h"
9
10 enum { /* Local APIC registers */
11 Id = 0x0020, /* Identification */
12 Ver = 0x0030, /* Version */
13 Tp = 0x0080, /* Task Priority */
14 Ap = 0x0090, /* Arbitration Priority */
15 Pp = 0x00a0, /* Processor Priority */
16 Eoi = 0x00b0, /* EOI */
17 Ld = 0x00d0, /* Logical Destination */
18 Df = 0x00e0, /* Destination Format */
19 Siv = 0x00f0, /* Spurious Interrupt Vector */
20 Is = 0x0100, /* Interrupt Status (8) */
21 Tm = 0x0180, /* Trigger Mode (8) */
22 Ir = 0x0200, /* Interrupt Request (8) */
23 Es = 0x0280, /* Error Status */
24 Iclo = 0x0300, /* Interrupt Command */
25 Ichi = 0x0310, /* Interrupt Command [63:32] */
26 Lvt0 = 0x0320, /* Local Vector Table 0 */
27 Lvt5 = 0x0330, /* Local Vector Table 5 */
28 Lvt4 = 0x0340, /* Local Vector Table 4 */
29 Lvt1 = 0x0350, /* Local Vector Table 1 */
30 Lvt2 = 0x0360, /* Local Vector Table 2 */
31 Lvt3 = 0x0370, /* Local Vector Table 3 */
32 Tic = 0x0380, /* Timer Initial Count */
33 Tcc = 0x0390, /* Timer Current Count */
34 Tdc = 0x03e0, /* Timer Divide Configuration */
35
36 Tlvt = Lvt0, /* Timer */
37 Lint0 = Lvt1, /* Local Interrupt 0 */
38 Lint1 = Lvt2, /* Local Interrupt 1 */
39 Elvt = Lvt3, /* Error */
40 Pmclvt = Lvt4, /* Performance Mon. Counter */
41 Thslvt = Lvt5, /* Thermal Sensor */
42 };
43
44 enum { /* Siv */
45 Swen = 0x00000100, /* Software Enable */
46 Fdis = 0x00000200, /* Focus Disable */
47 };
48
49 enum { /* Iclo */
50 Lassert = 0x00004000, /* Assert level */
51
52 DSnone = 0x00000000, /* Use Destination Field */
53 DSself = 0x00040000, /* Self is only destination */
54 DSallinc = 0x00080000, /* All including self */
55 DSallexc = 0x000c0000, /* All Excluding self */
56 };
57
58 enum { /* Tlvt */
59 Periodic = 0x00020000, /* Periodic Timer Mode */
60 };
61
62 enum { /* Tdc */
63 DivX2 = 0x00000000, /* Divide by 2 */
64 DivX4 = 0x00000001, /* Divide by 4 */
65 DivX8 = 0x00000002, /* Divide by 8 */
66 DivX16 = 0x00000003, /* Divide by 16 */
67 DivX32 = 0x00000008, /* Divide by 32 */
68 DivX64 = 0x00000009, /* Divide by 64 */
69 DivX128 = 0x0000000a, /* Divide by 128 */
70 DivX1 = 0x0000000b, /* Divide by 1 */
71 };
72
73 static u8int* apicbase;
74
75 static u32int
apicrget(int r)76 apicrget(int r)
77 {
78 return *((u32int*)(apicbase+r));
79 }
80
81 static void
apicrput(int r,u32int data)82 apicrput(int r, u32int data)
83 {
84 *((u32int*)(apicbase+r)) = data;
85 }
86
87 void
apictprput(int priority)88 apictprput(int priority)
89 {
90 apicrput(Tp, priority);
91 }
92
93 int
apiceoi(int vecno)94 apiceoi(int vecno)
95 {
96 apicrput(Eoi, 0);
97
98 return vecno;
99 }
100
101 int
apicisr(int vecno)102 apicisr(int vecno)
103 {
104 int isr;
105
106 isr = apicrget(Is + (vecno/32)*16);
107
108 return isr & (1<<(vecno%32));
109 }
110
111 void
apicinit(int apicno,uintmem pa,int isbp)112 apicinit(int apicno, uintmem pa, int isbp)
113 {
114 Apic *apic;
115
116 /*
117 * Mark the APIC useable if it has a good ID
118 * and the registers can be mapped.
119 * The APIC Extended Broadcast and ID bits in the HyperTransport
120 * Transaction Control register determine whether 4 or 8 bits
121 * are used for the APIC ID. There is also xAPIC and x2APIC
122 * to be dealt with sometime.
123 */
124 DBG("apic%d: pa %#P isbp %d\n", apicno, pa, isbp);
125 if(apicno >= Napic){
126 print("apic%d: out of range\n", apicno);
127 return;
128 }
129 if((apic = &xapic[apicno])->useable){
130 print("apicinit%d: already initialised\n", apicno);
131 return;
132 }
133 if(apicbase == nil){
134 if((apicbase = vmap(pa, 1024)) == nil){
135 print("apic%d: can't map apicbase\n", apicno);
136 return;
137 }
138 DBG("apic%d: apicbase %#P -> %#p\n", apicno, pa, apicbase);
139 }
140 apic->useable = 1;
141
142 /*
143 * Assign a machno to the processor associated with this
144 * APIC, it may not be an identity map.
145 * Machno 0 is always the bootstrap processor.
146 */
147 if(isbp){
148 apic->machno = 0;
149 m->apicno = apicno;
150 }
151 else
152 apic->machno = sys->nmach++;
153 }
154
155 void
apicdump(void)156 apicdump(void)
157 {
158 int i;
159 Apic *apic;
160
161 if(!DBGFLG)
162 return;
163
164 DBG("apicbase %#p sys->nmach %d\n", apicbase, sys->nmach);
165 for(i = 0; i < Napic; i++){
166 apic = &xapic[i];
167 if(!apic->useable || apic->addr != 0)
168 continue;
169 DBG("apic%d: machno %d lint0 %#8.8ux lint1 %#8.8ux\n",
170 i, apic->machno, apic->lvt[0], apic->lvt[1]);
171 DBG(" thslvt %#8.8ux pmclvt %#8.8ux elvt %#8.8ux\n",
172 apicrget(Thslvt), apicrget(Pmclvt), apicrget(Elvt));
173 DBG(" tlvt %#8.8ux lint0 %#8.8ux lint1 %#8.8ux siv %#8.8ux\n",
174 apicrget(Tlvt), apicrget(Lint0),
175 apicrget(Lint1), apicrget(Siv));
176 }
177 }
178
179 int
apiconline(void)180 apiconline(void)
181 {
182 Apic *apic;
183 u64int tsc;
184 u32int dfr, ver;
185 int apicno, nlvt;
186
187 if(apicbase == nil)
188 return 0;
189 if((apicno = ((apicrget(Id)>>24) & 0xff)) >= Napic)
190 return 0;
191 apic = &xapic[apicno];
192 if(!apic->useable || apic->addr != nil)
193 return 0;
194
195 /*
196 * Things that can only be done when on the processor
197 * owning the APIC, apicinit above runs on the bootstrap
198 * processor.
199 */
200 ver = apicrget(Ver);
201 nlvt = ((ver>>16) & 0xff) + 1;
202 if(nlvt > nelem(apic->lvt)){
203 print("apicinit%d: nlvt %d > max (%d)\n",
204 apicno, nlvt, nelem(apic->lvt));
205 nlvt = nelem(apic->lvt);
206 }
207 apic->nlvt = nlvt;
208 apic->ver = ver & 0xff;
209
210 /*
211 * These don't really matter in Physical mode;
212 * set the defaults anyway.
213 */
214 if(memcmp(&m->cpuinfo[0][1], "AuthenticAMD", 12) == 0)
215 dfr = 0xf0000000;
216 else
217 dfr = 0xffffffff;
218 apicrput(Df, dfr);
219 apicrput(Ld, 0x00000000);
220
221 /*
222 * Disable interrupts until ready by setting the Task Priority
223 * register to 0xff.
224 */
225 apicrput(Tp, 0xff);
226
227 /*
228 * Software-enable the APIC in the Spurious Interrupt Vector
229 * register and set the vector number. The vector number must have
230 * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
231 * is set in the HyperTransport Transaction Control register.
232 */
233 apicrput(Siv, Swen|IdtSPURIOUS);
234
235 /*
236 * Acknowledge any outstanding interrupts.
237 */
238 apicrput(Eoi, 0);
239
240 /*
241 * Use the TSC to determine the APIC timer frequency.
242 * It might be possible to snarf this from a chipset
243 * register instead.
244 */
245 apicrput(Tdc, DivX1);
246 apicrput(Tlvt, Im|IdtTIMER);
247 tsc = rdtsc() + m->cpuhz/10;
248 apicrput(Tic, 0xffffffff);
249
250 while(rdtsc() < tsc)
251 ;
252
253 apic->hz = (0xffffffff-apicrget(Tcc))*10;
254 apic->max = apic->hz/HZ;
255 apic->min = apic->hz/(100*HZ);
256 apic->div = ((m->cpuhz/apic->max)+HZ/2)/HZ;
257
258 if(m->machno == 0 || DBGFLG){
259 print("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
260 apic->hz, apic->max, apic->min, apic->div);
261 }
262
263 /*
264 * Mask interrupts on Performance Monitor Counter overflow and
265 * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
266 * and Lintr1 (Legacy NMI).
267 * Clear any Error Status (write followed by read) and enable
268 * the Error interrupt.
269 */
270 switch(apic->nlvt){
271 case 6:
272 apicrput(Thslvt, Im|IdtTHS);
273 /*FALLTHROUGH*/
274 case 5:
275 apicrput(Pmclvt, Im|IdtPMC);
276 /*FALLTHROUGH*/
277 default:
278 break;
279 }
280 apicrput(Lint1, apic->lvt[1]|Im|IdtLINT1);
281 apicrput(Lint0, apic->lvt[0]|Im|IdtLINT0);
282
283 apicrput(Es, 0);
284 apicrget(Es);
285 apicrput(Elvt, IdtERROR);
286
287 /*
288 * Issue an INIT Level De-Assert to synchronise arbitration ID's.
289 * (Necessary in this implementation? - not if Pentium 4 or Xeon
290 * (APIC Version >= 0x14), or AMD).
291 apicrput(Ichi, 0);
292 apicrput(Iclo, DSallinc|Lassert|MTir);
293 while(apicrget(Iclo) & Ds)
294 ;
295 */
296
297 /*
298 * Reload the timer to de-synchronise the processors.
299 * When the caller is ready for the APIC to accept interrupts,
300 * it should call apictprput to lower the task priority.
301 *
302 * The timer is enabled later by the core-specific startup
303 * i.e. don't start the timer unless the core needs it,
304 * to reduce the likelihood of at least one (spurious) interrupt
305 * from the timer when priority is lowered.
306 */
307 microdelay((TK2MS(1)*1000/sys->nmach) * m->machno);
308 apicrput(Tic, apic->max);
309
310 return 1;
311 }
312
313 void
apictimerenable(void)314 apictimerenable(void)
315 {
316 /*
317 * Perhaps apictimerenable/apictimerdisable should just
318 * clear/set Im in the existing settings of Tlvt, there may
319 * be a time when the timer is used in a different mode;
320 * if so will need to ensure the mode is set when the timer
321 * is initialised.
322 */
323 apicrput(Tlvt, Periodic|IdtTIMER);
324 }
325
326 void
apictimerdisable(void)327 apictimerdisable(void)
328 {
329 apicrput(Tlvt, Im|IdtTIMER);
330 }
331
332 void
apictimerset(uvlong next)333 apictimerset(uvlong next)
334 {
335 Mpl pl;
336 Apic *apic;
337 vlong period;
338
339 /*
340 * APIC Timer.
341 */
342 apic = &xapic[m->apicno];
343
344 pl = splhi();
345 lock(&m->apictimerlock);
346
347 period = apic->max;
348 if(next != 0){
349 period = next - fastticks(nil); /* fastticks is just rdtsc() */
350 period /= apic->div;
351
352 if(period < apic->min)
353 period = apic->min;
354 else if(period > apic->max - apic->min)
355 period = apic->max;
356 }
357 apicrput(Tic, period);
358
359 unlock(&m->apictimerlock);
360 splx(pl);
361 }
362
363 void
apictimerintr(Ureg * ureg,void *)364 apictimerintr(Ureg* ureg, void*)
365 {
366 timerintr(ureg, 0);
367 }
368
369 void
apicsipi(int apicno,uintptr pa)370 apicsipi(int apicno, uintptr pa)
371 {
372 int i;
373 u32int crhi, crlo;
374
375 /*
376 * SIPI - Start-up IPI.
377 * To do: checks on apic validity.
378 */
379 crhi = apicno<<24;
380 apicrput(Ichi, crhi);
381 apicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
382 microdelay(200);
383 apicrput(Iclo, DSnone|TMlevel|MTir);
384 millidelay(10);
385
386 crlo = DSnone|TMedge|MTsipi|((u32int)pa/(4*KiB));
387 for(i = 0; i < 2; i++){
388 apicrput(Ichi, crhi);
389 apicrput(Iclo, crlo);
390 microdelay(200);
391 }
392 }
393
394 void
apicipi(int apicno)395 apicipi(int apicno)
396 {
397 apicrput(Ichi, apicno<<24);
398 apicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
399 while(apicrget(Iclo) & Ds)
400 ;
401 }
402