Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | */ | |
a9415644 | 10 | #include <linux/capability.h> |
1da177e4 LT |
11 | #include <linux/errno.h> |
12 | #include <linux/linkage.h> | |
13 | #include <linux/mm.h> | |
4e950f6f | 14 | #include <linux/fs.h> |
1da177e4 | 15 | #include <linux/smp.h> |
1da177e4 LT |
16 | #include <linux/mman.h> |
17 | #include <linux/ptrace.h> | |
18 | #include <linux/sched.h> | |
19 | #include <linux/string.h> | |
20 | #include <linux/syscalls.h> | |
21 | #include <linux/file.h> | |
1da177e4 LT |
22 | #include <linux/utsname.h> |
23 | #include <linux/unistd.h> | |
24 | #include <linux/sem.h> | |
25 | #include <linux/msg.h> | |
26 | #include <linux/shm.h> | |
27 | #include <linux/compiler.h> | |
9ff77c46 | 28 | #include <linux/module.h> |
cba4fbbf | 29 | #include <linux/ipc.h> |
f1e39a4a | 30 | #include <linux/uaccess.h> |
5a0e3ad6 | 31 | #include <linux/slab.h> |
1091458d | 32 | #include <linux/random.h> |
1da177e4 | 33 | |
f1e39a4a | 34 | #include <asm/asm.h> |
1da177e4 LT |
35 | #include <asm/branch.h> |
36 | #include <asm/cachectl.h> | |
37 | #include <asm/cacheflush.h> | |
048eb582 | 38 | #include <asm/asm-offsets.h> |
1da177e4 LT |
39 | #include <asm/signal.h> |
40 | #include <asm/sim.h> | |
41 | #include <asm/shmparam.h> | |
42 | #include <asm/sysmips.h> | |
43 | #include <asm/uaccess.h> | |
44 | ||
8213bbf9 RB |
45 | /* |
46 | * For historic reasons the pipe(2) syscall on MIPS has an unusual calling | |
47 | * convention. It returns results in registers $v0 / $v1 which means there | |
48 | * is no need for it to do verify the validity of a userspace pointer | |
49 | * argument. Historically that used to be expensive in Linux. These days | |
50 | * the performance advantage is negligible. | |
51 | */ | |
52 | asmlinkage int sysm_pipe(nabi_no_regargs volatile struct pt_regs regs) | |
1da177e4 LT |
53 | { |
54 | int fd[2]; | |
55 | int error, res; | |
56 | ||
ed8cae8b | 57 | error = do_pipe_flags(fd, 0); |
1da177e4 LT |
58 | if (error) { |
59 | res = error; | |
60 | goto out; | |
61 | } | |
62 | regs.regs[3] = fd[1]; | |
63 | res = fd[0]; | |
64 | out: | |
65 | return res; | |
66 | } | |
67 | ||
68 | unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ | |
69 | ||
9ff77c46 RB |
70 | EXPORT_SYMBOL(shm_align_mask); |
71 | ||
1da177e4 LT |
72 | #define COLOUR_ALIGN(addr,pgoff) \ |
73 | ((((addr) + shm_align_mask) & ~shm_align_mask) + \ | |
74 | (((pgoff) << PAGE_SHIFT) & shm_align_mask)) | |
75 | ||
76 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | |
77 | unsigned long len, unsigned long pgoff, unsigned long flags) | |
78 | { | |
79 | struct vm_area_struct * vmm; | |
80 | int do_color_align; | |
81 | unsigned long task_size; | |
82 | ||
c52d0d30 DD |
83 | #ifdef CONFIG_32BIT |
84 | task_size = TASK_SIZE; | |
85 | #else /* Must be CONFIG_64BIT*/ | |
86 | task_size = test_thread_flag(TIF_32BIT_ADDR) ? TASK_SIZE32 : TASK_SIZE; | |
87 | #endif | |
1da177e4 | 88 | |
098362e7 DD |
89 | if (len > task_size) |
90 | return -ENOMEM; | |
91 | ||
1da177e4 | 92 | if (flags & MAP_FIXED) { |
098362e7 DD |
93 | /* Even MAP_FIXED mappings must reside within task_size. */ |
94 | if (task_size - len < addr) | |
95 | return -EINVAL; | |
96 | ||
1da177e4 LT |
97 | /* |
98 | * We do not accept a shared mapping if it would violate | |
99 | * cache aliasing constraints. | |
100 | */ | |
e77414e0 AV |
101 | if ((flags & MAP_SHARED) && |
102 | ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask)) | |
1da177e4 LT |
103 | return -EINVAL; |
104 | return addr; | |
105 | } | |
106 | ||
1da177e4 LT |
107 | do_color_align = 0; |
108 | if (filp || (flags & MAP_SHARED)) | |
109 | do_color_align = 1; | |
110 | if (addr) { | |
111 | if (do_color_align) | |
112 | addr = COLOUR_ALIGN(addr, pgoff); | |
113 | else | |
114 | addr = PAGE_ALIGN(addr); | |
115 | vmm = find_vma(current->mm, addr); | |
116 | if (task_size - len >= addr && | |
117 | (!vmm || addr + len <= vmm->vm_start)) | |
118 | return addr; | |
119 | } | |
1091458d | 120 | addr = current->mm->mmap_base; |
1da177e4 LT |
121 | if (do_color_align) |
122 | addr = COLOUR_ALIGN(addr, pgoff); | |
123 | else | |
124 | addr = PAGE_ALIGN(addr); | |
125 | ||
126 | for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { | |
127 | /* At this point: (!vmm || addr < vmm->vm_end). */ | |
128 | if (task_size - len < addr) | |
129 | return -ENOMEM; | |
130 | if (!vmm || addr + len <= vmm->vm_start) | |
131 | return addr; | |
132 | addr = vmm->vm_end; | |
133 | if (do_color_align) | |
134 | addr = COLOUR_ALIGN(addr, pgoff); | |
135 | } | |
136 | } | |
137 | ||
1091458d DD |
138 | void arch_pick_mmap_layout(struct mm_struct *mm) |
139 | { | |
140 | unsigned long random_factor = 0UL; | |
141 | ||
142 | if (current->flags & PF_RANDOMIZE) { | |
143 | random_factor = get_random_int(); | |
144 | random_factor = random_factor << PAGE_SHIFT; | |
145 | if (TASK_IS_32BIT_ADDR) | |
146 | random_factor &= 0xfffffful; | |
147 | else | |
148 | random_factor &= 0xffffffful; | |
149 | } | |
150 | ||
151 | mm->mmap_base = TASK_UNMAPPED_BASE + random_factor; | |
152 | mm->get_unmapped_area = arch_get_unmapped_area; | |
153 | mm->unmap_area = arch_unmap_area; | |
154 | } | |
155 | ||
dbda6ac0 RB |
156 | SYSCALL_DEFINE6(mips_mmap, unsigned long, addr, unsigned long, len, |
157 | unsigned long, prot, unsigned long, flags, unsigned long, | |
158 | fd, off_t, offset) | |
1da177e4 LT |
159 | { |
160 | unsigned long result; | |
161 | ||
162 | result = -EINVAL; | |
163 | if (offset & ~PAGE_MASK) | |
164 | goto out; | |
165 | ||
f8b72560 | 166 | result = sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); |
1da177e4 LT |
167 | |
168 | out: | |
169 | return result; | |
170 | } | |
171 | ||
dbda6ac0 RB |
172 | SYSCALL_DEFINE6(mips_mmap2, unsigned long, addr, unsigned long, len, |
173 | unsigned long, prot, unsigned long, flags, unsigned long, fd, | |
174 | unsigned long, pgoff) | |
1da177e4 | 175 | { |
947df17c PA |
176 | if (pgoff & (~PAGE_MASK >> 12)) |
177 | return -EINVAL; | |
178 | ||
f8b72560 | 179 | return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12)); |
1da177e4 LT |
180 | } |
181 | ||
182 | save_static_function(sys_fork); | |
f5dbeaf5 | 183 | static int __used noinline |
1da177e4 LT |
184 | _sys_fork(nabi_no_regargs struct pt_regs regs) |
185 | { | |
186 | return do_fork(SIGCHLD, regs.regs[29], ®s, 0, NULL, NULL); | |
187 | } | |
188 | ||
189 | save_static_function(sys_clone); | |
f5dbeaf5 | 190 | static int __used noinline |
1da177e4 LT |
191 | _sys_clone(nabi_no_regargs struct pt_regs regs) |
192 | { | |
193 | unsigned long clone_flags; | |
194 | unsigned long newsp; | |
3c37026d | 195 | int __user *parent_tidptr, *child_tidptr; |
1da177e4 LT |
196 | |
197 | clone_flags = regs.regs[4]; | |
198 | newsp = regs.regs[5]; | |
199 | if (!newsp) | |
200 | newsp = regs.regs[29]; | |
3c37026d RB |
201 | parent_tidptr = (int __user *) regs.regs[6]; |
202 | #ifdef CONFIG_32BIT | |
203 | /* We need to fetch the fifth argument off the stack. */ | |
204 | child_tidptr = NULL; | |
205 | if (clone_flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) { | |
206 | int __user *__user *usp = (int __user *__user *) regs.regs[29]; | |
207 | if (regs.regs[2] == __NR_syscall) { | |
208 | if (get_user (child_tidptr, &usp[5])) | |
209 | return -EFAULT; | |
210 | } | |
211 | else if (get_user (child_tidptr, &usp[4])) | |
212 | return -EFAULT; | |
213 | } | |
214 | #else | |
215 | child_tidptr = (int __user *) regs.regs[8]; | |
216 | #endif | |
1da177e4 LT |
217 | return do_fork(clone_flags, newsp, ®s, 0, |
218 | parent_tidptr, child_tidptr); | |
219 | } | |
220 | ||
221 | /* | |
222 | * sys_execve() executes a new program. | |
223 | */ | |
224 | asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs) | |
225 | { | |
226 | int error; | |
227 | char * filename; | |
228 | ||
be6e518b | 229 | filename = getname((char __user *) (long)regs.regs[4]); |
1da177e4 LT |
230 | error = PTR_ERR(filename); |
231 | if (IS_ERR(filename)) | |
232 | goto out; | |
be6e518b AN |
233 | error = do_execve(filename, (char __user *__user *) (long)regs.regs[5], |
234 | (char __user *__user *) (long)regs.regs[6], ®s); | |
1da177e4 LT |
235 | putname(filename); |
236 | ||
237 | out: | |
238 | return error; | |
239 | } | |
240 | ||
dbda6ac0 | 241 | SYSCALL_DEFINE1(set_thread_area, unsigned long, addr) |
3c37026d | 242 | { |
dc8f6029 | 243 | struct thread_info *ti = task_thread_info(current); |
3c37026d RB |
244 | |
245 | ti->tp_value = addr; | |
a3692020 RB |
246 | if (cpu_has_userlocal) |
247 | write_c0_userlocal(addr); | |
06be375b RB |
248 | |
249 | return 0; | |
3c37026d RB |
250 | } |
251 | ||
f1e39a4a RB |
252 | static inline int mips_atomic_set(struct pt_regs *regs, |
253 | unsigned long addr, unsigned long new) | |
1da177e4 | 254 | { |
f1e39a4a RB |
255 | unsigned long old, tmp; |
256 | unsigned int err; | |
257 | ||
258 | if (unlikely(addr & 3)) | |
259 | return -EINVAL; | |
260 | ||
261 | if (unlikely(!access_ok(VERIFY_WRITE, addr, 4))) | |
262 | return -EINVAL; | |
263 | ||
264 | if (cpu_has_llsc && R10000_LLSC_WAR) { | |
265 | __asm__ __volatile__ ( | |
a91be9ee | 266 | " .set mips3 \n" |
f1e39a4a RB |
267 | " li %[err], 0 \n" |
268 | "1: ll %[old], (%[addr]) \n" | |
269 | " move %[tmp], %[new] \n" | |
270 | "2: sc %[tmp], (%[addr]) \n" | |
271 | " beqzl %[tmp], 1b \n" | |
272 | "3: \n" | |
273 | " .section .fixup,\"ax\" \n" | |
274 | "4: li %[err], %[efault] \n" | |
275 | " j 3b \n" | |
276 | " .previous \n" | |
277 | " .section __ex_table,\"a\" \n" | |
278 | " "STR(PTR)" 1b, 4b \n" | |
279 | " "STR(PTR)" 2b, 4b \n" | |
280 | " .previous \n" | |
a91be9ee | 281 | " .set mips0 \n" |
f1e39a4a RB |
282 | : [old] "=&r" (old), |
283 | [err] "=&r" (err), | |
284 | [tmp] "=&r" (tmp) | |
285 | : [addr] "r" (addr), | |
286 | [new] "r" (new), | |
287 | [efault] "i" (-EFAULT) | |
288 | : "memory"); | |
289 | } else if (cpu_has_llsc) { | |
290 | __asm__ __volatile__ ( | |
a91be9ee | 291 | " .set mips3 \n" |
f1e39a4a RB |
292 | " li %[err], 0 \n" |
293 | "1: ll %[old], (%[addr]) \n" | |
294 | " move %[tmp], %[new] \n" | |
295 | "2: sc %[tmp], (%[addr]) \n" | |
296 | " bnez %[tmp], 4f \n" | |
297 | "3: \n" | |
298 | " .subsection 2 \n" | |
299 | "4: b 1b \n" | |
300 | " .previous \n" | |
301 | " \n" | |
302 | " .section .fixup,\"ax\" \n" | |
303 | "5: li %[err], %[efault] \n" | |
304 | " j 3b \n" | |
305 | " .previous \n" | |
306 | " .section __ex_table,\"a\" \n" | |
307 | " "STR(PTR)" 1b, 5b \n" | |
308 | " "STR(PTR)" 2b, 5b \n" | |
309 | " .previous \n" | |
a91be9ee | 310 | " .set mips0 \n" |
f1e39a4a RB |
311 | : [old] "=&r" (old), |
312 | [err] "=&r" (err), | |
313 | [tmp] "=&r" (tmp) | |
314 | : [addr] "r" (addr), | |
315 | [new] "r" (new), | |
316 | [efault] "i" (-EFAULT) | |
317 | : "memory"); | |
318 | } else { | |
319 | do { | |
320 | preempt_disable(); | |
321 | ll_bit = 1; | |
322 | ll_task = current; | |
323 | preempt_enable(); | |
324 | ||
325 | err = __get_user(old, (unsigned int *) addr); | |
326 | err |= __put_user(new, (unsigned int *) addr); | |
327 | if (err) | |
328 | break; | |
329 | rmb(); | |
330 | } while (!ll_bit); | |
331 | } | |
332 | ||
333 | if (unlikely(err)) | |
334 | return err; | |
335 | ||
336 | regs->regs[2] = old; | |
337 | regs->regs[7] = 0; /* No error */ | |
338 | ||
339 | /* | |
340 | * Don't let your children do this ... | |
341 | */ | |
342 | __asm__ __volatile__( | |
343 | " move $29, %0 \n" | |
344 | " j syscall_exit \n" | |
345 | : /* no outputs */ | |
346 | : "r" (regs)); | |
347 | ||
348 | /* unreached. Honestly. */ | |
349 | while (1); | |
350 | } | |
351 | ||
352 | save_static_function(sys_sysmips); | |
353 | static int __used noinline | |
354 | _sys_sysmips(nabi_no_regargs struct pt_regs regs) | |
355 | { | |
356 | long cmd, arg1, arg2, arg3; | |
357 | ||
358 | cmd = regs.regs[4]; | |
359 | arg1 = regs.regs[5]; | |
360 | arg2 = regs.regs[6]; | |
361 | arg3 = regs.regs[7]; | |
362 | ||
293c5bd1 | 363 | switch (cmd) { |
1da177e4 | 364 | case MIPS_ATOMIC_SET: |
f1e39a4a | 365 | return mips_atomic_set(®s, arg1, arg2); |
1da177e4 LT |
366 | |
367 | case MIPS_FIXADE: | |
293c5bd1 RB |
368 | if (arg1 & ~3) |
369 | return -EINVAL; | |
370 | ||
371 | if (arg1 & 1) | |
372 | set_thread_flag(TIF_FIXADE); | |
373 | else | |
374 | clear_thread_flag(TIF_FIXADE); | |
375 | if (arg1 & 2) | |
376 | set_thread_flag(TIF_LOGADE); | |
377 | else | |
378 | clear_thread_flag(TIF_FIXADE); | |
379 | ||
1da177e4 LT |
380 | return 0; |
381 | ||
382 | case FLUSH_CACHE: | |
383 | __flush_cache_all(); | |
384 | return 0; | |
1da177e4 LT |
385 | } |
386 | ||
387 | return -EINVAL; | |
388 | } | |
389 | ||
1da177e4 LT |
390 | /* |
391 | * No implemented yet ... | |
392 | */ | |
dbda6ac0 | 393 | SYSCALL_DEFINE3(cachectl, char *, addr, int, nbytes, int, op) |
1da177e4 LT |
394 | { |
395 | return -ENOSYS; | |
396 | } | |
397 | ||
398 | /* | |
399 | * If we ever come here the user sp is bad. Zap the process right away. | |
400 | * Due to the bad stack signaling wouldn't work. | |
401 | */ | |
402 | asmlinkage void bad_stack(void) | |
403 | { | |
404 | do_exit(SIGSEGV); | |
405 | } | |
fe74290d AB |
406 | |
407 | /* | |
408 | * Do a system call from kernel instead of calling sys_execve so we | |
409 | * end up with proper pt_regs. | |
410 | */ | |
411 | int kernel_execve(const char *filename, char *const argv[], char *const envp[]) | |
412 | { | |
413 | register unsigned long __a0 asm("$4") = (unsigned long) filename; | |
414 | register unsigned long __a1 asm("$5") = (unsigned long) argv; | |
415 | register unsigned long __a2 asm("$6") = (unsigned long) envp; | |
416 | register unsigned long __a3 asm("$7"); | |
417 | unsigned long __v0; | |
418 | ||
419 | __asm__ volatile (" \n" | |
420 | " .set noreorder \n" | |
421 | " li $2, %5 # __NR_execve \n" | |
422 | " syscall \n" | |
423 | " move %0, $2 \n" | |
424 | " .set reorder \n" | |
425 | : "=&r" (__v0), "=r" (__a3) | |
426 | : "r" (__a0), "r" (__a1), "r" (__a2), "i" (__NR_execve) | |
427 | : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", | |
428 | "memory"); | |
429 | ||
430 | if (__a3 == 0) | |
431 | return __v0; | |
432 | ||
433 | return -__v0; | |
434 | } |