xref: /openbmc/linux/arch/arm/kernel/sys_arm.c (revision 33fa9b13285e76fb95d940120964562e4c7081c2)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/arch/arm/kernel/sys_arm.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
51da177e4SLinus Torvalds  *  Copyright (C) 1995, 1996 Russell King.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
81da177e4SLinus Torvalds  * it under the terms of the GNU General Public License version 2 as
91da177e4SLinus Torvalds  * published by the Free Software Foundation.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *  This file contains various random system calls that
121da177e4SLinus Torvalds  *  have a non-standard calling sequence on the Linux/arm
131da177e4SLinus Torvalds  *  platform.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/sched.h>
181da177e4SLinus Torvalds #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/mm.h>
201da177e4SLinus Torvalds #include <linux/sem.h>
211da177e4SLinus Torvalds #include <linux/msg.h>
221da177e4SLinus Torvalds #include <linux/shm.h>
231da177e4SLinus Torvalds #include <linux/stat.h>
241da177e4SLinus Torvalds #include <linux/syscalls.h>
251da177e4SLinus Torvalds #include <linux/mman.h>
261da177e4SLinus Torvalds #include <linux/fs.h>
271da177e4SLinus Torvalds #include <linux/file.h>
281da177e4SLinus Torvalds #include <linux/utsname.h>
29cba4fbbfSAdrian Bunk #include <linux/ipc.h>
30*33fa9b13SRussell King #include <linux/uaccess.h>
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
331da177e4SLinus Torvalds 			       unsigned long new_len, unsigned long flags,
341da177e4SLinus Torvalds 			       unsigned long new_addr);
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds /* common code for old and new mmaps */
371da177e4SLinus Torvalds inline long do_mmap2(
381da177e4SLinus Torvalds 	unsigned long addr, unsigned long len,
391da177e4SLinus Torvalds 	unsigned long prot, unsigned long flags,
401da177e4SLinus Torvalds 	unsigned long fd, unsigned long pgoff)
411da177e4SLinus Torvalds {
421da177e4SLinus Torvalds 	int error = -EINVAL;
431da177e4SLinus Torvalds 	struct file * file = NULL;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds 	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
461da177e4SLinus Torvalds 
476119be0bSHugh Dickins 	if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
481da177e4SLinus Torvalds 		goto out;
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	error = -EBADF;
511da177e4SLinus Torvalds 	if (!(flags & MAP_ANONYMOUS)) {
521da177e4SLinus Torvalds 		file = fget(fd);
531da177e4SLinus Torvalds 		if (!file)
541da177e4SLinus Torvalds 			goto out;
551da177e4SLinus Torvalds 	}
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	down_write(&current->mm->mmap_sem);
581da177e4SLinus Torvalds 	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
591da177e4SLinus Torvalds 	up_write(&current->mm->mmap_sem);
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	if (file)
621da177e4SLinus Torvalds 		fput(file);
631da177e4SLinus Torvalds out:
641da177e4SLinus Torvalds 	return error;
651da177e4SLinus Torvalds }
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds struct mmap_arg_struct {
681da177e4SLinus Torvalds 	unsigned long addr;
691da177e4SLinus Torvalds 	unsigned long len;
701da177e4SLinus Torvalds 	unsigned long prot;
711da177e4SLinus Torvalds 	unsigned long flags;
721da177e4SLinus Torvalds 	unsigned long fd;
731da177e4SLinus Torvalds 	unsigned long offset;
741da177e4SLinus Torvalds };
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	int error = -EFAULT;
791da177e4SLinus Torvalds 	struct mmap_arg_struct a;
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	if (copy_from_user(&a, arg, sizeof(a)))
821da177e4SLinus Torvalds 		goto out;
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 	error = -EINVAL;
851da177e4SLinus Torvalds 	if (a.offset & ~PAGE_MASK)
861da177e4SLinus Torvalds 		goto out;
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
891da177e4SLinus Torvalds out:
901da177e4SLinus Torvalds 	return error;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds asmlinkage unsigned long
941da177e4SLinus Torvalds sys_arm_mremap(unsigned long addr, unsigned long old_len,
951da177e4SLinus Torvalds 	       unsigned long new_len, unsigned long flags,
961da177e4SLinus Torvalds 	       unsigned long new_addr)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	unsigned long ret = -EINVAL;
991da177e4SLinus Torvalds 
1006119be0bSHugh Dickins 	if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
1011da177e4SLinus Torvalds 		goto out;
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	down_write(&current->mm->mmap_sem);
1041da177e4SLinus Torvalds 	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
1051da177e4SLinus Torvalds 	up_write(&current->mm->mmap_sem);
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds out:
1081da177e4SLinus Torvalds 	return ret;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds /*
1121da177e4SLinus Torvalds  * Perform the select(nd, in, out, ex, tv) and mmap() system
1131da177e4SLinus Torvalds  * calls.
1141da177e4SLinus Torvalds  */
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds struct sel_arg_struct {
1171da177e4SLinus Torvalds 	unsigned long n;
1181da177e4SLinus Torvalds 	fd_set __user *inp, *outp, *exp;
1191da177e4SLinus Torvalds 	struct timeval __user *tvp;
1201da177e4SLinus Torvalds };
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds asmlinkage int old_select(struct sel_arg_struct __user *arg)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	struct sel_arg_struct a;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	if (copy_from_user(&a, arg, sizeof(a)))
1271da177e4SLinus Torvalds 		return -EFAULT;
1281da177e4SLinus Torvalds 	/* sys_select() does the appropriate kernel locking */
1291da177e4SLinus Torvalds 	return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
132dd35afc2SNicolas Pitre #if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
1331da177e4SLinus Torvalds /*
1341da177e4SLinus Torvalds  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
1351da177e4SLinus Torvalds  *
1361da177e4SLinus Torvalds  * This is really horribly ugly.
1371da177e4SLinus Torvalds  */
1381da177e4SLinus Torvalds asmlinkage int sys_ipc(uint call, int first, int second, int third,
1391da177e4SLinus Torvalds 		       void __user *ptr, long fifth)
1401da177e4SLinus Torvalds {
1411da177e4SLinus Torvalds 	int version, ret;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	version = call >> 16; /* hack for backward compatibility */
1441da177e4SLinus Torvalds 	call &= 0xffff;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	switch (call) {
1471da177e4SLinus Torvalds 	case SEMOP:
1481da177e4SLinus Torvalds 		return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
1491da177e4SLinus Torvalds 	case SEMTIMEDOP:
1501da177e4SLinus Torvalds 		return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
1511da177e4SLinus Torvalds 					(const struct timespec __user *)fifth);
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	case SEMGET:
1541da177e4SLinus Torvalds 		return sys_semget (first, second, third);
1551da177e4SLinus Torvalds 	case SEMCTL: {
1561da177e4SLinus Torvalds 		union semun fourth;
1571da177e4SLinus Torvalds 		if (!ptr)
1581da177e4SLinus Torvalds 			return -EINVAL;
1591da177e4SLinus Torvalds 		if (get_user(fourth.__pad, (void __user * __user *) ptr))
1601da177e4SLinus Torvalds 			return -EFAULT;
1611da177e4SLinus Torvalds 		return sys_semctl (first, second, third, fourth);
1621da177e4SLinus Torvalds 	}
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	case MSGSND:
1651da177e4SLinus Torvalds 		return sys_msgsnd(first, (struct msgbuf __user *) ptr,
1661da177e4SLinus Torvalds 				  second, third);
1671da177e4SLinus Torvalds 	case MSGRCV:
1681da177e4SLinus Torvalds 		switch (version) {
1691da177e4SLinus Torvalds 		case 0: {
1701da177e4SLinus Torvalds 			struct ipc_kludge tmp;
1711da177e4SLinus Torvalds 			if (!ptr)
1721da177e4SLinus Torvalds 				return -EINVAL;
1731da177e4SLinus Torvalds 			if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr,
1741da177e4SLinus Torvalds 					   sizeof (tmp)))
1751da177e4SLinus Torvalds 				return -EFAULT;
1761da177e4SLinus Torvalds 			return sys_msgrcv (first, tmp.msgp, second,
1771da177e4SLinus Torvalds 					   tmp.msgtyp, third);
1781da177e4SLinus Torvalds 		}
1791da177e4SLinus Torvalds 		default:
1801da177e4SLinus Torvalds 			return sys_msgrcv (first,
1811da177e4SLinus Torvalds 					   (struct msgbuf __user *) ptr,
1821da177e4SLinus Torvalds 					   second, fifth, third);
1831da177e4SLinus Torvalds 		}
1841da177e4SLinus Torvalds 	case MSGGET:
1851da177e4SLinus Torvalds 		return sys_msgget ((key_t) first, second);
1861da177e4SLinus Torvalds 	case MSGCTL:
1871da177e4SLinus Torvalds 		return sys_msgctl(first, second, (struct msqid_ds __user *)ptr);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	case SHMAT:
1901da177e4SLinus Torvalds 		switch (version) {
1911da177e4SLinus Torvalds 		default: {
1921da177e4SLinus Torvalds 			ulong raddr;
1931da177e4SLinus Torvalds 			ret = do_shmat(first, (char __user *)ptr, second, &raddr);
1941da177e4SLinus Torvalds 			if (ret)
1951da177e4SLinus Torvalds 				return ret;
1961da177e4SLinus Torvalds 			return put_user(raddr, (ulong __user *)third);
1971da177e4SLinus Torvalds 		}
1981da177e4SLinus Torvalds 		case 1: /* Of course, we don't support iBCS2! */
1991da177e4SLinus Torvalds 			return -EINVAL;
2001da177e4SLinus Torvalds 		}
2011da177e4SLinus Torvalds 	case SHMDT:
2021da177e4SLinus Torvalds 		return sys_shmdt ((char __user *)ptr);
2031da177e4SLinus Torvalds 	case SHMGET:
2041da177e4SLinus Torvalds 		return sys_shmget (first, second, third);
2051da177e4SLinus Torvalds 	case SHMCTL:
2061da177e4SLinus Torvalds 		return sys_shmctl (first, second,
2071da177e4SLinus Torvalds 				   (struct shmid_ds __user *) ptr);
2081da177e4SLinus Torvalds 	default:
2091da177e4SLinus Torvalds 		return -ENOSYS;
2101da177e4SLinus Torvalds 	}
2111da177e4SLinus Torvalds }
212dd35afc2SNicolas Pitre #endif
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds /* Fork a new task - this creates a new program thread.
2151da177e4SLinus Torvalds  * This is called indirectly via a small wrapper
2161da177e4SLinus Torvalds  */
2171da177e4SLinus Torvalds asmlinkage int sys_fork(struct pt_regs *regs)
2181da177e4SLinus Torvalds {
219f24284adSHyok S. Choi #ifdef CONFIG_MMU
2201da177e4SLinus Torvalds 	return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
221f24284adSHyok S. Choi #else
222f24284adSHyok S. Choi 	/* can not support in nommu mode */
223f24284adSHyok S. Choi 	return(-EINVAL);
224f24284adSHyok S. Choi #endif
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds /* Clone a task - this clones the calling program thread.
2281da177e4SLinus Torvalds  * This is called indirectly via a small wrapper
2291da177e4SLinus Torvalds  */
2301da177e4SLinus Torvalds asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
2311da177e4SLinus Torvalds 			 int __user *parent_tidptr, int tls_val,
2321da177e4SLinus Torvalds 			 int __user *child_tidptr, struct pt_regs *regs)
2331da177e4SLinus Torvalds {
2341da177e4SLinus Torvalds 	if (!newsp)
2351da177e4SLinus Torvalds 		newsp = regs->ARM_sp;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds asmlinkage int sys_vfork(struct pt_regs *regs)
2411da177e4SLinus Torvalds {
2421da177e4SLinus Torvalds 	return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
2431da177e4SLinus Torvalds }
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds /* sys_execve() executes a new program.
2461da177e4SLinus Torvalds  * This is called indirectly via a small wrapper
2471da177e4SLinus Torvalds  */
2481da177e4SLinus Torvalds asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv,
2491da177e4SLinus Torvalds 			  char __user * __user *envp, struct pt_regs *regs)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 	int error;
2521da177e4SLinus Torvalds 	char * filename;
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	filename = getname(filenamei);
2551da177e4SLinus Torvalds 	error = PTR_ERR(filename);
2561da177e4SLinus Torvalds 	if (IS_ERR(filename))
2571da177e4SLinus Torvalds 		goto out;
2581da177e4SLinus Torvalds 	error = do_execve(filename, argv, envp, regs);
2591da177e4SLinus Torvalds 	putname(filename);
2601da177e4SLinus Torvalds out:
2611da177e4SLinus Torvalds 	return error;
2621da177e4SLinus Torvalds }
2631da177e4SLinus Torvalds 
2643db03b4aSArnd Bergmann int kernel_execve(const char *filename, char *const argv[], char *const envp[])
2651da177e4SLinus Torvalds {
2661da177e4SLinus Torvalds 	struct pt_regs regs;
2671da177e4SLinus Torvalds 	int ret;
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 	memset(&regs, 0, sizeof(struct pt_regs));
2701da177e4SLinus Torvalds 	ret = do_execve((char *)filename, (char __user * __user *)argv,
2711da177e4SLinus Torvalds 			(char __user * __user *)envp, &regs);
2721da177e4SLinus Torvalds 	if (ret < 0)
2731da177e4SLinus Torvalds 		goto out;
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 	/*
2761da177e4SLinus Torvalds 	 * Save argc to the register structure for userspace.
2771da177e4SLinus Torvalds 	 */
2781da177e4SLinus Torvalds 	regs.ARM_r0 = ret;
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	/*
2811da177e4SLinus Torvalds 	 * We were successful.  We won't be returning to our caller, but
2821da177e4SLinus Torvalds 	 * instead to user space by manipulating the kernel stack.
2831da177e4SLinus Torvalds 	 */
2841da177e4SLinus Torvalds 	asm(	"add	r0, %0, %1\n\t"
2851da177e4SLinus Torvalds 		"mov	r1, %2\n\t"
2861da177e4SLinus Torvalds 		"mov	r2, %3\n\t"
2871da177e4SLinus Torvalds 		"bl	memmove\n\t"	/* copy regs to top of stack */
2881da177e4SLinus Torvalds 		"mov	r8, #0\n\t"	/* not a syscall */
2891da177e4SLinus Torvalds 		"mov	r9, %0\n\t"	/* thread structure */
2901da177e4SLinus Torvalds 		"mov	sp, r0\n\t"	/* reposition stack pointer */
2911da177e4SLinus Torvalds 		"b	ret_to_user"
2921da177e4SLinus Torvalds 		:
2931da177e4SLinus Torvalds 		: "r" (current_thread_info()),
2944f7a1812SRussell King 		  "Ir" (THREAD_START_SP - sizeof(regs)),
2951da177e4SLinus Torvalds 		  "r" (&regs),
2961da177e4SLinus Torvalds 		  "Ir" (sizeof(regs))
297c2f48086SNicolas Pitre 		: "r0", "r1", "r2", "r3", "ip", "lr", "memory");
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds  out:
3001da177e4SLinus Torvalds 	return ret;
3011da177e4SLinus Torvalds }
3023db03b4aSArnd Bergmann EXPORT_SYMBOL(kernel_execve);
30368d9102fSNicolas Pitre 
30468d9102fSNicolas Pitre /*
3056cbdc8c5SSimon Arlott  * Since loff_t is a 64 bit type we avoid a lot of ABI hassle
30668d9102fSNicolas Pitre  * with a different argument ordering.
30768d9102fSNicolas Pitre  */
30868d9102fSNicolas Pitre asmlinkage long sys_arm_fadvise64_64(int fd, int advice,
30968d9102fSNicolas Pitre 				     loff_t offset, loff_t len)
31068d9102fSNicolas Pitre {
31168d9102fSNicolas Pitre 	return sys_fadvise64_64(fd, offset, len, advice);
31268d9102fSNicolas Pitre }
313