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

Code before:
    }

    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 = 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.
                clear_debug_registers();
            }

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


Review comment (suggestion):
Please do this in the first commit where you added support for the breakpoint instruction exception.
We prefer to have all commits working properly.

Code after:
        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 = 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()) {