summaryrefslogtreecommitdiff
path: root/release/src/linux/linux/arch/mips/kernel/scall_o32.S
blob: 315e99bbdca9a3f5cfa19c189a8ef53f0db37eff (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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 1997, 1998, 1999, 2000, 2001 by Ralf Baechle
 * Copyright (C) 2001 MIPS Technologies, Inc.
 */
#include <linux/config.h>
#include <linux/errno.h>
#include <asm/asm.h>
#include <asm/current.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/isadep.h>
#include <asm/sysmips.h>
#include <asm/unistd.h>

/* Highest syscall used of any syscall flavour */
#define MAX_SYSCALL_NO	__NR_Linux + __NR_Linux_syscalls

	.align  5
NESTED(handle_sys, PT_SIZE, sp)
	.set	noat
	SAVE_SOME
	STI
	.set	at

	lw	t1, PT_EPC(sp)		# skip syscall on return

	sltiu	t0, v0, MAX_SYSCALL_NO + 1 # check syscall number
	addiu	t1, 4			# skip to next instruction
	beqz	t0, illegal_syscall
	sw	t1, PT_EPC(sp)

	sll	t0, v0, 2
	lw	t2, sys_call_table(t0)	# syscall routine
	lbu	t3, sys_narg_table(v0)	# number of arguments
	beqz	t2, illegal_syscall

	subu	t0, t3, 5		# 5 or more arguments?
	sw	a3, PT_R26(sp)		# save a3 for syscall restarting
	bgez	t0, stackargs

stack_done:
        sw      a3, PT_R26(sp)          # save for syscall restart
#ifdef CONFIG_PRINT_SYSCALLS

	SAVE_STATIC
	sw	t2, PT_R1(sp)
	move	a0,sp			# pass pointer to saved regs
	
	jal	strace

	RESTORE_STATIC
	lw	t2, PT_R1(sp)

	lw	a0, PT_R4(sp)		# Restore argument registers
	lw	a1, PT_R5(sp)
	lw	a2, PT_R6(sp)
	lw	a3, PT_R7(sp)
#endif

	lw	t0, TASK_PTRACE($28)	# syscall tracing enabled?
	andi	t0, _PT_TRACESYS
	bnez	t0, trace_a_syscall

	jalr	t2			# Do The Real Thing (TM)

	li	t0, -EMAXERRNO - 1	# error?
	sltu	t0, t0, v0
	sw	t0, PT_R7(sp)		# set error flag
	beqz	t0, 1f

	negu	v0			# error
	sw	v0, PT_R0(sp)		# set flag for syscall restarting
1:	sw	v0, PT_R2(sp)		# result

fast_ret_from_sys_call:
ret_from_schedule:
	mfc0	t0, CP0_STATUS		# need_resched and signals atomic test
	ori	t0, t0, 1
	xori	t0, t0, 1
	mtc0	t0, CP0_STATUS
	SSNOP; SSNOP; SSNOP

	lw	t2, TASK_NEED_RESCHED($28)
	lw	v0, TASK_SIGPENDING($28)
	bnez	t2, reschedule
	bnez	v0, signal_return
restore_all:
	RESTORE_SOME
	RESTORE_SP_AND_RET

/* ------------------------------------------------------------------------ */

FEXPORT(ret_from_fork)
	move	a0, v0			# prev
	jal	schedule_tail
	lw	t0, TASK_PTRACE($28)	# syscall tracing enabled?
	andi	t0, _PT_TRACESYS
	bnez	t0, tracesys_exit

static_ret_from_sys_call:
	RESTORE_STATIC
	j	fast_ret_from_sys_call

/* ------------------------------------------------------------------------ */

/* ret_from_sys_call should be here but is in entry.S.  */

/* ------------------------------------------------------------------------ */

/* Put this behind restore_all for the sake of the branch prediction.  */
signal_return:
	.type	signal_return, @function

	mfc0	t0, CP0_STATUS
	ori	t0, t0, 1
	mtc0	t0, CP0_STATUS

	SAVE_STATIC
	move	a0, zero
	move	a1, sp
	jal	do_signal
	RESTORE_STATIC
	b	restore_all

/* ------------------------------------------------------------------------ */

reschedule:
	jal	schedule
	b	ret_from_schedule

/* ------------------------------------------------------------------------ */

trace_a_syscall:
	SAVE_STATIC
	sw	t2, PT_R1(sp)
	jal	syscall_trace
	lw	t2, PT_R1(sp)

	lw	a0, PT_R4(sp)		# Restore argument registers
	lw	a1, PT_R5(sp)
	lw	a2, PT_R6(sp)
	lw	a3, PT_R7(sp)
	jalr	t2

	li	t0, -EMAXERRNO - 1	# error?
	sltu	t0, t0, v0
	sw	t0, PT_R7(sp)		# set error flag
	beqz	t0, 1f

	negu	v0			# error
	sw	v0, PT_R0(sp)		# set flag for syscall restarting
1:	sw	v0, PT_R2(sp)		# result

tracesys_exit:
	jal	syscall_trace
	j	static_ret_from_sys_call

/* ------------------------------------------------------------------------ */

	/*
	 * More than four arguments.  Try to deal with it by copying the
	 * stack arguments from the user stack to the kernel stack.
	 * This Sucks (TM).
	 */
stackargs:
	lw	t0, PT_R29(sp)		# get old user stack pointer
	subu	t3, 4
	sll	t1, t3, 2		# stack valid?

	addu	t1, t0			# end address
	or	t0, t1
	bltz	t0, bad_stack		# -> sp is bad

	lw	t0, PT_R29(sp)		# get old user stack pointer
	PTR_LA	t1, 3f			# copy 1 to 2 arguments
	sll	t3, t3, 4
	subu	t1, t3
	jr	t1

	/* Ok, copy the args from the luser stack to the kernel stack */
	/*
	 * I know Ralf doesn't like nops but this avoids code
	 * duplication for R3000 targets (and this is the
	 * only place where ".set reorder" doesn't help).
	 * Harald.
	 */
	.set    push
	.set    noreorder
	.set	nomacro
1:	lw	t1, 20(t0)		# argument #6 from usp
	nop
	sw	t1, 20(sp)
	nop
2:	lw	t1, 16(t0)		# argument #5 from usp
	nop
	sw	t1, 16(sp)
	nop
3:	.set	pop

	j	stack_done		# go back

	.section __ex_table,"a"
	PTR	1b,bad_stack
	PTR	2b,bad_stack
	.previous

/* ------------------------------------------------------------------------ */

	/*
	 * The stackpointer for a call with more than 4 arguments is bad.
	 * We probably should handle this case a bit more drastic.
	 */
bad_stack:
	negu	v0			# error
	sw	v0, PT_R0(sp)
	sw	v0, PT_R2(sp)
	li	t0, 1			# set error flag
	sw	t0, PT_R7(sp)
	j	fast_ret_from_sys_call

/* ------------------------------------------------------------------------ */

	/*
	 * The system call does not exist in this kernel
	 */
illegal_syscall:
	lw	t0, TASK_PTRACE($28)	# syscall tracing enabled?
	andi	t0, _PT_TRACESYS
	beqz	t0, 1f

	SAVE_STATIC
	jal	syscall_trace
	li	t0, _PT_TRACESYS

1:	li	v0, ENOSYS		# error
	sw	v0, PT_R0(sp)		# set flag for syscall restarting
	sw	v0, PT_R2(sp)
	li	t1, 1			# set error flag
	sw	t1, PT_R7(sp)
	bnez	t0, tracesys_exit

	j	fast_ret_from_sys_call
END(handle_sys)

LEAF(mips_atomic_set)
	andi	v0, a1, 3		# must be word aligned
	bnez	v0, bad_alignment

	lw	v1, THREAD_CURDS($28)	# in legal address range?
	addiu	a0, a1, 4
	or	a0, a0, a1
	and	a0, a0, v1
	bltz	a0, bad_address

#ifdef CONFIG_CPU_HAS_LLSC
	/* Ok, this is the ll/sc case.  World is sane :-)  */
1:	ll	v0, (a1)
	move	a0, a2
2:	sc	a0, (a1)
	beqz	a0, 1b

	.section __ex_table,"a"
	PTR	1b, bad_stack
	PTR	2b, bad_stack
	.previous
#else
	sw	a1, 16(sp)
	sw	a2, 20(sp)

	move	a0, sp
	move	a2, a1
	li	a1, 1
	jal	do_page_fault

	lw	a1, 16(sp)
	lw	a2, 20(sp)

	/*
	 * At this point the page should be readable and writable unless
	 * there was no more memory available.
	 */
1:	lw	v0, (a1)
2:	sw	a2, (a1)

	.section __ex_table,"a"
	PTR	1b, no_mem
	PTR	2b, no_mem
	.previous
#endif

	sw	zero, PT_R7(sp)		# success
	sw	v0, PT_R2(sp)		# result

	/* Success, so skip usual error handling garbage.  */
	lw	t0, TASK_PTRACE($28)	# syscall tracing enabled?
	andi	t0, _PT_TRACESYS
	beqz	t0, fast_ret_from_sys_call

	SAVE_STATIC
	jal	syscall_trace
	j	static_ret_from_sys_call

no_mem:	li	v0, -ENOMEM
	jr	ra

bad_address:
	li	v0, -EFAULT
	jr	ra

bad_alignment:
	li	v0, -EINVAL
	jr	ra
END(mips_atomic_set)

LEAF(sys_sysmips)
	beq	a0, MIPS_ATOMIC_SET, mips_atomic_set
	j	_sys_sysmips
END(sys_sysmips)

LEAF(sys_syscall)
	lw	t0, PT_R29(sp)		# user sp

	sltu	v0, a0, __NR_Linux + __NR_Linux_syscalls + 1
	beqz	v0, enosys

	sll	v0, a0, 2
	la	v1, sys_syscall
	lw	t2, sys_call_table(v0)	# function pointer
	lbu	t4, sys_narg_table(a0)	# number of arguments

	li	v0, -EINVAL
	beq	t2, v1, out		# do not recurse

	beqz	t2, enosys		# null function pointer?

	andi	v0, t0, 0x3		# unaligned stack pointer?
	bnez	v0, sigsegv

	addu	v0, t0, 16		# v0 = usp + 16
	addu	t1, v0, 12		# 3 32-bit arguments
	lw	v1, THREAD_CURDS($28)
	or	v0, v0, t1
	and	v1, v1, v0
	bltz	v1, efault

	move	a0, a1			# shift argument registers
	move	a1, a2
	move	a2, a3

1:	lw	a3, 16(t0)
2:	lw	t3, 20(t0)
3:	lw	t4, 24(t0)

	.section	__ex_table, "a"
	.word	1b, efault
	.word	2b, efault
	.word	3b, efault
	.previous

	sw	t3, 16(sp)		# put into new stackframe
	sw	t4, 20(sp)

	bnez	t4, 1f			# zero arguments?
	addu	a0, sp, 32		# then pass sp in a0
1:

	sw	t3, 16(sp)
	sw	v1, 20(sp)
	jr	t2
	/* Unreached */

enosys:	li	v0, -ENOSYS
	b	out

sigsegv:
	li	a0, _SIGSEGV
	move	a1, $28
	jal	force_sig
	/* Fall through */

efault:	li	v0, -EFAULT

out:	jr	ra
END(sys_syscall)