home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / mm / mprotect.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-14  |  5.7 KB  |  253 lines

  1. #define THREE_LEVEL
  2. /*
  3.  *    linux/mm/mprotect.c
  4.  *
  5.  *  (C) Copyright 1994 Linus Torvalds
  6.  */
  7. #include <linux/stat.h>
  8. #include <linux/sched.h>
  9. #include <linux/kernel.h>
  10. #include <linux/mm.h>
  11. #include <linux/shm.h>
  12. #include <linux/errno.h>
  13. #include <linux/mman.h>
  14. #include <linux/string.h>
  15. #include <linux/malloc.h>
  16.  
  17. #include <asm/segment.h>
  18. #include <asm/system.h>
  19. #include <asm/pgtable.h>
  20.  
  21. static inline void change_pte_range(pmd_t * pmd, unsigned long address,
  22.     unsigned long size, pgprot_t newprot)
  23. {
  24.     pte_t * pte;
  25.     unsigned long end;
  26.  
  27.     if (pmd_none(*pmd))
  28.         return;
  29.     if (pmd_bad(*pmd)) {
  30.         printk("change_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd));
  31.         pmd_clear(pmd);
  32.         return;
  33.     }
  34.     pte = pte_offset(pmd, address);
  35.     address &= ~PMD_MASK;
  36.     end = address + size;
  37.     if (end > PMD_SIZE)
  38.         end = PMD_SIZE;
  39.     do {
  40.         pte_t entry = *pte;
  41.         if (pte_present(entry))
  42.             *pte = pte_modify(entry, newprot);
  43.         address += PAGE_SIZE;
  44.         pte++;
  45.     } while (address < end);
  46. }
  47.  
  48. static inline void change_pmd_range(pgd_t * pgd, unsigned long address,
  49.     unsigned long size, pgprot_t newprot)
  50. {
  51.     pmd_t * pmd;
  52.     unsigned long end;
  53.  
  54.     if (pgd_none(*pgd))
  55.         return;
  56.     if (pgd_bad(*pgd)) {
  57.         printk("change_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd));
  58.         pgd_clear(pgd);
  59.         return;
  60.     }
  61.     pmd = pmd_offset(pgd, address);
  62.     address &= ~PGDIR_MASK;
  63.     end = address + size;
  64.     if (end > PGDIR_SIZE)
  65.         end = PGDIR_SIZE;
  66.     do {
  67.         change_pte_range(pmd, address, end - address, newprot);
  68.         address = (address + PMD_SIZE) & PMD_MASK;
  69.         pmd++;
  70.     } while (address < end);
  71. }
  72.  
  73. static void change_protection(unsigned long start, unsigned long end, pgprot_t newprot)
  74. {
  75.     pgd_t *dir;
  76.  
  77.     dir = pgd_offset(current, start);
  78.     while (start < end) {
  79.         change_pmd_range(dir, start, end - start, newprot);
  80.         start = (start + PGDIR_SIZE) & PGDIR_MASK;
  81.         dir++;
  82.     }
  83.     invalidate();
  84.     return;
  85. }
  86.  
  87. static inline int mprotect_fixup_all(struct vm_area_struct * vma,
  88.     int newflags, pgprot_t prot)
  89. {
  90.     vma->vm_flags = newflags;
  91.     vma->vm_page_prot = prot;
  92.     return 0;
  93. }
  94.  
  95. static inline int mprotect_fixup_start(struct vm_area_struct * vma,
  96.     unsigned long end,
  97.     int newflags, pgprot_t prot)
  98. {
  99.     struct vm_area_struct * n;
  100.  
  101.     n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
  102.     if (!n)
  103.         return -ENOMEM;
  104.     *n = *vma;
  105.     vma->vm_start = end;
  106.     n->vm_end = end;
  107.     vma->vm_offset += vma->vm_start - n->vm_start;
  108.     n->vm_flags = newflags;
  109.     n->vm_page_prot = prot;
  110.     if (n->vm_inode)
  111.         n->vm_inode->i_count++;
  112.     if (n->vm_ops && n->vm_ops->open)
  113.         n->vm_ops->open(n);
  114.     insert_vm_struct(current, n);
  115.     return 0;
  116. }
  117.  
  118. static inline int mprotect_fixup_end(struct vm_area_struct * vma,
  119.     unsigned long start,
  120.     int newflags, pgprot_t prot)
  121. {
  122.     struct vm_area_struct * n;
  123.  
  124.     n = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
  125.     if (!n)
  126.         return -ENOMEM;
  127.     *n = *vma;
  128.     vma->vm_end = start;
  129.     n->vm_start = start;
  130.     n->vm_offset += n->vm_start - vma->vm_start;
  131.     n->vm_flags = newflags;
  132.     n->vm_page_prot = prot;
  133.     if (n->vm_inode)
  134.         n->vm_inode->i_count++;
  135.     if (n->vm_ops && n->vm_ops->open)
  136.         n->vm_ops->open(n);
  137.     insert_vm_struct(current, n);
  138.     return 0;
  139. }
  140.  
  141. static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
  142.     unsigned long start, unsigned long end,
  143.     int newflags, pgprot_t prot)
  144. {
  145.     struct vm_area_struct * left, * right;
  146.  
  147.     left = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
  148.     if (!left)
  149.         return -ENOMEM;
  150.     right = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
  151.     if (!right) {
  152.         kfree(left);
  153.         return -ENOMEM;
  154.     }
  155.     *left = *vma;
  156.     *right = *vma;
  157.     left->vm_end = start;
  158.     vma->vm_start = start;
  159.     vma->vm_end = end;
  160.     right->vm_start = end;
  161.     vma->vm_offset += vma->vm_start - left->vm_start;
  162.     right->vm_offset += right->vm_start - left->vm_start;
  163.     vma->vm_flags = newflags;
  164.     vma->vm_page_prot = prot;
  165.     if (vma->vm_inode)
  166.         vma->vm_inode->i_count += 2;
  167.     if (vma->vm_ops && vma->vm_ops->open) {
  168.         vma->vm_ops->open(left);
  169.         vma->vm_ops->open(right);
  170.     }
  171.     insert_vm_struct(current, left);
  172.     insert_vm_struct(current, right);
  173.     return 0;
  174. }
  175.  
  176. static int mprotect_fixup(struct vm_area_struct * vma, 
  177.     unsigned long start, unsigned long end, unsigned int newflags)
  178. {
  179.     pgprot_t newprot;
  180.     int error;
  181.  
  182.     if (newflags == vma->vm_flags)
  183.         return 0;
  184.     newprot = protection_map[newflags & 0xf];
  185.     if (start == vma->vm_start)
  186.         if (end == vma->vm_end)
  187.             error = mprotect_fixup_all(vma, newflags, newprot);
  188.         else
  189.             error = mprotect_fixup_start(vma, end, newflags, newprot);
  190.     else if (end == vma->vm_end)
  191.         error = mprotect_fixup_end(vma, start, newflags, newprot);
  192.     else
  193.         error = mprotect_fixup_middle(vma, start, end, newflags, newprot);
  194.  
  195.     if (error)
  196.         return error;
  197.  
  198.     change_protection(start, end, newprot);
  199.     return 0;
  200. }
  201.  
  202. asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot)
  203. {
  204.     unsigned long nstart, end, tmp;
  205.     struct vm_area_struct * vma, * next;
  206.     int error;
  207.  
  208.     if (start & ~PAGE_MASK)
  209.         return -EINVAL;
  210.     len = (len + ~PAGE_MASK) & PAGE_MASK;
  211.     end = start + len;
  212.     if (end < start)
  213.         return -EINVAL;
  214.     if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
  215.         return -EINVAL;
  216.     if (end == start)
  217.         return 0;
  218.     vma = find_vma(current, start);
  219.     if (!vma || vma->vm_start > start)
  220.         return -EFAULT;
  221.  
  222.     for (nstart = start ; ; ) {
  223.         unsigned int newflags;
  224.  
  225.         /* Here we know that  vma->vm_start <= nstart < vma->vm_end. */
  226.  
  227.         newflags = prot | (vma->vm_flags & ~(PROT_READ | PROT_WRITE | PROT_EXEC));
  228.         if ((newflags & ~(newflags >> 4)) & 0xf) {
  229.             error = -EACCES;
  230.             break;
  231.         }
  232.  
  233.         if (vma->vm_end >= end) {
  234.             error = mprotect_fixup(vma, nstart, end, newflags);
  235.             break;
  236.         }
  237.  
  238.         tmp = vma->vm_end;
  239.         next = vma->vm_next;
  240.         error = mprotect_fixup(vma, nstart, tmp, newflags);
  241.         if (error)
  242.             break;
  243.         nstart = tmp;
  244.         vma = next;
  245.         if (!vma || vma->vm_start != nstart) {
  246.             error = -EFAULT;
  247.             break;
  248.         }
  249.     }
  250.     merge_segments(current, start, end);
  251.     return error;
  252. }
  253.