diff --git a/Kernel/Arch/init.cpp b/Kernel/Arch/init.cpp index 797b2b2e822..ad3a429bb69 100644 --- a/Kernel/Arch/init.cpp +++ b/Kernel/Arch/init.cpp @@ -296,9 +296,6 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT NO_SANITIZE_COVERAGE void init([[maybe_ for (ctor_func_t* ctor = start_ctors; ctor < end_ctors; ctor++) (*ctor)(); - InterruptManagement::initialize(); - ACPI::initialize(); - #if ARCH(RISCV64) MUST(unflatten_fdt()); @@ -308,6 +305,9 @@ extern "C" [[noreturn]] UNMAP_AFTER_INIT NO_SANITIZE_COVERAGE void init([[maybe_ init_delay_loop(); #endif + InterruptManagement::initialize(); + ACPI::initialize(); + // Initialize TimeManagement before using randomness! TimeManagement::initialize(0); diff --git a/Kernel/Arch/riscv64/IRQController.h b/Kernel/Arch/riscv64/IRQController.h index 88f3e848bc4..09e3ec7ed2e 100644 --- a/Kernel/Arch/riscv64/IRQController.h +++ b/Kernel/Arch/riscv64/IRQController.h @@ -24,9 +24,9 @@ public: virtual void enable(GenericInterruptHandler const&) = 0; virtual void disable(GenericInterruptHandler const&) = 0; - virtual void eoi(GenericInterruptHandler const&) const = 0; + virtual void eoi(GenericInterruptHandler const&) = 0; - virtual u64 pending_interrupts() const = 0; + virtual u8 pending_interrupt() const = 0; virtual StringView model() const = 0; diff --git a/Kernel/Arch/riscv64/InterruptManagement.cpp b/Kernel/Arch/riscv64/InterruptManagement.cpp index bf66a4eb91b..9766263d34e 100644 --- a/Kernel/Arch/riscv64/InterruptManagement.cpp +++ b/Kernel/Arch/riscv64/InterruptManagement.cpp @@ -5,8 +5,10 @@ */ #include +#include #include #include +#include #include namespace Kernel { @@ -34,7 +36,64 @@ void InterruptManagement::initialize() void InterruptManagement::find_controllers() { - // TODO: Once device tree support is in place, find interrupt controllers using that. + auto const& device_tree = DeviceTree::get(); + auto maybe_soc = device_tree.get_child("soc"sv); + if (!maybe_soc.has_value()) { + dmesgln("Interrupts: No `soc` node found in the device tree, Interrupts initialization will be skipped"); + return; + } + auto const& soc = maybe_soc.value(); + auto soc_address_cells = soc.get_property("#address-cells"sv).value().as(); + auto soc_size_cells = soc.get_property("#size-cells"sv).value().as(); + auto interrupt_controllers_seen = 0; + for (auto const& [node_name, node] : soc.children()) { + if (!node.has_property("interrupt-controller"sv)) + continue; + + interrupt_controllers_seen++; + + auto maybe_compatible = node.get_property("compatible"sv); + if (!maybe_compatible.has_value()) { + dmesgln("Interrupts: Devicetree node for {} does not have a 'compatible' string, rejecting", node_name); + continue; + } + + // Reject non sifive-compatible interrupt controllers + auto sifive_plic = false; + maybe_compatible->for_each_string([&sifive_plic](StringView compatible_string) -> IterationDecision { + if (compatible_string == "sifive,plic-1.0.0"sv) { + sifive_plic = true; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + if (!sifive_plic) + continue; + + auto maybe_reg = node.get_property("reg"sv); + if (!maybe_reg.has_value()) { + dmesgln("Interrupts: Devicetree node for {} does not have a physical address assigned to it, rejecting", node_name); + continue; + } + auto reg = maybe_reg.value(); + auto stream = reg.as_stream(); + FlatPtr paddr; + if (soc_address_cells == 1) + paddr = MUST(stream.read_value>()); + else + paddr = MUST(stream.read_value>()); + size_t size; + if (soc_size_cells == 1) + size = MUST(stream.read_value>()); + else + size = MUST(stream.read_value>()); + auto max_interrupt_id = node.get_property("riscv,ndev"sv).value().as(); + m_interrupt_controllers.append(adopt_lock_ref(*new (nothrow) PLIC(PhysicalAddress(paddr), size, max_interrupt_id + 1))); + } + + if (interrupt_controllers_seen > 0 && m_interrupt_controllers.is_empty()) { + dmesgln("Interrupts: {} interrupt controllers seen, but none are compatible", interrupt_controllers_seen); + } } u8 InterruptManagement::acquire_mapped_interrupt_number(u8 original_irq) @@ -49,7 +108,9 @@ Vector> const& InterruptManagement::controllers NonnullLockRefPtr InterruptManagement::get_responsible_irq_controller(size_t) { - TODO_RISCV64(); + // TODO: Support more interrupt controllers + VERIFY(m_interrupt_controllers.size() == 1); + return m_interrupt_controllers[0]; } void InterruptManagement::enumerate_interrupt_handlers(Function) diff --git a/Kernel/Arch/riscv64/Interrupts.cpp b/Kernel/Arch/riscv64/Interrupts.cpp index 4a26e988e72..e6e2e5e1722 100644 --- a/Kernel/Arch/riscv64/Interrupts.cpp +++ b/Kernel/Arch/riscv64/Interrupts.cpp @@ -23,7 +23,7 @@ namespace Kernel { extern "C" void syscall_handler(TrapFrame const*); // FIXME: Share this array with x86_64/aarch64 somehow and consider if this really needs to use raw pointers and not OwnPtrs -static Array s_interrupt_handlers; +static Array s_interrupt_handlers; void dump_registers(RegisterState const& regs) { @@ -55,6 +55,17 @@ extern "C" void trap_handler(TrapFrame& trap_frame) if (scause == RISCV64::CSR::SCAUSE::SupervisorTimerInterrupt) { RISCV64::Timer::the().handle_interrupt(*trap_frame.regs); + } else if (scause == RISCV64::CSR::SCAUSE::SupervisorExternalInterrupt) { + for (auto& interrupt_controller : InterruptManagement::the().controllers()) { + u8 pending_interrupt = 0; + while ((pending_interrupt = interrupt_controller->pending_interrupt())) { + auto* handler = s_interrupt_handlers[pending_interrupt]; + VERIFY(handler); + handler->increment_call_count(); + handler->handle_interrupt(*trap_frame.regs); + handler->eoi(); + } + } } else { TODO_RISCV64(); } diff --git a/Kernel/Arch/riscv64/Interrupts/PLIC.cpp b/Kernel/Arch/riscv64/Interrupts/PLIC.cpp new file mode 100644 index 00000000000..6ca6abb1e60 --- /dev/null +++ b/Kernel/Arch/riscv64/Interrupts/PLIC.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel { + +UNMAP_AFTER_INIT PLIC::PLIC(PhysicalAddress address, size_t size, u32 interrupt_count) + : m_registers(Memory::map_typed(address, size, Memory::Region::Access::ReadWrite).release_value_but_fixme_should_propagate_errors()) + , m_interrupt_count(interrupt_count) +{ + VERIFY(m_interrupt_count < 256); // TODO: Serenity currently only supports up to 256 unique interrupts, but the PLIC supports up to 1024 + initialize(); +} + +UNMAP_AFTER_INIT void PLIC::initialize() +{ + for (auto i = 1u; i < m_interrupt_count; ++i) { + // Initialize all interrupt priorities to 1 (0 means never-interrupt) + m_registers->interrupt_priority[i] = 1; + } + for (auto i = 0u; i <= ((m_interrupt_count - 1) >> 5); ++i) { + // Initialize all interrupt sources to disabled + m_registers->interrupt_enable_bitmap[interrupt_context][i] = 0; + } + // Initialize priority-threshold to 0 (accept any interrupt of priority 1 or above) + m_registers->contexts[interrupt_context].priority_threshold = 0; + // Enable external interrupts in the current hart + RISCV64::CSR::set_bits(RISCV64::CSR::Address::SIE, 1 << (to_underlying(RISCV64::CSR::SCAUSE::SupervisorExternalInterrupt) & ~RISCV64::CSR::SCAUSE_INTERRUPT_MASK)); +} + +void PLIC::enable(GenericInterruptHandler const& handler) +{ + auto interrupt_number = handler.interrupt_number(); + VERIFY(interrupt_number > 0); // Interrupt number 0 is reserved to mean no-interrupt + m_registers->interrupt_enable_bitmap[interrupt_context][interrupt_number >> 5] |= 1u << (interrupt_number & 0x1F); +} + +void PLIC::disable(GenericInterruptHandler const& handler) +{ + auto interrupt_number = handler.interrupt_number(); + VERIFY(interrupt_number > 0); // Interrupt number 0 is reserved to mean no-interrupt + m_registers->interrupt_enable_bitmap[interrupt_context][interrupt_number >> 5] &= ~(1u << (interrupt_number & 0x1F)); +} + +void PLIC::eoi(GenericInterruptHandler const& handler) +{ + m_registers->contexts[interrupt_context].claim_complete = handler.interrupt_number(); +} + +u8 PLIC::pending_interrupt() const +{ + return m_registers->contexts[interrupt_context].claim_complete; +} + +} diff --git a/Kernel/Arch/riscv64/Interrupts/PLIC.h b/Kernel/Arch/riscv64/Interrupts/PLIC.h new file mode 100644 index 00000000000..11f254a140f --- /dev/null +++ b/Kernel/Arch/riscv64/Interrupts/PLIC.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, Idan Horowitz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel { + +class PLIC final : public IRQController { +public: + PLIC(PhysicalAddress, size_t size, u32 interrupt_count); + + virtual void enable(GenericInterruptHandler const&) override; + virtual void disable(GenericInterruptHandler const&) override; + + virtual void eoi(GenericInterruptHandler const&) override; + + virtual u8 pending_interrupt() const override; + + virtual StringView model() const override { return "PLIC"sv; } + +private: + enum { + M_MODE_CONTEXT = 0, + S_MODE_CONTEXT, + CONTEXTS_PER_HART, + }; + static constexpr size_t interrupt_context = (0 * CONTEXTS_PER_HART) + S_MODE_CONTEXT; // We assume we only have hart 0, change this once we support SMP + + struct RegisterMap { + u32 interrupt_priority[1024]; + u32 interrupt_pending_bitmap[32]; + u32 reserved1[992]; + u32 interrupt_enable_bitmap[15872][32]; + u32 reserved2[14336]; + struct { + u32 priority_threshold; + u32 claim_complete; + u32 reserved3[1022]; + } contexts[15872]; + }; + static_assert(AssertSize()); + + void initialize(); + + Memory::TypedMapping m_registers; + u32 m_interrupt_count { 0 }; +}; + +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index b0dd38541ca..e7240a3fb41 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -539,6 +539,7 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64") Arch/riscv64/Delay.cpp Arch/riscv64/InterruptManagement.cpp Arch/riscv64/Interrupts.cpp + Arch/riscv64/Interrupts/PLIC.cpp Arch/riscv64/MMU.cpp Arch/riscv64/PageDirectory.cpp Arch/riscv64/Panic.cpp