summaryrefslogtreecommitdiffstats
path: root/cpukit/posix/src/mmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/posix/src/mmap.c')
-rw-r--r--cpukit/posix/src/mmap.c257
1 files changed, 147 insertions, 110 deletions
diff --git a/cpukit/posix/src/mmap.c b/cpukit/posix/src/mmap.c
index 5b6ac2e209..d9b663dd1e 100644
--- a/cpukit/posix/src/mmap.c
+++ b/cpukit/posix/src/mmap.c
@@ -5,6 +5,7 @@
/*
* Copyright (c) 2012 Chris Johns (chrisj@rtems.org)
* Copyright (c) 2017 Gedare Bloom (gedare@rtems.org)
+ * Copyright (c) 2017 Kevin kirspel (kirspkt@gmail.com)
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
@@ -107,30 +108,6 @@ bool mmap_mappings_lock_release(
return true;
}
-/* Helper function only gets called for mmap mappings of shared memory objects
- * with the MAP_SHARED flag.
- */
-static void *shm_mmap( rtems_libio_t *iop, size_t len, int prot, off_t off)
-{
- POSIX_Shm_Control *shm = iop_to_shm( iop );
- void *m;
-
- _Objects_Allocator_lock();
-
- m = (*shm->shm_object.ops->object_mmap)( &shm->shm_object, len, prot, off);
- if ( m != NULL ) {
- /* Keep a reference in the shared memory to prevent its removal. */
- ++shm->reference_count;
-
- /* Update atime */
- _POSIX_Shm_Update_atime(shm);
- }
-
- _Objects_Allocator_unlock();
-
- return m;
-}
-
void *mmap(
void *addr, size_t len, int prot, int flags, int fildes, off_t off
)
@@ -140,37 +117,25 @@ void *mmap(
ssize_t r;
rtems_libio_t *iop;
bool map_fixed;
+ bool map_anonymous;
+ bool map_shared;
+ bool map_private;
+ int err;
map_fixed = (flags & MAP_FIXED) == MAP_FIXED;
+ map_anonymous = (flags & MAP_ANON) == MAP_ANON;
+ map_shared = (flags & MAP_SHARED) == MAP_SHARED;
+ map_private = (flags & MAP_PRIVATE) == MAP_PRIVATE;
/* 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;
- }
-
- /* fstat ensures we have a good file descriptor. Hold on to iop. */
- iop = rtems_libio_iop( fildes );
+ iop = NULL;
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.
@@ -180,17 +145,6 @@ void *mmap(
return MAP_FAILED;
}
- /* Either MAP_SHARED or MAP_PRIVATE must be defined, but not both */
- if ( (flags & MAP_SHARED) == MAP_SHARED ) {
- if ( (flags & MAP_PRIVATE) == MAP_PRIVATE ) {
- errno = EINVAL;
- return MAP_FAILED;
- }
- } else if ( (flags & MAP_PRIVATE) != MAP_PRIVATE ) {
- errno = EINVAL;
- return MAP_FAILED;
- }
-
/*
* We can not normally provide restriction of write access. Reject any
* attempt to map without write permission, since we are not able to
@@ -201,27 +155,104 @@ void *mmap(
return MAP_FAILED;
}
- /* Check to see if the mapping is valid for a regular file. */
- if ( S_ISREG( sb.st_mode )
- /* FIXME: Should this be using strict inequality (>) comparisons? It would
- * be valid to map a region exactly equal to the st_size, e.g. see below. */
- && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
- errno = EOVERFLOW;
+ /*
+ * Anonymous mappings must have file descriptor set to -1 and the offset
+ * set to 0. Shared mappings are not supported with Anonymous mappings at
+ * this time
+ */
+ if ( map_anonymous && (fildes != -1 || off != 0 || map_shared) ) {
+ errno = EINVAL;
return MAP_FAILED;
}
/*
- * Check to see if the mapping is valid for other file/object types.
- * Does this satisfy for devices?
+ * If MAP_ANON is declared without MAP_PRIVATE or MAP_SHARED,
+ * force MAP_PRIVATE
*/
- if ( sb.st_size < off + len ) {
- errno = ENXIO;
+ if ( map_anonymous && !map_private && !map_shared ) {
+ flags |= MAP_PRIVATE;
+ map_private = true;
+ }
+
+ /* Check for supported flags */
+ if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | MAP_ANON)) != 0) {
+ errno = EINVAL;
return MAP_FAILED;
}
- /* Do not seek on character devices, pipes, sockets, or memory objects. */
- if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
- if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
+ /* Either MAP_SHARED or MAP_PRIVATE must be defined, but not both */
+ if ( map_shared ) {
+ if ( map_private ) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+ } else if ( !map_private ) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ /* Check for illegal addresses. Watch out for address wrap. */
+ if ( map_fixed ) {
+ if ((uintptr_t)addr & PAGE_MASK) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+ if ( addr == NULL ) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+ if (addr + len < addr) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+ }
+
+ if ( !map_anonymous ) {
+ /*
+ * 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;
+ }
+
+ /* fstat ensures we have a good file descriptor. Hold on to iop. */
+ iop = rtems_libio_iop( fildes );
+
+ /* 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;
+ }
+
+ /* Check to see if the mapping is valid for a regular file. */
+ if ( S_ISREG( sb.st_mode )
+ /* FIXME: Should this be using strict inequality (>) comparisons? It would
+ * be valid to map a region exactly equal to the st_size, e.g. see below. */
+ && (( off >= sb.st_size ) || (( off + len ) >= sb.st_size ))) {
+ errno = EOVERFLOW;
+ return MAP_FAILED;
+ }
+
+ /* Check to see if the mapping is valid for other file/object types. */
+ if ( !S_ISCHR( sb.st_mode ) && sb.st_size < off + len ) {
+ errno = ENXIO;
+ return MAP_FAILED;
+ }
+
+ /* Do not seek on character devices, pipes, sockets, or memory objects. */
+ if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ) {
+ if ( lseek( fildes, off, SEEK_SET ) < 0 ) {
+ return MAP_FAILED;
+ }
+ }
+
+ /* cdevs do not provide private mappings of any kind. */
+ if ( S_ISCHR( sb.st_mode ) && map_private ) {
+ errno = EINVAL;
return MAP_FAILED;
}
}
@@ -230,42 +261,37 @@ void *mmap(
mapping = malloc( sizeof( mmap_mapping ));
if ( !mapping ) {
errno = ENOMEM;
- return NULL;
+ return MAP_FAILED;
}
memset( mapping, 0, sizeof( mmap_mapping ));
mapping->len = len;
mapping->flags = flags;
mapping->iop = iop;
- /*
- * HACK: We should have a better generic way to distinguish between
- * shm objects and other mmap'd files. We need to know at munmap time
- * if the mapping is to a shared memory object in order to refcnt shms.
- * We could do this by providing mmap in the file operations if needed.
- */
- if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ||
- S_ISCHR( sb.st_mode ) || S_ISFIFO( sb.st_mode ) ||
- S_ISSOCK( sb.st_mode ) ) {
- mapping->is_shared_shm = false;
+ if ( !map_anonymous ) {
+ /*
+ * HACK: We should have a better generic way to distinguish between
+ * shm objects and other mmap'd files. We need to know at munmap time
+ * if the mapping is to a shared memory object in order to refcnt shms.
+ * We could do this by providing mmap in the file operations if needed.
+ */
+ if ( S_ISREG( sb.st_mode ) || S_ISBLK( sb.st_mode ) ||
+ S_ISCHR( sb.st_mode ) || S_ISFIFO( sb.st_mode ) ||
+ S_ISSOCK( sb.st_mode ) ) {
+ mapping->is_shared_shm = false;
+ } else {
+ mapping->is_shared_shm = true;
+ }
} else {
- mapping->is_shared_shm = true;
- }
-
- /*
- * MAP_SHARED currently is only supported for shared memory objects.
- */
- if ( (MAP_SHARED == (flags & MAP_SHARED)) && (mapping->is_shared_shm == false) ) {
- free( mapping );
- errno = ENOTSUP;
- return MAP_FAILED;
+ mapping->is_shared_shm = false;
}
if ( map_fixed ) {
mapping->addr = addr;
- } else if ( MAP_PRIVATE == (flags & MAP_PRIVATE) ) {
+ } else if ( map_private ) {
/* private mappings of shared memory do not need special treatment. */
mapping->is_shared_shm = false;
- mapping->addr = malloc( len );
+ posix_memalign( &mapping->addr, PAGE_SIZE, len );
if ( !mapping->addr ) {
free( mapping );
errno = ENOMEM;
@@ -306,32 +332,43 @@ void *mmap(
}
/* Populate the data */
- if ( MAP_PRIVATE == (flags & MAP_PRIVATE) ) {
- /*
- * Use read() for private mappings. This updates atime as needed.
- * Changes to the underlying object will NOT be reflected in the mapping.
- * The underlying object can be removed while the mapping exists.
- */
- r = read( fildes, mapping->addr, len );
+ if ( map_private ) {
+ if ( !map_anonymous ) {
+ /*
+ * Use read() for private mappings. This updates atime as needed.
+ * Changes to the underlying object will NOT be reflected in the mapping.
+ * The underlying object can be removed while the mapping exists.
+ */
+ r = read( fildes, mapping->addr, len );
- if ( r != len ) {
- mmap_mappings_lock_release( );
- if ( !map_fixed ) {
- free( mapping->addr );
+ if ( r != len ) {
+ mmap_mappings_lock_release( );
+ if ( !map_fixed ) {
+ free( mapping->addr );
+ }
+ free( mapping );
+ errno = ENXIO;
+ return MAP_FAILED;
}
+ } else if ( !map_fixed ) {
+ memset( mapping->addr, 0, len );
+ }
+ } else if ( map_shared ) {
+ err = (*iop->pathinfo.handlers->mmap_h)(
+ iop, &mapping->addr, len, prot, off );
+ if ( err != 0 ) {
+ mmap_mappings_lock_release( );
free( mapping );
- errno = ENXIO;
return MAP_FAILED;
}
- } else if ( MAP_SHARED == (flags & MAP_SHARED) ) {
- /* Currently only shm objects can be MAP_SHARED. */
- mapping->addr = shm_mmap(iop, len, prot, off);
}
- /* add an extra reference to the file associated with fildes that
- * is not removed by a subsequent close(). This reference shall be removed
- * when there are no more mappings to the file. */
- rtems_libio_increment_mapping_refcnt(iop);
+ if ( iop != NULL ) {
+ /* add an extra reference to the file associated with fildes that
+ * is not removed by a subsequent close(). This reference shall be removed
+ * when there are no more mappings to the file. */
+ rtems_libio_increment_mapping_refcnt(iop);
+ }
rtems_chain_append( &mmap_mappings, &mapping->node );