- Arm

el0t_64_sync_handler  // arch/arm64/kernel/entry-common.c
   el0_da        // arch/arm64/kernel/entry-common.c
      do_mem_abort    // arch/arm64/mm/fault.c
         do_translation_fault // arch/arm64/mm/fault.c
            do_page_fault  // arch/arm64/mm/fault.c

asmlinkage void noinstr el0t_64_sync_handler(struct pt_regs *regs)
{
unsigned long esr = read_sysreg(esr_el1);

switch (ESR_ELx_EC(esr)) {
case ESR_ELx_EC_SVC64:
el0_svc(regs);
break;
case ESR_ELx_EC_DABT_LOW:
el0_da(regs, esr); >>>
break;

static void noinstr el0_da(struct pt_regs *regs, unsigned long esr)
{
unsigned long far = read_sysreg(far_el1);

enter_from_user_mode(regs);
local_daif_restore(DAIF_PROCCTX);
do_mem_abort(far, esr, regs); >>>
exit_to_user_mode(regs);
}

void do_mem_abort(unsigned long far, unsigned long esr, struct pt_regs *regs)
{
        const struct fault_info *inf = esr_to_fault_info(esr);
        unsigned long addr = untagged_addr(far);

        if (!inf->fn(far, esr, regs))  >>>
                return; 

        if (!user_mode(regs))
                die_kernel_fault(inf->name, addr, esr, regs);

(where)
do_translation_fault function is registered in fault_info variable.

static const struct fault_info fault_info[] = {
        { do_bad,               SIGKILL, SI_KERNEL,     "ttbr address size fault"       },
        { do_bad,               SIGKILL, SI_KERNEL,     "level 1 address size fault"    },
        { do_bad,               SIGKILL, SI_KERNEL,     "level 2 address size fault"    },
        { do_bad,               SIGKILL, SI_KERNEL,     "level 3 address size fault"    },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 0 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 0 access flag fault"     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },


static int __kprobes do_translation_fault(unsigned long far, 
                                          unsigned long esr,
                                          struct pt_regs *regs)
{
        unsigned long addr = untagged_addr(far);

        if (is_ttbr0_addr(addr))
                return do_page_fault(far, esr, regs); >>>

        do_bad_area(far, esr, regs);
        return 0;
}

- RISC-V

handle_exception
do_page_fault
handle_page_fault

arch/riscv/kernel/entry.S 
SYM_DATA_START_LOCAL(excp_vect_table)
RISCV_PTR do_trap_insn_misaligned
ALT_INSN_FAULT(RISCV_PTR do_trap_insn_fault)
RISCV_PTR do_trap_insn_illegal
RISCV_PTR do_trap_break
RISCV_PTR do_trap_load_misaligned
RISCV_PTR do_trap_load_fault
RISCV_PTR do_trap_store_misaligned
RISCV_PTR do_trap_store_fault
RISCV_PTR do_trap_ecall_u /* system call */
RISCV_PTR do_trap_ecall_s
RISCV_PTR do_trap_unknown
RISCV_PTR do_trap_ecall_m
/* instruction page fault */
ALT_PAGE_FAULT(RISCV_PTR do_page_fault)
RISCV_PTR do_page_fault   /* load page fault */  >>>
RISCV_PTR do_trap_unknown
RISCV_PTR do_page_fault   /* store page fault */  >>>
SYM_DATA_END_LABEL(excp_vect_table, SYM_L_LOCAL, excp_vect_table_end)

asmlinkage __visible noinstr void do_page_fault(struct pt_regs *regs)
{
irqentry_state_t state = irqentry_enter(regs);

handle_page_fault(regs);

local_irq_disable();

irqentry_exit(regs, state);
}

static __always_inline bool is_ttbr0_addr(unsigned long addr)
{
/* entry assembly clears tags for TTBR0 addrs */
return addr < TASK_SIZE;
}

+ Recent posts