📄 sys_ia32.c
字号:
/* * sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Derived from sys_sparc32.c. * * Copyright (C) 2000 VA Linux Co * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 2000-2001 Hewlett-Packard Co * David Mosberger-Tang <davidm@hpl.hp.com> * * These routines maintain argument size conversion between 32bit and 64bit * environment. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sysctl.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/file.h>#include <linux/signal.h>#include <linux/utime.h>#include <linux/resource.h>#include <linux/times.h>#include <linux/utsname.h>#include <linux/timex.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/sem.h>#include <linux/msg.h>#include <linux/mm.h>#include <linux/shm.h>#include <linux/slab.h>#include <linux/uio.h>#include <linux/nfs_fs.h>#include <linux/smb_fs.h>#include <linux/smb_mount.h>#include <linux/ncp_fs.h>#include <linux/quota.h>#include <linux/module.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/cache.h>#include <linux/nfsd/xdr.h>#include <linux/nfsd/syscall.h>#include <linux/poll.h>#include <linux/personality.h>#include <linux/stat.h>#include <linux/ipc.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/semaphore.h>#include <net/scm.h>#include <net/sock.h>#include <asm/ia32.h>#define DEBUG 0#if DEBUG# define DBG(fmt...) printk(KERN_DEBUG fmt)#else# define DBG(fmt...)#endif#define A(__x) ((unsigned long)(__x))#define AA(__x) ((unsigned long)(__x))#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1)))#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))#define OFFSET4K(a) ((a) & 0xfff)#define PAGE_START(addr) ((addr) & PAGE_MASK)#define PAGE_OFF(addr) ((addr) & ~PAGE_MASK)extern asmlinkage long sys_execve (char *, char **, char **, struct pt_regs *);extern asmlinkage long sys_mprotect (unsigned long, size_t, unsigned long);extern asmlinkage long sys_munmap (unsigned long, size_t);extern unsigned long arch_get_unmapped_area (struct file *, unsigned long, unsigned long, unsigned long, unsigned long);/* forward declaration: */asmlinkage long sys32_mprotect (unsigned int, unsigned int, int);/* * Anything that modifies or inspects ia32 user virtual memory must hold this semaphore * while doing so. *//* XXX make per-mm: */static DECLARE_MUTEX(ia32_mmap_sem);static intnargs (unsigned int arg, char **ap){ unsigned int addr; int n, err; if (!arg) return 0; n = 0; do { err = get_user(addr, (unsigned int *)A(arg)); if (err) return err; if (ap) *ap++ = (char *) A(addr); arg += sizeof(unsigned int); n++; } while (addr); return n - 1;}asmlinkage longsys32_execve (char *filename, unsigned int argv, unsigned int envp, int dummy3, int dummy4, int dummy5, int dummy6, int dummy7, int stack){ struct pt_regs *regs = (struct pt_regs *)&stack; unsigned long old_map_base, old_task_size, tssd; char **av, **ae; int na, ne, len; long r; na = nargs(argv, NULL); if (na < 0) return na; ne = nargs(envp, NULL); if (ne < 0) return ne; len = (na + ne + 2) * sizeof(*av); av = kmalloc(len, GFP_KERNEL); if (!av) return -ENOMEM; ae = av + na + 1; av[na] = NULL; ae[ne] = NULL; r = nargs(argv, av); if (r < 0) goto out; r = nargs(envp, ae); if (r < 0) goto out; old_map_base = current->thread.map_base; old_task_size = current->thread.task_size; tssd = ia64_get_kr(IA64_KR_TSSD); /* we may be exec'ing a 64-bit process: reset map base, task-size, and io-base: */ current->thread.map_base = DEFAULT_MAP_BASE; current->thread.task_size = DEFAULT_TASK_SIZE; ia64_set_kr(IA64_KR_IO_BASE, current->thread.old_iob); ia64_set_kr(IA64_KR_TSSD, current->thread.old_k1); set_fs(KERNEL_DS); r = sys_execve(filename, av, ae, regs); if (r < 0) { /* oops, execve failed, switch back to old values... */ ia64_set_kr(IA64_KR_IO_BASE, IA32_IOBASE); ia64_set_kr(IA64_KR_TSSD, tssd); current->thread.map_base = old_map_base; current->thread.task_size = old_task_size; set_fs(USER_DS); /* establish new task-size as the address-limit */ out: kfree(av); } return r;}static inline intputstat (struct stat32 *ubuf, struct stat *kbuf){ int err; if (clear_user(ubuf, sizeof(*ubuf))) return 1; err = __put_user(kbuf->st_dev, &ubuf->st_dev); err |= __put_user(kbuf->st_ino, &ubuf->st_ino); err |= __put_user(kbuf->st_mode, &ubuf->st_mode); err |= __put_user(kbuf->st_nlink, &ubuf->st_nlink); err |= __put_user(kbuf->st_uid, &ubuf->st_uid); err |= __put_user(kbuf->st_gid, &ubuf->st_gid); err |= __put_user(kbuf->st_rdev, &ubuf->st_rdev); err |= __put_user(kbuf->st_size, &ubuf->st_size); err |= __put_user(kbuf->st_atime, &ubuf->st_atime); err |= __put_user(kbuf->st_mtime, &ubuf->st_mtime); err |= __put_user(kbuf->st_ctime, &ubuf->st_ctime); err |= __put_user(kbuf->st_blksize, &ubuf->st_blksize); err |= __put_user(kbuf->st_blocks, &ubuf->st_blocks); return err;}extern asmlinkage long sys_newstat (char * filename, struct stat * statbuf);asmlinkage longsys32_newstat (char *filename, struct stat32 *statbuf){ int ret; struct stat s; mm_segment_t old_fs = get_fs(); set_fs(KERNEL_DS); ret = sys_newstat(filename, &s); set_fs(old_fs); if (putstat(statbuf, &s)) return -EFAULT; return ret;}extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf);asmlinkage longsys32_newlstat (char *filename, struct stat32 *statbuf){ mm_segment_t old_fs = get_fs(); struct stat s; int ret; set_fs(KERNEL_DS); ret = sys_newlstat(filename, &s); set_fs(old_fs); if (putstat(statbuf, &s)) return -EFAULT; return ret;}extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf);asmlinkage longsys32_newfstat (unsigned int fd, struct stat32 *statbuf){ mm_segment_t old_fs = get_fs(); struct stat s; int ret; set_fs(KERNEL_DS); ret = sys_newfstat(fd, &s); set_fs(old_fs); if (putstat(statbuf, &s)) return -EFAULT; return ret;}#if PAGE_SHIFT > IA32_PAGE_SHIFTstatic intget_page_prot (unsigned long addr){ struct vm_area_struct *vma = find_vma(current->mm, addr); int prot = 0; if (!vma || vma->vm_start > addr) return 0; if (vma->vm_flags & VM_READ) prot |= PROT_READ; if (vma->vm_flags & VM_WRITE) prot |= PROT_WRITE; if (vma->vm_flags & VM_EXEC) prot |= PROT_EXEC; return prot;}/* * Map a subpage by creating an anonymous page that contains the union of the old page and * the subpage. */static unsigned longmmap_subpage (struct file *file, unsigned long start, unsigned long end, int prot, int flags, loff_t off){ void *page = (void *) get_zeroed_page(GFP_KERNEL); struct inode *inode; unsigned long ret; int old_prot = get_page_prot(start); DBG("mmap_subpage(file=%p,start=0x%lx,end=0x%lx,prot=%x,flags=%x,off=0x%llx)\n", file, start, end, prot, flags, off); if (!page) return -ENOMEM; if (old_prot) copy_from_user(page, (void *) PAGE_START(start), PAGE_SIZE); down_write(¤t->mm->mmap_sem); { ret = do_mmap(0, PAGE_START(start), PAGE_SIZE, prot | PROT_WRITE, flags | MAP_FIXED | MAP_ANONYMOUS, 0); } up_write(¤t->mm->mmap_sem); if (IS_ERR((void *) ret)) goto out; if (old_prot) { /* copy back the old page contents. */ if (PAGE_OFF(start)) copy_to_user((void *) PAGE_START(start), page, PAGE_OFF(start)); if (PAGE_OFF(end)) copy_to_user((void *) end, page + PAGE_OFF(end), PAGE_SIZE - PAGE_OFF(end)); } if (!(flags & MAP_ANONYMOUS)) { /* read the file contents */ inode = file->f_dentry->d_inode; if (!inode->i_fop || !file->f_op->read || ((*file->f_op->read)(file, (char *) start, end - start, &off) < 0)) { ret = -EINVAL; goto out; } } if (!(prot & PROT_WRITE)) ret = sys_mprotect(PAGE_START(start), PAGE_SIZE, prot | old_prot); out: free_page((unsigned long) page); return ret;}static unsigned longemulate_mmap (struct file *file, unsigned long start, unsigned long len, int prot, int flags, loff_t off){ unsigned long tmp, end, pend, pstart, ret, is_congruent, fudge = 0; struct inode *inode; loff_t poff; end = start + len; pstart = PAGE_START(start); pend = PAGE_ALIGN(end); if (flags & MAP_FIXED) { if (start > pstart) { if (flags & MAP_SHARED) printk(KERN_INFO "%s(%d): emulate_mmap() can't share head (addr=0x%lx)\n", current->comm, current->pid, start); ret = mmap_subpage(file, start, min(PAGE_ALIGN(start), end), prot, flags, off); if (IS_ERR((void *) ret)) return ret; pstart += PAGE_SIZE; if (pstart >= pend) return start; /* done */ } if (end < pend) { if (flags & MAP_SHARED) printk(KERN_INFO "%s(%d): emulate_mmap() can't share tail (end=0x%lx)\n", current->comm, current->pid, end); ret = mmap_subpage(file, max(start, PAGE_START(end)), end, prot, flags, (off + len) - PAGE_OFF(end)); if (IS_ERR((void *) ret)) return ret; pend -= PAGE_SIZE; if (pstart >= pend) return start; /* done */ } } else { /* * If a start address was specified, use it if the entire rounded out area * is available. */ if (start && !pstart) fudge = 1; /* handle case of mapping to range (0,PAGE_SIZE) */ tmp = arch_get_unmapped_area(file, pstart - fudge, pend - pstart, 0, flags); if (tmp != pstart) { pstart = tmp; start = pstart + PAGE_OFF(off); /* make start congruent with off */ end = start + len; pend = PAGE_ALIGN(end); } } poff = off + (pstart - start); /* note: (pstart - start) may be negative */ is_congruent = (flags & MAP_ANONYMOUS) || (PAGE_OFF(poff) == 0); if ((flags & MAP_SHARED) && !is_congruent) printk(KERN_INFO "%s(%d): emulate_mmap() can't share contents of incongruent mmap " "(addr=0x%lx,off=0x%llx)\n", current->comm, current->pid, start, off); DBG("mmap_body: mapping [0x%lx-0x%lx) %s with poff 0x%llx\n", pstart, pend, is_congruent ? "congruent" : "not congruent", poff); down_write(¤t->mm->mmap_sem); { if (!(flags & MAP_ANONYMOUS) && is_congruent) ret = do_mmap(file, pstart, pend - pstart, prot, flags | MAP_FIXED, poff); else ret = do_mmap(0, pstart, pend - pstart, prot | ((flags & MAP_ANONYMOUS) ? 0 : PROT_WRITE), flags | MAP_FIXED | MAP_ANONYMOUS, 0); } up_write(¤t->mm->mmap_sem); if (IS_ERR((void *) ret)) return ret; if (!is_congruent) { /* read the file contents */ inode = file->f_dentry->d_inode; if (!inode->i_fop || !file->f_op->read || ((*file->f_op->read)(file, (char *) pstart, pend - pstart, &poff) < 0)) { sys_munmap(pstart, pend - pstart); return -EINVAL; } if (!(prot & PROT_WRITE) && sys_mprotect(pstart, pend - pstart, prot) < 0) return EINVAL; } return start;}#endif /* PAGE_SHIFT > IA32_PAGE_SHIFT */static inline unsigned intget_prot32 (unsigned int prot){ if (prot & PROT_WRITE) /* on x86, PROT_WRITE implies PROT_READ which implies PROT_EEC */ prot |= PROT_READ | PROT_WRITE | PROT_EXEC; else if (prot & (PROT_READ | PROT_EXEC)) /* on x86, there is no distinction between PROT_READ and PROT_EXEC */ prot |= (PROT_READ | PROT_EXEC); return prot;}unsigned longia32_do_mmap (struct file *file, unsigned long addr, unsigned long len, int prot, int flags, loff_t offset){ DBG("ia32_do_mmap(file=%p,addr=0x%lx,len=0x%lx,prot=%x,flags=%x,offset=0x%llx)\n", file, addr, len, prot, flags, offset); if (file && (!file->f_op || !file->f_op->mmap)) return -ENODEV; len = IA32_PAGE_ALIGN(len); if (len == 0) return addr; if (len > IA32_PAGE_OFFSET || addr > IA32_PAGE_OFFSET - len) return -EINVAL; if (OFFSET4K(offset)) return -EINVAL; prot = get_prot32(prot);#if PAGE_SHIFT > IA32_PAGE_SHIFT down(&ia32_mmap_sem); { addr = emulate_mmap(file, addr, len, prot, flags, offset); } up(&ia32_mmap_sem);#else down_write(¤t->mm->mmap_sem); { addr = do_mmap(file, addr, len, prot, flags, offset); } up_write(¤t->mm->mmap_sem);#endif DBG("ia32_do_mmap: returning 0x%lx\n", addr); return addr;}/* * Linux/i386 didn't use to be able to handle more than 4 system call parameters, so these * system calls used a memory block for parameter passing.. */struct mmap_arg_struct { unsigned int addr; unsigned int len; unsigned int prot; unsigned int flags; unsigned int fd; unsigned int offset;};asmlinkage longsys32_mmap (struct mmap_arg_struct *arg){ struct mmap_arg_struct a; struct file *file = NULL; unsigned long addr; int flags; if (copy_from_user(&a, arg, sizeof(a))) return -EFAULT; if (OFFSET4K(a.offset)) return -EINVAL; flags = a.flags; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -