apic.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "apic.h"
  15. #include "io.h"
  16. enum { /* Local APIC registers */
  17. Id = 0x0020, /* Identification */
  18. Ver = 0x0030, /* Version */
  19. Tp = 0x0080, /* Task Priority */
  20. Ap = 0x0090, /* Arbitration Priority */
  21. Pp = 0x00a0, /* Processor Priority */
  22. Eoi = 0x00b0, /* EOI */
  23. Ld = 0x00d0, /* Logical Destination */
  24. Df = 0x00e0, /* Destination Format */
  25. Siv = 0x00f0, /* Spurious Interrupt Vector */
  26. Is = 0x0100, /* Interrupt Status (8) */
  27. Tm = 0x0180, /* Trigger Mode (8) */
  28. Ir = 0x0200, /* Interrupt Request (8) */
  29. Es = 0x0280, /* Error Status */
  30. Iclo = 0x0300, /* Interrupt Command */
  31. Ichi = 0x0310, /* Interrupt Command [63:32] */
  32. Lvt0 = 0x0320, /* Local Vector Table 0 */
  33. Lvt5 = 0x0330, /* Local Vector Table 5 */
  34. Lvt4 = 0x0340, /* Local Vector Table 4 */
  35. Lvt1 = 0x0350, /* Local Vector Table 1 */
  36. Lvt2 = 0x0360, /* Local Vector Table 2 */
  37. Lvt3 = 0x0370, /* Local Vector Table 3 */
  38. Tic = 0x0380, /* Timer Initial Count */
  39. Tcc = 0x0390, /* Timer Current Count */
  40. Tdc = 0x03e0, /* Timer Divide Configuration */
  41. Tlvt = Lvt0, /* Timer */
  42. Lint0 = Lvt1, /* Local Interrupt 0 */
  43. Lint1 = Lvt2, /* Local Interrupt 1 */
  44. Elvt = Lvt3, /* Error */
  45. Pclvt = Lvt4, /* Performance Counter */
  46. Tslvt = Lvt5, /* Thermal Sensor */
  47. };
  48. enum { /* Siv */
  49. Swen = 0x00000100, /* Software Enable */
  50. Fdis = 0x00000200, /* Focus Disable */
  51. };
  52. enum { /* Iclo */
  53. Lassert = 0x00004000, /* Assert level */
  54. DSnone = 0x00000000, /* Use Destination Field */
  55. DSself = 0x00040000, /* Self is only destination */
  56. DSallinc = 0x00080000, /* All including self */
  57. DSallexc = 0x000c0000, /* All Excluding self */
  58. };
  59. enum { /* Tlvt */
  60. Periodic = 0x00020000, /* Periodic Timer Mode */
  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. uint8_t* apicbase;
  73. static int apmachno = 1;
  74. Apic xlapic[Napic];
  75. Mach *xlapicmachptr[Napic]; /* maintained, but unused */
  76. static uint32_t
  77. apicrget(int r)
  78. {
  79. return *((volatile uint32_t*)(apicbase+r));
  80. }
  81. static void
  82. apicrput(int r, uint32_t data)
  83. {
  84. *((volatile uint32_t*)(apicbase+r)) = data;
  85. }
  86. int
  87. apiceoi(int vecno)
  88. {
  89. apicrput(Eoi, 0);
  90. return vecno;
  91. }
  92. int
  93. apicisr(int vecno)
  94. {
  95. int isr;
  96. isr = apicrget(Is + (vecno/32)*16);
  97. return isr & (1<<(vecno%32));
  98. }
  99. void
  100. apicinit(int apicno, uintmem pa, int isbp)
  101. {
  102. Apic *apic;
  103. /*
  104. * Mark the APIC useable if it has a good ID
  105. * and the registers can be mapped.
  106. * The APIC Extended Broadcast and ID bits in the HyperTransport
  107. * Transaction Control register determine whether 4 or 8 bits
  108. * are used for the APIC ID. There is also xAPIC and x2APIC
  109. * to be dealt with sometime.
  110. */
  111. DBG("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
  112. if(apicno >= Napic){
  113. print("apicinit%d: out of range\n", apicno);
  114. return;
  115. }
  116. if((apic = &xlapic[apicno])->useable){
  117. print("apicinit%d: already initialised\n", apicno);
  118. return;
  119. }
  120. if(apicbase == nil){
  121. if((apicbase = vmap(pa, 1024)) == nil){
  122. print("apicinit%d: can't map apicbase\n", apicno);
  123. return;
  124. }
  125. DBG("apicinit%d: apicbase %#p -> %#p\n", apicno, pa, apicbase);
  126. }
  127. apic->useable = 1;
  128. /*
  129. * Assign a machno to the processor associated with this
  130. * APIC, it may not be an identity map.
  131. * Machno 0 is always the bootstrap processor.
  132. */
  133. if(isbp){
  134. apic->Lapic.machno = 0;
  135. machp()->apicno = apicno;
  136. }
  137. else
  138. apic->Lapic.machno = apmachno++;
  139. }
  140. static void
  141. apicdump0(Apic *apic, int i)
  142. {
  143. if(!apic->useable || apic->Ioapic.addr != 0)
  144. return;
  145. DBG("apic%d: machno %d lint0 %#8.8ux lint1 %#8.8ux\n",
  146. i, apic->Lapic.machno, apic->Lapic.lvt[0], apic->Lapic.lvt[1]);
  147. DBG(" tslvt %#8.8ux pclvt %#8.8ux elvt %#8.8ux\n",
  148. apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
  149. DBG(" tlvt %#8.8ux lint0 %#8.8ux lint1 %#8.8ux siv %#8.8ux\n",
  150. apicrget(Tlvt), apicrget(Lint0),
  151. apicrget(Lint1), apicrget(Siv));
  152. }
  153. void
  154. apicdump(void)
  155. {
  156. int i;
  157. if(!DBGFLG)
  158. return;
  159. DBG("apicbase %#p apmachno %d\n", apicbase, apmachno);
  160. for(i = 0; i < Napic; i++)
  161. apicdump0(xlapic + i, i);
  162. for(i = 0; i < Napic; i++)
  163. apicdump0(xioapic + i, i);
  164. }
  165. static void
  166. apictimer(Ureg* ureg, void* v)
  167. {
  168. timerintr(ureg, 0);
  169. }
  170. /* this supports the ACPI NMI subtable in the MADT table. */
  171. void
  172. apicnmi(int id, int lint, int flags)
  173. {
  174. static char fail[128];
  175. int i;
  176. for(i = 0; i < Napic; i++) {
  177. if (!xlapic[i].useable)
  178. continue;
  179. if (xlapic[i].Lapic.machno != id)
  180. continue;
  181. print("CODE: xlapic[%d].Lapic.lvt[%d] = 0x%x\n", i, lint, 0x10400);
  182. print("CODE: xlapic[%d].Lapic.lvt[0] = 0x%x\n", i, 0x10700);
  183. /* Nothing much changes. The NMI is always lint1, and we need an extint
  184. * on lint0. So just do it. */
  185. /* TODO: pay attention to the flags, but they'll never change.
  186. * TODO: the _MP_ always had Im set, which seems wrong. But do what it did. */
  187. snprint(fail, sizeof(fail), "%s wants lint to be 1; it's %d\n", lint);
  188. if (lint != 1)
  189. panic(fail);
  190. /* Im, NMI, vector 0 */
  191. xlapic[i].Lapic.lvt[lint] = 0x10400;
  192. /* Im, ExtINT, vector 0 */
  193. xlapic[i].Lapic.lvt[0] = 0x10700;
  194. }
  195. }
  196. int
  197. apiconline(void)
  198. {
  199. Apic *apic;
  200. uint64_t tsc;
  201. uint32_t dfr, ver;
  202. int apicno, nlvt;
  203. if(apicbase == nil)
  204. return 0;
  205. if((apicno = ((apicrget(Id)>>24) & 0xff)) >= Napic)
  206. return 0;
  207. apic = &xlapic[apicno];
  208. if(!apic->useable || apic->Ioapic.addr != nil)
  209. return 0;
  210. /*
  211. * Things that can only be done when on the processor
  212. * owning the APIC, apicinit above runs on the bootstrap
  213. * processor.
  214. */
  215. ver = apicrget(Ver);
  216. nlvt = ((ver>>16) & 0xff) + 1;
  217. if(nlvt > nelem(apic->Lapic.lvt)){
  218. print("apicinit%d: nlvt %d > max (%d)\n",
  219. apicno, nlvt, nelem(apic->Lapic.lvt));
  220. nlvt = nelem(apic->Lapic.lvt);
  221. }
  222. apic->Lapic.nlvt = nlvt;
  223. apic->Lapic.ver = ver & 0xff;
  224. /*
  225. * These don't really matter in Physical mode;
  226. * set the defaults anyway.
  227. */
  228. if(memcmp(machp()->CPU.cpuinfo, "AuthenticAMD", 12) == 0)
  229. dfr = 0xf0000000;
  230. else
  231. dfr = 0xffffffff;
  232. apicrput(Df, dfr);
  233. apicrput(Ld, 0x00000000);
  234. /*
  235. * Disable interrupts until ready by setting the Task Priority
  236. * register to 0xff.
  237. */
  238. apicrput(Tp, 0xff);
  239. /*
  240. * Software-enable the APIC in the Spurious Interrupt Vector
  241. * register and set the vector number. The vector number must have
  242. * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
  243. * is set in the HyperTransport Transaction Control register.
  244. */
  245. apicrput(Siv, Swen|IdtSPURIOUS);
  246. /*
  247. * Acknowledge any outstanding interrupts.
  248. */
  249. apicrput(Eoi, 0);
  250. /*
  251. * Use the TSC to determine the APIC timer frequency.
  252. * It might be possible to snarf this from a chipset
  253. * register instead.
  254. */
  255. apicrput(Tdc, DivX1);
  256. apicrput(Tlvt, Im);
  257. tsc = rdtsc() + machp()->cpuhz/10;
  258. apicrput(Tic, 0xffffffff);
  259. while(rdtsc() < tsc)
  260. ;
  261. apic->Lapic.hz = (0xffffffff-apicrget(Tcc))*10;
  262. apic->Lapic.max = apic->Lapic.hz/HZ;
  263. apic->Lapic.min = apic->Lapic.hz/(100*HZ);
  264. apic->Lapic.div = ((machp()->cpuhz/apic->Lapic.max)+HZ/2)/HZ;
  265. if(machp()->machno == 0 || DBGFLG){
  266. print("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
  267. apic->Lapic.hz, apic->Lapic.max, apic->Lapic.min, apic->Lapic.div);
  268. }
  269. /*
  270. * Mask interrupts on Performance Counter overflow and
  271. * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
  272. * and Lintr1 (Legacy NMI).
  273. * Clear any Error Status (write followed by read) and enable
  274. * the Error interrupt.
  275. */
  276. switch(apic->Lapic.nlvt){
  277. case 6:
  278. apicrput(Tslvt, Im);
  279. /*FALLTHROUGH*/
  280. case 5:
  281. apicrput(Pclvt, Im);
  282. /*FALLTHROUGH*/
  283. default:
  284. break;
  285. }
  286. apicrput(Lint1, apic->Lapic.lvt[1]|Im|IdtLINT1);
  287. apicrput(Lint0, apic->Lapic.lvt[0]|Im|IdtLINT0);
  288. apicrput(Es, 0);
  289. apicrget(Es);
  290. apicrput(Elvt, IdtERROR);
  291. /*
  292. * Issue an INIT Level De-Assert to synchronise arbitration ID's.
  293. * (Necessary in this implementation? - not if Pentium 4 or Xeon
  294. * (APIC Version >= 0x14), or AMD).
  295. apicrput(Ichi, 0);
  296. apicrput(Iclo, DSallinc|Lassert|MTir);
  297. while(apicrget(Iclo) & Ds)
  298. ;
  299. */
  300. /*
  301. * Reload the timer to de-synchronise the processors,
  302. * then lower the task priority to allow interrupts to be
  303. * accepted by the APIC.
  304. */
  305. microdelay((TK2MS(1)*1000/apmachno) * machp()->machno);
  306. if(apic->Lapic.machno == 0){
  307. apicrput(Tic, apic->Lapic.max);
  308. intrenable(IdtTIMER, apictimer, 0, MKBUS(BusLAPIC, 0, 0, 0), "APIC timer");
  309. apicrput(Tlvt, Periodic|IrqTIMER);
  310. }
  311. if(machp()->machno == 0)
  312. apicrput(Tp, 0);
  313. xlapicmachptr[apicno] = machp();
  314. return 1;
  315. }
  316. /* To start timers on TCs as part of the boot process. */
  317. void
  318. apictimerenab(void)
  319. {
  320. Apic *apic;
  321. apic = &xlapic[(apicrget(Id)>>24) & 0xff];
  322. apiceoi(IdtTIMER);
  323. apicrput(Tic, apic->Lapic.max);
  324. apicrput(Tlvt, Periodic|IrqTIMER);
  325. }
  326. void
  327. apictimerset(uint64_t next)
  328. {
  329. Mpl pl;
  330. Apic *apic;
  331. int64_t period;
  332. apic = &xlapic[(apicrget(Id)>>24) & 0xff];
  333. pl = splhi();
  334. lock(&machp()->apictimerlock);
  335. period = apic->Lapic.max;
  336. if(next != 0){
  337. period = next - fastticks(nil); /* fastticks is just rdtsc() */
  338. period /= apic->Lapic.div;
  339. if(period < apic->Lapic.min)
  340. period = apic->Lapic.min;
  341. else if(period > apic->Lapic.max - apic->Lapic.min)
  342. period = apic->Lapic.max;
  343. }
  344. apicrput(Tic, period);
  345. unlock(&machp()->apictimerlock);
  346. splx(pl);
  347. }
  348. void
  349. apicsipi(int apicno, uintmem pa)
  350. {
  351. int i;
  352. uint32_t crhi, crlo;
  353. /*
  354. * SIPI - Start-up IPI.
  355. * To do: checks on apic validity.
  356. */
  357. crhi = apicno<<24;
  358. apicrput(Ichi, crhi);
  359. apicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
  360. microdelay(200);
  361. apicrput(Iclo, DSnone|TMlevel|MTir);
  362. millidelay(10);
  363. crlo = DSnone|TMedge|MTsipi|((uint32_t)pa/(4*KiB));
  364. for(i = 0; i < 2; i++){
  365. apicrput(Ichi, crhi);
  366. apicrput(Iclo, crlo);
  367. microdelay(200);
  368. }
  369. }
  370. void
  371. apicipi(int apicno)
  372. {
  373. apicrput(Ichi, apicno<<24);
  374. apicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
  375. while(apicrget(Iclo) & Ds)
  376. ;
  377. }
  378. void
  379. apicpri(int pri)
  380. {
  381. apicrput(Tp, pri);
  382. }