diff options
Diffstat (limited to 'arch/sparc64/solaris')
-rw-r--r-- | arch/sparc64/solaris/Makefile | 10 | ||||
-rw-r--r-- | arch/sparc64/solaris/conv.h | 38 | ||||
-rw-r--r-- | arch/sparc64/solaris/entry64.S | 218 | ||||
-rw-r--r-- | arch/sparc64/solaris/fs.c | 739 | ||||
-rw-r--r-- | arch/sparc64/solaris/ioctl.c | 820 | ||||
-rw-r--r-- | arch/sparc64/solaris/ipc.c | 127 | ||||
-rw-r--r-- | arch/sparc64/solaris/misc.c | 784 | ||||
-rw-r--r-- | arch/sparc64/solaris/signal.c | 430 | ||||
-rw-r--r-- | arch/sparc64/solaris/signal.h | 108 | ||||
-rw-r--r-- | arch/sparc64/solaris/socket.c | 415 | ||||
-rw-r--r-- | arch/sparc64/solaris/socksys.c | 211 | ||||
-rw-r--r-- | arch/sparc64/solaris/socksys.h | 208 | ||||
-rw-r--r-- | arch/sparc64/solaris/systbl.S | 314 | ||||
-rw-r--r-- | arch/sparc64/solaris/timod.c | 959 |
14 files changed, 5381 insertions, 0 deletions
diff --git a/arch/sparc64/solaris/Makefile b/arch/sparc64/solaris/Makefile new file mode 100644 index 00000000000..8c8663033bf --- /dev/null +++ b/arch/sparc64/solaris/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the Solaris binary emulation. +# + +EXTRA_AFLAGS := -ansi + +solaris-objs := entry64.o fs.o misc.o signal.o systbl.o socket.o \ + ioctl.o ipc.o socksys.o timod.o + +obj-$(CONFIG_SOLARIS_EMUL) += solaris.o diff --git a/arch/sparc64/solaris/conv.h b/arch/sparc64/solaris/conv.h new file mode 100644 index 00000000000..5faf59a9de3 --- /dev/null +++ b/arch/sparc64/solaris/conv.h @@ -0,0 +1,38 @@ +/* $Id: conv.h,v 1.4 1998/08/15 20:42:51 davem Exp $ + * conv.h: Utility macros for Solaris emulation + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +/* #define DEBUG_SOLARIS */ +#define DEBUG_SOLARIS_KMALLOC + +#ifndef __ASSEMBLY__ + +#include <asm/unistd.h> + +/* Use this to get at 32-bit user passed pointers. */ +#define A(__x) \ +({ unsigned long __ret; \ + __asm__ ("srl %0, 0, %0" \ + : "=r" (__ret) \ + : "0" (__x)); \ + (void __user *)__ret; \ +}) + +extern unsigned sys_call_table[]; +extern unsigned sys_call_table32[]; +extern unsigned sunos_sys_table[]; + +#define SYS(name) ((long)sys_call_table[__NR_##name]) +#define SUNOS(x) ((long)sunos_sys_table[x]) + +#ifdef DEBUG_SOLARIS +#define SOLD(s) printk("%s,%d,%s(): %s\n",__FILE__,__LINE__,__FUNCTION__,(s)) +#define SOLDD(s) printk("solaris: "); printk s +#else +#define SOLD(s) +#define SOLDD(s) +#endif + +#endif /* __ASSEMBLY__ */ diff --git a/arch/sparc64/solaris/entry64.S b/arch/sparc64/solaris/entry64.S new file mode 100644 index 00000000000..0cc9dad75c5 --- /dev/null +++ b/arch/sparc64/solaris/entry64.S @@ -0,0 +1,218 @@ +/* $Id: entry64.S,v 1.7 2002/02/09 19:49:31 davem Exp $ + * entry64.S: Solaris syscall emulation entry point. + * + * Copyright (C) 1996,1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + */ + +#include <linux/errno.h> + +#include <asm/head.h> +#include <asm/asi.h> +#include <asm/smp.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/signal.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/thread_info.h> + +#include "conv.h" + +#define NR_SYSCALLS 256 + + .text +solaris_syscall_trace: + call syscall_trace + nop + srl %i0, 0, %o0 + mov %i4, %o4 + srl %i1, 0, %o1 + mov %i5, %o5 + andcc %l3, 1, %g0 + be,pt %icc, 2f + srl %i2, 0, %o2 + b,pt %xcc, 2f + add %sp, PTREGS_OFF, %o0 + +solaris_sucks: +/* Solaris is a big system which needs to be able to do all the things + * in Inf+1 different ways */ + add %i6, 0x5c, %o0 + mov %i0, %g1 + mov %i1, %i0 + mov %i2, %i1 + srl %o0, 0, %o0 + mov %i3, %i2 + movrz %g1, 256, %g1 /* Ensure we don't loop forever */ + mov %i4, %i3 + mov %i5, %i4 + ba,pt %xcc, solaris_sparc_syscall +exen: lduwa [%o0] ASI_S, %i5 + +exenf: ba,pt %xcc, solaris_sparc_syscall + clr %i5 + +/* For shared binaries, binfmt_elf32 already sets up personality + and exec_domain. This is to handle static binaries as well */ +solaris_reg: + call solaris_register + nop + ba,pt %xcc, 1f + mov %i4, %o4 + +linux_syscall_for_solaris: + sethi %hi(sys_call_table32), %l6 + or %l6, %lo(sys_call_table32), %l6 + sll %l3, 2, %l4 + ba,pt %xcc, 10f + lduw [%l6 + %l4], %l3 + + /* Solaris system calls enter here... */ + .align 32 + .globl solaris_sparc_syscall, entry64_personality_patch +solaris_sparc_syscall: +entry64_personality_patch: + ldub [%g4 + 0x0], %l0 + cmp %g1, 255 + bg,pn %icc, solaris_unimplemented + srl %g1, 0, %g1 + sethi %hi(solaris_sys_table), %l7 + or %l7, %lo(solaris_sys_table), %l7 + brz,pn %g1, solaris_sucks + mov %i4, %o4 + sll %g1, 2, %l4 + cmp %l0, 1 + bne,pn %icc, solaris_reg +1: srl %i0, 0, %o0 + lduw [%l7 + %l4], %l3 + srl %i1, 0, %o1 + ldx [%g6 + TI_FLAGS], %l5 + cmp %l3, NR_SYSCALLS + bleu,a,pn %xcc, linux_syscall_for_solaris + nop + andcc %l3, 1, %g0 + bne,a,pn %icc, 10f + add %sp, PTREGS_OFF, %o0 +10: srl %i2, 0, %o2 + mov %i5, %o5 + andn %l3, 3, %l7 + andcc %l5, _TIF_SYSCALL_TRACE, %g0 + bne,pn %icc, solaris_syscall_trace + mov %i0, %l5 +2: call %l7 + srl %i3, 0, %o3 +ret_from_solaris: + stx %o0, [%sp + PTREGS_OFF + PT_V9_I0] + ldx [%g6 + TI_FLAGS], %l6 + sra %o0, 0, %o0 + mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2 + ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %g3 + cmp %o0, -ERESTART_RESTARTBLOCK + sllx %g2, 32, %g2 + bgeu,pn %xcc, 1f + andcc %l6, _TIF_SYSCALL_TRACE, %l6 + + /* System call success, clear Carry condition code. */ + andn %g3, %g2, %g3 + stx %g3, [%sp + PTREGS_OFF + PT_V9_TSTATE] + bne,pn %icc, solaris_syscall_trace2 + ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %l1 + andcc %l1, 1, %g0 + bne,pn %icc, 2f + clr %l6 + add %l1, 0x4, %l2 + stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC] ! pc = npc + call rtrap + stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] !npc = npc+4 + + /* When tnpc & 1, this comes from setcontext and we don't want to advance pc */ +2: andn %l1, 3, %l1 + call rtrap + stx %l1, [%sp + PTREGS_OFF + PT_V9_TNPC] !npc = npc&~3 + +1: + /* System call failure, set Carry condition code. + * Also, get abs(errno) to return to the process. + */ + sub %g0, %o0, %o0 + or %g3, %g2, %g3 + cmp %o0, ERANGE /* 0-ERANGE are identity mapped */ + bleu,pt %icc, 1f + cmp %o0, EMEDIUMTYPE + bgu,pn %icc, 1f + sethi %hi(solaris_err_table), %l6 + sll %o0, 2, %o0 + or %l6, %lo(solaris_err_table), %l6 + ldsw [%l6 + %o0], %o0 +1: stx %o0, [%sp + PTREGS_OFF + PT_V9_I0] + mov 1, %l6 + stx %g3, [%sp + PTREGS_OFF + PT_V9_TSTATE] + bne,pn %icc, solaris_syscall_trace2 + ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %l1 + andcc %l1, 1, %g0 + bne,pn %icc, 2b + add %l1, 0x4, %l2 + stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC] ! pc = npc + call rtrap + stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] !npc = npc+4 + +solaris_syscall_trace2: + call syscall_trace + add %l1, 0x4, %l2 /* npc = npc+4 */ + andcc %l1, 1, %g0 + bne,pn %icc, 2b + nop + stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC] + call rtrap + stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] + + /* This one is tricky, so that's why we do it in assembly */ + .globl solaris_sigsuspend +solaris_sigsuspend: + call do_sol_sigsuspend + nop + brlz,pn %o0, ret_from_solaris + nop + call sys_sigsuspend + stx %o0, [%sp + PTREGS_OFF + PT_V9_I0] + + .globl solaris_getpid +solaris_getpid: + call sys_getppid + nop + call sys_getpid + stx %o0, [%sp + PTREGS_OFF + PT_V9_I1] + b,pt %xcc, ret_from_solaris + nop + + .globl solaris_getuid +solaris_getuid: + call sys_geteuid + nop + call sys_getuid + stx %o1, [%sp + PTREGS_OFF + PT_V9_I1] + b,pt %xcc, ret_from_solaris + nop + + .globl solaris_getgid +solaris_getgid: + call sys_getegid + nop + call sys_getgid + stx %o1, [%sp + PTREGS_OFF + PT_V9_I1] + b,pt %xcc, ret_from_solaris + nop + + .globl solaris_unimplemented +solaris_unimplemented: + call do_sol_unimplemented + add %sp, PTREGS_OFF, %o0 + ba,pt %xcc, ret_from_solaris + nop + + .section __ex_table,#alloc + .align 4 + .word exen, exenf + diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c new file mode 100644 index 00000000000..d7c99fa8966 --- /dev/null +++ b/arch/sparc64/solaris/fs.c @@ -0,0 +1,739 @@ +/* $Id: fs.c,v 1.27 2002/02/08 03:57:14 davem Exp $ + * fs.c: fs related syscall emulation for Solaris + * + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * + * 1999-08-19 Implemented solaris F_FREESP (truncate) + * fcntl, by Jason Rappleye (rappleye@ccr.buffalo.edu) + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/namei.h> +#include <linux/mm.h> +#include <linux/file.h> +#include <linux/stat.h> +#include <linux/smp_lock.h> +#include <linux/limits.h> +#include <linux/resource.h> +#include <linux/quotaops.h> +#include <linux/mount.h> +#include <linux/vfs.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/ptrace.h> + +#include "conv.h" + +#define R3_VERSION 1 +#define R4_VERSION 2 + +typedef struct { + s32 tv_sec; + s32 tv_nsec; +} timestruct_t; + +struct sol_stat { + u32 st_dev; + s32 st_pad1[3]; /* network id */ + u32 st_ino; + u32 st_mode; + u32 st_nlink; + u32 st_uid; + u32 st_gid; + u32 st_rdev; + s32 st_pad2[2]; + s32 st_size; + s32 st_pad3; /* st_size, off_t expansion */ + timestruct_t st_atime; + timestruct_t st_mtime; + timestruct_t st_ctime; + s32 st_blksize; + s32 st_blocks; + char st_fstype[16]; + s32 st_pad4[8]; /* expansion area */ +}; + +struct sol_stat64 { + u32 st_dev; + s32 st_pad1[3]; /* network id */ + u64 st_ino; + u32 st_mode; + u32 st_nlink; + u32 st_uid; + u32 st_gid; + u32 st_rdev; + s32 st_pad2[2]; + s64 st_size; + timestruct_t st_atime; + timestruct_t st_mtime; + timestruct_t st_ctime; + s64 st_blksize; + s32 st_blocks; + char st_fstype[16]; + s32 st_pad4[4]; /* expansion area */ +}; + +#define UFSMAGIC (((unsigned)'u'<<24)||((unsigned)'f'<<16)||((unsigned)'s'<<8)) + +static inline int putstat(struct sol_stat __user *ubuf, struct kstat *kbuf) +{ + if (kbuf->size > MAX_NON_LFS || + !sysv_valid_dev(kbuf->dev) || + !sysv_valid_dev(kbuf->rdev)) + return -EOVERFLOW; + if (put_user (sysv_encode_dev(kbuf->dev), &ubuf->st_dev) || + __put_user (kbuf->ino, &ubuf->st_ino) || + __put_user (kbuf->mode, &ubuf->st_mode) || + __put_user (kbuf->nlink, &ubuf->st_nlink) || + __put_user (kbuf->uid, &ubuf->st_uid) || + __put_user (kbuf->gid, &ubuf->st_gid) || + __put_user (sysv_encode_dev(kbuf->rdev), &ubuf->st_rdev) || + __put_user (kbuf->size, &ubuf->st_size) || + __put_user (kbuf->atime.tv_sec, &ubuf->st_atime.tv_sec) || + __put_user (kbuf->atime.tv_nsec, &ubuf->st_atime.tv_nsec) || + __put_user (kbuf->mtime.tv_sec, &ubuf->st_mtime.tv_sec) || + __put_user (kbuf->mtime.tv_nsec, &ubuf->st_mtime.tv_nsec) || + __put_user (kbuf->ctime.tv_sec, &ubuf->st_ctime.tv_sec) || + __put_user (kbuf->ctime.tv_nsec, &ubuf->st_ctime.tv_nsec) || + __put_user (kbuf->blksize, &ubuf->st_blksize) || + __put_user (kbuf->blocks, &ubuf->st_blocks) || + __put_user (UFSMAGIC, (unsigned __user *)ubuf->st_fstype)) + return -EFAULT; + return 0; +} + +static inline int putstat64(struct sol_stat64 __user *ubuf, struct kstat *kbuf) +{ + if (!sysv_valid_dev(kbuf->dev) || !sysv_valid_dev(kbuf->rdev)) + return -EOVERFLOW; + if (put_user (sysv_encode_dev(kbuf->dev), &ubuf->st_dev) || + __put_user (kbuf->ino, &ubuf->st_ino) || + __put_user (kbuf->mode, &ubuf->st_mode) || + __put_user (kbuf->nlink, &ubuf->st_nlink) || + __put_user (kbuf->uid, &ubuf->st_uid) || + __put_user (kbuf->gid, &ubuf->st_gid) || + __put_user (sysv_encode_dev(kbuf->rdev), &ubuf->st_rdev) || + __put_user (kbuf->size, &ubuf->st_size) || + __put_user (kbuf->atime.tv_sec, &ubuf->st_atime.tv_sec) || + __put_user (kbuf->atime.tv_nsec, &ubuf->st_atime.tv_nsec) || + __put_user (kbuf->mtime.tv_sec, &ubuf->st_mtime.tv_sec) || + __put_user (kbuf->mtime.tv_nsec, &ubuf->st_mtime.tv_nsec) || + __put_user (kbuf->ctime.tv_sec, &ubuf->st_ctime.tv_sec) || + __put_user (kbuf->ctime.tv_nsec, &ubuf->st_ctime.tv_nsec) || + __put_user (kbuf->blksize, &ubuf->st_blksize) || + __put_user (kbuf->blocks, &ubuf->st_blocks) || + __put_user (UFSMAGIC, (unsigned __user *)ubuf->st_fstype)) + return -EFAULT; + return 0; +} + +asmlinkage int solaris_stat(u32 filename, u32 statbuf) +{ + struct kstat s; + int ret = vfs_stat(A(filename), &s); + if (!ret) + return putstat(A(statbuf), &s); + return ret; +} + +asmlinkage int solaris_xstat(int vers, u32 filename, u32 statbuf) +{ + /* Solaris doesn't bother with looking at vers, so we do neither */ + return solaris_stat(filename, statbuf); +} + +asmlinkage int solaris_stat64(u32 filename, u32 statbuf) +{ + struct kstat s; + int ret = vfs_stat(A(filename), &s); + if (!ret) + return putstat64(A(statbuf), &s); + return ret; +} + +asmlinkage int solaris_lstat(u32 filename, u32 statbuf) +{ + struct kstat s; + int ret = vfs_lstat(A(filename), &s); + if (!ret) + return putstat(A(statbuf), &s); + return ret; +} + +asmlinkage int solaris_lxstat(int vers, u32 filename, u32 statbuf) +{ + return solaris_lstat(filename, statbuf); +} + +asmlinkage int solaris_lstat64(u32 filename, u32 statbuf) +{ + struct kstat s; + int ret = vfs_lstat(A(filename), &s); + if (!ret) + return putstat64(A(statbuf), &s); + return ret; +} + +asmlinkage int solaris_fstat(unsigned int fd, u32 statbuf) +{ + struct kstat s; + int ret = vfs_fstat(fd, &s); + if (!ret) + return putstat(A(statbuf), &s); + return ret; +} + +asmlinkage int solaris_fxstat(int vers, u32 fd, u32 statbuf) +{ + return solaris_fstat(fd, statbuf); +} + +asmlinkage int solaris_fstat64(unsigned int fd, u32 statbuf) +{ + struct kstat s; + int ret = vfs_fstat(fd, &s); + if (!ret) + return putstat64(A(statbuf), &s); + return ret; +} + +asmlinkage int solaris_mknod(u32 path, u32 mode, s32 dev) +{ + int (*sys_mknod)(const char __user *,int,unsigned) = + (int (*)(const char __user *,int,unsigned))SYS(mknod); + int major = sysv_major(dev); + int minor = sysv_minor(dev); + + /* minor is guaranteed to be OK for MKDEV, major might be not */ + if (major > 0xfff) + return -EINVAL; + return sys_mknod(A(path), mode, new_encode_dev(MKDEV(major,minor))); +} + +asmlinkage int solaris_xmknod(int vers, u32 path, u32 mode, s32 dev) +{ + return solaris_mknod(path, mode, dev); +} + +asmlinkage int solaris_getdents64(unsigned int fd, void __user *dirent, unsigned int count) +{ + int (*sys_getdents)(unsigned int, void __user *, unsigned int) = + (int (*)(unsigned int, void __user *, unsigned int))SYS(getdents); + + return sys_getdents(fd, dirent, count); +} + +/* This statfs thingie probably will go in the near future, but... */ + +struct sol_statfs { + short f_type; + s32 f_bsize; + s32 f_frsize; + s32 f_blocks; + s32 f_bfree; + u32 f_files; + u32 f_ffree; + char f_fname[6]; + char f_fpack[6]; +}; + +asmlinkage int solaris_statfs(u32 path, u32 buf, int len, int fstype) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + int (*sys_statfs)(const char __user *,struct statfs __user *) = + (int (*)(const char __user *,struct statfs __user *))SYS(statfs); + struct sol_statfs __user *ss = A(buf); + + if (len != sizeof(struct sol_statfs)) return -EINVAL; + if (!fstype) { + /* FIXME: mixing userland and kernel pointers */ + set_fs (KERNEL_DS); + ret = sys_statfs(A(path), &s); + set_fs (old_fs); + if (!ret) { + if (put_user (s.f_type, &ss->f_type) || + __put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __clear_user (&ss->f_fname, 12)) + return -EFAULT; + } + return ret; + } +/* Linux can't stat unmounted filesystems so we + * simply lie and claim 100MB of 1GB is free. Sorry. + */ + if (put_user (fstype, &ss->f_type) || + __put_user (1024, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (1024*1024, &ss->f_blocks) || + __put_user (100*1024, &ss->f_bfree) || + __put_user (60000, &ss->f_files) || + __put_user (50000, &ss->f_ffree) || + __clear_user (&ss->f_fname, 12)) + return -EFAULT; + return 0; +} + +asmlinkage int solaris_fstatfs(u32 fd, u32 buf, int len, int fstype) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + int (*sys_fstatfs)(unsigned,struct statfs __user *) = + (int (*)(unsigned,struct statfs __user *))SYS(fstatfs); + struct sol_statfs __user *ss = A(buf); + + if (len != sizeof(struct sol_statfs)) return -EINVAL; + if (!fstype) { + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (!ret) { + if (put_user (s.f_type, &ss->f_type) || + __put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __clear_user (&ss->f_fname, 12)) + return -EFAULT; + } + return ret; + } + /* Otherwise fstatfs is the same as statfs */ + return solaris_statfs(0, buf, len, fstype); +} + +struct sol_statvfs { + u32 f_bsize; + u32 f_frsize; + u32 f_blocks; + u32 f_bfree; + u32 f_bavail; + u32 f_files; + u32 f_ffree; + u32 f_favail; + u32 f_fsid; + char f_basetype[16]; + u32 f_flag; + u32 f_namemax; + char f_fstr[32]; + u32 f_filler[16]; +}; + +struct sol_statvfs64 { + u32 f_bsize; + u32 f_frsize; + u64 f_blocks; + u64 f_bfree; + u64 f_bavail; + u64 f_files; + u64 f_ffree; + u64 f_favail; + u32 f_fsid; + char f_basetype[16]; + u32 f_flag; + u32 f_namemax; + char f_fstr[32]; + u32 f_filler[16]; +}; + +static int report_statvfs(struct vfsmount *mnt, struct inode *inode, u32 buf) +{ + struct kstatfs s; + int error; + struct sol_statvfs __user *ss = A(buf); + + error = vfs_statfs(mnt->mnt_sb, &s); + if (!error) { + const char *p = mnt->mnt_sb->s_type->name; + int i = 0; + int j = strlen (p); + + if (j > 15) j = 15; + if (IS_RDONLY(inode)) i = 1; + if (mnt->mnt_flags & MNT_NOSUID) i |= 2; + if (!sysv_valid_dev(inode->i_sb->s_dev)) + return -EOVERFLOW; + if (put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_bavail, &ss->f_bavail) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __put_user (s.f_ffree, &ss->f_favail) || + __put_user (sysv_encode_dev(inode->i_sb->s_dev), &ss->f_fsid) || + __copy_to_user (ss->f_basetype,p,j) || + __put_user (0, (char __user *)&ss->f_basetype[j]) || + __put_user (s.f_namelen, &ss->f_namemax) || + __put_user (i, &ss->f_flag) || + __clear_user (&ss->f_fstr, 32)) + return -EFAULT; + } + return error; +} + +static int report_statvfs64(struct vfsmount *mnt, struct inode *inode, u32 buf) +{ + struct kstatfs s; + int error; + struct sol_statvfs64 __user *ss = A(buf); + + error = vfs_statfs(mnt->mnt_sb, &s); + if (!error) { + const char *p = mnt->mnt_sb->s_type->name; + int i = 0; + int j = strlen (p); + + if (j > 15) j = 15; + if (IS_RDONLY(inode)) i = 1; + if (mnt->mnt_flags & MNT_NOSUID) i |= 2; + if (!sysv_valid_dev(inode->i_sb->s_dev)) + return -EOVERFLOW; + if (put_user (s.f_bsize, &ss->f_bsize) || + __put_user (0, &ss->f_frsize) || + __put_user (s.f_blocks, &ss->f_blocks) || + __put_user (s.f_bfree, &ss->f_bfree) || + __put_user (s.f_bavail, &ss->f_bavail) || + __put_user (s.f_files, &ss->f_files) || + __put_user (s.f_ffree, &ss->f_ffree) || + __put_user (s.f_ffree, &ss->f_favail) || + __put_user (sysv_encode_dev(inode->i_sb->s_dev), &ss->f_fsid) || + __copy_to_user (ss->f_basetype,p,j) || + __put_user (0, (char __user *)&ss->f_basetype[j]) || + __put_user (s.f_namelen, &ss->f_namemax) || + __put_user (i, &ss->f_flag) || + __clear_user (&ss->f_fstr, 32)) + return -EFAULT; + } + return error; +} + +asmlinkage int solaris_statvfs(u32 path, u32 buf) +{ + struct nameidata nd; + int error; + + error = user_path_walk(A(path),&nd); + if (!error) { + struct inode * inode = nd.dentry->d_inode; + error = report_statvfs(nd.mnt, inode, buf); + path_release(&nd); + } + return error; +} + +asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf) +{ + struct file * file; + int error; + + error = -EBADF; + file = fget(fd); + if (file) { + error = report_statvfs(file->f_vfsmnt, file->f_dentry->d_inode, buf); + fput(file); + } + + return error; +} + +asmlinkage int solaris_statvfs64(u32 path, u32 buf) +{ + struct nameidata nd; + int error; + + lock_kernel(); + error = user_path_walk(A(path), &nd); + if (!error) { + struct inode * inode = nd.dentry->d_inode; + error = report_statvfs64(nd.mnt, inode, buf); + path_release(&nd); + } + unlock_kernel(); + return error; +} + +asmlinkage int solaris_fstatvfs64(unsigned int fd, u32 buf) +{ + struct file * file; + int error; + + error = -EBADF; + file = fget(fd); + if (file) { + lock_kernel(); + error = report_statvfs64(file->f_vfsmnt, file->f_dentry->d_inode, buf); + unlock_kernel(); + fput(file); + } + return error; +} + +extern asmlinkage long sparc32_open(const char * filename, int flags, int mode); + +asmlinkage int solaris_open(u32 fname, int flags, u32 mode) +{ + const char *filename = (const char *)(long)fname; + int fl = flags & 0xf; + + /* Translate flags first. */ + if (flags & 0x2000) fl |= O_LARGEFILE; + if (flags & 0x8050) fl |= O_SYNC; + if (flags & 0x80) fl |= O_NONBLOCK; + if (flags & 0x100) fl |= O_CREAT; + if (flags & 0x200) fl |= O_TRUNC; + if (flags & 0x400) fl |= O_EXCL; + if (flags & 0x800) fl |= O_NOCTTY; + flags = fl; + + return sparc32_open(filename, flags, mode); +} + +#define SOL_F_SETLK 6 +#define SOL_F_SETLKW 7 +#define SOL_F_FREESP 11 +#define SOL_F_ISSTREAM 13 +#define SOL_F_GETLK 14 +#define SOL_F_PRIV 15 +#define SOL_F_NPRIV 16 +#define SOL_F_QUOTACTL 17 +#define SOL_F_BLOCKS 18 +#define SOL_F_BLKSIZE 19 +#define SOL_F_GETOWN 23 +#define SOL_F_SETOWN 24 + +struct sol_flock { + short l_type; + short l_whence; + u32 l_start; + u32 l_len; + s32 l_sysid; + s32 l_pid; + s32 l_pad[4]; +}; + +asmlinkage int solaris_fcntl(unsigned fd, unsigned cmd, u32 arg) +{ + int (*sys_fcntl)(unsigned,unsigned,unsigned long) = + (int (*)(unsigned,unsigned,unsigned long))SYS(fcntl); + int ret, flags; + + switch (cmd) { + case F_DUPFD: + case F_GETFD: + case F_SETFD: return sys_fcntl(fd, cmd, (unsigned long)arg); + case F_GETFL: + flags = sys_fcntl(fd, cmd, 0); + ret = flags & 0xf; + if (flags & O_SYNC) ret |= 0x8050; + if (flags & O_NONBLOCK) ret |= 0x80; + return ret; + case F_SETFL: + flags = arg & 0xf; + if (arg & 0x8050) flags |= O_SYNC; + if (arg & 0x80) flags |= O_NONBLOCK; + return sys_fcntl(fd, cmd, (long)flags); + case SOL_F_GETLK: + case SOL_F_SETLK: + case SOL_F_SETLKW: + { + struct flock f; + struct sol_flock __user *p = A(arg); + mm_segment_t old_fs = get_fs(); + + switch (cmd) { + case SOL_F_GETLK: cmd = F_GETLK; break; + case SOL_F_SETLK: cmd = F_SETLK; break; + case SOL_F_SETLKW: cmd = F_SETLKW; break; + } + + if (get_user (f.l_type, &p->l_type) || + __get_user (f.l_whence, &p->l_whence) || + __get_user (f.l_start, &p->l_start) || + __get_user (f.l_len, &p->l_len) || + __get_user (f.l_pid, &p->l_sysid)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs(old_fs); + + if (__put_user (f.l_type, &p->l_type) || + __put_user (f.l_whence, &p->l_whence) || + __put_user (f.l_start, &p->l_start) || + __put_user (f.l_len, &p->l_len) || + __put_user (f.l_pid, &p->l_pid) || + __put_user (0, &p->l_sysid)) + return -EFAULT; + + return ret; + } + case SOL_F_FREESP: + { + int length; + int (*sys_newftruncate)(unsigned int, unsigned long)= + (int (*)(unsigned int, unsigned long))SYS(ftruncate); + + if (get_user(length, &((struct sol_flock __user *)A(arg))->l_start)) + return -EFAULT; + + return sys_newftruncate(fd, length); + } + }; + return -EINVAL; +} + +asmlinkage int solaris_ulimit(int cmd, int val) +{ + switch (cmd) { + case 1: /* UL_GETFSIZE - in 512B chunks */ + return current->signal->rlim[RLIMIT_FSIZE].rlim_cur >> 9; + case 2: /* UL_SETFSIZE */ + if ((unsigned long)val > (LONG_MAX>>9)) return -ERANGE; + val <<= 9; + task_lock(current->group_leader); + if (val > current->signal->rlim[RLIMIT_FSIZE].rlim_max) { + if (!capable(CAP_SYS_RESOURCE)) { + task_unlock(current->group_leader); + return -EPERM; + } + current->signal->rlim[RLIMIT_FSIZE].rlim_max = val; + } + current->signal->rlim[RLIMIT_FSIZE].rlim_cur = val; + task_unlock(current->group_leader); + return 0; + case 3: /* UL_GMEMLIM */ + return current->signal->rlim[RLIMIT_DATA].rlim_cur; + case 4: /* UL_GDESLIM */ + return NR_OPEN; + } + return -EINVAL; +} + +/* At least at the time I'm writing this, Linux doesn't have ACLs, so we + just fake this */ +asmlinkage int solaris_acl(u32 filename, int cmd, int nentries, u32 aclbufp) +{ + return -ENOSYS; +} + +asmlinkage int solaris_facl(unsigned int fd, int cmd, int nentries, u32 aclbufp) +{ + return -ENOSYS; +} + +asmlinkage int solaris_pread(unsigned int fd, char __user *buf, u32 count, u32 pos) +{ + ssize_t (*sys_pread64)(unsigned int, char __user *, size_t, loff_t) = + (ssize_t (*)(unsigned int, char __user *, size_t, loff_t))SYS(pread64); + + return sys_pread64(fd, buf, count, (loff_t)pos); +} + +asmlinkage int solaris_pwrite(unsigned int fd, char __user *buf, u32 count, u32 pos) +{ + ssize_t (*sys_pwrite64)(unsigned int, char __user *, size_t, loff_t) = + (ssize_t (*)(unsigned int, char __user *, size_t, loff_t))SYS(pwrite64); + + return sys_pwrite64(fd, buf, count, (loff_t)pos); +} + +/* POSIX.1 names */ +#define _PC_LINK_MAX 1 +#define _PC_MAX_CANON 2 +#define _PC_MAX_INPUT 3 +#define _PC_NAME_MAX 4 +#define _PC_PATH_MAX 5 +#define _PC_PIPE_BUF 6 +#define _PC_NO_TRUNC 7 +#define _PC_VDISABLE 8 +#define _PC_CHOWN_RESTRICTED 9 +/* POSIX.4 names */ +#define _PC_ASYNC_IO 10 +#define _PC_PRIO_IO 11 +#define _PC_SYNC_IO 12 +#define _PC_LAST 12 + +/* This is not a real and complete implementation yet, just to keep + * the easy Solaris binaries happy. + */ +asmlinkage int solaris_fpathconf(int fd, int name) +{ + int ret; + + switch(name) { + case _PC_LINK_MAX: + ret = LINK_MAX; + break; + case _PC_MAX_CANON: + ret = MAX_CANON; + break; + case _PC_MAX_INPUT: + ret = MAX_INPUT; + break; + case _PC_NAME_MAX: + ret = NAME_MAX; + break; + case _PC_PATH_MAX: + ret = PATH_MAX; + break; + case _PC_PIPE_BUF: + ret = PIPE_BUF; + break; + case _PC_CHOWN_RESTRICTED: + ret = 1; + break; + case _PC_NO_TRUNC: + case _PC_VDISABLE: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +asmlinkage int solaris_pathconf(u32 path, int name) +{ + return solaris_fpathconf(0, name); +} + +/* solaris_llseek returns long long - quite difficult */ +asmlinkage long solaris_llseek(struct pt_regs *regs, u32 off_hi, u32 off_lo, int whence) +{ + int (*sys_llseek)(unsigned int, unsigned long, unsigned long, loff_t __user *, unsigned int) = + (int (*)(unsigned int, unsigned long, unsigned long, loff_t __user *, unsigned int))SYS(_llseek); + int ret; + mm_segment_t old_fs = get_fs(); + loff_t retval; + + set_fs(KERNEL_DS); + ret = sys_llseek((unsigned int)regs->u_regs[UREG_I0], off_hi, off_lo, &retval, whence); + set_fs(old_fs); + if (ret < 0) return ret; + regs->u_regs[UREG_I1] = (u32)retval; + return (retval >> 32); +} + +/* Have to mask out all but lower 3 bits */ +asmlinkage int solaris_access(u32 filename, long mode) +{ + int (*sys_access)(const char __user *, int) = + (int (*)(const char __user *, int))SYS(access); + + return sys_access(A(filename), mode & 7); +} diff --git a/arch/sparc64/solaris/ioctl.c b/arch/sparc64/solaris/ioctl.c new file mode 100644 index 00000000000..cac0a1cf005 --- /dev/null +++ b/arch/sparc64/solaris/ioctl.c @@ -0,0 +1,820 @@ +/* $Id: ioctl.c,v 1.17 2002/02/08 03:57:14 davem Exp $ + * ioctl.c: Solaris ioctl emulation. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997,1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) + * + * Streams & timod emulation based on code + * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) + * + * 1999-08-19 Implemented solaris 'm' (mag tape) and + * 'O' (openprom) ioctls, by Jason Rappleye + * (rappleye@ccr.buffalo.edu) + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/syscalls.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/netdevice.h> +#include <linux/mtio.h> +#include <linux/time.h> +#include <linux/compat.h> + +#include <net/sock.h> + +#include <asm/uaccess.h> +#include <asm/termios.h> +#include <asm/openpromio.h> + +#include "conv.h" +#include "socksys.h" + +extern asmlinkage int compat_sys_ioctl(unsigned int fd, unsigned int cmd, + u32 arg); +asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg); + +extern int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len, + char __user *data_buf, int data_len, int flags); +extern int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, int __user *ctl_len, + char __user *data_buf, int data_maxlen, int __user *data_len, int *flags); + +/* termio* stuff {{{ */ + +struct solaris_termios { + u32 c_iflag; + u32 c_oflag; + u32 c_cflag; + u32 c_lflag; + u8 c_cc[19]; +}; + +struct solaris_termio { + u16 c_iflag; + u16 c_oflag; + u16 c_cflag; + u16 c_lflag; + s8 c_line; + u8 c_cc[8]; +}; + +struct solaris_termiox { + u16 x_hflag; + u16 x_cflag; + u16 x_rflag[5]; + u16 x_sflag; +}; + +static u32 solaris_to_linux_cflag(u32 cflag) +{ + cflag &= 0x7fdff000; + if (cflag & 0x200000) { + int baud = cflag & 0xf; + cflag &= ~0x20000f; + switch (baud) { + case 0: baud = B57600; break; + case 1: baud = B76800; break; + case 2: baud = B115200; break; + case 3: baud = B153600; break; + case 4: baud = B230400; break; + case 5: baud = B307200; break; + case 6: baud = B460800; break; + } + cflag |= CBAUDEX | baud; + } + return cflag; +} + +static u32 linux_to_solaris_cflag(u32 cflag) +{ + cflag &= ~(CMSPAR | CIBAUD); + if (cflag & CBAUDEX) { + int baud = cflag & CBAUD; + cflag &= ~CBAUD; + switch (baud) { + case B57600: baud = 0; break; + case B76800: baud = 1; break; + case B115200: baud = 2; break; + case B153600: baud = 3; break; + case B230400: baud = 4; break; + case B307200: baud = 5; break; + case B460800: baud = 6; break; + case B614400: baud = 7; break; + case B921600: baud = 8; break; +#if 0 + case B1843200: baud = 9; break; +#endif + } + cflag |= 0x200000 | baud; + } + return cflag; +} + +static inline int linux_to_solaris_termio(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct solaris_termio __user *p = A(arg); + int ret; + + ret = sys_ioctl(fd, cmd, (unsigned long)p); + if (!ret) { + u32 cflag; + + if (__get_user (cflag, &p->c_cflag)) + return -EFAULT; + cflag = linux_to_solaris_cflag(cflag); + if (__put_user (cflag, &p->c_cflag)) + return -EFAULT; + } + return ret; +} + +static int solaris_to_linux_termio(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + struct solaris_termio s; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&s, (struct solaris_termio __user *)A(arg), sizeof(struct solaris_termio))) + return -EFAULT; + s.c_cflag = solaris_to_linux_cflag(s.c_cflag); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)&s); + set_fs(old_fs); + return ret; +} + +static inline int linux_to_solaris_termios(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + struct solaris_termios s; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)&s); + set_fs(old_fs); + if (!ret) { + struct solaris_termios __user *p = A(arg); + if (put_user (s.c_iflag, &p->c_iflag) || + __put_user (s.c_oflag, &p->c_oflag) || + __put_user (linux_to_solaris_cflag(s.c_cflag), &p->c_cflag) || + __put_user (s.c_lflag, &p->c_lflag) || + __copy_to_user (p->c_cc, s.c_cc, 16) || + __clear_user (p->c_cc + 16, 2)) + return -EFAULT; + } + return ret; +} + +static int solaris_to_linux_termios(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + struct solaris_termios s; + struct solaris_termios __user *p = A(arg); + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, TCGETS, (unsigned long)&s); + set_fs(old_fs); + if (ret) return ret; + if (put_user (s.c_iflag, &p->c_iflag) || + __put_user (s.c_oflag, &p->c_oflag) || + __put_user (s.c_cflag, &p->c_cflag) || + __put_user (s.c_lflag, &p->c_lflag) || + __copy_from_user (s.c_cc, p->c_cc, 16)) + return -EFAULT; + s.c_cflag = solaris_to_linux_cflag(s.c_cflag); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, cmd, (unsigned long)&s); + set_fs(old_fs); + return ret; +} + +static inline int solaris_T(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 1: /* TCGETA */ + return linux_to_solaris_termio(fd, TCGETA, arg); + case 2: /* TCSETA */ + return solaris_to_linux_termio(fd, TCSETA, arg); + case 3: /* TCSETAW */ + return solaris_to_linux_termio(fd, TCSETAW, arg); + case 4: /* TCSETAF */ + return solaris_to_linux_termio(fd, TCSETAF, arg); + case 5: /* TCSBRK */ + return sys_ioctl(fd, TCSBRK, arg); + case 6: /* TCXONC */ + return sys_ioctl(fd, TCXONC, arg); + case 7: /* TCFLSH */ + return sys_ioctl(fd, TCFLSH, arg); + case 13: /* TCGETS */ + return linux_to_solaris_termios(fd, TCGETS, arg); + case 14: /* TCSETS */ + return solaris_to_linux_termios(fd, TCSETS, arg); + case 15: /* TCSETSW */ + return solaris_to_linux_termios(fd, TCSETSW, arg); + case 16: /* TCSETSF */ + return solaris_to_linux_termios(fd, TCSETSF, arg); + case 103: /* TIOCSWINSZ */ + return sys_ioctl(fd, TIOCSWINSZ, arg); + case 104: /* TIOCGWINSZ */ + return sys_ioctl(fd, TIOCGWINSZ, arg); + } + return -ENOSYS; +} + +static inline int solaris_t(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 20: /* TIOCGPGRP */ + return sys_ioctl(fd, TIOCGPGRP, arg); + case 21: /* TIOCSPGRP */ + return sys_ioctl(fd, TIOCSPGRP, arg); + } + return -ENOSYS; +} + +/* }}} */ + +/* A pseudo STREAMS support {{{ */ + +struct strioctl { + int cmd, timeout, len; + u32 data; +}; + +struct solaris_si_sockparams { + int sp_family; + int sp_type; + int sp_protocol; +}; + +struct solaris_o_si_udata { + int tidusize; + int addrsize; + int optsize; + int etsdusize; + int servtype; + int so_state; + int so_options; + int tsdusize; +}; + +struct solaris_si_udata { + int tidusize; + int addrsize; + int optsize; + int etsdusize; + int servtype; + int so_state; + int so_options; + int tsdusize; + struct solaris_si_sockparams sockparams; +}; + +#define SOLARIS_MODULE_TIMOD 0 +#define SOLARIS_MODULE_SOCKMOD 1 +#define SOLARIS_MODULE_MAX 2 + +static struct module_info { + const char *name; + /* can be expanded further if needed */ +} module_table[ SOLARIS_MODULE_MAX + 1 ] = { + /* the ordering here must match the module numbers above! */ + { "timod" }, + { "sockmod" }, + { NULL } +}; + +static inline int solaris_sockmod(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct inode *ino; + /* I wonder which of these tests are superfluous... --patrik */ + spin_lock(¤t->files->file_lock); + if (! current->files->fd[fd] || + ! current->files->fd[fd]->f_dentry || + ! (ino = current->files->fd[fd]->f_dentry->d_inode) || + ! S_ISSOCK(ino->i_mode)) { + spin_unlock(¤t->files->file_lock); + return TBADF; + } + spin_unlock(¤t->files->file_lock); + + switch (cmd & 0xff) { + case 109: /* SI_SOCKPARAMS */ + { + struct solaris_si_sockparams si; + if (copy_from_user (&si, A(arg), sizeof(si))) + return (EFAULT << 8) | TSYSERR; + + /* Should we modify socket ino->socket_i.ops and type? */ + return 0; + } + case 110: /* SI_GETUDATA */ + { + int etsdusize, servtype; + struct solaris_si_udata __user *p = A(arg); + switch (SOCKET_I(ino)->type) { + case SOCK_STREAM: + etsdusize = 1; + servtype = 2; + break; + default: + etsdusize = -2; + servtype = 3; + break; + } + if (put_user(16384, &p->tidusize) || + __put_user(sizeof(struct sockaddr), &p->addrsize) || + __put_user(-1, &p->optsize) || + __put_user(etsdusize, &p->etsdusize) || + __put_user(servtype, &p->servtype) || + __put_user(0, &p->so_state) || + __put_user(0, &p->so_options) || + __put_user(16384, &p->tsdusize) || + __put_user(SOCKET_I(ino)->ops->family, &p->sockparams.sp_family) || + __put_user(SOCKET_I(ino)->type, &p->sockparams.sp_type) || + __put_user(SOCKET_I(ino)->ops->family, &p->sockparams.sp_protocol)) + return (EFAULT << 8) | TSYSERR; + return 0; + } + case 101: /* O_SI_GETUDATA */ + { + int etsdusize, servtype; + struct solaris_o_si_udata __user *p = A(arg); + switch (SOCKET_I(ino)->type) { + case SOCK_STREAM: + etsdusize = 1; + servtype = 2; + break; + default: + etsdusize = -2; + servtype = 3; + break; + } + if (put_user(16384, &p->tidusize) || + __put_user(sizeof(struct sockaddr), &p->addrsize) || + __put_user(-1, &p->optsize) || + __put_user(etsdusize, &p->etsdusize) || + __put_user(servtype, &p->servtype) || + __put_user(0, &p->so_state) || + __put_user(0, &p->so_options) || + __put_user(16384, &p->tsdusize)) + return (EFAULT << 8) | TSYSERR; + return 0; + } + case 102: /* SI_SHUTDOWN */ + case 103: /* SI_LISTEN */ + case 104: /* SI_SETMYNAME */ + case 105: /* SI_SETPEERNAME */ + case 106: /* SI_GETINTRANSIT */ + case 107: /* SI_TCL_LINK */ + case 108: /* SI_TCL_UNLINK */ + ; + } + return TNOTSUPPORT; +} + +static inline int solaris_timod(unsigned int fd, unsigned int cmd, u32 arg, + int len, int __user *len_p) +{ + int ret; + + switch (cmd & 0xff) { + case 141: /* TI_OPTMGMT */ + { + int i; + u32 prim; + SOLD("TI_OPMGMT entry"); + ret = timod_putmsg(fd, A(arg), len, NULL, -1, 0); + SOLD("timod_putmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + i = MSG_HIPRI; + SOLD("calling timod_getmsg()"); + ret = timod_getmsg(fd, A(arg), len, len_p, NULL, -1, NULL, &i); + SOLD("timod_getmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + SOLD("ret ok"); + if (get_user(prim, (u32 __user *)A(arg))) + return (EFAULT << 8) | TSYSERR; + SOLD("got prim"); + if (prim == T_ERROR_ACK) { + u32 tmp, tmp2; + SOLD("prim is T_ERROR_ACK"); + if (get_user(tmp, (u32 __user *)A(arg)+3) || + get_user(tmp2, (u32 __user *)A(arg)+2)) + return (EFAULT << 8) | TSYSERR; + return (tmp2 << 8) | tmp; + } + SOLD("TI_OPMGMT return 0"); + return 0; + } + case 142: /* TI_BIND */ + { + int i; + u32 prim; + SOLD("TI_BIND entry"); + ret = timod_putmsg(fd, A(arg), len, NULL, -1, 0); + SOLD("timod_putmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + len = 1024; /* Solaris allows arbitrary return size */ + i = MSG_HIPRI; + SOLD("calling timod_getmsg()"); + ret = timod_getmsg(fd, A(arg), len, len_p, NULL, -1, NULL, &i); + SOLD("timod_getmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + SOLD("ret ok"); + if (get_user(prim, (u32 __user *)A(arg))) + return (EFAULT << 8) | TSYSERR; + SOLD("got prim"); + if (prim == T_ERROR_ACK) { + u32 tmp, tmp2; + SOLD("prim is T_ERROR_ACK"); + if (get_user(tmp, (u32 __user *)A(arg)+3) || + get_user(tmp2, (u32 __user *)A(arg)+2)) + return (EFAULT << 8) | TSYSERR; + return (tmp2 << 8) | tmp; + } + SOLD("no ERROR_ACK requested"); + if (prim != T_OK_ACK) + return TBADSEQ; + SOLD("OK_ACK requested"); + i = MSG_HIPRI; + SOLD("calling timod_getmsg()"); + ret = timod_getmsg(fd, A(arg), len, len_p, NULL, -1, NULL, &i); + SOLD("timod_getmsg() returned"); + if (ret) + return (-ret << 8) | TSYSERR; + SOLD("TI_BIND return ok"); + return 0; + } + case 140: /* TI_GETINFO */ + case 143: /* TI_UNBIND */ + case 144: /* TI_GETMYNAME */ + case 145: /* TI_GETPEERNAME */ + case 146: /* TI_SETMYNAME */ + case 147: /* TI_SETPEERNAME */ + ; + } + return TNOTSUPPORT; +} + +static inline int solaris_S(struct file *filp, unsigned int fd, unsigned int cmd, u32 arg) +{ + char *p; + int ret; + mm_segment_t old_fs; + struct strioctl si; + struct inode *ino; + struct sol_socket_struct *sock; + struct module_info *mi; + + ino = filp->f_dentry->d_inode; + if (!S_ISSOCK(ino->i_mode)) + return -EBADF; + sock = filp->private_data; + if (! sock) { + printk("solaris_S: NULL private_data\n"); + return -EBADF; + } + if (sock->magic != SOLARIS_SOCKET_MAGIC) { + printk("solaris_S: invalid magic\n"); + return -EBADF; + } + + + switch (cmd & 0xff) { + case 1: /* I_NREAD */ + return -ENOSYS; + case 2: /* I_PUSH */ + { + p = getname (A(arg)); + if (IS_ERR (p)) + return PTR_ERR(p); + ret = -EINVAL; + for (mi = module_table; mi->name; mi++) { + if (strcmp(mi->name, p) == 0) { + sol_module m; + if (sock->modcount >= MAX_NR_STREAM_MODULES) { + ret = -ENXIO; + break; + } + m = (sol_module) (mi - module_table); + sock->module[sock->modcount++] = m; + ret = 0; + break; + } + } + putname (p); + return ret; + } + case 3: /* I_POP */ + if (sock->modcount <= 0) return -EINVAL; + sock->modcount--; + return 0; + case 4: /* I_LOOK */ + { + const char *p; + if (sock->modcount <= 0) return -EINVAL; + p = module_table[(unsigned)sock->module[sock->modcount]].name; + if (copy_to_user (A(arg), p, strlen(p))) + return -EFAULT; + return 0; + } + case 5: /* I_FLUSH */ + return 0; + case 8: /* I_STR */ + if (copy_from_user(&si, A(arg), sizeof(struct strioctl))) + return -EFAULT; + /* We ignore what module is actually at the top of stack. */ + switch ((si.cmd >> 8) & 0xff) { + case 'I': + return solaris_sockmod(fd, si.cmd, si.data); + case 'T': + return solaris_timod(fd, si.cmd, si.data, si.len, + &((struct strioctl __user *)A(arg))->len); + default: + return solaris_ioctl(fd, si.cmd, si.data); + } + case 9: /* I_SETSIG */ + return sys_ioctl(fd, FIOSETOWN, current->pid); + case 10: /* I_GETSIG */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + sys_ioctl(fd, FIOGETOWN, (unsigned long)&ret); + set_fs(old_fs); + if (ret == current->pid) return 0x3ff; + else return -EINVAL; + case 11: /* I_FIND */ + { + int i; + p = getname (A(arg)); + if (IS_ERR (p)) + return PTR_ERR(p); + ret = 0; + for (i = 0; i < sock->modcount; i++) { + unsigned m = sock->module[i]; + if (strcmp(module_table[m].name, p) == 0) { + ret = 1; + break; + } + } + putname (p); + return ret; + } + case 19: /* I_SWROPT */ + case 32: /* I_SETCLTIME */ + return 0; /* Lie */ + } + return -ENOSYS; +} + +static inline int solaris_s(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 0: /* SIOCSHIWAT */ + case 2: /* SIOCSLOWAT */ + return 0; /* We don't support them */ + case 1: /* SIOCGHIWAT */ + case 3: /* SIOCGLOWAT */ + if (put_user (0, (u32 __user *)A(arg))) + return -EFAULT; + return 0; /* Lie */ + case 7: /* SIOCATMARK */ + return sys_ioctl(fd, SIOCATMARK, arg); + case 8: /* SIOCSPGRP */ + return sys_ioctl(fd, SIOCSPGRP, arg); + case 9: /* SIOCGPGRP */ + return sys_ioctl(fd, SIOCGPGRP, arg); + } + return -ENOSYS; +} + +static inline int solaris_r(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 10: /* SIOCADDRT */ + return compat_sys_ioctl(fd, SIOCADDRT, arg); + case 11: /* SIOCDELRT */ + return compat_sys_ioctl(fd, SIOCDELRT, arg); + } + return -ENOSYS; +} + +static inline int solaris_i(unsigned int fd, unsigned int cmd, u32 arg) +{ + switch (cmd & 0xff) { + case 12: /* SIOCSIFADDR */ + return compat_sys_ioctl(fd, SIOCSIFADDR, arg); + case 13: /* SIOCGIFADDR */ + return compat_sys_ioctl(fd, SIOCGIFADDR, arg); + case 14: /* SIOCSIFDSTADDR */ + return compat_sys_ioctl(fd, SIOCSIFDSTADDR, arg); + case 15: /* SIOCGIFDSTADDR */ + return compat_sys_ioctl(fd, SIOCGIFDSTADDR, arg); + case 16: /* SIOCSIFFLAGS */ + return compat_sys_ioctl(fd, SIOCSIFFLAGS, arg); + case 17: /* SIOCGIFFLAGS */ + return compat_sys_ioctl(fd, SIOCGIFFLAGS, arg); + case 18: /* SIOCSIFMEM */ + return compat_sys_ioctl(fd, SIOCSIFMEM, arg); + case 19: /* SIOCGIFMEM */ + return compat_sys_ioctl(fd, SIOCGIFMEM, arg); + case 20: /* SIOCGIFCONF */ + return compat_sys_ioctl(fd, SIOCGIFCONF, arg); + case 21: /* SIOCSIFMTU */ + return compat_sys_ioctl(fd, SIOCSIFMTU, arg); + case 22: /* SIOCGIFMTU */ + return compat_sys_ioctl(fd, SIOCGIFMTU, arg); + case 23: /* SIOCGIFBRDADDR */ + return compat_sys_ioctl(fd, SIOCGIFBRDADDR, arg); + case 24: /* SIOCSIFBRDADDR */ + return compat_sys_ioctl(fd, SIOCSIFBRDADDR, arg); + case 25: /* SIOCGIFNETMASK */ + return compat_sys_ioctl(fd, SIOCGIFNETMASK, arg); + case 26: /* SIOCSIFNETMASK */ + return compat_sys_ioctl(fd, SIOCSIFNETMASK, arg); + case 27: /* SIOCGIFMETRIC */ + return compat_sys_ioctl(fd, SIOCGIFMETRIC, arg); + case 28: /* SIOCSIFMETRIC */ + return compat_sys_ioctl(fd, SIOCSIFMETRIC, arg); + case 30: /* SIOCSARP */ + return compat_sys_ioctl(fd, SIOCSARP, arg); + case 31: /* SIOCGARP */ + return compat_sys_ioctl(fd, SIOCGARP, arg); + case 32: /* SIOCDARP */ + return compat_sys_ioctl(fd, SIOCDARP, arg); + case 52: /* SIOCGETNAME */ + case 53: /* SIOCGETPEER */ + { + struct sockaddr uaddr; + int uaddr_len = sizeof(struct sockaddr), ret; + long args[3]; + mm_segment_t old_fs = get_fs(); + int (*sys_socketcall)(int, unsigned long *) = + (int (*)(int, unsigned long *))SYS(socketcall); + + args[0] = fd; args[1] = (long)&uaddr; args[2] = (long)&uaddr_len; + set_fs(KERNEL_DS); + ret = sys_socketcall(((cmd & 0xff) == 52) ? SYS_GETSOCKNAME : SYS_GETPEERNAME, + args); + set_fs(old_fs); + if (ret >= 0) { + if (copy_to_user(A(arg), &uaddr, uaddr_len)) + return -EFAULT; + } + return ret; + } +#if 0 + case 86: /* SIOCSOCKSYS */ + return socksys_syscall(fd, arg); +#endif + case 87: /* SIOCGIFNUM */ + { + struct net_device *d; + int i = 0; + + read_lock_bh(&dev_base_lock); + for (d = dev_base; d; d = d->next) i++; + read_unlock_bh(&dev_base_lock); + + if (put_user (i, (int __user *)A(arg))) + return -EFAULT; + return 0; + } + } + return -ENOSYS; +} + +static int solaris_m(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret; + + switch (cmd & 0xff) { + case 1: /* MTIOCTOP */ + ret = sys_ioctl(fd, MTIOCTOP, (unsigned long)&arg); + break; + case 2: /* MTIOCGET */ + ret = sys_ioctl(fd, MTIOCGET, (unsigned long)&arg); + break; + case 3: /* MTIOCGETDRIVETYPE */ + case 4: /* MTIOCPERSISTENT */ + case 5: /* MTIOCPERSISTENTSTATUS */ + case 6: /* MTIOCLRERR */ + case 7: /* MTIOCGUARANTEEDORDER */ + case 8: /* MTIOCRESERVE */ + case 9: /* MTIOCRELEASE */ + case 10: /* MTIOCFORCERESERVE */ + case 13: /* MTIOCSTATE */ + case 14: /* MTIOCREADIGNOREILI */ + case 15: /* MTIOCREADIGNOREEOFS */ + case 16: /* MTIOCSHORTFMK */ + default: + ret = -ENOSYS; /* linux doesn't support these */ + break; + }; + + return ret; +} + +static int solaris_O(unsigned int fd, unsigned int cmd, u32 arg) +{ + int ret = -EINVAL; + + switch (cmd & 0xff) { + case 1: /* OPROMGETOPT */ + ret = sys_ioctl(fd, OPROMGETOPT, arg); + break; + case 2: /* OPROMSETOPT */ + ret = sys_ioctl(fd, OPROMSETOPT, arg); + break; + case 3: /* OPROMNXTOPT */ + ret = sys_ioctl(fd, OPROMNXTOPT, arg); + break; + case 4: /* OPROMSETOPT2 */ + ret = sys_ioctl(fd, OPROMSETOPT2, arg); + break; + case 5: /* OPROMNEXT */ + ret = sys_ioctl(fd, OPROMNEXT, arg); + break; + case 6: /* OPROMCHILD */ + ret = sys_ioctl(fd, OPROMCHILD, arg); + break; + case 7: /* OPROMGETPROP */ + ret = sys_ioctl(fd, OPROMGETPROP, arg); + break; + case 8: /* OPROMNXTPROP */ + ret = sys_ioctl(fd, OPROMNXTPROP, arg); + break; + case 9: /* OPROMU2P */ + ret = sys_ioctl(fd, OPROMU2P, arg); + break; + case 10: /* OPROMGETCONS */ + ret = sys_ioctl(fd, OPROMGETCONS, arg); + break; + case 11: /* OPROMGETFBNAME */ + ret = sys_ioctl(fd, OPROMGETFBNAME, arg); + break; + case 12: /* OPROMGETBOOTARGS */ + ret = sys_ioctl(fd, OPROMGETBOOTARGS, arg); + break; + case 13: /* OPROMGETVERSION */ + case 14: /* OPROMPATH2DRV */ + case 15: /* OPROMDEV2PROMNAME */ + case 16: /* OPROMPROM2DEVNAME */ + case 17: /* OPROMGETPROPLEN */ + default: + ret = -EINVAL; + break; + }; + return ret; +} + +/* }}} */ + +asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg) +{ + struct file *filp; + int error = -EBADF; + + filp = fget(fd); + if (!filp) + goto out; + + lock_kernel(); + error = -EFAULT; + switch ((cmd >> 8) & 0xff) { + case 'S': error = solaris_S(filp, fd, cmd, arg); break; + case 'T': error = solaris_T(fd, cmd, arg); break; + case 'i': error = solaris_i(fd, cmd, arg); break; + case 'r': error = solaris_r(fd, cmd, arg); break; + case 's': error = solaris_s(fd, cmd, arg); break; + case 't': error = solaris_t(fd, cmd, arg); break; + case 'f': error = sys_ioctl(fd, cmd, arg); break; + case 'm': error = solaris_m(fd, cmd, arg); break; + case 'O': error = solaris_O(fd, cmd, arg); break; + default: + error = -ENOSYS; + break; + } + unlock_kernel(); + fput(filp); +out: + if (error == -ENOSYS) { + unsigned char c = cmd>>8; + + if (c < ' ' || c > 126) c = '.'; + printk("solaris_ioctl: Unknown cmd fd(%d) cmd(%08x '%c') arg(%08x)\n", + (int)fd, (unsigned int)cmd, c, (unsigned int)arg); + error = -EINVAL; + } + return error; +} diff --git a/arch/sparc64/solaris/ipc.c b/arch/sparc64/solaris/ipc.c new file mode 100644 index 00000000000..8cef5fd57b2 --- /dev/null +++ b/arch/sparc64/solaris/ipc.c @@ -0,0 +1,127 @@ +/* $Id: ipc.c,v 1.5 1999/12/09 00:41:00 davem Exp $ + * ipc.c: Solaris IPC emulation + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/smp_lock.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/shm.h> +#include <linux/sem.h> +#include <linux/msg.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/ipc.h> + +#include "conv.h" + +struct solaris_ipc_perm { + s32 uid; + s32 gid; + s32 cuid; + s32 cgid; + u32 mode; + u32 seq; + int key; + s32 pad[4]; +}; + +struct solaris_shmid_ds { + struct solaris_ipc_perm shm_perm; + int shm_segsz; + u32 shm_amp; + unsigned short shm_lkcnt; + char __padxx[2]; + s32 shm_lpid; + s32 shm_cpid; + u32 shm_nattch; + u32 shm_cnattch; + s32 shm_atime; + s32 shm_pad1; + s32 shm_dtime; + s32 shm_pad2; + s32 shm_ctime; + s32 shm_pad3; + unsigned short shm_cv; + char shm_pad4[2]; + u32 shm_sptas; + s32 shm_pad5[2]; +}; + +asmlinkage long solaris_shmsys(int cmd, u32 arg1, u32 arg2, u32 arg3) +{ + int (*sys_ipc)(unsigned,int,int,unsigned long,void __user *,long) = + (int (*)(unsigned,int,int,unsigned long,void __user *,long))SYS(ipc); + mm_segment_t old_fs; + unsigned long raddr; + int ret; + + switch (cmd) { + case 0: /* shmat */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ipc(SHMAT, arg1, arg3 & ~0x4000, (unsigned long)&raddr, A(arg2), 0); + set_fs(old_fs); + if (ret >= 0) return (u32)raddr; + else return ret; + case 1: /* shmctl */ + switch (arg2) { + case 3: /* SHM_LOCK */ + case 4: /* SHM_UNLOCK */ + return sys_ipc(SHMCTL, arg1, (arg2 == 3) ? SHM_LOCK : SHM_UNLOCK, 0, NULL, 0); + case 10: /* IPC_RMID */ + return sys_ipc(SHMCTL, arg1, IPC_RMID, 0, NULL, 0); + case 11: /* IPC_SET */ + { + struct shmid_ds s; + struct solaris_shmid_ds __user *p = A(arg3); + + if (get_user (s.shm_perm.uid, &p->shm_perm.uid) || + __get_user (s.shm_perm.gid, &p->shm_perm.gid) || + __get_user (s.shm_perm.mode, &p->shm_perm.mode)) + return -EFAULT; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0); + set_fs(old_fs); + return ret; + } + case 12: /* IPC_STAT */ + { + struct shmid_ds s; + struct solaris_shmid_ds __user *p = A(arg3); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ipc(SHMCTL, arg1, IPC_SET, 0, &s, 0); + set_fs(old_fs); + if (put_user (s.shm_perm.uid, &(p->shm_perm.uid)) || + __put_user (s.shm_perm.gid, &(p->shm_perm.gid)) || + __put_user (s.shm_perm.cuid, &(p->shm_perm.cuid)) || + __put_user (s.shm_perm.cgid, &(p->shm_perm.cgid)) || + __put_user (s.shm_perm.mode, &(p->shm_perm.mode)) || + __put_user (s.shm_perm.seq, &(p->shm_perm.seq)) || + __put_user (s.shm_perm.key, &(p->shm_perm.key)) || + __put_user (s.shm_segsz, &(p->shm_segsz)) || + __put_user (s.shm_lpid, &(p->shm_lpid)) || + __put_user (s.shm_cpid, &(p->shm_cpid)) || + __put_user (s.shm_nattch, &(p->shm_nattch)) || + __put_user (s.shm_atime, &(p->shm_atime)) || + __put_user (s.shm_dtime, &(p->shm_dtime)) || + __put_user (s.shm_ctime, &(p->shm_ctime))) + return -EFAULT; + return ret; + } + default: return -EINVAL; + } + case 2: /* shmdt */ + return sys_ipc(SHMDT, 0, 0, 0, A(arg1), 0); + case 3: /* shmget */ + return sys_ipc(SHMGET, arg1, arg2, arg3, NULL, 0); + } + return -EINVAL; +} diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c new file mode 100644 index 00000000000..15b4cfe0755 --- /dev/null +++ b/arch/sparc64/solaris/misc.c @@ -0,0 +1,784 @@ +/* $Id: misc.c,v 1.36 2002/02/09 19:49:31 davem Exp $ + * misc.c: Miscellaneous syscall emulation for Solaris + * + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/smp_lock.h> +#include <linux/utsname.h> +#include <linux/limits.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/timex.h> +#include <linux/major.h> +#include <linux/compat.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/oplib.h> +#include <asm/idprom.h> +#include <asm/smp.h> + +#include "conv.h" + +/* Conversion from Linux to Solaris errnos. 0-34 are identity mapped. + Some Linux errnos (EPROCLIM, EDOTDOT, ERREMOTE, EUCLEAN, ENOTNAM, + ENAVAIL, EISNAM, EREMOTEIO, ENOMEDIUM, EMEDIUMTYPE) have no Solaris + equivalents. I return EINVAL in that case, which is very wrong. If + someone suggest a better value for them, you're welcomed. + On the other side, Solaris ECANCELED and ENOTSUP have no Linux equivalents, + but that doesn't matter here. --jj */ +int solaris_err_table[] = { +/* 0 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +/* 10 */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +/* 20 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, +/* 30 */ 30, 31, 32, 33, 34, 22, 150, 149, 95, 96, +/* 40 */ 97, 98, 99, 120, 121, 122, 123, 124, 125, 126, +/* 50 */ 127, 128, 129, 130, 131, 132, 133, 134, 143, 144, +/* 60 */ 145, 146, 90, 78, 147, 148, 93, 22, 94, 49, +/* 70 */ 151, 66, 60, 62, 63, 35, 77, 36, 45, 46, +/* 80 */ 64, 22, 67, 68, 69, 70, 71, 74, 22, 82, +/* 90 */ 89, 92, 79, 81, 37, 38, 39, 40, 41, 42, +/* 100 */ 43, 44, 50, 51, 52, 53, 54, 55, 56, 57, +/* 110 */ 87, 61, 84, 65, 83, 80, 91, 22, 22, 22, +/* 120 */ 22, 22, 88, 86, 85, 22, 22, +}; + +#define SOLARIS_NR_OPEN 256 + +static u32 do_solaris_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u64 off) +{ + struct file *file = NULL; + unsigned long retval, ret_type; + + /* Do we need it here? */ + set_personality(PER_SVR4); + if (flags & MAP_NORESERVE) { + static int cnt; + + if (cnt < 5) { + printk("%s: unimplemented Solaris MAP_NORESERVE mmap() flag\n", + current->comm); + cnt++; + } + flags &= ~MAP_NORESERVE; + } + retval = -EBADF; + if(!(flags & MAP_ANONYMOUS)) { + if(fd >= SOLARIS_NR_OPEN) + goto out; + file = fget(fd); + if (!file) + goto out; + else { + struct inode * inode = file->f_dentry->d_inode; + if(imajor(inode) == MEM_MAJOR && + iminor(inode) == 5) { + flags |= MAP_ANONYMOUS; + fput(file); + file = NULL; + } + } + } + + retval = -EINVAL; + len = PAGE_ALIGN(len); + if(!(flags & MAP_FIXED)) + addr = 0; + else if (len > 0xf0000000UL || addr > 0xf0000000UL - len) + goto out_putf; + ret_type = flags & _MAP_NEW; + flags &= ~_MAP_NEW; + + down_write(¤t->mm->mmap_sem); + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + retval = do_mmap(file, + (unsigned long) addr, (unsigned long) len, + (unsigned long) prot, (unsigned long) flags, off); + up_write(¤t->mm->mmap_sem); + if(!ret_type) + retval = ((retval < 0xf0000000) ? 0 : retval); + +out_putf: + if (file) + fput(file); +out: + return (u32) retval; +} + +asmlinkage u32 solaris_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off) +{ + return do_solaris_mmap(addr, len, prot, flags, fd, (u64) off); +} + +asmlinkage u32 solaris_mmap64(struct pt_regs *regs, u32 len, u32 prot, u32 flags, u32 fd, u32 offhi) +{ + u32 offlo; + + if (regs->u_regs[UREG_G1]) { + if (get_user (offlo, (u32 __user *)(long)((u32)regs->u_regs[UREG_I6] + 0x5c))) + return -EFAULT; + } else { + if (get_user (offlo, (u32 __user *)(long)((u32)regs->u_regs[UREG_I6] + 0x60))) + return -EFAULT; + } + return do_solaris_mmap((u32)regs->u_regs[UREG_I0], len, prot, flags, fd, (((u64)offhi)<<32)|offlo); +} + +asmlinkage int solaris_brk(u32 brk) +{ + int (*sunos_brk)(u32) = (int (*)(u32))SUNOS(17); + + return sunos_brk(brk); +} + +static int __set_utsfield(char __user *to, int to_size, + const char *from, int from_size, + int dotchop, int countfrom) +{ + int len = countfrom ? (to_size > from_size ? + from_size : to_size) : to_size; + int off; + + if (copy_to_user(to, from, len)) + return -EFAULT; + + off = len < to_size? len: len - 1; + if (dotchop) { + const char *p = strnchr(from, len, '.'); + if (p) off = p - from; + } + + if (__put_user('\0', to + off)) + return -EFAULT; + + return 0; +} + +#define set_utsfield(to, from, dotchop, countfrom) \ + __set_utsfield((to), sizeof(to), \ + (from), sizeof(from), \ + (dotchop), (countfrom)) + +struct sol_uname { + char sysname[9]; + char nodename[9]; + char release[9]; + char version[9]; + char machine[9]; +}; + +struct sol_utsname { + char sysname[257]; + char nodename[257]; + char release[257]; + char version[257]; + char machine[257]; +}; + +static char *machine(void) +{ + switch (sparc_cpu_model) { + case sun4: return "sun4"; + case sun4c: return "sun4c"; + case sun4e: return "sun4e"; + case sun4m: return "sun4m"; + case sun4d: return "sun4d"; + case sun4u: return "sun4u"; + default: return "sparc"; + } +} + +static char *platform(char *buffer) +{ + int len; + + *buffer = 0; + len = prom_getproperty(prom_root_node, "name", buffer, 256); + if(len > 0) + buffer[len] = 0; + if (*buffer) { + char *p; + + for (p = buffer; *p; p++) + if (*p == '/' || *p == ' ') *p = '_'; + return buffer; + } + + return "sun4u"; +} + +static char *serial(char *buffer) +{ + int node = prom_getchild(prom_root_node); + int len; + + node = prom_searchsiblings(node, "options"); + *buffer = 0; + len = prom_getproperty(node, "system-board-serial#", buffer, 256); + if(len > 0) + buffer[len] = 0; + if (!*buffer) + return "4512348717234"; + else + return buffer; +} + +asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2) +{ + struct sol_uname __user *v = A(buf); + int err; + + switch (which) { + case 0: /* old uname */ + /* Let's cheat */ + err = set_utsfield(v->sysname, "SunOS", 1, 0); + down_read(&uts_sem); + err |= set_utsfield(v->nodename, system_utsname.nodename, + 1, 1); + up_read(&uts_sem); + err |= set_utsfield(v->release, "2.6", 0, 0); + err |= set_utsfield(v->version, "Generic", 0, 0); + err |= set_utsfield(v->machine, machine(), 0, 0); + return (err ? -EFAULT : 0); + case 2: /* ustat */ + return -ENOSYS; + case 3: /* fusers */ + return -ENOSYS; + default: + return -ENOSYS; + } +} + +asmlinkage int solaris_utsname(u32 buf) +{ + struct sol_utsname __user *v = A(buf); + int err; + + /* Why should we not lie a bit? */ + down_read(&uts_sem); + err = set_utsfield(v->sysname, "SunOS", 0, 0); + err |= set_utsfield(v->nodename, system_utsname.nodename, 1, 1); + err |= set_utsfield(v->release, "5.6", 0, 0); + err |= set_utsfield(v->version, "Generic", 0, 0); + err |= set_utsfield(v->machine, machine(), 0, 0); + up_read(&uts_sem); + + return (err ? -EFAULT : 0); +} + +#define SI_SYSNAME 1 /* return name of operating system */ +#define SI_HOSTNAME 2 /* return name of node */ +#define SI_RELEASE 3 /* return release of operating system */ +#define SI_VERSION 4 /* return version field of utsname */ +#define SI_MACHINE 5 /* return kind of machine */ +#define SI_ARCHITECTURE 6 /* return instruction set arch */ +#define SI_HW_SERIAL 7 /* return hardware serial number */ +#define SI_HW_PROVIDER 8 /* return hardware manufacturer */ +#define SI_SRPC_DOMAIN 9 /* return secure RPC domain */ +#define SI_PLATFORM 513 /* return platform identifier */ + +asmlinkage int solaris_sysinfo(int cmd, u32 buf, s32 count) +{ + char *p, *q, *r; + char buffer[256]; + int len; + + /* Again, we cheat :)) */ + switch (cmd) { + case SI_SYSNAME: r = "SunOS"; break; + case SI_HOSTNAME: + r = buffer + 256; + down_read(&uts_sem); + for (p = system_utsname.nodename, q = buffer; + q < r && *p && *p != '.'; *q++ = *p++); + up_read(&uts_sem); + *q = 0; + r = buffer; + break; + case SI_RELEASE: r = "5.6"; break; + case SI_MACHINE: r = machine(); break; + case SI_ARCHITECTURE: r = "sparc"; break; + case SI_HW_PROVIDER: r = "Sun_Microsystems"; break; + case SI_HW_SERIAL: r = serial(buffer); break; + case SI_PLATFORM: r = platform(buffer); break; + case SI_SRPC_DOMAIN: r = ""; break; + case SI_VERSION: r = "Generic"; break; + default: return -EINVAL; + } + len = strlen(r) + 1; + if (count < len) { + if (copy_to_user(A(buf), r, count - 1) || + __put_user(0, (char __user *)A(buf) + count - 1)) + return -EFAULT; + } else { + if (copy_to_user(A(buf), r, len)) + return -EFAULT; + } + return len; +} + +#define SOLARIS_CONFIG_NGROUPS 2 +#define SOLARIS_CONFIG_CHILD_MAX 3 +#define SOLARIS_CONFIG_OPEN_FILES 4 +#define SOLARIS_CONFIG_POSIX_VER 5 +#define SOLARIS_CONFIG_PAGESIZE 6 +#define SOLARIS_CONFIG_CLK_TCK 7 +#define SOLARIS_CONFIG_XOPEN_VER 8 +#define SOLARIS_CONFIG_PROF_TCK 10 +#define SOLARIS_CONFIG_NPROC_CONF 11 +#define SOLARIS_CONFIG_NPROC_ONLN 12 +#define SOLARIS_CONFIG_AIO_LISTIO_MAX 13 +#define SOLARIS_CONFIG_AIO_MAX 14 +#define SOLARIS_CONFIG_AIO_PRIO_DELTA_MAX 15 +#define SOLARIS_CONFIG_DELAYTIMER_MAX 16 +#define SOLARIS_CONFIG_MQ_OPEN_MAX 17 +#define SOLARIS_CONFIG_MQ_PRIO_MAX 18 +#define SOLARIS_CONFIG_RTSIG_MAX 19 +#define SOLARIS_CONFIG_SEM_NSEMS_MAX 20 +#define SOLARIS_CONFIG_SEM_VALUE_MAX 21 +#define SOLARIS_CONFIG_SIGQUEUE_MAX 22 +#define SOLARIS_CONFIG_SIGRT_MIN 23 +#define SOLARIS_CONFIG_SIGRT_MAX 24 +#define SOLARIS_CONFIG_TIMER_MAX 25 +#define SOLARIS_CONFIG_PHYS_PAGES 26 +#define SOLARIS_CONFIG_AVPHYS_PAGES 27 + +asmlinkage int solaris_sysconf(int id) +{ + switch (id) { + case SOLARIS_CONFIG_NGROUPS: return NGROUPS_MAX; + case SOLARIS_CONFIG_CHILD_MAX: return CHILD_MAX; + case SOLARIS_CONFIG_OPEN_FILES: return OPEN_MAX; + case SOLARIS_CONFIG_POSIX_VER: return 199309; + case SOLARIS_CONFIG_PAGESIZE: return PAGE_SIZE; + case SOLARIS_CONFIG_XOPEN_VER: return 3; + case SOLARIS_CONFIG_CLK_TCK: + case SOLARIS_CONFIG_PROF_TCK: + return sparc64_get_clock_tick(smp_processor_id()); +#ifdef CONFIG_SMP + case SOLARIS_CONFIG_NPROC_CONF: return NR_CPUS; + case SOLARIS_CONFIG_NPROC_ONLN: return num_online_cpus(); +#else + case SOLARIS_CONFIG_NPROC_CONF: return 1; + case SOLARIS_CONFIG_NPROC_ONLN: return 1; +#endif + case SOLARIS_CONFIG_SIGRT_MIN: return 37; + case SOLARIS_CONFIG_SIGRT_MAX: return 44; + case SOLARIS_CONFIG_PHYS_PAGES: + case SOLARIS_CONFIG_AVPHYS_PAGES: + { + struct sysinfo s; + + si_meminfo(&s); + if (id == SOLARIS_CONFIG_PHYS_PAGES) + return s.totalram >>= PAGE_SHIFT; + else + return s.freeram >>= PAGE_SHIFT; + } + /* XXX support these as well -jj */ + case SOLARIS_CONFIG_AIO_LISTIO_MAX: return -EINVAL; + case SOLARIS_CONFIG_AIO_MAX: return -EINVAL; + case SOLARIS_CONFIG_AIO_PRIO_DELTA_MAX: return -EINVAL; + case SOLARIS_CONFIG_DELAYTIMER_MAX: return -EINVAL; + case SOLARIS_CONFIG_MQ_OPEN_MAX: return -EINVAL; + case SOLARIS_CONFIG_MQ_PRIO_MAX: return -EINVAL; + case SOLARIS_CONFIG_RTSIG_MAX: return -EINVAL; + case SOLARIS_CONFIG_SEM_NSEMS_MAX: return -EINVAL; + case SOLARIS_CONFIG_SEM_VALUE_MAX: return -EINVAL; + case SOLARIS_CONFIG_SIGQUEUE_MAX: return -EINVAL; + case SOLARIS_CONFIG_TIMER_MAX: return -EINVAL; + default: return -EINVAL; + } +} + +asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) +{ + int ret; + + switch (cmd) { + case 0: /* getpgrp */ + return process_group(current); + case 1: /* setpgrp */ + { + int (*sys_setpgid)(pid_t,pid_t) = + (int (*)(pid_t,pid_t))SYS(setpgid); + + /* can anyone explain me the difference between + Solaris setpgrp and setsid? */ + ret = sys_setpgid(0, 0); + if (ret) return ret; + current->signal->tty = NULL; + return process_group(current); + } + case 2: /* getsid */ + { + int (*sys_getsid)(pid_t) = (int (*)(pid_t))SYS(getsid); + return sys_getsid(pid); + } + case 3: /* setsid */ + { + int (*sys_setsid)(void) = (int (*)(void))SYS(setsid); + return sys_setsid(); + } + case 4: /* getpgid */ + { + int (*sys_getpgid)(pid_t) = (int (*)(pid_t))SYS(getpgid); + return sys_getpgid(pid); + } + case 5: /* setpgid */ + { + int (*sys_setpgid)(pid_t,pid_t) = + (int (*)(pid_t,pid_t))SYS(setpgid); + return sys_setpgid(pid,pgid); + } + } + return -EINVAL; +} + +asmlinkage int solaris_gettimeofday(u32 tim) +{ + int (*sys_gettimeofday)(struct timeval *, struct timezone *) = + (int (*)(struct timeval *, struct timezone *))SYS(gettimeofday); + + return sys_gettimeofday((struct timeval *)(u64)tim, NULL); +} + +#define RLIM_SOL_INFINITY32 0x7fffffff +#define RLIM_SOL_SAVED_MAX32 0x7ffffffe +#define RLIM_SOL_SAVED_CUR32 0x7ffffffd +#define RLIM_SOL_INFINITY ((u64)-3) +#define RLIM_SOL_SAVED_MAX ((u64)-2) +#define RLIM_SOL_SAVED_CUR ((u64)-1) +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) +#define RLIMIT_SOL_NOFILE 5 +#define RLIMIT_SOL_VMEM 6 + +struct rlimit32 { + u32 rlim_cur; + u32 rlim_max; +}; + +asmlinkage int solaris_getrlimit(unsigned int resource, struct rlimit32 __user *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + int (*sys_getrlimit)(unsigned int, struct rlimit *) = + (int (*)(unsigned int, struct rlimit *))SYS(getrlimit); + + if (resource > RLIMIT_SOL_VMEM) + return -EINVAL; + switch (resource) { + case RLIMIT_SOL_NOFILE: resource = RLIMIT_NOFILE; break; + case RLIMIT_SOL_VMEM: resource = RLIMIT_AS; break; + default: break; + } + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + if (r.rlim_cur == RLIM_INFINITY) + r.rlim_cur = RLIM_SOL_INFINITY32; + else if ((u64)r.rlim_cur > RLIM_SOL_INFINITY32) + r.rlim_cur = RLIM_SOL_SAVED_CUR32; + if (r.rlim_max == RLIM_INFINITY) + r.rlim_max = RLIM_SOL_INFINITY32; + else if ((u64)r.rlim_max > RLIM_SOL_INFINITY32) + r.rlim_max = RLIM_SOL_SAVED_MAX32; + ret = put_user (r.rlim_cur, &rlim->rlim_cur); + ret |= __put_user (r.rlim_max, &rlim->rlim_max); + } + return ret; +} + +asmlinkage int solaris_setrlimit(unsigned int resource, struct rlimit32 __user *rlim) +{ + struct rlimit r, rold; + int ret; + mm_segment_t old_fs = get_fs (); + int (*sys_getrlimit)(unsigned int, struct rlimit __user *) = + (int (*)(unsigned int, struct rlimit __user *))SYS(getrlimit); + int (*sys_setrlimit)(unsigned int, struct rlimit __user *) = + (int (*)(unsigned int, struct rlimit __user *))SYS(setrlimit); + + if (resource > RLIMIT_SOL_VMEM) + return -EINVAL; + switch (resource) { + case RLIMIT_SOL_NOFILE: resource = RLIMIT_NOFILE; break; + case RLIMIT_SOL_VMEM: resource = RLIMIT_AS; break; + default: break; + } + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &rold); + if (!ret) { + if (r.rlim_cur == RLIM_SOL_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + else if (r.rlim_cur == RLIM_SOL_SAVED_CUR32) + r.rlim_cur = rold.rlim_cur; + else if (r.rlim_cur == RLIM_SOL_SAVED_MAX32) + r.rlim_cur = rold.rlim_max; + if (r.rlim_max == RLIM_SOL_INFINITY32) + r.rlim_max = RLIM_INFINITY; + else if (r.rlim_max == RLIM_SOL_SAVED_CUR32) + r.rlim_max = rold.rlim_cur; + else if (r.rlim_max == RLIM_SOL_SAVED_MAX32) + r.rlim_max = rold.rlim_max; + ret = sys_setrlimit(resource, &r); + } + set_fs (old_fs); + return ret; +} + +asmlinkage int solaris_getrlimit64(unsigned int resource, struct rlimit __user *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + int (*sys_getrlimit)(unsigned int, struct rlimit __user *) = + (int (*)(unsigned int, struct rlimit __user *))SYS(getrlimit); + + if (resource > RLIMIT_SOL_VMEM) + return -EINVAL; + switch (resource) { + case RLIMIT_SOL_NOFILE: resource = RLIMIT_NOFILE; break; + case RLIMIT_SOL_VMEM: resource = RLIMIT_AS; break; + default: break; + } + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + if (r.rlim_cur == RLIM_INFINITY) + r.rlim_cur = RLIM_SOL_INFINITY; + if (r.rlim_max == RLIM_INFINITY) + r.rlim_max = RLIM_SOL_INFINITY; + ret = put_user (r.rlim_cur, &rlim->rlim_cur); + ret |= __put_user (r.rlim_max, &rlim->rlim_max); + } + return ret; +} + +asmlinkage int solaris_setrlimit64(unsigned int resource, struct rlimit __user *rlim) +{ + struct rlimit r, rold; + int ret; + mm_segment_t old_fs = get_fs (); + int (*sys_getrlimit)(unsigned int, struct rlimit __user *) = + (int (*)(unsigned int, struct rlimit __user *))SYS(getrlimit); + int (*sys_setrlimit)(unsigned int, struct rlimit __user *) = + (int (*)(unsigned int, struct rlimit __user *))SYS(setrlimit); + + if (resource > RLIMIT_SOL_VMEM) + return -EINVAL; + switch (resource) { + case RLIMIT_SOL_NOFILE: resource = RLIMIT_NOFILE; break; + case RLIMIT_SOL_VMEM: resource = RLIMIT_AS; break; + default: break; + } + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &rold); + if (!ret) { + if (r.rlim_cur == RLIM_SOL_INFINITY) + r.rlim_cur = RLIM_INFINITY; + else if (r.rlim_cur == RLIM_SOL_SAVED_CUR) + r.rlim_cur = rold.rlim_cur; + else if (r.rlim_cur == RLIM_SOL_SAVED_MAX) + r.rlim_cur = rold.rlim_max; + if (r.rlim_max == RLIM_SOL_INFINITY) + r.rlim_max = RLIM_INFINITY; + else if (r.rlim_max == RLIM_SOL_SAVED_CUR) + r.rlim_max = rold.rlim_cur; + else if (r.rlim_max == RLIM_SOL_SAVED_MAX) + r.rlim_max = rold.rlim_max; + ret = sys_setrlimit(resource, &r); + } + set_fs (old_fs); + return ret; +} + +struct sol_ntptimeval { + struct compat_timeval time; + s32 maxerror; + s32 esterror; +}; + +struct sol_timex { + u32 modes; + s32 offset; + s32 freq; + s32 maxerror; + s32 esterror; + s32 status; + s32 constant; + s32 precision; + s32 tolerance; + s32 ppsfreq; + s32 jitter; + s32 shift; + s32 stabil; + s32 jitcnt; + s32 calcnt; + s32 errcnt; + s32 stbcnt; +}; + +asmlinkage int solaris_ntp_gettime(struct sol_ntptimeval __user *ntp) +{ + int (*sys_adjtimex)(struct timex __user *) = + (int (*)(struct timex __user *))SYS(adjtimex); + struct timex t; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + t.modes = 0; + ret = sys_adjtimex(&t); + set_fs(old_fs); + if (ret < 0) + return ret; + ret = put_user (t.time.tv_sec, &ntp->time.tv_sec); + ret |= __put_user (t.time.tv_usec, &ntp->time.tv_usec); + ret |= __put_user (t.maxerror, &ntp->maxerror); + ret |= __put_user (t.esterror, &ntp->esterror); + return ret; +} + +asmlinkage int solaris_ntp_adjtime(struct sol_timex __user *txp) +{ + int (*sys_adjtimex)(struct timex __user *) = + (int (*)(struct timex __user *))SYS(adjtimex); + struct timex t; + int ret, err; + mm_segment_t old_fs = get_fs(); + + ret = get_user (t.modes, &txp->modes); + ret |= __get_user (t.offset, &txp->offset); + ret |= __get_user (t.freq, &txp->freq); + ret |= __get_user (t.maxerror, &txp->maxerror); + ret |= __get_user (t.esterror, &txp->esterror); + ret |= __get_user (t.status, &txp->status); + ret |= __get_user (t.constant, &txp->constant); + set_fs(KERNEL_DS); + ret = sys_adjtimex(&t); + set_fs(old_fs); + if (ret < 0) + return ret; + err = put_user (t.offset, &txp->offset); + err |= __put_user (t.freq, &txp->freq); + err |= __put_user (t.maxerror, &txp->maxerror); + err |= __put_user (t.esterror, &txp->esterror); + err |= __put_user (t.status, &txp->status); + err |= __put_user (t.constant, &txp->constant); + err |= __put_user (t.precision, &txp->precision); + err |= __put_user (t.tolerance, &txp->tolerance); + err |= __put_user (t.ppsfreq, &txp->ppsfreq); + err |= __put_user (t.jitter, &txp->jitter); + err |= __put_user (t.shift, &txp->shift); + err |= __put_user (t.stabil, &txp->stabil); + err |= __put_user (t.jitcnt, &txp->jitcnt); + err |= __put_user (t.calcnt, &txp->calcnt); + err |= __put_user (t.errcnt, &txp->errcnt); + err |= __put_user (t.stbcnt, &txp->stbcnt); + if (err) + return -EFAULT; + return ret; +} + +asmlinkage int do_sol_unimplemented(struct pt_regs *regs) +{ + printk ("Unimplemented Solaris syscall %d %08x %08x %08x %08x\n", + (int)regs->u_regs[UREG_G1], + (int)regs->u_regs[UREG_I0], + (int)regs->u_regs[UREG_I1], + (int)regs->u_regs[UREG_I2], + (int)regs->u_regs[UREG_I3]); + return -ENOSYS; +} + +asmlinkage void solaris_register(void) +{ + set_personality(PER_SVR4); +} + +extern long solaris_to_linux_signals[], linux_to_solaris_signals[]; + +struct exec_domain solaris_exec_domain = { + .name = "Solaris", + .handler = NULL, + .pers_low = 1, /* PER_SVR4 personality */ + .pers_high = 1, + .signal_map = solaris_to_linux_signals, + .signal_invmap =linux_to_solaris_signals, + .module = THIS_MODULE, + .next = NULL +}; + +extern int init_socksys(void); + +#ifdef MODULE + +MODULE_AUTHOR("Jakub Jelinek (jj@ultra.linux.cz), Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)"); +MODULE_DESCRIPTION("Solaris binary emulation module"); +MODULE_LICENSE("GPL"); + +#ifdef __sparc_v9__ +extern u32 tl0_solaris[8]; +#define update_ttable(x) \ + tl0_solaris[3] = (((long)(x) - (long)tl0_solaris - 3) >> 2) | 0x40000000; \ + __asm__ __volatile__ ("membar #StoreStore; flush %0" : : "r" (&tl0_solaris[3])) +#else +#endif + +extern u32 solaris_sparc_syscall[]; +extern u32 solaris_syscall[]; +extern void cleanup_socksys(void); + +extern u32 entry64_personality_patch; + +int init_module(void) +{ + int ret; + + SOLDD(("Solaris module at %p\n", solaris_sparc_syscall)); + register_exec_domain(&solaris_exec_domain); + if ((ret = init_socksys())) { + unregister_exec_domain(&solaris_exec_domain); + return ret; + } + update_ttable(solaris_sparc_syscall); + entry64_personality_patch |= + (offsetof(struct task_struct, personality) + + (sizeof(unsigned long) - 1)); + __asm__ __volatile__("membar #StoreStore; flush %0" + : : "r" (&entry64_personality_patch)); + return 0; +} + +void cleanup_module(void) +{ + update_ttable(solaris_syscall); + cleanup_socksys(); + unregister_exec_domain(&solaris_exec_domain); +} + +#else +int init_solaris_emul(void) +{ + register_exec_domain(&solaris_exec_domain); + init_socksys(); + return 0; +} +#endif + diff --git a/arch/sparc64/solaris/signal.c b/arch/sparc64/solaris/signal.c new file mode 100644 index 00000000000..7fa2634e208 --- /dev/null +++ b/arch/sparc64/solaris/signal.c @@ -0,0 +1,430 @@ +/* $Id: signal.c,v 1.7 2000/09/05 21:44:54 davem Exp $ + * signal.c: Signal emulation for Solaris + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#include <linux/types.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> + +#include <asm/uaccess.h> +#include <asm/svr4.h> +#include <asm/string.h> + +#include "conv.h" +#include "signal.h" + +#define _S(nr) (1L<<((nr)-1)) + +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +long linux_to_solaris_signals[] = { + 0, + SOLARIS_SIGHUP, SOLARIS_SIGINT, + SOLARIS_SIGQUIT, SOLARIS_SIGILL, + SOLARIS_SIGTRAP, SOLARIS_SIGIOT, + SOLARIS_SIGEMT, SOLARIS_SIGFPE, + SOLARIS_SIGKILL, SOLARIS_SIGBUS, + SOLARIS_SIGSEGV, SOLARIS_SIGSYS, + SOLARIS_SIGPIPE, SOLARIS_SIGALRM, + SOLARIS_SIGTERM, SOLARIS_SIGURG, + SOLARIS_SIGSTOP, SOLARIS_SIGTSTP, + SOLARIS_SIGCONT, SOLARIS_SIGCLD, + SOLARIS_SIGTTIN, SOLARIS_SIGTTOU, + SOLARIS_SIGPOLL, SOLARIS_SIGXCPU, + SOLARIS_SIGXFSZ, SOLARIS_SIGVTALRM, + SOLARIS_SIGPROF, SOLARIS_SIGWINCH, + SOLARIS_SIGUSR1, SOLARIS_SIGUSR1, + SOLARIS_SIGUSR2, -1, +}; + +long solaris_to_linux_signals[] = { + 0, + SIGHUP, SIGINT, SIGQUIT, SIGILL, + SIGTRAP, SIGIOT, SIGEMT, SIGFPE, + SIGKILL, SIGBUS, SIGSEGV, SIGSYS, + SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, + SIGUSR2, SIGCHLD, -1, SIGWINCH, + SIGURG, SIGPOLL, SIGSTOP, SIGTSTP, + SIGCONT, SIGTTIN, SIGTTOU, SIGVTALRM, + SIGPROF, SIGXCPU, SIGXFSZ, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, -1, -1, +}; + +static inline long mapsig(long sig) +{ + if ((unsigned long)sig > SOLARIS_NSIGNALS) + return -EINVAL; + return solaris_to_linux_signals[sig]; +} + +asmlinkage int solaris_kill(int pid, int sig) +{ + int (*sys_kill)(int,int) = + (int (*)(int,int))SYS(kill); + int s = mapsig(sig); + + if (s < 0) return s; + return sys_kill(pid, s); +} + +static long sig_handler(int sig, u32 arg, int one_shot) +{ + struct sigaction sa, old; + int ret; + mm_segment_t old_fs = get_fs(); + int (*sys_sigaction)(int,struct sigaction __user *,struct sigaction __user *) = + (int (*)(int,struct sigaction __user *,struct sigaction __user *))SYS(sigaction); + + sigemptyset(&sa.sa_mask); + sa.sa_restorer = NULL; + sa.sa_handler = (__sighandler_t)A(arg); + sa.sa_flags = 0; + if (one_shot) sa.sa_flags = SA_ONESHOT | SA_NOMASK; + set_fs (KERNEL_DS); + ret = sys_sigaction(sig, (void __user *)&sa, (void __user *)&old); + set_fs (old_fs); + if (ret < 0) return ret; + return (u32)(unsigned long)old.sa_handler; +} + +static inline long solaris_signal(int sig, u32 arg) +{ + return sig_handler (sig, arg, 1); +} + +static long solaris_sigset(int sig, u32 arg) +{ + if (arg != 2) /* HOLD */ { + spin_lock_irq(¤t->sighand->siglock); + sigdelsetmask(¤t->blocked, _S(sig)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + return sig_handler (sig, arg, 0); + } else { + spin_lock_irq(¤t->sighand->siglock); + sigaddsetmask(¤t->blocked, (_S(sig) & ~_BLOCKABLE)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + return 0; + } +} + +static inline long solaris_sighold(int sig) +{ + return solaris_sigset(sig, 2); +} + +static inline long solaris_sigrelse(int sig) +{ + spin_lock_irq(¤t->sighand->siglock); + sigdelsetmask(¤t->blocked, _S(sig)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + return 0; +} + +static inline long solaris_sigignore(int sig) +{ + return sig_handler(sig, (u32)(unsigned long)SIG_IGN, 0); +} + +static inline long solaris_sigpause(int sig) +{ + printk ("Need to support solaris sigpause\n"); + return -ENOSYS; +} + +asmlinkage long solaris_sigfunc(int sig, u32 arg) +{ + int func = sig & ~0xff; + + sig = mapsig(sig & 0xff); + if (sig < 0) return sig; + switch (func) { + case 0: return solaris_signal(sig, arg); + case 0x100: return solaris_sigset(sig, arg); + case 0x200: return solaris_sighold(sig); + case 0x400: return solaris_sigrelse(sig); + case 0x800: return solaris_sigignore(sig); + case 0x1000: return solaris_sigpause(sig); + } + return -EINVAL; +} + +typedef struct { + u32 __sigbits[4]; +} sol_sigset_t; + +static inline int mapin(u32 *p, sigset_t *q) +{ + int i; + u32 x; + int sig; + + sigemptyset(q); + x = p[0]; + for (i = 1; i <= SOLARIS_NSIGNALS; i++) { + if (x & 1) { + sig = solaris_to_linux_signals[i]; + if (sig == -1) + return -EINVAL; + sigaddsetmask(q, (1L << (sig - 1))); + } + x >>= 1; + if (i == 32) + x = p[1]; + } + return 0; +} + +static inline int mapout(sigset_t *q, u32 *p) +{ + int i; + int sig; + + p[0] = 0; + p[1] = 0; + for (i = 1; i <= 32; i++) { + if (sigismember(q, sigmask(i))) { + sig = linux_to_solaris_signals[i]; + if (sig == -1) + return -EINVAL; + if (sig > 32) + p[1] |= 1L << (sig - 33); + else + p[0] |= 1L << (sig - 1); + } + } + return 0; +} + +asmlinkage int solaris_sigprocmask(int how, u32 in, u32 out) +{ + sigset_t in_s, *ins, out_s, *outs; + mm_segment_t old_fs = get_fs(); + int ret; + int (*sys_sigprocmask)(int,sigset_t __user *,sigset_t __user *) = + (int (*)(int,sigset_t __user *,sigset_t __user *))SYS(sigprocmask); + + ins = NULL; outs = NULL; + if (in) { + u32 tmp[2]; + + if (copy_from_user (tmp, (void __user *)A(in), 2*sizeof(u32))) + return -EFAULT; + ins = &in_s; + if (mapin (tmp, ins)) return -EINVAL; + } + if (out) outs = &out_s; + set_fs (KERNEL_DS); + ret = sys_sigprocmask((how == 3) ? SIG_SETMASK : how, + (void __user *)ins, (void __user *)outs); + set_fs (old_fs); + if (ret) return ret; + if (out) { + u32 tmp[4]; + + tmp[2] = 0; tmp[3] = 0; + if (mapout (outs, tmp)) return -EINVAL; + if (copy_to_user((void __user *)A(out), tmp, 4*sizeof(u32))) + return -EFAULT; + } + return 0; +} + +asmlinkage long do_sol_sigsuspend(u32 mask) +{ + sigset_t s; + u32 tmp[2]; + + if (copy_from_user (tmp, (sol_sigset_t __user *)A(mask), 2*sizeof(u32))) + return -EFAULT; + if (mapin (tmp, &s)) return -EINVAL; + return (long)s.sig[0]; +} + +struct sol_sigaction { + int sa_flags; + u32 sa_handler; + u32 sa_mask[4]; + int sa_resv[2]; +}; + +asmlinkage int solaris_sigaction(int sig, u32 act, u32 old) +{ + u32 tmp, tmp2[4]; + struct sigaction s, s2; + int ret; + mm_segment_t old_fs = get_fs(); + struct sol_sigaction __user *p = (void __user *)A(old); + int (*sys_sigaction)(int,struct sigaction __user *,struct sigaction __user *) = + (int (*)(int,struct sigaction __user *,struct sigaction __user *))SYS(sigaction); + + sig = mapsig(sig); + if (sig < 0) { + /* We cheat a little bit for Solaris only signals */ + if (old && clear_user(p, sizeof(struct sol_sigaction))) + return -EFAULT; + return 0; + } + if (act) { + if (get_user (tmp, &p->sa_flags)) + return -EFAULT; + s.sa_flags = 0; + if (tmp & SOLARIS_SA_ONSTACK) s.sa_flags |= SA_STACK; + if (tmp & SOLARIS_SA_RESTART) s.sa_flags |= SA_RESTART; + if (tmp & SOLARIS_SA_NODEFER) s.sa_flags |= SA_NOMASK; + if (tmp & SOLARIS_SA_RESETHAND) s.sa_flags |= SA_ONESHOT; + if (tmp & SOLARIS_SA_NOCLDSTOP) s.sa_flags |= SA_NOCLDSTOP; + if (get_user (tmp, &p->sa_handler) || + copy_from_user (tmp2, &p->sa_mask, 2*sizeof(u32))) + return -EFAULT; + s.sa_handler = (__sighandler_t)A(tmp); + if (mapin (tmp2, &s.sa_mask)) return -EINVAL; + s.sa_restorer = NULL; + } + set_fs(KERNEL_DS); + ret = sys_sigaction(sig, act ? (void __user *)&s : NULL, + old ? (void __user *)&s2 : NULL); + set_fs(old_fs); + if (ret) return ret; + if (old) { + if (mapout (&s2.sa_mask, tmp2)) return -EINVAL; + tmp = 0; tmp2[2] = 0; tmp2[3] = 0; + if (s2.sa_flags & SA_STACK) tmp |= SOLARIS_SA_ONSTACK; + if (s2.sa_flags & SA_RESTART) tmp |= SOLARIS_SA_RESTART; + if (s2.sa_flags & SA_NOMASK) tmp |= SOLARIS_SA_NODEFER; + if (s2.sa_flags & SA_ONESHOT) tmp |= SOLARIS_SA_RESETHAND; + if (s2.sa_flags & SA_NOCLDSTOP) tmp |= SOLARIS_SA_NOCLDSTOP; + if (put_user (tmp, &p->sa_flags) || + __put_user ((u32)(unsigned long)s2.sa_handler, &p->sa_handler) || + copy_to_user (&p->sa_mask, tmp2, 4*sizeof(u32))) + return -EFAULT; + } + return 0; +} + +asmlinkage int solaris_sigpending(int which, u32 set) +{ + sigset_t s; + u32 tmp[4]; + switch (which) { + case 1: /* sigpending */ + spin_lock_irq(¤t->sighand->siglock); + sigandsets(&s, ¤t->blocked, ¤t->pending.signal); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + break; + case 2: /* sigfillset - I just set signals which have linux equivalents */ + sigfillset(&s); + break; + default: return -EINVAL; + } + if (mapout (&s, tmp)) return -EINVAL; + tmp[2] = 0; tmp[3] = 0; + if (copy_to_user ((u32 __user *)A(set), tmp, sizeof(tmp))) + return -EFAULT; + return 0; +} + +asmlinkage int solaris_wait(u32 stat_loc) +{ + unsigned __user *p = (unsigned __user *)A(stat_loc); + int (*sys_wait4)(pid_t,unsigned __user *, int, struct rusage __user *) = + (int (*)(pid_t,unsigned __user *, int, struct rusage __user *))SYS(wait4); + int ret, status; + + ret = sys_wait4(-1, p, WUNTRACED, NULL); + if (ret >= 0 && stat_loc) { + if (get_user (status, p)) + return -EFAULT; + if (((status - 1) & 0xffff) < 0xff) + status = linux_to_solaris_signals[status & 0x7f] & 0x7f; + else if ((status & 0xff) == 0x7f) + status = (linux_to_solaris_signals[(status >> 8) & 0xff] << 8) | 0x7f; + if (__put_user (status, p)) + return -EFAULT; + } + return ret; +} + +asmlinkage int solaris_waitid(int idtype, s32 pid, u32 info, int options) +{ + int (*sys_wait4)(pid_t,unsigned __user *, int, struct rusage __user *) = + (int (*)(pid_t,unsigned __user *, int, struct rusage __user *))SYS(wait4); + int opts, status, ret; + + switch (idtype) { + case 0: /* P_PID */ break; + case 1: /* P_PGID */ pid = -pid; break; + case 7: /* P_ALL */ pid = -1; break; + default: return -EINVAL; + } + opts = 0; + if (options & SOLARIS_WUNTRACED) opts |= WUNTRACED; + if (options & SOLARIS_WNOHANG) opts |= WNOHANG; + current->state = TASK_RUNNING; + ret = sys_wait4(pid, (unsigned int __user *)A(info), opts, NULL); + if (ret < 0) return ret; + if (info) { + struct sol_siginfo __user *s = (void __user *)A(info); + + if (get_user (status, (unsigned int __user *)A(info))) + return -EFAULT; + + if (__put_user (SOLARIS_SIGCLD, &s->si_signo) || + __put_user (ret, &s->_data._proc._pid)) + return -EFAULT; + + switch (status & 0xff) { + case 0: ret = SOLARIS_CLD_EXITED; + status = (status >> 8) & 0xff; + break; + case 0x7f: + status = (status >> 8) & 0xff; + switch (status) { + case SIGSTOP: + case SIGTSTP: ret = SOLARIS_CLD_STOPPED; + default: ret = SOLARIS_CLD_EXITED; + } + status = linux_to_solaris_signals[status]; + break; + default: + if (status & 0x80) ret = SOLARIS_CLD_DUMPED; + else ret = SOLARIS_CLD_KILLED; + status = linux_to_solaris_signals[status & 0x7f]; + break; + } + + if (__put_user (ret, &s->si_code) || + __put_user (status, &s->_data._proc._pdata._cld._status)) + return -EFAULT; + } + return 0; +} + +extern int svr4_setcontext(svr4_ucontext_t *c, struct pt_regs *regs); +extern int svr4_getcontext(svr4_ucontext_t *c, struct pt_regs *regs); + +asmlinkage int solaris_context(struct pt_regs *regs) +{ + switch ((unsigned)regs->u_regs[UREG_I0]) { + case 0: /* getcontext */ + return svr4_getcontext((svr4_ucontext_t *)(long)(u32)regs->u_regs[UREG_I1], regs); + case 1: /* setcontext */ + return svr4_setcontext((svr4_ucontext_t *)(long)(u32)regs->u_regs[UREG_I1], regs); + default: + return -EINVAL; + + } +} + +asmlinkage int solaris_sigaltstack(u32 ss, u32 oss) +{ +/* XXX Implement this soon */ + return 0; +} diff --git a/arch/sparc64/solaris/signal.h b/arch/sparc64/solaris/signal.h new file mode 100644 index 00000000000..e9157080305 --- /dev/null +++ b/arch/sparc64/solaris/signal.h @@ -0,0 +1,108 @@ +/* $Id: signal.h,v 1.3 1998/04/12 06:20:33 davem Exp $ + * signal.h: Signal emulation for Solaris + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + */ + +#define SOLARIS_SIGHUP 1 +#define SOLARIS_SIGINT 2 +#define SOLARIS_SIGQUIT 3 +#define SOLARIS_SIGILL 4 +#define SOLARIS_SIGTRAP 5 +#define SOLARIS_SIGIOT 6 +#define SOLARIS_SIGEMT 7 +#define SOLARIS_SIGFPE 8 +#define SOLARIS_SIGKILL 9 +#define SOLARIS_SIGBUS 10 +#define SOLARIS_SIGSEGV 11 +#define SOLARIS_SIGSYS 12 +#define SOLARIS_SIGPIPE 13 +#define SOLARIS_SIGALRM 14 +#define SOLARIS_SIGTERM 15 +#define SOLARIS_SIGUSR1 16 +#define SOLARIS_SIGUSR2 17 +#define SOLARIS_SIGCLD 18 +#define SOLARIS_SIGPWR 19 +#define SOLARIS_SIGWINCH 20 +#define SOLARIS_SIGURG 21 +#define SOLARIS_SIGPOLL 22 +#define SOLARIS_SIGSTOP 23 +#define SOLARIS_SIGTSTP 24 +#define SOLARIS_SIGCONT 25 +#define SOLARIS_SIGTTIN 26 +#define SOLARIS_SIGTTOU 27 +#define SOLARIS_SIGVTALRM 28 +#define SOLARIS_SIGPROF 29 +#define SOLARIS_SIGXCPU 30 +#define SOLARIS_SIGXFSZ 31 +#define SOLARIS_SIGWAITING 32 +#define SOLARIS_SIGLWP 33 +#define SOLARIS_SIGFREEZE 34 +#define SOLARIS_SIGTHAW 35 +#define SOLARIS_SIGCANCEL 36 +#define SOLARIS_SIGRTMIN 37 +#define SOLARIS_SIGRTMAX 44 +#define SOLARIS_NSIGNALS 44 + + +#define SOLARIS_SA_ONSTACK 1 +#define SOLARIS_SA_RESETHAND 2 +#define SOLARIS_SA_RESTART 4 +#define SOLARIS_SA_SIGINFO 8 +#define SOLARIS_SA_NODEFER 16 +#define SOLARIS_SA_NOCLDWAIT 0x10000 +#define SOLARIS_SA_NOCLDSTOP 0x20000 + +struct sol_siginfo { + int si_signo; + int si_code; + int si_errno; + union { + char pad[128-3*sizeof(int)]; + struct { + s32 _pid; + union { + struct { + s32 _uid; + s32 _value; + } _kill; + struct { + s32 _utime; + int _status; + s32 _stime; + } _cld; + } _pdata; + } _proc; + struct { /* SIGSEGV, SIGBUS, SIGILL and SIGFPE */ + u32 _addr; + int _trapno; + } _fault; + struct { /* SIGPOLL, SIGXFSZ */ + int _fd; + s32 _band; + } _file; + } _data; +}; + +#define SOLARIS_WUNTRACED 0x04 +#define SOLARIS_WNOHANG 0x40 +#define SOLARIS_WEXITED 0x01 +#define SOLARIS_WTRAPPED 0x02 +#define SOLARIS_WSTOPPED WUNTRACED +#define SOLARIS_WCONTINUED 0x08 +#define SOLARIS_WNOWAIT 0x80 + +#define SOLARIS_TRAP_BRKPT 1 +#define SOLARIS_TRAP_TRACE 2 +#define SOLARIS_CLD_EXITED 1 +#define SOLARIS_CLD_KILLED 2 +#define SOLARIS_CLD_DUMPED 3 +#define SOLARIS_CLD_TRAPPED 4 +#define SOLARIS_CLD_STOPPED 5 +#define SOLARIS_CLD_CONTINUED 6 +#define SOLARIS_POLL_IN 1 +#define SOLARIS_POLL_OUT 2 +#define SOLARIS_POLL_MSG 3 +#define SOLARIS_POLL_ERR 4 +#define SOLARIS_POLL_PRI 5 +#define SOLARIS_POLL_HUP 6 diff --git a/arch/sparc64/solaris/socket.c b/arch/sparc64/solaris/socket.c new file mode 100644 index 00000000000..ec8e074c4ea --- /dev/null +++ b/arch/sparc64/solaris/socket.c @@ -0,0 +1,415 @@ +/* $Id: socket.c,v 1.6 2002/02/08 03:57:14 davem Exp $ + * socket.c: Socket syscall emulation for Solaris 2.6+ + * + * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) + * + * 1999-08-19 Fixed socketpair code + * Jason Rappleye (rappleye@ccr.buffalo.edu) + */ + +#include <linux/types.h> +#include <linux/smp_lock.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/socket.h> +#include <linux/file.h> +#include <linux/net.h> +#include <linux/compat.h> +#include <net/compat.h> + +#include <asm/uaccess.h> +#include <asm/string.h> +#include <asm/oplib.h> +#include <asm/idprom.h> + +#include "conv.h" + +#define SOCK_SOL_STREAM 2 +#define SOCK_SOL_DGRAM 1 +#define SOCK_SOL_RAW 4 +#define SOCK_SOL_RDM 5 +#define SOCK_SOL_SEQPACKET 6 + +#define SOL_SO_SNDLOWAT 0x1003 +#define SOL_SO_RCVLOWAT 0x1004 +#define SOL_SO_SNDTIMEO 0x1005 +#define SOL_SO_RCVTIMEO 0x1006 +#define SOL_SO_STATE 0x2000 + +#define SOL_SS_NDELAY 0x040 +#define SOL_SS_NONBLOCK 0x080 +#define SOL_SS_ASYNC 0x100 + +#define SO_STATE 0x000e + +static int socket_check(int family, int type) +{ + if (family != PF_UNIX && family != PF_INET) + return -ESOCKTNOSUPPORT; + switch (type) { + case SOCK_SOL_STREAM: type = SOCK_STREAM; break; + case SOCK_SOL_DGRAM: type = SOCK_DGRAM; break; + case SOCK_SOL_RAW: type = SOCK_RAW; break; + case SOCK_SOL_RDM: type = SOCK_RDM; break; + case SOCK_SOL_SEQPACKET: type = SOCK_SEQPACKET; break; + default: return -EINVAL; + } + return type; +} + +static int solaris_to_linux_sockopt(int optname) +{ + switch (optname) { + case SOL_SO_SNDLOWAT: optname = SO_SNDLOWAT; break; + case SOL_SO_RCVLOWAT: optname = SO_RCVLOWAT; break; + case SOL_SO_SNDTIMEO: optname = SO_SNDTIMEO; break; + case SOL_SO_RCVTIMEO: optname = SO_RCVTIMEO; break; + case SOL_SO_STATE: optname = SO_STATE; break; + }; + + return optname; +} + +asmlinkage int solaris_socket(int family, int type, int protocol) +{ + int (*sys_socket)(int, int, int) = + (int (*)(int, int, int))SYS(socket); + + type = socket_check (family, type); + if (type < 0) return type; + return sys_socket(family, type, protocol); +} + +asmlinkage int solaris_socketpair(int *usockvec) +{ + int (*sys_socketpair)(int, int, int, int *) = + (int (*)(int, int, int, int *))SYS(socketpair); + + /* solaris socketpair really only takes one arg at the syscall + * level, int * usockvec. The libs apparently take care of + * making sure that family==AF_UNIX and type==SOCK_STREAM. The + * pointer we really want ends up residing in the first (and + * supposedly only) argument. + */ + + return sys_socketpair(AF_UNIX, SOCK_STREAM, 0, (int *)usockvec); +} + +asmlinkage int solaris_bind(int fd, struct sockaddr *addr, int addrlen) +{ + int (*sys_bind)(int, struct sockaddr *, int) = + (int (*)(int, struct sockaddr *, int))SUNOS(104); + + return sys_bind(fd, addr, addrlen); +} + +asmlinkage int solaris_setsockopt(int fd, int level, int optname, u32 optval, int optlen) +{ + int (*sunos_setsockopt)(int, int, int, u32, int) = + (int (*)(int, int, int, u32, int))SUNOS(105); + + optname = solaris_to_linux_sockopt(optname); + if (optname < 0) + return optname; + if (optname == SO_STATE) + return 0; + + return sunos_setsockopt(fd, level, optname, optval, optlen); +} + +asmlinkage int solaris_getsockopt(int fd, int level, int optname, u32 optval, u32 optlen) +{ + int (*sunos_getsockopt)(int, int, int, u32, u32) = + (int (*)(int, int, int, u32, u32))SUNOS(118); + + optname = solaris_to_linux_sockopt(optname); + if (optname < 0) + return optname; + + if (optname == SO_STATE) + optname = SOL_SO_STATE; + + return sunos_getsockopt(fd, level, optname, optval, optlen); +} + +asmlinkage int solaris_connect(int fd, struct sockaddr __user *addr, int addrlen) +{ + int (*sys_connect)(int, struct sockaddr __user *, int) = + (int (*)(int, struct sockaddr __user *, int))SYS(connect); + + return sys_connect(fd, addr, addrlen); +} + +asmlinkage int solaris_accept(int fd, struct sockaddr __user *addr, int __user *addrlen) +{ + int (*sys_accept)(int, struct sockaddr __user *, int __user *) = + (int (*)(int, struct sockaddr __user *, int __user *))SYS(accept); + + return sys_accept(fd, addr, addrlen); +} + +asmlinkage int solaris_listen(int fd, int backlog) +{ + int (*sys_listen)(int, int) = + (int (*)(int, int))SUNOS(106); + + return sys_listen(fd, backlog); +} + +asmlinkage int solaris_shutdown(int fd, int how) +{ + int (*sys_shutdown)(int, int) = + (int (*)(int, int))SYS(shutdown); + + return sys_shutdown(fd, how); +} + +#define MSG_SOL_OOB 0x1 +#define MSG_SOL_PEEK 0x2 +#define MSG_SOL_DONTROUTE 0x4 +#define MSG_SOL_EOR 0x8 +#define MSG_SOL_CTRUNC 0x10 +#define MSG_SOL_TRUNC 0x20 +#define MSG_SOL_WAITALL 0x40 +#define MSG_SOL_DONTWAIT 0x80 + +static int solaris_to_linux_msgflags(int flags) +{ + int fl = flags & (MSG_OOB|MSG_PEEK|MSG_DONTROUTE); + + if (flags & MSG_SOL_EOR) fl |= MSG_EOR; + if (flags & MSG_SOL_CTRUNC) fl |= MSG_CTRUNC; + if (flags & MSG_SOL_TRUNC) fl |= MSG_TRUNC; + if (flags & MSG_SOL_WAITALL) fl |= MSG_WAITALL; + if (flags & MSG_SOL_DONTWAIT) fl |= MSG_DONTWAIT; + return fl; +} + +static int linux_to_solaris_msgflags(int flags) +{ + int fl = flags & (MSG_OOB|MSG_PEEK|MSG_DONTROUTE); + + if (flags & MSG_EOR) fl |= MSG_SOL_EOR; + if (flags & MSG_CTRUNC) fl |= MSG_SOL_CTRUNC; + if (flags & MSG_TRUNC) fl |= MSG_SOL_TRUNC; + if (flags & MSG_WAITALL) fl |= MSG_SOL_WAITALL; + if (flags & MSG_DONTWAIT) fl |= MSG_SOL_DONTWAIT; + return fl; +} + +asmlinkage int solaris_recvfrom(int s, char __user *buf, int len, int flags, u32 from, u32 fromlen) +{ + int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *) = + (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom); + + return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), A(from), A(fromlen)); +} + +asmlinkage int solaris_recv(int s, char __user *buf, int len, int flags) +{ + int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *) = + (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom); + + return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), NULL, NULL); +} + +asmlinkage int solaris_sendto(int s, char __user *buf, int len, int flags, u32 to, u32 tolen) +{ + int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *) = + (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(sendto); + + return sys_sendto(s, buf, len, solaris_to_linux_msgflags(flags), A(to), A(tolen)); +} + +asmlinkage int solaris_send(int s, char *buf, int len, int flags) +{ + int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int *) = + (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(sendto); + + return sys_sendto(s, buf, len, solaris_to_linux_msgflags(flags), NULL, NULL); +} + +asmlinkage int solaris_getpeername(int fd, struct sockaddr *addr, int *addrlen) +{ + int (*sys_getpeername)(int, struct sockaddr *, int *) = + (int (*)(int, struct sockaddr *, int *))SYS(getpeername); + + return sys_getpeername(fd, addr, addrlen); +} + +asmlinkage int solaris_getsockname(int fd, struct sockaddr *addr, int *addrlen) +{ + int (*sys_getsockname)(int, struct sockaddr *, int *) = + (int (*)(int, struct sockaddr *, int *))SYS(getsockname); + + return sys_getsockname(fd, addr, addrlen); +} + +/* XXX This really belongs in some header file... -DaveM */ +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - + 16 for IP, 16 for IPX, + 24 for IPv6, + about 80 for AX.25 */ + +struct sol_nmsghdr { + u32 msg_name; + int msg_namelen; + u32 msg_iov; + u32 msg_iovlen; + u32 msg_control; + u32 msg_controllen; + u32 msg_flags; +}; + +struct sol_cmsghdr { + u32 cmsg_len; + int cmsg_level; + int cmsg_type; + unsigned char cmsg_data[0]; +}; + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, + struct sol_nmsghdr __user *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = A(tmp1); + kmsg->msg_iov = A(tmp2); + kmsg->msg_control = A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + kmsg->msg_flags = solaris_to_linux_msgflags(kmsg->msg_flags); + + return err; +} + +asmlinkage int solaris_sendmsg(int fd, struct sol_nmsghdr __user *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_compat_iovec(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + struct sol_cmsghdr __user *ucmsg = kern_msg.msg_control; + unsigned long *kcmsg; + compat_size_t cmlen; + + if(kern_msg.msg_controllen > sizeof(ctl) && + kern_msg.msg_controllen <= 256) { + err = -ENOBUFS; + ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); + if(!ctl_buf) + goto out_freeiov; + } + __get_user(cmlen, &ucmsg->cmsg_len); + kcmsg = (unsigned long *) ctl_buf; + *kcmsg++ = (unsigned long)cmlen; + err = -EFAULT; + if(copy_from_user(kcmsg, &ucmsg->cmsg_level, + kern_msg.msg_controllen - sizeof(compat_size_t))) + goto out_freectl; + kern_msg.msg_control = ctl_buf; + } + kern_msg.msg_flags = solaris_to_linux_msgflags(user_flags); + + lock_kernel(); + 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); + } + unlock_kernel(); + +out_freectl: + /* 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 solaris_recvmsg(int fd, struct sol_nmsghdr __user *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 __user *uaddr; + int __user *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_compat_iovec(&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; + + lock_kernel(); + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); + if(err >= 0) + len = err; + sockfd_put(sock); + } + unlock_kernel(); + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(err >= 0) { + err = __put_user(linux_to_solaris_msgflags(kern_msg.msg_flags), &user_msg->msg_flags); + if(!err) { + /* XXX Convert cmsg back into userspace 32-bit format... */ + err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr, + &user_msg->msg_controllen); + } + } + + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + return len; +} diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c new file mode 100644 index 00000000000..d7c1c76582c --- /dev/null +++ b/arch/sparc64/solaris/socksys.c @@ -0,0 +1,211 @@ +/* $Id: socksys.c,v 1.21 2002/02/08 03:57:14 davem Exp $ + * socksys.c: /dev/inet/ stuff for Solaris emulation. + * + * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997, 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) + * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) + */ + +/* + * Dave, _please_ give me specifications on this fscking mess so that I + * could at least get it into the state when it wouldn't screw the rest of + * the kernel over. socksys.c and timod.c _stink_ and we are not talking + * H2S here, it's isopropilmercaptan in concentrations way over LD50. -- AV + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/syscalls.h> +#include <linux/in.h> +#include <linux/devfs_fs_kernel.h> + +#include <net/sock.h> + +#include <asm/uaccess.h> +#include <asm/termios.h> + +#include "conv.h" +#include "socksys.h" + +static int af_inet_protocols[] = { +IPPROTO_ICMP, IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_IPIP, IPPROTO_TCP, +IPPROTO_EGP, IPPROTO_PUP, IPPROTO_UDP, IPPROTO_IDP, IPPROTO_RAW, +0, 0, 0, 0, 0, 0, +}; + +#ifndef DEBUG_SOLARIS_KMALLOC + +#define mykmalloc kmalloc +#define mykfree kfree + +#else + +extern void * mykmalloc(size_t s, int gfp); +extern void mykfree(void *); + +#endif + +static unsigned int (*sock_poll)(struct file *, poll_table *); + +static struct file_operations socksys_file_ops = { + /* Currently empty */ +}; + +static int socksys_open(struct inode * inode, struct file * filp) +{ + int family, type, protocol, fd; + struct dentry *dentry; + int (*sys_socket)(int,int,int) = + (int (*)(int,int,int))SUNOS(97); + struct sol_socket_struct * sock; + + family = ((iminor(inode) >> 4) & 0xf); + switch (family) { + case AF_UNIX: + type = SOCK_STREAM; + protocol = 0; + break; + case AF_INET: + protocol = af_inet_protocols[iminor(inode) & 0xf]; + switch (protocol) { + case IPPROTO_TCP: type = SOCK_STREAM; break; + case IPPROTO_UDP: type = SOCK_DGRAM; break; + default: type = SOCK_RAW; break; + } + break; + default: + type = SOCK_RAW; + protocol = 0; + break; + } + + fd = sys_socket(family, type, protocol); + if (fd < 0) + return fd; + /* + * N.B. The following operations are not legal! + * + * No shit. WTF is it supposed to do, anyway? + * + * Try instead: + * d_delete(filp->f_dentry), then d_instantiate with sock inode + */ + dentry = filp->f_dentry; + filp->f_dentry = dget(fcheck(fd)->f_dentry); + filp->f_dentry->d_inode->i_rdev = inode->i_rdev; + filp->f_dentry->d_inode->i_flock = inode->i_flock; + SOCKET_I(filp->f_dentry->d_inode)->file = filp; + filp->f_op = &socksys_file_ops; + sock = (struct sol_socket_struct*) + mykmalloc(sizeof(struct sol_socket_struct), GFP_KERNEL); + if (!sock) return -ENOMEM; + SOLDD(("sock=%016lx(%016lx)\n", sock, filp)); + sock->magic = SOLARIS_SOCKET_MAGIC; + sock->modcount = 0; + sock->state = TS_UNBND; + sock->offset = 0; + sock->pfirst = sock->plast = NULL; + filp->private_data = sock; + SOLDD(("filp->private_data %016lx\n", filp->private_data)); + + sys_close(fd); + dput(dentry); + return 0; +} + +static int socksys_release(struct inode * inode, struct file * filp) +{ + struct sol_socket_struct * sock; + struct T_primsg *it; + + /* XXX: check this */ + sock = (struct sol_socket_struct *)filp->private_data; + SOLDD(("sock release %016lx(%016lx)\n", sock, filp)); + it = sock->pfirst; + while (it) { + struct T_primsg *next = it->next; + + SOLDD(("socksys_release %016lx->%016lx\n", it, next)); + mykfree((char*)it); + it = next; + } + filp->private_data = NULL; + SOLDD(("socksys_release %016lx\n", sock)); + mykfree((char*)sock); + return 0; +} + +static unsigned int socksys_poll(struct file * filp, poll_table * wait) +{ + struct inode *ino; + unsigned int mask = 0; + + ino=filp->f_dentry->d_inode; + if (ino && S_ISSOCK(ino->i_mode)) { + struct sol_socket_struct *sock; + sock = (struct sol_socket_struct*)filp->private_data; + if (sock && sock->pfirst) { + mask |= POLLIN | POLLRDNORM; + if (sock->pfirst->pri == MSG_HIPRI) + mask |= POLLPRI; + } + } + if (sock_poll) + mask |= (*sock_poll)(filp, wait); + return mask; +} + +static struct file_operations socksys_fops = { + .open = socksys_open, + .release = socksys_release, +}; + +int __init +init_socksys(void) +{ + int ret; + struct file * file; + int (*sys_socket)(int,int,int) = + (int (*)(int,int,int))SUNOS(97); + int (*sys_close)(unsigned int) = + (int (*)(unsigned int))SYS(close); + + ret = register_chrdev (30, "socksys", &socksys_fops); + if (ret < 0) { + printk ("Couldn't register socksys character device\n"); + return ret; + } + ret = sys_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ret < 0) { + printk ("Couldn't create socket\n"); + return ret; + } + + devfs_mk_cdev(MKDEV(30, 0), S_IFCHR|S_IRUSR|S_IWUSR, "socksys"); + + file = fcheck(ret); + /* N.B. Is this valid? Suppose the f_ops are in a module ... */ + socksys_file_ops = *file->f_op; + sys_close(ret); + sock_poll = socksys_file_ops.poll; + socksys_file_ops.poll = socksys_poll; + socksys_file_ops.release = socksys_release; + return 0; +} + +void +cleanup_socksys(void) +{ + if (unregister_chrdev(30, "socksys")) + printk ("Couldn't unregister socksys character device\n"); + devfs_remove ("socksys"); +} diff --git a/arch/sparc64/solaris/socksys.h b/arch/sparc64/solaris/socksys.h new file mode 100644 index 00000000000..5d1b78ec160 --- /dev/null +++ b/arch/sparc64/solaris/socksys.h @@ -0,0 +1,208 @@ +/* $Id: socksys.h,v 1.2 1998/03/26 08:46:07 jj Exp $ + * socksys.h: Definitions for STREAMS modules emulation code. + * + * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) + */ + +#define MSG_HIPRI 0x01 +#define MSG_ANY 0x02 +#define MSG_BAND 0x04 + +#define MORECTL 1 +#define MOREDATA 2 + +#define TBADADDR 1 +#define TBADOPT 2 +#define TACCES 3 +#define TBADF 4 +#define TNOADDR 5 +#define TOUTSTATE 6 +#define TBADSEQ 7 +#define TSYSERR 8 +#define TLOOK 9 +#define TBADDATA 10 +#define TBUFOVFLW 11 +#define TFLOW 12 +#define TNODATA 13 +#define TNODIS 14 +#define TNOUDERR 15 +#define TBADFLAG 16 +#define TNOREL 17 +#define TNOTSUPPORT 18 +#define TSTATECHNG 19 + +#define T_CONN_REQ 0 +#define T_CONN_RES 1 +#define T_DISCON_REQ 2 +#define T_DATA_REQ 3 +#define T_EXDATA_REQ 4 +#define T_INFO_REQ 5 +#define T_BIND_REQ 6 +#define T_UNBIND_REQ 7 +#define T_UNITDATA_REQ 8 +#define T_OPTMGMT_REQ 9 +#define T_ORDREL_REQ 10 + +#define T_CONN_IND 11 +#define T_CONN_CON 12 +#define T_DISCON_IND 13 +#define T_DATA_IND 14 +#define T_EXDATA_IND 15 +#define T_INFO_ACK 16 +#define T_BIND_ACK 17 +#define T_ERROR_ACK 18 +#define T_OK_ACK 19 +#define T_UNITDATA_IND 20 +#define T_UDERROR_IND 21 +#define T_OPTMGMT_ACK 22 +#define T_ORDREL_IND 23 + +#define T_NEGOTIATE 0x0004 +#define T_FAILURE 0x0040 + +#define TS_UNBND 0 /* unbound */ +#define TS_WACK_BREQ 1 /* waiting for T_BIND_REQ ack */ +#define TS_WACK_UREQ 2 /* waiting for T_UNBIND_REQ ack */ +#define TS_IDLE 3 /* idle */ +#define TS_WACK_OPTREQ 4 /* waiting for T_OPTMGMT_REQ ack */ +#define TS_WACK_CREQ 5 /* waiting for T_CONN_REQ ack */ +#define TS_WCON_CREQ 6 /* waiting for T_CONN_REQ confirmation */ +#define TS_WRES_CIND 7 /* waiting for T_CONN_IND */ +#define TS_WACK_CRES 8 /* waiting for T_CONN_RES ack */ +#define TS_DATA_XFER 9 /* data transfer */ +#define TS_WIND_ORDREL 10 /* releasing read but not write */ +#define TS_WREQ_ORDREL 11 /* wait to release write but not read */ +#define TS_WACK_DREQ6 12 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ7 13 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ9 14 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ10 15 /* waiting for T_DISCON_REQ ack */ +#define TS_WACK_DREQ11 16 /* waiting for T_DISCON_REQ ack */ +#define TS_NOSTATES 17 + +struct T_conn_req { + s32 PRIM_type; + s32 DEST_length; + s32 DEST_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_bind_req { + s32 PRIM_type; + s32 ADDR_length; + s32 ADDR_offset; + u32 CONIND_number; +}; + +struct T_unitdata_req { + s32 PRIM_type; + s32 DEST_length; + s32 DEST_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_optmgmt_req { + s32 PRIM_type; + s32 OPT_length; + s32 OPT_offset; + s32 MGMT_flags; +}; + +struct T_bind_ack { + s32 PRIM_type; + s32 ADDR_length; + s32 ADDR_offset; + u32 CONIND_number; +}; + +struct T_error_ack { + s32 PRIM_type; + s32 ERROR_prim; + s32 TLI_error; + s32 UNIX_error; +}; + +struct T_ok_ack { + s32 PRIM_type; + s32 CORRECT_prim; +}; + +struct T_conn_ind { + s32 PRIM_type; + s32 SRC_length; + s32 SRC_offset; + s32 OPT_length; + s32 OPT_offset; + s32 SEQ_number; +}; + +struct T_conn_con { + s32 PRIM_type; + s32 RES_length; + s32 RES_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_discon_ind { + s32 PRIM_type; + s32 DISCON_reason; + s32 SEQ_number; +}; + +struct T_unitdata_ind { + s32 PRIM_type; + s32 SRC_length; + s32 SRC_offset; + s32 OPT_length; + s32 OPT_offset; +}; + +struct T_optmgmt_ack { + s32 PRIM_type; + s32 OPT_length; + s32 OPT_offset; + s32 MGMT_flags; +}; + +struct opthdr { + s32 level; + s32 name; + s32 len; + char value[0]; +}; + +struct T_primsg { + struct T_primsg *next; + unsigned char pri; + unsigned char band; + int length; + s32 type; +}; + +struct strbuf { + s32 maxlen; + s32 len; + u32 buf; +} ; + +/* Constants used by STREAMS modules emulation code */ + +typedef char sol_module; + +#define MAX_NR_STREAM_MODULES 16 + +/* Private data structure assigned to sockets. */ + +struct sol_socket_struct { + int magic; + int modcount; + sol_module module[MAX_NR_STREAM_MODULES]; + long state; + int offset; + struct T_primsg *pfirst, *plast; +}; + +#define SOLARIS_SOCKET_MAGIC 0xADDED + diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S new file mode 100644 index 00000000000..d25667eeae1 --- /dev/null +++ b/arch/sparc64/solaris/systbl.S @@ -0,0 +1,314 @@ +/* $Id: systbl.S,v 1.11 2000/03/13 21:57:35 davem Exp $ + * systbl.S: System call entry point table for Solaris compatibility. + * + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) + */ + +#include <asm/unistd.h> + +/* Fall back to sys_call_table32 entry */ +#define CHAIN(name) __NR_##name + +/* Pass pt_regs pointer as first argument */ +#define REGS(name) name+1 + +/* Hack till all be implemented */ +#define solaris_getpmsg solaris_unimplemented +#define solaris_hrtsys solaris_unimplemented +#define solaris_msgsys solaris_unimplemented +#define solaris_putpmsg solaris_unimplemented +#define solaris_semsys solaris_unimplemented + + .data + .globl solaris_sys_table +solaris_sys_table: + .word solaris_unimplemented /* nosys 0 */ + .word CHAIN(exit) /* exit d 1 */ + .word CHAIN(fork) /* fork 2 */ + .word CHAIN(read) /* read dpd 3 */ + .word CHAIN(write) /* write dpd 4 */ + .word solaris_open /* open soo 5 */ + .word CHAIN(close) /* close d 6 */ + .word solaris_wait /* wait xxx 7 */ + .word CHAIN(creat) /* creat so 8 */ + .word CHAIN(link) /* link ss 9 */ + .word CHAIN(unlink) /* unlink s 10 */ + .word solaris_unimplemented /* exec sxx 11 */ + .word CHAIN(chdir) /* chdir s 12 */ + .word CHAIN(time) /* time 13 */ + .word solaris_mknod /* mknod sox 14 */ + .word CHAIN(chmod) /* chmod so 15 */ + .word CHAIN(chown) /* chown sdd 16 */ + .word solaris_brk /* brk/break x 17 */ + .word solaris_stat /* stat sp 18 */ + .word CHAIN(lseek) /* seek/lseek ddd 19 */ + .word solaris_getpid /* getpid 20 */ + .word solaris_unimplemented /* mount 21 */ + .word CHAIN(umount) /* umount s 22 */ + .word CHAIN(setuid) /* setuid d 23 */ + .word solaris_getuid /* getuid 24 */ + .word CHAIN(stime) /* stime d 25 */ +#if 0 + .word solaris_ptrace /* ptrace xdxx 26 */ +#else + .word CHAIN(ptrace) /* ptrace xdxx 26 */ +#endif + .word CHAIN(alarm) /* alarm d 27 */ + .word solaris_fstat /* fstat dp 28 */ + .word CHAIN(pause) /* pause 29 */ + .word CHAIN(utime) /* utime xx 30 */ + .word solaris_unimplemented /* stty 31 */ + .word solaris_unimplemented /* gtty 32 */ + .word solaris_access /* access so 33 */ + .word CHAIN(nice) /* nice d 34 */ + .word solaris_statfs /* statfs spdd 35 */ + .word CHAIN(sync) /* sync 36 */ + .word solaris_kill /* kill dd 37 */ + .word solaris_fstatfs /* fstatfs dpdd 38 */ + .word solaris_procids /* pgrpsys ddd 39 */ + .word solaris_unimplemented /* xenix 40 */ + .word CHAIN(dup) /* dup d 41 */ + .word CHAIN(pipe) /* pipe 42 */ + .word CHAIN(times) /* times p 43 */ + .word 44 /*CHAIN(profil)*/ /* prof xxxx 44 */ + .word solaris_unimplemented /* lock/plock 45 */ + .word CHAIN(setgid) /* setgid d 46 */ + .word solaris_getgid /* getgid 47 */ + .word solaris_sigfunc /* sigfunc xx 48 */ + .word REGS(solaris_msgsys) /* msgsys dxddd 49 */ + .word solaris_unimplemented /* syssun/3b 50 */ + .word CHAIN(acct) /* acct/sysacct x 51 */ + .word solaris_shmsys /* shmsys ddxo 52 */ + .word REGS(solaris_semsys) /* semsys dddx 53 */ + .word solaris_ioctl /* ioctl dxx 54 */ + .word solaris_unimplemented /* uadmin xxx 55 */ + .word solaris_unimplemented /* reserved:exch 56 */ + .word solaris_utssys /* utssys x 57 */ + .word CHAIN(fsync) /* fsync d 58 */ + .word CHAIN(execve) /* execv spp 59 */ + .word CHAIN(umask) /* umask o 60 */ + .word CHAIN(chroot) /* chroot s 61 */ + .word solaris_fcntl /* fcntl dxx 62 */ + .word solaris_ulimit /* ulimit xx 63 */ + .word solaris_unimplemented /* ? 64 */ + .word solaris_unimplemented /* ? 65 */ + .word solaris_unimplemented /* ? 66 */ + .word solaris_unimplemented /* ? 67 */ + .word solaris_unimplemented /* ? 68 */ + .word solaris_unimplemented /* ? 69 */ + .word solaris_unimplemented /* advfs 70 */ + .word solaris_unimplemented /* unadvfs 71 */ + .word solaris_unimplemented /* rmount 72 */ + .word solaris_unimplemented /* rumount 73 */ + .word solaris_unimplemented /* rfstart 74 */ + .word solaris_unimplemented /* ? 75 */ + .word solaris_unimplemented /* rdebug 76 */ + .word solaris_unimplemented /* rfstop 77 */ + .word solaris_unimplemented /* rfsys 78 */ + .word CHAIN(rmdir) /* rmdir s 79 */ + .word CHAIN(mkdir) /* mkdir so 80 */ + .word CHAIN(getdents) /* getdents dxd 81 */ + .word solaris_unimplemented /* libattach 82 */ + .word solaris_unimplemented /* libdetach 83 */ + .word CHAIN(sysfs) /* sysfs dxx 84 */ + .word solaris_getmsg /* getmsg dxxx 85 */ + .word solaris_putmsg /* putmsg dxxd 86 */ + .word CHAIN(poll) /* poll xdd 87 */ + .word solaris_lstat /* lstat sp 88 */ + .word CHAIN(symlink) /* symlink ss 89 */ + .word CHAIN(readlink) /* readlink spd 90 */ + .word CHAIN(setgroups) /* setgroups dp 91 */ + .word CHAIN(getgroups) /* getgroups dp 92 */ + .word CHAIN(fchmod) /* fchmod do 93 */ + .word CHAIN(fchown) /* fchown ddd 94 */ + .word solaris_sigprocmask /* sigprocmask dxx 95 */ + .word solaris_sigsuspend /* sigsuspend x 96 */ + .word solaris_sigaltstack /* sigaltstack xx 97 */ + .word solaris_sigaction /* sigaction dxx 98 */ + .word solaris_sigpending /* sigpending dd 99 */ + .word REGS(solaris_context) /* context 100 */ + .word solaris_unimplemented /* evsys 101 */ + .word solaris_unimplemented /* evtrapret 102 */ + .word solaris_statvfs /* statvfs sp 103 */ + .word solaris_fstatvfs /* fstatvfs dp 104 */ + .word solaris_unimplemented /* unknown 105 */ + .word solaris_unimplemented /* nfssys 106 */ + .word solaris_waitid /* waitid ddxd 107 */ + .word solaris_unimplemented /* sigsendsys ddd 108 */ + .word REGS(solaris_hrtsys) /* hrtsys xxx 109 */ + .word solaris_unimplemented /* acancel dxd 110 */ + .word solaris_unimplemented /* async 111 */ + .word solaris_unimplemented /* priocntlsys 112 */ + .word solaris_pathconf /* pathconf sd 113 */ + .word CHAIN(mincore) /* mincore d 114 */ + .word solaris_mmap /* mmap xxxxdx 115 */ + .word CHAIN(mprotect) /* mprotect xdx 116 */ + .word CHAIN(munmap) /* munmap xd 117 */ + .word solaris_fpathconf /* fpathconf dd 118 */ + .word CHAIN(fork) /* fork 119 */ + .word solaris_unimplemented /* fchdir d 120 */ + .word CHAIN(readv) /* readv dxd 121 */ + .word CHAIN(writev) /* writev dxd 122 */ + .word solaris_xstat /* xstat dsx 123 */ + .word solaris_lxstat /* lxstat dsx 124 */ + .word solaris_fxstat /* fxstat ddx 125 */ + .word solaris_xmknod /* xmknod dsox 126 */ + .word solaris_unimplemented /* syslocal d 127 */ + .word solaris_setrlimit /* setrlimit dp 128 */ + .word solaris_getrlimit /* getrlimit dp 129 */ + .word CHAIN(chown) /* lchown sdd 130 */ + .word solaris_unimplemented /* memcntl 131 */ + .word solaris_getpmsg /* getpmsg dxxxx 132 */ + .word solaris_putpmsg /* putpmsg dxxdd 133 */ + .word CHAIN(rename) /* rename ss 134 */ + .word solaris_utsname /* uname x 135 */ + .word solaris_unimplemented /* setegid 136 */ + .word solaris_sysconf /* sysconfig d 137 */ + .word solaris_unimplemented /* adjtime 138 */ + .word solaris_sysinfo /* systeminfo dsd 139 */ + .word solaris_unimplemented /* ? 140 */ + .word solaris_unimplemented /* seteuid 141 */ + .word solaris_unimplemented /* ? 142 */ + .word solaris_unimplemented /* ? 143 */ + .word solaris_unimplemented /* secsys dx 144 */ + .word solaris_unimplemented /* filepriv sdxd 145 */ + .word solaris_unimplemented /* procpriv dxd 146 */ + .word solaris_unimplemented /* devstat sdx 147 */ + .word solaris_unimplemented /* aclipc ddddx 148 */ + .word solaris_unimplemented /* fdevstat ddx 149 */ + .word solaris_unimplemented /* flvlfile ddx 150 */ + .word solaris_unimplemented /* lvlfile sdx 151 */ + .word solaris_unimplemented /* ? 152 */ + .word solaris_unimplemented /* fchroot d 153 */ + .word solaris_unimplemented /* lvlproc dx 154 */ + .word solaris_unimplemented /* ? 155 */ + .word solaris_gettimeofday /* gettimeofday x 156 */ + .word CHAIN(getitimer) /* getitimer dx 157 */ + .word CHAIN(setitimer) /* setitimer dxx 158 */ + .word solaris_unimplemented /* lwp-xxx 159 */ + .word solaris_unimplemented /* lwp-xxx 160 */ + .word solaris_unimplemented /* lwp-xxx 161 */ + .word solaris_unimplemented /* lwp-xxx 162 */ + .word solaris_unimplemented /* lwp-xxx 163 */ + .word solaris_unimplemented /* lwp-xxx 164 */ + .word solaris_unimplemented /* lwp-xxx 165 */ + .word solaris_unimplemented /* lwp-xxx 166 */ + .word solaris_unimplemented /* lwp-xxx 167 */ + .word solaris_unimplemented /* lwp-xxx 168 */ + .word solaris_unimplemented /* lwp-xxx 169 */ + .word solaris_unimplemented /* lwp-xxx 170 */ + .word solaris_unimplemented /* lwp-xxx 171 */ + .word solaris_unimplemented /* lwp-xxx 172 */ + .word solaris_pread /* pread dpdd 173 */ + .word solaris_pwrite /* pwrite dpdd 174 */ + .word REGS(solaris_llseek) /* llseek dLd 175 */ + .word solaris_unimplemented /* lwpself 176 */ + .word solaris_unimplemented /* lwpinfo 177 */ + .word solaris_unimplemented /* lwpprivate 178 */ + .word solaris_unimplemented /* processorbind 179 */ + .word solaris_unimplemented /* processorexbind 180 */ + .word solaris_unimplemented /* 181 */ + .word solaris_unimplemented /* sync_mailbox 182 */ + .word solaris_unimplemented /* prepblock 183 */ + .word solaris_unimplemented /* block 184 */ + .word solaris_acl /* acl sddp 185 */ + .word solaris_unimplemented /* unblock 186 */ + .word solaris_unimplemented /* cancelblock 187 */ + .word solaris_unimplemented /* ? 188 */ + .word solaris_unimplemented /* xxxxx 189 */ + .word solaris_unimplemented /* xxxxxe 190 */ + .word solaris_unimplemented /* 191 */ + .word solaris_unimplemented /* 192 */ + .word solaris_unimplemented /* 193 */ + .word solaris_unimplemented /* 194 */ + .word solaris_unimplemented /* 195 */ + .word solaris_unimplemented /* 196 */ + .word solaris_unimplemented /* 197 */ + .word solaris_unimplemented /* 198 */ + .word CHAIN(nanosleep) /* nanosleep dd 199 */ + .word solaris_facl /* facl dddp 200 */ + .word solaris_unimplemented /* 201 */ + .word CHAIN(setreuid) /* setreuid dd 202 */ + .word CHAIN(setregid) /* setregid dd 203 */ + .word solaris_unimplemented /* 204 */ + .word solaris_unimplemented /* 205 */ + .word solaris_unimplemented /* 206 */ + .word solaris_unimplemented /* 207 */ + .word solaris_unimplemented /* 208 */ + .word solaris_unimplemented /* 209 */ + .word solaris_unimplemented /* 210 */ + .word solaris_unimplemented /* 211 */ + .word solaris_unimplemented /* 212 */ + .word solaris_getdents64 /* getdents64 dpd 213 */ + .word REGS(solaris_mmap64) /* mmap64 xxxxdX 214 */ + .word solaris_stat64 /* stat64 sP 215 */ + .word solaris_lstat64 /* lstat64 sP 216 */ + .word solaris_fstat64 /* fstat64 dP 217 */ + .word solaris_statvfs64 /* statvfs64 sP 218 */ + .word solaris_fstatvfs64 /* fstatvfs64 dP 219 */ + .word solaris_setrlimit64 /* setrlimit64 dP 220 */ + .word solaris_getrlimit64 /* getrlimit64 dP 221 */ + .word CHAIN(pread64) /* pread64 dpdD 222 */ + .word CHAIN(pwrite64) /* pwrite64 dpdD 223 */ + .word CHAIN(creat) /* creat64 so 224 */ + .word solaris_open /* open64 soo 225 */ + .word solaris_unimplemented /* 226 */ + .word solaris_unimplemented /* 227 */ + .word solaris_unimplemented /* 228 */ + .word solaris_unimplemented /* 229 */ + .word solaris_socket /* socket ddd 230 */ + .word solaris_socketpair /* socketpair dddp 231 */ + .word solaris_bind /* bind dpd 232 */ + .word solaris_listen /* listen dd 233 */ + .word solaris_accept /* accept dpp 234 */ + .word solaris_connect /* connect dpd 235 */ + .word solaris_shutdown /* shutdown dd 236 */ + .word solaris_recv /* recv dpdd 237 */ + .word solaris_recvfrom /* recvfrom dpddpp 238 */ + .word solaris_recvmsg /* recvmsg dpd 239 */ + .word solaris_send /* send dpdd 240 */ + .word solaris_sendmsg /* sendmsg dpd 241 */ + .word solaris_sendto /* sendto dpddpd 242 */ + .word solaris_getpeername /* getpeername dpp 243 */ + .word solaris_getsockname /* getsockname dpp 244 */ + .word solaris_getsockopt /* getsockopt dddpp 245 */ + .word solaris_setsockopt /* setsockopt dddpp 246 */ + .word solaris_unimplemented /* 247 */ + .word solaris_ntp_gettime /* ntp_gettime p 248 */ + .word solaris_ntp_adjtime /* ntp_adjtime p 249 */ + .word solaris_unimplemented /* 250 */ + .word solaris_unimplemented /* 251 */ + .word solaris_unimplemented /* 252 */ + .word solaris_unimplemented /* 253 */ + .word solaris_unimplemented /* 254 */ + .word solaris_unimplemented /* 255 */ + .word solaris_unimplemented /* 256 */ + .word solaris_unimplemented /* 257 */ + .word solaris_unimplemented /* 258 */ + .word solaris_unimplemented /* 259 */ + .word solaris_unimplemented /* 260 */ + .word solaris_unimplemented /* 261 */ + .word solaris_unimplemented /* 262 */ + .word solaris_unimplemented /* 263 */ + .word solaris_unimplemented /* 264 */ + .word solaris_unimplemented /* 265 */ + .word solaris_unimplemented /* 266 */ + .word solaris_unimplemented /* 267 */ + .word solaris_unimplemented /* 268 */ + .word solaris_unimplemented /* 269 */ + .word solaris_unimplemented /* 270 */ + .word solaris_unimplemented /* 271 */ + .word solaris_unimplemented /* 272 */ + .word solaris_unimplemented /* 273 */ + .word solaris_unimplemented /* 274 */ + .word solaris_unimplemented /* 275 */ + .word solaris_unimplemented /* 276 */ + .word solaris_unimplemented /* 277 */ + .word solaris_unimplemented /* 278 */ + .word solaris_unimplemented /* 279 */ + .word solaris_unimplemented /* 280 */ + .word solaris_unimplemented /* 281 */ + .word solaris_unimplemented /* 282 */ + .word solaris_unimplemented /* 283 */ + diff --git a/arch/sparc64/solaris/timod.c b/arch/sparc64/solaris/timod.c new file mode 100644 index 00000000000..022c80f4339 --- /dev/null +++ b/arch/sparc64/solaris/timod.c @@ -0,0 +1,959 @@ +/* $Id: timod.c,v 1.19 2002/02/08 03:57:14 davem Exp $ + * timod.c: timod emulation. + * + * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz) + * + * Streams & timod emulation based on code + * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk) + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/netdevice.h> +#include <linux/poll.h> + +#include <net/sock.h> + +#include <asm/uaccess.h> +#include <asm/termios.h> + +#include "conv.h" +#include "socksys.h" + +asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg); + +static DEFINE_SPINLOCK(timod_pagelock); +static char * page = NULL ; + +#ifndef DEBUG_SOLARIS_KMALLOC + +#define mykmalloc kmalloc +#define mykfree kfree + +#else + +void * mykmalloc(size_t s, int gfp) +{ + static char * page; + static size_t free; + void * r; + s = ((s + 63) & ~63); + if( s > PAGE_SIZE ) { + SOLD("too big size, calling real kmalloc"); + return kmalloc(s, gfp); + } + if( s > free ) { + /* we are wasting memory, but we don't care */ + page = (char *)__get_free_page(gfp); + free = PAGE_SIZE; + } + r = page; + page += s; + free -= s; + return r; +} + +void mykfree(void *p) +{ +} + +#endif + +#ifndef DEBUG_SOLARIS + +#define BUF_SIZE PAGE_SIZE +#define PUT_MAGIC(a,m) +#define SCHECK_MAGIC(a,m) +#define BUF_OFFSET 0 +#define MKCTL_TRAILER 0 + +#else + +#define BUF_SIZE (PAGE_SIZE-2*sizeof(u64)) +#define BUFPAGE_MAGIC 0xBADC0DEDDEADBABEL +#define MKCTL_MAGIC 0xDEADBABEBADC0DEDL +#define PUT_MAGIC(a,m) do{(*(u64*)(a))=(m);}while(0) +#define SCHECK_MAGIC(a,m) do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\ + __FILE__,__LINE__,__FUNCTION__,(m),(a));}while(0) +#define BUF_OFFSET sizeof(u64) +#define MKCTL_TRAILER sizeof(u64) + +#endif + +static char *getpage( void ) +{ + char *r; + SOLD("getting page"); + spin_lock(&timod_pagelock); + if (page) { + r = page; + page = NULL; + spin_unlock(&timod_pagelock); + SOLD("got cached"); + return r + BUF_OFFSET; + } + spin_unlock(&timod_pagelock); + SOLD("getting new"); + r = (char *)__get_free_page(GFP_KERNEL); + PUT_MAGIC(r,BUFPAGE_MAGIC); + PUT_MAGIC(r+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC); + return r + BUF_OFFSET; +} + +static void putpage(char *p) +{ + SOLD("putting page"); + p = p - BUF_OFFSET; + SCHECK_MAGIC(p,BUFPAGE_MAGIC); + SCHECK_MAGIC(p+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC); + spin_lock(&timod_pagelock); + if (page) { + spin_unlock(&timod_pagelock); + free_page((unsigned long)p); + SOLD("freed it"); + } else { + page = p; + spin_unlock(&timod_pagelock); + SOLD("cached it"); + } +} + +static struct T_primsg *timod_mkctl(int size) +{ + struct T_primsg *it; + + SOLD("creating primsg"); + it = (struct T_primsg *)mykmalloc(size+sizeof(*it)-sizeof(s32)+2*MKCTL_TRAILER, GFP_KERNEL); + if (it) { + SOLD("got it"); + it->pri = MSG_HIPRI; + it->length = size; + PUT_MAGIC((char*)((u64)(((char *)&it->type)+size+7)&~7),MKCTL_MAGIC); + } + return it; +} + +static void timod_wake_socket(unsigned int fd) +{ + struct socket *sock; + + SOLD("wakeing socket"); + sock = SOCKET_I(current->files->fd[fd]->f_dentry->d_inode); + wake_up_interruptible(&sock->wait); + read_lock(&sock->sk->sk_callback_lock); + if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags)) + __kill_fasync(sock->fasync_list, SIGIO, POLL_IN); + read_unlock(&sock->sk->sk_callback_lock); + SOLD("done"); +} + +static void timod_queue(unsigned int fd, struct T_primsg *it) +{ + struct sol_socket_struct *sock; + + SOLD("queuing primsg"); + sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + it->next = sock->pfirst; + sock->pfirst = it; + if (!sock->plast) + sock->plast = it; + timod_wake_socket(fd); + SOLD("done"); +} + +static void timod_queue_end(unsigned int fd, struct T_primsg *it) +{ + struct sol_socket_struct *sock; + + SOLD("queuing primsg at end"); + sock = (struct sol_socket_struct *)current->files->fd[fd]->private_data; + it->next = NULL; + if (sock->plast) + sock->plast->next = it; + else + sock->pfirst = it; + sock->plast = it; + SOLD("done"); +} + +static void timod_error(unsigned int fd, int prim, int terr, int uerr) +{ + struct T_primsg *it; + + SOLD("making error"); + it = timod_mkctl(sizeof(struct T_error_ack)); + if (it) { + struct T_error_ack *err = (struct T_error_ack *)&it->type; + + SOLD("got it"); + err->PRIM_type = T_ERROR_ACK; + err->ERROR_prim = prim; + err->TLI_error = terr; + err->UNIX_error = uerr; /* FIXME: convert this */ + timod_queue(fd, it); + } + SOLD("done"); +} + +static void timod_ok(unsigned int fd, int prim) +{ + struct T_primsg *it; + struct T_ok_ack *ok; + + SOLD("creating ok ack"); + it = timod_mkctl(sizeof(*ok)); + if (it) { + SOLD("got it"); + ok = (struct T_ok_ack *)&it->type; + ok->PRIM_type = T_OK_ACK; + ok->CORRECT_prim = prim; + timod_queue(fd, it); + } + SOLD("done"); +} + +static int timod_optmgmt(unsigned int fd, int flag, char __user *opt_buf, int opt_len, int do_ret) +{ + int error, failed; + int ret_space, ret_len; + long args[5]; + char *ret_pos,*ret_buf; + int (*sys_socketcall)(int, unsigned long *) = + (int (*)(int, unsigned long *))SYS(socketcall); + mm_segment_t old_fs = get_fs(); + + SOLD("entry"); + SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret)); + if (!do_ret && (!opt_buf || opt_len <= 0)) + return 0; + SOLD("getting page"); + ret_pos = ret_buf = getpage(); + ret_space = BUF_SIZE; + ret_len = 0; + + error = failed = 0; + SOLD("looping"); + while(opt_len >= sizeof(struct opthdr)) { + struct opthdr *opt; + int orig_opt_len; + SOLD("loop start"); + opt = (struct opthdr *)ret_pos; + if (ret_space < sizeof(struct opthdr)) { + failed = TSYSERR; + break; + } + SOLD("getting opthdr"); + if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) || + opt->len > opt_len) { + failed = TBADOPT; + break; + } + SOLD("got opthdr"); + if (flag == T_NEGOTIATE) { + char *buf; + + SOLD("handling T_NEGOTIATE"); + buf = ret_pos + sizeof(struct opthdr); + if (ret_space < opt->len + sizeof(struct opthdr) || + copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) { + failed = TSYSERR; + break; + } + SOLD("got optdata"); + args[0] = fd; + args[1] = opt->level; + args[2] = opt->name; + args[3] = (long)buf; + args[4] = opt->len; + SOLD("calling SETSOCKOPT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_SETSOCKOPT, args); + set_fs(old_fs); + if (error) { + failed = TBADOPT; + break; + } + SOLD("SETSOCKOPT ok"); + } + orig_opt_len = opt->len; + opt->len = ret_space - sizeof(struct opthdr); + if (opt->len < 0) { + failed = TSYSERR; + break; + } + args[0] = fd; + args[1] = opt->level; + args[2] = opt->name; + args[3] = (long)(ret_pos+sizeof(struct opthdr)); + args[4] = (long)&opt->len; + SOLD("calling GETSOCKOPT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_GETSOCKOPT, args); + set_fs(old_fs); + if (error) { + failed = TBADOPT; + break; + } + SOLD("GETSOCKOPT ok"); + ret_space -= sizeof(struct opthdr) + opt->len; + ret_len += sizeof(struct opthdr) + opt->len; + ret_pos += sizeof(struct opthdr) + opt->len; + opt_len -= sizeof(struct opthdr) + orig_opt_len; + opt_buf += sizeof(struct opthdr) + orig_opt_len; + SOLD("loop end"); + } + SOLD("loop done"); + if (do_ret) { + SOLD("generating ret msg"); + if (failed) + timod_error(fd, T_OPTMGMT_REQ, failed, -error); + else { + struct T_primsg *it; + it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len); + if (it) { + struct T_optmgmt_ack *ack = + (struct T_optmgmt_ack *)&it->type; + SOLD("got primsg"); + ack->PRIM_type = T_OPTMGMT_ACK; + ack->OPT_length = ret_len; + ack->OPT_offset = sizeof(struct T_optmgmt_ack); + ack->MGMT_flags = (failed ? T_FAILURE : flag); + memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack), + ret_buf, ret_len); + timod_queue(fd, it); + } + } + } + SOLDD(("put_page %p\n", ret_buf)); + putpage(ret_buf); + SOLD("done"); + return 0; +} + +int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len, + char __user *data_buf, int data_len, int flags) +{ + int ret, error, terror; + char *buf; + struct file *filp; + struct inode *ino; + struct sol_socket_struct *sock; + mm_segment_t old_fs = get_fs(); + long args[6]; + int (*sys_socketcall)(int, unsigned long __user *) = + (int (*)(int, unsigned long __user *))SYS(socketcall); + int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int) = + (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int))SYS(sendto); + filp = current->files->fd[fd]; + ino = filp->f_dentry->d_inode; + sock = (struct sol_socket_struct *)filp->private_data; + SOLD("entry"); + if (get_user(ret, (int __user *)A(ctl_buf))) + return -EFAULT; + switch (ret) { + case T_BIND_REQ: + { + struct T_bind_req req; + + SOLDD(("bind %016lx(%016lx)\n", sock, filp)); + SOLD("T_BIND_REQ"); + if (sock->state != TS_UNBND) { + timod_error(fd, T_BIND_REQ, TOUTSTATE, 0); + return 0; + } + SOLD("state ok"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) { + timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("got ctl req"); + if (req.ADDR_offset && req.ADDR_length) { + if (req.ADDR_length > BUF_SIZE) { + timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("req size ok"); + buf = getpage(); + if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) { + timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT); + putpage(buf); + return 0; + } + SOLD("got ctl data"); + args[0] = fd; + args[1] = (long)buf; + args[2] = req.ADDR_length; + SOLD("calling BIND"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_BIND, args); + set_fs(old_fs); + putpage(buf); + SOLD("BIND returned"); + } else + error = 0; + if (!error) { + struct T_primsg *it; + if (req.CONIND_number) { + args[0] = fd; + args[1] = req.CONIND_number; + SOLD("calling LISTEN"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_LISTEN, args); + set_fs(old_fs); + SOLD("LISTEN done"); + } + it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr)); + if (it) { + struct T_bind_ack *ack; + + ack = (struct T_bind_ack *)&it->type; + ack->PRIM_type = T_BIND_ACK; + ack->ADDR_offset = sizeof(*ack); + ack->ADDR_length = sizeof(struct sockaddr); + ack->CONIND_number = req.CONIND_number; + args[0] = fd; + args[1] = (long)(ack+sizeof(*ack)); + args[2] = (long)&ack->ADDR_length; + set_fs(KERNEL_DS); + sys_socketcall(SYS_GETSOCKNAME,args); + set_fs(old_fs); + sock->state = TS_IDLE; + timod_ok(fd, T_BIND_REQ); + timod_queue_end(fd, it); + SOLD("BIND done"); + return 0; + } + } + SOLD("some error"); + switch (error) { + case -EINVAL: + terror = TOUTSTATE; + error = 0; + break; + case -EACCES: + terror = TACCES; + error = 0; + break; + case -EADDRNOTAVAIL: + case -EADDRINUSE: + terror = TNOADDR; + error = 0; + break; + default: + terror = TSYSERR; + break; + } + timod_error(fd, T_BIND_REQ, terror, -error); + SOLD("BIND done"); + return 0; + } + case T_CONN_REQ: + { + struct T_conn_req req; + unsigned short oldflags; + struct T_primsg *it; + SOLD("T_CONN_REQ"); + if (sock->state != TS_UNBND && sock->state != TS_IDLE) { + timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); + return 0; + } + SOLD("state ok"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("got ctl req"); + if (ctl_len > BUF_SIZE) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("req size ok"); + buf = getpage(); + if (copy_from_user(buf, ctl_buf, ctl_len)) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + putpage(buf); + return 0; + } +#ifdef DEBUG_SOLARIS + { + char * ptr = buf; + int len = ctl_len; + printk("returned data (%d bytes): ",len); + while( len-- ) { + if (!(len & 7)) + printk(" "); + printk("%02x",(unsigned char)*ptr++); + } + printk("\n"); + } +#endif + SOLD("got ctl data"); + args[0] = fd; + args[1] = (long)buf+req.DEST_offset; + args[2] = req.DEST_length; + oldflags = filp->f_flags; + filp->f_flags &= ~O_NONBLOCK; + SOLD("calling CONNECT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_CONNECT, args); + set_fs(old_fs); + filp->f_flags = oldflags; + SOLD("CONNECT done"); + if (!error) { + struct T_conn_con *con; + SOLD("no error"); + it = timod_mkctl(ctl_len); + if (!it) { + putpage(buf); + return -ENOMEM; + } + con = (struct T_conn_con *)&it->type; +#ifdef DEBUG_SOLARIS + { + char * ptr = buf; + int len = ctl_len; + printk("returned data (%d bytes): ",len); + while( len-- ) { + if (!(len & 7)) + printk(" "); + printk("%02x",(unsigned char)*ptr++); + } + printk("\n"); + } +#endif + memcpy(con, buf, ctl_len); + SOLD("copied ctl_buf"); + con->PRIM_type = T_CONN_CON; + sock->state = TS_DATA_XFER; + } else { + struct T_discon_ind *dis; + SOLD("some error"); + it = timod_mkctl(sizeof(*dis)); + if (!it) { + putpage(buf); + return -ENOMEM; + } + SOLD("got primsg"); + dis = (struct T_discon_ind *)&it->type; + dis->PRIM_type = T_DISCON_IND; + dis->DISCON_reason = -error; /* FIXME: convert this as in iABI_errors() */ + dis->SEQ_number = 0; + } + putpage(buf); + timod_ok(fd, T_CONN_REQ); + it->pri = 0; + timod_queue_end(fd, it); + SOLD("CONNECT done"); + return 0; + } + case T_OPTMGMT_REQ: + { + struct T_optmgmt_req req; + SOLD("OPTMGMT_REQ"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) + return -EFAULT; + SOLD("got req"); + return timod_optmgmt(fd, req.MGMT_flags, + req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL, + req.OPT_length, 1); + } + case T_UNITDATA_REQ: + { + struct T_unitdata_req req; + + int err; + SOLD("T_UNITDATA_REQ"); + if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) { + timod_error(fd, T_CONN_REQ, TOUTSTATE, 0); + return 0; + } + SOLD("state ok"); + if (copy_from_user(&req, ctl_buf, sizeof(req))) { + timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT); + return 0; + } + SOLD("got ctl req"); +#ifdef DEBUG_SOLARIS + { + char * ptr = ctl_buf+req.DEST_offset; + int len = req.DEST_length; + printk("socket address (%d bytes): ",len); + while( len-- ) { + char c; + if (get_user(c,ptr)) + printk("??"); + else + printk("%02x",(unsigned char)c); + ptr++; + } + printk("\n"); + } +#endif + err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr __user *)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length); + if (err == data_len) + return 0; + if(err >= 0) { + printk("timod: sendto failed to send all the data\n"); + return 0; + } + timod_error(fd, T_CONN_REQ, TSYSERR, -err); + return 0; + } + default: + printk(KERN_INFO "timod_putmsg: unsupported command %u.\n", ret); + break; + } + return -EINVAL; +} + +int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, s32 __user *ctl_len, + char __user *data_buf, int data_maxlen, s32 __user *data_len, int *flags_p) +{ + int error; + int oldflags; + struct file *filp; + struct inode *ino; + struct sol_socket_struct *sock; + struct T_unitdata_ind udi; + mm_segment_t old_fs = get_fs(); + long args[6]; + char __user *tmpbuf; + int tmplen; + int (*sys_socketcall)(int, unsigned long __user *) = + (int (*)(int, unsigned long __user *))SYS(socketcall); + int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *); + + SOLD("entry"); + SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p)); + filp = current->files->fd[fd]; + ino = filp->f_dentry->d_inode; + sock = (struct sol_socket_struct *)filp->private_data; + SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL)); + if ( ctl_maxlen > 0 && !sock->pfirst && SOCKET_I(ino)->type == SOCK_STREAM + && sock->state == TS_IDLE) { + SOLD("calling LISTEN"); + args[0] = fd; + args[1] = -1; + set_fs(KERNEL_DS); + sys_socketcall(SYS_LISTEN, args); + set_fs(old_fs); + SOLD("LISTEN done"); + } + if (!(filp->f_flags & O_NONBLOCK)) { + struct poll_wqueues wait_table; + poll_table *wait; + + poll_initwait(&wait_table); + wait = &wait_table.pt; + for(;;) { + SOLD("loop"); + set_current_state(TASK_INTERRUPTIBLE); + /* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */ + /* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */ + /* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */ + if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI)) + break; + SOLD("cond 1 passed"); + if ( + #if 1 + *flags_p != MSG_HIPRI && + #endif + ((filp->f_op->poll(filp, wait) & POLLIN) || + (filp->f_op->poll(filp, NULL) & POLLIN) || + signal_pending(current)) + ) { + break; + } + if( *flags_p == MSG_HIPRI ) { + SOLD("avoiding lockup"); + break ; + } + if(wait_table.error) { + SOLD("wait-table error"); + poll_freewait(&wait_table); + return wait_table.error; + } + SOLD("scheduling"); + schedule(); + } + SOLD("loop done"); + current->state = TASK_RUNNING; + poll_freewait(&wait_table); + if (signal_pending(current)) { + SOLD("signal pending"); + return -EINTR; + } + } + if (ctl_maxlen >= 0 && sock->pfirst) { + struct T_primsg *it = sock->pfirst; + int l = min_t(int, ctl_maxlen, it->length); + SCHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC); + SOLD("purting ctl data"); + if(copy_to_user(ctl_buf, + (char*)&it->type + sock->offset, l)) + return -EFAULT; + SOLD("pur it"); + if(put_user(l, ctl_len)) + return -EFAULT; + SOLD("set ctl_len"); + *flags_p = it->pri; + it->length -= l; + if (it->length) { + SOLD("more ctl"); + sock->offset += l; + return MORECTL; + } else { + SOLD("removing message"); + sock->pfirst = it->next; + if (!sock->pfirst) + sock->plast = NULL; + SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst)); + mykfree(it); + sock->offset = 0; + SOLD("ctl done"); + return 0; + } + } + *flags_p = 0; + if (ctl_maxlen >= 0) { + SOLD("ACCEPT perhaps?"); + if (SOCKET_I(ino)->type == SOCK_STREAM && sock->state == TS_IDLE) { + struct T_conn_ind ind; + char *buf = getpage(); + int len = BUF_SIZE; + + SOLD("trying ACCEPT"); + if (put_user(ctl_maxlen - sizeof(ind), ctl_len)) + return -EFAULT; + args[0] = fd; + args[1] = (long)buf; + args[2] = (long)&len; + oldflags = filp->f_flags; + filp->f_flags |= O_NONBLOCK; + SOLD("calling ACCEPT"); + set_fs(KERNEL_DS); + error = sys_socketcall(SYS_ACCEPT, args); + set_fs(old_fs); + filp->f_flags = oldflags; + if (error < 0) { + SOLD("some error"); + putpage(buf); + return error; + } + if (error) { + SOLD("connect"); + putpage(buf); + if (sizeof(ind) > ctl_maxlen) { + SOLD("generating CONN_IND"); + ind.PRIM_type = T_CONN_IND; + ind.SRC_length = len; + ind.SRC_offset = sizeof(ind); + ind.OPT_length = ind.OPT_offset = 0; + ind.SEQ_number = error; + if(copy_to_user(ctl_buf, &ind, sizeof(ind))|| + put_user(sizeof(ind)+ind.SRC_length,ctl_len)) + return -EFAULT; + SOLD("CONN_IND created"); + } + if (data_maxlen >= 0) + put_user(0, data_len); + SOLD("CONN_IND done"); + return 0; + } + if (len>ctl_maxlen) { + SOLD("data don't fit"); + putpage(buf); + return -EFAULT; /* XXX - is this ok ? */ + } + if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){ + SOLD("can't copy data"); + putpage(buf); + return -EFAULT; + } + SOLD("ACCEPT done"); + putpage(buf); + } + } + SOLD("checking data req"); + if (data_maxlen <= 0) { + if (data_maxlen == 0) + put_user(0, data_len); + if (ctl_maxlen >= 0) + put_user(0, ctl_len); + return -EAGAIN; + } + SOLD("wants data"); + if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { + SOLD("udi fits"); + tmpbuf = ctl_buf + sizeof(udi); + tmplen = ctl_maxlen - sizeof(udi); + } else { + SOLD("udi does not fit"); + tmpbuf = NULL; + tmplen = 0; + } + if (put_user(tmplen, ctl_len)) + return -EFAULT; + SOLD("set ctl_len"); + oldflags = filp->f_flags; + filp->f_flags |= O_NONBLOCK; + SOLD("calling recvfrom"); + sys_recvfrom = (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom); + error = sys_recvfrom(fd, data_buf, data_maxlen, 0, (struct sockaddr __user *)tmpbuf, ctl_len); + filp->f_flags = oldflags; + if (error < 0) + return error; + SOLD("error >= 0" ) ; + if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) { + SOLD("generating udi"); + udi.PRIM_type = T_UNITDATA_IND; + if (get_user(udi.SRC_length, ctl_len)) + return -EFAULT; + udi.SRC_offset = sizeof(udi); + udi.OPT_length = udi.OPT_offset = 0; + if (copy_to_user(ctl_buf, &udi, sizeof(udi)) || + put_user(sizeof(udi)+udi.SRC_length, ctl_len)) + return -EFAULT; + SOLD("udi done"); + } else { + if (put_user(0, ctl_len)) + return -EFAULT; + } + put_user(error, data_len); + SOLD("done"); + return 0; +} + +asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) +{ + struct file *filp; + struct inode *ino; + struct strbuf __user *ctlptr; + struct strbuf __user *datptr; + struct strbuf ctl, dat; + int __user *flgptr; + int flags; + int error = -EBADF; + + SOLD("entry"); + lock_kernel(); + if(fd >= NR_OPEN) goto out; + + filp = current->files->fd[fd]; + if(!filp) goto out; + + ino = filp->f_dentry->d_inode; + if (!ino || !S_ISSOCK(ino->i_mode)) + goto out; + + ctlptr = (struct strbuf __user *)A(arg1); + datptr = (struct strbuf __user *)A(arg2); + flgptr = (int __user *)A(arg3); + + error = -EFAULT; + + if (ctlptr) { + if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) || + put_user(-1,&ctlptr->len)) + goto out; + } else + ctl.maxlen = -1; + + if (datptr) { + if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) || + put_user(-1,&datptr->len)) + goto out; + } else + dat.maxlen = -1; + + if (get_user(flags,flgptr)) + goto out; + + switch (flags) { + case 0: + case MSG_HIPRI: + case MSG_ANY: + case MSG_BAND: + break; + default: + error = -EINVAL; + goto out; + } + + error = timod_getmsg(fd,A(ctl.buf),ctl.maxlen,&ctlptr->len, + A(dat.buf),dat.maxlen,&datptr->len,&flags); + + if (!error && put_user(flags,flgptr)) + error = -EFAULT; +out: + unlock_kernel(); + SOLD("done"); + return error; +} + +asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3) +{ + struct file *filp; + struct inode *ino; + struct strbuf __user *ctlptr; + struct strbuf __user *datptr; + struct strbuf ctl, dat; + int flags = (int) arg3; + int error = -EBADF; + + SOLD("entry"); + lock_kernel(); + if(fd >= NR_OPEN) goto out; + + filp = current->files->fd[fd]; + if(!filp) goto out; + + ino = filp->f_dentry->d_inode; + if (!ino) goto out; + + if (!S_ISSOCK(ino->i_mode) && + (imajor(ino) != 30 || iminor(ino) != 1)) + goto out; + + ctlptr = A(arg1); + datptr = A(arg2); + + error = -EFAULT; + + if (ctlptr) { + if (copy_from_user(&ctl,ctlptr,sizeof(ctl))) + goto out; + if (ctl.len < 0 && flags) { + error = -EINVAL; + goto out; + } + } else { + ctl.len = 0; + ctl.buf = 0; + } + + if (datptr) { + if (copy_from_user(&dat,datptr,sizeof(dat))) + goto out; + } else { + dat.len = 0; + dat.buf = 0; + } + + error = timod_putmsg(fd,A(ctl.buf),ctl.len, + A(dat.buf),dat.len,flags); +out: + unlock_kernel(); + SOLD("done"); + return error; +} |