diff --git a/include/nuttx/mm/map.h b/include/nuttx/mm/map.h index f845054e349..ac7d0c442e6 100644 --- a/include/nuttx/mm/map.h +++ b/include/nuttx/mm/map.h @@ -44,7 +44,7 @@ struct task_group_s; struct mm_map_entry_s { FAR struct mm_map_entry *flink; /* this is used as sq_entry_t */ - FAR const void *vaddr; + FAR void *vaddr; size_t length; off_t offset; int prot; @@ -63,7 +63,7 @@ struct mm_map_entry_s */ int (*munmap)(FAR struct task_group_s *group, - FAR struct mm_map_entry_s *map, + FAR struct mm_map_entry_s *entry, FAR void *start, size_t length); }; @@ -73,11 +73,159 @@ struct mm_map_entry_s struct mm_map_s { sq_queue_t mm_map_sq; - mutex_t mm_map_mutex; + rmutex_t mm_map_mutex; }; /**************************************************************************** * Public Function Prototypes ****************************************************************************/ -#endif /* __INCLUDE_NUTTX_MM_MM_MAP_H */ +/**************************************************************************** + * Name: mm_map_lock + * + * Description: + * Get exclusive access current task_group's mm_map + * + * Input Parameters: + * None + * + * Returned Value: + * OK on success + * A negated errno value on failure + * + ****************************************************************************/ + +int mm_map_lock(void); + +/**************************************************************************** + * Name: mm_map_unlock + * + * Description: + * Relinquish exclusive access to current task_group's mm_map + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mm_map_unlock(void); + +/**************************************************************************** + * Name: mm_map_initialize + * + * Description: + * Initialization function, called only by group_initialize + * + * Input Parameters: + * mm - Pointer to the mm_map structure to be initialized + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mm_map_initialize(FAR struct mm_map_s *mm); + +/**************************************************************************** + * Name: mm_map_destroy + * + * Description: + * Uninitialization function, called only by group_release + * + * Input Parameters: + * mm - Pointer to the mm_map structure to be initialized + * + * Returned Value: + * None + * + ****************************************************************************/ + +void mm_map_destroy(FAR struct mm_map_s *mm); + +/**************************************************************************** + * Name: mm_map_add + * + * Description: + * Adds a virtual memory area into the list of mappings + * + * Input Parameters: + * entry - A pointer to mm_map_entry_s, mapping info to be added + * + * Returned Value: + * OK Added successfully + * -EINVAL: Invalid attempt to get the semaphore + * -EINTR: The wait was interrupted by the receipt of a signal. + * -ENOMEM: Out of memory + * + ****************************************************************************/ + +int mm_map_add(FAR struct mm_map_entry_s *entry); + +/**************************************************************************** + * Name: mm_map_next + * + * Description: + * Returns the next mapping in the list, following the argument. + * Can be used to iterate through all the mappings. Returns the first + * mapping when the argument "entry" is NULL. + * + * Input Parameters: + * entry - Pointer to a single mapping in this task group or NULL to get + * the first one + * + * Returned Value: + * Pointer to the next mapping + * + ****************************************************************************/ + +FAR struct mm_map_entry_s *mm_map_next( + FAR const struct mm_map_entry_s *entry); + +/**************************************************************************** + * Name: mm_map_find + * + * Description: + * Find the first mapping matching address and length + * + * Input Parameters: + * vaddr - Start address of the mapped area + * length - Length of the mapping + * + * Returned Value: + * Pointer to the mapping, NULL if not found + * + ****************************************************************************/ + +FAR struct mm_map_entry_s *mm_map_find(FAR const void *vaddr, + size_t length); + +/**************************************************************************** + * Name: mm_map_remove + * + * Description: + * Removes a virtual memory area from the list of mappings + * Sets the given pointer argument to NULL after successful removal + * + * Input Parameters: + * mm - Pointer to the list of entries, from which the entry is + * removed. If passed mm is NULL, the function doesn't do + * anything, but just returns OK. + * + * entry - Pointer to the entry to be removed. If the passed entry is + * NULL the function doesn't do anything but just returns OK + * + * Returned Value: + * OK: Removed successfully + * -EINVAL: Invalid attempt to get the semaphore + * -EINTR: The wait was interrupted by the receipt of a signal. + * -ENOENT: Memory area not found + * + ****************************************************************************/ + +int mm_map_remove(FAR struct mm_map_s *mm, + FAR struct mm_map_entry_s *entry); + +#endif /* __INCLUDE_NUTTX_MM_MAP_H */ diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index bf8db3ff819..ce450a8137a 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -180,6 +181,14 @@ # define TCB_REG_OFF(reg) (reg * sizeof(uint32_t)) #endif +/* Get a pointer to the process' memory map struct from the task_group */ + +#define get_group_mm(group) (group ? &group->tg_mm_map : NULL) + +/* Get a pointer to current the process' memory map struct */ + +#define get_current_mm() (get_group_mm(nxsched_self()->group)) + /**************************************************************************** * Public Type Definitions ****************************************************************************/ @@ -506,6 +515,10 @@ struct task_group_s struct group_shm_s tg_shm; /* Task shared memory logic */ #endif + + /* Virtual memory mapping info ********************************************/ + + struct mm_map_s tg_mm_map; /* Task mmappings */ }; /* struct tcb_s *************************************************************/ diff --git a/mm/Makefile b/mm/Makefile index 1d1d5f24135..a9d0c2272b2 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -32,6 +32,7 @@ include circbuf/Make.defs include mempool/Make.defs include kasan/Make.defs include ubsan/Make.defs +include map/Make.defs BINDIR ?= bin diff --git a/mm/map/Make.defs b/mm/map/Make.defs new file mode 100644 index 00000000000..1d1f602d2bf --- /dev/null +++ b/mm/map/Make.defs @@ -0,0 +1,28 @@ +############################################################################ +# mm/map/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +# Virtual memory map list support + +CSRCS += mm_map.c + +# Add the shared memory directory to the build + +DEPPATH += --dep-path map +VPATH += :map diff --git a/mm/map/mm_map.c b/mm/map/mm_map.c new file mode 100644 index 00000000000..22126bb4b7b --- /dev/null +++ b/mm/map/mm_map.c @@ -0,0 +1,315 @@ +/**************************************************************************** + * mm/map/mm_map.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static bool in_range(FAR const void *start, size_t length, + FAR const void *range_start, size_t range_length) +{ + FAR const char *u_start = (FAR const char *)start; + FAR const char *u_end = u_start + length; + FAR const char *r_start = (FAR const char *)range_start; + FAR const char *r_end = r_start + range_length; + + return (u_start >= r_start && u_start < r_end && /* Start is in range. */ + u_end >= r_start && u_end <= r_end); /* End is in range. */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mm_map_lock + * + * Description: + * Get exclusive access to task_group's mm_map + * + ****************************************************************************/ + +int mm_map_lock(void) +{ + return nxrmutex_lock(&get_current_mm()->mm_map_mutex); +} + +/**************************************************************************** + * Name: mm_map_unlock + * + * Description: + * Relinquish exclusive access to task_group's mm_map + * + ****************************************************************************/ + +void mm_map_unlock(void) +{ + DEBUGVERIFY(nxrmutex_unlock(&get_current_mm()->mm_map_mutex)); +} + +/**************************************************************************** + * Name: mm_map_initialize + * + * Description: + * Allocates a task group specific mm_map stucture. Called when the group + * is initialized + * + ****************************************************************************/ + +void mm_map_initialize(FAR struct mm_map_s *mm) +{ + sq_init(&mm->mm_map_sq); + nxrmutex_init(&mm->mm_map_mutex); +} + +/**************************************************************************** + * Name: mm_map_destroy + * + * Description: + * De-allocates a task group specific mm_map stucture and the mm_map_mutex + * + ****************************************************************************/ + +void mm_map_destroy(FAR struct mm_map_s *mm) +{ + FAR struct mm_map_entry_s *entry; + + while ((entry = (FAR struct mm_map_entry_s *)sq_remfirst(&mm->mm_map_sq))) + { + /* Pass null as group argument to indicate that actual MMU mappings + * must not be touched. The process is being deleted and we don't + * know in which context we are. Only kernel memory allocations + * need to be freed by drivers + */ + + /* Unmap the whole region */ + + if (entry->munmap) + { + if (entry->munmap(NULL, entry, entry->vaddr, entry->length) < 0) + { + /* This would be an error in the driver. It has defined munmap, + * but is not able to munmap the full area which it has mapped + */ + + merr("Driver munmap failed\n"); + } + } + + kmm_free(entry); + } + + nxrmutex_destroy(&mm->mm_map_mutex); +} + +/**************************************************************************** + * Name: mm_map_add + * + * Description: + * Add a mapping to task group's mm_map list + * + ****************************************************************************/ + +int mm_map_add(FAR struct mm_map_entry_s *entry) +{ + FAR struct mm_map_s *mm = get_current_mm(); + FAR struct mm_map_entry_s *new_entry; + int ret; + + if (!entry) + { + return -EINVAL; + } + + /* Copy the provided mapping and add to the list */ + + new_entry = kmm_malloc(sizeof(struct mm_map_entry_s)); + if (!new_entry) + { + return -EINVAL; + } + + *new_entry = *entry; + + ret = nxrmutex_lock(&mm->mm_map_mutex); + if (ret < 0) + { + kmm_free(new_entry); + return ret; + } + + sq_addfirst((sq_entry_t *)new_entry, &mm->mm_map_sq); + + nxrmutex_unlock(&mm->mm_map_mutex); + + return OK; +} + +/**************************************************************************** + * Name: mm_map_next + * + * Description: + * Returns the next mapping in the list. + * + ****************************************************************************/ + +FAR struct mm_map_entry_s *mm_map_next( + FAR const struct mm_map_entry_s *entry) +{ + FAR struct mm_map_s *mm = get_current_mm(); + FAR struct mm_map_entry_s *next_entry = NULL; + + if (nxrmutex_lock(&mm->mm_map_mutex) == OK) + { + if (entry == NULL) + { + next_entry = (struct mm_map_entry_s *)sq_peek(&mm->mm_map_sq); + } + else + { + next_entry = (struct mm_map_entry_s *) + sq_next(((sq_entry_t *)entry)); + } + + nxrmutex_unlock(&mm->mm_map_mutex); + } + + return next_entry; +} + +/**************************************************************************** + * Name: mm_map_find + * + * Description: + * Find the first mapping containing the range from the task group's list + * + ****************************************************************************/ + +FAR struct mm_map_entry_s *mm_map_find(FAR const void *vaddr, size_t length) +{ + FAR struct mm_map_s *mm = get_current_mm(); + FAR struct mm_map_entry_s *found_entry = NULL; + + if (nxrmutex_lock(&mm->mm_map_mutex) == OK) + { + found_entry = (struct mm_map_entry_s *)sq_peek(&mm->mm_map_sq); + + while (found_entry && !in_range(vaddr, length, + found_entry->vaddr, + found_entry->length)) + { + found_entry = (struct mm_map_entry_s *) + sq_next(((sq_entry_t *)found_entry)); + } + + nxrmutex_unlock(&mm->mm_map_mutex); + } + + return found_entry; +} + +/**************************************************************************** + * Name: mm_map_remove + * + * Description: + * Remove a mapping from the task group's list + * + ****************************************************************************/ + +int mm_map_remove(FAR struct mm_map_s *mm, + FAR struct mm_map_entry_s *entry) +{ + FAR struct mm_map_entry_s *prev_entry; + FAR struct mm_map_entry_s *removed_entry = NULL; + int ret; + + if (!mm || !entry) + { + return OK; + } + + ret = nxrmutex_lock(&mm->mm_map_mutex); + if (ret < 0) + { + return ret; + } + + prev_entry = (struct mm_map_entry_s *)sq_peek(&mm->mm_map_sq); + + /* Check if the list was empty */ + + if (!prev_entry) + { + nxrmutex_unlock(&mm->mm_map_mutex); + return -ENOENT; + } + + /* Check if removing the first item */ + + if (entry == prev_entry) + { + sq_remfirst(&mm->mm_map_sq); + removed_entry = prev_entry; + } + else + { + /* Loop through the remaining items to find the one to be removed */ + + while ((removed_entry = (struct mm_map_entry_s *) + sq_next(((sq_entry_t *)prev_entry)))) + { + if (entry == removed_entry) + { + sq_remafter((sq_entry_t *)prev_entry, &mm->mm_map_sq); + break; + } + + prev_entry = removed_entry; + } + } + + nxrmutex_unlock(&mm->mm_map_mutex); + + /* If the item was removed, also delete the entry struct */ + + if (removed_entry) + { + kmm_free(removed_entry); + return OK; + } + + return -ENOENT; +} + +#endif /* defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) */ diff --git a/sched/group/group_create.c b/sched/group/group_create.c index fe7533629a6..ac31f659f9e 100644 --- a/sched/group/group_create.c +++ b/sched/group/group_create.c @@ -245,6 +245,10 @@ void group_initialize(FAR struct task_tcb_s *tcb) DEBUGASSERT(tcb && tcb->cmn.group); group = tcb->cmn.group; + /* Allocate mm_map list if required */ + + mm_map_initialize(&group->tg_mm_map); + #ifdef HAVE_GROUP_MEMBERS /* Assign the PID of this new task as a member of the group. */ diff --git a/sched/group/group_leave.c b/sched/group/group_leave.c index 2b1d11fdc0e..4aa4797e781 100644 --- a/sched/group/group_leave.c +++ b/sched/group/group_leave.c @@ -176,6 +176,10 @@ static inline void group_release(FAR struct task_group_s *group) env_release(group); #endif + /* Destroy the mm_map list */ + + mm_map_destroy(&group->tg_mm_map); + #if defined(CONFIG_BUILD_KERNEL) && defined(CONFIG_MM_SHM) /* Release any resource held by shared memory virtual page allocator */