summaryrefslogtreecommitdiff
path: root/arch/arc/kernel/entry.S
blob: 3c7e74aba6794e8c1be2c99a7aa3dbe0393e1a1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Common Low Level Interrupts/Traps/Exceptions(non-TLB) Handling for ARC
 * (included from entry-<isa>.S
 *
 * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 */

/*------------------------------------------------------------------
 *    Function                            ABI
 *------------------------------------------------------------------
 *
 *  Arguments                           r0 - r7
 *  Caller Saved Registers              r0 - r12
 *  Callee Saved Registers              r13- r25
 *  Global Pointer (gp)                 r26
 *  Frame Pointer (fp)                  r27
 *  Stack Pointer (sp)                  r28
 *  Branch link register (blink)        r31
 *------------------------------------------------------------------
 */

;################### Special Sys Call Wrappers ##########################

ENTRY(sys_clone_wrapper)
	SAVE_CALLEE_SAVED_USER
	bl  @sys_clone
	DISCARD_CALLEE_SAVED_USER

	GET_CURR_THR_INFO_FLAGS   r10
	and.f 0, r10, _TIF_SYSCALL_WORK
	bnz   tracesys_exit

	b .Lret_from_system_call
END(sys_clone_wrapper)

ENTRY(sys_clone3_wrapper)
	SAVE_CALLEE_SAVED_USER
	bl  @sys_clone3
	DISCARD_CALLEE_SAVED_USER

	GET_CURR_THR_INFO_FLAGS   r10
	and.f 0, r10, _TIF_SYSCALL_WORK
	bnz   tracesys_exit

	b .Lret_from_system_call
END(sys_clone3_wrapper)

ENTRY(ret_from_fork)
	; when the forked child comes here from the __switch_to function
	; r0 has the last task pointer.
	; put last task in scheduler queue
	jl   @schedule_tail

	ld   r9, [sp, PT_status32]
	brne r9, 0, 1f

	jl.d [r14]		; kernel thread entry point
	mov  r0, r13		; (see PF_KTHREAD block in copy_thread)

1:
	; Return to user space
	; 1. Any forked task (Reach here via BRne above)
	; 2. First ever init task (Reach here via return from JL above)
	;    This is the historic "kernel_execve" use-case, to return to init
	;    user mode, in a round about way since that is always done from
	;    a kernel thread which is executed via JL above but always returns
	;    out whenever kernel_execve (now inline do_fork()) is involved
	b    ret_from_exception
END(ret_from_fork)

;################### Non TLB Exception Handling #############################

; ---------------------------------------------
; Instruction Error Exception Handler
; ---------------------------------------------

ENTRY(instr_service)

	EXCEPTION_PROLOGUE

	bl  do_insterror_or_kprobe
	b   ret_from_exception
END(instr_service)

; ---------------------------------------------
; Machine Check Exception Handler
; ---------------------------------------------

ENTRY(EV_MachineCheck)

	EXCEPTION_PROLOGUE_KEEP_AE	; ECR returned in r10

	lr  r0, [efa]
	mov r1, sp

	; MC exceptions disable MMU
	ARC_MMU_REENABLE r3

	lsr  	r3, r10, 8
	bmsk 	r3, r3, 7
	brne    r3, ECR_C_MCHK_DUP_TLB, 1f

	bl      do_tlb_overlap_fault
	b       ret_from_exception

1:
	; DEAD END: can't do much, display Regs and HALT
	SAVE_CALLEE_SAVED_USER

	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r10
	st  sp, [r10, THREAD_CALLEE_REG]

	j  do_machine_check_fault

END(EV_MachineCheck)

; ---------------------------------------------
; Privilege Violation Exception Handler
; ---------------------------------------------
ENTRY(EV_PrivilegeV)

	EXCEPTION_PROLOGUE

	bl  do_privilege_fault
	b   ret_from_exception
END(EV_PrivilegeV)

; ---------------------------------------------
; Extension Instruction Exception Handler
; ---------------------------------------------
ENTRY(EV_Extension)

	EXCEPTION_PROLOGUE

	bl  do_extension_fault
	b   ret_from_exception
END(EV_Extension)

;################ Trap Handling (Syscall, Breakpoint) ##################

; ---------------------------------------------
; syscall Tracing
; ---------------------------------------------
tracesys:
	; safekeep EFA (r12) if syscall tracer wanted PC
	; for traps, ERET is pre-commit so points to next-PC
	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r11
	st  r12, [r11, THREAD_FAULT_ADDR]	; thread.fault_address

	; PRE syscall trace hook
	mov r0, sp				; pt_regs
	bl  @syscall_trace_enter

	; Tracing code now returns the syscall num (orig or modif)
	mov r8, r0

	; Do the Sys Call as we normally would.
	cmp     r8,  NR_syscalls - 1
	mov.hi  r0, -ENOSYS
	bhi     tracesys_exit

	; Restore the sys-call args. Mere invocation of the hook abv could have
	; clobbered them (since they are in scratch regs). The tracer could also
	; have deliberately changed the syscall args: r0-r7
	ld  r0, [sp, PT_r0]
	ld  r1, [sp, PT_r1]
	ld  r2, [sp, PT_r2]
	ld  r3, [sp, PT_r3]
	ld  r4, [sp, PT_r4]
	ld  r5, [sp, PT_r5]
	ld  r6, [sp, PT_r6]
	ld  r7, [sp, PT_r7]
	ld.as   r9, [sys_call_table, r8]
	jl      [r9]

tracesys_exit:
	st  r0, [sp, PT_r0]

	; POST syscall trace hook
	mov r0, sp		; pt_regs needed
	bl  @syscall_trace_exit

	; don't call ret_from_system_call as it saves r0, already done above
	b   ret_from_exception

; ---------------------------------------------
; Breakpoint TRAP
; ---------------------------------------------
trap_with_param:
	mov r0, r12	; EFA in case ptracer/gdb wants stop_pc
	mov r1, sp	; pt_regs

	; save callee regs in case tracer/gdb wants to peek
	SAVE_CALLEE_SAVED_USER

	; safekeep ref to callee regs
	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r10
	st  sp, [r10, THREAD_CALLEE_REG]

	; call the non syscall trap handler
	bl  do_non_swi_trap

	; unwind stack to discard callee regs
	DISCARD_CALLEE_SAVED_USER

	b   ret_from_exception

; ---------------------------------------------
; syscall TRAP
; ABI: (r0-r7) up to 8 args, (r8) syscall number
; ---------------------------------------------

ENTRY(EV_Trap)

	EXCEPTION_PROLOGUE_KEEP_AE

	lr  r12, [efa]

	FAKE_RET_FROM_EXCPN

	;============ TRAP N : breakpoints, kprobes etc
	bmsk.f 0, r10, 7
	bnz    trap_with_param

	;============ TRAP 0 (no param): syscall

	; syscall tracing ongoing, invoke pre-post-hooks around syscall
	GET_CURR_THR_INFO_FLAGS   r10
	and.f 0, r10, _TIF_SYSCALL_WORK
	bnz   tracesys  ; this never comes back

	;============ Normal syscall case

	cmp     r8,  NR_syscalls - 1
	mov.hi  r0, -ENOSYS
	bhi     .Lret_from_system_call

	ld.as   r9,[sys_call_table, r8]
	jl      [r9]

.Lret_from_system_call:
	st  r0, [sp, PT_r0]     ; sys call return value in pt_regs

	; fall through to ret_from_exception
END(EV_Trap)

;############# Return from Intr/Excp/Trap (Linux Specifics) ##############
;
; If ret to user mode do we need to handle signals, schedule() et al.

ENTRY(ret_from_exception)

	; Pre-{IRQ,Trap,Exception} K/U mode from pt_regs->status32
	ld  r8, [sp, PT_status32]   ; returning to User/Kernel Mode

	bbit0  r8, STATUS_U_BIT, resume_kernel_mode

	; Before returning to User mode check-for-and-complete any pending work
	; such as rescheduling/signal-delivery etc.
resume_user_mode_begin:

	; Disable IRQs to ensures that chk for pending work itself is atomic
	; (and we don't end up missing a NEED_RESCHED/SIGPENDING due to an
	; interim IRQ).
	IRQ_DISABLE	r10

	; Fast Path return to user mode if no pending work
	GET_CURR_THR_INFO_FLAGS   r9
	and.f  0,  r9, _TIF_WORK_MASK
	bz     .Lrestore_regs

	; --- (Slow Path #1) task preemption ---
	bbit0  r9, TIF_NEED_RESCHED, .Lchk_pend_signals
	mov    blink, resume_user_mode_begin  ; tail-call to U mode ret chks
	j      @schedule 	; BTST+Bnz causes relo error in link

.Lchk_pend_signals:
	IRQ_ENABLE	r10

	; --- (Slow Path #2) pending signal  ---
	mov r0, sp	; pt_regs for arg to do_signal()/do_notify_resume()

	GET_CURR_THR_INFO_FLAGS   r9
	and.f  0,  r9, _TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL
	bz .Lchk_notify_resume

	; Normal Trap/IRQ entry only saves Scratch (caller-saved) regs
	; in pt_reg since the "C" ABI (kernel code) will automatically
	; save/restore callee-saved regs.
	;
	; However, here we need to explicitly save callee regs because
	; (i)  If this signal causes coredump - full regfile needed
	; (ii) If signal is SIGTRAP/SIGSTOP, task is being traced thus
	;      tracer might call PEEKUSR(CALLEE reg)
	;
	; NOTE: SP will grow up by size of CALLEE Reg-File
	SAVE_CALLEE_SAVED_USER

	; save location of saved Callee Regs @ thread_struct->callee
	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r10
	st  sp, [r10, THREAD_CALLEE_REG]

	bl  @do_signal

	; Ideally we want to discard the Callee reg above, however if this was
	; a tracing signal, tracer could have done a POKEUSR(CALLEE reg)
	RESTORE_CALLEE_SAVED_USER

	b      resume_user_mode_begin	; loop back to start of U mode ret

	; --- (Slow Path #3) notify_resume ---
.Lchk_notify_resume:
	btst   r9, TIF_NOTIFY_RESUME
	blnz   @do_notify_resume
	b      resume_user_mode_begin	; unconditionally back to U mode ret chks
					; for single exit point from this block

resume_kernel_mode:

	; Disable Interrupts from this point on
	; CONFIG_PREEMPTION: This is a must for preempt_schedule_irq()
	; !CONFIG_PREEMPTION: To ensure restore_regs is intr safe
	IRQ_DISABLE	r9

#ifdef CONFIG_PREEMPTION

	; Can't preempt if preemption disabled
	GET_CURR_THR_INFO_FROM_SP   r10
	ld  r8, [r10, THREAD_INFO_PREEMPT_COUNT]
	brne  r8, 0, .Lrestore_regs

	; check if this task's NEED_RESCHED flag set
	ld  r9, [r10, THREAD_INFO_FLAGS]
	bbit0  r9, TIF_NEED_RESCHED, .Lrestore_regs

	; Invoke PREEMPTION
	jl      preempt_schedule_irq

	; preempt_schedule_irq() always returns with IRQ disabled
#endif

	b	.Lrestore_regs

##### DONT ADD CODE HERE - .Lrestore_regs actually follows in entry-<isa>.S