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 <AK/Optional.h>
28#include <AK/StringView.h>
29#include <Kernel/ACPI/MultiProcessorParser.h>
30#include <Kernel/Arch/i386/CPU.h>
31#include <Kernel/Interrupts/APIC.h>
32#include <Kernel/Interrupts/IOAPIC.h>
33#include <Kernel/Interrupts/InterruptManagement.h>
34#include <Kernel/VM/MemoryManager.h>
35
36#define IOAPIC_REDIRECTION_ENTRY_OFFSET 0x10
37namespace Kernel {
38enum DeliveryMode {
39 Normal = 0,
40 LowPriority = 1,
41 SMI = 2,
42 NMI = 3,
43 INIT = 4,
44 External = 7
45};
46
47IOAPIC::IOAPIC(ioapic_mmio_regs& regs, u32 gsi_base)
48 : m_physical_access_registers(regs)
49 , m_gsi_base(gsi_base)
50 , m_id((read_register(0x0) >> 24) & 0xFF)
51 , m_version(read_register(0x1) & 0xFF)
52 , m_redirection_entries_count((read_register(0x1) >> 16) + 1)
53{
54 InterruptDisabler disabler;
55 klog() << "IOAPIC ID: 0x" << String::format("%x", m_id);
56 klog() << "IOAPIC Version: 0x" << String::format("%x", m_version) << ", Redirection Entries count - " << m_redirection_entries_count;
57 klog() << "IOAPIC Arbitration ID 0x" << String::format("%x", read_register(0x2));
58 mask_all_redirection_entries();
59}
60
61void IOAPIC::initialize()
62{
63}
64
65void IOAPIC::map_interrupt_redirection(u8 interrupt_vector)
66{
67 InterruptDisabler disabler;
68 for (auto redirection_override : InterruptManagement::the().isa_overrides()) {
69 ASSERT(!redirection_override.is_null());
70 if (redirection_override->source() != interrupt_vector)
71 continue;
72 bool active_low;
73 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
74 switch ((redirection_override->flags() & 0b11)) {
75 case 0:
76 active_low = false;
77 break;
78 case 1:
79 active_low = false;
80 break;
81 case 2:
82 ASSERT_NOT_REACHED(); // Reserved value
83 case 3:
84 active_low = true;
85 break;
86 }
87
88 bool trigger_level_mode;
89 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
90 switch (((redirection_override->flags() >> 2) & 0b11)) {
91 case 0:
92 trigger_level_mode = false;
93 break;
94 case 1:
95 trigger_level_mode = false;
96 break;
97 case 2:
98 ASSERT_NOT_REACHED(); // Reserved value
99 case 3:
100 trigger_level_mode = true;
101 break;
102 }
103 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);
104 return;
105 }
106 isa_identity_map(interrupt_vector);
107}
108
109void IOAPIC::isa_identity_map(int index)
110{
111 InterruptDisabler disabler;
112 configure_redirection_entry(index, InterruptManagement::acquire_mapped_interrupt_number(index) + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, false, true, 0);
113}
114
115void IOAPIC::map_pci_interrupts()
116{
117 InterruptDisabler disabler;
118 configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, true, true, 0);
119}
120
121bool IOAPIC::is_enabled() const
122{
123 return !is_hard_disabled();
124}
125
126void IOAPIC::spurious_eoi(const GenericInterruptHandler& handler) const
127{
128 InterruptDisabler disabler;
129 ASSERT(handler.type() == HandlerType::SpuriousInterruptHandler);
130 ASSERT(handler.interrupt_number() == APIC::spurious_interrupt_vector());
131 klog() << "IOAPIC::spurious_eoi - Spurious Interrupt occurred";
132}
133
134void IOAPIC::map_isa_interrupts()
135{
136 InterruptDisabler disabler;
137 for (auto redirection_override : InterruptManagement::the().isa_overrides()) {
138 ASSERT(!redirection_override.is_null());
139 if ((redirection_override->gsi() < gsi_base()) || (redirection_override->gsi() >= (gsi_base() + m_redirection_entries_count)))
140 continue;
141 bool active_low;
142 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
143 switch ((redirection_override->flags() & 0b11)) {
144 case 0:
145 active_low = false;
146 break;
147 case 1:
148 active_low = false;
149 break;
150 case 2:
151 ASSERT_NOT_REACHED();
152 case 3:
153 active_low = true;
154 break;
155 }
156
157 bool trigger_level_mode;
158 // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags.
159 switch (((redirection_override->flags() >> 2) & 0b11)) {
160 case 0:
161 trigger_level_mode = false;
162 break;
163 case 1:
164 trigger_level_mode = false;
165 break;
166 case 2:
167 ASSERT_NOT_REACHED();
168 case 3:
169 trigger_level_mode = true;
170 break;
171 }
172 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);
173 }
174}
175
176void IOAPIC::reset_all_redirection_entries() const
177{
178 InterruptDisabler disabler;
179 for (size_t index = 0; index < m_redirection_entries_count; index++)
180 reset_redirection_entry(index);
181}
182
183void IOAPIC::hard_disable()
184{
185 InterruptDisabler disabler;
186 reset_all_redirection_entries();
187 IRQController::hard_disable();
188}
189
190void IOAPIC::reset_redirection_entry(int index) const
191{
192 InterruptDisabler disabler;
193 configure_redirection_entry(index, 0, 0, false, false, false, true, 0);
194}
195
196void 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
197{
198 InterruptDisabler disabler;
199 ASSERT((u32)index < m_redirection_entries_count);
200 u32 redirection_entry1 = interrupt_vector | (delivery_mode & 0b111) << 8 | logical_destination << 11 | active_low << 13 | trigger_level_mode << 15 | masked << 16;
201 u32 redirection_entry2 = destination << 24;
202 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry1);
203#ifdef IOAPIC_DEBUG
204 dbg() << "IOAPIC Value: 0x" << String::format("%x", read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET));
205#endif
206 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET + 1, redirection_entry2);
207#ifdef IOAPIC_DEBUG
208 dbg() << "IOAPIC Value: 0x" << String::format("%x", read_register((index << 1) + 0x11));
209#endif
210}
211
212void IOAPIC::mask_all_redirection_entries() const
213{
214 InterruptDisabler disabler;
215 for (size_t index = 0; index < m_redirection_entries_count; index++)
216 mask_redirection_entry(index);
217}
218
219void IOAPIC::mask_redirection_entry(u8 index) const
220{
221 ASSERT((u32)index < m_redirection_entries_count);
222 u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET);
223 if (redirection_entry & (1 << 16))
224 return;
225 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry | (1 << 16));
226}
227
228bool IOAPIC::is_redirection_entry_masked(u8 index) const
229{
230 ASSERT((u32)index < m_redirection_entries_count);
231 return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & (1 << 16)) != 0;
232}
233
234void IOAPIC::unmask_redirection_entry(u8 index) const
235{
236 ASSERT((u32)index < m_redirection_entries_count);
237 u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET);
238 if (!(redirection_entry & (1 << 16)))
239 return;
240 write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry & ~(1 << 16));
241}
242
243bool IOAPIC::is_vector_enabled(u8 interrupt_vector) const
244{
245 InterruptDisabler disabler;
246 return is_redirection_entry_masked(interrupt_vector);
247}
248
249u8 IOAPIC::read_redirection_entry_vector(u8 index) const
250{
251 ASSERT((u32)index < m_redirection_entries_count);
252 return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & 0xFF);
253}
254
255Optional<int> IOAPIC::find_redirection_entry_by_vector(u8 vector) const
256{
257 InterruptDisabler disabler;
258 for (size_t index = 0; index < m_redirection_entries_count; index++) {
259 if (read_redirection_entry_vector(index) == (InterruptManagement::acquire_mapped_interrupt_number(vector) + IRQ_VECTOR_BASE))
260 return index;
261 }
262 return {};
263}
264
265void IOAPIC::disable(const GenericInterruptHandler& handler)
266{
267 InterruptDisabler disabler;
268 ASSERT(!is_hard_disabled());
269 u8 interrupt_vector = handler.interrupt_number();
270 ASSERT(interrupt_vector >= gsi_base() && interrupt_vector < interrupt_vectors_count());
271 auto found_index = find_redirection_entry_by_vector(interrupt_vector);
272 if (!found_index.has_value()) {
273 map_interrupt_redirection(interrupt_vector);
274 found_index = find_redirection_entry_by_vector(interrupt_vector);
275 }
276 ASSERT(found_index.has_value());
277 mask_redirection_entry(found_index.value());
278}
279
280void IOAPIC::enable(const GenericInterruptHandler& handler)
281{
282 InterruptDisabler disabler;
283 ASSERT(!is_hard_disabled());
284 u8 interrupt_vector = handler.interrupt_number();
285 ASSERT(interrupt_vector >= gsi_base() && interrupt_vector < interrupt_vectors_count());
286 auto found_index = find_redirection_entry_by_vector(interrupt_vector);
287 if (!found_index.has_value()) {
288 map_interrupt_redirection(interrupt_vector);
289 found_index = find_redirection_entry_by_vector(interrupt_vector);
290 }
291 ASSERT(found_index.has_value());
292 unmask_redirection_entry(found_index.value());
293}
294
295void IOAPIC::eoi(const GenericInterruptHandler& handler) const
296{
297 InterruptDisabler disabler;
298 ASSERT(!is_hard_disabled());
299 ASSERT(handler.interrupt_number() >= gsi_base() && handler.interrupt_number() < interrupt_vectors_count());
300 ASSERT(handler.type() != HandlerType::SpuriousInterruptHandler);
301 APIC::eoi();
302}
303
304u16 IOAPIC::get_isr() const
305{
306 InterruptDisabler disabler;
307 ASSERT_NOT_REACHED();
308}
309
310u16 IOAPIC::get_irr() const
311{
312 InterruptDisabler disabler;
313 ASSERT_NOT_REACHED();
314}
315
316void IOAPIC::write_register(u32 index, u32 value) const
317{
318 InterruptDisabler disabler;
319 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);
320 auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr();
321 regs.select = index;
322 regs.window = value;
323#ifdef IOAPIC_DEBUG
324 dbg() << "IOAPIC Writing, Value 0x" << String::format("%x", regs.window) << " @ offset 0x" << String::format("%x", regs.select);
325#endif
326}
327u32 IOAPIC::read_register(u32 index) const
328{
329 InterruptDisabler disabler;
330 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);
331 auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr();
332 regs.select = index;
333#ifdef IOAPIC_DEBUG
334 dbg() << "IOAPIC Reading, Value 0x" << String::format("%x", regs.window) << " @ offset 0x" << String::format("%x", regs.select);
335#endif
336 return regs.window;
337}
338}