• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1995, 1996, 1997, 2000, 2001, 05 by Ralf Baechle
7  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
8  * Copyright (C) 2001 MIPS Technologies, Inc.
9  */
10 #include <linux/capability.h>
11 #include <linux/errno.h>
12 #include <linux/linkage.h>
13 #include <linux/fs.h>
14 #include <linux/smp.h>
15 #include <linux/ptrace.h>
16 #include <linux/string.h>
17 #include <linux/syscalls.h>
18 #include <linux/file.h>
19 #include <linux/utsname.h>
20 #include <linux/unistd.h>
21 #include <linux/sem.h>
22 #include <linux/msg.h>
23 #include <linux/shm.h>
24 #include <linux/compiler.h>
25 #include <linux/ipc.h>
26 #include <linux/uaccess.h>
27 #include <linux/slab.h>
28 #include <linux/elf.h>
29 #include <linux/prctl.h>
30 
31 #include <asm/asm.h>
32 #include <asm/branch.h>
33 #include <asm/cachectl.h>
34 #include <asm/cacheflush.h>
35 #include <asm/asm-offsets.h>
36 #include <asm/signal.h>
37 #include <asm/sim.h>
38 #include <asm/shmparam.h>
39 #include <asm/sysmips.h>
40 #include <asm/uaccess.h>
41 #include <asm/switch_to.h>
42 #include <asm/fpu.h>
43 
44 extern unsigned int system_has_fpu;
45 extern unsigned int global_fpu_id;
46 /*
47  * For historic reasons the pipe(2) syscall on MIPS has an unusual calling
48  * convention.	It returns results in registers $v0 / $v1 which means there
49  * is no need for it to do verify the validity of a userspace pointer
50  * argument.  Historically that used to be expensive in Linux.	These days
51  * the performance advantage is negligible.
52  */
sysm_pipe(void)53 asmlinkage int sysm_pipe(void)
54 {
55 	int fd[2];
56 	int error = do_pipe_flags(fd, 0);
57 	if (error)
58 		return error;
59 	current_pt_regs()->regs[3] = fd[1];
60 	return fd[0];
61 }
62 
SYSCALL_DEFINE6(mips_mmap,unsigned long,addr,unsigned long,len,unsigned long,prot,unsigned long,flags,unsigned long,fd,off_t,offset)63 SYSCALL_DEFINE6(mips_mmap, unsigned long, addr, unsigned long, len,
64 	unsigned long, prot, unsigned long, flags, unsigned long,
65 	fd, off_t, offset)
66 {
67 	unsigned long result;
68 
69 	result = -EINVAL;
70 	if (offset & ~PAGE_MASK)
71 		goto out;
72 
73 	result = sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
74 
75 out:
76 	return result;
77 }
78 
SYSCALL_DEFINE6(mips_mmap2,unsigned long,addr,unsigned long,len,unsigned long,prot,unsigned long,flags,unsigned long,fd,unsigned long,pgoff)79 SYSCALL_DEFINE6(mips_mmap2, unsigned long, addr, unsigned long, len,
80 	unsigned long, prot, unsigned long, flags, unsigned long, fd,
81 	unsigned long, pgoff)
82 {
83 	if (pgoff & (~PAGE_MASK >> 12))
84 		return -EINVAL;
85 
86 	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12));
87 }
88 
89 save_static_function(sys_fork);
90 save_static_function(sys_clone);
91 
SYSCALL_DEFINE1(set_thread_area,unsigned long,addr)92 SYSCALL_DEFINE1(set_thread_area, unsigned long, addr)
93 {
94 	struct thread_info *ti = task_thread_info(current);
95 
96 	ti->tp_value = addr;
97 	if (cpu_has_userlocal)
98 		write_c0_userlocal(addr);
99 
100 	return 0;
101 }
102 
mips_atomic_set(unsigned long addr,unsigned long new)103 static inline int mips_atomic_set(unsigned long addr, unsigned long new)
104 {
105 	unsigned long old, tmp;
106 	struct pt_regs *regs;
107 	unsigned int err;
108 
109 	if (unlikely(addr & 3))
110 		return -EINVAL;
111 
112 	if (unlikely(!access_ok(VERIFY_WRITE, addr, 4)))
113 		return -EINVAL;
114 
115 	if (cpu_has_llsc && R10000_LLSC_WAR) {
116 		__asm__ __volatile__ (
117 		"	.set	mips3					\n"
118 		"	li	%[err], 0				\n"
119 		"1:	ll	%[old], (%[addr])			\n"
120 		"	move	%[tmp], %[new]				\n"
121 		"2:	sc	%[tmp], (%[addr])			\n"
122 		"	beqzl	%[tmp], 1b				\n"
123 		"3:							\n"
124 		"	.section .fixup,\"ax\"				\n"
125 		"4:	li	%[err], %[efault]			\n"
126 		"	j	3b					\n"
127 		"	.previous					\n"
128 		"	.section __ex_table,\"a\"			\n"
129 		"	"STR(PTR)"	1b, 4b				\n"
130 		"	"STR(PTR)"	2b, 4b				\n"
131 		"	.previous					\n"
132 		"	.set	mips0					\n"
133 		: [old] "=&r" (old),
134 		  [err] "=&r" (err),
135 		  [tmp] "=&r" (tmp)
136 		: [addr] "r" (addr),
137 		  [new] "r" (new),
138 		  [efault] "i" (-EFAULT)
139 		: "memory");
140 	} else if (cpu_has_llsc) {
141 		__asm__ __volatile__ (
142 #ifdef CONFIG_CPU_MIPSR6
143 		"       .set    mips64r6                                \n"
144 #else
145 		"       .set    mips3                                   \n"
146 #endif
147 		"       li      %[err], 0                               \n"
148 		"1:	ll	%[old], (%[addr])			\n"
149 		"	move	%[tmp], %[new]				\n"
150 		"2:	sc	%[tmp], (%[addr])			\n"
151 		"	bnez	%[tmp], 4f				\n"
152 		"3:							\n"
153 		"	.subsection 2					\n"
154 		"4:	b	1b					\n"
155 		"	.previous					\n"
156 		"							\n"
157 		"	.section .fixup,\"ax\"				\n"
158 		"5:	li	%[err], %[efault]			\n"
159 		"	j	3b					\n"
160 		"	.previous					\n"
161 		"	.section __ex_table,\"a\"			\n"
162 		"	"STR(PTR)"	1b, 5b				\n"
163 		"	"STR(PTR)"	2b, 5b				\n"
164 		"	.previous					\n"
165 		"	.set	mips0					\n"
166 		: [old] "=&r" (old),
167 		  [err] "=&r" (err),
168 		  [tmp] "=&r" (tmp)
169 		: [addr] "r" (addr),
170 		  [new] "r" (new),
171 		  [efault] "i" (-EFAULT)
172 		: "memory");
173 	} else {
174 		do {
175 			preempt_disable();
176 			ll_bit = 1;
177 			ll_task = current;
178 			preempt_enable();
179 
180 			err = __get_user(old, (unsigned int *) addr);
181 			err |= __put_user(new, (unsigned int *) addr);
182 			if (err)
183 				break;
184 			rmb();
185 		} while (!ll_bit);
186 	}
187 
188 	if (unlikely(err))
189 		return err;
190 
191 	regs = current_pt_regs();
192 	regs->regs[2] = old;
193 	regs->regs[7] = 0;	/* No error */
194 
195 	/*
196 	 * Don't let your children do this ...
197 	 */
198 	__asm__ __volatile__(
199 	"	move	$29, %0						\n"
200 	"	j	syscall_exit					\n"
201 	: /* no outputs */
202 	: "r" (regs));
203 
204 	/* unreached.  Honestly.  */
205 	unreachable();
206 }
207 
mips_lose_fpu(void)208 asmlinkage void mips_lose_fpu(void)
209 {
210 	preempt_disable();
211 	clear_thread_flag(TIF_FPU_LOSE_REQUEST);
212 	lose_fpu_inatomic(1);
213 	preempt_enable_no_resched();
214 }
215 
mips_switch_fpu_mode(void * info)216 void mips_switch_fpu_mode(void *info)
217 {
218 	struct mm_struct *mm = info;
219 
220 	if ((current->mm == mm) && (is_fpu_owner() || is_msa_enabled()))
221 		set_thread_flag(TIF_FPU_LOSE_REQUEST);
222 }
223 
mips_fpu_prctl(unsigned long type,unsigned long param)224 unsigned int mips_fpu_prctl(unsigned long type, unsigned long param)
225 {
226 	register unsigned long val;
227 	register unsigned long mask;
228 	register unsigned long *addr;
229 
230 	switch (type) {
231 	case PR_SET_FP_MODE:
232 		if (param & ~(PR_FP_MODE_FR|PR_FP_MODE_FRE))
233 			return -EINVAL;
234 
235 #ifndef CONFIG_MIPS_INCOMPATIBLE_ARCH_EMULATION
236 		if (system_has_fpu) {
237 			if ((param & PR_FP_MODE_FRE) && !cpu_has_fre)
238 				return -EOPNOTSUPP;
239 			if ((param & PR_FP_MODE_FR) && !(global_fpu_id & MIPS_FPIR_F64))
240 				return -EOPNOTSUPP;
241 			if (!(param & PR_FP_MODE_FR)) {
242 				unsigned int res;
243 				unsigned int status;
244 
245 				/* test FPU with FR0 capability */
246 				local_irq_disable();
247 				status = change_c0_status(ST0_FR|ST0_CU1, ST0_CU1);
248 				enable_fpu_hazard();
249 				res = read_c0_status();
250 				write_c0_status(status);
251 				disable_fpu_hazard();
252 				local_irq_enable();
253 				if (res & ST0_FR)
254 					return -EOPNOTSUPP;
255 			}
256 		}
257 #endif
258 		val = (param & PR_FP_MODE_FR)? LTIF_FPU_FR : 0;
259 		val |= (param & PR_FP_MODE_FRE)? LTIF_FPU_FRE : 0;
260 		mask = ~(LTIF_FPU_FR|LTIF_FPU_FRE);
261 		preempt_disable();
262 		addr = &(current->mm->context.thread_flags);
263 		__asm__ __volatile__(
264 			".set push                      \n"
265 			".set noreorder                 \n"
266 			".set noat                      \n"
267 #ifdef CONFIG_64BIT
268 			"1: lld     $1, 0(%0)           \n"
269 			"   and     $1, $1, %1          \n"
270 			"   or      $1, $1, %2          \n"
271 			"   scd     $1, 0(%0)           \n"
272 #else
273 			"1: ll      $1, 0(%0)           \n"
274 			"   and     $1, $1, %1          \n"
275 			"   or      $1, $1, %2          \n"
276 			"   sc      $1, 0(%0)           \n"
277 #endif
278 			"   beqz    $1, 1b              \n"
279 			"     nop                       \n"
280 			".set pop                       \n"
281 			:
282 			: "r"(addr), "r"(mask), "r"(val)
283 			: "memory");
284 		smp_llsc_mb();
285 		/* send a "barrier" for FPU mode - force other CPUs to lose FPU */
286 		if (atomic_read(&current->mm->mm_users) != 1)
287 			smp_call_function(mips_switch_fpu_mode, (void *)(current->mm), 1);
288 		lose_fpu(1);
289 		preempt_enable();
290 		break;
291 
292 	case PR_GET_FP_MODE:
293 		if (unlikely(param & 3))
294 			return -EINVAL;
295 
296 		if (unlikely(!access_ok(VERIFY_WRITE, param, 4)))
297 			return -EINVAL;
298 
299 		val = (current_thread_info()->local_flags & LTIF_FPU_FR)? PR_FP_MODE_FR : 0;
300 		val |= (current_thread_info()->local_flags & LTIF_FPU_FRE)? PR_FP_MODE_FRE : 0;
301 		if (put_user(val, (unsigned long *)param))
302 			return -EINVAL;
303 		break;
304 
305 	default:
306 		return -EINVAL;
307 	}
308 
309 	return 0;
310 }
311 
mips_get_process_fp_mode(struct task_struct * task)312 long mips_get_process_fp_mode(struct task_struct *task)
313 {
314 	unsigned long val;
315 
316 	val = (((struct thread_info *)task_stack_page(task))->local_flags & LTIF_FPU_FR)? PR_FP_MODE_FR : 0;
317 	val |= (((struct thread_info *)task_stack_page(task))->local_flags & LTIF_FPU_FRE)? PR_FP_MODE_FRE : 0;
318 
319 	return val;
320 }
321 
mips_set_process_fp_mode(struct task_struct * task,unsigned long value)322 long mips_set_process_fp_mode(struct task_struct *task,
323 				    unsigned long value)
324 {
325 	return mips_fpu_prctl(PR_SET_FP_MODE, value);
326 }
327 
SYSCALL_DEFINE3(sysmips,long,cmd,long,arg1,long,arg2)328 SYSCALL_DEFINE3(sysmips, long, cmd, long, arg1, long, arg2)
329 {
330 	switch (cmd) {
331 	case MIPS_ATOMIC_SET:
332 		return mips_atomic_set(arg1, arg2);
333 
334 	case MIPS_FIXADE:
335 		if (arg1 & ~3)
336 			return -EINVAL;
337 
338 		if (arg1 & 1)
339 			set_thread_flag(TIF_FIXADE);
340 		else
341 			clear_thread_flag(TIF_FIXADE);
342 		if (arg1 & 2)
343 			set_thread_flag(TIF_LOGADE);
344 		else
345 			clear_thread_flag(TIF_LOGADE);
346 
347 		return 0;
348 
349 	case FLUSH_CACHE:
350 		__flush_cache_all();
351 		return 0;
352 
353 	case MIPS_FPU_PRCTL:
354 		return mips_fpu_prctl(arg1, arg2);
355 	}
356 
357 	return -EINVAL;
358 }
359 
360 /*
361  * No implemented yet ...
362  */
SYSCALL_DEFINE3(cachectl,char *,addr,int,nbytes,int,op)363 SYSCALL_DEFINE3(cachectl, char *, addr, int, nbytes, int, op)
364 {
365 	return -ENOSYS;
366 }
367 
368 /*
369  * If we ever come here the user sp is bad.  Zap the process right away.
370  * Due to the bad stack signaling wouldn't work.
371  */
bad_stack(void)372 asmlinkage void bad_stack(void)
373 {
374 	do_exit(SIGSEGV);
375 }
376