diff --git a/fs/mmap/fs_mmap.c b/fs/mmap/fs_mmap.c index baab91f4871..c764af29bfa 100644 --- a/fs/mmap/fs_mmap.c +++ b/fs/mmap/fs_mmap.c @@ -37,10 +37,156 @@ #include "inode/inode.h" #include "fs_rammap.h" +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: file_mmap_ + ****************************************************************************/ + +static int file_mmap_(FAR struct file *filep, FAR void *start, + size_t length, int prot, int flags, + off_t offset, bool kernel, FAR void **mapped) +{ + FAR void *addr; + int ret; + + /* Since only a tiny subset of mmap() functionality, we have to verify many + * things. + */ + +#ifdef CONFIG_DEBUG_FEATURES + /* Fixed mappings and protections are not currently supported. These + * options could be supported in the KERNEL build with an MMU, but that + * logic is not in place. + */ + + if ((flags & (MAP_FIXED | MAP_DENYWRITE)) != 0) + { + ferr("ERROR: Unsupported options, prot=%x flags=%04x\n", prot, flags); + return -ENOSYS; + } + +#ifndef CONFIG_FS_RAMMAP + if ((flags & MAP_PRIVATE) != 0) + { + ferr("ERROR: MAP_PRIVATE is not supported without file mapping" + "emulation\n"); + return -ENOSYS; + } +#endif /* CONFIG_FS_RAMMAP */ + + /* A length of 0 is invalid. */ + + if (length == 0) + { + ferr("ERROR: Invalid length, length=%zu\n", length); + return -EINVAL; + } +#endif /* CONFIG_DEBUG_FEATURES */ + + if ((filep->f_oflags & O_WROK) == 0 && prot == PROT_WRITE && + (flags & MAP_SHARED) != 0) + { + ferr("ERROR: Unsupported options for read-only file descriptor," + "prot=%x flags=%04x\n", prot, flags); + return -EACCES; + } + + if ((filep->f_oflags & O_RDOK) == 0) + { + ferr("ERROR: File descriptor does not have read permission\n"); + return -EACCES; + } + + /* Check if we are just be asked to allocate memory, i.e., MAP_ANONYMOUS + * set meaning that the memory is not backed up from a file. The file + * descriptor should be -1 (or refer to opened /dev/zero) in this case. + * The file descriptor is ignored in either case. + */ + + if ((flags & MAP_ANONYMOUS) != 0) + { + /* REVISIT: Should reside outside of the heap. That is really the + * only purpose of MAP_ANONYMOUS: To get non-heap memory. In KERNEL + * build, this could be accomplished using pgalloc(), provided that + * you had logic in place to assign a virtual address to the mapping. + */ + + *mapped = kernel ? kmm_zalloc(length) : kumm_zalloc(length); + if (*mapped == NULL) + { + ferr("ERROR: kumm_alloc() failed: %d\n", ret); + return -ENOMEM; + } + + return OK; + } + + if ((flags & MAP_PRIVATE) != 0) + { +#ifdef CONFIG_FS_RAMMAP + /* Allocate memory and copy the file into memory. We would, of course, + * do much better in the KERNEL build using the MMU. + */ + + return rammap(filep, length, offset, kernel, mapped); +#endif + } + + /* Perform the ioctl to get the base address of the file in 'mapped' + * in memory. (casting to uintptr_t first eliminates complaints on some + * architectures where the sizeof long is different from the size of + * a pointer). + */ + + ret = file_ioctl(filep, FIOC_MMAP, (unsigned long)((uintptr_t)&addr)); + if (ret < 0) + { + /* Not directly mappable, probably because the underlying media does + * not support random access. + */ + +#ifdef CONFIG_FS_RAMMAP + /* Allocate memory and copy the file into memory. We would, of course, + * do much better in the KERNEL build using the MMU. + */ + + return rammap(filep, length, offset, kernel, mapped); +#else + ferr("ERROR: file_ioctl(FIOC_MMAP) failed: %d\n", ret); + return ret; +#endif + } + + /* Return the offset address */ + + *mapped = (FAR void *)(((FAR uint8_t *)addr) + offset); + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: file_mmap + * + * Description: + * Equivalent to the standard mmap() function except that is accepts + * a struct file instance instead of a file descriptor and it does not set + * the errno variable. + * + ****************************************************************************/ + +int file_mmap(FAR struct file *filep, FAR void *start, size_t length, + int prot, int flags, off_t offset, FAR void **mapped) +{ + return file_mmap_(filep, start, length, + prot, flags, offset, true, mapped); +} + /**************************************************************************** * Name: mmap * @@ -118,47 +264,10 @@ FAR void *mmap(FAR void *start, size_t length, int prot, int flags, int fd, off_t offset) { - FAR void *addr; FAR struct file *filep; + FAR void *mapped; int ret; - /* Since only a tiny subset of mmap() functionality, we have to verify many - * things. - */ - -#ifdef CONFIG_DEBUG_FEATURES - /* Fixed mappings and protections are not currently supported. These - * options could be supported in the KERNEL build with an MMU, but that - * logic is not in place. - */ - - if ((flags & (MAP_FIXED | MAP_DENYWRITE)) != 0) - { - ferr("ERROR: Unsupported options, prot=%x flags=%04x\n", prot, flags); - ret = -ENOSYS; - goto errout; - } - -#ifndef CONFIG_FS_RAMMAP - if ((flags & MAP_PRIVATE) != 0) - { - ferr("ERROR: MAP_PRIVATE is not supported without file mapping" - "emulation\n"); - ret = -ENOSYS; - goto errout; - } -#endif /* CONFIG_FS_RAMMAP */ - - /* A length of 0 is invalid. */ - - if (length == 0) - { - ferr("ERROR: Invalid length, length=%zu\n", length); - ret = -EINVAL; - goto errout; - } -#endif /* CONFIG_DEBUG_FEATURES */ - if (fs_getfilep(fd, &filep) < 0) { ferr("ERROR: Invalid file descriptor, fd=%d\n", fd); @@ -166,89 +275,14 @@ FAR void *mmap(FAR void *start, size_t length, int prot, int flags, goto errout; } - if ((filep->f_oflags & O_WROK) == 0 && prot == PROT_WRITE && - (flags & MAP_SHARED) != 0) - { - ferr("ERROR: Unsupported options for read-only file descriptor," - "fd=%d prot=%x flags=%04x\n", fd, prot, flags); - ret = -EACCES; - goto errout; - } - - if ((filep->f_oflags & O_RDOK) == 0) - { - ferr("ERROR: File descriptor does not have read permission," - "fd=%d\n", fd); - ret = -EACCES; - goto errout; - } - - /* Check if we are just be asked to allocate memory, i.e., MAP_ANONYMOUS - * set meaning that the memory is not backed up from a file. The file - * descriptor should be -1 (or refer to opened /dev/zero) in this case. - * The file descriptor is ignored in either case. - */ - - if ((flags & MAP_ANONYMOUS) != 0) - { - FAR void *alloc; - - /* REVISIT: Should reside outside of the heap. That is really the - * only purpose of MAP_ANONYMOUS: To get non-heap memory. In KERNEL - * build, this could be accomplished using pgalloc(), provided that - * you had logic in place to assign a virtual address to the mapping. - */ - - alloc = kumm_zalloc(length); - if (alloc == NULL) - { - ferr("ERROR: kumm_alloc() failed: %d\n", ret); - ret = -ENOMEM; - goto errout; - } - - return alloc; - } - - if ((flags & MAP_PRIVATE) != 0) - { -#ifdef CONFIG_FS_RAMMAP - /* Allocate memory and copy the file into memory. We would, of course, - * do much better in the KERNEL build using the MMU. - */ - - return rammap(fd, length, offset); -#endif - } - - /* Perform the ioctl to get the base address of the file in 'mapped' - * in memory. (casting to uintptr_t first eliminates complaints on some - * architectures where the sizeof long is different from the size of - * a pointer). - */ - - ret = nx_ioctl(fd, FIOC_MMAP, (unsigned long)((uintptr_t)&addr)); + ret = file_mmap_(filep, start, length, + prot, flags, offset, false, &mapped); if (ret < 0) { - /* Not directly mappable, probably because the underlying media does - * not support random access. - */ - -#ifdef CONFIG_FS_RAMMAP - /* Allocate memory and copy the file into memory. We would, of course, - * do much better in the KERNEL build using the MMU. - */ - - return rammap(fd, length, offset); -#else - ferr("ERROR: nx_ioctl(FIOC_MMAP) failed: %d\n", ret); goto errout; -#endif } - /* Return the offset address */ - - return (FAR void *)(((FAR uint8_t *)addr) + offset); + return mapped; errout: set_errno(-ret); diff --git a/fs/mmap/fs_munmap.c b/fs/mmap/fs_munmap.c index bb4f1271e92..9ef45f44142 100644 --- a/fs/mmap/fs_munmap.c +++ b/fs/mmap/fs_munmap.c @@ -37,10 +37,149 @@ #include "inode/inode.h" #include "fs_rammap.h" +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int file_munmap_(FAR void *start, size_t length, bool kernel) +{ +#ifdef CONFIG_FS_RAMMAP + FAR struct fs_rammap_s *prev; + FAR struct fs_rammap_s *curr; + FAR void *newaddr; + unsigned int offset; + int ret; + + /* Find a region containing this start and length in the list of regions */ + + ret = nxsem_wait(&g_rammaps.exclsem); + if (ret < 0) + { + return ret; + } + + /* Search the list of regions */ + + for (prev = NULL, curr = g_rammaps.head; curr; + prev = curr, curr = curr->flink) + { + /* Does this region include any part of the specified range? */ + + if ((uintptr_t)start < (uintptr_t)curr->addr + curr->length && + (uintptr_t)start + length >= (uintptr_t)curr->addr) + { + break; + } + } + + /* Did we find the region */ + + if (!curr) + { + ferr("ERROR: Region not found\n"); + ret = -EINVAL; + goto errout_with_semaphore; + } + + /* Get the offset from the beginning of the region and the actual number + * of bytes to "unmap". All mappings must extend to the end of the region. + * There is no support for free a block of memory but leaving a block of + * memory at the end. This is a consequence of using kumm_realloc() to + * simulate the unmapping. + */ + + offset = start - curr->addr; + if (offset + length < curr->length) + { + ferr("ERROR: Cannot umap without unmapping to the end\n"); + ret = -ENOSYS; + goto errout_with_semaphore; + } + + /* Okay.. the region is beging umapped to the end. Make sure the length + * indicates that. + */ + + length = curr->length - offset; + + /* Are we unmapping the entire region (offset == 0)? */ + + if (length >= curr->length) + { + /* Yes.. remove the mapping from the list */ + + if (prev) + { + prev->flink = curr->flink; + } + else + { + g_rammaps.head = curr->flink; + } + + /* Then free the region */ + + if (curr->kernel) + { + kmm_free(curr) + } + else + { + kumm_free(curr); + } + } + + /* No.. We have been asked to "unmap' only a portion of the memory + * (offset > 0). + */ + + else + { + if (kernel) + { + newaddr = kmm_realloc(curr->addr, + sizeof(struct fs_rammap_s) + length); + } + else + { + newaddr = kumm_realloc(curr->addr, + sizeof(struct fs_rammap_s) + length); + } + + DEBUGASSERT(newaddr == (FAR void *)(curr->addr)); + UNUSED(newaddr); /* May not be used */ + curr->length = length; + } + + nxsem_post(&g_rammaps.exclsem); + return OK; + +errout_with_semaphore: + nxsem_post(&g_rammaps.exclsem); + return ret; +#else + return OK; +#endif /* CONFIG_FS_RAMMAP */ +} + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: file_mummap + * + * Description: + * Equivalent to the standard file_mummap() function except it does not set + * the errno variable. + * + ****************************************************************************/ + +int file_munmap(FAR void *start, size_t length) +{ + return file_munmap_(start, length, true); +} + /**************************************************************************** * Name: munmap * @@ -93,110 +232,14 @@ int munmap(FAR void *start, size_t length) { -#ifdef CONFIG_FS_RAMMAP - FAR struct fs_rammap_s *prev; - FAR struct fs_rammap_s *curr; - FAR void *newaddr; - unsigned int offset; int ret; - int errcode; - /* Find a region containing this start and length in the list of regions */ - - ret = nxsem_wait(&g_rammaps.exclsem); + ret = file_munmap_(start, length, false); if (ret < 0) { - errcode = ret; - goto errout; + set_errno(-ret); + ret = ERROR; } - /* Search the list of regions */ - - for (prev = NULL, curr = g_rammaps.head; curr; - prev = curr, curr = curr->flink) - { - /* Does this region include any part of the specified range? */ - - if ((uintptr_t)start < (uintptr_t)curr->addr + curr->length && - (uintptr_t)start + length >= (uintptr_t)curr->addr) - { - break; - } - } - - /* Did we find the region */ - - if (!curr) - { - ferr("ERROR: Region not found\n"); - errcode = EINVAL; - goto errout_with_semaphore; - } - - /* Get the offset from the beginning of the region and the actual number - * of bytes to "unmap". All mappings must extend to the end of the region. - * There is no support for free a block of memory but leaving a block of - * memory at the end. This is a consequence of using kumm_realloc() to - * simulate the unmapping. - */ - - offset = start - curr->addr; - if (offset + length < curr->length) - { - ferr("ERROR: Cannot umap without unmapping to the end\n"); - errcode = ENOSYS; - goto errout_with_semaphore; - } - - /* Okay.. the region is beging umapped to the end. Make sure the length - * indicates that. - */ - - length = curr->length - offset; - - /* Are we unmapping the entire region (offset == 0)? */ - - if (length >= curr->length) - { - /* Yes.. remove the mapping from the list */ - - if (prev) - { - prev->flink = curr->flink; - } - else - { - g_rammaps.head = curr->flink; - } - - /* Then free the region */ - - kumm_free(curr); - } - - /* No.. We have been asked to "unmap' only a portion of the memory - * (offset > 0). - */ - - else - { - newaddr = kumm_realloc(curr->addr, - sizeof(struct fs_rammap_s) + length); - DEBUGASSERT(newaddr == (FAR void *)(curr->addr)); - UNUSED(newaddr); /* May not be used */ - curr->length = length; - } - - nxsem_post(&g_rammaps.exclsem); - return OK; - -errout_with_semaphore: - nxsem_post(&g_rammaps.exclsem); - -errout: - set_errno(errcode); - return ERROR; -#else - return OK; -#endif /* CONFIG_FS_RAMMAP */ + return ret; } diff --git a/fs/mmap/fs_rammap.c b/fs/mmap/fs_rammap.c index 2145a7a782e..30f5d610a96 100644 --- a/fs/mmap/fs_rammap.c +++ b/fs/mmap/fs_rammap.c @@ -62,14 +62,15 @@ struct fs_allmaps_s g_rammaps = * Support simulation of memory mapped files by copying files into RAM. * * Input Parameters: - * fd file descriptor of the backing file -- required. + * filep file descriptor of the backing file -- required. * length The length of the mapping. For exception #1 above, this length * ignored: The entire underlying media is always accessible. * offset The offset into the file to map + * kernel kmm_zalloc or kumm_zalloc + * mapped The pointer to the mapped area * * Returned Value: - * On success, rammmap() returns a pointer to the mapped area. On error, - * the value MAP_FAILED is returned, and errno is set appropriately. + * On success, rammmap returns 0. Otherwise errno is returned appropriately. * * EBADF * 'fd' is not a valid file descriptor. @@ -80,7 +81,8 @@ struct fs_allmaps_s g_rammaps = * ****************************************************************************/ -FAR void *rammap(int fd, size_t length, off_t offset) +int rammap(FAR struct file *filep, size_t length, + off_t offset, bool kernel, FAR void **mapped) { FAR struct fs_rammap_s *map; FAR uint8_t *alloc; @@ -104,12 +106,13 @@ FAR void *rammap(int fd, size_t length, off_t offset) /* Allocate a region of memory of the specified size */ - alloc = (FAR uint8_t *)kumm_malloc(sizeof(struct fs_rammap_s) + length); + alloc = (FAR uint8_t *)kernel ? + kmm_malloc(sizeof(struct fs_rammap_s) + length); + kumm_malloc(sizeof(struct fs_rammap_s) + length); if (!alloc) { ferr("ERROR: Region allocation failed, length: %d\n", (int)length); - ret = -ENOMEM; - goto errout; + return -ENOMEM; } /* Initialize the region */ @@ -122,7 +125,7 @@ FAR void *rammap(int fd, size_t length, off_t offset) /* Seek to the specified file offset */ - fpos = nx_seek(fd, offset, SEEK_SET); + fpos = file_seek(filep, offset, SEEK_SET); if (fpos < 0) { /* Seek failed... errno has already been set, but EINVAL is probably @@ -139,7 +142,7 @@ FAR void *rammap(int fd, size_t length, off_t offset) rdbuffer = map->addr; while (length > 0) { - nread = nx_read(fd, rdbuffer, length); + nread = file_read(filep, rdbuffer, length); if (nread < 0) { /* Handle the special case where the read was interrupted by a @@ -187,14 +190,20 @@ FAR void *rammap(int fd, size_t length, off_t offset) g_rammaps.head = map; nxsem_post(&g_rammaps.exclsem); - return map->addr; + *mapped = map->addr; + return OK; errout_with_region: - kumm_free(alloc); + if (kernel) + { + kmm_free(alloc); + } + else + { + kumm_free(alloc); + } -errout: - set_errno(-ret); - return MAP_FAILED; + return ret; } #endif /* CONFIG_FS_RAMMAP */ diff --git a/fs/mmap/fs_rammap.h b/fs/mmap/fs_rammap.h index 096c9c3fcfd..ec4fbbc87c7 100644 --- a/fs/mmap/fs_rammap.h +++ b/fs/mmap/fs_rammap.h @@ -88,14 +88,15 @@ extern struct fs_allmaps_s g_rammaps; * Support simulation of memory mapped files by copying files into RAM. * * Input Parameters: - * fd file descriptor of the backing file -- required. + * filep file descriptor of the backing file -- required. * length The length of the mapping. For exception #1 above, this length * ignored: The entire underlying media is always accessible. * offset The offset into the file to map + * kernel kmm_zalloc or kumm_zalloc + * mapped The pointer to the mapped area * * Returned Value: - * On success, rammmap() returns a pointer to the mapped area. On error, - * the value MAP_FAILED is returned, and errno is set appropriately. + * On success rammmap returns 0. Otherwise errno is returned appropriately. * * EBADF * 'fd' is not a valid file descriptor. @@ -106,7 +107,8 @@ extern struct fs_allmaps_s g_rammaps; * ****************************************************************************/ -FAR void *rammap(int fd, size_t length, off_t offset); +int rammap(FAR struct file *filep, size_t length, + off_t offset, bool kernel, FAR void **mapped); #endif /* CONFIG_FS_RAMMAP */ #endif /* __FS_MMAP_RAMMAP_H */ diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index 6a2a5572841..af6157e7582 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -1168,6 +1168,30 @@ int file_fsync(FAR struct file *filep); int file_truncate(FAR struct file *filep, off_t length); #endif +/**************************************************************************** + * Name: file_mmap + * + * Description: + * Equivalent to the standard mmap() function except that is accepts + * a struct file instance instead of a file descriptor and it does not set + * the errno variable. + * + ****************************************************************************/ + +int file_mmap(FAR struct file *filep, FAR void *start, size_t length, + int prot, int flags, off_t offset, FAR void **mapped); + +/**************************************************************************** + * Name: file_mummap + * + * Description: + * Equivalent to the standard mummap() function except it does not set + * the errno variable. + * + ****************************************************************************/ + +int file_munmap(FAR void *start, size_t length); + /**************************************************************************** * Name: file_ioctl *