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