forked from Minki/linux
8759145114
xtensa is now in -rc1, with the obsolete syscalls still in there, so I guess this about the last chance to correct the ABI. Applying the patch obviously breaks all sorts of user space binaries and probably also requires the appropriate changes to be made to libc. On the other hand, if a decision is made to keep the broken interface, it should at least be a conscious one instead of an oversight. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Cc: Chris Zankel <chris@zankel.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
271 lines
6.3 KiB
C
271 lines
6.3 KiB
C
/*
|
|
* arch/xtensa/kernel/syscall.c
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
*
|
|
* Copyright (C) 2001 - 2005 Tensilica Inc.
|
|
* Copyright (C) 2000 Silicon Graphics, Inc.
|
|
* Copyright (C) 1995 - 2000 by Ralf Baechle
|
|
*
|
|
* Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
|
|
* Marc Gauthier <marc@tensilica.com, marc@alumni.uwaterloo.ca>
|
|
* Chris Zankel <chris@zankel.net>
|
|
* Kevin Chea
|
|
*
|
|
*/
|
|
|
|
#define DEBUG 0
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/linkage.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/file.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/stringify.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/msg.h>
|
|
#include <linux/shm.h>
|
|
#include <linux/errno.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/signal.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/hardirq.h>
|
|
#include <asm/mman.h>
|
|
#include <asm/shmparam.h>
|
|
#include <asm/page.h>
|
|
#include <asm/ipc.h>
|
|
|
|
extern void do_syscall_trace(void);
|
|
typedef int (*syscall_t)(void *a0,...);
|
|
extern syscall_t sys_call_table[];
|
|
extern unsigned char sys_narg_table[];
|
|
|
|
/*
|
|
* sys_pipe() is the normal C calling standard for creating a pipe. It's not
|
|
* the way unix traditional does this, though.
|
|
*/
|
|
|
|
int sys_pipe(int __user *userfds)
|
|
{
|
|
int fd[2];
|
|
int error;
|
|
|
|
error = do_pipe(fd);
|
|
if (!error) {
|
|
if (copy_to_user(userfds, fd, 2 * sizeof(int)))
|
|
error = -EFAULT;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Common code for old and new mmaps.
|
|
*/
|
|
long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
|
|
unsigned long flags, unsigned long fd, unsigned long pgoff)
|
|
{
|
|
int error = -EBADF;
|
|
struct file * file = NULL;
|
|
|
|
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
|
|
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;
|
|
}
|
|
|
|
int sys_clone(struct pt_regs *regs)
|
|
{
|
|
unsigned long clone_flags;
|
|
unsigned long newsp;
|
|
int __user *parent_tidptr, *child_tidptr;
|
|
clone_flags = regs->areg[4];
|
|
newsp = regs->areg[3];
|
|
parent_tidptr = (int __user *)regs->areg[5];
|
|
child_tidptr = (int __user *)regs->areg[6];
|
|
if (!newsp)
|
|
newsp = regs->areg[1];
|
|
return do_fork(clone_flags,newsp,regs,0,parent_tidptr,child_tidptr);
|
|
}
|
|
|
|
/*
|
|
* sys_execve() executes a new program.
|
|
*/
|
|
|
|
int sys_execve(struct pt_regs *regs)
|
|
{
|
|
int error;
|
|
char * filename;
|
|
|
|
filename = getname((char *) (long)regs->areg[5]);
|
|
error = PTR_ERR(filename);
|
|
if (IS_ERR(filename))
|
|
goto out;
|
|
error = do_execve(filename, (char **) (long)regs->areg[3],
|
|
(char **) (long)regs->areg[4], regs);
|
|
putname(filename);
|
|
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
int sys_uname(struct old_utsname * name)
|
|
{
|
|
if (name && !copy_to_user(name, &system_utsname, sizeof (*name)))
|
|
return 0;
|
|
return -EFAULT;
|
|
}
|
|
|
|
/*
|
|
* Build the string table for the builtin "poor man's strace".
|
|
*/
|
|
|
|
#if DEBUG
|
|
#define SYSCALL(fun, narg) #fun,
|
|
static char *sfnames[] = {
|
|
#include "syscalls.h"
|
|
};
|
|
#undef SYS
|
|
#endif
|
|
|
|
void system_call (struct pt_regs *regs)
|
|
{
|
|
syscall_t syscall;
|
|
unsigned long parm0, parm1, parm2, parm3, parm4, parm5;
|
|
int nargs, res;
|
|
unsigned int syscallnr;
|
|
int ps;
|
|
|
|
#if DEBUG
|
|
int i;
|
|
unsigned long parms[6];
|
|
char *sysname;
|
|
#endif
|
|
|
|
regs->syscall = regs->areg[2];
|
|
|
|
do_syscall_trace();
|
|
|
|
/* Have to load after syscall_trace because strace
|
|
* sometimes changes regs->syscall.
|
|
*/
|
|
syscallnr = regs->syscall;
|
|
|
|
parm0 = parm1 = parm2 = parm3 = parm4 = parm5 = 0;
|
|
|
|
/* Restore interrupt level to syscall invoker's.
|
|
* If this were in assembly, we wouldn't disable
|
|
* interrupts in the first place:
|
|
*/
|
|
local_save_flags (ps);
|
|
local_irq_restore((ps & ~XCHAL_PS_INTLEVEL_MASK) |
|
|
(regs->ps & XCHAL_PS_INTLEVEL_MASK) );
|
|
|
|
if (syscallnr > __NR_Linux_syscalls) {
|
|
regs->areg[2] = -ENOSYS;
|
|
return;
|
|
}
|
|
|
|
syscall = sys_call_table[syscallnr];
|
|
nargs = sys_narg_table[syscallnr];
|
|
|
|
if (syscall == NULL) {
|
|
regs->areg[2] = -ENOSYS;
|
|
return;
|
|
}
|
|
|
|
/* There shouldn't be more than six arguments in the table! */
|
|
|
|
if (nargs > 6)
|
|
panic("Internal error - too many syscall arguments (%d)!\n",
|
|
nargs);
|
|
|
|
/* Linux takes system-call arguments in registers. The ABI
|
|
* and Xtensa software conventions require the system-call
|
|
* number in a2. If an argument exists in a2, we move it to
|
|
* the next available register. Note that for improved
|
|
* efficiency, we do NOT shift all parameters down one
|
|
* register to maintain the original order.
|
|
*
|
|
* At best case (zero arguments), we just write the syscall
|
|
* number to a2. At worst case (1 to 6 arguments), we move
|
|
* the argument in a2 to the next available register, then
|
|
* write the syscall number to a2.
|
|
*
|
|
* For clarity, the following truth table enumerates all
|
|
* possibilities.
|
|
*
|
|
* arguments syscall number arg0, arg1, arg2, arg3, arg4, arg5
|
|
* --------- -------------- ----------------------------------
|
|
* 0 a2
|
|
* 1 a2 a3
|
|
* 2 a2 a4, a3
|
|
* 3 a2 a5, a3, a4
|
|
* 4 a2 a6, a3, a4, a5
|
|
* 5 a2 a7, a3, a4, a5, a6
|
|
* 6 a2 a8, a3, a4, a5, a6, a7
|
|
*/
|
|
if (nargs) {
|
|
parm0 = regs->areg[nargs+2];
|
|
parm1 = regs->areg[3];
|
|
parm2 = regs->areg[4];
|
|
parm3 = regs->areg[5];
|
|
parm4 = regs->areg[6];
|
|
parm5 = regs->areg[7];
|
|
} else /* nargs == 0 */
|
|
parm0 = (unsigned long) regs;
|
|
|
|
#if DEBUG
|
|
parms[0] = parm0;
|
|
parms[1] = parm1;
|
|
parms[2] = parm2;
|
|
parms[3] = parm3;
|
|
parms[4] = parm4;
|
|
parms[5] = parm5;
|
|
|
|
sysname = sfnames[syscallnr];
|
|
if (strncmp(sysname, "sys_", 4) == 0)
|
|
sysname = sysname + 4;
|
|
|
|
printk("\017SYSCALL:I:%x:%d:%s %s(", regs->pc, current->pid,
|
|
current->comm, sysname);
|
|
for (i = 0; i < nargs; i++)
|
|
printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
|
|
printk(")\n");
|
|
#endif
|
|
|
|
res = syscall((void *)parm0, parm1, parm2, parm3, parm4, parm5);
|
|
|
|
#if DEBUG
|
|
printk("\017SYSCALL:O:%d:%s %s(",current->pid, current->comm, sysname);
|
|
for (i = 0; i < nargs; i++)
|
|
printk((i>0) ? ", %#lx" : "%#lx", parms[i]);
|
|
if (res < 4096)
|
|
printk(") = %d\n", res);
|
|
else
|
|
printk(") = %#x\n", res);
|
|
#endif /* DEBUG */
|
|
|
|
regs->areg[2] = res;
|
|
do_syscall_trace();
|
|
}
|