diff options
Diffstat (limited to 'arch/arm64/kernel/sys_ilp32.c')
-rw-r--r-- | arch/arm64/kernel/sys_ilp32.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/arch/arm64/kernel/sys_ilp32.c b/arch/arm64/kernel/sys_ilp32.c new file mode 100644 index 000000000000..889f1d39cab8 --- /dev/null +++ b/arch/arm64/kernel/sys_ilp32.c @@ -0,0 +1,278 @@ +/* + * AArch64- ILP32 specific system calls implementation + * + * Copyright (C) 2015 Cavium Inc. + * Author: Andrew Pinski <apinski@cavium.com> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/msg.h> +#include <linux/export.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/syscalls.h> +#include <linux/compat.h> + +/* + * Wrappers to pass the pt_regs argument. + */ +asmlinkage long sys_rt_sigreturn_wrapper(void); +#define sys_rt_sigreturn sys_rt_sigreturn_wrapper +#define sys_rt_sigsuspend compat_sys_rt_sigsuspend +#define sys_rt_sigaction compat_sys_rt_sigaction +#define sys_rt_sigprocmask compat_sys_rt_sigprocmask +#define sys_rt_sigpending compat_sys_rt_sigpending +#define sys_rt_sigtimedwait compat_sys_rt_sigtimedwait +#define sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +#define sys_rt_sigpending compat_sys_rt_sigpending + +/* Using Compat syscalls where necessary */ +#define sys_ioctl compat_sys_ioctl +/* iovec */ +#define sys_readv compat_sys_readv +#define sys_writev compat_sys_writev +#define sys_preadv compat_sys_preadv64 +#define sys_pwritev compat_sys_pwritev64 +#define sys_vmsplice compat_sys_vmsplice +/* robust_list_head */ +#define sys_set_robust_list compat_sys_set_robust_list +#define sys_get_robust_list compat_sys_get_robust_list + +/* kexec_segment */ +#define sys_kexec_load compat_sys_kexec_load + +/* Ptrace has some structures which are different between ILP32 and LP64 */ +#define sys_ptrace compat_sys_ptrace + +/* struct msghdr */ +#define sys_msgctl compat_sys_msgctl +#define sys_recvfrom compat_sys_recvfrom +#define sys_recvmmsg compat_sys_recvmmsg +#define sys_sendmmsg compat_sys_sendmmsg +#define sys_sendmsg compat_sys_sendmsg +#define sys_recvmsg compat_sys_recvmsg +#define sys_msgsnd compat_sys_msgsnd +#define sys_msgrcv compat_sys_msgrcv + +#define sys_setsockopt compat_sys_setsockopt +#define sys_getsockopt compat_sys_getsockopt + +/* Array of pointers */ +#define sys_execve compat_sys_execve +#define sys_move_pages compat_sys_move_pages + +/* iovec */ +#define sys_process_vm_readv compat_sys_process_vm_readv +#define sys_process_vm_writev compat_sys_process_vm_writev + +/* Pointer in struct */ +#define sys_mount compat_sys_mount + +/* NUMA */ +/* unsigned long bitmaps */ +#define sys_get_mempolicy compat_sys_get_mempolicy +#define sys_set_mempolicy compat_sys_set_mempolicy +#define sys_mbind compat_sys_mbind +/* array of pointers */ +/* unsigned long bitmaps */ +#define sys_migrate_pages compat_sys_migrate_pages + +/* Scheduler */ +/* unsigned long bitmaps */ +#define sys_sched_setaffinity compat_sys_sched_setaffinity +#define sys_sched_getaffinity compat_sys_sched_getaffinity + +/* iov usage */ +#define sys_keyctl compat_sys_keyctl + +/* aio */ +/* Pointer to Pointer */ +#define sys_io_setup compat_sys_io_setup +/* Array of pointers */ +#define sys_io_submit compat_sys_io_submit + +#define sys_nanosleep compat_sys_nanosleep + +#define sys_lseek sys_llseek + +#define sys_setitimer compat_sys_setitimer +#define sys_getitimer compat_sys_getitimer + +#define sys_gettimeofday compat_sys_gettimeofday +#define sys_settimeofday compat_sys_settimeofday +#define sys_adjtimex compat_sys_adjtimex + +#define sys_clock_gettime compat_sys_clock_gettime +#define sys_clock_settime compat_sys_clock_settime + +#define sys_timerfd_gettime compat_sys_timerfd_gettime +#define sys_timerfd_settime compat_sys_timerfd_settime +#define sys_utimensat compat_sys_utimensat + +#define sys_getrlimit compat_sys_getrlimit +#define sys_setrlimit compat_sys_setrlimit +#define sys_getrusage compat_sys_getrusage + +#define sys_futex compat_sys_futex +#define sys_get_robust_list compat_sys_get_robust_list +#define sys_set_robust_list compat_sys_set_robust_list + +#define sys_pselect6 compat_sys_pselect6 +#define sys_ppoll compat_sys_ppoll + +asmlinkage long compat_sys_mmap2_wrapper(void); +#define sys_mmap compat_sys_mmap2_wrapper + +asmlinkage long compat_sys_fstatfs64_wrapper(void); +#define sys_fstatfs compat_sys_fstatfs64_wrapper +asmlinkage long compat_sys_statfs64_wrapper(void); +#define sys_statfs compat_sys_statfs64_wrapper + +/* We need to make sure the pointer gets copied correctly. */ +asmlinkage long ilp32_sys_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification) +{ + struct sigevent __user *p = NULL; + if (u_notification) { + struct sigevent n; + p = compat_alloc_user_space(sizeof(*p)); + if (copy_from_user(&n, u_notification, sizeof(*p))) + return -EFAULT; + if (n.sigev_notify == SIGEV_THREAD) + n.sigev_value.sival_ptr = compat_ptr((uintptr_t)n.sigev_value.sival_ptr); + if (copy_to_user(p, &n, sizeof(*p))) + return -EFAULT; + } + return sys_mq_notify(mqdes, p); +} + +/* sigevent contains sigval_t which is now 64bit always + but need special handling due to padding for SIGEV_THREAD. */ +#define sys_mq_notify ilp32_sys_mq_notify + +/* sigaltstack needs some special handling as the + padding for stack_t might not be non-zero. */ +long ilp32_sys_sigaltstack(const stack_t __user *uss_ptr, + stack_t __user *uoss_ptr) +{ + stack_t uss, uoss; + int ret; + mm_segment_t seg; + + if (uss_ptr) { + if (!access_ok(VERIFY_READ, uss_ptr, sizeof(*uss_ptr))) + return -EFAULT; + if (__get_user(uss.ss_sp, &uss_ptr->ss_sp) | + __get_user(uss.ss_flags, &uss_ptr->ss_flags) | + __get_user(uss.ss_size, &uss_ptr->ss_size)) + return -EFAULT; + /* Zero extend the sp address and the size. */ + uss.ss_sp = (void *)(uintptr_t)(unsigned int)(uintptr_t)uss.ss_sp; + uss.ss_size = (size_t)(unsigned int)uss.ss_size; + } + seg = get_fs(); + set_fs(KERNEL_DS); + /* Note we need to use uoss as we have changed the segment to the + kernel one so passing an user one around is wrong. */ + ret = sys_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL), + (stack_t __force __user *) &uoss); + set_fs(seg); + if (ret >= 0 && uoss_ptr) { + if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_t)) || + __put_user(uoss.ss_sp, &uoss_ptr->ss_sp) || + __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) || + __put_user(uoss.ss_size, &uoss_ptr->ss_size)) + ret = -EFAULT; + } + return ret; +} + +/* sigaltstack needs some special handling as the padding + for stack_t might not be non-zero. */ +#define sys_sigaltstack ilp32_sys_sigaltstack + +/* fs */ + +#define sys_fcntl compat_sys_fcntl + +static long __cp_new_stat64(struct kstat *stat, struct __stat64 __user *statbuf) +{ + struct __stat64 tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.st_dev = huge_encode_dev(stat->dev); + tmp.st_rdev = huge_encode_dev(stat->rdev); + + tmp.st_ino = stat->ino; + if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) + return -EOVERFLOW; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); + tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); + tmp.st_atime = stat->atime.tv_sec; + tmp.st_atime_nsec = stat->atime.tv_nsec; + tmp.st_mtime = stat->mtime.tv_sec; + tmp.st_mtime_nsec = stat->mtime.tv_nsec; + tmp.st_ctime = stat->ctime.tv_sec; + tmp.st_ctime_nsec = stat->ctime.tv_nsec; + tmp.st_size = stat->size; + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +long ilp32_sys_fstat64(int fd, struct __stat64 __user * statbuf) +{ + struct kstat stat; + int error = vfs_fstat(fd, &stat); + + if (!error) + error = __cp_new_stat64(&stat, statbuf); + + return error; +} + +#define sys_newfstat ilp32_sys_fstat64 + +long ilp32_sys_fstatat64(int dfd, const char __user * filename, + struct __stat64 __user * statbuf, int flag) +{ + struct kstat stat; + int error; + + error = vfs_fstatat(dfd, filename, &stat, flag); + if (error) + return error; + return __cp_new_stat64(&stat, statbuf); +} + +#define sys_newfstatat ilp32_sys_fstatat64 + +#include <asm/syscall.h> + +#undef __SYSCALL +#define __SYSCALL(nr, sym) [nr] = sym, + +/* + * The sys_call_ilp32_table array must be 4K aligned to be accessible from + * kernel/entry.S. + */ +void *sys_call_ilp32_table[__NR_syscalls] __aligned(4096) = { + [0 ... __NR_syscalls - 1] = sys_ni_syscall, +#include <asm/unistd.h> +}; |