FAR void *rammap(int fd, size_t length, off_t offset) { FAR struct fs_rammap_s *map; FAR uint8_t *alloc; FAR uint8_t *rdbuffer; ssize_t nread; off_t fpos; int errcode; int ret; /* There is a major design flaw that I have not yet thought of fix for: * The goal is to have a single region of memory that represents a single * file and can be shared by many threads. That is, given a filename a * thread should be able to open the file, get a file descriptor, and * call mmap() to get a memory region. Different file descriptors opened * with the same file path should get the same memory region when mapped. * * The design flaw is that I don't have sufficient knowledge to know that * these different file descriptors map to the same file. So, for the time * being, a new memory region is created each time that rammap() is called. * Not very useful! */ /* Allocate a region of memory of the specified size */ alloc = (FAR uint8_t *)kumm_malloc(sizeof(struct fs_rammap_s) + length); if (!alloc) { ferr("ERROR: Region allocation failed, length: %d\n", (int)length); errcode = ENOMEM; goto errout; } /* Initialize the region */ map = (FAR struct fs_rammap_s *)alloc; memset(map, 0, sizeof(struct fs_rammap_s)); map->addr = alloc + sizeof(struct fs_rammap_s); map->length = length; map->offset = offset; /* Seek to the specified file offset */ fpos = lseek(fd, offset, SEEK_SET); if (fpos == (off_t)-1) { /* Seek failed... errno has already been set, but EINVAL is probably * the correct response. */ ferr("ERROR: Seek to position %d failed\n", (int)offset); errcode = EINVAL; goto errout_with_region; } /* Read the file data into the memory region */ rdbuffer = map->addr; while (length > 0) { nread = read(fd, rdbuffer, length); if (nread < 0) { /* Handle the special case where the read was interrupted by a * signal. */ errcode = get_errno(); if (errcode != EINTR) { /* All other read errors are bad. errno is already set. * (but maybe should be forced to EINVAL?). NOTE that if * FS DEBUG is enabled, then the following ferr() macro will * destroy the errno value. */ ferr("ERROR: Read failed: offset=%d errno=%d\n", (int)offset, errcode); #ifdef CONFIG_DEBUG_FS goto errout_with_region; #else goto errout_with_errno; #endif } } /* Check for end of file. */ if (nread == 0) { break; } /* Increment number of bytes read */ rdbuffer += nread; length -= nread; } /* Zero any memory beyond the amount read from the file */ memset(rdbuffer, 0, length); /* Add the buffer to the list of regions */ rammap_initialize(); ret = sem_wait(&g_rammaps.exclsem); if (ret < 0) { goto errout_with_errno; } map->flink = g_rammaps.head; g_rammaps.head = map; sem_post(&g_rammaps.exclsem); return map->addr; errout_with_region: kumm_free(alloc); errout: set_errno(errcode); return MAP_FAILED; errout_with_errno: kumm_free(alloc); return MAP_FAILED; }
int munmap(FAR void *start, size_t length) { FAR struct fs_rammap_s *prev; FAR struct fs_rammap_s *curr; FAR void *newaddr; unsigned int offset; int ret; int err; /* Find a region containing this start and length in the list of regions */ rammap_initialize(); ret = sem_wait(&g_rammaps.exclsem); if (ret < 0) { return ERROR; } /* Seach 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) { fdbg("Region not found\n"); err = 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) { fdbg("Cannot umap without unmapping to the end\n"); err = 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)); curr->length = length; } sem_post(&g_rammaps.exclsem); return OK; errout_with_semaphore: sem_post(&g_rammaps.exclsem); set_errno(err); return ERROR; }