mirror of
https://github.com/torvalds/linux.git
synced 2024-12-16 16:12:52 +00:00
4f7a18124c
Various places in the ARM kernel implicitly assumed that kernel stacks are always 8K due to hard coded constants. Replace these constants with definitions. Correct the allowable range of kernel stack pointer values within the allocation. Arrange for the entire kernel stack to be zeroed, not just the upper 4K if CONFIG_DEBUG_STACK_USAGE is set. Signed-off-by: Russell King <rmk@arm.linux.org.uk>
314 lines
7.3 KiB
C
314 lines
7.3 KiB
C
/*
|
|
* linux/arch/arm/kernel/sys_arm.c
|
|
*
|
|
* Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
|
|
* Copyright (C) 1995, 1996 Russell King.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This file contains various random system calls that
|
|
* have a non-standard calling sequence on the Linux/arm
|
|
* platform.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/msg.h>
|
|
#include <linux/shm.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/utsname.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/ipc.h>
|
|
|
|
extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
|
|
unsigned long new_len, unsigned long flags,
|
|
unsigned long new_addr);
|
|
|
|
/*
|
|
* sys_pipe() is the normal C calling standard for creating
|
|
* a pipe. It's not the way unix traditionally does this, though.
|
|
*/
|
|
asmlinkage int sys_pipe(unsigned long __user *fildes)
|
|
{
|
|
int fd[2];
|
|
int error;
|
|
|
|
error = do_pipe(fd);
|
|
if (!error) {
|
|
if (copy_to_user(fildes, fd, 2*sizeof(int)))
|
|
error = -EFAULT;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/* common code for old and new mmaps */
|
|
inline long do_mmap2(
|
|
unsigned long addr, unsigned long len,
|
|
unsigned long prot, unsigned long flags,
|
|
unsigned long fd, unsigned long pgoff)
|
|
{
|
|
int error = -EINVAL;
|
|
struct file * file = NULL;
|
|
|
|
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
|
|
|
|
if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
|
|
goto out;
|
|
|
|
error = -EBADF;
|
|
if (!(flags & MAP_ANONYMOUS)) {
|
|
file = fget(fd);
|
|
if (!file)
|
|
goto out;
|
|
}
|
|
|
|
down_write(¤t->mm->mmap_sem);
|
|
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
|
|
up_write(¤t->mm->mmap_sem);
|
|
|
|
if (file)
|
|
fput(file);
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
struct mmap_arg_struct {
|
|
unsigned long addr;
|
|
unsigned long len;
|
|
unsigned long prot;
|
|
unsigned long flags;
|
|
unsigned long fd;
|
|
unsigned long offset;
|
|
};
|
|
|
|
asmlinkage int old_mmap(struct mmap_arg_struct __user *arg)
|
|
{
|
|
int error = -EFAULT;
|
|
struct mmap_arg_struct a;
|
|
|
|
if (copy_from_user(&a, arg, sizeof(a)))
|
|
goto out;
|
|
|
|
error = -EINVAL;
|
|
if (a.offset & ~PAGE_MASK)
|
|
goto out;
|
|
|
|
error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
asmlinkage unsigned long
|
|
sys_arm_mremap(unsigned long addr, unsigned long old_len,
|
|
unsigned long new_len, unsigned long flags,
|
|
unsigned long new_addr)
|
|
{
|
|
unsigned long ret = -EINVAL;
|
|
|
|
if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
|
|
goto out;
|
|
|
|
down_write(¤t->mm->mmap_sem);
|
|
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
|
|
up_write(¤t->mm->mmap_sem);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Perform the select(nd, in, out, ex, tv) and mmap() system
|
|
* calls.
|
|
*/
|
|
|
|
struct sel_arg_struct {
|
|
unsigned long n;
|
|
fd_set __user *inp, *outp, *exp;
|
|
struct timeval __user *tvp;
|
|
};
|
|
|
|
asmlinkage int old_select(struct sel_arg_struct __user *arg)
|
|
{
|
|
struct sel_arg_struct a;
|
|
|
|
if (copy_from_user(&a, arg, sizeof(a)))
|
|
return -EFAULT;
|
|
/* sys_select() does the appropriate kernel locking */
|
|
return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
|
|
}
|
|
|
|
/*
|
|
* sys_ipc() is the de-multiplexer for the SysV IPC calls..
|
|
*
|
|
* This is really horribly ugly.
|
|
*/
|
|
asmlinkage int sys_ipc(uint call, int first, int second, int third,
|
|
void __user *ptr, long fifth)
|
|
{
|
|
int version, ret;
|
|
|
|
version = call >> 16; /* hack for backward compatibility */
|
|
call &= 0xffff;
|
|
|
|
switch (call) {
|
|
case SEMOP:
|
|
return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
|
|
case SEMTIMEDOP:
|
|
return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
|
|
(const struct timespec __user *)fifth);
|
|
|
|
case SEMGET:
|
|
return sys_semget (first, second, third);
|
|
case SEMCTL: {
|
|
union semun fourth;
|
|
if (!ptr)
|
|
return -EINVAL;
|
|
if (get_user(fourth.__pad, (void __user * __user *) ptr))
|
|
return -EFAULT;
|
|
return sys_semctl (first, second, third, fourth);
|
|
}
|
|
|
|
case MSGSND:
|
|
return sys_msgsnd(first, (struct msgbuf __user *) ptr,
|
|
second, third);
|
|
case MSGRCV:
|
|
switch (version) {
|
|
case 0: {
|
|
struct ipc_kludge tmp;
|
|
if (!ptr)
|
|
return -EINVAL;
|
|
if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr,
|
|
sizeof (tmp)))
|
|
return -EFAULT;
|
|
return sys_msgrcv (first, tmp.msgp, second,
|
|
tmp.msgtyp, third);
|
|
}
|
|
default:
|
|
return sys_msgrcv (first,
|
|
(struct msgbuf __user *) ptr,
|
|
second, fifth, third);
|
|
}
|
|
case MSGGET:
|
|
return sys_msgget ((key_t) first, second);
|
|
case MSGCTL:
|
|
return sys_msgctl(first, second, (struct msqid_ds __user *)ptr);
|
|
|
|
case SHMAT:
|
|
switch (version) {
|
|
default: {
|
|
ulong raddr;
|
|
ret = do_shmat(first, (char __user *)ptr, second, &raddr);
|
|
if (ret)
|
|
return ret;
|
|
return put_user(raddr, (ulong __user *)third);
|
|
}
|
|
case 1: /* Of course, we don't support iBCS2! */
|
|
return -EINVAL;
|
|
}
|
|
case SHMDT:
|
|
return sys_shmdt ((char __user *)ptr);
|
|
case SHMGET:
|
|
return sys_shmget (first, second, third);
|
|
case SHMCTL:
|
|
return sys_shmctl (first, second,
|
|
(struct shmid_ds __user *) ptr);
|
|
default:
|
|
return -ENOSYS;
|
|
}
|
|
}
|
|
|
|
/* Fork a new task - this creates a new program thread.
|
|
* This is called indirectly via a small wrapper
|
|
*/
|
|
asmlinkage int sys_fork(struct pt_regs *regs)
|
|
{
|
|
return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
|
|
}
|
|
|
|
/* Clone a task - this clones the calling program thread.
|
|
* This is called indirectly via a small wrapper
|
|
*/
|
|
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
|
int __user *parent_tidptr, int tls_val,
|
|
int __user *child_tidptr, struct pt_regs *regs)
|
|
{
|
|
if (!newsp)
|
|
newsp = regs->ARM_sp;
|
|
|
|
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
|
|
}
|
|
|
|
asmlinkage int sys_vfork(struct pt_regs *regs)
|
|
{
|
|
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
|
|
}
|
|
|
|
/* sys_execve() executes a new program.
|
|
* This is called indirectly via a small wrapper
|
|
*/
|
|
asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv,
|
|
char __user * __user *envp, struct pt_regs *regs)
|
|
{
|
|
int error;
|
|
char * filename;
|
|
|
|
filename = getname(filenamei);
|
|
error = PTR_ERR(filename);
|
|
if (IS_ERR(filename))
|
|
goto out;
|
|
error = do_execve(filename, argv, envp, regs);
|
|
putname(filename);
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
long execve(const char *filename, char **argv, char **envp)
|
|
{
|
|
struct pt_regs regs;
|
|
int ret;
|
|
|
|
memset(®s, 0, sizeof(struct pt_regs));
|
|
ret = do_execve((char *)filename, (char __user * __user *)argv,
|
|
(char __user * __user *)envp, ®s);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
/*
|
|
* Save argc to the register structure for userspace.
|
|
*/
|
|
regs.ARM_r0 = ret;
|
|
|
|
/*
|
|
* We were successful. We won't be returning to our caller, but
|
|
* instead to user space by manipulating the kernel stack.
|
|
*/
|
|
asm( "add r0, %0, %1\n\t"
|
|
"mov r1, %2\n\t"
|
|
"mov r2, %3\n\t"
|
|
"bl memmove\n\t" /* copy regs to top of stack */
|
|
"mov r8, #0\n\t" /* not a syscall */
|
|
"mov r9, %0\n\t" /* thread structure */
|
|
"mov sp, r0\n\t" /* reposition stack pointer */
|
|
"b ret_to_user"
|
|
:
|
|
: "r" (current_thread_info()),
|
|
"Ir" (THREAD_START_SP - sizeof(regs)),
|
|
"r" (®s),
|
|
"Ir" (sizeof(regs))
|
|
: "r0", "r1", "r2", "r3", "ip", "memory");
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(execve);
|