ioapic.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. "use strict";
  2. // http://download.intel.com/design/chipsets/datashts/29056601.pdf
  3. /** @const */
  4. var IOAPIC_ADDRESS = 0xFEC00000;
  5. /** @const */
  6. var IOREGSEL = 0;
  7. /** @const */
  8. var IOWIN = 0x10;
  9. /** @const */
  10. var IOAPIC_IRQ_COUNT = 24;
  11. /** @const */
  12. var IOAPIC_ID = 0; // must match value in seabios
  13. /** @const */
  14. var IOAPIC_CONFIG_TRIGGER_MODE_LEVEL = 1 << 15;
  15. /** @const */
  16. var IOAPIC_CONFIG_MASKED = 1 << 16;
  17. /** @const */
  18. var IOAPIC_CONFIG_DELIVS = 1 << 12;
  19. /** @const */
  20. var IOAPIC_CONFIG_REMOTE_IRR = 1 << 14;
  21. /** @const */
  22. var IOAPIC_CONFIG_READONLY_MASK = IOAPIC_CONFIG_REMOTE_IRR | IOAPIC_CONFIG_DELIVS | 0xFFFE0000;
  23. /** @const */
  24. var IOAPIC_DELIVERY_FIXED = 0;
  25. /** @const */
  26. var IOAPIC_DELIVERY_LOWEST_PRIORITY = 1;
  27. /** @const */
  28. var IOAPIC_DELIVERY_NMI = 4;
  29. /** @const */
  30. var IOAPIC_DELIVERY_INIT = 5;
  31. /**
  32. * @constructor
  33. * @param {CPU} cpu
  34. */
  35. function IOAPIC(cpu)
  36. {
  37. /** @type {CPU} */
  38. this.cpu = cpu;
  39. this.ioredtbl_config = new Int32Array(IOAPIC_IRQ_COUNT);
  40. this.ioredtbl_destination = new Int32Array(IOAPIC_IRQ_COUNT);
  41. for(var i = 0; i < this.ioredtbl_config.length; i++)
  42. {
  43. // disable interrupts
  44. this.ioredtbl_config[i] = IOAPIC_CONFIG_MASKED;
  45. }
  46. // IOAPIC register selection
  47. this.ioregsel = 0;
  48. this.ioapic_id = IOAPIC_ID;
  49. this.irr = 0;
  50. this.irq_value = 0;
  51. dbg_assert(MMAP_BLOCK_SIZE >= 0x20);
  52. cpu.io.mmap_register(IOAPIC_ADDRESS, MMAP_BLOCK_SIZE,
  53. (addr) =>
  54. {
  55. dbg_assert(false, "unsupported read8 from ioapic: " + h(addr));
  56. return 0;
  57. },
  58. (addr, value) =>
  59. {
  60. dbg_assert(false, "unsupported write8 from ioapic: " + h(addr));
  61. },
  62. (addr) =>
  63. {
  64. addr = addr - IOAPIC_ADDRESS | 0;
  65. if(addr === IOREGSEL)
  66. {
  67. return this.ioregsel;
  68. }
  69. else if(addr === IOWIN)
  70. {
  71. return this.read(this.ioregsel);
  72. }
  73. else
  74. {
  75. dbg_log("Unexpected IOAPIC register read: " + h(addr), LOG_APIC);
  76. dbg_assert(false);
  77. return 0;
  78. }
  79. },
  80. (addr, value) =>
  81. {
  82. addr = addr - IOAPIC_ADDRESS | 0;
  83. if(addr === IOREGSEL)
  84. {
  85. this.ioregsel = value;
  86. }
  87. else if(addr === IOWIN)
  88. {
  89. this.write(this.ioregsel, value);
  90. }
  91. else
  92. {
  93. dbg_log("Unexpected IOAPIC register write: " + h(addr) + " <- " + h(value >>> 0, 8), LOG_APIC);
  94. dbg_assert(false);
  95. }
  96. });
  97. }
  98. IOAPIC.prototype.remote_eoi = function(vector)
  99. {
  100. for(var i = 0; i < IOAPIC_IRQ_COUNT; i++)
  101. {
  102. var config = this.ioredtbl_config[i];
  103. if((config & 0xFF) === vector && (config & IOAPIC_CONFIG_REMOTE_IRR))
  104. {
  105. dbg_log("Clear remote IRR for irq=" + h(i), LOG_APIC);
  106. this.ioredtbl_config[i] &= ~IOAPIC_CONFIG_REMOTE_IRR;
  107. this.check_irq(i);
  108. }
  109. }
  110. };
  111. IOAPIC.prototype.check_irq = function(irq)
  112. {
  113. var mask = 1 << irq;
  114. if((this.irr & mask) === 0)
  115. {
  116. return;
  117. }
  118. var config = this.ioredtbl_config[irq];
  119. if((config & IOAPIC_CONFIG_MASKED) === 0)
  120. {
  121. var delivery_mode = config >> 8 & 7;
  122. var destination_mode = config >> 11 & 1;
  123. var vector = config & 0xFF;
  124. var destination = this.ioredtbl_destination[irq] >>> 24;
  125. var is_level = (config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL) === IOAPIC_CONFIG_TRIGGER_MODE_LEVEL;
  126. if((config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL) === 0)
  127. {
  128. this.irr &= ~mask;
  129. }
  130. else
  131. {
  132. this.ioredtbl_config[irq] |= IOAPIC_CONFIG_REMOTE_IRR;
  133. if(config & IOAPIC_CONFIG_REMOTE_IRR)
  134. {
  135. dbg_log("No route: level interrupt and remote IRR still set", LOG_APIC);
  136. return;
  137. }
  138. }
  139. if(delivery_mode === IOAPIC_DELIVERY_FIXED || delivery_mode === IOAPIC_DELIVERY_LOWEST_PRIORITY)
  140. {
  141. this.cpu.devices.apic.route(vector, delivery_mode, is_level, destination, destination_mode);
  142. }
  143. else
  144. {
  145. dbg_assert(false, "TODO");
  146. }
  147. this.ioredtbl_config[irq] &= ~IOAPIC_CONFIG_DELIVS;
  148. }
  149. };
  150. IOAPIC.prototype.set_irq = function(i)
  151. {
  152. if(i >= IOAPIC_IRQ_COUNT)
  153. {
  154. dbg_assert(false, "Bad irq: " + i, LOG_APIC);
  155. return;
  156. }
  157. var mask = 1 << i;
  158. if((this.irq_value & mask) === 0)
  159. {
  160. APIC_LOG_VERBOSE && dbg_log("apic set irq " + i, LOG_APIC);
  161. this.irq_value |= mask;
  162. var config = this.ioredtbl_config[i];
  163. if((config & (IOAPIC_CONFIG_TRIGGER_MODE_LEVEL|IOAPIC_CONFIG_MASKED)) ===
  164. IOAPIC_CONFIG_MASKED)
  165. {
  166. // edge triggered and masked
  167. return;
  168. }
  169. this.irr |= mask;
  170. this.check_irq(i);
  171. }
  172. };
  173. IOAPIC.prototype.clear_irq = function(i)
  174. {
  175. if(i >= IOAPIC_IRQ_COUNT)
  176. {
  177. dbg_assert(false, "Bad irq: " + i, LOG_APIC);
  178. return;
  179. }
  180. var mask = 1 << i;
  181. if((this.irq_value & mask) === mask)
  182. {
  183. this.irq_value &= ~mask;
  184. var config = this.ioredtbl_config[i];
  185. if(config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL)
  186. {
  187. this.irr &= ~mask;
  188. }
  189. }
  190. };
  191. IOAPIC.prototype.read = function(reg)
  192. {
  193. if(reg === 0)
  194. {
  195. dbg_log("IOAPIC Read id", LOG_APIC);
  196. return this.ioapic_id << 24;
  197. }
  198. else if(reg === 1)
  199. {
  200. dbg_log("IOAPIC Read version", LOG_APIC);
  201. return 0x11 | IOAPIC_IRQ_COUNT - 1 << 16;
  202. }
  203. else if(reg === 2)
  204. {
  205. dbg_log("IOAPIC Read arbitration id", LOG_APIC);
  206. return this.ioapic_id << 24;
  207. }
  208. else if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
  209. {
  210. var irq = reg - 0x10 >> 1;
  211. var index = reg & 1;
  212. if(index)
  213. {
  214. var value = this.ioredtbl_destination[irq];
  215. dbg_log("IOAPIC Read destination irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
  216. }
  217. else
  218. {
  219. var value = this.ioredtbl_config[irq];
  220. dbg_log("IOAPIC Read config irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
  221. }
  222. return value;
  223. }
  224. else
  225. {
  226. dbg_log("IOAPIC register read outside of range " + h(reg), LOG_APIC);
  227. dbg_assert(false);
  228. return 0;
  229. }
  230. };
  231. IOAPIC.prototype.write = function(reg, value)
  232. {
  233. //dbg_log("IOAPIC write " + h(reg) + " <- " + h(value, 8), LOG_APIC);
  234. if(reg === 0)
  235. {
  236. this.ioapic_id = value >>> 24 & 0x0F;
  237. }
  238. else if(reg === 1 || reg === 2)
  239. {
  240. dbg_log("Invalid write: " + reg, LOG_APIC);
  241. }
  242. else if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
  243. {
  244. var irq = reg - 0x10 >> 1;
  245. var index = reg & 1;
  246. if(index)
  247. {
  248. this.ioredtbl_destination[irq] = value & 0xFF000000;
  249. dbg_log("Write destination " + h(value >>> 0, 8) + " irq=" + h(irq) + " dest=" + h(value >>> 24, 2), LOG_APIC);
  250. }
  251. else
  252. {
  253. var old_value = this.ioredtbl_config[irq];
  254. this.ioredtbl_config[irq] = value & ~IOAPIC_CONFIG_READONLY_MASK | old_value & IOAPIC_CONFIG_READONLY_MASK;
  255. var vector = value & 0xFF;
  256. var delivery_mode = value >> 8 & 7;
  257. var destination_mode = value >> 11 & 1;
  258. var is_level = value >> 15 & 1;
  259. var disabled = value >> 16 & 1;
  260. dbg_log("Write config " + h(value >>> 0, 8) +
  261. " irq=" + h(irq) +
  262. " vector=" + h(vector, 2) +
  263. " deliverymode=" + DELIVERY_MODES[delivery_mode] +
  264. " destmode=" + DESTINATION_MODES[destination_mode] +
  265. " is_level=" + is_level +
  266. " disabled=" + disabled, LOG_APIC);
  267. this.check_irq(irq);
  268. }
  269. }
  270. else
  271. {
  272. dbg_log("IOAPIC register write outside of range " + h(reg) + ": " + h(value >>> 0, 8), LOG_APIC);
  273. dbg_assert(false);
  274. }
  275. };
  276. IOAPIC.prototype.get_state = function()
  277. {
  278. var state = [];
  279. state[0] = this.ioredtbl_config;
  280. state[1] = this.ioredtbl_destination;
  281. state[2] = this.ioregsel;
  282. state[3] = this.ioapic_id;
  283. state[4] = this.irr;
  284. state[5] = this.irq_value;
  285. return state;
  286. };
  287. IOAPIC.prototype.set_state = function(state)
  288. {
  289. this.ioredtbl_config = state[0];
  290. this.ioredtbl_destination = state[1];
  291. this.ioregsel = state[2];
  292. this.ioapic_id = state[3];
  293. this.irr = state[4];
  294. this.irq_value = state[5];
  295. };