| 1 | /* SPDX-License-Identifier: GPL-2.0-only */
|
| 2 | /*
|
| 3 | * Common Low Level Interrupts/Traps/Exceptions(non-TLB) Handling for ARC
|
| 4 | * (included from entry-<isa>.S
|
| 5 | *
|
| 6 | * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
|
| 7 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
|
| 8 | */
|
| 9 |
|
| 10 | /*------------------------------------------------------------------
|
| 11 | * Function ABI
|
| 12 | *------------------------------------------------------------------
|
| 13 | *
|
| 14 | * Arguments r0 - r7
|
| 15 | * Caller Saved Registers r0 - r12
|
| 16 | * Callee Saved Registers r13- r25
|
| 17 | * Global Pointer (gp) r26
|
| 18 | * Frame Pointer (fp) r27
|
| 19 | * Stack Pointer (sp) r28
|
| 20 | * Branch link register (blink) r31
|
| 21 | *------------------------------------------------------------------
|
| 22 | */
|
| 23 |
|
| 24 | ; |
| 25 |
|
| 26 | ENTRY(sys_clone_wrapper)
|
| 27 | SAVE_CALLEE_SAVED_USER
|
| 28 | bl @sys_clone
|
| 29 | DISCARD_CALLEE_SAVED_USER
|
| 30 |
|
| 31 | GET_CURR_THR_INFO_FLAGS r10
|
| 32 | and.f 0, r10, _TIF_SYSCALL_WORK
|
| 33 | bnz tracesys_exit
|
| 34 |
|
| 35 | b .Lret_from_system_call
|
| 36 | END(sys_clone_wrapper)
|
| 37 |
|
| 38 | ENTRY(sys_clone3_wrapper)
|
| 39 | SAVE_CALLEE_SAVED_USER
|
| 40 | bl @sys_clone3
|
| 41 | DISCARD_CALLEE_SAVED_USER
|
| 42 |
|
| 43 | GET_CURR_THR_INFO_FLAGS r10
|
| 44 | and.f 0, r10, _TIF_SYSCALL_WORK
|
| 45 | bnz tracesys_exit
|
| 46 |
|
| 47 | b .Lret_from_system_call
|
| 48 | END(sys_clone3_wrapper)
|
| 49 |
|
| 50 | ENTRY(ret_from_fork)
|
| 51 | ; when the forked child comes here from the __switch_to function
|
| 52 | ; r0 has the last task pointer.
|
| 53 | ; put last task in scheduler queue
|
| 54 | jl @schedule_tail
|
| 55 |
|
| 56 | ld r9, [sp, PT_status32]
|
| 57 | brne r9, 0, 1f
|
| 58 |
|
| 59 | jl.d [r14] ; kernel thread entry point
|
| 60 | mov r0, r13 ; (see PF_KTHREAD block in copy_thread)
|
| 61 |
|
| 62 | 1:
|
| 63 | ; Return to user space
|
| 64 | ; 1. Any forked task (Reach here via BRne above)
|
| 65 | ; 2. First ever init task (Reach here via return from JL above)
|
| 66 | ; This is the historic "kernel_execve" use-case, to return to init
|
| 67 | ; user mode, in a round about way since that is always done from
|
| 68 | ; a kernel thread which is executed via JL above but always returns
|
| 69 | ; out whenever kernel_execve (now inline do_fork()) is involved
|
| 70 | b ret_from_exception
|
| 71 | END(ret_from_fork)
|
| 72 |
|
| 73 | ; |
| 74 |
|
| 75 | ; ---------------------------------------------
|
| 76 | ; Instruction Error Exception Handler
|
| 77 | ; ---------------------------------------------
|
| 78 |
|
| 79 | ENTRY(instr_service)
|
| 80 |
|
| 81 | EXCEPTION_PROLOGUE
|
| 82 |
|
| 83 | bl do_insterror_or_kprobe
|
| 84 | b ret_from_exception
|
| 85 | END(instr_service)
|
| 86 |
|
| 87 | ; ---------------------------------------------
|
| 88 | ; Machine Check Exception Handler
|
| 89 | ; ---------------------------------------------
|
| 90 |
|
| 91 | ENTRY(EV_MachineCheck)
|
| 92 |
|
| 93 | EXCEPTION_PROLOGUE_KEEP_AE ; ECR returned in r10
|
| 94 |
|
| 95 | lr r0, [efa]
|
| 96 | mov r1, sp
|
| 97 |
|
| 98 | ; MC exceptions disable MMU
|
| 99 | ARC_MMU_REENABLE r3
|
| 100 |
|
| 101 | lsr r3, r10, 8
|
| 102 | bmsk r3, r3, 7
|
| 103 | brne r3, ECR_C_MCHK_DUP_TLB, 1f
|
| 104 |
|
| 105 | bl do_tlb_overlap_fault
|
| 106 | b ret_from_exception
|
| 107 |
|
| 108 | 1:
|
| 109 | ; DEAD END: can't do much, display Regs and HALT
|
| 110 | SAVE_CALLEE_SAVED_USER
|
| 111 |
|
| 112 | GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10
|
| 113 | st sp, [r10, THREAD_CALLEE_REG]
|
| 114 |
|
| 115 | j do_machine_check_fault
|
| 116 |
|
| 117 | END(EV_MachineCheck)
|
| 118 |
|
| 119 | ; ---------------------------------------------
|
| 120 | ; Privilege Violation Exception Handler
|
| 121 | ; ---------------------------------------------
|
| 122 | ENTRY(EV_PrivilegeV)
|
| 123 |
|
| 124 | EXCEPTION_PROLOGUE
|
| 125 |
|
| 126 | bl do_privilege_fault
|
| 127 | b ret_from_exception
|
| 128 | END(EV_PrivilegeV)
|
| 129 |
|
| 130 | ; ---------------------------------------------
|
| 131 | ; Extension Instruction Exception Handler
|
| 132 | ; ---------------------------------------------
|
| 133 | ENTRY(EV_Extension)
|
| 134 |
|
| 135 | EXCEPTION_PROLOGUE
|
| 136 |
|
| 137 | bl do_extension_fault
|
| 138 | b ret_from_exception
|
| 139 | END(EV_Extension)
|
| 140 |
|
| 141 | ;################ Trap Handling (Syscall, Breakpoint) ##################
|
| 142 |
|
| 143 | ; ---------------------------------------------
|
| 144 | ; syscall Tracing
|
| 145 | ; ---------------------------------------------
|
| 146 | tracesys:
|
| 147 | ; safekeep EFA (r12) if syscall tracer wanted PC
|
| 148 | ; for traps, ERET is pre-commit so points to next-PC
|
| 149 | GET_CURR_TASK_FIELD_PTR TASK_THREAD, r11
|
| 150 | st r12, [r11, THREAD_FAULT_ADDR] ; thread.fault_address
|
| 151 |
|
| 152 | ; PRE syscall trace hook
|
| 153 | mov r0, sp ; pt_regs
|
| 154 | bl @syscall_trace_enter
|
| 155 |
|
| 156 | ; Tracing code now returns the syscall num (orig or modif)
|
| 157 | mov r8, r0
|
| 158 |
|
| 159 | ; Do the Sys Call as we normally would.
|
| 160 | cmp r8, NR_syscalls - 1
|
| 161 | mov.hi r0, -ENOSYS
|
| 162 | bhi tracesys_exit
|
| 163 |
|
| 164 | ; Restore the sys-call args. Mere invocation of the hook abv could have
|
| 165 | ; clobbered them (since they are in scratch regs). The tracer could also
|
| 166 | ; have deliberately changed the syscall args: r0-r7
|
| 167 | ld r0, [sp, PT_r0]
|
| 168 | ld r1, [sp, PT_r1]
|
| 169 | ld r2, [sp, PT_r2]
|
| 170 | ld r3, [sp, PT_r3]
|
| 171 | ld r4, [sp, PT_r4]
|
| 172 | ld r5, [sp, PT_r5]
|
| 173 | ld r6, [sp, PT_r6]
|
| 174 | ld r7, [sp, PT_r7]
|
| 175 | ld.as r9, [sys_call_table, r8]
|
| 176 | jl [r9]
|
| 177 |
|
| 178 | tracesys_exit:
|
| 179 | st r0, [sp, PT_r0]
|
| 180 |
|
| 181 | ; POST syscall trace hook
|
| 182 | mov r0, sp ; pt_regs needed
|
| 183 | bl @syscall_trace_exit
|
| 184 |
|
| 185 | ; don't call ret_from_system_call as it saves r0, already done above
|
| 186 | b ret_from_exception
|
| 187 |
|
| 188 | ; ---------------------------------------------
|
| 189 | ; Breakpoint TRAP
|
| 190 | ; ---------------------------------------------
|
| 191 | trap_with_param:
|
| 192 | mov r0, r12 ; EFA in case ptracer/gdb wants stop_pc
|
| 193 | mov r1, sp ; pt_regs
|
| 194 |
|
| 195 | ; save callee regs in case tracer/gdb wants to peek
|
| 196 | SAVE_CALLEE_SAVED_USER
|
| 197 |
|
| 198 | ; safekeep ref to callee regs
|
| 199 | GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10
|
| 200 | st sp, [r10, THREAD_CALLEE_REG]
|
| 201 |
|
| 202 | ; call the non syscall trap handler
|
| 203 | bl do_non_swi_trap
|
| 204 |
|
| 205 | ; unwind stack to discard callee regs
|
| 206 | DISCARD_CALLEE_SAVED_USER
|
| 207 |
|
| 208 | b ret_from_exception
|
| 209 |
|
| 210 | ; ---------------------------------------------
|
| 211 | ; syscall TRAP
|
| 212 | ; ABI: (r0-r7) up to 8 args, (r8) syscall number
|
| 213 | ; ---------------------------------------------
|
| 214 |
|
| 215 | ENTRY(EV_Trap)
|
| 216 |
|
| 217 | EXCEPTION_PROLOGUE_KEEP_AE
|
| 218 |
|
| 219 | lr r12, [efa]
|
| 220 |
|
| 221 | FAKE_RET_FROM_EXCPN
|
| 222 |
|
| 223 | ;============ TRAP N : breakpoints, kprobes etc
|
| 224 | bmsk.f 0, r10, 7
|
| 225 | bnz trap_with_param
|
| 226 |
|
| 227 | ;============ TRAP 0 (no param): syscall
|
| 228 |
|
| 229 | ; syscall tracing ongoing, invoke pre-post-hooks around syscall
|
| 230 | GET_CURR_THR_INFO_FLAGS r10
|
| 231 | and.f 0, r10, _TIF_SYSCALL_WORK
|
| 232 | bnz tracesys ; this never comes back
|
| 233 |
|
| 234 | ;============ Normal syscall case
|
| 235 |
|
| 236 | cmp r8, NR_syscalls - 1
|
| 237 | mov.hi r0, -ENOSYS
|
| 238 | bhi .Lret_from_system_call
|
| 239 |
|
| 240 | ld.as r9,[sys_call_table, r8]
|
| 241 | jl [r9]
|
| 242 |
|
| 243 | .Lret_from_system_call:
|
| 244 | st r0, [sp, PT_r0] ; sys call return value in pt_regs
|
| 245 |
|
| 246 | ; fall through to ret_from_exception
|
| 247 | END(EV_Trap)
|
| 248 |
|
| 249 | ; |
| 250 | ;
|
| 251 | ; If ret to user mode do we need to handle signals, schedule() et al.
|
| 252 |
|
| 253 | ENTRY(ret_from_exception)
|
| 254 |
|
| 255 | ; Pre-{IRQ,Trap,Exception} K/U mode from pt_regs->status32
|
| 256 | ld r8, [sp, PT_status32] ; returning to User/Kernel Mode
|
| 257 |
|
| 258 | bbit0 r8, STATUS_U_BIT, resume_kernel_mode
|
| 259 |
|
| 260 | ; Before returning to User mode check-for-and-complete any pending work
|
| 261 | ; such as rescheduling/signal-delivery etc.
|
| 262 | resume_user_mode_begin:
|
| 263 |
|
| 264 | ; Disable IRQs to ensures that chk for pending work itself is atomic
|
| 265 | ; (and we don't end up missing a NEED_RESCHED/SIGPENDING due to an
|
| 266 | ; interim IRQ).
|
| 267 | IRQ_DISABLE r10
|
| 268 |
|
| 269 | ; Fast Path return to user mode if no pending work
|
| 270 | GET_CURR_THR_INFO_FLAGS r9
|
| 271 | and.f 0, r9, _TIF_WORK_MASK
|
| 272 | bz .Lrestore_regs
|
| 273 |
|
| 274 | ; --- (Slow Path #1) task preemption ---
|
| 275 | bbit0 r9, TIF_NEED_RESCHED, .Lchk_pend_signals
|
| 276 | mov blink, resume_user_mode_begin ; tail-call to U mode ret chks
|
| 277 | j @schedule ; BTST+Bnz causes relo error in link
|
| 278 |
|
| 279 | .Lchk_pend_signals:
|
| 280 | IRQ_ENABLE r10
|
| 281 |
|
| 282 | ; --- (Slow Path #2) pending signal ---
|
| 283 | mov r0, sp ; pt_regs for arg to do_signal()/do_notify_resume()
|
| 284 |
|
| 285 | GET_CURR_THR_INFO_FLAGS r9
|
| 286 | and.f 0, r9, _TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL
|
| 287 | bz .Lchk_notify_resume
|
| 288 |
|
| 289 | ; Normal Trap/IRQ entry only saves Scratch (caller-saved) regs
|
| 290 | ; in pt_reg since the "C" ABI (kernel code) will automatically
|
| 291 | ; save/restore callee-saved regs.
|
| 292 | ;
|
| 293 | ; However, here we need to explicitly save callee regs because
|
| 294 | ; (i) If this signal causes coredump - full regfile needed
|
| 295 | ; (ii) If signal is SIGTRAP/SIGSTOP, task is being traced thus
|
| 296 | ; tracer might call PEEKUSR(CALLEE reg)
|
| 297 | ;
|
| 298 | ; NOTE: SP will grow up by size of CALLEE Reg-File
|
| 299 | SAVE_CALLEE_SAVED_USER
|
| 300 |
|
| 301 | ; save location of saved Callee Regs @ thread_struct->callee
|
| 302 | GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10
|
| 303 | st sp, [r10, THREAD_CALLEE_REG]
|
| 304 |
|
| 305 | bl @do_signal
|
| 306 |
|
| 307 | ; Ideally we want to discard the Callee reg above, however if this was
|
| 308 | ; a tracing signal, tracer could have done a POKEUSR(CALLEE reg)
|
| 309 | RESTORE_CALLEE_SAVED_USER
|
| 310 |
|
| 311 | b resume_user_mode_begin ; loop back to start of U mode ret
|
| 312 |
|
| 313 | ; --- (Slow Path #3) notify_resume ---
|
| 314 | .Lchk_notify_resume:
|
| 315 | btst r9, TIF_NOTIFY_RESUME
|
| 316 | blnz @do_notify_resume
|
| 317 | b resume_user_mode_begin ; unconditionally back to U mode ret chks
|
| 318 | ; for single exit point from this block
|
| 319 |
|
| 320 | resume_kernel_mode:
|
| 321 |
|
| 322 | ; Disable Interrupts from this point on
|
| 323 | ; CONFIG_PREEMPTION: This is a must for preempt_schedule_irq()
|
| 324 | ; !CONFIG_PREEMPTION: To ensure restore_regs is intr safe
|
| 325 | IRQ_DISABLE r9
|
| 326 |
|
| 327 | #ifdef CONFIG_PREEMPTION
|
| 328 |
|
| 329 | ; Can't preempt if preemption disabled
|
| 330 | GET_CURR_THR_INFO_FROM_SP r10
|
| 331 | ld r8, [r10, THREAD_INFO_PREEMPT_COUNT]
|
| 332 | brne r8, 0, .Lrestore_regs
|
| 333 |
|
| 334 | ; check if this task's NEED_RESCHED flag set
|
| 335 | ld r9, [r10, THREAD_INFO_FLAGS]
|
| 336 | bbit0 r9, TIF_NEED_RESCHED, .Lrestore_regs
|
| 337 |
|
| 338 | ; Invoke PREEMPTION
|
| 339 | jl preempt_schedule_irq
|
| 340 |
|
| 341 | ; preempt_schedule_irq() always returns with IRQ disabled
|
| 342 | #endif
|
| 343 |
|
| 344 | b .Lrestore_regs
|
| 345 |
|
| 346 | ##### DONT ADD CODE HERE - .Lrestore_regs actually follows in entry-<isa>.S
|
| 347 |
|