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