summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/sys_sparc32.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /arch/sparc64/kernel/sys_sparc32.c
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'arch/sparc64/kernel/sys_sparc32.c')
-rw-r--r--arch/sparc64/kernel/sys_sparc32.c386
1 files changed, 291 insertions, 95 deletions
diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
index b6ca9448c..e2c199f60 100644
--- a/arch/sparc64/kernel/sys_sparc32.c
+++ b/arch/sparc64/kernel/sys_sparc32.c
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.44 1997/07/20 09:18:47 davem Exp $
+/* $Id: sys_sparc32.c,v 1.55 1997/09/04 01:54:51 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -44,6 +44,7 @@
#include <asm/ipc.h>
#include <asm/uaccess.h>
#include <asm/fpumacro.h>
+#include <asm/semaphore.h>
/* As gcc will warn about casting u32 to some ptr, we have to cast it to
* unsigned long first, and that's what is A() for.
@@ -53,6 +54,33 @@
*/
#define A(x) ((unsigned long)x)
+extern char * getname_quicklist;
+extern int getname_quickcount;
+extern struct semaphore getname_quicklock;
+extern int kerneld_msqid;
+
+/* Tuning: increase locality by reusing same pages again...
+ * if getname_quicklist becomes too long on low memory machines, either a limit
+ * should be added or after a number of cycles some pages should
+ * be released again ...
+ */
+static inline char * get_page(void)
+{
+ char * res;
+ down(&getname_quicklock);
+ res = getname_quicklist;
+ if (res) {
+ getname_quicklist = *(char**)res;
+ getname_quickcount--;
+ }
+ else
+ res = (char*)__get_free_page(GFP_KERNEL);
+ up(&getname_quicklock);
+ return res;
+}
+
+#define putname32 putname
+
/* In order to reduce some races, while at the same time doing additional
* checking and hopefully speeding things up, we copy filenames to the
* kernel data space before using them..
@@ -74,44 +102,67 @@ static inline int do_getname32(u32 filename, char *page)
return retval;
}
-/* This is a single page for faster getname.
- * If the page is available when entering getname, use it.
- * If the page is not available, call __get_free_page instead.
- * This works even though do_getname can block (think about it).
- * -- Michael Chastain, based on idea of Linus Torvalds, 1 Dec 1996.
- * We don't use the common getname/putname from namei.c, so that
- * this still works well, as every routine which calls getname32
- * will then call getname, then putname and then putname32.
- */
-static unsigned long name_page_cache32 = 0;
+char * getname32(u32 filename)
+{
+ char *tmp, *result;
+
+ result = ERR_PTR(-ENOMEM);
+ tmp = get_page();
+ if (tmp) {
+ int retval = do_getname32(filename, tmp);
-void putname32(char * name)
+ result = tmp;
+ if (retval < 0) {
+ putname32(tmp);
+ result = ERR_PTR(retval);
+ }
+ }
+ return result;
+}
+
+/* 32-bit timeval and related flotsam. */
+
+struct timeval32
{
- if (name_page_cache32 == 0)
- name_page_cache32 = (unsigned long) name;
- else
- free_page((unsigned long) name);
+ int tv_sec, tv_usec;
+};
+
+struct itimerval32
+{
+ struct timeval32 it_interval;
+ struct timeval32 it_value;
+};
+
+static inline long get_tv32(struct timeval *o, struct timeval32 *i)
+{
+ return (!access_ok(VERIFY_READ, tv32, sizeof(*tv32)) ||
+ (__get_user(o->tv_sec, &i->tv_sec) |
+ __get_user(o->tv_usec, &i->tv_usec)));
}
-int getname32(u32 filename, char **result)
+static inline long put_tv32(struct timeval32 *o, struct timeval *i)
{
- unsigned long page;
- int retval;
+ return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) ||
+ (__put_user(i->tv_sec, &o->tv_sec) |
+ __put_user(i->tv_usec, &o->tv_usec)));
+}
- page = name_page_cache32;
- name_page_cache32 = 0;
- if (!page) {
- page = __get_free_page(GFP_KERNEL);
- if (!page)
- return -ENOMEM;
- }
+static inline long get_it32(struct itimerval *o, struct itimerval32 *i)
+{
+ return (!access_ok(VERIFY_READ, i32, sizeof(*i32)) ||
+ (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) |
+ __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) |
+ __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) |
+ __get_user(o->it_value.tv_usec, &i->it_value.tv_usec)));
+}
- retval = do_getname32(filename, (char *) page);
- if (retval < 0)
- putname32( (char *) page );
- else
- *result = (char *) page;
- return retval;
+static inline long put_it32(struct itimerval32 *o, struct itimerval *i)
+{
+ return (!access_ok(VERIFY_WRITE, i32, sizeof(*i32)) ||
+ (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) |
+ __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) |
+ __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) |
+ __put_user(i->it_value.tv_usec, &o->it_value.tv_usec)));
}
extern asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
@@ -266,14 +317,25 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u
switch (call) {
case MSGSND:
{
- struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_KERNEL);
+ struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_KERNEL);
if (!p) err = -ENOMEM;
else {
- if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
- __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
- err = -EFAULT;
- else {
+ err = 0;
+ if (first == kerneld_msqid) {
+ *(int *)p->mtext = 0;
+ if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+ __copy_from_user(&p->mtext[4], &(((struct msgbuf32 *)A(ptr))->mtext[0]), 4) ||
+ __copy_from_user(&p->mtext[8], &(((struct msgbuf32 *)A(ptr))->mtext[4]), second-4))
+ err = -EFAULT;
+ else
+ second += 4;
+ } else {
+ if (get_user(p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+ __copy_from_user(p->mtext, &(((struct msgbuf32 *)A(ptr))->mtext), second))
+ err = -EFAULT;
+ }
+ if (!err) {
unsigned long old_fs = get_fs();
set_fs (KERNEL_DS);
err = sys_msgsnd (first, p, second, third);
@@ -287,6 +349,7 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u
{
struct msgbuf *p;
unsigned long old_fs;
+ long msgtyp = fifth;
if (!version) {
struct ipc_kludge tmp;
@@ -297,20 +360,35 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u
if(copy_from_user(&tmp,(struct ipc_kludge *)A(ptr), sizeof (tmp)))
goto out;
ptr = tmp.msgp;
- fifth = tmp.msgtyp;
+ msgtyp = tmp.msgtyp;
}
- p = kmalloc (second + sizeof (struct msgbuf), GFP_KERNEL);
+
+ p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_KERNEL);
if (!p) {
err = -EFAULT;
goto out;
}
+
old_fs = get_fs();
set_fs (KERNEL_DS);
- err = sys_msgrcv (first, p, second, fifth, third);
+ err = sys_msgrcv (first, p, second + 4, msgtyp, third);
set_fs (old_fs);
- if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
- __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, second))
- err = -EFAULT;
+
+ if (err < 0)
+ goto out;
+
+ if (first == kerneld_msqid) {
+ if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+ __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[0]), &p->mtext[4], 4) ||
+ __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext[4]), &p->mtext[8], err-8))
+ err = -EFAULT;
+ else
+ err -= 4;
+ } else {
+ if (put_user (p->mtype, &(((struct msgbuf32 *)A(ptr))->mtype)) ||
+ __copy_to_user(&(((struct msgbuf32 *)A(ptr))->mtext), p->mtext, err))
+ err = -EFAULT;
+ }
kfree (p);
goto out;
}
@@ -472,8 +550,9 @@ asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u
err = -EINVAL;
goto out;
}
- else
- err = -EINVAL;
+
+ err = -EINVAL;
+
out:
unlock_kernel();
return err;
@@ -565,8 +644,9 @@ asmlinkage int sys32_quotactl(int cmd, u32 special, int id, u32 addr)
return sys_quotactl(cmd, (const char *)A(special),
id, (caddr_t)A(addr));
}
- err = getname32 (special, &spec);
- if (err) return err;
+ spec = getname32 (special);
+ err = PTR_ERR(spec);
+ if (IS_ERR(spec)) return err;
old_fs = get_fs ();
set_fs (KERNEL_DS);
err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)A(addr));
@@ -608,8 +688,9 @@ asmlinkage int sys32_statfs(u32 path, u32 buf)
unsigned long old_fs = get_fs();
char *pth;
- ret = getname32 (path, &pth);
- if (!ret) {
+ pth = getname32 (path);
+ ret = PTR_ERR(pth);
+ if (!IS_ERR(pth)) {
set_fs (KERNEL_DS);
ret = sys_statfs((const char *)pth, &s);
set_fs (old_fs);
@@ -651,8 +732,9 @@ asmlinkage int sys32_utime(u32 filename, u32 times)
if (get_user (t.actime, &(((struct utimbuf32 *)A(times))->actime)) ||
__get_user (t.modtime, &(((struct utimbuf32 *)A(times))->modtime)))
return -EFAULT;
- ret = getname32 (filename, &filenam);
- if (!ret) {
+ filenam = getname32 (filename);
+ ret = PTR_ERR(filenam);
+ if (!IS_ERR(filenam)) {
old_fs = get_fs();
set_fs (KERNEL_DS);
ret = sys_utime(filenam, &t);
@@ -1010,7 +1092,7 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp)
lock_kernel ();
p = (char *)__get_free_page (GFP_KERNEL);
if (!p)
- goto out;
+ goto out_nofree;
q = (u32 *)p;
Inp = (u32 *)A(inp);
@@ -1019,7 +1101,13 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp)
ret = -EFAULT;
- nn = (n + (8 * sizeof(unsigned long)) - 1) / (8 * sizeof (unsigned long));
+ nn = (n + (8 * sizeof(long)) - 1) / (8 * sizeof(long));
+ if (inp && verify_area(VERIFY_WRITE, Inp, nn*sizeof(long)))
+ goto out;
+ if (outp && verify_area(VERIFY_WRITE, Outp, nn*sizeof(long)))
+ goto out;
+ if (exp && verify_area(VERIFY_WRITE, Exp, nn*sizeof(long)))
+ goto out;
for (i = 0; i < nn; i++, Inp += 2, Outp += 2, Exp += 2, q += 2) {
if(inp && (__get_user (q[1], Inp) || __get_user (q[0], Inp+1)))
goto out;
@@ -1033,7 +1121,7 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp)
ktvp = NULL;
if(tvp) {
- if(copy_from_user(&kern_tv, (struct timeval *)A(tvp), sizeof(*ktvp)))
+ if (get_tv32(&kern_tv, (struct timeval32 *)A(tvp)))
goto out;
ktvp = &kern_tv;
}
@@ -1048,8 +1136,12 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp)
ktvp);
set_fs (old_fs);
- if(tvp && !(current->personality & STICKY_TIMEOUTS))
- copy_to_user((struct timeval *)A(tvp), &kern_tv, sizeof(*ktvp));
+ if(tvp && !(current->personality & STICKY_TIMEOUTS)) {
+ if (put_tv32((struct timeval32 *)A(tvp), &kern_tv)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ }
q = (u32 *)p;
Inp = (u32 *)A(inp);
@@ -1079,6 +1171,7 @@ asmlinkage int sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp)
}
out:
free_page ((unsigned long)p);
+out_nofree:
unlock_kernel();
return ret;
}
@@ -1111,8 +1204,9 @@ asmlinkage int sys32_newstat(u32 filename, u32 statbuf)
char *filenam;
unsigned long old_fs = get_fs();
- ret = getname32 (filename, &filenam);
- if (!ret) {
+ filenam = getname32 (filename);
+ ret = PTR_ERR(filenam);
+ if (!IS_ERR(filenam)) {
set_fs (KERNEL_DS);
ret = sys_newstat(filenam, &s);
set_fs (old_fs);
@@ -1132,8 +1226,9 @@ asmlinkage int sys32_newlstat(u32 filename, u32 statbuf)
char *filenam;
unsigned long old_fs = get_fs();
- ret = getname32 (filename, &filenam);
- if (!ret) {
+ filenam = getname32 (filename);
+ ret = PTR_ERR(filenam);
+ if (!IS_ERR(filenam)) {
set_fs (KERNEL_DS);
ret = sys_newlstat(filenam, &s);
set_fs (old_fs);
@@ -1239,12 +1334,12 @@ static void *do_smb_super_data_conv(void *raw_data)
struct smb_mount_data *s = (struct smb_mount_data *)raw_data;
struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data;
- s->dir_mode = s32->dir_mode;
- s->file_mode = s32->file_mode;
- s->gid = s32->gid;
- s->uid = s32->uid;
- memmove (&s->addr, &s32->addr, (((long)&s->uid) - ((long)&s->addr)));
+ s->version = s32->version;
s->mounted_uid = s32->mounted_uid;
+ s->uid = s32->uid;
+ s->gid = s32->gid;
+ s->file_mode = s32->file_mode;
+ s->dir_mode = s32->dir_mode;
return raw_data;
}
@@ -1344,8 +1439,8 @@ asmlinkage int sys32_mount(u32 dev_name, u32 dir_name, u32 type, u32 new_flags,
}
struct rusage32 {
- struct timeval ru_utime;
- struct timeval ru_stime;
+ struct timeval32 ru_utime;
+ struct timeval32 ru_stime;
s32 ru_maxrss;
s32 ru_ixrss;
s32 ru_idrss;
@@ -1703,7 +1798,7 @@ struct timex32 {
s32 constant;
s32 precision;
s32 tolerance;
- struct timeval time;
+ struct timeval32 time;
s32 tick;
s32 ppsfreq;
s32 jitter;
@@ -2110,54 +2205,45 @@ asmlinkage int sparc32_sigaction (int signum, u32 action, u32 oldaction)
{
struct sigaction32 new_sa, old_sa;
struct sigaction *p;
- int err = -EINVAL;
- lock_kernel();
if(signum < 0) {
current->tss.new_signal = 1;
signum = -signum;
}
-
if (signum<1 || signum>32)
- goto out;
+ return -EINVAL;
p = signum - 1 + current->sig->action;
if (action) {
- err = -EINVAL;
if (signum==SIGKILL || signum==SIGSTOP)
- goto out;
- err = -EFAULT;
+ return -EINVAL;
if(copy_from_user(&new_sa, A(action), sizeof(struct sigaction32)))
- goto out;
+ return -EFAULT;
if (((__sighandler_t)A(new_sa.sa_handler)) != SIG_DFL &&
((__sighandler_t)A(new_sa.sa_handler)) != SIG_IGN) {
- err = verify_area(VERIFY_READ, (__sighandler_t)A(new_sa.sa_handler), 1);
+ int err = verify_area(VERIFY_READ,
+ (__sighandler_t)A(new_sa.sa_handler), 1);
if (err)
- goto out;
+ return err;
}
}
-
if (oldaction) {
- err = -EFAULT;
old_sa.sa_handler = (unsigned)(u64)(p->sa_handler);
old_sa.sa_mask = (sigset_t32)(p->sa_mask);
old_sa.sa_flags = (unsigned)(p->sa_flags);
old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer);
if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32)))
- goto out;
+ return -EFAULT;
}
-
if (action) {
+ spin_lock_irq(&current->sig->siglock);
p->sa_handler = (__sighandler_t)A(new_sa.sa_handler);
p->sa_mask = (sigset_t)(new_sa.sa_mask);
p->sa_flags = new_sa.sa_flags;
p->sa_restorer = (void (*)(void))A(new_sa.sa_restorer);
check_pending(signum);
+ spin_unlock_irq(&current->sig->siglock);
}
-
- err = 0;
-out:
- unlock_kernel();
- return err;
+ return 0;
}
/*
@@ -2298,7 +2384,7 @@ asmlinkage int sparc32_execve(struct pt_regs *regs)
base = 1;
lock_kernel();
- filename = getname((char *)(unsigned long)(u32)regs->u_regs[base + UREG_I0]);
+ filename = getname((char *)A((u32)regs->u_regs[base + UREG_I0]));
error = PTR_ERR(filename);
if(IS_ERR(filename))
goto out;
@@ -2310,6 +2396,7 @@ asmlinkage int sparc32_execve(struct pt_regs *regs)
if(!error) {
fprs_write(0);
regs->fprs = 0;
+ regs->tstate &= ~TSTATE_PEF;
}
out:
unlock_kernel();
@@ -2369,8 +2456,12 @@ asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_si
case QM_MODULES:
case QM_REFS:
case QM_DEPS:
- if (name_user && (ret = getname32 (name_user, &usernam)))
- return ret;
+ if (name_user) {
+ usernam = getname32 (name_user);
+ ret = PTR_ERR(usernam);
+ if (IS_ERR(usernam))
+ return ret;
+ }
buff = kmalloc (bufsiz, GFP_KERNEL);
if (!buff) {
if (name_user) putname32 (usernam);
@@ -2431,8 +2522,12 @@ qmsym_toshort:
if (name_user) putname32 (usernam);
return ret;
case QM_INFO:
- if (name_user && (ret = getname32 (name_user, &usernam)))
- return ret;
+ if (name_user) {
+ usernam = getname32 (name_user);
+ ret = PTR_ERR(usernam);
+ if (IS_ERR(usernam))
+ return ret;
+ }
set_fs (KERNEL_DS);
ret = sys_query_module (usernam, which, (char *)&mi, sizeof (mi), &val);
set_fs (old_fs);
@@ -2720,9 +2815,7 @@ static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res
return 0;
}
-extern asmlinkage int sys_nfsservctl(int cmd,
- struct nfsctl_arg *arg,
- union nfsctl_res *resp);
+extern asmlinkage int sys_nfsservctl(int cmd, void *arg, void *resp);
int asmlinkage sys32_nfsservctl(int cmd, u32 u_argp, u32 u_resp)
{
@@ -2793,3 +2886,106 @@ done:
kfree(kres);
return err;
}
+
+/* Translations due to time_t size differences. Which affects all
+ sorts of things, like timeval and itimerval. */
+
+extern struct timezone sys_tz;
+extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz);
+
+asmlinkage int sys32_gettimeofday(u32 tv, u32 tz)
+{
+ if (tv) {
+ struct timeval ktv;
+ do_gettimeofday(&ktv);
+ if (put_tv32((struct timeval32 *)A(tv), &ktv))
+ return -EFAULT;
+ }
+ if (tz) {
+ if (copy_to_user((void*)A(tz), &sys_tz, sizeof(sys_tz)))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+asmlinkage int sys32_settimeofday(u32 tv, u32 tz)
+{
+ struct timeval ktv;
+ struct timezone ktz;
+
+ if (tv) {
+ if (get_tv32(&ktv, (struct timeval32 *)A(tv)))
+ return -EFAULT;
+ }
+ if (tz) {
+ if (copy_from_user(&ktz, (void*)A(tz), sizeof(ktz)))
+ return -EFAULT;
+ }
+
+ return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL);
+}
+
+extern int do_getitimer(int which, struct itimerval *value);
+
+asmlinkage int sys32_getitimer(int which, u32 it)
+{
+ struct itimerval kit;
+ int error;
+
+ error = do_getitimer(which, &kit);
+ if (!error && put_it32((struct itimerval32 *)A(it), &kit))
+ error = -EFAULT;
+
+ return error;
+}
+
+extern int do_setitimer(int which, struct itimerval *, struct itimerval *);
+
+asmlinkage int sys32_setitimer(int which, u32 in, u32 out)
+{
+ struct itimerval kin, kout;
+ int error;
+
+ if (in) {
+ if (get_it32(&kin, (struct itimerval32 *)A(in)))
+ return -EFAULT;
+ } else
+ memset(&kin, 0, sizeof(kin));
+
+ error = do_setitimer(which, &kin, out ? &kout : NULL);
+ if (error || !out)
+ return error;
+ if (put_it32((struct itimerval32 *)A(out), &kout))
+ return -EFAULT;
+
+ return 0;
+
+}
+
+asmlinkage int sys_utimes(char *, struct timeval *);
+
+asmlinkage int sys32_utimes(u32 filename, u32 tvs)
+{
+ char *kfilename;
+ struct timeval ktvs[2];
+ unsigned long old_fs;
+ int ret;
+
+ kfilename = getname32(filename);
+ ret = PTR_ERR(kfilename);
+ if (!IS_ERR(kfilename)) {
+ if (tvs) {
+ if (get_tv32(&ktvs[0], (struct timeval32 *)A(tvs)) ||
+ get_tv32(&ktvs[1], 1+(struct timeval32 *)A(tvs)))
+ return -EFAULT;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_utimes(kfilename, &ktvs[0]);
+ set_fs(old_fs);
+
+ putname32(kfilename);
+ }
+ return ret;
+}