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/FixedArray.h>
28#include <AK/StringView.h>
29#include <Kernel/ACPI/MultiProcessorParser.h>
30#include <Kernel/Arch/i386/CPU.h>
31#include <Kernel/CommandLine.h>
32#include <Kernel/Interrupts/APIC.h>
33#include <Kernel/Interrupts/IOAPIC.h>
34#include <Kernel/Interrupts/InterruptManagement.h>
35#include <Kernel/Interrupts/PIC.h>
36#include <Kernel/Interrupts/SpuriousInterruptHandler.h>
37#include <Kernel/Interrupts/UnhandledInterruptHandler.h>
38#include <Kernel/Syscall.h>
39#include <Kernel/VM/MemoryManager.h>
40#include <LibBareMetal/IO.h>
41
42#define PCAT_COMPAT_FLAG 0x1
43
44namespace Kernel {
45
46static InterruptManagement* s_interrupt_management;
47
48bool InterruptManagement::initialized()
49{
50 return (s_interrupt_management != nullptr);
51}
52
53InterruptManagement& InterruptManagement::the()
54{
55 ASSERT(InterruptManagement::initialized());
56 return *s_interrupt_management;
57}
58
59void InterruptManagement::initialize()
60{
61 ASSERT(!InterruptManagement::initialized());
62 s_interrupt_management = new InterruptManagement();
63
64 if (kernel_command_line().lookup("smp").value_or("off") == "on")
65 InterruptManagement::the().switch_to_ioapic_mode();
66 else
67 InterruptManagement::the().switch_to_pic_mode();
68}
69
70void InterruptManagement::enumerate_interrupt_handlers(Function<void(GenericInterruptHandler&)> callback)
71{
72 for (int i = 0; i < GENERIC_INTERRUPT_HANDLERS_COUNT; i++) {
73 auto& handler = get_interrupt_handler(i);
74 if (handler.type() != HandlerType::UnhandledInterruptHandler)
75 callback(handler);
76 }
77}
78
79IRQController& InterruptManagement::get_interrupt_controller(int index)
80{
81 ASSERT(index >= 0);
82 ASSERT(!m_interrupt_controllers[index].is_null());
83 return *m_interrupt_controllers[index];
84}
85
86Vector<RefPtr<ISAInterruptOverrideMetadata>> InterruptManagement::isa_overrides()
87{
88 return m_isa_interrupt_overrides;
89}
90
91u8 InterruptManagement::acquire_mapped_interrupt_number(u8 original_irq)
92{
93 if (!InterruptManagement::initialized()) {
94 // This is necessary, because we install UnhandledInterruptHandlers before we actually initialize the Interrupt Management object...
95 return original_irq;
96 }
97 return InterruptManagement::the().get_mapped_interrupt_vector(original_irq);
98}
99
100u8 InterruptManagement::acquire_irq_number(u8 mapped_interrupt_vector)
101{
102 ASSERT(InterruptManagement::initialized());
103 return InterruptManagement::the().get_irq_vector(mapped_interrupt_vector);
104}
105
106u8 InterruptManagement::get_mapped_interrupt_vector(u8 original_irq)
107{
108 // FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient.
109 // FIXME: Find a better way to handle conflict with Syscall interrupt gate.
110 ASSERT((original_irq + IRQ_VECTOR_BASE) != syscall_vector);
111 return original_irq;
112}
113
114u8 InterruptManagement::get_irq_vector(u8 mapped_interrupt_vector)
115{
116 // FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient.
117 return mapped_interrupt_vector;
118}
119
120RefPtr<IRQController> InterruptManagement::get_responsible_irq_controller(u8 interrupt_vector)
121{
122 if (m_interrupt_controllers.size() == 1 && m_interrupt_controllers[0]->type() == IRQControllerType::i8259) {
123 return m_interrupt_controllers[0];
124 }
125 for (auto irq_controller : m_interrupt_controllers) {
126 if (irq_controller->gsi_base() <= interrupt_vector)
127 if (!irq_controller->is_hard_disabled())
128 return irq_controller;
129 }
130 ASSERT_NOT_REACHED();
131}
132
133PhysicalAddress InterruptManagement::search_for_madt()
134{
135 dbg() << "Early access to ACPI tables for interrupt setup";
136 auto rsdp = ACPI::StaticParsing::find_rsdp();
137 if (rsdp.is_null())
138 return {};
139 return ACPI::StaticParsing::find_table(rsdp, "APIC");
140}
141
142InterruptManagement::InterruptManagement()
143 : m_madt(search_for_madt())
144{
145}
146
147void InterruptManagement::switch_to_pic_mode()
148{
149 klog() << "Interrupts: Switch to Legacy PIC mode";
150 InterruptDisabler disabler;
151 m_smp_enabled = false;
152 m_interrupt_controllers[0] = adopt(*new PIC());
153 SpuriousInterruptHandler::initialize(7);
154 SpuriousInterruptHandler::initialize(15);
155 for (auto& irq_controller : m_interrupt_controllers) {
156 ASSERT(irq_controller);
157 if (irq_controller->type() == IRQControllerType::i82093AA) {
158 irq_controller->hard_disable();
159 dbg() << "Interrupts: Detected " << irq_controller->model() << " - Disabled";
160 } else {
161 dbg() << "Interrupts: Detected " << irq_controller->model();
162 }
163 }
164}
165
166void InterruptManagement::switch_to_ioapic_mode()
167{
168 klog() << "Interrupts: Switch to IOAPIC mode";
169 InterruptDisabler disabler;
170
171 if (m_madt.is_null()) {
172 dbg() << "Interrupts: ACPI MADT is not available, reverting to PIC mode";
173 switch_to_pic_mode();
174 return;
175 }
176
177 dbg() << "Interrupts: MADT @ P " << m_madt.as_ptr();
178 locate_apic_data();
179 m_smp_enabled = true;
180 if (m_interrupt_controllers.size() == 1) {
181 if (get_interrupt_controller(0).type() == IRQControllerType::i8259) {
182 klog() << "Interrupts: NO IOAPIC detected, Reverting to PIC mode.";
183 return;
184 }
185 }
186 for (auto& irq_controller : m_interrupt_controllers) {
187 ASSERT(irq_controller);
188 if (irq_controller->type() == IRQControllerType::i8259) {
189 irq_controller->hard_disable();
190 dbg() << "Interrupts: Detected " << irq_controller->model() << " Disabled";
191 } else {
192 dbg() << "Interrupts: Detected " << irq_controller->model();
193 }
194 }
195 APIC::init();
196 APIC::enable_bsp();
197 MultiProcessorParser::initialize();
198 locate_pci_interrupt_overrides();
199}
200
201void InterruptManagement::locate_apic_data()
202{
203 ASSERT(!m_madt.is_null());
204 auto region = MM.allocate_kernel_region(m_madt.page_base(), (PAGE_SIZE * 2), "Initializing Interrupts", Region::Access::Read);
205 auto& madt = *(const ACPI::Structures::MADT*)region->vaddr().offset(m_madt.offset_in_page()).as_ptr();
206
207 int irq_controller_count = 0;
208 if (madt.flags & PCAT_COMPAT_FLAG) {
209 m_interrupt_controllers[0] = adopt(*new PIC());
210 irq_controller_count++;
211 }
212 size_t entry_index = 0;
213 size_t entries_length = madt.h.length - sizeof(ACPI::Structures::MADT);
214 auto* madt_entry = madt.entries;
215 while (entries_length > 0) {
216 size_t entry_length = madt_entry->length;
217 if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::IOAPIC) {
218 auto* ioapic_entry = (const ACPI::Structures::MADTEntries::IOAPIC*)madt_entry;
219 dbg() << "IOAPIC found @ MADT entry " << entry_index << ", MMIO Registers @ Px" << String::format("%x", ioapic_entry->ioapic_address);
220 m_interrupt_controllers.resize(1 + irq_controller_count);
221 m_interrupt_controllers[irq_controller_count] = adopt(*new IOAPIC(*(ioapic_mmio_regs*)ioapic_entry->ioapic_address, ioapic_entry->gsi_base));
222 irq_controller_count++;
223 }
224 if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::InterruptSourceOverride) {
225 auto* interrupt_override_entry = (const ACPI::Structures::MADTEntries::InterruptSourceOverride*)madt_entry;
226 m_isa_interrupt_overrides.append(adopt(*new ISAInterruptOverrideMetadata(
227 interrupt_override_entry->bus,
228 interrupt_override_entry->source,
229 interrupt_override_entry->global_system_interrupt,
230 interrupt_override_entry->flags)));
231 dbg() << "Interrupts: Overriding INT 0x" << String::format("%x", interrupt_override_entry->source) << " with GSI " << interrupt_override_entry->global_system_interrupt << ", for bus 0x" << String::format("%x", interrupt_override_entry->bus);
232 }
233 madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress((u32)madt_entry).offset(entry_length).get());
234 entries_length -= entry_length;
235 entry_index++;
236 }
237}
238void InterruptManagement::locate_pci_interrupt_overrides()
239{
240 m_pci_interrupt_overrides = MultiProcessorParser::the().get_pci_interrupt_redirections();
241}
242
243ISAInterruptOverrideMetadata::ISAInterruptOverrideMetadata(u8 bus, u8 source, u32 global_system_interrupt, u16 flags)
244 : m_bus(bus)
245 , m_source(source)
246 , m_global_system_interrupt(global_system_interrupt)
247 , m_flags(flags)
248{
249}
250
251u8 ISAInterruptOverrideMetadata::bus() const
252{
253 return m_bus;
254}
255u8 ISAInterruptOverrideMetadata::source() const
256{
257 return m_source;
258}
259u32 ISAInterruptOverrideMetadata::gsi() const
260{
261 return m_global_system_interrupt;
262}
263u16 ISAInterruptOverrideMetadata::flags() const
264{
265 return m_flags;
266}
267}