From e760ebcacbf8d6a5afcf31955dc299bef871acce Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 22 Feb 2020 19:29:18 +0200 Subject: [PATCH] Kernel: Add the IOAPIC class This class inherits from IRQController class, and represents the 82093AA IOAPIC chip. --- Kernel/Interrupts/IOAPIC.cpp | 310 +++++++++++++++++++++++++++++++++++ Kernel/Interrupts/IOAPIC.h | 87 ++++++++++ 2 files changed, 397 insertions(+) create mode 100644 Kernel/Interrupts/IOAPIC.cpp create mode 100644 Kernel/Interrupts/IOAPIC.h diff --git a/Kernel/Interrupts/IOAPIC.cpp b/Kernel/Interrupts/IOAPIC.cpp new file mode 100644 index 0000000000..d3d373bcee --- /dev/null +++ b/Kernel/Interrupts/IOAPIC.cpp @@ -0,0 +1,310 @@ +/* + * 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 + +#define IOAPIC_REDIRECTION_ENTRY_OFFSET 0x10 +namespace Kernel { +enum DeliveryMode { + Normal = 0, + LowPriority = 1, + SMI = 2, + NMI = 3, + INIT = 4, + External = 7 +}; + +IOAPIC::IOAPIC(ioapic_mmio_regs& regs, u32 gsi_base, Vector>& isa_overrides, Vector>& pci_overrides) + : m_physical_access_registers(regs) + , m_gsi_base(gsi_base) + , m_id((read_register(0x0) >> 24) & 0xFF) + , m_version(read_register(0x1) & 0xFF) + , m_redirection_entries((read_register(0x1) >> 16) + 1) + , m_isa_interrupt_overrides(isa_overrides) + , m_pci_interrupt_overrides(pci_overrides) +{ + kprintf("IOAPIC ID: 0x%x\n", m_id); + kprintf("IOAPIC Version: 0x%x, Redirection Entries count - %u\n", m_version, m_redirection_entries); + kprintf("IOAPIC Arbitration ID 0x%x\n", read_register(0x2)); + mask_all_redirection_entries(); +} + +void IOAPIC::initialize() +{ +} + +void IOAPIC::map_interrupt_redirection(u8 interrupt_vector) +{ + if (interrupt_vector == 11) { + configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::LowPriority, false, true, true, true, 0); + return; + } + for (auto redirection_override : m_isa_interrupt_overrides) { + ASSERT(!redirection_override.is_null()); + if (redirection_override->source() != interrupt_vector) + continue; + bool active_low; + // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. + switch ((redirection_override->flags() & 0b11)) { + case 0: + active_low = false; + break; + case 1: + active_low = false; + break; + case 2: + ASSERT_NOT_REACHED(); // Reserved value + case 3: + active_low = true; + break; + } + + bool trigger_level_mode; + // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. + switch (((redirection_override->flags() >> 2) & 0b11)) { + case 0: + trigger_level_mode = false; + break; + case 1: + trigger_level_mode = false; + break; + case 2: + ASSERT_NOT_REACHED(); // Reserved value + case 3: + trigger_level_mode = true; + break; + } + configure_redirection_entry(redirection_override->gsi(), redirection_override->source() + IRQ_VECTOR_BASE, DeliveryMode::LowPriority, false, active_low, trigger_level_mode, true, 0); + return; + } + isa_identity_map(interrupt_vector); +} + +void IOAPIC::isa_identity_map(int index) +{ + configure_redirection_entry(index, index + IRQ_VECTOR_BASE, DeliveryMode::Normal, true, false, false, true, 1); +} + + +void IOAPIC::map_pci_interrupts() +{ + configure_redirection_entry(11, 11 + IRQ_VECTOR_BASE, DeliveryMode::Normal, false, false, true, true, 0); +} + +void IOAPIC::map_isa_interrupts() +{ + InterruptDisabler disabler; + for (auto redirection_override : m_isa_interrupt_overrides) { + ASSERT(!redirection_override.is_null()); + bool active_low; + // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. + switch ((redirection_override->flags() & 0b11)) { + case 0: + active_low = false; + break; + case 1: + active_low = false; + break; + case 2: + ASSERT_NOT_REACHED(); + case 3: + active_low = true; + break; + } + + bool trigger_level_mode; + // See ACPI spec Version 6.2, page 205 to learn more about Interrupt Overriding Flags. + switch (((redirection_override->flags() >> 2) & 0b11)) { + case 0: + trigger_level_mode = false; + break; + case 1: + trigger_level_mode = false; + break; + case 2: + ASSERT_NOT_REACHED(); + case 3: + trigger_level_mode = true; + break; + } + configure_redirection_entry(redirection_override->gsi(), redirection_override->source() + IRQ_VECTOR_BASE, 0, false, active_low, trigger_level_mode, true, 0); + } +} + +void IOAPIC::reset_all_redirection_entries() const +{ + InterruptDisabler disabler; + for (size_t index = 0; index < m_redirection_entries; index++) + reset_redirection_entry(index); +} + +void IOAPIC::hard_disable() +{ + InterruptDisabler disabler; + reset_all_redirection_entries(); + IRQController::hard_disable(); +} + +void IOAPIC::reset_redirection_entry(int index) const +{ + InterruptDisabler disabler; + configure_redirection_entry(index, 0, 0, false, false, false, true, 0); +} + +void 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 +{ + ASSERT((u32)index < m_redirection_entries); + u32 redirection_entry1 = interrupt_vector | (delivery_mode & 0b111) << 8 | logical_destination << 11 | active_low << 13 | trigger_level_mode << 15 | masked << 16; + u32 redirection_entry2 = destination << 24; + write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry1); +#ifdef IOAPIC_DEBUG + dbgprintf("IOAPIC Value: 0x%x\n", read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET)); +#endif + write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET + 1, redirection_entry2); +#ifdef IOAPIC_DEBUG + dbgprintf("IOAPIC Value: 0x%x\n", read_register((index << 1) + 0x11)); +#endif +} + +void IOAPIC::mask_all_redirection_entries() const +{ + InterruptDisabler disabler; + for (size_t index = 0; index < m_redirection_entries; index++) + mask_redirection_entry(index); +} + +void IOAPIC::mask_redirection_entry(u8 index) const +{ + ASSERT((u32)index < m_redirection_entries); + u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) | (1 << 16); + write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry); +} + +bool IOAPIC::is_redirection_entry_masked(u8 index) const +{ + ASSERT((u32)index < m_redirection_entries); + return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & (1 << 16)) != 0; +} + +void IOAPIC::unmask_redirection_entry(u8 index) const +{ + ASSERT((u32)index < m_redirection_entries); + u32 redirection_entry = read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET); + write_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET, redirection_entry & ~(1 << 16)); +} + +bool IOAPIC::is_vector_enabled(u8 interrupt_vector) const +{ + InterruptDisabler disabler; + return is_redirection_entry_masked(interrupt_vector); +} + +u8 IOAPIC::read_redirection_entry_vector(u8 index) const +{ + ASSERT((u32)index < m_redirection_entries); + return (read_register((index << 1) + IOAPIC_REDIRECTION_ENTRY_OFFSET) & 0xFF); +} + +int IOAPIC::find_redirection_entry_by_vector(u8 vector) const +{ + InterruptDisabler disabler; + for (size_t index = 0; index < m_redirection_entries; index++) { + if (read_redirection_entry_vector(index) == (vector + IRQ_VECTOR_BASE)) + return index; + } + return -1; +} + +void IOAPIC::disable(u8 interrupt_vector) +{ + InterruptDisabler disabler; + int index = find_redirection_entry_by_vector(interrupt_vector); + if (index == (-1)) { + map_interrupt_redirection(interrupt_vector); + index = find_redirection_entry_by_vector(interrupt_vector); + } + ASSERT(index != (-1)); + mask_redirection_entry(index); +} + +void IOAPIC::enable(u8 interrupt_vector) +{ + InterruptDisabler disabler; + int index = find_redirection_entry_by_vector(interrupt_vector); + if (index == (-1)) { + map_interrupt_redirection(interrupt_vector); + index = find_redirection_entry_by_vector(interrupt_vector); + } + ASSERT(index != (-1)); + unmask_redirection_entry(index); +} + +void IOAPIC::eoi(u8) const +{ + InterruptDisabler disabler; + APIC::eoi(); +} + +u16 IOAPIC::get_isr() const +{ + InterruptDisabler disabler; + ASSERT_NOT_REACHED(); +} + +u16 IOAPIC::get_irr() const +{ + InterruptDisabler disabler; + ASSERT_NOT_REACHED(); +} + +void IOAPIC::write_register(u32 index, u32 value) const +{ + InterruptDisabler disabler; + 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); + auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr(); + regs.select = index; + regs.window = value; +#ifdef IOAPIC_DEBUG + dbgprintf("IOAPIC Writing, Value 0x%x @ offset 0x%x\n", regs.window, regs.select); +#endif +} +u32 IOAPIC::read_register(u32 index) const +{ + InterruptDisabler disabler; + 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); + auto& regs = *(volatile ioapic_mmio_regs*)region->vaddr().offset(offset_in_page(&m_physical_access_registers)).as_ptr(); + regs.select = index; +#ifdef IOAPIC_DEBUG + dbgprintf("IOAPIC Reading, Value 0x%x @ offset 0x%x\n", regs.window, regs.select); +#endif + return regs.window; +} +} diff --git a/Kernel/Interrupts/IOAPIC.h b/Kernel/Interrupts/IOAPIC.h new file mode 100644 index 0000000000..4192421dfa --- /dev/null +++ b/Kernel/Interrupts/IOAPIC.h @@ -0,0 +1,87 @@ +/* + * 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 + +namespace Kernel { +struct [[gnu::packed]] ioapic_mmio_regs +{ + volatile u32 select; + u32 reserved[3]; + volatile u32 window; +}; + +class ISAInterruptOverrideMetadata; +class PCIInterruptOverrideMetadata; + +class IOAPIC final : public IRQController { +public: + IOAPIC(ioapic_mmio_regs& regs, u32 gsi_base, Vector>& overrides, Vector>& pci_overrides); + virtual void enable(u8 number) override; + virtual void disable(u8 number) override; + virtual void hard_disable() override; + virtual void eoi(u8 number) const override; + virtual bool is_vector_enabled(u8 number) const override; + virtual u16 get_isr() const override; + virtual u16 get_irr() const override; + virtual u32 get_gsi_base() const override { return m_gsi_base; } + virtual const char* model() const override { return "IOAPIC"; }; + virtual IRQControllerType type() const override { return IRQControllerType::i82093AA; } + +private: + void 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; + void reset_redirection_entry(int index) const; + void map_interrupt_redirection(u8 interrupt_vector); + void reset_all_redirection_entries() const; + + void mask_all_redirection_entries() const; + void mask_redirection_entry(u8 index) const; + void unmask_redirection_entry(u8 index) const; + bool is_redirection_entry_masked(u8 index) const; + + u8 read_redirection_entry_vector(u8 index) const; + int find_redirection_entry_by_vector(u8 vector) const; + void configure_redirections() const; + + void write_register(u32 index, u32 value) const; + u32 read_register(u32 index) const; + + virtual void initialize() override; + void map_isa_interrupts(); + void map_pci_interrupts(); + void isa_identity_map(int index); + + ioapic_mmio_regs& m_physical_access_registers; + u32 m_gsi_base; + u8 m_id; + u8 m_version; + u32 m_redirection_entries; + Vector> m_isa_interrupt_overrides; + Vector> m_pci_interrupt_overrides; +}; +}