mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-24 10:12:25 -05:00
7c0540a229
This has KString, KBuffer, DoubleBuffer, KBufferBuilder, IOWindow, UserOrKernelBuffer and ScopedCritical classes being moved to the Kernel/Library subdirectory. Also, move the panic and assertions handling code to that directory.
233 lines
9.1 KiB
C++
233 lines
9.1 KiB
C++
/*
|
|
* Copyright (c) 2022, Timon Kruiper <timonkruiper@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <Kernel/Arch/CPU.h>
|
|
#include <Kernel/Arch/Interrupts.h>
|
|
#include <Kernel/Arch/PageFault.h>
|
|
#include <Kernel/Arch/TrapFrame.h>
|
|
#include <Kernel/Arch/aarch64/InterruptManagement.h>
|
|
#include <Kernel/Interrupts/GenericInterruptHandler.h>
|
|
#include <Kernel/Interrupts/SharedIRQHandler.h>
|
|
#include <Kernel/Interrupts/UnhandledInterruptHandler.h>
|
|
#include <Kernel/KSyms.h>
|
|
#include <Kernel/Library/Panic.h>
|
|
#include <Kernel/Library/StdLib.h>
|
|
|
|
namespace Kernel {
|
|
|
|
extern "C" void syscall_handler(TrapFrame const*);
|
|
|
|
static void dump_exception_syndrome_register(Aarch64::ESR_EL1 const& esr_el1)
|
|
{
|
|
dbgln("Exception Syndrome: EC({:#b}) IL({:#b}) ISS({:#b}) ISS2({:#b})", esr_el1.EC, esr_el1.IL, esr_el1.ISS, esr_el1.ISS2);
|
|
dbgln(" Class: {}", Aarch64::exception_class_to_string(esr_el1.EC));
|
|
|
|
if (Aarch64::exception_class_is_data_abort(esr_el1.EC))
|
|
dbgln(" Data Fault Status Code: {}", Aarch64::data_fault_status_code_to_string(esr_el1.ISS));
|
|
if (Aarch64::exception_class_has_set_far(esr_el1.EC))
|
|
dbgln(" Faulting Virtual Address: 0x{:x}", Aarch64::FAR_EL1::read().virtual_address);
|
|
}
|
|
|
|
void dump_registers(RegisterState const& regs)
|
|
{
|
|
auto esr_el1 = Kernel::Aarch64::ESR_EL1::read();
|
|
dump_exception_syndrome_register(esr_el1);
|
|
|
|
// Special registers
|
|
Aarch64::SPSR_EL1 spsr_el1 = {};
|
|
memcpy(&spsr_el1, (u8 const*)®s.spsr_el1, sizeof(u64));
|
|
|
|
dbgln("Saved Program Status: (NZCV({:#b}) DAIF({:#b}) M({:#b})) / 0x{:x}", ((regs.spsr_el1 >> 28) & 0b1111), ((regs.spsr_el1 >> 6) & 0b1111), regs.spsr_el1 & 0b1111, regs.spsr_el1);
|
|
dbgln("Exception Link Register: 0x{:x}", regs.elr_el1);
|
|
dbgln("Stack Pointer (EL0): 0x{:x}", regs.sp_el0);
|
|
|
|
dbgln(" x0={:p} x1={:p} x2={:p} x3={:p} x4={:p}", regs.x[0], regs.x[1], regs.x[2], regs.x[3], regs.x[4]);
|
|
dbgln(" x5={:p} x6={:p} x7={:p} x8={:p} x9={:p}", regs.x[5], regs.x[6], regs.x[7], regs.x[8], regs.x[9]);
|
|
dbgln("x10={:p} x11={:p} x12={:p} x13={:p} x14={:p}", regs.x[10], regs.x[11], regs.x[12], regs.x[13], regs.x[14]);
|
|
dbgln("x15={:p} x16={:p} x17={:p} x18={:p} x19={:p}", regs.x[15], regs.x[16], regs.x[17], regs.x[18], regs.x[19]);
|
|
dbgln("x20={:p} x21={:p} x22={:p} x23={:p} x24={:p}", regs.x[20], regs.x[21], regs.x[22], regs.x[23], regs.x[24]);
|
|
dbgln("x25={:p} x26={:p} x27={:p} x28={:p} x29={:p}", regs.x[25], regs.x[26], regs.x[27], regs.x[28], regs.x[29]);
|
|
dbgln("x30={:p}", regs.x[30]);
|
|
}
|
|
|
|
static ErrorOr<PageFault> page_fault_from_exception_syndrome_register(VirtualAddress fault_address, Aarch64::ESR_EL1 esr_el1)
|
|
{
|
|
PageFault fault { fault_address };
|
|
|
|
u8 data_fault_status_code = esr_el1.ISS & 0x3f;
|
|
if (data_fault_status_code >= 0b001100 && data_fault_status_code <= 0b001111) {
|
|
fault.set_type(PageFault::Type::ProtectionViolation);
|
|
} else if (data_fault_status_code >= 0b000100 && data_fault_status_code <= 0b000111) {
|
|
fault.set_type(PageFault::Type::PageNotPresent);
|
|
} else {
|
|
dbgln("Unknown DFSC: {}", Aarch64::data_fault_status_code_to_string(esr_el1.ISS));
|
|
return Error::from_errno(EFAULT);
|
|
}
|
|
|
|
fault.set_access((esr_el1.ISS & (1 << 6)) == (1 << 6) ? PageFault::Access::Write : PageFault::Access::Read);
|
|
|
|
fault.set_mode(Aarch64::exception_class_is_data_or_instruction_abort_from_lower_exception_level(esr_el1.EC) ? ExecutionMode::User : ExecutionMode::Kernel);
|
|
|
|
if (Aarch64::exception_class_is_instruction_abort(esr_el1.EC))
|
|
fault.set_instruction_fetch(true);
|
|
|
|
return fault;
|
|
}
|
|
|
|
extern "C" void exception_common(Kernel::TrapFrame* trap_frame);
|
|
extern "C" void exception_common(Kernel::TrapFrame* trap_frame)
|
|
{
|
|
Processor::current().enter_trap(*trap_frame, false);
|
|
|
|
auto esr_el1 = Kernel::Aarch64::ESR_EL1::read();
|
|
auto fault_address = Aarch64::FAR_EL1::read().virtual_address;
|
|
Processor::enable_interrupts();
|
|
|
|
if (Aarch64::exception_class_is_data_abort(esr_el1.EC) || Aarch64::exception_class_is_instruction_abort(esr_el1.EC)) {
|
|
auto page_fault_or_error = page_fault_from_exception_syndrome_register(VirtualAddress(fault_address), esr_el1);
|
|
if (page_fault_or_error.is_error()) {
|
|
dump_registers(*trap_frame->regs);
|
|
handle_crash(*trap_frame->regs, "Unknown page fault", SIGSEGV, false);
|
|
} else {
|
|
auto page_fault = page_fault_or_error.release_value();
|
|
page_fault.handle(*trap_frame->regs);
|
|
}
|
|
} else if (Aarch64::exception_class_is_svc_instruction_execution(esr_el1.EC)) {
|
|
syscall_handler(trap_frame);
|
|
} else {
|
|
dump_registers(*trap_frame->regs);
|
|
handle_crash(*trap_frame->regs, "Unexpected exception", SIGSEGV, false);
|
|
}
|
|
|
|
Processor::disable_interrupts();
|
|
Processor::current().exit_trap(*trap_frame);
|
|
}
|
|
|
|
static Array<GenericInterruptHandler*, 64> s_interrupt_handlers;
|
|
|
|
extern "C" void handle_interrupt(TrapFrame&);
|
|
extern "C" void handle_interrupt(TrapFrame& trap_frame)
|
|
{
|
|
Processor::current().enter_trap(trap_frame, true);
|
|
|
|
for (auto& interrupt_controller : InterruptManagement::the().controllers()) {
|
|
auto pending_interrupts = interrupt_controller->pending_interrupts();
|
|
|
|
// TODO: Add these interrupts as a source of entropy for randomness.
|
|
u8 irq = 0;
|
|
while (pending_interrupts) {
|
|
if ((pending_interrupts & 0b1) != 0b1) {
|
|
irq += 1;
|
|
pending_interrupts >>= 1;
|
|
continue;
|
|
}
|
|
|
|
auto* handler = s_interrupt_handlers[irq];
|
|
VERIFY(handler);
|
|
handler->increment_call_count();
|
|
handler->handle_interrupt(*trap_frame.regs);
|
|
handler->eoi();
|
|
|
|
irq += 1;
|
|
pending_interrupts >>= 1;
|
|
}
|
|
}
|
|
|
|
Processor::current().exit_trap(trap_frame);
|
|
}
|
|
|
|
// FIXME: Share the code below with Arch/x86_64/Interrupts.cpp
|
|
// While refactoring, the interrupt handlers can also be moved into the InterruptManagement class.
|
|
GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number)
|
|
{
|
|
auto*& handler_slot = s_interrupt_handlers[interrupt_number];
|
|
VERIFY(handler_slot != nullptr);
|
|
return *handler_slot;
|
|
}
|
|
|
|
static void revert_to_unused_handler(u8 interrupt_number)
|
|
{
|
|
auto handler = new UnhandledInterruptHandler(interrupt_number);
|
|
handler->register_interrupt_handler();
|
|
}
|
|
|
|
void register_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler)
|
|
{
|
|
auto*& handler_slot = s_interrupt_handlers[interrupt_number];
|
|
if (handler_slot == nullptr) {
|
|
handler_slot = &handler;
|
|
return;
|
|
}
|
|
if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) {
|
|
auto* unhandled_handler = static_cast<UnhandledInterruptHandler*>(handler_slot);
|
|
unhandled_handler->unregister_interrupt_handler();
|
|
delete unhandled_handler;
|
|
handler_slot = &handler;
|
|
return;
|
|
}
|
|
if (handler_slot->is_shared_handler()) {
|
|
VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler);
|
|
static_cast<SharedIRQHandler*>(handler_slot)->register_handler(handler);
|
|
return;
|
|
}
|
|
if (!handler_slot->is_shared_handler()) {
|
|
if (handler_slot->type() == HandlerType::SpuriousInterruptHandler) {
|
|
// FIXME: Add support for spurious interrupts on aarch64
|
|
TODO_AARCH64();
|
|
}
|
|
VERIFY(handler_slot->type() == HandlerType::IRQHandler);
|
|
auto& previous_handler = *handler_slot;
|
|
handler_slot = nullptr;
|
|
SharedIRQHandler::initialize(interrupt_number);
|
|
VERIFY(handler_slot);
|
|
static_cast<SharedIRQHandler*>(handler_slot)->register_handler(previous_handler);
|
|
static_cast<SharedIRQHandler*>(handler_slot)->register_handler(handler);
|
|
return;
|
|
}
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
void unregister_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler)
|
|
{
|
|
auto*& handler_slot = s_interrupt_handlers[interrupt_number];
|
|
VERIFY(handler_slot != nullptr);
|
|
if (handler_slot->type() == HandlerType::UnhandledInterruptHandler)
|
|
return;
|
|
if (handler_slot->is_shared_handler()) {
|
|
VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler);
|
|
auto* shared_handler = static_cast<SharedIRQHandler*>(handler_slot);
|
|
shared_handler->unregister_handler(handler);
|
|
if (!shared_handler->sharing_devices_count()) {
|
|
handler_slot = nullptr;
|
|
revert_to_unused_handler(interrupt_number);
|
|
}
|
|
return;
|
|
}
|
|
if (!handler_slot->is_shared_handler()) {
|
|
VERIFY(handler_slot->type() == HandlerType::IRQHandler);
|
|
handler_slot = nullptr;
|
|
revert_to_unused_handler(interrupt_number);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void initialize_interrupts()
|
|
{
|
|
for (u8 i = 0; i < s_interrupt_handlers.size(); ++i) {
|
|
auto* handler = new UnhandledInterruptHandler(i);
|
|
handler->register_interrupt_handler();
|
|
}
|
|
}
|
|
|
|
// Sets the reserved flag on `number_of_irqs` if it finds unused interrupt handler on
|
|
// a contiguous range.
|
|
ErrorOr<u8> reserve_interrupt_handlers([[maybe_unused]] u8 number_of_irqs)
|
|
{
|
|
TODO();
|
|
return Error::from_errno(EINVAL);
|
|
}
|
|
|
|
}
|