Repository: SerenityOS/serenity
File: Kernel/Arch/aarch64/Interrupts.cpp (C++)

Code before:
extern "C" void exception_common(Kernel::TrapFrame* trap_frame)
{
    Processor::current().enter_trap(*trap_frame, false);

    auto esr_el1 = bit_cast<Aarch64::ESR_EL1>(trap_frame->regs->esr_el1);
    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()) {
            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 if (Aarch64::exception_class_is_breakpoint_instruction(esr_el1.EC) || Aarch64::exception_class_is_software_step(esr_el1.EC)) {
        if (trap_frame->regs->previous_mode() == ExecutionMode::User) {
            // When breakpoint instruction exception is triggered, the ip is set to the address of the breakpoint instruction,
            // so we need to increase the ip past the breakpoint instruction to stay consistent with the user‑mode debugger’s behavior.
            if (Aarch64::exception_class_is_breakpoint_instruction(esr_el1.EC)) {
                trap_frame->regs->set_ip(trap_frame->regs->ip() + 4);
            }

            auto* current_thread = Thread::current();
            auto& current_process = current_thread->process();

            if (auto* tracer = current_process.tracer()) {
                tracer->set_regs(*trap_frame->regs);
            }

            current_thread->send_urgent_signal_to_self(SIGTRAP);
        } else {
            if (Aarch64::exception_class_is_breakpoint_instruction(esr_el1.EC))
                handle_crash(*trap_frame->regs, "Unexpected breakpoint instruction exception", SIGTRAP, false);
            if (Aarch64::exception_class_is_software_step(esr_el1.EC))
                handle_crash(*trap_frame->regs, "Unexpected software step exception", SIGTRAP, false);
        }
    } else {
        handle_crash(*trap_frame->regs, "Unexpected exception", SIGSEGV, false);
    }

    Processor::disable_interrupts();
    Processor::current().exit_trap(*trap_frame);
}

// This spinlock is used to reserve IRQs that can be later used by interrupt mechanism such as MSIx
static Spinlock<LockRank::None> s_interrupt_handler_lock {};
// A GICv2 supports a maximum of 1020 interrupts.

Review comment (bug):
I'm not sure if we handle software step exceptions correctly in this commit.
Any reason why you don't add this check in "Kernel/aarch64+LibDebug: Implement full software step support"?

Code after:
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 = bit_cast<Aarch64::ESR_EL1>(trap_frame->regs->esr_el1);
    auto fault_address = Aarch64::FAR_EL1::read().virtual_address;
    if (Aarch64::exception_class_is_breakpoint_instruction(esr_el1.EC)) {
        // When breakpoint instruction exception is triggered, the pc is set to the address of the
        // breakpoint instruction, so we need to increase the pc past the breakpoint instruction to
        // stay consistent with the user‑mode debugger’s behavior.
        trap_frame->regs->set_ip(trap_frame->regs->ip() + 4);
    }
    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()) {
            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 if (Aarch64::exception_class_is_breakpoint_instruction(esr_el1.EC) || Aarch64::exception_class_is_software_step(esr_el1.EC)) {
        if (trap_frame->regs->previous_mode() == ExecutionMode::User) {
            auto* current_thread = Thread::current();
            auto& current_process = current_thread->process();

            if (Aarch64::exception_class_is_software_step(esr_el1.EC)) {
                // Clear the software step bit in the MDSCR_EL1 register to disable software step.
                read_debug_registers_into(current_thread->debug_register_state());
                current_thread->debug_register_state().mdscr_el1 &= ~Aarch64::MDSCR_EL1_SS_FLAG;
                write_debug_registers_from(current_thread->debug_register_state());
            }

            if (auto* tracer = current_process.tracer()) {
                tracer->set_regs(*trap_frame->regs);
            }

            current_thread->send_urgent_signal_to_self(SIGTRAP);
        } else {
            if (Aarch64::exception_class_is_breakpoint_instruction(esr_el1.EC))
                handle_crash(*trap_frame->regs, "Unexpected breakpoint instruction exception", SIGTRAP, false);
            if (Aarch64::exception_class_is_software_step(esr_el1.EC))
                handle_crash(*trap_frame->regs, "Unexpected software step exception", SIGTRAP, false);
        }
    } else {
        handle_crash(*trap_frame->regs, "Unexpected exception", SIGSEGV, false);
    }