From b201b2336372aea45c16daf72ee0264ec3d2f26b Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 22 Feb 2020 19:36:40 +0200 Subject: [PATCH] Kernel: Add Interrupt Management and Generic Interrupt Handler The GenericInterruptHandler class will be used to represent an abstract interrupt handler. The InterruptManagement class will represent a centralized component to manage interrupts. --- Kernel/Interrupts/GenericInterruptHandler.cpp | 61 +++++ Kernel/Interrupts/GenericInterruptHandler.h | 74 +++++ Kernel/Interrupts/InterruptManagement.cpp | 252 ++++++++++++++++++ Kernel/Interrupts/InterruptManagement.h | 96 +++++++ 4 files changed, 483 insertions(+) create mode 100644 Kernel/Interrupts/GenericInterruptHandler.cpp create mode 100644 Kernel/Interrupts/GenericInterruptHandler.h create mode 100644 Kernel/Interrupts/InterruptManagement.cpp create mode 100644 Kernel/Interrupts/InterruptManagement.h diff --git a/Kernel/Interrupts/GenericInterruptHandler.cpp b/Kernel/Interrupts/GenericInterruptHandler.cpp new file mode 100644 index 00000000000..00074b36193 --- /dev/null +++ b/Kernel/Interrupts/GenericInterruptHandler.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +namespace Kernel { +GenericInterruptHandler& GenericInterruptHandler::from(u8 interrupt_number) +{ + return get_interrupt_handler(interrupt_number); +} + +GenericInterruptHandler::GenericInterruptHandler(u8 interrupt_number) + : m_interrupt_number(interrupt_number) +{ + register_generic_interrupt_handler(interrupt_number, *this); +} + +GenericInterruptHandler::~GenericInterruptHandler() +{ + unregister_generic_interrupt_handler(m_interrupt_number, *this); +} + +void GenericInterruptHandler::change_interrupt_number(u8 number) +{ + ASSERT_INTERRUPTS_ENABLED(); + unregister_generic_interrupt_handler(interrupt_number(), *this); + m_interrupt_number = number; + register_generic_interrupt_handler(interrupt_number(), *this); +} +void GenericInterruptHandler::increment_invoking_counter() +{ + m_invoking_count++; +} +} diff --git a/Kernel/Interrupts/GenericInterruptHandler.h b/Kernel/Interrupts/GenericInterruptHandler.h new file mode 100644 index 00000000000..3b90830365e --- /dev/null +++ b/Kernel/Interrupts/GenericInterruptHandler.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +enum class HandlerPurpose : u8 { + IRQHandler = 1, + SharedIRQHandler = 2, + UnhandledInterruptHandler = 3, +}; + +class GenericInterruptHandler { +public: + static GenericInterruptHandler& from(u8 interrupt_number); + virtual ~GenericInterruptHandler(); + virtual void handle_interrupt(RegisterState& regs) = 0; + + u8 interrupt_number() const { return m_interrupt_number; } + + bool is_enabled() const { return m_enabled; } + + size_t get_invoking_count() const { return m_invoking_count; } + + virtual size_t get_sharing_devices_count() const = 0; + virtual bool is_shared_handler() const = 0; + virtual bool is_sharing_with_others() const = 0; + + virtual HandlerPurpose purpose() const = 0; + + virtual bool eoi() = 0; + void increment_invoking_counter(); + +protected: + void enable_interrupt_vector(); + void disable_interrupt_vector(); + void change_interrupt_number(u8 number); + explicit GenericInterruptHandler(u8 interrupt_number); + +private: + size_t m_invoking_count { 0 }; + bool m_enabled { false }; + u8 m_interrupt_number { 0 }; +}; +} diff --git a/Kernel/Interrupts/InterruptManagement.cpp b/Kernel/Interrupts/InterruptManagement.cpp new file mode 100644 index 00000000000..f4d297f57ce --- /dev/null +++ b/Kernel/Interrupts/InterruptManagement.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PCAT_COMPAT_FLAG 0x1 + +namespace Kernel { + +static InterruptManagement* s_interrupt_management; + +bool InterruptManagement::initialized() +{ + return (s_interrupt_management != nullptr); +} + +InterruptManagement& InterruptManagement::the() +{ + ASSERT(InterruptManagement::initialized()); + return *s_interrupt_management; +} + +void InterruptManagement::initialize() +{ + ASSERT(!InterruptManagement::initialized()); + s_interrupt_management = new InterruptManagement(true); +} + +InterruptManagement::InterruptManagement(bool create_default_controller) +{ + if (create_default_controller) + m_interrupt_controllers[0] = make(); +} + +void InterruptManagement::enable(u8 interrupt_vector) +{ + for (auto& irq_controller : InterruptManagement::the().m_interrupt_controllers) { + if (irq_controller->get_gsi_base() <= interrupt_vector) + if (!irq_controller->is_hard_disabled()) + irq_controller->enable(interrupt_vector); + } +} + +void InterruptManagement::enumerate_interrupt_handlers(Function callback) +{ + for (int i = 0; i < GENERIC_INTERRUPT_HANDLERS_COUNT; i++) { + auto& handler = get_interrupt_handler(i); + if (handler.get_invoking_count() > 0) + callback(handler); + } +} + +void InterruptManagement::disable(u8 interrupt_vector) +{ + for (auto& irq_controller : InterruptManagement::the().m_interrupt_controllers) { + ASSERT(irq_controller != nullptr); + if (irq_controller->get_gsi_base() <= interrupt_vector) + if (!irq_controller->is_hard_disabled()) + irq_controller->disable(interrupt_vector); + } +} + +void InterruptManagement::eoi(u8 interrupt_vector) +{ + for (auto& irq_controller : InterruptManagement::the().m_interrupt_controllers) { + ASSERT(irq_controller != nullptr); + if (irq_controller->get_gsi_base() <= interrupt_vector) + if (!irq_controller->is_hard_disabled()) + irq_controller->eoi(interrupt_vector); + } +} + +IRQController& InterruptManagement::get_interrupt_controller(int index) +{ + ASSERT(index >= 0); + ASSERT(m_interrupt_controllers[index] != nullptr); + return *m_interrupt_controllers[index]; +} + +void InterruptManagement::switch_to_pic_mode() +{ + kprintf("Interrupts: PIC mode by default\n"); +} + +void InterruptManagement::switch_to_ioapic_mode() +{ + kprintf("Interrupts: Switch to IOAPIC mode failed, Reverting to PIC mode\n"); +} + +void AdvancedInterruptManagement::initialize(ACPI_RAW::MADT& p_madt) +{ + ASSERT(!InterruptManagement::initialized()); + s_interrupt_management = new AdvancedInterruptManagement(p_madt); +} + +AdvancedInterruptManagement::AdvancedInterruptManagement(ACPI_RAW::MADT& p_madt) + : InterruptManagement(false) + , m_madt(p_madt) +{ + // FIXME: Check what is the actual data size then map accordingly + dbg() << "Interrupts: MADT @ P " << &p_madt; + locate_isa_interrupt_overrides(p_madt); + locate_ioapics(p_madt); +} + +void AdvancedInterruptManagement::switch_to_pic_mode() +{ + kprintf("Interrupts: Switch to Legacy PIC mode\n"); + for (auto& irq_controller : m_interrupt_controllers) { + ASSERT(irq_controller); + if (irq_controller->type() == IRQControllerType::i82093AA) { + irq_controller->hard_disable(); + dbg() << "Interrupts: Detected " << irq_controller->model() << " - Disabled"; + } else { + dbg() << "Interrupts: Detected " << irq_controller->model(); + } + } +} + +void AdvancedInterruptManagement::switch_to_ioapic_mode() +{ + kprintf("Interrupts: Switch to IOAPIC mode\n"); + if (m_interrupt_controllers.size() == 1) { + if (get_interrupt_controller(0).type() == IRQControllerType::i8259) { + kprintf("Interrupts: NO IOAPIC detected, Reverting to PIC mode.\n"); + return; + } + } + for (auto& irq_controller : m_interrupt_controllers) { + ASSERT(irq_controller); + if (irq_controller->type() == IRQControllerType::i8259) { + irq_controller->hard_disable(); + dbg() << "Interrupts: Detected " << irq_controller->model() << " Disabled"; + } else { + dbg() << "Interrupts: Detected " << irq_controller->model(); + } + } +} + +void AdvancedInterruptManagement::locate_ioapics(ACPI_RAW::MADT& p_madt) +{ + auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(&p_madt)), (PAGE_SIZE * 2), "Initializing Interrupts", Region::Access::Read); + auto& madt = *(const ACPI_RAW::MADT*)region->vaddr().offset(offset_in_page(&p_madt)).as_ptr(); + + int index = 0; + if (madt.flags & PCAT_COMPAT_FLAG) { + m_interrupt_controllers[0] = make(); + index++; + } + size_t entry_index = 0; + size_t entries_length = madt.h.length - sizeof(ACPI_RAW::MADT); + auto* madt_entry = madt.entries; + while (entries_length > 0) { + size_t entry_length = madt_entry->length; + if (madt_entry->type == (u8)ACPI_RAW::MADTEntryType::IOAPIC) { + auto* ioapic_entry = (const ACPI_RAW::MADT_IOAPIC*)madt_entry; + dbg() << "IOAPIC found @ MADT entry " << entry_index << ", MMIO Registers @ Px" << String::format("%x", ioapic_entry->ioapic_address); + m_interrupt_controllers.resize(1 + index); + m_interrupt_controllers[index] = make(*(ioapic_mmio_regs*)ioapic_entry->ioapic_address, ioapic_entry->gsi_base, m_isa_interrupt_overrides, m_pci_interrupt_overrides); + index++; + } + madt_entry = (ACPI_RAW::MADTEntryHeader*)(VirtualAddress((u32)madt_entry).offset(entry_length).get()); + entries_length -= entry_length; + entry_index++; + } +} +void AdvancedInterruptManagement::locate_pci_interrupt_overrides() +{ + // FIXME: calling the MultiProcessorParser causes a pagefault. + ASSERT_NOT_REACHED(); + m_pci_interrupt_overrides = MultiProcessorParser::the().get_pci_interrupt_redirections(); +} + +void AdvancedInterruptManagement::locate_isa_interrupt_overrides(ACPI_RAW::MADT& p_madt) +{ + auto region = MM.allocate_kernel_region(PhysicalAddress(page_base_of(&p_madt)), (PAGE_SIZE * 2), "Initializing Interrupts", Region::Access::Read); + auto& madt = *(const ACPI_RAW::MADT*)region->vaddr().offset(offset_in_page(&p_madt)).as_ptr(); + + size_t entry_index = 0; + size_t entries_length = madt.h.length - sizeof(ACPI_RAW::MADT); + auto* madt_entry = madt.entries; + while (entries_length > 0) { + size_t entry_length = madt_entry->length; + if (madt_entry->type == (u8)ACPI_RAW::MADTEntryType::InterruptSourceOverride) { + auto* interrupt_override_entry = (const ACPI_RAW::MADT_InterruptSourceOverride*)madt_entry; + m_isa_interrupt_overrides.append(adopt(*new ISAInterruptOverrideMetadata( + interrupt_override_entry->bus, + interrupt_override_entry->source, + interrupt_override_entry->global_system_interrupt, + interrupt_override_entry->flags))); + 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); + } + madt_entry = (ACPI_RAW::MADTEntryHeader*)(VirtualAddress((u32)madt_entry).offset(entry_length).get()); + entries_length -= entry_length; + entry_index++; + } +} + +ISAInterruptOverrideMetadata::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 ISAInterruptOverrideMetadata::bus() const +{ + return m_bus; +} +u8 ISAInterruptOverrideMetadata::source() const +{ + return m_source; +} +u32 ISAInterruptOverrideMetadata::gsi() const +{ + return m_global_system_interrupt; +} +u16 ISAInterruptOverrideMetadata::flags() const +{ + return m_flags; +} +} diff --git a/Kernel/Interrupts/InterruptManagement.h b/Kernel/Interrupts/InterruptManagement.h new file mode 100644 index 00000000000..111b4907810 --- /dev/null +++ b/Kernel/Interrupts/InterruptManagement.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class InterruptManagement { +public: + static InterruptManagement& the(); + static void initialize(); + static bool initialized(); + + virtual void switch_to_pic_mode(); + virtual void switch_to_ioapic_mode(); + + void enable(u8 interrupt_vector); + void disable(u8 interrupt_vector); + void eoi(u8 interrupt_vector); + void enumerate_interrupt_handlers(Function); + IRQController& get_interrupt_controller(int index); + +protected: + explicit InterruptManagement(bool create_default_controller); + FixedArray> m_interrupt_controllers { 1 }; +}; + +class ISAInterruptOverrideMetadata; +class AdvancedInterruptManagement : public InterruptManagement { + friend class IOAPIC; + +public: + static void initialize(ACPI_RAW::MADT& madt); + virtual void switch_to_ioapic_mode() override; + virtual void switch_to_pic_mode() override; + +private: + explicit AdvancedInterruptManagement(ACPI_RAW::MADT& madt); + void locate_ioapics(ACPI_RAW::MADT& madt); + void locate_isa_interrupt_overrides(ACPI_RAW::MADT& madt); + void locate_pci_interrupt_overrides(); + Vector> m_isa_interrupt_overrides; + Vector> m_pci_interrupt_overrides; + ACPI_RAW::MADT& m_madt; +}; + +class ISAInterruptOverrideMetadata : public RefCounted { +public: + ISAInterruptOverrideMetadata(u8 bus, u8 source, u32 global_system_interrupt, u16 flags); + u8 bus() const; + u8 source() const; + u32 gsi() const; + u16 flags() const; + +private: + u8 m_bus; + u8 m_source; + u32 m_global_system_interrupt; + u16 m_flags; +}; +}