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 | ;################### 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 | 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 | ;################### Non TLB Exception Handling ############################# |
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 excpetions 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) upto 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 | ;############# Return from Intr/Excp/Trap (Linux Specifics) ############## |
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 | |
348 | |