ioapic.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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. typedef struct Rbus Rbus;
  17. typedef struct Rdt Rdt;
  18. struct Rbus {
  19. Rbus *next;
  20. int devno;
  21. Rdt *rdt;
  22. };
  23. struct Rdt {
  24. Apic *apic;
  25. int intin;
  26. uint32_t lo;
  27. int ref; /* could map to multiple busses */
  28. int enabled; /* times enabled */
  29. };
  30. enum { /* IOAPIC registers */
  31. Ioregsel = 0x00, /* indirect register address */
  32. Iowin = 0x04, /* indirect register data */
  33. Ioipa = 0x08, /* IRQ Pin Assertion */
  34. Ioeoi = 0x10, /* EOI */
  35. Ioapicid = 0x00, /* Identification */
  36. Ioapicver = 0x01, /* Version */
  37. Ioapicarb = 0x02, /* Arbitration */
  38. Ioabcfg = 0x03, /* Boot Coniguration */
  39. Ioredtbl = 0x10, /* Redirection Table */
  40. };
  41. static Rdt rdtarray[Nrdt];
  42. static int nrdtarray;
  43. static int gsib;
  44. static Rbus* rdtbus[Nbus];
  45. static Rdt* rdtvecno[IdtMAX+1];
  46. static Lock idtnolock;
  47. static int idtno = IdtIOAPIC;
  48. Apic xioapic[Napic];
  49. static void
  50. rtblget(Apic* apic, int sel, uint32_t* hi, uint32_t* lo)
  51. {
  52. sel = Ioredtbl + 2*sel;
  53. *(apic->addr+Ioregsel) = sel+1;
  54. *hi = *(apic->addr+Iowin);
  55. *(apic->addr+Ioregsel) = sel;
  56. *lo = *(apic->addr+Iowin);
  57. }
  58. static void
  59. rtblput(Apic* apic, int sel, uint32_t hi, uint32_t lo)
  60. {
  61. sel = Ioredtbl + 2*sel;
  62. *(apic->addr+Ioregsel) = sel+1;
  63. *(apic->addr+Iowin) = hi;
  64. *(apic->addr+Ioregsel) = sel;
  65. *(apic->addr+Iowin) = lo;
  66. }
  67. Rdt*
  68. rdtlookup(Apic *apic, int intin)
  69. {
  70. int i;
  71. Rdt *r;
  72. for(i = 0; i < nrdtarray; i++){
  73. r = rdtarray + i;
  74. if(apic == r->apic && intin == r->intin)
  75. return r;
  76. }
  77. return nil;
  78. }
  79. void
  80. ioapicintrinit(int busno, int apicno, int intin, int devno, uint32_t lo)
  81. {
  82. Rbus *rbus;
  83. Rdt *rdt;
  84. Apic *apic;
  85. if(busno >= Nbus || apicno >= Napic || nrdtarray >= Nrdt)
  86. return;
  87. apic = &xioapic[apicno];
  88. if(!apic->useable || intin >= apic->nrdt)
  89. return;
  90. rdt = rdtlookup(apic, intin);
  91. if(rdt == nil){
  92. rdt = &rdtarray[nrdtarray++];
  93. rdt->apic = apic;
  94. rdt->intin = intin;
  95. rdt->lo = lo;
  96. }else{
  97. if(lo != rdt->lo){
  98. print("mutiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
  99. busno, apicno, intin, devno, lo, rdt->lo);
  100. return;
  101. }
  102. DBG("dup rdt %d %d %d %d %.8ux\n", busno, apicno, intin, devno, lo);
  103. }
  104. rdt->ref++;
  105. rbus = malloc(sizeof *rbus);
  106. rbus->rdt = rdt;
  107. rbus->devno = devno;
  108. rbus->next = rdtbus[busno];
  109. rdtbus[busno] = rbus;
  110. }
  111. void
  112. ioapicinit(int id, uintptr_t pa)
  113. {
  114. Apic *apic;
  115. /*
  116. * Mark the IOAPIC useable if it has a good ID
  117. * and the registers can be mapped.
  118. */
  119. if(id >= Napic)
  120. return;
  121. apic = &xioapic[id];
  122. if(apic->useable || (apic->addr = vmap(pa, 1024)) == nil)
  123. return;
  124. apic->useable = 1;
  125. /*
  126. * Initialise the I/O APIC.
  127. * The MultiProcessor Specification says it is the
  128. * responsibility of the O/S to set the APIC ID.
  129. */
  130. lock(apic);
  131. *(apic->addr+Ioregsel) = Ioapicver;
  132. apic->nrdt = ((*(apic->addr+Iowin)>>16) & 0xff) + 1;
  133. apic->gsib = gsib;
  134. gsib += apic->nrdt;
  135. *(apic->addr+Ioregsel) = Ioapicid;
  136. *(apic->addr+Iowin) = id<<24;
  137. unlock(apic);
  138. }
  139. void
  140. ioapicdump(void)
  141. {
  142. int i, n;
  143. Rbus *rbus;
  144. Rdt *rdt;
  145. Apic *apic;
  146. uint32_t hi, lo;
  147. if(!DBGFLG)
  148. return;
  149. for(i = 0; i < Napic; i++){
  150. apic = &xioapic[i];
  151. if(!apic->useable || apic->addr == 0)
  152. continue;
  153. print("ioapic %d addr %#p nrdt %d gsib %d\n",
  154. i, apic->addr, apic->nrdt, apic->gsib);
  155. for(n = 0; n < apic->nrdt; n++){
  156. lock(apic);
  157. rtblget(apic, n, &hi, &lo);
  158. unlock(apic);
  159. print(" rdt %2.2d %#8.8ux %#8.8ux\n", n, hi, lo);
  160. }
  161. }
  162. for(i = 0; i < Nbus; i++){
  163. if((rbus = rdtbus[i]) == nil)
  164. continue;
  165. print("iointr bus %d:\n", i);
  166. for(; rbus != nil; rbus = rbus->next){
  167. rdt = rbus->rdt;
  168. print(" apic %ld devno %#ux (%d %d) intin %d lo %#ux ref %d\n",
  169. rdt->apic-xioapic, rbus->devno, rbus->devno>>2,
  170. rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
  171. }
  172. }
  173. }
  174. void
  175. ioapiconline(void)
  176. {
  177. int i;
  178. Apic *apic;
  179. for(apic = xioapic; apic < &xioapic[Napic]; apic++){
  180. if(!apic->useable || apic->addr == nil)
  181. continue;
  182. for(i = 0; i < apic->nrdt; i++){
  183. lock(apic);
  184. rtblput(apic, i, 0, Im);
  185. unlock(apic);
  186. }
  187. }
  188. ioapicdump();
  189. }
  190. static int dfpolicy = 0;
  191. static void
  192. ioapicintrdd(uint32_t* hi, uint32_t* lo)
  193. {
  194. int i;
  195. static int df;
  196. static Lock dflock;
  197. /*
  198. * Set delivery mode (lo) and destination field (hi),
  199. * according to interrupt routing policy.
  200. */
  201. /*
  202. * The bulk of this code was written ~1995, when there was
  203. * one architecture and one generation of hardware, the number
  204. * of CPUs was up to 4(8) and the choices for interrupt routing
  205. * were physical, or flat logical (optionally with lowest
  206. * priority interrupt). Logical mode hasn't scaled well with
  207. * the increasing number of packages/cores/threads, so the
  208. * fall-back is to physical mode, which works across all processor
  209. * generations, both AMD and Intel, using the APIC and xAPIC.
  210. *
  211. * Interrupt routing policy can be set here.
  212. */
  213. switch(dfpolicy){
  214. default: /* noise core 0 */
  215. *hi = sys->machptr[0]->apicno<<24;
  216. break;
  217. case 1: /* round-robin */
  218. /*
  219. * Assign each interrupt to a different CPU on a round-robin
  220. * Some idea of the packages/cores/thread topology would be
  221. * useful here, e.g. to not assign interrupts to more than one
  222. * thread in a core. But, as usual, Intel make that an onerous
  223. * task.
  224. */
  225. lock(&dflock);
  226. for(;;){
  227. i = df++;
  228. if(df >= sys->nmach+1)
  229. df = 0;
  230. if(sys->machptr[i] == nil || !sys->machptr[i]->online)
  231. continue;
  232. i = sys->machptr[i]->apicno;
  233. if(xlapic[i].useable && xlapic[i].addr == 0)
  234. break;
  235. }
  236. unlock(&dflock);
  237. *hi = i<<24;
  238. break;
  239. }
  240. *lo |= Pm|MTf;
  241. }
  242. int
  243. nextvec(void)
  244. {
  245. uint vecno;
  246. lock(&idtnolock);
  247. vecno = idtno;
  248. idtno = (idtno+8) % IdtMAX;
  249. if(idtno < IdtIOAPIC)
  250. idtno += IdtIOAPIC;
  251. unlock(&idtnolock);
  252. return vecno;
  253. }
  254. static int
  255. msimask(Vkey *v, int mask)
  256. {
  257. Pcidev *p;
  258. p = pcimatchtbdf(v->tbdf);
  259. if(p == nil)
  260. return -1;
  261. return pcimsimask(p, mask);
  262. }
  263. static int
  264. intrenablemsi(Vctl* v, Pcidev *p)
  265. {
  266. uint vno, lo, hi;
  267. uint64_t msivec;
  268. vno = nextvec();
  269. lo = IPlow | TMedge | vno;
  270. ioapicintrdd(&hi, &lo);
  271. if(lo & Lm)
  272. lo |= MTlp;
  273. msivec = (uint64_t)hi<<32 | lo;
  274. if(pcimsienable(p, msivec) == -1)
  275. return -1;
  276. v->isr = apicisr;
  277. v->eoi = apiceoi;
  278. v->vno = vno;
  279. v->type = "msi";
  280. v->mask = msimask;
  281. DBG("msiirq: %T: enabling %.16llux %s irq %d vno %d\n", p->tbdf, msivec, v->name, v->irq, vno);
  282. return vno;
  283. }
  284. int
  285. disablemsi(Vctl* v, Pcidev *p)
  286. {
  287. if(p == nil)
  288. return -1;
  289. return pcimsimask(p, 1);
  290. }
  291. int
  292. ioapicintrenable(Vctl* v)
  293. {
  294. Rbus *rbus;
  295. Rdt *rdt;
  296. uint32_t hi, lo;
  297. int busno, devno, vecno;
  298. /*
  299. * Bridge between old and unspecified new scheme,
  300. * the work in progress...
  301. */
  302. if(v->tbdf == BUSUNKNOWN){
  303. if(v->irq >= IrqLINT0 && v->irq <= MaxIrqLAPIC){
  304. if(v->irq != IrqSPURIOUS)
  305. v->isr = apiceoi;
  306. v->type = "lapic";
  307. return v->irq;
  308. }
  309. else{
  310. /*
  311. * Legacy ISA.
  312. * Make a busno and devno using the
  313. * ISA bus number and the irq.
  314. */
  315. extern int mpisabusno;
  316. if(mpisabusno == -1)
  317. panic("no ISA bus allocated");
  318. busno = mpisabusno;
  319. devno = v->irq<<2;
  320. }
  321. }
  322. else if(BUSTYPE(v->tbdf) == BusPCI){
  323. /*
  324. * PCI.
  325. * Make a devno from BUSDNO(tbdf) and pcidev->intp.
  326. */
  327. Pcidev *pcidev;
  328. busno = BUSBNO(v->tbdf);
  329. if((pcidev = pcimatchtbdf(v->tbdf)) == nil)
  330. panic("no PCI dev for tbdf %#8.8ux\n", v->tbdf);
  331. if((vecno = intrenablemsi(v, pcidev)) != -1)
  332. return vecno;
  333. disablemsi(v, pcidev);
  334. if((devno = pcicfgr8(pcidev, PciINTP)) == 0)
  335. panic("no INTP for tbdf %#8.8ux\n", v->tbdf);
  336. devno = BUSDNO(v->tbdf)<<2|(devno-1);
  337. DBG("ioapicintrenable: tbdf %#8.8ux busno %d devno %d\n",
  338. v->tbdf, busno, devno);
  339. }
  340. else{
  341. SET(busno); SET(devno);
  342. panic("unknown tbdf %#8.8ux\n", v->tbdf);
  343. }
  344. rdt = nil;
  345. for(rbus = rdtbus[busno]; rbus != nil; rbus = rbus->next)
  346. if(rbus->devno == devno){
  347. rdt = rbus->rdt;
  348. break;
  349. }
  350. if(rdt == nil){
  351. extern int mpisabusno;
  352. /*
  353. * First crack in the smooth exterior of the new code:
  354. * some BIOS make an MPS table where the PCI devices are
  355. * just defaulted to ISA.
  356. * Rewrite this to be cleaner.
  357. */
  358. if((busno = mpisabusno) == -1)
  359. return -1;
  360. devno = v->irq<<2;
  361. for(rbus = rdtbus[busno]; rbus != nil; rbus = rbus->next)
  362. if(rbus->devno == devno){
  363. rdt = rbus->rdt;
  364. break;
  365. }
  366. DBG("isa: tbdf %#8.8ux busno %d devno %d %#p\n",
  367. v->tbdf, busno, devno, rdt);
  368. }
  369. if(rdt == nil)
  370. return -1;
  371. /*
  372. * Second crack:
  373. * what to do about devices that intrenable/intrdisable frequently?
  374. * 1) there is no ioapicdisable yet;
  375. * 2) it would be good to reuse freed vectors.
  376. * Oh bugger.
  377. */
  378. /*
  379. * This is a low-frequency event so just lock
  380. * the whole IOAPIC to initialise the RDT entry
  381. * rather than putting a Lock in each entry.
  382. */
  383. lock(rdt->apic);
  384. DBG("%T: %ld/%d/%d (%d)\n", v->tbdf, rdt->apic - xioapic, rbus->devno, rdt->intin, devno);
  385. if((rdt->lo & 0xff) == 0){
  386. vecno = nextvec();
  387. rdt->lo |= vecno;
  388. rdtvecno[vecno] = rdt;
  389. }else
  390. DBG("%T: mutiple irq bus %d dev %d\n", v->tbdf, busno, devno);
  391. rdt->enabled++;
  392. lo = (rdt->lo & ~Im);
  393. ioapicintrdd(&hi, &lo);
  394. rtblput(rdt->apic, rdt->intin, hi, lo);
  395. vecno = lo & 0xff;
  396. unlock(rdt->apic);
  397. DBG("busno %d devno %d hi %#8.8ux lo %#8.8ux vecno %d\n",
  398. busno, devno, hi, lo, vecno);
  399. v->isr = apicisr;
  400. v->eoi = apiceoi;
  401. v->vno = vecno;
  402. v->type = "ioapic";
  403. return vecno;
  404. }
  405. int
  406. ioapicintrdisable(int vecno)
  407. {
  408. Rdt *rdt;
  409. /*
  410. * FOV. Oh dear. This isn't very good.
  411. * Fortunately rdtvecno[vecno] is static
  412. * once assigned.
  413. * Must do better.
  414. *
  415. * What about any pending interrupts?
  416. */
  417. if(vecno < 0 || vecno > MaxVectorAPIC){
  418. panic("ioapicintrdisable: vecno %d out of range", vecno);
  419. return -1;
  420. }
  421. if((rdt = rdtvecno[vecno]) == nil){
  422. panic("ioapicintrdisable: vecno %d has no rdt", vecno);
  423. return -1;
  424. }
  425. lock(rdt->apic);
  426. rdt->enabled--;
  427. if(rdt->enabled == 0)
  428. rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
  429. unlock(rdt->apic);
  430. return 0;
  431. }