IOAPIC.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Optional.h>
  7. #include <Kernel/Arch/InterruptDisabler.h>
  8. #include <Kernel/Debug.h>
  9. #include <Kernel/Interrupts/APIC.h>
  10. #include <Kernel/Interrupts/IOAPIC.h>
  11. #include <Kernel/Interrupts/InterruptManagement.h>
  12. #include <Kernel/Sections.h>
  13. #define IOAPIC_REDIRECTION_ENTRY_OFFSET 0x10
  14. namespace Kernel {
  15. enum DeliveryMode {
  16. Normal = 0,
  17. LowPriority = 1,
  18. SMI = 2,
  19. NMI = 3,
  20. INIT = 4,
  21. External = 7
  22. };
  23. UNMAP_AFTER_INIT IOAPIC::IOAPIC(PhysicalAddress address, u32 gsi_base)
  24. : m_address(address)
  25. , m_regs(Memory::map_typed_writable<ioapic_mmio_regs>(m_address).release_value_but_fixme_should_propagate_errors())
  26. , m_gsi_base(gsi_base)
  27. , m_id((read_register(0x0) >> 24) & 0xFF)
  28. , m_version(read_register(0x1) & 0xFF)
  29. , m_redirection_entries_count((read_register(0x1) >> 16) + 1)
  30. {
  31. InterruptDisabler disabler;
  32. dmesgln("IOAPIC ID: {:#x}", m_id);
  33. dmesgln("IOAPIC Version: {:#x}, redirection entries: {}", m_version, m_redirection_entries_count);
  34. dmesgln("IOAPIC Arbitration ID {:#x}", read_register(0x2));
  35. mask_all_redirection_entries();
  36. }
  37. UNMAP_AFTER_INIT void IOAPIC::initialize()
  38. {
  39. }
  40. void IOAPIC::map_interrupt_redirection(u8 interrupt_vector)
  41. {
  42. InterruptDisabler disabler;
  43. for (auto redirection_override : InterruptManagement::the().isa_overrides()) {
  44. if (redirection_override.source() != interrupt_vector)
  45. continue;
  46. bool active_low = false;
  47. // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
  48. switch ((redirection_override.flags() & 0b11)) {
  49. case 0:
  50. active_low = false;
  51. break;
  52. case 1:
  53. active_low = false;
  54. break;
  55. case 2:
  56. VERIFY_NOT_REACHED(); // Reserved value
  57. case 3:
  58. active_low = true;
  59. break;
  60. }
  61. bool trigger_level_mode = false;
  62. // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
  63. switch (((redirection_override.flags() >> 2) & 0b11)) {
  64. case 0:
  65. trigger_level_mode = false;
  66. break;
  67. case 1:
  68. trigger_level_mode = false;
  69. break;
  70. case 2:
  71. VERIFY_NOT_REACHED(); // Reserved value
  72. case 3:
  73. trigger_level_mode = true;
  74. break;
  75. }
  76. configure_redirection_entry(redirection_override.gsi() - gsi_base(), InterruptManagement::acquire_mapped_interrupt_number(redirection_override.source()) + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, active_low, trigger_level_mode, true, 0);
  77. return;
  78. }
  79. isa_identity_map(interrupt_vector);
  80. }
  81. void IOAPIC::isa_identity_map(int index)
  82. {
  83. InterruptDisabler disabler;
  84. configure_redirection_entry(index, InterruptManagement::acquire_mapped_interrupt_number(index) + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, false, true, 0);
  85. }
  86. void IOAPIC::map_pci_interrupts()
  87. {
  88. InterruptDisabler disabler;
  89. configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, true, true, 0);
  90. }
  91. bool IOAPIC::is_enabled() const
  92. {
  93. return !is_hard_disabled();
  94. }
  95. void IOAPIC::spurious_eoi(GenericInterruptHandler const& handler) const
  96. {
  97. InterruptDisabler disabler;
  98. VERIFY(handler.type() == HandlerType::SpuriousInterruptHandler);
  99. VERIFY(handler.interrupt_number() == APIC::spurious_interrupt_vector());
  100. dbgln("IOAPIC: Spurious interrupt");
  101. }
  102. void IOAPIC::map_isa_interrupts()
  103. {
  104. InterruptDisabler disabler;
  105. for (auto redirection_override : InterruptManagement::the().isa_overrides()) {
  106. if ((redirection_override.gsi() < gsi_base()) || (redirection_override.gsi() >= (gsi_base() + m_redirection_entries_count)))
  107. continue;
  108. bool active_low = false;
  109. // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
  110. switch ((redirection_override.flags() & 0b11)) {
  111. case 0:
  112. active_low = false;
  113. break;
  114. case 1:
  115. active_low = false;
  116. break;
  117. case 2:
  118. VERIFY_NOT_REACHED();
  119. case 3:
  120. active_low = true;
  121. break;
  122. }
  123. bool trigger_level_mode = false;
  124. // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
  125. switch (((redirection_override.flags() >> 2) & 0b11)) {
  126. case 0:
  127. trigger_level_mode = false;
  128. break;
  129. case 1:
  130. trigger_level_mode = false;
  131. break;
  132. case 2:
  133. VERIFY_NOT_REACHED();
  134. case 3:
  135. trigger_level_mode = true;
  136. break;
  137. }
  138. configure_redirection_entry(redirection_override.gsi() - gsi_base(), InterruptManagement::acquire_mapped_interrupt_number(redirection_override.source()) + IRQ_VECTOR_BASE, 0, false, active_low, trigger_level_mode, true, 0);
  139. }
  140. }
  141. void IOAPIC::reset_all_redirection_entries() const
  142. {
  143. InterruptDisabler disabler;
  144. for (size_t index = 0; index < m_redirection_entries_count; index++)
  145. reset_redirection_entry(index);
  146. }
  147. void IOAPIC::hard_disable()
  148. {
  149. InterruptDisabler disabler;
  150. reset_all_redirection_entries();
  151. IRQController::hard_disable();
  152. }
  153. void IOAPIC::reset_redirection_entry(int index) const
  154. {
  155. InterruptDisabler disabler;
  156. configure_redirection_entry(index, 0, 0, false, false, false, true, 0);
  157. }
  158. void IOAPIC::configure_redirection_entry(int index, u8 interrupt_vector, u8 delivery_mode, bool logical_destination, bool active_low, bool trigger_level_mode, bool masked, u8 destination) const
  159. {
  160. InterruptDisabler disabler;
  161. VERIFY((u32)index < m_redirection_entries_count);
  162. u32 redirection_entry1 = interrupt_vector | (delivery_mode & 0b111) << 8 | logical_destination << 11 | active_low << 13 | trigger_level_mode << 15 | masked << 16;
  163. u32 redirection_entry2 = destination << 24;
  164. write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry1);
  165. if constexpr (IOAPIC_DEBUG)
  166. dbgln("IOAPIC Value: {:#x}", read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET));
  167. write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET + 1, redirection_entry2);
  168. if constexpr (IOAPIC_DEBUG)
  169. dbgln("IOAPIC Value: {:#x}", read_register((index << 1) + 0x11));
  170. }
  171. void IOAPIC::mask_all_redirection_entries() const
  172. {
  173. InterruptDisabler disabler;
  174. for (size_t index = 0; index < m_redirection_entries_count; index++)
  175. mask_redirection_entry(index);
  176. }
  177. void IOAPIC::mask_redirection_entry(u8 index) const
  178. {
  179. VERIFY((u32)index < m_redirection_entries_count);
  180. u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET);
  181. if (redirection_entry & (1 << 16))
  182. return;
  183. write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry | (1 << 16));
  184. }
  185. bool IOAPIC::is_redirection_entry_masked(u8 index) const
  186. {
  187. VERIFY((u32)index < m_redirection_entries_count);
  188. return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & (1 << 16)) != 0;
  189. }
  190. void IOAPIC::unmask_redirection_entry(u8 index) const
  191. {
  192. VERIFY((u32)index < m_redirection_entries_count);
  193. u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET);
  194. if (!(redirection_entry & (1 << 16)))
  195. return;
  196. write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry & ~(1 << 16));
  197. }
  198. bool IOAPIC::is_vector_enabled(u8 interrupt_vector) const
  199. {
  200. InterruptDisabler disabler;
  201. return is_redirection_entry_masked(interrupt_vector);
  202. }
  203. u8 IOAPIC::read_redirection_entry_vector(u8 index) const
  204. {
  205. VERIFY((u32)index < m_redirection_entries_count);
  206. return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & 0xFF);
  207. }
  208. Optional<int> IOAPIC::find_redirection_entry_by_vector(u8 vector) const
  209. {
  210. InterruptDisabler disabler;
  211. for (size_t index = 0; index < m_redirection_entries_count; index++) {
  212. if (read_redirection_entry_vector(index) == (InterruptManagement::acquire_mapped_interrupt_number(vector) + IRQ_VECTOR_BASE))
  213. return index;
  214. }
  215. return {};
  216. }
  217. void IOAPIC::disable(GenericInterruptHandler const& handler)
  218. {
  219. InterruptDisabler disabler;
  220. VERIFY(!is_hard_disabled());
  221. u8 interrupt_vector = handler.interrupt_number();
  222. VERIFY(interrupt_vector >= gsi_base() && interrupt_vector < interrupt_vectors_count());
  223. auto found_index = find_redirection_entry_by_vector(interrupt_vector);
  224. if (!found_index.has_value()) {
  225. map_interrupt_redirection(interrupt_vector);
  226. found_index = find_redirection_entry_by_vector(interrupt_vector);
  227. }
  228. VERIFY(found_index.has_value());
  229. mask_redirection_entry(found_index.value());
  230. }
  231. void IOAPIC::enable(GenericInterruptHandler const& handler)
  232. {
  233. InterruptDisabler disabler;
  234. VERIFY(!is_hard_disabled());
  235. u8 interrupt_vector = handler.interrupt_number();
  236. VERIFY(interrupt_vector >= gsi_base() && interrupt_vector < interrupt_vectors_count());
  237. auto found_index = find_redirection_entry_by_vector(interrupt_vector);
  238. if (!found_index.has_value()) {
  239. map_interrupt_redirection(interrupt_vector);
  240. found_index = find_redirection_entry_by_vector(interrupt_vector);
  241. }
  242. VERIFY(found_index.has_value());
  243. unmask_redirection_entry(found_index.value());
  244. }
  245. void IOAPIC::eoi(GenericInterruptHandler const& handler) const
  246. {
  247. InterruptDisabler disabler;
  248. VERIFY(!is_hard_disabled());
  249. VERIFY(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count());
  250. VERIFY(handler.type() != HandlerType::SpuriousInterruptHandler);
  251. APIC::the().eoi();
  252. }
  253. u16 IOAPIC::get_isr() const
  254. {
  255. InterruptDisabler disabler;
  256. VERIFY_NOT_REACHED();
  257. }
  258. u16 IOAPIC::get_irr() const
  259. {
  260. InterruptDisabler disabler;
  261. VERIFY_NOT_REACHED();
  262. }
  263. void IOAPIC::write_register(u32 index, u32 value) const
  264. {
  265. InterruptDisabler disabler;
  266. m_regs->select = index;
  267. m_regs->window = value;
  268. dbgln_if(IOAPIC_DEBUG, "IOAPIC Writing, Value {:#x} @ offset {:#x}", (u32)m_regs->window, (u32)m_regs->select);
  269. }
  270. u32 IOAPIC::read_register(u32 index) const
  271. {
  272. InterruptDisabler disabler;
  273. m_regs->select = index;
  274. dbgln_if(IOAPIC_DEBUG, "IOAPIC Reading, Value {:#x} @ offset {:#x}", (u32)m_regs->window, (u32)m_regs->select);
  275. return m_regs->window;
  276. }
  277. }