/* * mmap() - POSIX 1003.1b 6.3.1 - map pages of memory * * 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.com/license/LICENSE. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "rtems/libio_.h" #include "mmap-internal.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; }