Serenity Operating System
1/*
2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <Kernel/ACPI/MultiProcessorParser.h>
28#include <Kernel/Arch/i386/CPU.h>
29#include <Kernel/Interrupts/APIC.h>
30#include <Kernel/Interrupts/IOAPIC.h>
31#include <Kernel/Interrupts/InterruptManagement.h>
32#include <Kernel/VM/MemoryManager.h>
33
34#define IOAPIC_REDIRECTION_ENTRY_OFFSET 0x10
35namespace Kernel {
36enum DeliveryMode {
37 Normal = 0,
38 LowPriority = 1,
39 SMI = 2,
40 NMI = 3,
41 INIT = 4,
42 External = 7
43};
44
45IOAPIC::IOAPIC(ioapic_mmio_regs& regs, u32 gsi_base, Vector<RefPtr<ISAInterruptOverrideMetadata>>& isa_overrides, Vector<RefPtr<PCIInterruptOverrideMetadata>>& pci_overrides)
46 : m_physical_access_registers(regs)
47 , m_gsi_base(gsi_base)
48 , m_id((read_register(0x0) >> 24) & 0xFF)
49 , m_version(read_register(0x1) & 0xFF)
50 , m_redirection_entries((read_register(0x1) >> 16) + 1)
51 , m_isa_interrupt_overrides(isa_overrides)
52 , m_pci_interrupt_overrides(pci_overrides)
53{
54 kprintf("IOAPIC ID: 0x%x\n", m_id);
55 kprintf("IOAPIC Version: 0x%x, Redirection Entries count - %u\n", m_version, m_redirection_entries);
56 kprintf("IOAPIC Arbitration ID 0x%x\n", read_register(0x2));
57 mask_all_redirection_entries();
58}
59
60void IOAPIC::initialize()
61{
62}
63
64void IOAPIC::map_interrupt_redirection(u8 interrupt_vector)
65{
66 if (interrupt_vector == 11) {
67 configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::LowPriority, false, true, true, true, 0);
68 return;
69 }
70 for (auto redirection_override : m_isa_interrupt_overrides) {
71 ASSERT(!redirection_override.is_null());
72 if (redirection_override->source() != interrupt_vector)
73 continue;
74 bool active_low;
75 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
76 switch ((redirection_override->flags() & 0b11)) {
77 case 0:
78 active_low = false;
79 break;
80 case 1:
81 active_low = false;
82 break;
83 case 2:
84 ASSERT_NOT_REACHED(); // Reserved value
85 case 3:
86 active_low = true;
87 break;
88 }
89
90 bool trigger_level_mode;
91 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
92 switch (((redirection_override->flags() >> 2) & 0b11)) {
93 case 0:
94 trigger_level_mode = false;
95 break;
96 case 1:
97 trigger_level_mode = false;
98 break;
99 case 2:
100 ASSERT_NOT_REACHED(); // Reserved value
101 case 3:
102 trigger_level_mode = true;
103 break;
104 }
105 configure_redirection_entry(redirection_override->gsi(), redirection_override->source() + IRQ_VECTOR_BASE, DeliveryMode::LowPriority, false, active_low, trigger_level_mode, true, 0);
106 return;
107 }
108 isa_identity_map(interrupt_vector);
109}
110
111void IOAPIC::isa_identity_map(int index)
112{
113 configure_redirection_entry(index, index + IRQ_VECTOR_BASE, DeliveryMode::Normal, true, false, false, true, 1);
114}
115
116
117void IOAPIC::map_pci_interrupts()
118{
119 configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, true, true, 0);
120}
121
122void IOAPIC::map_isa_interrupts()
123{
124 InterruptDisabler disabler;
125 for (auto redirection_override : m_isa_interrupt_overrides) {
126 ASSERT(!redirection_override.is_null());
127 bool active_low;
128 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
129 switch ((redirection_override->flags() & 0b11)) {
130 case 0:
131 active_low = false;
132 break;
133 case 1:
134 active_low = false;
135 break;
136 case 2:
137 ASSERT_NOT_REACHED();
138 case 3:
139 active_low = true;
140 break;
141 }
142
143 bool trigger_level_mode;
144 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
145 switch (((redirection_override->flags() >> 2) & 0b11)) {
146 case 0:
147 trigger_level_mode = false;
148 break;
149 case 1:
150 trigger_level_mode = false;
151 break;
152 case 2:
153 ASSERT_NOT_REACHED();
154 case 3:
155 trigger_level_mode = true;
156 break;
157 }
158 configure_redirection_entry(redirection_override->gsi(), redirection_override->source() + IRQ_VECTOR_BASE, 0, false, active_low, trigger_level_mode, true, 0);
159 }
160}
161
162void IOAPIC::reset_all_redirection_entries() const
163{
164 InterruptDisabler disabler;
165 for (size_t index = 0; index < m_redirection_entries; index++)
166 reset_redirection_entry(index);
167}
168
169void IOAPIC::hard_disable()
170{
171 InterruptDisabler disabler;
172 reset_all_redirection_entries();
173 IRQController::hard_disable();
174}
175
176void IOAPIC::reset_redirection_entry(int index) const
177{
178 InterruptDisabler disabler;
179 configure_redirection_entry(index, 0, 0, false, false, false, true, 0);
180}
181
182void 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
183{
184 ASSERT((u32)index < m_redirection_entries);
185 u32 redirection_entry1 = interrupt_vector | (delivery_mode & 0b111) << 8 | logical_destination << 11 | active_low << 13 | trigger_level_mode << 15 | masked << 16;
186 u32 redirection_entry2 = destination << 24;
187 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry1);
188#ifdef IOAPIC_DEBUG
189 dbgprintf("IOAPIC Value: 0x%x\n", read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET));
190#endif
191 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET + 1, redirection_entry2);
192#ifdef IOAPIC_DEBUG
193 dbgprintf("IOAPIC Value: 0x%x\n", read_register((index << 1) + 0x11));
194#endif
195}
196
197void IOAPIC::mask_all_redirection_entries() const
198{
199 InterruptDisabler disabler;
200 for (size_t index = 0; index < m_redirection_entries; index++)
201 mask_redirection_entry(index);
202}
203
204void IOAPIC::mask_redirection_entry(u8 index) const
205{
206 ASSERT((u32)index < m_redirection_entries);
207 u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) | (1 << 16);
208 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry);
209}
210
211bool IOAPIC::is_redirection_entry_masked(u8 index) const
212{
213 ASSERT((u32)index < m_redirection_entries);
214 return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & (1 << 16)) != 0;
215}
216
217void IOAPIC::unmask_redirection_entry(u8 index) const
218{
219 ASSERT((u32)index < m_redirection_entries);
220 u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET);
221 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry & ~(1 << 16));
222}
223
224bool IOAPIC::is_vector_enabled(u8 interrupt_vector) const
225{
226 InterruptDisabler disabler;
227 return is_redirection_entry_masked(interrupt_vector);
228}
229
230u8 IOAPIC::read_redirection_entry_vector(u8 index) const
231{
232 ASSERT((u32)index < m_redirection_entries);
233 return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & 0xFF);
234}
235
236int IOAPIC::find_redirection_entry_by_vector(u8 vector) const
237{
238 InterruptDisabler disabler;
239 for (size_t index = 0; index < m_redirection_entries; index++) {
240 if (read_redirection_entry_vector(index) == (vector + IRQ_VECTOR_BASE))
241 return index;
242 }
243 return -1;
244}
245
246void IOAPIC::disable(u8 interrupt_vector)
247{
248 InterruptDisabler disabler;
249 int index = find_redirection_entry_by_vector(interrupt_vector);
250 if (index == (-1)) {
251 map_interrupt_redirection(interrupt_vector);
252 index = find_redirection_entry_by_vector(interrupt_vector);
253 }
254 ASSERT(index != (-1));
255 mask_redirection_entry(index);
256}
257
258void IOAPIC::enable(u8 interrupt_vector)
259{
260 InterruptDisabler disabler;
261 int index = find_redirection_entry_by_vector(interrupt_vector);
262 if (index == (-1)) {
263 map_interrupt_redirection(interrupt_vector);
264 index = find_redirection_entry_by_vector(interrupt_vector);
265 }
266 ASSERT(index != (-1));
267 unmask_redirection_entry(index);
268}
269
270void IOAPIC::eoi(u8) const
271{
272 InterruptDisabler disabler;
273 APIC::eoi();
274}
275
276u16 IOAPIC::get_isr() const
277{
278 InterruptDisabler disabler;
279 ASSERT_NOT_REACHED();
280}
281
282u16 IOAPIC::get_irr() const
283{
284 InterruptDisabler disabler;
285 ASSERT_NOT_REACHED();
286}
287
288void IOAPIC::write_register(u32 index, u32 value) const
289{
290 InterruptDisabler disabler;
291 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(&m_physical_access_registers)), (PAGE_SIZE * 2), "IOAPIC Write", Region::Access::Read | Region::Access::Write);
292 auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr();
293 regs.select = index;
294 regs.window = value;
295#ifdef IOAPIC_DEBUG
296 dbgprintf("IOAPIC Writing, Value 0x%x @ offset 0x%x\n", regs.window, regs.select);
297#endif
298}
299u32 IOAPIC::read_register(u32 index) const
300{
301 InterruptDisabler disabler;
302 auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(&m_physical_access_registers)), (PAGE_SIZE * 2), "IOAPIC Read", Region::Access::Read | Region::Access::Write);
303 auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr();
304 regs.select = index;
305#ifdef IOAPIC_DEBUG
306 dbgprintf("IOAPIC Reading, Value 0x%x @ offset 0x%x\n", regs.window, regs.select);
307#endif
308 return regs.window;
309}
310}