Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:21

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  */
0006 
0007 /*
0008  * Copyright (c) 2012 Chris Johns (chrisj@rtems.org)
0009  * Copyright (c) 2017 Gedare Bloom (gedare@rtems.org)
0010  * Copyright (c) 2017 Kevin kirspel (kirspkt@gmail.com)
0011  *
0012  * Redistribution and use in source and binary forms, with or without
0013  * modification, are permitted provided that the following conditions
0014  * are met:
0015  * 1. Redistributions of source code must retain the above copyright
0016  *    notice, this list of conditions and the following disclaimer.
0017  * 2. Redistributions in binary form must reproduce the above copyright
0018  *    notice, this list of conditions and the following disclaimer in the
0019  *    documentation and/or other materials provided with the distribution.
0020  *
0021  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0022  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0025  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0026  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0027  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0029  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0030  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0031  * POSSIBILITY OF SUCH DAMAGE.
0032  */
0033 
0034 #ifdef HAVE_CONFIG_H
0035 #include "config.h"
0036 #endif
0037 
0038 #include <rtems.h>
0039 #include <errno.h>
0040 #include <stdlib.h>
0041 #include <string.h>
0042 #include <sys/mman.h>
0043 #include <sys/stat.h>
0044 #include <unistd.h>
0045 
0046 #include "rtems/libio_.h"
0047 
0048 #include <rtems/posix/mmanimpl.h>
0049 #include <rtems/posix/shmimpl.h>
0050 
0051 
0052 /**
0053  * mmap chain of mappings.
0054  */
0055 CHAIN_DEFINE_EMPTY( mmap_mappings );
0056 
0057 void *mmap(
0058   void *addr, size_t len, int prot, int flags, int fildes, off_t off
0059 )
0060 {
0061   struct stat     sb;
0062   mmap_mapping   *mapping;
0063   mmap_mapping   *current_mapping;
0064   ssize_t         r;
0065   rtems_libio_t  *iop;
0066   bool            map_fixed;
0067   bool            map_anonymous;
0068   bool            map_shared;
0069   bool            map_private;
0070   bool            is_shared_shm;
0071   int             err;
0072 
0073   map_fixed = (flags & MAP_FIXED) == MAP_FIXED;
0074   map_anonymous = (flags & MAP_ANON) == MAP_ANON;
0075   map_shared = (flags & MAP_SHARED) == MAP_SHARED;
0076   map_private = (flags & MAP_PRIVATE) == MAP_PRIVATE;
0077 
0078   /* Clear errno. */
0079   errno = 0;
0080   iop = NULL;
0081 
0082   if ( len == 0 ) {
0083     errno = EINVAL;
0084     return MAP_FAILED;
0085   }
0086 
0087   /*
0088    * We can provide read, write and execute because the memory in RTEMS does
0089    * not normally have protections but we cannot hide access to memory.
0090    */
0091   if ( prot == PROT_NONE ) {
0092     errno = ENOTSUP;
0093     return MAP_FAILED;
0094   }
0095 
0096   /*
0097    * We can not normally provide restriction of write access. Reject any
0098    * attempt to map without write permission, since we are not able to
0099    * prevent a write from succeeding.
0100    */
0101   if ( PROT_WRITE != (prot & PROT_WRITE) ) {
0102     errno = ENOTSUP;
0103     return MAP_FAILED;
0104   }
0105 
0106   /*
0107    * Anonymous mappings must have file descriptor set to -1 and the offset
0108    * set to 0. Shared mappings are not supported with Anonymous mappings at
0109    * this time
0110    */
0111   if ( map_anonymous && (fildes != -1 || off != 0 || map_shared) ) {
0112     errno = EINVAL;
0113     return MAP_FAILED;
0114   }
0115 
0116   /*
0117    * If MAP_ANON is declared without MAP_PRIVATE or MAP_SHARED,
0118    * force MAP_PRIVATE
0119    */
0120   if ( map_anonymous && !map_private && !map_shared ) {
0121     flags |= MAP_PRIVATE;
0122     map_private = true;
0123   }
0124 
0125   /* Check for supported flags */
0126   if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | MAP_ANON)) != 0) {
0127     errno = EINVAL;
0128     return MAP_FAILED;
0129   }
0130 
0131   /* Either MAP_SHARED or MAP_PRIVATE must be defined, but not both */
0132   if ( map_shared ) {
0133     if ( map_private ) {
0134       errno = EINVAL;
0135       return MAP_FAILED;
0136     }
0137   } else if ( !map_private ) {
0138     errno = EINVAL;
0139     return MAP_FAILED;
0140   }
0141 
0142   /* Check for illegal addresses. Watch out for address wrap. */
0143   if ( map_fixed ) {
0144     if ((uintptr_t)addr & PAGE_MASK) {
0145       errno = EINVAL;
0146       return MAP_FAILED;
0147     }
0148     if ( addr == NULL ) {
0149       errno = EINVAL;
0150       return MAP_FAILED;
0151     }
0152     if (addr + len < addr) {
0153       errno = EINVAL;
0154       return MAP_FAILED;
0155     }
0156   }
0157 
0158   if ( !map_anonymous ) {
0159     /*
0160      * Get a stat of the file to get the dev + inode number and to make sure the
0161      * fd is ok. The normal libio calls cannot be used because we need to return
0162      * MAP_FAILED on error and they return -1 directly without coming back to
0163      * here.
0164      */
0165     if ( fstat( fildes, &sb ) < 0 ) {
0166       errno = EBADF;
0167       return MAP_FAILED;
0168     }
0169 
0170     /* fstat ensures we have a good file descriptor. Hold on to iop. */
0171     iop = rtems_libio_iop( fildes );
0172 
0173     /* Check the type of file we have and make sure it is supported. */
0174     if ( S_ISDIR( sb.st_mode ) || S_ISLNK( sb.st_mode )) {
0175       errno = ENODEV;
0176       return MAP_FAILED;
0177     }
0178 
0179     /* Check to see if the mapping is valid for a regular file. */
0180     if ( S_ISREG( sb.st_mode )
0181     /* FIXME: Should this be using strict inequality (>) comparisons? It would
0182      * be valid to map a region exactly equal to the st_size, e.g. see below. */
0183          && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
0184       errno = EOVERFLOW;
0185       return MAP_FAILED;
0186     }
0187 
0188     /* Check to see if the mapping is valid for other file/object types. */
0189     if ( !S_ISCHR( sb.st_mode ) && sb.st_size < off + len ) {
0190       errno = ENXIO;
0191       return MAP_FAILED;
0192     }
0193 
0194     /* Do not seek on character devices, pipes, sockets, or memory objects. */
0195     if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
0196       if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
0197         return MAP_FAILED;
0198       }
0199     }
0200 
0201     /* cdevs do not provide private mappings of any kind. */
0202     if ( S_ISCHR( sb.st_mode ) && map_private ) {
0203       errno = EINVAL;
0204       return MAP_FAILED;
0205     }
0206   }
0207 
0208   /* Create the mapping */
0209   mapping = malloc( sizeof( mmap_mapping ));
0210   if ( !mapping ) {
0211     errno = ENOMEM;
0212     return MAP_FAILED;
0213   }
0214   memset( mapping, 0, sizeof( mmap_mapping ));
0215   mapping->len = len;
0216   mapping->flags = flags;
0217 
0218   if ( !map_anonymous ) {
0219     /*
0220      * HACK: We should have a better generic way to distinguish between
0221      * shm objects and other mmap'd files. We need to know at munmap time
0222      * if the mapping is to a shared memory object in order to refcnt shms.
0223      * We could do this by providing mmap in the file operations if needed.
0224      */
0225     if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ||
0226          S_ISCHR( sb.st_mode ) || S_ISFIFO( sb.st_mode ) ||
0227          S_ISSOCK( sb.st_mode ) ) {
0228       is_shared_shm = false;
0229     } else {
0230       is_shared_shm = true;
0231     }
0232   } else {
0233     is_shared_shm = false;
0234   }
0235 
0236   if ( map_fixed ) {
0237     mapping->addr = addr;
0238   } else if ( map_private ) {
0239     /* private mappings of shared memory do not need special treatment. */
0240     is_shared_shm = false;
0241     err = posix_memalign( &mapping->addr, PAGE_SIZE, len );
0242     if ( err != 0 ) {
0243       free( mapping );
0244       errno = ENOMEM;
0245       return MAP_FAILED;
0246     }
0247   }
0248 
0249   /* MAP_FIXED is not supported for shared memory objects with MAP_SHARED. */
0250   if ( map_fixed && is_shared_shm ) {
0251     free( mapping );
0252     errno = ENOTSUP;
0253     return MAP_FAILED;
0254   }
0255 
0256   mmap_mappings_lock_obtain();
0257 
0258   if ( map_fixed ) {
0259     rtems_chain_node* node = rtems_chain_first (&mmap_mappings);
0260     while ( !rtems_chain_is_tail( &mmap_mappings, node )) {
0261       /*
0262        * If the map is fixed see if this address is already mapped. At this
0263        * point in time if there is an overlap in the mappings we return an
0264        * error. POSIX allows us to also return successfully by unmapping
0265        * the overlapping prior mappings.
0266        */
0267       current_mapping = (mmap_mapping*) node;
0268       if ( ( addr >= current_mapping->addr ) &&
0269            ( addr < ( current_mapping->addr + current_mapping->len )) ) {
0270         free( mapping );
0271         mmap_mappings_lock_release( );
0272         errno = ENXIO;
0273         return MAP_FAILED;
0274       }
0275       node = rtems_chain_next( node );
0276     }
0277   }
0278 
0279   /* Populate the data */
0280   if ( map_private ) {
0281     if ( !map_anonymous ) {
0282       /*
0283        * Use read() for private mappings. This updates atime as needed.
0284        * Changes to the underlying object will NOT be reflected in the mapping.
0285        * The underlying object can be removed while the mapping exists.
0286        */
0287       r = read( fildes, mapping->addr, len );
0288 
0289       if ( r != len ) {
0290         mmap_mappings_lock_release( );
0291         if ( !map_fixed ) {
0292           free( mapping->addr );
0293         }
0294         free( mapping );
0295         errno = ENXIO;
0296         return MAP_FAILED;
0297       }
0298     } else if ( !map_fixed ) {
0299       memset( mapping->addr, 0, len );
0300     }
0301   } else if ( map_shared ) {
0302     if ( is_shared_shm ) {
0303       /* FIXME: This use of implementation details is a hack. */
0304       mapping->shm = iop_to_shm( iop );
0305     }
0306 
0307     err = (*iop->pathinfo.handlers->mmap_h)(
0308         iop, &mapping->addr, len, prot, off );
0309     if ( err != 0 ) {
0310       mmap_mappings_lock_release( );
0311       free( mapping );
0312       return MAP_FAILED;
0313     }
0314   }
0315 
0316   rtems_chain_append_unprotected( &mmap_mappings, &mapping->node );
0317 
0318   mmap_mappings_lock_release( );
0319 
0320   return mapping->addr;
0321 }