]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/apic.c
syscallfmt: use up->syserrstr instead of up->errstr (import from sources)
[plan9front.git] / sys / src / 9 / pc / apic.c
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 static uchar lapictdxtab[] = {          /* LapicTDCR */
80         0x0B,   /* divide by 1 */
81         0x00,   /* divide by 2 */
82         0x01,   /* divide by 4 */
83         0x02,   /* divide by 8 */
84         0x03,   /* divide by 16 */
85         0x08,   /* divide by 32 */
86         0x09,   /* divide by 64 */
87         0x0A,   /* divide by 128 */
88 };
89
90 static ulong* lapicbase;
91
92 struct
93 {
94         uvlong  hz;
95         ulong   max;
96         ulong   min;
97         ulong   div;
98         int     tdx;
99 } lapictimer;
100
101 static ulong
102 lapicr(int r)
103 {
104         return *(lapicbase+(r/sizeof(*lapicbase)));
105 }
106
107 static void
108 lapicw(int r, ulong data)
109 {
110         *(lapicbase+(r/sizeof(*lapicbase))) = data;
111         data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
112         USED(data);
113 }
114
115 void
116 lapiconline(void)
117 {
118         /*
119          * Reload the timer to de-synchronise the processors,
120          * then lower the task priority to allow interrupts to be
121          * accepted by the APIC.
122          */
123         microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
124         lapicw(LapicTICR, lapictimer.max);
125         lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
126
127         lapicw(LapicTPR, 0);
128 }
129
130 /*
131  *  use the i8253 clock to figure out our lapic timer rate.
132  */
133 static void
134 lapictimerinit(void)
135 {
136 Retry:
137         lapicw(LapicTDCR, lapictdxtab[lapictimer.tdx]);
138         lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
139
140         if(lapictimer.hz == 0ULL){
141                 uvlong x, v, hz;
142
143                 x = fastticks(&hz);
144                 x += hz/10;
145                 lapicw(LapicTICR, 0xffffffff);
146                 do{
147                         v = fastticks(nil);
148                 }while(v < x);
149
150                 v = (0xffffffffUL-lapicr(LapicTCCR))*10;
151                 if(v > hz-(hz/10)){
152                         if(v > hz+(hz/10) && lapictimer.tdx < nelem(lapictdxtab)-1){
153                                 lapictimer.tdx++;
154                                 goto Retry;
155                         }
156                         v = hz;
157                 }
158                 assert(v != 0);
159
160                 lapictimer.hz = v;
161                 lapictimer.div = hz/lapictimer.hz;
162                 lapictimer.max = lapictimer.hz/HZ;
163                 lapictimer.min = lapictimer.hz/(100*HZ);
164         }
165 }
166
167 void
168 lapicinit(Apic* apic)
169 {
170         ulong dfr, ldr, lvt;
171
172         if(lapicbase == 0)
173                 lapicbase = apic->addr;
174
175         /*
176          * These don't really matter in Physical mode;
177          * set the defaults anyway.
178          */
179         if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
180                 dfr = 0xf0000000;
181         else
182                 dfr = 0xffffffff;
183         ldr = 0x00000000;
184
185         lapicw(LapicDFR, dfr);
186         lapicw(LapicLDR, ldr);
187         lapicw(LapicTPR, 0xff);
188         lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
189
190         lapictimerinit();
191
192         /*
193          * Some Pentium revisions have a bug whereby spurious
194          * interrupts are generated in the through-local mode.
195          */
196         switch(m->cpuidax & 0xFFF){
197         case 0x526:                             /* stepping cB1 */
198         case 0x52B:                             /* stepping E0 */
199         case 0x52C:                             /* stepping cC0 */
200                 wrmsr(0x0E, 1<<14);             /* TR12 */
201                 break;
202         }
203
204         /*
205          * Set the local interrupts. It's likely these should just be
206          * masked off for SMP mode as some Pentium Pros have problems if
207          * LINT[01] are set to ExtINT.
208          * Acknowledge any outstanding interrupts.
209         lapicw(LapicLINT0, apic->lintr[0]);
210         lapicw(LapicLINT1, apic->lintr[1]);
211          */
212         lapiceoi(0);
213
214         lvt = (lapicr(LapicVER)>>16) & 0xFF;
215         if(lvt >= 4)
216                 lapicw(LapicPCINT, ApicIMASK);
217         lapicw(LapicERROR, VectorPIC+IrqERROR);
218         lapicw(LapicESR, 0);
219         lapicr(LapicESR);
220
221         /*
222          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
223          */
224         lapicw(LapicICRHI, 0);
225         lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
226         while(lapicr(LapicICRLO) & ApicDELIVS)
227                 ;
228
229         /*
230          * Do not allow acceptance of interrupts until all initialisation
231          * for this processor is done. For the bootstrap processor this can be
232          * early duing initialisation. For the application processors this should
233          * be after the bootstrap processor has lowered priority and is accepting
234          * interrupts.
235         lapicw(LapicTPR, 0);
236          */
237 }
238
239 void
240 lapicstartap(Apic* apic, int v)
241 {
242         int i;
243         ulong crhi;
244
245         crhi = apic->apicno<<24;
246         lapicw(LapicICRHI, crhi);
247         lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
248         microdelay(200);
249         lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
250         delay(10);
251
252         for(i = 0; i < 2; i++){
253                 lapicw(LapicICRHI, crhi);
254                 lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
255                 microdelay(200);
256         }
257 }
258
259 void
260 lapicerror(Ureg*, void*)
261 {
262         ulong esr;
263
264         lapicw(LapicESR, 0);
265         esr = lapicr(LapicESR);
266         switch(m->cpuidax & 0xFFF){
267         case 0x526:                             /* stepping cB1 */
268         case 0x52B:                             /* stepping E0 */
269         case 0x52C:                             /* stepping cC0 */
270                 return;
271         }
272         print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
273 }
274
275 void
276 lapicspurious(Ureg*, void*)
277 {
278         print("cpu%d: lapicspurious\n", m->machno);
279 }
280
281 int
282 lapicisr(int v)
283 {
284         ulong isr;
285
286         isr = lapicr(LapicISR + (v/32));
287
288         return isr & (1<<(v%32));
289 }
290
291 int
292 lapiceoi(int v)
293 {
294         lapicw(LapicEOI, 0);
295
296         return v;
297 }
298
299 void
300 lapicicrw(ulong hi, ulong lo)
301 {
302         lapicw(LapicICRHI, hi);
303         lapicw(LapicICRLO, lo);
304 }
305
306 void
307 ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
308 {
309         ulong *iowin;
310
311         iowin = apic->addr+(0x10/sizeof(ulong));
312         sel = IoapicRDT + 2*sel;
313
314         lock(apic);
315         *apic->addr = sel+1;
316         if(hi)
317                 *hi = *iowin;
318         *apic->addr = sel;
319         if(lo)
320                 *lo = *iowin;
321         unlock(apic);
322 }
323
324 void
325 ioapicrdtw(Apic* apic, int sel, int hi, int lo)
326 {
327         ulong *iowin;
328
329         iowin = apic->addr+(0x10/sizeof(ulong));
330         sel = IoapicRDT + 2*sel;
331
332         lock(apic);
333         *apic->addr = sel+1;
334         *iowin = hi;
335         *apic->addr = sel;
336         *iowin = lo;
337         unlock(apic);
338 }
339
340 void
341 ioapicinit(Apic* apic, int apicno)
342 {
343         int hi, lo, v;
344         ulong *iowin;
345
346         /*
347          * Initialise the I/O APIC.
348          * The MultiProcessor Specification says it is the responsibility
349          * of the O/S to set the APIC id.
350          * Make sure interrupts are all masked off for now.
351          */
352         iowin = apic->addr+(0x10/sizeof(ulong));
353         lock(apic);
354         *apic->addr = IoapicVER;
355         apic->mre = (*iowin>>16) & 0xFF;
356
357         *apic->addr = IoapicID;
358         *iowin = apicno<<24;
359         unlock(apic);
360
361         hi = 0;
362         lo = ApicIMASK;
363         for(v = 0; v <= apic->mre; v++)
364                 ioapicrdtw(apic, v, hi, lo);
365 }
366
367 void
368 lapictimerset(uvlong next)
369 {
370         vlong period;
371         int x;
372
373         x = splhi();
374         lock(&m->apictimerlock);
375
376         period = lapictimer.max;
377         if(next != 0){
378                 period = next - fastticks(nil);
379                 period /= lapictimer.div;
380
381                 if(period < lapictimer.min)
382                         period = lapictimer.min;
383                 else if(period > lapictimer.max - lapictimer.min)
384                         period = lapictimer.max;
385         }
386         lapicw(LapicTICR, period);
387
388         unlock(&m->apictimerlock);
389         splx(x);
390 }
391
392 void
393 lapicclock(Ureg *u, void*)
394 {
395         /*
396          * since the MTRR updates need to be synchronized across processors,
397          * we want to do this within the clock tick.
398          */
399         mtrrclock();
400         timerintr(u, 0);
401 }
402
403 void
404 lapicintron(void)
405 {
406         lapicw(LapicTPR, 0);
407 }
408
409 void
410 lapicintroff(void)
411 {
412         lapicw(LapicTPR, 0xFF);
413 }
414
415 void
416 lapicnmienable(void)
417 {
418         lapicw(LapicPCINT, ApicNMI);
419 }
420
421 void
422 lapicnmidisable(void)
423 {
424         lapicw(LapicPCINT, ApicIMASK);
425 }