root/mm/mmap.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mmap_chr
  2. sys_mmap
  3. sys_munmap

   1 /*
   2  *      linux/mm/mmap.c
   3  *
   4  * Written by obz.
   5  */
   6 #include <sys/stat.h>
   7 #include <linux/sched.h>
   8 #include <linux/kernel.h>
   9 #include <asm/segment.h>
  10 #include <asm/system.h>
  11 #include <errno.h>
  12 #include <sys/mman.h>
  13 
  14 /*
  15  * description of effects of mapping type and prot in current implementation.
  16  * this is due to the current handling of page faults in memory.c. the expected
  17  * behavior is in parens:
  18  *
  19  * map_type     prot
  20  *              PROT_NONE       PROT_READ       PROT_WRITE      PROT_EXEC
  21  * MAP_SHARED   r: (no) yes     r: (yes) yes    r: (no) yes     r: (no) no
  22  *              w: (no) yes     w: (no) copy    w: (yes) yes    w: (no) no
  23  *              x: (no) no      x: (no) no      x: (no) no      x: (yes) no
  24  *              
  25  * MAP_PRIVATE  r: (no) yes     r: (yes) yes    r: (no) yes     r: (no) no
  26  *              w: (no) copy    w: (no) copy    w: (copy) copy  w: (no) no
  27  *              x: (no) no      x: (no) no      x: (no) no      x: (yes) no
  28  *
  29  * the permissions are encoded as cxwr (copy,exec,write,read)
  30  */
  31 #define MTYP(T) ((T) & MAP_TYPE)
  32 #define PREAD(T,P) (((P) & PROT_READ) ? 1 : 0)
  33 #define PWRITE(T,P) (((P) & PROT_WRITE) ? (MTYP(T) == MAP_SHARED ? 2 : 10) : 0)
  34 #define PEXEC(T,P) (((P) & PROT_EXEC) ? 4 : 0)
  35 #define PERMISS(T,P) (PREAD(T,P)|PWRITE(T,P)|PEXEC(T,P))
  36 
  37 #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
  38                           current->start_code + current->end_code)
  39 
  40 extern int remap_page_range(unsigned long from, unsigned long to,
  41                             unsigned long size, int permiss);
  42 extern int unmap_page_range(unsigned long from, unsigned long size);
  43 
  44 static caddr_t
  45 mmap_chr(unsigned long addr, size_t len, int prot, int flags,
     /* [previous][next][first][last][top][bottom][index][help] */
  46          struct inode *inode, unsigned long off)
  47 {
  48         int major, minor;
  49         extern unsigned long HIGH_MEMORY;
  50 
  51         major = MAJOR(inode->i_rdev);
  52         minor = MINOR(inode->i_rdev);
  53 
  54         /*
  55          * for character devices, only /dev/mem may be mapped. when the
  56          * swapping code is modified to allow arbitrary sources of pages,
  57          * then we can open it up to regular files.
  58          */
  59 
  60         if (major != 1 || minor != 1)
  61                 return (caddr_t)-ENODEV;
  62 
  63         /*
  64          * we only allow mappings from address 0 to HIGH_MEMORY, since thats
  65          * the range of our memory [actually this is a lie. the buffer cache
  66          * and ramdisk occupy higher memory, but the paging stuff won't
  67          * let us map to it anyway, so we break it here].
  68          *
  69          * this call is very dangerous! because of the lack of adequate
  70          * tagging of frames, it is possible to mmap over a frame belonging
  71          * to another (innocent) process. with MAP_SHARED|MAP_WRITE, this
  72          * rogue process can trample over the other's data! we ignore this :{
  73          * for now, we hope people will malloc the required amount of space,
  74          * then mmap over it. the mm needs serious work before this can be
  75          * truly useful.
  76          */
  77 
  78         if (len > HIGH_MEMORY || off > HIGH_MEMORY - len) /* avoid overflow */
  79                 return (caddr_t)-ENXIO;
  80 
  81         if (remap_page_range(addr, off, len, PERMISS(flags, prot)))
  82                 return (caddr_t)-EAGAIN;
  83         
  84         return (caddr_t)addr;
  85 }
  86 
  87 caddr_t
  88 sys_mmap(unsigned long *buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
  89 {
  90         unsigned long base, addr;
  91         unsigned long len, limit, off;
  92         int prot, flags, fd;
  93         struct file *file;
  94         struct inode *inode;
  95 
  96         addr = (unsigned long)  get_fs_long(buffer);    /* user address space*/
  97         len = (size_t)          get_fs_long(buffer+1);  /* nbytes of mapping */
  98         prot = (int)            get_fs_long(buffer+2);  /* protection */
  99         flags = (int)           get_fs_long(buffer+3);  /* mapping type */
 100         fd = (int)              get_fs_long(buffer+4);  /* object to map */
 101         off = (unsigned long)   get_fs_long(buffer+5);  /* offset in object */
 102 
 103         if (fd >= NR_OPEN || fd < 0 || !(file = current->filp[fd]))
 104                 return (caddr_t) -EBADF;
 105         if (addr > TASK_SIZE || (addr+(unsigned long) len) > TASK_SIZE)
 106                 return (caddr_t) -EINVAL;
 107         inode = file->f_inode;
 108 
 109         /*
 110          * do simple checking here so the lower-level routines won't have
 111          * to. we assume access permissions have been handled by the open
 112          * of the memory object, so we don't do any here.
 113          */
 114 
 115         switch (flags & MAP_TYPE) {
 116         case MAP_SHARED:
 117                 if ((prot & PROT_WRITE) && !(file->f_mode & 2))
 118                         return (caddr_t)-EINVAL;
 119                 /* fall through */
 120         case MAP_PRIVATE:
 121                 if (!(file->f_mode & 1))
 122                         return (caddr_t)-EINVAL;
 123                 break;
 124 
 125         default:
 126                 return (caddr_t)-EINVAL;
 127         }
 128 
 129         /*
 130          * obtain the address to map to. we verify (or select) it and ensure
 131          * that it represents a valid section of the address space. we assume
 132          * that if PROT_EXEC is specified this should be in the code segment.
 133          */
 134         if (prot & PROT_EXEC) {
 135                 base = get_base(current->ldt[1]);       /* cs */
 136                 limit = get_limit(0x0f);                /* cs limit */
 137         } else {
 138                 base = get_base(current->ldt[2]);       /* ds */
 139                 limit = get_limit(0x17);                /* ds limit */
 140         }
 141 
 142         if (flags & MAP_FIXED) {
 143                 /*
 144                  * if MAP_FIXED is specified, we have to map exactly at this
 145                  * address. it must be page aligned and not ambiguous.
 146                  */
 147                 if ((addr & 0xfff) || addr > 0x7fffffff || addr == 0 ||
 148                     (off & 0xfff))
 149                         return (caddr_t)-EINVAL;
 150                 if (addr + len > limit)
 151                         return (caddr_t)-ENOMEM;
 152         } else {
 153                 /*
 154                  * we're given a hint as to where to put the address.
 155                  * that we still need to search for a range of pages which
 156                  * are not mapped and which won't impact the stack or data
 157                  * segment.
 158                  * in linux, we only have a code segment and data segment.
 159                  * since data grows up and stack grows down, we're sort of
 160                  * stuck. placing above the data will break malloc, below
 161                  * the stack will cause stack overflow. because of this
 162                  * we don't allow nonspecified mappings...
 163                  */
 164                 return (caddr_t)-ENOMEM;
 165         }
 166 
 167         /*
 168          * determine the object being mapped and call the appropriate
 169          * specific mapper. the address has already been validated, but
 170          * not unmapped
 171          */
 172         if (S_ISCHR(inode->i_mode))
 173                 addr = (unsigned long)mmap_chr(base + addr, len, prot, flags,
 174                                                inode, off);
 175         else
 176                 addr = (unsigned long)-ENODEV;
 177         if ((long)addr > 0)
 178                 addr -= base;
 179 
 180         return (caddr_t)addr;
 181 }
 182 
 183 int sys_munmap(unsigned long addr, size_t len)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185         unsigned long base, limit;
 186 
 187         base = get_base(current->ldt[2]);       /* map into ds */
 188         limit = get_limit(0x17);                /* ds limit */
 189 
 190         if ((addr & 0xfff) || addr > 0x7fffffff || addr == 0 ||
 191             addr + len > limit)
 192                 return -EINVAL;
 193         if (unmap_page_range(base + addr, len))
 194                 return -EAGAIN; /* should never happen */
 195         return 0;
 196 }

/* [previous][next][first][last][top][bottom][index][help] */