Skip to content

Commit

Permalink
Kernel: Simplify interrupt management
Browse files Browse the repository at this point in the history
The IRQController object is RefCounted, and is shared between the
InterruptManagement class & IRQ handlers' classes.

IRQHandler, SharedIRQHandler & SpuriousInterruptHandler classes
use a responsible IRQ controller directly instead of calling
InterruptManagement for disable(), enable() or eoi().

Also, the initialization process of InterruptManagement is
simplified, so it doesn't rely on an ACPI parser to be initialized.
  • Loading branch information
supercomputer7 authored and awesomekling committed Feb 28, 2020
1 parent f96cf25 commit 6f914ed
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 126 deletions.
3 changes: 2 additions & 1 deletion Kernel/Interrupts/IRQController.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#pragma once

#include <AK/RefCounted.h>
#include <AK/String.h>
#include <AK/Types.h>

Expand All @@ -36,7 +37,7 @@ enum class IRQControllerType {
i82093AA = 2 /* Intel 82093AA I/O ADVANCED PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC) */
};

class IRQController {
class IRQController : public RefCounted<IRQController> {
public:
virtual ~IRQController() {}

Expand Down
9 changes: 6 additions & 3 deletions Kernel/Interrupts/IRQHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace Kernel {

IRQHandler::IRQHandler(u8 irq)
: GenericInterruptHandler(irq)
, m_responsible_irq_controller(InterruptManagement::the().get_responsible_irq_controller(irq))
{
}

Expand All @@ -47,7 +48,8 @@ bool IRQHandler::eoi()
dbg() << "EOI IRQ " << interrupt_number();
#endif
if (!m_shared_with_others) {
InterruptManagement::the().eoi(interrupt_number());
ASSERT(!m_responsible_irq_controller.is_null());
m_responsible_irq_controller->eoi(interrupt_number());
return true;
}
return false;
Expand All @@ -59,7 +61,7 @@ void IRQHandler::enable_irq()
dbg() << "Enable IRQ " << interrupt_number();
#endif
if (!m_shared_with_others)
InterruptManagement::the().enable(interrupt_number());
m_responsible_irq_controller->enable(interrupt_number());
else
m_enabled = true;
}
Expand All @@ -70,7 +72,7 @@ void IRQHandler::disable_irq()
dbg() << "Disable IRQ " << interrupt_number();
#endif
if (!m_shared_with_others)
InterruptManagement::the().disable(interrupt_number());
m_responsible_irq_controller->disable(interrupt_number());
else
m_enabled = false;
}
Expand All @@ -79,6 +81,7 @@ void IRQHandler::change_irq_number(u8 irq)
{
InterruptDisabler disabler;
change_interrupt_number(irq);
m_responsible_irq_controller = InterruptManagement::the().get_responsible_irq_controller(irq);
}

}
5 changes: 4 additions & 1 deletion Kernel/Interrupts/IRQHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@

#pragma once

#include <AK/RefPtr.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <Kernel/Arch/i386/CPU.h>
#include <Kernel/Interrupts/GenericInterruptHandler.h>
#include <Kernel/Interrupts/IRQController.h>

namespace Kernel {

Expand All @@ -53,11 +55,12 @@ class IRQHandler : public GenericInterruptHandler {

protected:
void change_irq_number(u8 irq);
explicit IRQHandler(u8 irq);
IRQHandler(u8 irq);

private:
bool m_shared_with_others { false };
bool m_enabled { false };
RefPtr<IRQController> m_responsible_irq_controller;
};

}
143 changes: 48 additions & 95 deletions Kernel/Interrupts/InterruptManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,7 @@ InterruptManagement& InterruptManagement::the()
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<PIC>();
}

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);
}
s_interrupt_management = new InterruptManagement();
}

void InterruptManagement::enumerate_interrupt_handlers(Function<void(GenericInterruptHandler&)> callback)
Expand All @@ -80,66 +65,49 @@ void InterruptManagement::enumerate_interrupt_handlers(Function<void(GenericInte
}
}

void InterruptManagement::disable(u8 interrupt_vector)
IRQController& InterruptManagement::get_interrupt_controller(int index)
{
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);
}
ASSERT(index >= 0);
ASSERT(!m_interrupt_controllers[index].is_null());
return *m_interrupt_controllers[index];
}

void InterruptManagement::eoi(u8 interrupt_vector)
RefPtr<IRQController> InterruptManagement::get_responsible_irq_controller(u8 interrupt_vector)
{
if (m_interrupt_controllers.size() == 1 && m_interrupt_controllers[0]->type() == IRQControllerType::i8259) {
m_interrupt_controllers[0]->eoi(interrupt_vector);
return;
return m_interrupt_controllers[0];
}
for (auto& irq_controller : InterruptManagement::the().m_interrupt_controllers) {
ASSERT(irq_controller != nullptr);
for (auto irq_controller : m_interrupt_controllers) {
if (irq_controller->get_gsi_base() <= interrupt_vector)
if (!irq_controller->is_hard_disabled())
irq_controller->eoi(interrupt_vector);
return irq_controller;
}
ASSERT_NOT_REACHED();
}

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");
SpuriousInterruptHandler::initialize(7);
SpuriousInterruptHandler::initialize(15);
}

void InterruptManagement::switch_to_ioapic_mode()
PhysicalAddress InterruptManagement::search_for_madt()
{
kprintf("Interrupts: Switch to IOAPIC mode failed, Reverting to PIC mode\n");
dbg() << "Early access to ACPI tables for interrupt setup";
auto rsdp = ACPI::StaticParsing::search_rsdp();
if (rsdp.is_null())
return {};
return ACPI::StaticParsing::search_table(rsdp, "APIC");
}

void AdvancedInterruptManagement::initialize(PhysicalAddress p_madt)
InterruptManagement::InterruptManagement()
: m_madt(search_for_madt())
{
ASSERT(!InterruptManagement::initialized());
s_interrupt_management = new AdvancedInterruptManagement(p_madt);
}
if (m_madt.is_null()) {
m_interrupt_controllers[0] = adopt(*new PIC());
return;
}

AdvancedInterruptManagement::AdvancedInterruptManagement(PhysicalAddress 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.as_ptr();
locate_isa_interrupt_overrides(p_madt);
locate_ioapics(p_madt);
dbg() << "Interrupts: MADT @ P " << m_madt.as_ptr();
locate_apic_data();
}

void AdvancedInterruptManagement::switch_to_pic_mode()
void InterruptManagement::switch_to_pic_mode()
{
kprintf("Interrupts: Switch to Legacy PIC mode\n");
SpuriousInterruptHandler::initialize(7);
Expand All @@ -155,7 +123,7 @@ void AdvancedInterruptManagement::switch_to_pic_mode()
}
}

void AdvancedInterruptManagement::switch_to_ioapic_mode()
void InterruptManagement::switch_to_ioapic_mode()
{
kprintf("Interrupts: Switch to IOAPIC mode\n");
if (m_interrupt_controllers.size() == 1) {
Expand All @@ -175,64 +143,49 @@ void AdvancedInterruptManagement::switch_to_ioapic_mode()
}
}

void AdvancedInterruptManagement::locate_ioapics(PhysicalAddress p_madt)
void InterruptManagement::locate_apic_data()
{
auto region = MM.allocate_kernel_region(p_madt.page_base(), (PAGE_SIZE * 2), "Initializing Interrupts", Region::Access::Read);
auto& madt = *(const ACPI_RAW::MADT*)region->vaddr().offset(p_madt.offset_in_page().get()).as_ptr();
ASSERT(!m_madt.is_null());
auto region = MM.allocate_kernel_region(m_madt.page_base(), (PAGE_SIZE * 2), "Initializing Interrupts", Region::Access::Read);
auto& madt = *(const ACPI::Structures::MADT*)region->vaddr().offset(m_madt.offset_in_page().get()).as_ptr();

int index = 0;
int irq_controller_count = 0;
if (madt.flags & PCAT_COMPAT_FLAG) {
m_interrupt_controllers[0] = make<PIC>();
index++;
m_interrupt_controllers[0] = adopt(*new PIC());
irq_controller_count++;
}
size_t entry_index = 0;
size_t entries_length = madt.h.length - sizeof(ACPI_RAW::MADT);
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_RAW::MADTEntryType::IOAPIC) {
auto* ioapic_entry = (const ACPI_RAW::MADT_IOAPIC*)madt_entry;
if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::IOAPIC) {
auto* ioapic_entry = (const ACPI::Structures::MADTEntries::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>(*(ioapic_mmio_regs*)ioapic_entry->ioapic_address, ioapic_entry->gsi_base, m_isa_interrupt_overrides, m_pci_interrupt_overrides);
index++;
m_interrupt_controllers.resize(1 + irq_controller_count);
m_interrupt_controllers[irq_controller_count] = adopt(*new IOAPIC(*(ioapic_mmio_regs*)ioapic_entry->ioapic_address, ioapic_entry->gsi_base, m_isa_interrupt_overrides, m_pci_interrupt_overrides));
irq_controller_count++;
}
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(PhysicalAddress p_madt)
{
auto region = MM.allocate_kernel_region(p_madt.page_base(), (PAGE_SIZE * 2), "Initializing Interrupts", Region::Access::Read);
auto& madt = *(const ACPI_RAW::MADT*)region->vaddr().offset(p_madt.offset_in_page().get()).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;
if (madt_entry->type == (u8)ACPI::Structures::MADTEntryType::InterruptSourceOverride) {
auto* interrupt_override_entry = (const ACPI::Structures::MADTEntries::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());
madt_entry = (ACPI::Structures::MADTEntryHeader*)(VirtualAddress((u32)madt_entry).offset(entry_length).get());
entries_length -= entry_length;
entry_index++;
}
}
void InterruptManagement::locate_pci_interrupt_overrides()
{
// FIXME: calling the MultiProcessorParser causes a pagefault.
ASSERT_NOT_REACHED();
m_pci_interrupt_overrides = MultiProcessorParser::the().get_pci_interrupt_redirections();
}

ISAInterruptOverrideMetadata::ISAInterruptOverrideMetadata(u8 bus, u8 source, u32 global_system_interrupt, u16 flags)
: m_bus(bus)
Expand Down
27 changes: 7 additions & 20 deletions Kernel/Interrupts/InterruptManagement.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@

namespace Kernel {

class ISAInterruptOverrideMetadata;

class InterruptManagement {
public:
static InterruptManagement& the();
Expand All @@ -48,32 +50,17 @@ class InterruptManagement {

virtual void switch_to_pic_mode();
virtual void switch_to_ioapic_mode();
RefPtr<IRQController> get_responsible_irq_controller(u8 interrupt_vector);

void enable(u8 interrupt_vector);
void disable(u8 interrupt_vector);
void eoi(u8 interrupt_vector);
void enumerate_interrupt_handlers(Function<void(GenericInterruptHandler&)>);
IRQController& get_interrupt_controller(int index);

protected:
explicit InterruptManagement(bool create_default_controller);
FixedArray<OwnPtr<IRQController>> m_interrupt_controllers { 1 };
};

class ISAInterruptOverrideMetadata;
class AdvancedInterruptManagement : public InterruptManagement {
friend class IOAPIC;

public:
static void initialize(PhysicalAddress madt);
virtual void switch_to_ioapic_mode() override;
virtual void switch_to_pic_mode() override;

private:
explicit AdvancedInterruptManagement(PhysicalAddress madt);
void locate_ioapics(PhysicalAddress madt);
void locate_isa_interrupt_overrides(PhysicalAddress madt);
InterruptManagement();
PhysicalAddress search_for_madt();
void locate_apic_data();
void locate_pci_interrupt_overrides();
FixedArray<RefPtr<IRQController>> m_interrupt_controllers { 1 };
Vector<RefPtr<ISAInterruptOverrideMetadata>> m_isa_interrupt_overrides;
Vector<RefPtr<PCIInterruptOverrideMetadata>> m_pci_interrupt_overrides;
PhysicalAddress m_madt;
Expand Down
7 changes: 4 additions & 3 deletions Kernel/Interrupts/SharedIRQHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ bool SharedIRQHandler::eoi()
#ifdef INTERRUPT_DEBUG
dbg() << "EOI IRQ " << interrupt_number();
#endif
InterruptManagement::the().eoi(interrupt_number());
m_responsible_irq_controller->eoi(interrupt_number());
return true;
}

SharedIRQHandler::SharedIRQHandler(u8 irq)
: GenericInterruptHandler(irq)
, m_responsible_irq_controller(InterruptManagement::the().get_responsible_irq_controller(irq))
{
#ifdef INTERRUPT_DEBUG
kprintf("Shared Interrupt Handler registered @ %d\n", m_interrupt_number);
Expand Down Expand Up @@ -114,15 +115,15 @@ void SharedIRQHandler::enable_interrupt_vector()
if (m_enabled)
return;
m_enabled = true;
InterruptManagement::the().enable(interrupt_number());
m_responsible_irq_controller->enable(interrupt_number());
}

void SharedIRQHandler::disable_interrupt_vector()
{
if (!m_enabled)
return;
m_enabled = false;
InterruptManagement::the().disable(interrupt_number());
m_responsible_irq_controller->disable(interrupt_number());
}

}
2 changes: 2 additions & 0 deletions Kernel/Interrupts/SharedIRQHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <AK/HashTable.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Types.h>
#include <Kernel/Arch/i386/CPU.h>
#include <Kernel/Interrupts/GenericInterruptHandler.h>
Expand Down Expand Up @@ -57,5 +58,6 @@ class SharedIRQHandler final : public GenericInterruptHandler {
explicit SharedIRQHandler(u8 interrupt_number);
bool m_enabled;
HashTable<GenericInterruptHandler*> m_handlers;
RefPtr<IRQController> m_responsible_irq_controller;
};
}
Loading

0 comments on commit 6f914ed

Please sign in to comment.