ioapic.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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. addr = addr - IOAPIC_ADDRESS | 0;
  56. if(addr >= IOWIN && addr < IOWIN + 4)
  57. {
  58. const byte = addr - IOWIN;
  59. dbg_log("ioapic read8 byte " + byte + " " + h(this.ioregsel), LOG_APIC);
  60. return this.read(this.ioregsel) >> (8 * byte) & 0xFF;
  61. }
  62. else
  63. {
  64. dbg_log("Unexpected IOAPIC register read: " + h(addr >>> 0), LOG_APIC);
  65. dbg_assert(false);
  66. return 0;
  67. }
  68. },
  69. (addr, value) =>
  70. {
  71. dbg_assert(false, "unsupported write8 from ioapic: " + h(addr >>> 0));
  72. },
  73. (addr) =>
  74. {
  75. addr = addr - IOAPIC_ADDRESS | 0;
  76. if(addr === IOREGSEL)
  77. {
  78. return this.ioregsel;
  79. }
  80. else if(addr === IOWIN)
  81. {
  82. return this.read(this.ioregsel);
  83. }
  84. else
  85. {
  86. dbg_log("Unexpected IOAPIC register read: " + h(addr >>> 0), LOG_APIC);
  87. dbg_assert(false);
  88. return 0;
  89. }
  90. },
  91. (addr, value) =>
  92. {
  93. addr = addr - IOAPIC_ADDRESS | 0;
  94. if(addr === IOREGSEL)
  95. {
  96. this.ioregsel = value;
  97. }
  98. else if(addr === IOWIN)
  99. {
  100. this.write(this.ioregsel, value);
  101. }
  102. else
  103. {
  104. dbg_log("Unexpected IOAPIC register write: " + h(addr >>> 0) + " <- " + h(value >>> 0, 8), LOG_APIC);
  105. dbg_assert(false);
  106. }
  107. });
  108. }
  109. IOAPIC.prototype.remote_eoi = function(vector)
  110. {
  111. for(var i = 0; i < IOAPIC_IRQ_COUNT; i++)
  112. {
  113. var config = this.ioredtbl_config[i];
  114. if((config & 0xFF) === vector && (config & IOAPIC_CONFIG_REMOTE_IRR))
  115. {
  116. dbg_log("Clear remote IRR for irq=" + h(i), LOG_APIC);
  117. this.ioredtbl_config[i] &= ~IOAPIC_CONFIG_REMOTE_IRR;
  118. this.check_irq(i);
  119. }
  120. }
  121. };
  122. IOAPIC.prototype.check_irq = function(irq)
  123. {
  124. var mask = 1 << irq;
  125. if((this.irr & mask) === 0)
  126. {
  127. return;
  128. }
  129. var config = this.ioredtbl_config[irq];
  130. if((config & IOAPIC_CONFIG_MASKED) === 0)
  131. {
  132. var delivery_mode = config >> 8 & 7;
  133. var destination_mode = config >> 11 & 1;
  134. var vector = config & 0xFF;
  135. var destination = this.ioredtbl_destination[irq] >>> 24;
  136. var is_level = (config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL) === IOAPIC_CONFIG_TRIGGER_MODE_LEVEL;
  137. if((config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL) === 0)
  138. {
  139. this.irr &= ~mask;
  140. }
  141. else
  142. {
  143. this.ioredtbl_config[irq] |= IOAPIC_CONFIG_REMOTE_IRR;
  144. if(config & IOAPIC_CONFIG_REMOTE_IRR)
  145. {
  146. dbg_log("No route: level interrupt and remote IRR still set", LOG_APIC);
  147. return;
  148. }
  149. }
  150. if(delivery_mode === IOAPIC_DELIVERY_FIXED || delivery_mode === IOAPIC_DELIVERY_LOWEST_PRIORITY)
  151. {
  152. this.cpu.devices.apic.route(vector, delivery_mode, is_level, destination, destination_mode);
  153. }
  154. else
  155. {
  156. dbg_assert(false, "TODO");
  157. }
  158. this.ioredtbl_config[irq] &= ~IOAPIC_CONFIG_DELIVS;
  159. }
  160. };
  161. IOAPIC.prototype.set_irq = function(i)
  162. {
  163. if(i >= IOAPIC_IRQ_COUNT)
  164. {
  165. dbg_assert(false, "Bad irq: " + i, LOG_APIC);
  166. return;
  167. }
  168. var mask = 1 << i;
  169. if((this.irq_value & mask) === 0)
  170. {
  171. APIC_LOG_VERBOSE && dbg_log("apic set irq " + i, LOG_APIC);
  172. this.irq_value |= mask;
  173. var config = this.ioredtbl_config[i];
  174. if((config & (IOAPIC_CONFIG_TRIGGER_MODE_LEVEL|IOAPIC_CONFIG_MASKED)) ===
  175. IOAPIC_CONFIG_MASKED)
  176. {
  177. // edge triggered and masked
  178. return;
  179. }
  180. this.irr |= mask;
  181. this.check_irq(i);
  182. }
  183. };
  184. IOAPIC.prototype.clear_irq = function(i)
  185. {
  186. if(i >= IOAPIC_IRQ_COUNT)
  187. {
  188. dbg_assert(false, "Bad irq: " + i, LOG_APIC);
  189. return;
  190. }
  191. var mask = 1 << i;
  192. if((this.irq_value & mask) === mask)
  193. {
  194. this.irq_value &= ~mask;
  195. var config = this.ioredtbl_config[i];
  196. if(config & IOAPIC_CONFIG_TRIGGER_MODE_LEVEL)
  197. {
  198. this.irr &= ~mask;
  199. }
  200. }
  201. };
  202. IOAPIC.prototype.read = function(reg)
  203. {
  204. if(reg === 0)
  205. {
  206. dbg_log("IOAPIC Read id", LOG_APIC);
  207. return this.ioapic_id << 24;
  208. }
  209. else if(reg === 1)
  210. {
  211. dbg_log("IOAPIC Read version", LOG_APIC);
  212. return 0x11 | IOAPIC_IRQ_COUNT - 1 << 16;
  213. }
  214. else if(reg === 2)
  215. {
  216. dbg_log("IOAPIC Read arbitration id", LOG_APIC);
  217. return this.ioapic_id << 24;
  218. }
  219. else if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
  220. {
  221. var irq = reg - 0x10 >> 1;
  222. var index = reg & 1;
  223. if(index)
  224. {
  225. var value = this.ioredtbl_destination[irq];
  226. dbg_log("IOAPIC Read destination irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
  227. }
  228. else
  229. {
  230. var value = this.ioredtbl_config[irq];
  231. dbg_log("IOAPIC Read config irq=" + h(irq) + " -> " + h(value, 8), LOG_APIC);
  232. }
  233. return value;
  234. }
  235. else
  236. {
  237. dbg_log("IOAPIC register read outside of range " + h(reg), LOG_APIC);
  238. dbg_assert(false);
  239. return 0;
  240. }
  241. };
  242. IOAPIC.prototype.write = function(reg, value)
  243. {
  244. //dbg_log("IOAPIC write " + h(reg) + " <- " + h(value, 8), LOG_APIC);
  245. if(reg === 0)
  246. {
  247. this.ioapic_id = value >>> 24 & 0x0F;
  248. }
  249. else if(reg === 1 || reg === 2)
  250. {
  251. dbg_log("Invalid write: " + reg, LOG_APIC);
  252. }
  253. else if(reg >= 0x10 && reg < 0x10 + 2 * IOAPIC_IRQ_COUNT)
  254. {
  255. var irq = reg - 0x10 >> 1;
  256. var index = reg & 1;
  257. if(index)
  258. {
  259. this.ioredtbl_destination[irq] = value & 0xFF000000;
  260. dbg_log("Write destination " + h(value >>> 0, 8) + " irq=" + h(irq) + " dest=" + h(value >>> 24, 2), LOG_APIC);
  261. }
  262. else
  263. {
  264. var old_value = this.ioredtbl_config[irq];
  265. this.ioredtbl_config[irq] = value & ~IOAPIC_CONFIG_READONLY_MASK | old_value & IOAPIC_CONFIG_READONLY_MASK;
  266. var vector = value & 0xFF;
  267. var delivery_mode = value >> 8 & 7;
  268. var destination_mode = value >> 11 & 1;
  269. var is_level = value >> 15 & 1;
  270. var disabled = value >> 16 & 1;
  271. dbg_log("Write config " + h(value >>> 0, 8) +
  272. " irq=" + h(irq) +
  273. " vector=" + h(vector, 2) +
  274. " deliverymode=" + DELIVERY_MODES[delivery_mode] +
  275. " destmode=" + DESTINATION_MODES[destination_mode] +
  276. " is_level=" + is_level +
  277. " disabled=" + disabled, LOG_APIC);
  278. this.check_irq(irq);
  279. }
  280. }
  281. else
  282. {
  283. dbg_log("IOAPIC register write outside of range " + h(reg) + ": " + h(value >>> 0, 8), LOG_APIC);
  284. dbg_assert(false);
  285. }
  286. };
  287. IOAPIC.prototype.get_state = function()
  288. {
  289. var state = [];
  290. state[0] = this.ioredtbl_config;
  291. state[1] = this.ioredtbl_destination;
  292. state[2] = this.ioregsel;
  293. state[3] = this.ioapic_id;
  294. state[4] = this.irr;
  295. state[5] = this.irq_value;
  296. return state;
  297. };
  298. IOAPIC.prototype.set_state = function(state)
  299. {
  300. this.ioredtbl_config = state[0];
  301. this.ioredtbl_destination = state[1];
  302. this.ioregsel = state[2];
  303. this.ioapic_id = state[3];
  304. this.irr = state[4];
  305. this.irq_value = state[5];
  306. };