diff options
author | Timon Kruiper <timonkruiper@gmail.com> | 2022-05-30 09:04:37 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-06-02 13:14:12 +0100 |
commit | f085903f62e07f80ba345d26a63e34fbd9f82235 (patch) | |
tree | 85f617657af94fe44b7cffac82cb6519913e0b1e /Kernel/Arch | |
parent | 63ee2781fb89f97e370ce56f5a6634c60c463dea (diff) | |
download | serenity-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.h | 17 | ||||
-rw-r--r-- | Kernel/Arch/InterruptManagement.h | 17 | ||||
-rw-r--r-- | Kernel/Arch/aarch64/Dummy.cpp | 2 | ||||
-rw-r--r-- | Kernel/Arch/aarch64/IRQController.h | 31 | ||||
-rw-r--r-- | Kernel/Arch/aarch64/InterruptManagement.h | 22 | ||||
-rw-r--r-- | Kernel/Arch/x86/IRQController.h | 45 | ||||
-rw-r--r-- | Kernel/Arch/x86/InterruptManagement.h | 81 | ||||
-rw-r--r-- | Kernel/Arch/x86/common/InterruptManagement.cpp | 245 |
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++; + } +} + +} |