/* SPDX-License-Identifier: GPL-2.0 * * Copyright (C) 1999, 2000, 2002 Niibe Yutaka * Copyright (C) 2003 - 2008 Paul Mundt */ ! NOTE: ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address ! to be jumped is too far, but it causes illegal slot exception. /* * entry.S contains the system-call and fault low-level handling routines. * This also contains the timer-interrupt handler, as well as all interrupts * and faults that can result in a task-switch. * * NOTE: This code handles signal-recognition, which happens every time * after a timer-interrupt and after each system call. * * NOTE: This code uses a convention that instructions in the delay slot * of a transfer-control instruction are indented by an extra space, thus: * * jmp @k0 ! control-transfer instruction * ldc k1, ssr ! delay slot * * Stack layout in 'ret_from_syscall': * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in ptrace.c and ptrace.h * * r0 * ... * r15 = stack pointer * spc * pr * ssr * gbr * mach * macl * syscall # * */ #include <asm/dwarf.h> #if defined(CONFIG_PREEMPTION) # define preempt_stop() cli ; TRACE_IRQS_OFF #else # define preempt_stop() # define resume_kernel __restore_all #endif .align 2 ENTRY(exception_error) ! TRACE_IRQS_ON sti mov.l 1f, r0 jmp @r0 nop .align 2 1: .long do_exception_error .align 2 ret_from_exception: CFI_STARTPROC simple CFI_DEF_CFA r14, 0 CFI_REL_OFFSET 17, 64 CFI_REL_OFFSET 15, 60 CFI_REL_OFFSET 14, 56 CFI_REL_OFFSET 13, 52 CFI_REL_OFFSET 12, 48 CFI_REL_OFFSET 11, 44 CFI_REL_OFFSET 10, 40 CFI_REL_OFFSET 9, 36 CFI_REL_OFFSET 8, 32 preempt_stop() ENTRY(ret_from_irq) ! mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register shll r0 shll r0 ! kernel space? get_current_thread_info r8, r0 bt resume_kernel ! Yes, it's from kernel, go back soon #ifdef CONFIG_PREEMPTION bra resume_userspace nop ENTRY(resume_kernel) cli TRACE_IRQS_OFF mov.l @(TI_PRE_COUNT,r8), r0 ! current_thread_info->preempt_count tst r0, r0 bf noresched need_resched: mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #_TIF_NEED_RESCHED, r0 ! need_resched set? bt noresched mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register shlr r0 and #(0xf0>>1), r0 ! interrupts off (exception path)? cmp/eq #(0xf0>>1), r0 bt noresched mov.l 1f, r0 jsr @r0 ! call preempt_schedule_irq nop bra need_resched nop noresched: bra __restore_all nop .align 2 1: .long preempt_schedule_irq #endif ENTRY(resume_userspace) ! r8: current_thread_info cli TRACE_IRQS_OFF mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #(_TIF_WORK_MASK & 0xff), r0 bt/s __restore_all tst #_TIF_NEED_RESCHED, r0 .align 2 work_pending: ! r0: current_thread_info->flags ! r8: current_thread_info ! t: result of "tst #_TIF_NEED_RESCHED, r0" bf/s work_resched tst #(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME), r0 work_notifysig: bt/s __restore_all mov r15, r4 mov r12, r5 ! set arg1(save_r0) mov r0, r6 sti mov.l 2f, r1 mov.l 3f, r0 jmp @r1 lds r0, pr work_resched: mov.l 1f, r1 jsr @r1 ! schedule nop cli TRACE_IRQS_OFF ! mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #(_TIF_WORK_MASK & 0xff), r0 bt __restore_all bra work_pending tst #_TIF_NEED_RESCHED, r0 .align 2 1: .long schedule 2: .long do_notify_resume 3: .long resume_userspace .align 2 syscall_exit_work: ! r0: current_thread_info->flags ! r8: current_thread_info tst #(_TIF_WORK_SYSCALL_MASK & 0xff), r0 bt/s work_pending tst #_TIF_NEED_RESCHED, r0 TRACE_IRQS_ON sti mov r15, r4 mov.l 8f, r0 ! do_syscall_trace_leave jsr @r0 nop bra resume_userspace nop __restore_all: mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register shlr2 r0 and #0x3c, r0 cmp/eq #0x3c, r0 bt 1f TRACE_IRQS_ON bra 2f nop 1: TRACE_IRQS_OFF 2: mov.l 3f, r0 jmp @r0 nop .align 2 3: .long restore_all .align 2 syscall_badsys: ! Bad syscall number get_current_thread_info r8, r0 mov #-ENOSYS, r0 bra resume_userspace mov.l r0, @(OFF_R0,r15) ! Return value /* * The main debug trap handler. * * r8=TRA (not the trap number!) * * Note: This assumes that the trapa value is left in its original * form (without the shlr2 shift) so the calculation for the jump * call table offset remains a simple in place mask. */ debug_trap: mov r8, r0 and #(0xf << 2), r0 mov.l 1f, r8 add r0, r8 mov.l @r8, r8 jsr @r8 nop bra ret_from_exception nop CFI_ENDPROC .align 2 1: .long debug_trap_table /* * Syscall interface: * * Syscall #: R3 * Arguments #0 to #3: R4--R7 * Arguments #4 to #6: R0, R1, R2 * TRA: See following table. * * (TRA>>2) Purpose * -------- ------- * 0x00-0x0f original SH-3/4 syscall ABI (not in general use). * 0x10-0x1f general SH-3/4 syscall ABI. * 0x1f unified SH-2/3/4 syscall ABI (preferred). * 0x20-0x2f original SH-2 syscall ABI. * 0x30-0x3f debug traps used by the kernel. * 0x40-0xff Not supported by all parts, so left unhandled. * * For making system calls, any trap number in the range for the * given cpu model may be used, but the unified trap number 0x1f is * preferred for compatibility with all models. * * The low bits of the trap number were once documented as matching * the number of arguments, but they were never actually used as such * by the kernel. SH-2 originally used its own separate trap range * because several hardware exceptions fell in the range used for the * SH-3/4 syscall ABI. * * This code also handles delegating other traps to the BIOS/gdb stub. * * Note: When we're first called, the TRA value must be shifted * right 2 bits in order to get the value that was used as the "trapa" * argument. */ .align 2 .globl ret_from_fork ret_from_fork: mov.l 1f, r8 jsr @r8 mov r0, r4 bra syscall_exit nop .align 2 .globl ret_from_kernel_thread ret_from_kernel_thread: mov.l 1f, r8 jsr @r8 mov r0, r4 mov.l @(OFF_R5,r15), r5 ! fn jsr @r5 mov.l @(OFF_R4,r15), r4 ! arg bra syscall_exit nop .align 2 1: .long schedule_tail /* * The poorly named main trapa decode and dispatch routine, for * system calls and debug traps through their respective jump tables. */ ENTRY(system_call) setup_frame_reg #if !defined(CONFIG_CPU_SH2) mov.l 1f, r9 mov.l @r9, r8 ! Read from TRA (Trap Address) Register #endif mov #OFF_TRA, r10 add r15, r10 mov.l r8, @r10 ! set TRA value to tra /* * Check the trap type */ mov #((0x20 << 2) - 1), r9 cmp/hi r9, r8 bt/s debug_trap ! it's a debug trap.. nop TRACE_IRQS_ON sti ! get_current_thread_info r8, r10 mov.l @(TI_FLAGS,r8), r8 mov #(_TIF_WORK_SYSCALL_MASK & 0xff), r10 mov #(_TIF_WORK_SYSCALL_MASK >> 8), r9 tst r10, r8 shll8 r9 bf syscall_trace_entry tst r9, r8 bf syscall_trace_entry ! mov.l 6f, r8 ! Number of syscalls cmp/hs r8, r3 bt syscall_badsys ! syscall_call: shll2 r3 ! x4 mov.l 3f, r8 ! Load the address of sys_call_table add r8, r3 mov.l @r3, r8 mov.l @(OFF_R2,r15), r2 mov.l @(OFF_R1,r15), r1 mov.l @(OFF_R0,r15), r0 mov.l r2, @-r15 mov.l r1, @-r15 mov.l r0, @-r15 jsr @r8 ! jump to specific syscall handler nop add #12, r15 mov.l @(OFF_R0,r15), r12 ! save r0 mov.l r0, @(OFF_R0,r15) ! save the return value ! syscall_exit: cli TRACE_IRQS_OFF ! get_current_thread_info r8, r0 mov.l @(TI_FLAGS,r8), r0 ! current_thread_info->flags tst #(_TIF_ALLWORK_MASK & 0xff), r0 mov #(_TIF_ALLWORK_MASK >> 8), r1 bf syscall_exit_work shlr8 r0 tst r0, r1 bf syscall_exit_work bra __restore_all nop .align 2 syscall_trace_entry: ! Yes it is traced. mov r15, r4 mov.l 7f, r11 ! Call do_syscall_trace_enter which notifies jsr @r11 ! superior (will chomp R[0-7]) nop cmp/eq #-1, r0 bt syscall_exit ! Reload R0-R4 from kernel stack, where the ! parent may have modified them using ! ptrace(POKEUSR). (Note that R0-R2 are ! reloaded from the kernel stack by syscall_call ! below, so don't need to be reloaded here.) ! This allows the parent to rewrite system calls ! and args on the fly. mov.l @(OFF_R4,r15), r4 ! arg0 mov.l @(OFF_R5,r15), r5 mov.l @(OFF_R6,r15), r6 mov.l @(OFF_R7,r15), r7 ! arg3 mov.l @(OFF_R3,r15), r3 ! syscall_nr ! mov.l 6f, r10 ! Number of syscalls cmp/hs r10, r3 bf syscall_call mov #-ENOSYS, r0 bra syscall_exit mov.l r0, @(OFF_R0,r15) ! Return value .align 2 #if !defined(CONFIG_CPU_SH2) 1: .long TRA #endif 6: .long NR_syscalls 3: .long sys_call_table 7: .long do_syscall_trace_enter 8: .long do_syscall_trace_leave