summaryrefslogblamecommitdiffstats
path: root/cpukit/posix/src/mmap.c
blob: c05a65e07874fa58ec35ae7e345ade08aee02a35 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
  
                                                    
  


                                                          





                   


                   
                     


















































































                                                                              
 
           
                                                                    

 




























































































































                                                                              
 
/*
 * Copyright (c) 2012 Chris Johns (chrisj@rtems.org)
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include "rtems/libio_.h"

#include <rtems/posix/mmanimpl.h> 

#define RTEMS_MUTEX_ATTRIBS \
  (RTEMS_PRIORITY | RTEMS_SIMPLE_BINARY_SEMAPHORE | \
   RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL)

/**
 * Mmap chain of mappings.
 */
rtems_chain_control mmap_mappings;

/**
 * The id of the MMAP lock.
 */
static rtems_id mmap_mappings_lock;

/**
 * Create the lock.
 */
static
bool mmap_mappings_lock_create(
  void
)
{
  /*
   * Lock the mapping table. We only create a lock if a call is made. First we
   * test if a mapping lock is present. If one is present we lock it. If not
   * the libio lock is locked and we then test the mapping lock again. If not
   * present we create the mapping lock then release libio lock.
   */
  if ( mmap_mappings_lock == 0 ) {
    rtems_status_code sc = RTEMS_SUCCESSFUL;
    rtems_chain_initialize_empty( &mmap_mappings );
    rtems_semaphore_obtain( rtems_libio_semaphore,
                            RTEMS_WAIT, RTEMS_NO_TIMEOUT );
    if ( mmap_mappings_lock == 0 )
      sc = rtems_semaphore_create( rtems_build_name( 'M', 'M', 'A', 'P' ),
                                   1,
                                   RTEMS_MUTEX_ATTRIBS,
                                   RTEMS_NO_PRIORITY,
                                   &mmap_mappings_lock );
    rtems_semaphore_release( rtems_libio_semaphore );
    if ( sc != RTEMS_SUCCESSFUL ) {
      errno = EINVAL;
      return false;
    }
  }
  return true;
}

bool mmap_mappings_lock_obtain(
  void
)
{
  if ( mmap_mappings_lock_create( ) ) {
    rtems_status_code sc;
    sc = rtems_semaphore_obtain( mmap_mappings_lock,
                                 RTEMS_WAIT, RTEMS_NO_TIMEOUT );
    if ( sc != RTEMS_SUCCESSFUL ) {
      errno = EINVAL;
      return false;
    }
  }
  return true;
}

bool mmap_mappings_lock_release(
  void
)
{
  rtems_status_code sc;
  sc = rtems_semaphore_release( mmap_mappings_lock );
  if (( sc != RTEMS_SUCCESSFUL ) && ( errno == 0 )) {
    errno = EINVAL;
    return false;
  }
  return true;
}

void *mmap(
  void *addr, size_t len, int prot, int flags, int fildes, off_t off
)
{
  struct stat   sb;
  mmap_mapping* mapping;
  ssize_t       r;
  
  /*
   * Clear errno.
   */
  errno = 0;
  
  /*
   * Get a stat of the file to get the dev + inode number and to make sure the
   * fd is ok. The normal libio calls cannot be used because we need to return
   * MAP_FAILED on error and they return -1 directly without coming back to
   * here.
   */
  if ( fstat( fildes, &sb ) < 0 ) {
    errno = EBADF;
    return MAP_FAILED;
  }

  if ( len == 0 ) {
    errno = EINVAL;
    return MAP_FAILED;
  }

  /*
   * Check the type of file we have and make sure it is supported.
   */
  if ( S_ISDIR( sb.st_mode ) || S_ISLNK( sb.st_mode )) {
    errno = ENODEV;
    return MAP_FAILED;
  }
  
  /*
   * We can provide read, write and execute because the memory in RTEMS does
   * not normally have protections but we cannot hide access to memory.
   */
  if ( prot == PROT_NONE ) {
    errno = EINVAL;
    return MAP_FAILED;
  }
  
  /*
   * Check to see if the mapping is valid for the file.
   */
  if ( S_ISREG( sb.st_mode )
       && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
    errno = EOVERFLOW;
    return MAP_FAILED;
  }

  /*
   * Obtain the mmap lock. Sets errno on failure.
   */
  if ( !mmap_mappings_lock_obtain( ) )
    return MAP_FAILED;

  if (( flags & MAP_FIXED ) == MAP_FIXED ) {
    rtems_chain_node* node = rtems_chain_first (&mmap_mappings);
    while ( !rtems_chain_is_tail( &mmap_mappings, node )) {
      /*
       * If the map is fixed see if this address is already mapped. At this
       * point in time if there is an overlap in the mappings we return an
       * error.
       */
      mapping = (mmap_mapping*) node;
      if ( ( addr >= mapping->addr ) &&
           ( addr < ( mapping->addr + mapping->len )) ) {
        mmap_mappings_lock_release( );
        errno = ENXIO;
        return MAP_FAILED;
      }
      node = rtems_chain_next( node );
    }
  }

  mapping = malloc( sizeof( mmap_mapping ));
  if ( !mapping ) {
    mmap_mappings_lock_release( );
    errno = ENOMEM;
    return NULL;
  }

  memset( mapping, 0, sizeof( mmap_mapping ));

  mapping->len = len;
  mapping->flags = flags;
  
  if (( flags & MAP_FIXED ) != MAP_FIXED ) {
    mapping->addr = malloc( len );
    if ( !mapping->addr ) {
      mmap_mappings_lock_release( );
      free( mapping );
      errno = ENOMEM;
      return MAP_FAILED;
    }

    /*
     * Do not seek on character devices, pipes or sockets.
     */
    if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
      if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
        mmap_mappings_lock_release( );
        free( mapping->addr );
        free( mapping );
        return MAP_FAILED;
      }
    }
  }

  r = read( fildes, mapping->addr, len );

  if ( r != len ) {
    mmap_mappings_lock_release( );
    free( mapping->addr );
    free( mapping );
    errno = ENXIO;
    return MAP_FAILED;
  }

  rtems_chain_append( &mmap_mappings, &mapping->node );

  mmap_mappings_lock_release( );
  
  return mapping->addr;
}