📄 sys_ia32.c
字号:
return 1; } return 0; } vma = find_vma(current->mm, pstart); if (!vma || vma->vm_start > pstart) { return -ENOMEM; } /* new a partial_page */ pp = kmem_cache_alloc(partial_page_cachep, GFP_KERNEL); if (!pp) return -ENOMEM; pp->base = pstart; pp->bitmap = 0; for (i = 0; i < start_bit; i++) set_bit(i, &(pp->bitmap)); for (i = end_bit; i < PAGE_SIZE / IA32_PAGE_SIZE; i++) set_bit(i, &(pp->bitmap)); pp->next = NULL; __ia32_insert_pp(current->thread.ppl, pp, prev, rb_link, rb_parent); return 0;}/* * Delete pp between PAGE_ALIGN(start) and PAGE_START(end) by calling * __ia32_delete_pp_range(). Unset possible partial pages by calling * __ia32_unset_pp(). * The returned value see __ia32_unset_pp(). */static intia32_unset_pp(unsigned int *startp, unsigned int *endp){ unsigned int start = *startp, end = *endp; int ret = 0; down_write(¤t->mm->mmap_sem); __ia32_delete_pp_range(PAGE_ALIGN(start), PAGE_START(end)); if (end < PAGE_ALIGN(start)) { ret = __ia32_unset_pp(start, end); if (ret == 1) { *startp = PAGE_START(start); *endp = PAGE_ALIGN(end); } if (ret == 0) { /* to shortcut sys_munmap() in sys32_munmap() */ *startp = PAGE_START(start); *endp = PAGE_START(end); } } else { if (offset_in_page(start)) { ret = __ia32_unset_pp(start, PAGE_ALIGN(start)); if (ret == 1) *startp = PAGE_START(start); if (ret == 0) *startp = PAGE_ALIGN(start); if (ret < 0) goto out; } if (offset_in_page(end)) { ret = __ia32_unset_pp(PAGE_START(end), end); if (ret == 1) *endp = PAGE_ALIGN(end); if (ret == 0) *endp = PAGE_START(end); } } out: up_write(¤t->mm->mmap_sem); return ret;}/* * Compare the range between @start and @end with bitmap in partial page. * @start and @end should be IA32 page aligned and in the same IA64 page. */static int__ia32_compare_pp(unsigned int start, unsigned int end){ struct partial_page *pp, *prev; struct rb_node ** rb_link, *rb_parent; unsigned int pstart, start_bit, end_bit, size; unsigned int first_bit, next_zero_bit; /* the first range in bitmap */ pstart = PAGE_START(start); pp = __ia32_find_pp(current->thread.ppl, pstart, &prev, &rb_link, &rb_parent); if (!pp) return 1; start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; size = sizeof(pp->bitmap) * 8; first_bit = find_first_bit(&pp->bitmap, size); next_zero_bit = find_next_zero_bit(&pp->bitmap, size, first_bit); if ((start_bit < first_bit) || (end_bit > next_zero_bit)) { /* exceeds the first range in bitmap */ return -ENOMEM; } else if ((start_bit == first_bit) && (end_bit == next_zero_bit)) { first_bit = find_next_bit(&pp->bitmap, size, next_zero_bit); if ((next_zero_bit < first_bit) && (first_bit < size)) return 1; /* has next range */ else return 0; /* no next range */ } else return 1;}/* * @start and @end should be IA32 page aligned, but don't need to be in the * same IA64 page. Split @start and @end to make sure they're in the same IA64 * page, then call __ia32_compare_pp(). * * Take this as example: the range is the 1st and 2nd 4K page. * Return 0 if they fit bitmap exactly, i.e. bitmap = 00000011; * Return 1 if the range doesn't cover whole bitmap, e.g. bitmap = 00001111; * Return -ENOMEM if the range exceeds the bitmap, e.g. bitmap = 00000001 or * bitmap = 00000101. */static intia32_compare_pp(unsigned int *startp, unsigned int *endp){ unsigned int start = *startp, end = *endp; int retval = 0; down_write(¤t->mm->mmap_sem); if (end < PAGE_ALIGN(start)) { retval = __ia32_compare_pp(start, end); if (retval == 0) { *startp = PAGE_START(start); *endp = PAGE_ALIGN(end); } } else { if (offset_in_page(start)) { retval = __ia32_compare_pp(start, PAGE_ALIGN(start)); if (retval == 0) *startp = PAGE_START(start); if (retval < 0) goto out; } if (offset_in_page(end)) { retval = __ia32_compare_pp(PAGE_START(end), end); if (retval == 0) *endp = PAGE_ALIGN(end); } } out: up_write(¤t->mm->mmap_sem); return retval;}static void__ia32_drop_pp_list(struct partial_page_list *ppl){ struct partial_page *pp = ppl->pp_head; while (pp) { struct partial_page *next = pp->next; kmem_cache_free(partial_page_cachep, pp); pp = next; } kfree(ppl);}voidia32_drop_partial_page_list(struct task_struct *task){ struct partial_page_list* ppl = task->thread.ppl; if (ppl && atomic_dec_and_test(&ppl->pp_count)) __ia32_drop_pp_list(ppl);}/* * Copy current->thread.ppl to ppl (already initialized). */static int__ia32_copy_pp_list(struct partial_page_list *ppl){ struct partial_page *pp, *tmp, *prev; struct rb_node **rb_link, *rb_parent; ppl->pp_head = NULL; ppl->pp_hint = NULL; ppl->ppl_rb = RB_ROOT; rb_link = &ppl->ppl_rb.rb_node; rb_parent = NULL; prev = NULL; for (pp = current->thread.ppl->pp_head; pp; pp = pp->next) { tmp = kmem_cache_alloc(partial_page_cachep, GFP_KERNEL); if (!tmp) return -ENOMEM; *tmp = *pp; __ia32_insert_pp(ppl, tmp, prev, rb_link, rb_parent); prev = tmp; rb_link = &tmp->pp_rb.rb_right; rb_parent = &tmp->pp_rb; } return 0;}intia32_copy_partial_page_list(struct task_struct *p, unsigned long clone_flags){ int retval = 0; if (clone_flags & CLONE_VM) { atomic_inc(¤t->thread.ppl->pp_count); p->thread.ppl = current->thread.ppl; } else { p->thread.ppl = ia32_init_pp_list(); if (!p->thread.ppl) return -ENOMEM; down_write(¤t->mm->mmap_sem); { retval = __ia32_copy_pp_list(p->thread.ppl); } up_write(¤t->mm->mmap_sem); } return retval;}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) { ia32_set_pp((unsigned int)start, (unsigned int)end, flags); 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) goto out; /* 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) - offset_in_page(end)); if (IS_ERR((void *) ret)) return ret; pend -= PAGE_SIZE; if (pstart >= pend) goto out; /* 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 + offset_in_page(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) || (offset_in_page(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(NULL, 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 __user *) 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; } if (!(flags & MAP_FIXED)) ia32_set_pp((unsigned int)start, (unsigned int)end, flags);out: 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) { if (flags & MAP_FIXED) return -ENOMEM; else 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 __user *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); if (!(flags & MAP_ANONYMOUS)) { file = fget(a.fd); if (!file) return -EBADF; } addr = ia32_do_mmap(file, a.addr, a.len, a.prot, flags, a.offset); if (file) fput(file); return addr;}asmlinkage longsys32_mmap2 (unsigned int addr, unsigned int len, unsigned int prot, unsigned int flags, unsigned int fd, unsigned int pgoff){ struct file *file = NULL; unsigned long retval; flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); if (!(flags & MAP_ANONYMOUS)) { file = fget(fd); if (!file) return -EBADF; } retval = ia32_do_mmap(file, addr, len, prot, flags, (unsigned long) pgoff << IA32_PAGE_SHIFT); if (file) fput(file); return retval;}asmlinkage longsys32_munmap (unsigned int start, unsigned int len){ unsigned int end = start + len; long ret;#if PAGE_SHIFT <= IA32_PAGE_SHIFT ret = sys_munmap(start, end - start);#else if (OFFSET4K(start)) return -EINVAL; end = IA32_PAGE_ALIGN(end); if (start >= end) return -EINVAL; ret = ia32_unset_pp(&start, &end); if (ret < 0) return ret; if (start >= end) return 0; down(&ia32_mmap_sem); { ret = sys_munmap(start, end - start); } up(&ia32_mmap_sem);#endif return ret;}#if PAGE_SHIFT > IA32_PAGE_SHIFT/* * When mprotect()ing a partial page, we set the permission to the union of the old * settings and the new settings. In other words, it's only possible to make access to a * partial page less restrictive. */static longmprotect_subpage (unsigned long address, int new_prot){ int old_prot; struct vm_area_struct *vma; if (new_prot == PROT_NONE) return 0; /* optimize case where nothing changes... */ vma = find_vma(current->mm, address); old_prot = get_page_prot(vma, address); return sys_mprotect(address, PAGE_SIZE, new_prot | old_prot);}#endif /* PAGE_SHIFT > IA32_PAGE_SHIFT */asmlinkage longsys32_mprotect (unsigned int start, unsigned int len, int prot){ unsigned int end = start + len;#if PAGE_SHIFT > IA32_PAGE_SHIFT long retval = 0;#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -