summaryrefslogtreecommitdiff
path: root/Kernel/Arch
diff options
context:
space:
mode:
authorTimon Kruiper <timonkruiper@gmail.com>2022-05-30 09:04:37 +0200
committerLinus Groh <mail@linusgroh.de>2022-06-02 13:14:12 +0100
commitf085903f62e07f80ba345d26a63e34fbd9f82235 (patch)
tree85f617657af94fe44b7cffac82cb6519913e0b1e /Kernel/Arch
parent63ee2781fb89f97e370ce56f5a6634c60c463dea (diff)
downloadserenity-f085903f62e07f80ba345d26a63e34fbd9f82235.zip
Kernel: Move IRQController and InterruptManagement to Arch directory
These 2 classes currently contain much code that is x86(_64) specific. Move them to the architecture specific directory. This also allows for a simpler implementation for aarch64.
Diffstat (limited to 'Kernel/Arch')
-rw-r--r--Kernel/Arch/IRQController.h17
-rw-r--r--Kernel/Arch/InterruptManagement.h17
-rw-r--r--Kernel/Arch/aarch64/Dummy.cpp2
-rw-r--r--Kernel/Arch/aarch64/IRQController.h31
-rw-r--r--Kernel/Arch/aarch64/InterruptManagement.h22
-rw-r--r--Kernel/Arch/x86/IRQController.h45
-rw-r--r--Kernel/Arch/x86/InterruptManagement.h81
-rw-r--r--Kernel/Arch/x86/common/InterruptManagement.cpp245
8 files changed, 459 insertions, 1 deletions
diff --git a/Kernel/Arch/IRQController.h b/Kernel/Arch/IRQController.h
new file mode 100644
index 0000000000..b48d352863
--- /dev/null
+++ b/Kernel/Arch/IRQController.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2022, Timon Kruiper <timonkruiper@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Platform.h>
+
+#if ARCH(X86_64) || ARCH(I386)
+# include <Kernel/Arch/x86/IRQController.h>
+#elif ARCH(AARCH64)
+# include <Kernel/Arch/aarch64/IRQController.h>
+#else
+# error "Unknown architecture"
+#endif
diff --git a/Kernel/Arch/InterruptManagement.h b/Kernel/Arch/InterruptManagement.h
new file mode 100644
index 0000000000..b78fd5dc87
--- /dev/null
+++ b/Kernel/Arch/InterruptManagement.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2022, Timon Kruiper <timonkruiper@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Platform.h>
+
+#if ARCH(X86_64) || ARCH(I386)
+# include <Kernel/Arch/x86/InterruptManagement.h>
+#elif ARCH(AARCH64)
+# include <Kernel/Arch/aarch64/InterruptManagement.h>
+#else
+# error "Unknown architecture"
+#endif
diff --git a/Kernel/Arch/aarch64/Dummy.cpp b/Kernel/Arch/aarch64/Dummy.cpp
index fb09e631f2..07dda5445a 100644
--- a/Kernel/Arch/aarch64/Dummy.cpp
+++ b/Kernel/Arch/aarch64/Dummy.cpp
@@ -7,8 +7,8 @@
#include <AK/Singleton.h>
#include <AK/Types.h>
+#include <Kernel/Arch/aarch64/InterruptManagement.h>
#include <Kernel/FileSystem/Inode.h>
-#include <Kernel/Interrupts/InterruptManagement.h>
#include <Kernel/KString.h>
#include <Kernel/Locking/SpinlockProtected.h>
#include <Kernel/Memory/SharedInodeVMObject.h>
diff --git a/Kernel/Arch/aarch64/IRQController.h b/Kernel/Arch/aarch64/IRQController.h
new file mode 100644
index 0000000000..35882a6ffc
--- /dev/null
+++ b/Kernel/Arch/aarch64/IRQController.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/Types.h>
+
+namespace Kernel {
+
+class GenericInterruptHandler;
+
+class IRQController : public RefCounted<IRQController> {
+public:
+ virtual ~IRQController() = default;
+
+ virtual void enable(GenericInterruptHandler const&) = 0;
+ virtual void disable(GenericInterruptHandler const&) = 0;
+
+ virtual void eoi(GenericInterruptHandler const&) const = 0;
+
+ virtual StringView model() const = 0;
+
+protected:
+ IRQController() = default;
+};
+
+}
diff --git a/Kernel/Arch/aarch64/InterruptManagement.h b/Kernel/Arch/aarch64/InterruptManagement.h
new file mode 100644
index 0000000000..6c80429e67
--- /dev/null
+++ b/Kernel/Arch/aarch64/InterruptManagement.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <Kernel/Arch/aarch64/IRQController.h>
+
+namespace Kernel {
+
+class InterruptManagement {
+public:
+ static InterruptManagement& the();
+
+ static u8 acquire_mapped_interrupt_number(u8 original_irq);
+
+ RefPtr<IRQController> get_responsible_irq_controller(u8 interrupt_vector);
+};
+
+}
diff --git a/Kernel/Arch/x86/IRQController.h b/Kernel/Arch/x86/IRQController.h
new file mode 100644
index 0000000000..43d12f561c
--- /dev/null
+++ b/Kernel/Arch/x86/IRQController.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/RefCounted.h>
+#include <AK/Types.h>
+
+namespace Kernel {
+
+class GenericInterruptHandler;
+
+enum class IRQControllerType {
+ i8259 = 1, /* Intel 8259 Dual PIC */
+ i82093AA = 2 /* Intel 82093AA I/O ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC) */
+};
+
+class IRQController : public RefCounted<IRQController> {
+public:
+ virtual ~IRQController() = default;
+
+ virtual void enable(GenericInterruptHandler const&) = 0;
+ virtual void disable(GenericInterruptHandler const&) = 0;
+ virtual void hard_disable() { m_hard_disabled = true; }
+ virtual bool is_vector_enabled(u8 number) const = 0;
+ virtual bool is_enabled() const = 0;
+ bool is_hard_disabled() const { return m_hard_disabled; }
+ virtual void eoi(GenericInterruptHandler const&) const = 0;
+ virtual void spurious_eoi(GenericInterruptHandler const&) const = 0;
+ virtual size_t interrupt_vectors_count() const = 0;
+ virtual u32 gsi_base() const = 0;
+ virtual u16 get_isr() const = 0;
+ virtual u16 get_irr() const = 0;
+ virtual StringView model() const = 0;
+ virtual IRQControllerType type() const = 0;
+
+protected:
+ IRQController() = default;
+ virtual void initialize() = 0;
+ bool m_hard_disabled { false };
+};
+}
diff --git a/Kernel/Arch/x86/InterruptManagement.h b/Kernel/Arch/x86/InterruptManagement.h
new file mode 100644
index 0000000000..b6fa990812
--- /dev/null
+++ b/Kernel/Arch/x86/InterruptManagement.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/NonnullOwnPtr.h>
+#include <AK/OwnPtr.h>
+#include <AK/RefCounted.h>
+#include <AK/RefPtr.h>
+#include <AK/Types.h>
+#include <Kernel/Arch/x86/IRQController.h>
+#include <Kernel/Firmware/ACPI/Definitions.h>
+#include <Kernel/Interrupts/GenericInterruptHandler.h>
+#include <Kernel/Interrupts/IOAPIC.h>
+
+namespace Kernel {
+
+class ISAInterruptOverrideMetadata {
+public:
+ ISAInterruptOverrideMetadata(u8 bus, u8 source, u32 global_system_interrupt, u16 flags)
+ : m_bus(bus)
+ , m_source(source)
+ , m_global_system_interrupt(global_system_interrupt)
+ , m_flags(flags)
+ {
+ }
+
+ u8 bus() const { return m_bus; }
+ u8 source() const { return m_source; }
+ u32 gsi() const { return m_global_system_interrupt; }
+ u16 flags() const { return m_flags; }
+
+private:
+ const u8 m_bus;
+ const u8 m_source;
+ const u32 m_global_system_interrupt;
+ const u16 m_flags;
+};
+
+class InterruptManagement {
+public:
+ static InterruptManagement& the();
+ static void initialize();
+ static bool initialized();
+ static u8 acquire_mapped_interrupt_number(u8 original_irq);
+ static u8 acquire_irq_number(u8 mapped_interrupt_vector);
+
+ virtual void switch_to_pic_mode();
+ virtual void switch_to_ioapic_mode();
+
+ bool smp_enabled() const { return m_smp_enabled; }
+ RefPtr<IRQController> get_responsible_irq_controller(u8 interrupt_vector);
+ RefPtr<IRQController> get_responsible_irq_controller(IRQControllerType controller_type, u8 interrupt_vector);
+
+ Vector<ISAInterruptOverrideMetadata> const& isa_overrides() const { return m_isa_interrupt_overrides; }
+
+ u8 get_mapped_interrupt_vector(u8 original_irq);
+ u8 get_irq_vector(u8 mapped_interrupt_vector);
+
+ void enumerate_interrupt_handlers(Function<void(GenericInterruptHandler&)>);
+ IRQController& get_interrupt_controller(int index);
+
+protected:
+ virtual ~InterruptManagement() = default;
+
+private:
+ InterruptManagement();
+ PhysicalAddress search_for_madt();
+ void locate_apic_data();
+ bool m_smp_enabled { false };
+ Vector<RefPtr<IRQController>> m_interrupt_controllers;
+ Vector<ISAInterruptOverrideMetadata> m_isa_interrupt_overrides;
+ Vector<PCIInterruptOverrideMetadata> m_pci_interrupt_overrides;
+ PhysicalAddress m_madt;
+};
+
+}
diff --git a/Kernel/Arch/x86/common/InterruptManagement.cpp b/Kernel/Arch/x86/common/InterruptManagement.cpp
new file mode 100644
index 0000000000..08174be0ec
--- /dev/null
+++ b/Kernel/Arch/x86/common/InterruptManagement.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/ByteReader.h>
+#include <Kernel/API/Syscall.h>
+#include <Kernel/Arch/InterruptDisabler.h>
+#include <Kernel/Arch/Interrupts.h>
+#include <Kernel/Arch/x86/InterruptManagement.h>
+#include <Kernel/CommandLine.h>
+#include <Kernel/Firmware/MultiProcessor/Parser.h>
+#include <Kernel/Interrupts/APIC.h>
+#include <Kernel/Interrupts/IOAPIC.h>
+#include <Kernel/Interrupts/PIC.h>
+#include <Kernel/Interrupts/SharedIRQHandler.h>
+#include <Kernel/Interrupts/SpuriousInterruptHandler.h>
+#include <Kernel/Memory/TypedMapping.h>
+#include <Kernel/Sections.h>
+
+#define PCAT_COMPAT_FLAG 0x1
+
+namespace Kernel {
+
+static InterruptManagement* s_interrupt_management;
+
+bool InterruptManagement::initialized()
+{
+ return (s_interrupt_management != nullptr);
+}
+
+InterruptManagement& InterruptManagement::the()
+{
+ VERIFY(InterruptManagement::initialized());
+ return *s_interrupt_management;
+}
+
+UNMAP_AFTER_INIT void InterruptManagement::initialize()
+{
+ VERIFY(!InterruptManagement::initialized());
+ s_interrupt_management = new InterruptManagement();
+ if (!kernel_command_line().is_smp_enabled_without_ioapic_enabled()) {
+ dbgln("Can't enable SMP mode without IOAPIC mode being enabled");
+ }
+ if (!kernel_command_line().is_ioapic_enabled() && !kernel_command_line().is_smp_enabled())
+ InterruptManagement::the().switch_to_pic_mode();
+ else
+ InterruptManagement::the().switch_to_ioapic_mode();
+}
+
+void InterruptManagement::enumerate_interrupt_handlers(Function<void(GenericInterruptHandler&)> callback)
+{
+ for (int i = 0; i < GENERIC_INTERRUPT_HANDLERS_COUNT; i++) {
+ auto& handler = get_interrupt_handler(i);
+ if (handler.type() == HandlerType::SharedIRQHandler) {
+ static_cast<SharedIRQHandler&>(handler).enumerate_handlers(callback);
+ continue;
+ }
+ if (handler.type() != HandlerType::UnhandledInterruptHandler)
+ callback(handler);
+ }
+}
+
+IRQController& InterruptManagement::get_interrupt_controller(int index)
+{
+ VERIFY(index >= 0);
+ VERIFY(!m_interrupt_controllers[index].is_null());
+ return *m_interrupt_controllers[index];
+}
+
+u8 InterruptManagement::acquire_mapped_interrupt_number(u8 original_irq)
+{
+ if (!InterruptManagement::initialized()) {
+ // This is necessary, because we install UnhandledInterruptHandlers before we actually initialize the Interrupt Management object...
+ return original_irq;
+ }
+ return InterruptManagement::the().get_mapped_interrupt_vector(original_irq);
+}
+
+u8 InterruptManagement::acquire_irq_number(u8 mapped_interrupt_vector)
+{
+ VERIFY(InterruptManagement::initialized());
+ return InterruptManagement::the().get_irq_vector(mapped_interrupt_vector);
+}
+
+u8 InterruptManagement::get_mapped_interrupt_vector(u8 original_irq)
+{
+ // FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient.
+ // FIXME: Find a better way to handle conflict with Syscall interrupt gate.
+ VERIFY((original_irq + IRQ_VECTOR_BASE) != syscall_vector);
+ return original_irq;
+}
+
+u8 InterruptManagement::get_irq_vector(u8 mapped_interrupt_vector)
+{
+ // FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient.
+ return mapped_interrupt_vector;
+}
+
+RefPtr<IRQController> InterruptManagement::get_responsible_irq_controller(IRQControllerType controller_type, u8 interrupt_vector)
+{
+ for (auto& irq_controller : m_interrupt_controllers) {
+ if (irq_controller->gsi_base() <= interrupt_vector && irq_controller->type() == controller_type)
+ return irq_controller;
+ }
+ VERIFY_NOT_REACHED();
+}
+
+RefPtr<IRQController> InterruptManagement::get_responsible_irq_controller(u8 interrupt_vector)
+{
+ if (m_interrupt_controllers.size() == 1 && m_interrupt_controllers[0]->type() == IRQControllerType::i8259) {
+ return m_interrupt_controllers[0];
+ }
+ for (auto& irq_controller : m_interrupt_controllers) {
+ if (irq_controller->gsi_base() <= interrupt_vector)
+ if (!irq_controller->is_hard_disabled())
+ return irq_controller;
+ }
+ VERIFY_NOT_REACHED();
+}
+
+UNMAP_AFTER_INIT PhysicalAddress InterruptManagement::search_for_madt()
+{
+ dbgln("Early access to ACPI tables for interrupt setup");
+ auto rsdp = ACPI::StaticParsing::find_rsdp();
+ if (!rsdp.has_value())
+ return {};
+ auto apic = ACPI::StaticParsing::find_table(rsdp.value(), "APIC");
+ if (!apic.has_value())
+ return {};
+ return apic.value();
+}
+
+UNMAP_AFTER_INIT InterruptManagement::InterruptManagement()
+ : m_madt(search_for_madt())
+{
+ m_interrupt_controllers.resize(1);
+}
+
+UNMAP_AFTER_INIT void InterruptManagement::switch_to_pic_mode()
+{
+ dmesgln("Interrupts: Switch to Legacy PIC mode");
+ InterruptDisabler disabler;
+ m_smp_enabled = false;
+ m_interrupt_controllers[0] = adopt_ref(*new PIC());
+ SpuriousInterruptHandler::initialize(7);
+ SpuriousInterruptHandler::initialize(15);
+ for (auto& irq_controller : m_interrupt_controllers) {
+ VERIFY(irq_controller);
+ if (irq_controller->type() == IRQControllerType::i82093AA) {
+ irq_controller->hard_disable();
+ dbgln("Interrupts: Detected {} - Disabled", irq_controller->model());
+ } else {
+ dbgln("Interrupts: Detected {}", irq_controller->model());
+ }
+ }
+}
+
+UNMAP_AFTER_INIT void InterruptManagement::switch_to_ioapic_mode()
+{
+ dmesgln("Interrupts: Switch to IOAPIC mode");
+ InterruptDisabler disabler;
+
+ if (m_madt.is_null()) {
+ dbgln("Interrupts: ACPI MADT is not available, reverting to PIC mode");
+ switch_to_pic_mode();
+ return;
+ }
+
+ dbgln("Interrupts: MADT @ P {}", m_madt.as_ptr());
+ locate_apic_data();
+ m_smp_enabled = true;
+ if (m_interrupt_controllers.size() == 1) {
+ if (get_interrupt_controller(0).type() == IRQControllerType::i8259) {
+ dmesgln("Interrupts: NO IOAPIC detected, Reverting to PIC mode.");
+ return;
+ }
+ }
+ for (auto& irq_controller : m_interrupt_controllers) {
+ VERIFY(irq_controller);
+ if (irq_controller->type() == IRQControllerType::i8259) {
+ irq_controller->hard_disable();
+ dbgln("Interrupts: Detected {} - Disabled", irq_controller->model());
+ SpuriousInterruptHandler::initialize_for_disabled_master_pic();
+ SpuriousInterruptHandler::initialize_for_disabled_slave_pic();
+ } else {
+ dbgln("Interrupts: Detected {}", irq_controller->model());
+ }
+ }
+
+ if (auto mp_parser = MultiProcessorParser::autodetect()) {
+ m_pci_interrupt_overrides = mp_parser->get_pci_interrupt_redirections();
+ }
+
+ APIC::initialize();
+ APIC::the().init_bsp();
+}
+
+UNMAP_AFTER_INIT void InterruptManagement::locate_apic_data()
+{
+ VERIFY(!m_madt.is_null());
+ auto madt = Memory::map_typed<ACPI::Structures::MADT>(m_madt).release_value_but_fixme_should_propagate_errors();
+
+ int irq_controller_count = 0;
+ if (madt->flags & PCAT_COMPAT_FLAG) {
+ m_interrupt_controllers[0] = adopt_ref(*new PIC());
+ irq_controller_count++;
+ }
+ size_t entry_index = 0;
+ size_t entries_length = madt->h.length - sizeof(ACPI::Structures::MADT);
+ auto* madt_entry = madt->entries;
+ while (entries_length > 0) {
+ size_t entry_length = madt_entry->length;
+ if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::IOAPIC) {
+ auto* ioapic_entry = (const ACPI::Structures::MADTEntries::IOAPIC*)madt_entry;
+ dbgln("IOAPIC found @ MADT entry {}, MMIO Registers @ {}", entry_index, PhysicalAddress(ioapic_entry->ioapic_address));
+ m_interrupt_controllers.resize(1 + irq_controller_count);
+ m_interrupt_controllers[irq_controller_count] = adopt_ref(*new IOAPIC(PhysicalAddress(ioapic_entry->ioapic_address), ioapic_entry->gsi_base));
+ irq_controller_count++;
+ }
+ if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::InterruptSourceOverride) {
+ auto* interrupt_override_entry = (const ACPI::Structures::MADTEntries::InterruptSourceOverride*)madt_entry;
+ u32 global_system_interrupt = 0;
+ ByteReader::load<u32>(reinterpret_cast<u8 const*>(&interrupt_override_entry->global_system_interrupt), global_system_interrupt);
+ u16 flags = 0;
+ ByteReader::load<u16>(reinterpret_cast<u8 const*>(&interrupt_override_entry->flags), flags);
+ MUST(m_isa_interrupt_overrides.try_empend(
+ interrupt_override_entry->bus,
+ interrupt_override_entry->source,
+ global_system_interrupt,
+ flags));
+
+ dbgln("Interrupts: Overriding INT {:#x} with GSI {}, for bus {:#x}",
+ interrupt_override_entry->source,
+ global_system_interrupt,
+ interrupt_override_entry->bus);
+ }
+ madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress(madt_entry).offset(entry_length).get());
+ entries_length -= entry_length;
+ entry_index++;
+ }
+}
+
+}