📄 sys_sunos.c
字号:
/* $Id: sys_sunos.c,v 1.135 2001/08/13 14:40:10 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) * * Based upon preliminary work which is: * * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu) * */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/mman.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/resource.h>#include <linux/ipc.h>#include <linux/shm.h>#include <linux/msg.h>#include <linux/sem.h>#include <linux/signal.h>#include <linux/uio.h>#include <linux/utsname.h>#include <linux/major.h>#include <linux/stat.h>#include <linux/slab.h>#include <linux/pagemap.h>#include <linux/errno.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#ifndef KERNEL_DS#include <linux/segment.h>#endif#include <asm/page.h>#include <asm/pgtable.h>#include <asm/pconf.h>#include <asm/idprom.h> /* for gethostid() */#include <asm/unistd.h>#include <asm/system.h>/* For the nfs mount emulation */#include <linux/socket.h>#include <linux/in.h>#include <linux/nfs.h>#include <linux/nfs2.h>#include <linux/nfs_mount.h>/* for sunos_select */#include <linux/time.h>#include <linux/personality.h>/* NR_OPEN is now larger and dynamic in recent kernels. */#define SUNOS_NR_OPEN 256/* We use the SunOS mmap() semantics. */asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off){ struct file * file = NULL; unsigned long retval, ret_type; if(flags & MAP_NORESERVE) { static int cnt; if (cnt++ < 10) printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n", current->comm); flags &= ~MAP_NORESERVE; } retval = -EBADF; if(!(flags & MAP_ANONYMOUS)) { if (fd >= SUNOS_NR_OPEN) goto out; file = fget(fd); if (!file) goto out; } retval = -EINVAL; /* If this is ld.so or a shared library doing an mmap * of /dev/zero, transform it into an anonymous mapping. * SunOS is so stupid some times... hmph! */ if (file) { if(MAJOR(file->f_dentry->d_inode->i_rdev) == MEM_MAJOR && MINOR(file->f_dentry->d_inode->i_rdev) == 5) { flags |= MAP_ANONYMOUS; fput(file); file = 0; } } ret_type = flags & _MAP_NEW; flags &= ~_MAP_NEW; if(!(flags & MAP_FIXED)) addr = 0; else { if (ARCH_SUN4C_SUN4 && (len > 0x20000000 || ((flags & MAP_FIXED) && addr < 0xe0000000 && addr + len > 0x20000000))) goto out_putf; /* See asm-sparc/uaccess.h */ if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE) goto out_putf; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); down_write(¤t->mm->mmap_sem); retval = do_mmap(file, addr, len, prot, flags, off); up_write(¤t->mm->mmap_sem); if(!ret_type) retval = ((retval < PAGE_OFFSET) ? 0 : retval);out_putf: if (file) fput(file);out: return retval;}/* lmbench calls this, just say "yeah, ok" */asmlinkage int sunos_mctl(unsigned long addr, unsigned long len, int function, char *arg){ return 0;}/* SunOS is completely broken... it returns 0 on success, otherwise * ENOMEM. For sys_sbrk() it wants the old brk value as a return * on success and ENOMEM as before on failure. */asmlinkage int sunos_brk(unsigned long brk){ int freepages, retval = -ENOMEM; unsigned long rlim; unsigned long newbrk, oldbrk; down_write(¤t->mm->mmap_sem); if(ARCH_SUN4C_SUN4) { if(brk >= 0x20000000 && brk < 0xe0000000) { goto out; } } if (brk < current->mm->end_code) goto out; newbrk = PAGE_ALIGN(brk); oldbrk = PAGE_ALIGN(current->mm->brk); retval = 0; if (oldbrk == newbrk) { current->mm->brk = brk; goto out; } /* * Always allow shrinking brk */ if (brk <= current->mm->brk) { current->mm->brk = brk; do_munmap(current->mm, newbrk, oldbrk-newbrk); goto out; } /* * Check against rlimit and stack.. */ retval = -ENOMEM; rlim = current->rlim[RLIMIT_DATA].rlim_cur; if (rlim >= RLIM_INFINITY) rlim = ~0; if (brk - current->mm->end_code > rlim) goto out; /* * Check against existing mmap mappings. */ if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE)) goto out; /* * stupid algorithm to decide if we have enough memory: while * simple, it hopefully works in most obvious cases.. Easy to * fool it, but this should catch most mistakes. */ freepages = atomic_read(&buffermem_pages) >> PAGE_SHIFT; freepages += atomic_read(&page_cache_size); freepages >>= 1; freepages += nr_free_pages(); freepages += nr_swap_pages; freepages -= num_physpages >> 4; freepages -= (newbrk-oldbrk) >> PAGE_SHIFT; if (freepages < 0) goto out; /* * Ok, we have probably got enough memory - let it rip. */ current->mm->brk = brk; do_brk(oldbrk, newbrk-oldbrk); retval = 0;out: up_write(¤t->mm->mmap_sem); return retval;}asmlinkage unsigned long sunos_sbrk(int increment){ int error; unsigned long oldbrk; /* This should do it hopefully... */ lock_kernel(); oldbrk = current->mm->brk; error = sunos_brk(((int) current->mm->brk) + increment); if(!error) error = oldbrk; unlock_kernel(); return error;}/* XXX Completely undocumented, and completely magic... * XXX I believe it is to increase the size of the stack by * XXX argument 'increment' and return the new end of stack * XXX area. Wheee... */asmlinkage unsigned long sunos_sstk(int increment){ lock_kernel(); printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n", current->comm, increment); unlock_kernel(); return -1;}/* Give hints to the kernel as to what paging strategy to use... * Completely bogus, don't remind me. */#define VA_NORMAL 0 /* Normal vm usage expected */#define VA_ABNORMAL 1 /* Abnormal/random vm usage probable */#define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */#define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */static char *vstrings[] = { "VA_NORMAL", "VA_ABNORMAL", "VA_SEQUENTIAL", "VA_INVALIDATE",};asmlinkage void sunos_vadvise(unsigned long strategy){ /* I wanna see who uses this... */ lock_kernel(); printk("%s: Advises us to use %s paging strategy\n", current->comm, strategy <= 3 ? vstrings[strategy] : "BOGUS"); unlock_kernel();}/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE * resource limit and is for backwards compatibility with older sunos * revs. */asmlinkage long sunos_getdtablesize(void){ return SUNOS_NR_OPEN;}#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))asmlinkage unsigned long sunos_sigblock(unsigned long blk_mask){ unsigned long old; spin_lock_irq(¤t->sigmask_lock); old = current->blocked.sig[0]; current->blocked.sig[0] |= (blk_mask & _BLOCKABLE); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); return old;}asmlinkage unsigned long sunos_sigsetmask(unsigned long newmask){ unsigned long retval; spin_lock_irq(¤t->sigmask_lock); retval = current->blocked.sig[0]; current->blocked.sig[0] = (newmask & _BLOCKABLE); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); return retval;}/* SunOS getdents is very similar to the newer Linux (iBCS2 compliant) *//* getdents system call, the format of the structure just has a different *//* layout (d_off+d_ino instead of d_ino+d_off) */struct sunos_dirent { long d_off; unsigned long d_ino; unsigned short d_reclen; unsigned short d_namlen; char d_name[1];};struct sunos_dirent_callback { struct sunos_dirent *curr; struct sunos_dirent *previous; int count; int error;};#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))static int sunos_filldir(void * __buf, const char * name, int namlen, loff_t offset, ino_t ino, unsigned int d_type){ struct sunos_dirent * dirent; struct sunos_dirent_callback * buf = (struct sunos_dirent_callback *) __buf; int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; dirent = buf->previous; if (dirent) put_user(offset, &dirent->d_off); dirent = buf->curr; buf->previous = dirent; put_user(ino, &dirent->d_ino); put_user(namlen, &dirent->d_namlen); put_user(reclen, &dirent->d_reclen); copy_to_user(dirent->d_name, name, namlen); put_user(0, dirent->d_name + namlen); ((char *) dirent) += reclen; buf->curr = dirent; buf->count -= reclen; return 0;}asmlinkage int sunos_getdents(unsigned int fd, void * dirent, int cnt){ struct file * file; struct sunos_dirent * lastdirent; struct sunos_dirent_callback buf; int error = -EBADF; if (fd >= SUNOS_NR_OPEN) goto out; file = fget(fd); if (!file) goto out; error = -EINVAL; if (cnt < (sizeof(struct sunos_dirent) + 255)) goto out_putf; buf.curr = (struct sunos_dirent *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; error = vfs_readdir(file, sunos_filldir, &buf); if (error < 0) goto out_putf; lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, &lastdirent->d_off); error = cnt - buf.count; }out_putf: fput(file);out: return error;}/* Old sunos getdirentries, severely broken compatibility stuff here. */struct sunos_direntry { unsigned long d_ino; unsigned short d_reclen; unsigned short d_namlen; char d_name[1];};struct sunos_direntry_callback { struct sunos_direntry *curr; struct sunos_direntry *previous; int count; int error;};static int sunos_filldirentry(void * __buf, const char * name, int namlen, loff_t offset, ino_t ino, unsigned int d_type){ struct sunos_direntry * dirent; struct sunos_direntry_callback * buf = (struct sunos_direntry_callback *) __buf; int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) return -EINVAL; dirent = buf->previous; dirent = buf->curr; buf->previous = dirent; put_user(ino, &dirent->d_ino); put_user(namlen, &dirent->d_namlen); put_user(reclen, &dirent->d_reclen); copy_to_user(dirent->d_name, name, namlen); put_user(0, dirent->d_name + namlen); ((char *) dirent) += reclen; buf->curr = dirent; buf->count -= reclen; return 0;}asmlinkage int sunos_getdirentries(unsigned int fd, void * dirent, int cnt, unsigned int *basep){ struct file * file; struct sunos_direntry * lastdirent; struct sunos_direntry_callback buf; int error = -EBADF; if (fd >= SUNOS_NR_OPEN) goto out; file = fget(fd); if (!file) goto out; error = -EINVAL; if(cnt < (sizeof(struct sunos_direntry) + 255)) goto out_putf; buf.curr = (struct sunos_direntry *) dirent; buf.previous = NULL; buf.count = cnt; buf.error = 0; error = vfs_readdir(file, sunos_filldirentry, &buf); if (error < 0) goto out_putf; lastdirent = buf.previous; error = buf.error; if (lastdirent) { put_user(file->f_pos, basep); error = cnt - buf.count; }out_putf: fput(file);out: return error;}struct sunos_utsname { char sname[9]; char nname[9]; char nnext[56]; char rel[9]; char ver[9]; char mach[9];};asmlinkage int sunos_uname(struct sunos_utsname *name){ int ret; down_read(&uts_sem); ret = copy_to_user(&name->sname[0], &system_utsname.sysname[0], sizeof(name->sname) - 1); if (!ret) { ret |= __copy_to_user(&name->nname[0], &system_utsname.nodename[0], sizeof(name->nname) - 1); ret |= __put_user('\0', &name->nname[8]); ret |= __copy_to_user(&name->rel[0], &system_utsname.release[0], sizeof(name->rel) - 1); ret |= __copy_to_user(&name->ver[0], &system_utsname.version[0], sizeof(name->ver) - 1); ret |= __copy_to_user(&name->mach[0], &system_utsname.machine[0], sizeof(name->mach) - 1); } up_read(&uts_sem); return ret;}asmlinkage int sunos_nosys(void){ struct pt_regs *regs; siginfo_t info; static int cnt; lock_kernel(); regs = current->thread.kregs; info.si_signo = SIGSYS; info.si_errno = 0; info.si_code = __SI_FAULT|0x100; info.si_addr = (void *)regs->pc; info.si_trapno = regs->u_regs[UREG_G1]; send_sig_info(SIGSYS, &info, current); if (cnt++ < 4) { printk("Process makes ni_syscall number %d, register dump:\n", (int) regs->u_regs[UREG_G1]); show_regs(regs); } unlock_kernel(); return -ENOSYS;}/* This is not a real and complete implementation yet, just to keep * the easy SunOS binaries happy. */asmlinkage int sunos_fpathconf(int fd, int name){ int ret; switch(name) { case _PCONF_LINK: ret = LINK_MAX; break; case _PCONF_CANON: ret = MAX_CANON; break; case _PCONF_INPUT: ret = MAX_INPUT; break; case _PCONF_NAME: ret = NAME_MAX; break; case _PCONF_PATH: ret = PATH_MAX; break; case _PCONF_PIPE: ret = PIPE_BUF; break; case _PCONF_CHRESTRICT: /* XXX Investigate XXX */ ret = 1; break; case _PCONF_NOTRUNC: /* XXX Investigate XXX */ case _PCONF_VDISABLE: ret = 0; break; default: ret = -EINVAL; break; } return ret;}asmlinkage int sunos_pathconf(char *path, int name){ int ret; ret = sunos_fpathconf(0, name); /* XXX cheese XXX */ return ret;}/* SunOS mount system call emulation */extern asmlinkage intsys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp);asmlinkage int sunos_select(int width, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp){ int ret; /* SunOS binaries expect that select won't change the tvp contents */ ret = sys_select (width, inp, outp, exp, tvp); if (ret == -EINTR && tvp) { time_t sec, usec; __get_user(sec, &tvp->tv_sec); __get_user(usec, &tvp->tv_usec); if (sec == 0 && usec == 0) ret = 0; } return ret;}asmlinkage void sunos_nop(void){ return;}/* SunOS mount/umount. */#define SMNT_RDONLY 1#define SMNT_NOSUID 2#define SMNT_NEWTYPE 4#define SMNT_GRPID 8#define SMNT_REMOUNT 16#define SMNT_NOSUB 32#define SMNT_MULTI 64#define SMNT_SYS5 128struct sunos_fh_t { char fh_data [NFS_FHSIZE];};struct sunos_nfs_mount_args { struct sockaddr_in *addr; /* file server address */ struct nfs_fh *fh; /* File handle to be mounted */ int flags; /* flags */ int wsize; /* write size in bytes */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -