Commit d258243d authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: 31bit emulation.

Bug fixes for the 31 bit emulation layer. New interface
[un]register_ioctl32_conversion.
parent 35adc636
...@@ -1024,6 +1024,8 @@ int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) ...@@ -1024,6 +1024,8 @@ int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
static void ioctl32_insert(struct ioctl32_list *entry) static void ioctl32_insert(struct ioctl32_list *entry)
{ {
int hash = ioctl32_hash(entry->handler.cmd); int hash = ioctl32_hash(entry->handler.cmd);
entry->next = 0;
if (!ioctl32_hash_table[hash]) if (!ioctl32_hash_table[hash])
ioctl32_hash_table[hash] = entry; ioctl32_hash_table[hash] = entry;
else { else {
...@@ -1032,10 +1034,51 @@ static void ioctl32_insert(struct ioctl32_list *entry) ...@@ -1032,10 +1034,51 @@ static void ioctl32_insert(struct ioctl32_list *entry)
while (l->next) while (l->next)
l = l->next; l = l->next;
l->next = entry; l->next = entry;
entry->next = 0;
} }
} }
int register_ioctl32_conversion(unsigned int cmd,
int (*handler)(unsigned int, unsigned int,
unsigned long, struct file *))
{
struct ioctl32_list *l, *new;
int hash;
hash = ioctl32_hash(cmd);
for (l = ioctl32_hash_table[hash]; l != NULL; l = l->next)
if (l->handler.cmd == cmd)
return -EBUSY;
new = kmalloc(sizeof(struct ioctl32_list), GFP_KERNEL);
if (new == NULL)
return -ENOMEM;
new->handler.cmd = cmd;
new->handler.function = (void *) handler;
ioctl32_insert(new);
return 0;
}
int unregister_ioctl32_conversion(unsigned int cmd)
{
struct ioctl32_list *p, *l;
int hash;
hash = ioctl32_hash(cmd);
p = NULL;
for (l = ioctl32_hash_table[hash]; l != NULL; l = l->next) {
if (l->handler.cmd == cmd)
break;
p = l;
}
if (l == NULL)
return -ENOENT;
if (p == NULL)
ioctl32_hash_table[hash] = l->next;
else
p->next = l->next;
kfree(l);
return 0;
}
static int __init init_ioctl32(void) static int __init init_ioctl32(void)
{ {
int i; int i;
......
...@@ -2492,8 +2492,23 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up ...@@ -2492,8 +2492,23 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up
CMSG32_ALIGN(sizeof(struct cmsghdr32))); CMSG32_ALIGN(sizeof(struct cmsghdr32)));
kcmsg32->cmsg_len = clen32; kcmsg32->cmsg_len = clen32;
switch (kcmsg32->cmsg_type) {
/*
* The timestamp type's data needs to be converted
* from 64-bit time values to 32-bit time values
*/
case SO_TIMESTAMP: {
__kernel_time_t32* ptr_time32 = CMSG32_DATA(kcmsg32);
__kernel_time_t* ptr_time = CMSG_DATA(ucmsg);
get_user(*ptr_time32, ptr_time);
get_user(*(ptr_time32+1), ptr_time+1);
kcmsg32->cmsg_len -= 2*(sizeof(__kernel_time_t) -
sizeof(__kernel_time_t32));
}
default:;
}
ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64));
wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); wp = (((char *)kcmsg32) + CMSG32_ALIGN(kcmsg32->cmsg_len));
} }
/* Copy back fixed up data, and adjust pointers. */ /* Copy back fixed up data, and adjust pointers. */
...@@ -2516,148 +2531,6 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up ...@@ -2516,148 +2531,6 @@ static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_up
kmsg->msg_control = (void *) orig_cmsg_uptr; kmsg->msg_control = (void *) orig_cmsg_uptr;
} }
#if 0
asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags)
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
struct iovec iov[UIO_FASTIOV];
unsigned char ctl[sizeof(struct cmsghdr) + 20];
unsigned char *ctl_buf = ctl;
struct msghdr kern_msg;
int err, total_len;
if(msghdr_from_user32_to_kern(&kern_msg, user_msg))
return -EFAULT;
if(kern_msg.msg_iovlen > UIO_MAXIOV)
return -EINVAL;
err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ);
if (err < 0)
goto out;
total_len = err;
if(kern_msg.msg_controllen) {
err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl));
if(err)
goto out_freeiov;
ctl_buf = kern_msg.msg_control;
}
kern_msg.msg_flags = user_flags;
sock = sockfd_lookup(fd, &err);
if (sock != NULL) {
if (sock->file->f_flags & O_NONBLOCK)
kern_msg.msg_flags |= MSG_DONTWAIT;
err = sock_sendmsg(sock, &kern_msg, total_len);
sockfd_put(sock);
}
/* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
if(ctl_buf != ctl)
kfree(ctl_buf);
out_freeiov:
if(kern_msg.msg_iov != iov)
kfree(kern_msg.msg_iov);
out:
return err;
}
asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags)
{
struct iovec iovstack[UIO_FASTIOV];
struct msghdr kern_msg;
char addr[MAX_SOCK_ADDR];
struct socket *sock;
struct iovec *iov = iovstack;
struct sockaddr *uaddr;
int *uaddr_len;
unsigned long cmsg_ptr;
int err, total_len, len = 0;
if(msghdr_from_user32_to_kern(&kern_msg, user_msg))
return -EFAULT;
if(kern_msg.msg_iovlen > UIO_MAXIOV)
return -EINVAL;
uaddr = kern_msg.msg_name;
uaddr_len = &user_msg->msg_namelen;
err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE);
if (err < 0)
goto out;
total_len = err;
cmsg_ptr = (unsigned long) kern_msg.msg_control;
kern_msg.msg_flags = 0;
sock = sockfd_lookup(fd, &err);
if (sock != NULL) {
struct sock_iocb *si;
struct kiocb iocb;
if (sock->file->f_flags & O_NONBLOCK)
user_flags |= MSG_DONTWAIT;
init_sync_kiocb(&iocb, NULL);
si = kiocb_to_siocb(&iocb);
si->sock = sock;
si->scm = &si->async_scm;
si->msg = &kern_msg;
si->size = total_len;
si->flags = user_flags;
memset(si->scm, 0, sizeof(*si->scm));
err = sock->ops->recvmsg(&iocb, sock, &kern_msg, total_len,
user_flags, si->scm);
if (-EIOCBQUEUED == err)
err = wait_on_sync_kiocb(&iocb);
if(err >= 0) {
len = err;
if(!kern_msg.msg_control) {
if(sock->passcred || si->scm->fp)
kern_msg.msg_flags |= MSG_CTRUNC;
if(si->scm->fp)
__scm_destroy(si->scm);
} else {
/* If recvmsg processing itself placed some
* control messages into user space, it's is
* using 64-bit CMSG processing, so we need
* to fix it up before we tack on more stuff.
*/
if((unsigned long) kern_msg.msg_control != cmsg_ptr)
cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr);
/* Wheee... */
if(sock->passcred)
put_cmsg32(&kern_msg,
SOL_SOCKET, SCM_CREDENTIALS,
sizeof(si->scm->creds),
&si->scm->creds);
if(si->scm->fp != NULL)
scm_detach_fds32(&kern_msg, si->scm);
}
}
sockfd_put(sock);
}
if(uaddr != NULL && err >= 0)
err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len);
if(cmsg_ptr != 0 && err >= 0) {
unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control);
__kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr);
err |= __put_user(uclen, &user_msg->msg_controllen);
}
if(err >= 0)
err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags);
if(kern_msg.msg_iov != iov)
kfree(kern_msg.msg_iov);
out:
if(err < 0)
return err;
return len;
}
#endif
/* /*
* BSD sendmsg interface * BSD sendmsg interface
*/ */
...@@ -2742,6 +2615,63 @@ int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags) ...@@ -2742,6 +2615,63 @@ int sys32_sendmsg(int fd, struct msghdr32 *msg, unsigned flags)
return err; return err;
} }
static __inline__ void
scm_recv32(struct socket *sock, struct msghdr *msg,
struct scm_cookie *scm, int flags, unsigned long cmsg_ptr)
{
if(!msg->msg_control)
{
if(sock->passcred || scm->fp)
msg->msg_flags |= MSG_CTRUNC;
scm_destroy(scm);
return;
}
/* If recvmsg processing itself placed some
* control messages into user space, it's is
* using 64-bit CMSG processing, so we need
* to fix it up before we tack on more stuff.
*/
if((unsigned long) msg->msg_control != cmsg_ptr)
cmsg32_recvmsg_fixup(msg, cmsg_ptr);
/* Wheee... */
if(sock->passcred)
put_cmsg32(msg,
SOL_SOCKET, SCM_CREDENTIALS,
sizeof(scm->creds), &scm->creds);
if(!scm->fp)
return;
scm_detach_fds32(msg, scm);
}
static int
sock_recvmsg32(struct socket *sock, struct msghdr *msg, int size, int flags,
unsigned long cmsg_ptr)
{
struct sock_iocb *si;
struct kiocb iocb;
init_sync_kiocb(&iocb, NULL);
si = kiocb_to_siocb(&iocb);
si->sock = sock;
si->scm = &si->async_scm;
si->msg = msg;
si->size = size;
si->flags = flags;
memset(si->scm, 0, sizeof(*si->scm));
size = sock->ops->recvmsg(&iocb, sock, msg, size, flags, si->scm);
if (size >= 0)
scm_recv32(sock, msg, si->scm, flags, cmsg_ptr);
if (-EIOCBQUEUED == size)
size = wait_on_sync_kiocb(&iocb);
return size;
}
/* /*
* BSD recvmsg interface * BSD recvmsg interface
*/ */
...@@ -2755,8 +2685,6 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags) ...@@ -2755,8 +2685,6 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
struct msghdr msg_sys; struct msghdr msg_sys;
unsigned long cmsg_ptr; unsigned long cmsg_ptr;
int err, iov_size, total_len, len; int err, iov_size, total_len, len;
struct sock_iocb *si;
struct kiocb iocb;
/* kernel mode address */ /* kernel mode address */
char addr[MAX_SOCK_ADDR]; char addr[MAX_SOCK_ADDR];
...@@ -2803,20 +2731,7 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags) ...@@ -2803,20 +2731,7 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
if (sock->file->f_flags & O_NONBLOCK) if (sock->file->f_flags & O_NONBLOCK)
flags |= MSG_DONTWAIT; flags |= MSG_DONTWAIT;
err = sock_recvmsg32(sock, &msg_sys, total_len, flags, cmsg_ptr);
init_sync_kiocb(&iocb, NULL);
si = kiocb_to_siocb(&iocb);
si->sock = sock;
si->scm = &si->async_scm;
si->msg = &msg_sys;
si->size = total_len;
si->flags = flags;
memset(si->scm, 0, sizeof(*si->scm));
err = sock->ops->recvmsg(&iocb, sock, &msg_sys, total_len,
flags, si->scm);
if (-EIOCBQUEUED == err)
err = wait_on_sync_kiocb(&iocb);
if (err < 0) if (err < 0)
goto out_freeiov; goto out_freeiov;
len = err; len = err;
...@@ -2828,27 +2743,13 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags) ...@@ -2828,27 +2743,13 @@ sys32_recvmsg (int fd, struct msghdr32 *msg, unsigned int flags)
if (err < 0) if (err < 0)
goto out_freeiov; goto out_freeiov;
} }
if(!msg_sys.msg_control) { err = __put_user(msg_sys.msg_flags, &msg->msg_flags);
if(sock->passcred || si->scm->fp) if (err)
msg_sys.msg_flags |= MSG_CTRUNC; goto out_freeiov;
if(si->scm->fp) err = __put_user((__kernel_size_t32) ((unsigned long)msg_sys.msg_control - cmsg_ptr), &msg->msg_controllen);
__scm_destroy(si->scm); if (err)
} else { goto out_freeiov;
/* If recvmsg processing itself placed some err = len;
* control messages into user space, it's is
* using 64-bit CMSG processing, so we need
* to fix it up before we tack on more stuff.
*/
if((unsigned long) msg_sys.msg_control != cmsg_ptr)
cmsg32_recvmsg_fixup(&msg_sys, cmsg_ptr);
/* Wheee... */
if(sock->passcred)
put_cmsg32(&msg_sys,
SOL_SOCKET, SCM_CREDENTIALS,
sizeof(si->scm->creds), &si->scm->creds);
if(si->scm->fp != NULL)
scm_detach_fds32(&msg_sys, si->scm);
}
out_freeiov: out_freeiov:
if (iov != iovstack) if (iov != iovstack)
...@@ -2941,6 +2842,20 @@ asmlinkage int sys32_setsockopt(int fd, int level, int optname, ...@@ -2941,6 +2842,20 @@ asmlinkage int sys32_setsockopt(int fd, int level, int optname,
if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER) if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER)
return do_set_icmpv6_filter(fd, level, optname, return do_set_icmpv6_filter(fd, level, optname,
optval, optlen); optval, optlen);
if (level == SOL_SOCKET &&
(optname == SO_SNDTIMEO || optname == SO_RCVTIMEO)) {
long ret;
struct timeval tmp;
mm_segment_t old_fs;
if (get_tv32(&tmp, (struct timeval32 *)optval ))
return -EFAULT;
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_setsockopt(fd, level, optname, (char *) &tmp, sizeof(struct timeval));
set_fs(old_fs);
return ret;
}
return sys_setsockopt(fd, level, optname, optval, optlen); return sys_setsockopt(fd, level, optname, optval, optlen);
} }
...@@ -3897,12 +3812,16 @@ typedef __kernel_ssize_t32 ssize_t32; ...@@ -3897,12 +3812,16 @@ typedef __kernel_ssize_t32 ssize_t32;
asmlinkage ssize_t32 sys32_pread64(unsigned int fd, char *ubuf, asmlinkage ssize_t32 sys32_pread64(unsigned int fd, char *ubuf,
__kernel_size_t32 count, u32 poshi, u32 poslo) __kernel_size_t32 count, u32 poshi, u32 poslo)
{ {
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
} }
asmlinkage ssize_t32 sys32_pwrite64(unsigned int fd, char *ubuf, asmlinkage ssize_t32 sys32_pwrite64(unsigned int fd, char *ubuf,
__kernel_size_t32 count, u32 poshi, u32 poslo) __kernel_size_t32 count, u32 poshi, u32 poslo)
{ {
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
} }
...@@ -4436,26 +4355,38 @@ asmlinkage int ...@@ -4436,26 +4355,38 @@ asmlinkage int
sys32_futex(void *uaddr, int op, int val, sys32_futex(void *uaddr, int op, int val,
struct timespec32 *timeout32) struct timespec32 *timeout32)
{ {
long ret; struct timespec tmp;
struct timespec tmp, *timeout; mm_segment_t old_fs;
int ret;
ret = -ENOMEM;
timeout = kmalloc(sizeof(*timeout), GFP_USER);
if (!timeout)
goto out;
ret = -EINVAL;
if (get_user (tmp.tv_sec, &timeout32->tv_sec) || if (get_user (tmp.tv_sec, &timeout32->tv_sec) ||
get_user (tmp.tv_nsec, &timeout32->tv_nsec) || get_user (tmp.tv_nsec, &timeout32->tv_nsec))
put_user (tmp.tv_sec, &timeout->tv_sec) || return -EINVAL;
put_user (tmp.tv_nsec, &timeout->tv_nsec))
goto out_free;
ret = sys_futex(uaddr, op, val, timeout); old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_futex(uaddr, op, val, &tmp);
set_fs(old_fs);
out_free:
kfree(timeout);
out:
return ret; return ret;
} }
asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count);
asmlinkage ssize_t32 sys32_read(unsigned int fd, char * buf, size_t count)
{
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_read(fd, buf, count);
}
asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count);
asmlinkage ssize_t32 sys32_write(unsigned int fd, char * buf, size_t count)
{
if ((ssize_t32) count < 0)
return -EINVAL;
return sys_write(fd, buf, count);
}
...@@ -60,6 +60,20 @@ EXPORT_SYMBOL(dump_fpu); ...@@ -60,6 +60,20 @@ EXPORT_SYMBOL(dump_fpu);
EXPORT_SYMBOL(overflowuid); EXPORT_SYMBOL(overflowuid);
EXPORT_SYMBOL(overflowgid); EXPORT_SYMBOL(overflowgid);
#ifdef CONFIG_S390_SUPPORT
/*
* Dynamically add/remove 31 bit ioctl conversion functions.
*/
extern int register_ioctl32_conversion(unsigned int cmd,
int (*handler)(unsigned int,
unsigned int,
unsigned long,
struct file *));
int unregister_ioctl32_conversion(unsigned int cmd);
EXPORT_SYMBOL(register_ioctl32_conversion);
EXPORT_SYMBOL(unregister_ioctl32_conversion);
#endif
/* /*
* misc. * misc.
*/ */
......
...@@ -17,14 +17,14 @@ sys32_read_wrapper: ...@@ -17,14 +17,14 @@ sys32_read_wrapper:
llgfr %r2,%r2 # unsigned int llgfr %r2,%r2 # unsigned int
llgtr %r3,%r3 # char * llgtr %r3,%r3 # char *
llgfr %r4,%r4 # size_t llgfr %r4,%r4 # size_t
jg sys_read # branch to sys_read jg sys32_read # branch to sys_read
.globl sys32_write_wrapper .globl sys32_write_wrapper
sys32_write_wrapper: sys32_write_wrapper:
llgfr %r2,%r2 # unsigned int llgfr %r2,%r2 # unsigned int
llgtr %r3,%r3 # const char * llgtr %r3,%r3 # const char *
llgfr %r4,%r4 # size_t llgfr %r4,%r4 # size_t
jg sys_write # branch to system call jg sys32_write # branch to system call
.globl sys32_open_wrapper .globl sys32_open_wrapper
sys32_open_wrapper: sys32_open_wrapper:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment