summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/scall_o32.S
blob: eb25c34e5d7d51069b039d9854bbba8b1fc026e1 (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
/*
 * Handle ABI O32 style syscalls.
 *
 * Copyright (C) 1997, 1998 by Ralf Baechle
 *
 * $Id: scall_o32.S,v 1.1 1998/03/12 19:06:20 ralf Exp $
 */
#include <asm/asm.h>
#include <linux/errno.h>
#include <asm/current.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/unistd.h>

/* This duplicates the definition from <linux/sched.h> */
#define PF_TRACESYS	0x00000020	/* tracing system calls */

/* This duplicates the definition from <asm/signal.h> */
#define SIGILL		4		/* Illegal instruction (ANSI).  */

/* 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_ALL
	STI
	.set	at

	/*
	 * By convention "li v0,<syscallno>" is always preceeding
	 * the syscall instruction.  So if we're in a delay slot
	 * userland is screwed up.
	 */
	lw	t0, PT_CAUSE(sp)	# delay slot?
	lw	t1, PT_EPC(sp)		# skip syscall on return
	bltz	t0, sigill_and_out

	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)

	/* XXX Put both in one cacheline, should save a bit. */
	sll	t0, v0, 2
	lw	s0, sys_call_table(t0)	# syscall routine
	lbu	s1, sys_narg_table(v0)	# number of arguments
	beqz	s0, illegal_syscall;

	subu	t0, s1, 5		# 5 or more arguments?
	bgezal	t0, stackargs

	lw	s3, TASK_FLAGS($28)	# syscall tracing enabled?
	andi	s3, PF_TRACESYS
	bnez	s3, trace_a_syscall

	jalr	s0			# 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
	j	ret_from_sys_call

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

trace_a_syscall:
	jal	syscall_trace

	jalr	s0			# 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

	jal	syscall_trace
	j	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:
	move	s3, ra			# save return address

	lw	t0, PT_R29(sp)		# get old user stack pointer
	subu	s2, s1, 4
	sll	t1, s2, 2		# stack valid?

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

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

	/* Ok, copy the args from the luser stack to the kernel stack */
1:	lw	t1, 20(t0)		# argument #6 from usp
	sw	t1, 20(sp)
2:	lw	t1, 16(t0)		# argument #5 from usp
	sw	t1, 16(sp)

3:	jr	s3			# 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.
	 */
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	ret_from_sys_call

	/*
	 * The system call does not exist in this kernel
	 */
illegal_syscall:
	li	v0, ENOSYS			# error
	sw	v0, PT_R2(sp)
	li	t0, 1				# set error flag
	sw	t0, PT_R7(sp)
	j	ret_from_sys_call

sigill_and_out:
	li	t0, -1				# not a sys call
	REG_S	t0, PT_OR2(sp)
	li	a0, SIGILL
	move	a2, $28
	jal	force_sig
	j	ret_from_sys_call
	END(handle_sys)