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)
|