mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
Kernel: Support the RISC-V PLIC
By supporting the RISC-V PLIC (Platform-Level Interrupt Controller) we can now handle device (external) interrupts.
This commit is contained in:
parent
0a2d520b15
commit
dfa2c98497
Notes:
sideshowbarker
2024-07-17 05:41:34 +09:00
Author: https://github.com/IdanHo Commit: https://github.com/SerenityOS/serenity/commit/dfa2c98497 Pull-request: https://github.com/SerenityOS/serenity/pull/24117 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/spholz
7 changed files with 195 additions and 8 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
*/
|
||||
|
||||
#include <Kernel/Arch/Interrupts.h>
|
||||
#include <Kernel/Arch/riscv64/CPU.h>
|
||||
#include <Kernel/Arch/riscv64/IRQController.h>
|
||||
#include <Kernel/Arch/riscv64/InterruptManagement.h>
|
||||
#include <Kernel/Arch/riscv64/Interrupts/PLIC.h>
|
||||
#include <Kernel/Interrupts/SharedIRQHandler.h>
|
||||
|
||||
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<u32>();
|
||||
auto soc_size_cells = soc.get_property("#size-cells"sv).value().as<u32>();
|
||||
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<BigEndian<u32>>());
|
||||
else
|
||||
paddr = MUST(stream.read_value<BigEndian<u64>>());
|
||||
size_t size;
|
||||
if (soc_size_cells == 1)
|
||||
size = MUST(stream.read_value<BigEndian<u32>>());
|
||||
else
|
||||
size = MUST(stream.read_value<BigEndian<u64>>());
|
||||
auto max_interrupt_id = node.get_property("riscv,ndev"sv).value().as<u32>();
|
||||
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<NonnullLockRefPtr<IRQController>> const& InterruptManagement::controllers
|
|||
|
||||
NonnullLockRefPtr<IRQController> 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<void(GenericInterruptHandler&)>)
|
||||
|
|
|
@ -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<GenericInterruptHandler*, 64> s_interrupt_handlers;
|
||||
static Array<GenericInterruptHandler*, 256> 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();
|
||||
}
|
||||
|
|
59
Kernel/Arch/riscv64/Interrupts/PLIC.cpp
Normal file
59
Kernel/Arch/riscv64/Interrupts/PLIC.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/riscv64/Interrupts/PLIC.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
UNMAP_AFTER_INIT PLIC::PLIC(PhysicalAddress address, size_t size, u32 interrupt_count)
|
||||
: m_registers(Memory::map_typed<RegisterMap volatile>(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;
|
||||
}
|
||||
|
||||
}
|
55
Kernel/Arch/riscv64/Interrupts/PLIC.h
Normal file
55
Kernel/Arch/riscv64/Interrupts/PLIC.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Kernel/Arch/riscv64/IRQController.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
|
||||
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<RegisterMap, 0x4000000>());
|
||||
|
||||
void initialize();
|
||||
|
||||
Memory::TypedMapping<RegisterMap volatile> m_registers;
|
||||
u32 m_interrupt_count { 0 };
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue