diff options
Diffstat (limited to 'arch/sparc/kernel/sys_sparc.c')
-rw-r--r-- | arch/sparc/kernel/sys_sparc.c | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c new file mode 100644 index 000000000000..0cdfc9d294b4 --- /dev/null +++ b/arch/sparc/kernel/sys_sparc.c | |||
@@ -0,0 +1,485 @@ | |||
1 | /* $Id: sys_sparc.c,v 1.70 2001/04/14 01:12:02 davem Exp $ | ||
2 | * linux/arch/sparc/kernel/sys_sparc.c | ||
3 | * | ||
4 | * This file contains various random system calls that | ||
5 | * have a non-standard calling sequence on the Linux/sparc | ||
6 | * platform. | ||
7 | */ | ||
8 | |||
9 | #include <linux/errno.h> | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/file.h> | ||
15 | #include <linux/sem.h> | ||
16 | #include <linux/msg.h> | ||
17 | #include <linux/shm.h> | ||
18 | #include <linux/stat.h> | ||
19 | #include <linux/syscalls.h> | ||
20 | #include <linux/mman.h> | ||
21 | #include <linux/utsname.h> | ||
22 | #include <linux/smp.h> | ||
23 | #include <linux/smp_lock.h> | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/ipc.h> | ||
27 | |||
28 | /* #define DEBUG_UNIMP_SYSCALL */ | ||
29 | |||
30 | /* XXX Make this per-binary type, this way we can detect the type of | ||
31 | * XXX a binary. Every Sparc executable calls this very early on. | ||
32 | */ | ||
33 | asmlinkage unsigned long sys_getpagesize(void) | ||
34 | { | ||
35 | return PAGE_SIZE; /* Possibly older binaries want 8192 on sun4's? */ | ||
36 | } | ||
37 | |||
38 | #define COLOUR_ALIGN(addr) (((addr)+SHMLBA-1)&~(SHMLBA-1)) | ||
39 | |||
40 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) | ||
41 | { | ||
42 | struct vm_area_struct * vmm; | ||
43 | |||
44 | if (flags & MAP_FIXED) { | ||
45 | /* We do not accept a shared mapping if it would violate | ||
46 | * cache aliasing constraints. | ||
47 | */ | ||
48 | if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1))) | ||
49 | return -EINVAL; | ||
50 | return addr; | ||
51 | } | ||
52 | |||
53 | /* See asm-sparc/uaccess.h */ | ||
54 | if (len > TASK_SIZE - PAGE_SIZE) | ||
55 | return -ENOMEM; | ||
56 | if (ARCH_SUN4C_SUN4 && len > 0x20000000) | ||
57 | return -ENOMEM; | ||
58 | if (!addr) | ||
59 | addr = TASK_UNMAPPED_BASE; | ||
60 | |||
61 | if (flags & MAP_SHARED) | ||
62 | addr = COLOUR_ALIGN(addr); | ||
63 | else | ||
64 | addr = PAGE_ALIGN(addr); | ||
65 | |||
66 | for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { | ||
67 | /* At this point: (!vmm || addr < vmm->vm_end). */ | ||
68 | if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) { | ||
69 | addr = PAGE_OFFSET; | ||
70 | vmm = find_vma(current->mm, PAGE_OFFSET); | ||
71 | } | ||
72 | if (TASK_SIZE - PAGE_SIZE - len < addr) | ||
73 | return -ENOMEM; | ||
74 | if (!vmm || addr + len <= vmm->vm_start) | ||
75 | return addr; | ||
76 | addr = vmm->vm_end; | ||
77 | if (flags & MAP_SHARED) | ||
78 | addr = COLOUR_ALIGN(addr); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | asmlinkage unsigned long sparc_brk(unsigned long brk) | ||
83 | { | ||
84 | if(ARCH_SUN4C_SUN4) { | ||
85 | if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000)) | ||
86 | return current->mm->brk; | ||
87 | } | ||
88 | return sys_brk(brk); | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * sys_pipe() is the normal C calling standard for creating | ||
93 | * a pipe. It's not the way unix traditionally does this, though. | ||
94 | */ | ||
95 | asmlinkage int sparc_pipe(struct pt_regs *regs) | ||
96 | { | ||
97 | int fd[2]; | ||
98 | int error; | ||
99 | |||
100 | error = do_pipe(fd); | ||
101 | if (error) | ||
102 | goto out; | ||
103 | regs->u_regs[UREG_I1] = fd[1]; | ||
104 | error = fd[0]; | ||
105 | out: | ||
106 | return error; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * sys_ipc() is the de-multiplexer for the SysV IPC calls.. | ||
111 | * | ||
112 | * This is really horribly ugly. | ||
113 | */ | ||
114 | |||
115 | asmlinkage int sys_ipc (uint call, int first, int second, int third, void __user *ptr, long fifth) | ||
116 | { | ||
117 | int version, err; | ||
118 | |||
119 | version = call >> 16; /* hack for backward compatibility */ | ||
120 | call &= 0xffff; | ||
121 | |||
122 | if (call <= SEMCTL) | ||
123 | switch (call) { | ||
124 | case SEMOP: | ||
125 | err = sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL); | ||
126 | goto out; | ||
127 | case SEMTIMEDOP: | ||
128 | err = sys_semtimedop (first, (struct sembuf __user *)ptr, second, (const struct timespec __user *) fifth); | ||
129 | goto out; | ||
130 | case SEMGET: | ||
131 | err = sys_semget (first, second, third); | ||
132 | goto out; | ||
133 | case SEMCTL: { | ||
134 | union semun fourth; | ||
135 | err = -EINVAL; | ||
136 | if (!ptr) | ||
137 | goto out; | ||
138 | err = -EFAULT; | ||
139 | if (get_user(fourth.__pad, | ||
140 | (void __user * __user *)ptr)) | ||
141 | goto out; | ||
142 | err = sys_semctl (first, second, third, fourth); | ||
143 | goto out; | ||
144 | } | ||
145 | default: | ||
146 | err = -ENOSYS; | ||
147 | goto out; | ||
148 | } | ||
149 | if (call <= MSGCTL) | ||
150 | switch (call) { | ||
151 | case MSGSND: | ||
152 | err = sys_msgsnd (first, (struct msgbuf __user *) ptr, | ||
153 | second, third); | ||
154 | goto out; | ||
155 | case MSGRCV: | ||
156 | switch (version) { | ||
157 | case 0: { | ||
158 | struct ipc_kludge tmp; | ||
159 | err = -EINVAL; | ||
160 | if (!ptr) | ||
161 | goto out; | ||
162 | err = -EFAULT; | ||
163 | if (copy_from_user(&tmp, (struct ipc_kludge __user *) ptr, sizeof (tmp))) | ||
164 | goto out; | ||
165 | err = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); | ||
166 | goto out; | ||
167 | } | ||
168 | case 1: default: | ||
169 | err = sys_msgrcv (first, | ||
170 | (struct msgbuf __user *) ptr, | ||
171 | second, fifth, third); | ||
172 | goto out; | ||
173 | } | ||
174 | case MSGGET: | ||
175 | err = sys_msgget ((key_t) first, second); | ||
176 | goto out; | ||
177 | case MSGCTL: | ||
178 | err = sys_msgctl (first, second, (struct msqid_ds __user *) ptr); | ||
179 | goto out; | ||
180 | default: | ||
181 | err = -ENOSYS; | ||
182 | goto out; | ||
183 | } | ||
184 | if (call <= SHMCTL) | ||
185 | switch (call) { | ||
186 | case SHMAT: | ||
187 | switch (version) { | ||
188 | case 0: default: { | ||
189 | ulong raddr; | ||
190 | err = do_shmat (first, (char __user *) ptr, second, &raddr); | ||
191 | if (err) | ||
192 | goto out; | ||
193 | err = -EFAULT; | ||
194 | if (put_user (raddr, (ulong __user *) third)) | ||
195 | goto out; | ||
196 | err = 0; | ||
197 | goto out; | ||
198 | } | ||
199 | case 1: /* iBCS2 emulator entry point */ | ||
200 | err = -EINVAL; | ||
201 | goto out; | ||
202 | } | ||
203 | case SHMDT: | ||
204 | err = sys_shmdt ((char __user *)ptr); | ||
205 | goto out; | ||
206 | case SHMGET: | ||
207 | err = sys_shmget (first, second, third); | ||
208 | goto out; | ||
209 | case SHMCTL: | ||
210 | err = sys_shmctl (first, second, (struct shmid_ds __user *) ptr); | ||
211 | goto out; | ||
212 | default: | ||
213 | err = -ENOSYS; | ||
214 | goto out; | ||
215 | } | ||
216 | else | ||
217 | err = -ENOSYS; | ||
218 | out: | ||
219 | return err; | ||
220 | } | ||
221 | |||
222 | /* Linux version of mmap */ | ||
223 | static unsigned long do_mmap2(unsigned long addr, unsigned long len, | ||
224 | unsigned long prot, unsigned long flags, unsigned long fd, | ||
225 | unsigned long pgoff) | ||
226 | { | ||
227 | struct file * file = NULL; | ||
228 | unsigned long retval = -EBADF; | ||
229 | |||
230 | if (!(flags & MAP_ANONYMOUS)) { | ||
231 | file = fget(fd); | ||
232 | if (!file) | ||
233 | goto out; | ||
234 | } | ||
235 | |||
236 | retval = -EINVAL; | ||
237 | len = PAGE_ALIGN(len); | ||
238 | if (ARCH_SUN4C_SUN4 && | ||
239 | (len > 0x20000000 || | ||
240 | ((flags & MAP_FIXED) && | ||
241 | addr < 0xe0000000 && addr + len > 0x20000000))) | ||
242 | goto out_putf; | ||
243 | |||
244 | /* See asm-sparc/uaccess.h */ | ||
245 | if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE) | ||
246 | goto out_putf; | ||
247 | |||
248 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
249 | |||
250 | down_write(¤t->mm->mmap_sem); | ||
251 | retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); | ||
252 | up_write(¤t->mm->mmap_sem); | ||
253 | |||
254 | out_putf: | ||
255 | if (file) | ||
256 | fput(file); | ||
257 | out: | ||
258 | return retval; | ||
259 | } | ||
260 | |||
261 | asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len, | ||
262 | unsigned long prot, unsigned long flags, unsigned long fd, | ||
263 | unsigned long pgoff) | ||
264 | { | ||
265 | /* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE | ||
266 | we have. */ | ||
267 | return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12)); | ||
268 | } | ||
269 | |||
270 | asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, | ||
271 | unsigned long prot, unsigned long flags, unsigned long fd, | ||
272 | unsigned long off) | ||
273 | { | ||
274 | return do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT); | ||
275 | } | ||
276 | |||
277 | long sparc_remap_file_pages(unsigned long start, unsigned long size, | ||
278 | unsigned long prot, unsigned long pgoff, | ||
279 | unsigned long flags) | ||
280 | { | ||
281 | /* This works on an existing mmap so we don't need to validate | ||
282 | * the range as that was done at the original mmap call. | ||
283 | */ | ||
284 | return sys_remap_file_pages(start, size, prot, | ||
285 | (pgoff >> (PAGE_SHIFT - 12)), flags); | ||
286 | } | ||
287 | |||
288 | extern unsigned long do_mremap(unsigned long addr, | ||
289 | unsigned long old_len, unsigned long new_len, | ||
290 | unsigned long flags, unsigned long new_addr); | ||
291 | |||
292 | asmlinkage unsigned long sparc_mremap(unsigned long addr, | ||
293 | unsigned long old_len, unsigned long new_len, | ||
294 | unsigned long flags, unsigned long new_addr) | ||
295 | { | ||
296 | struct vm_area_struct *vma; | ||
297 | unsigned long ret = -EINVAL; | ||
298 | if (ARCH_SUN4C_SUN4) { | ||
299 | if (old_len > 0x20000000 || new_len > 0x20000000) | ||
300 | goto out; | ||
301 | if (addr < 0xe0000000 && addr + old_len > 0x20000000) | ||
302 | goto out; | ||
303 | } | ||
304 | if (old_len > TASK_SIZE - PAGE_SIZE || | ||
305 | new_len > TASK_SIZE - PAGE_SIZE) | ||
306 | goto out; | ||
307 | down_write(¤t->mm->mmap_sem); | ||
308 | if (flags & MREMAP_FIXED) { | ||
309 | if (ARCH_SUN4C_SUN4 && | ||
310 | new_addr < 0xe0000000 && | ||
311 | new_addr + new_len > 0x20000000) | ||
312 | goto out_sem; | ||
313 | if (new_addr + new_len > TASK_SIZE - PAGE_SIZE) | ||
314 | goto out_sem; | ||
315 | } else if ((ARCH_SUN4C_SUN4 && addr < 0xe0000000 && | ||
316 | addr + new_len > 0x20000000) || | ||
317 | addr + new_len > TASK_SIZE - PAGE_SIZE) { | ||
318 | unsigned long map_flags = 0; | ||
319 | struct file *file = NULL; | ||
320 | |||
321 | ret = -ENOMEM; | ||
322 | if (!(flags & MREMAP_MAYMOVE)) | ||
323 | goto out_sem; | ||
324 | |||
325 | vma = find_vma(current->mm, addr); | ||
326 | if (vma) { | ||
327 | if (vma->vm_flags & VM_SHARED) | ||
328 | map_flags |= MAP_SHARED; | ||
329 | file = vma->vm_file; | ||
330 | } | ||
331 | |||
332 | new_addr = get_unmapped_area(file, addr, new_len, | ||
333 | vma ? vma->vm_pgoff : 0, | ||
334 | map_flags); | ||
335 | ret = new_addr; | ||
336 | if (new_addr & ~PAGE_MASK) | ||
337 | goto out_sem; | ||
338 | flags |= MREMAP_FIXED; | ||
339 | } | ||
340 | ret = do_mremap(addr, old_len, new_len, flags, new_addr); | ||
341 | out_sem: | ||
342 | up_write(¤t->mm->mmap_sem); | ||
343 | out: | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | /* we come to here via sys_nis_syscall so it can setup the regs argument */ | ||
348 | asmlinkage unsigned long | ||
349 | c_sys_nis_syscall (struct pt_regs *regs) | ||
350 | { | ||
351 | static int count = 0; | ||
352 | |||
353 | if (count++ > 5) | ||
354 | return -ENOSYS; | ||
355 | printk ("%s[%d]: Unimplemented SPARC system call %d\n", | ||
356 | current->comm, current->pid, (int)regs->u_regs[1]); | ||
357 | #ifdef DEBUG_UNIMP_SYSCALL | ||
358 | show_regs (regs); | ||
359 | #endif | ||
360 | return -ENOSYS; | ||
361 | } | ||
362 | |||
363 | /* #define DEBUG_SPARC_BREAKPOINT */ | ||
364 | |||
365 | asmlinkage void | ||
366 | sparc_breakpoint (struct pt_regs *regs) | ||
367 | { | ||
368 | siginfo_t info; | ||
369 | |||
370 | lock_kernel(); | ||
371 | #ifdef DEBUG_SPARC_BREAKPOINT | ||
372 | printk ("TRAP: Entering kernel PC=%x, nPC=%x\n", regs->pc, regs->npc); | ||
373 | #endif | ||
374 | info.si_signo = SIGTRAP; | ||
375 | info.si_errno = 0; | ||
376 | info.si_code = TRAP_BRKPT; | ||
377 | info.si_addr = (void __user *)regs->pc; | ||
378 | info.si_trapno = 0; | ||
379 | force_sig_info(SIGTRAP, &info, current); | ||
380 | |||
381 | #ifdef DEBUG_SPARC_BREAKPOINT | ||
382 | printk ("TRAP: Returning to space: PC=%x nPC=%x\n", regs->pc, regs->npc); | ||
383 | #endif | ||
384 | unlock_kernel(); | ||
385 | } | ||
386 | |||
387 | asmlinkage int | ||
388 | sparc_sigaction (int sig, const struct old_sigaction __user *act, | ||
389 | struct old_sigaction __user *oact) | ||
390 | { | ||
391 | struct k_sigaction new_ka, old_ka; | ||
392 | int ret; | ||
393 | |||
394 | if (sig < 0) { | ||
395 | current->thread.new_signal = 1; | ||
396 | sig = -sig; | ||
397 | } | ||
398 | |||
399 | if (act) { | ||
400 | unsigned long mask; | ||
401 | |||
402 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
403 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
404 | __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) | ||
405 | return -EFAULT; | ||
406 | __get_user(new_ka.sa.sa_flags, &act->sa_flags); | ||
407 | __get_user(mask, &act->sa_mask); | ||
408 | siginitset(&new_ka.sa.sa_mask, mask); | ||
409 | new_ka.ka_restorer = NULL; | ||
410 | } | ||
411 | |||
412 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
413 | |||
414 | if (!ret && oact) { | ||
415 | /* In the clone() case we could copy half consistent | ||
416 | * state to the user, however this could sleep and | ||
417 | * deadlock us if we held the signal lock on SMP. So for | ||
418 | * now I take the easy way out and do no locking. | ||
419 | */ | ||
420 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
421 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
422 | __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) | ||
423 | return -EFAULT; | ||
424 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags); | ||
425 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); | ||
426 | } | ||
427 | |||
428 | return ret; | ||
429 | } | ||
430 | |||
431 | asmlinkage long | ||
432 | sys_rt_sigaction(int sig, | ||
433 | const struct sigaction __user *act, | ||
434 | struct sigaction __user *oact, | ||
435 | void __user *restorer, | ||
436 | size_t sigsetsize) | ||
437 | { | ||
438 | struct k_sigaction new_ka, old_ka; | ||
439 | int ret; | ||
440 | |||
441 | /* XXX: Don't preclude handling different sized sigset_t's. */ | ||
442 | if (sigsetsize != sizeof(sigset_t)) | ||
443 | return -EINVAL; | ||
444 | |||
445 | /* All tasks which use RT signals (effectively) use | ||
446 | * new style signals. | ||
447 | */ | ||
448 | current->thread.new_signal = 1; | ||
449 | |||
450 | if (act) { | ||
451 | new_ka.ka_restorer = restorer; | ||
452 | if (copy_from_user(&new_ka.sa, act, sizeof(*act))) | ||
453 | return -EFAULT; | ||
454 | } | ||
455 | |||
456 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
457 | |||
458 | if (!ret && oact) { | ||
459 | if (copy_to_user(oact, &old_ka.sa, sizeof(*oact))) | ||
460 | return -EFAULT; | ||
461 | } | ||
462 | |||
463 | return ret; | ||
464 | } | ||
465 | |||
466 | asmlinkage int sys_getdomainname(char __user *name, int len) | ||
467 | { | ||
468 | int nlen; | ||
469 | int err = -EFAULT; | ||
470 | |||
471 | down_read(&uts_sem); | ||
472 | |||
473 | nlen = strlen(system_utsname.domainname) + 1; | ||
474 | |||
475 | if (nlen < len) | ||
476 | len = nlen; | ||
477 | if (len > __NEW_UTS_LEN) | ||
478 | goto done; | ||
479 | if (copy_to_user(name, system_utsname.domainname, len)) | ||
480 | goto done; | ||
481 | err = 0; | ||
482 | done: | ||
483 | up_read(&uts_sem); | ||
484 | return err; | ||
485 | } | ||