userspace: adjust arch memory domain interface

The current API was assuming too much, in that it expected that
arch-specific memory domain configuration is only maintained
in some global area, and updates to domains that are not currently
active have no effect.

This was true when all memory domain state was tracked in page
tables or MPU registers, but no longer works when arch-specific
memory management information is stored in thread-specific areas.

This is needed for: #13441 #13074 #15135

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2019-07-25 18:18:03 -07:00 committed by Carles Cufí
commit 8915e41b7b
7 changed files with 131 additions and 67 deletions

View file

@ -9,6 +9,7 @@
#include <kernel.h> #include <kernel.h>
#include <soc.h> #include <soc.h>
#include <arch/arc/v2/mpu/arc_core_mpu.h> #include <arch/arc/v2/mpu/arc_core_mpu.h>
#include <kernel_structs.h>
/* /*
* @brief Configure MPU for the thread * @brief Configure MPU for the thread
@ -37,6 +38,10 @@ int z_arch_mem_domain_max_partitions_get(void)
void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain, void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
u32_t partition_id) u32_t partition_id)
{ {
if (_current->mem_domain_info.mem_domain != domain) {
return;
}
arc_core_mpu_disable(); arc_core_mpu_disable();
arc_core_mpu_remove_mem_partition(domain, partition_id); arc_core_mpu_remove_mem_partition(domain, partition_id);
arc_core_mpu_enable(); arc_core_mpu_enable();
@ -45,8 +50,12 @@ void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
/* /*
* Configure MPU memory domain * Configure MPU memory domain
*/ */
void z_arch_mem_domain_configure(struct k_thread *thread) void z_arch_mem_domain_thread_add(struct k_thread *thread)
{ {
if (_current != thread) {
return;
}
arc_core_mpu_disable(); arc_core_mpu_disable();
arc_core_mpu_configure_mem_domain(thread); arc_core_mpu_configure_mem_domain(thread);
arc_core_mpu_enable(); arc_core_mpu_enable();
@ -57,6 +66,10 @@ void z_arch_mem_domain_configure(struct k_thread *thread)
*/ */
void z_arch_mem_domain_destroy(struct k_mem_domain *domain) void z_arch_mem_domain_destroy(struct k_mem_domain *domain)
{ {
if (_current->mem_domain_info.mem_domain != domain) {
return;
}
arc_core_mpu_disable(); arc_core_mpu_disable();
arc_core_mpu_remove_mem_domain(domain); arc_core_mpu_remove_mem_domain(domain);
arc_core_mpu_enable(); arc_core_mpu_enable();
@ -68,6 +81,15 @@ void z_arch_mem_domain_partition_add(struct k_mem_domain *domain,
/* No-op on this architecture */ /* No-op on this architecture */
} }
void z_arch_mem_domain_thread_remove(struct k_thread *thread)
{
if (_current != thread) {
return;
}
z_arch_mem_domain_destroy(thread->mem_domain_info.mem_domain);
}
/* /*
* Validate the given buffer is user accessible or not * Validate the given buffer is user accessible or not
*/ */

View file

@ -8,6 +8,7 @@
#include <init.h> #include <init.h>
#include <kernel.h> #include <kernel.h>
#include <soc.h> #include <soc.h>
#include <kernel_structs.h>
#include "arm_core_mpu_dev.h" #include "arm_core_mpu_dev.h"
#include <linker/linker-defs.h> #include <linker/linker-defs.h>
@ -282,8 +283,12 @@ int z_arch_mem_domain_max_partitions_get(void)
/** /**
* @brief Configure the memory domain of the thread. * @brief Configure the memory domain of the thread.
*/ */
void z_arch_mem_domain_configure(struct k_thread *thread) void z_arch_mem_domain_thread_add(struct k_thread *thread)
{ {
if (_current != thread) {
return;
}
/* Request to configure memory domain for a thread. /* Request to configure memory domain for a thread.
* This triggers re-programming of the entire dynamic * This triggers re-programming of the entire dynamic
* memory map. * memory map.
@ -304,6 +309,11 @@ void z_arch_mem_domain_destroy(struct k_mem_domain *domain)
*/ */
int i; int i;
struct k_mem_partition partition; struct k_mem_partition partition;
if (_current->mem_domain_info.mem_domain != domain) {
return;
}
/* Partitions belonging to the memory domain will be reset /* Partitions belonging to the memory domain will be reset
* to default (Privileged RW, Unprivileged NA) permissions. * to default (Privileged RW, Unprivileged NA) permissions.
*/ */
@ -338,6 +348,10 @@ void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
*/ */
k_mem_partition_attr_t reset_attr = K_MEM_PARTITION_P_RW_U_NA; k_mem_partition_attr_t reset_attr = K_MEM_PARTITION_P_RW_U_NA;
if (_current->mem_domain_info.mem_domain != domain) {
return;
}
arm_core_mpu_mem_partition_config_update( arm_core_mpu_mem_partition_config_update(
&domain->partitions[partition_id], &reset_attr); &domain->partitions[partition_id], &reset_attr);
} }
@ -348,6 +362,15 @@ void z_arch_mem_domain_partition_add(struct k_mem_domain *domain,
/* No-op on this architecture */ /* No-op on this architecture */
} }
void z_arch_mem_domain_thread_remove(struct k_thread *thread)
{
if (_current != thread) {
return;
}
z_arch_mem_domain_destroy(thread->mem_domain_info.mem_domain);
}
/* /*
* Validate the given buffer is user accessible or not * Validate the given buffer is user accessible or not
*/ */

View file

@ -159,8 +159,10 @@ void _x86_swap_update_page_tables(struct k_thread *incoming,
/* Ensure that the outgoing mem domain configuration /* Ensure that the outgoing mem domain configuration
* is set back to default state. * is set back to default state.
*/ */
z_arch_mem_domain_destroy(outgoing->mem_domain_info.mem_domain); z_x86_mem_domain_pages_update(outgoing->mem_domain_info.mem_domain,
z_arch_mem_domain_configure(incoming); X86_MEM_DOMAIN_RESET_PAGES);
z_x86_mem_domain_pages_update(incoming->mem_domain_info.mem_domain,
X86_MEM_DOMAIN_SET_PAGES);
} }
} }

View file

@ -8,6 +8,7 @@
#include <ia32/mmustructs.h> #include <ia32/mmustructs.h>
#include <linker/linker-defs.h> #include <linker/linker-defs.h>
#include <kernel_internal.h> #include <kernel_internal.h>
#include <kernel_structs.h>
#include <init.h> #include <init.h>
#include <ctype.h> #include <ctype.h>
@ -377,11 +378,8 @@ static inline void activate_partition(struct k_mem_partition *partition)
partition->size, attr, mask); partition->size, attr, mask);
} }
/* Helper macros needed to be passed to x86_update_mem_domain_pages */
#define X86_MEM_DOMAIN_SET_PAGES (0U)
#define X86_MEM_DOMAIN_RESET_PAGES (1U)
/* Pass 1 to page_conf if reset of mem domain pages is needed else pass a 0*/ /* Pass 1 to page_conf if reset of mem domain pages is needed else pass a 0*/
static inline void x86_mem_domain_pages_update(struct k_mem_domain *mem_domain, void z_x86_mem_domain_pages_update(struct k_mem_domain *mem_domain,
u32_t page_conf) u32_t page_conf)
{ {
u32_t partition_index; u32_t partition_index;
@ -424,9 +422,13 @@ out:
} }
/* Load the partitions of the thread. */ /* Load the partitions of the thread. */
void z_arch_mem_domain_configure(struct k_thread *thread) void z_arch_mem_domain_thread_add(struct k_thread *thread)
{ {
x86_mem_domain_pages_update(thread->mem_domain_info.mem_domain, if (_current != thread) {
return;
}
z_x86_mem_domain_pages_update(thread->mem_domain_info.mem_domain,
X86_MEM_DOMAIN_SET_PAGES); X86_MEM_DOMAIN_SET_PAGES);
} }
@ -435,7 +437,20 @@ void z_arch_mem_domain_configure(struct k_thread *thread)
*/ */
void z_arch_mem_domain_destroy(struct k_mem_domain *domain) void z_arch_mem_domain_destroy(struct k_mem_domain *domain)
{ {
x86_mem_domain_pages_update(domain, X86_MEM_DOMAIN_RESET_PAGES); if (_current->mem_domain_info.mem_domain != domain) {
return;
}
z_x86_mem_domain_pages_update(domain, X86_MEM_DOMAIN_RESET_PAGES);
}
void z_arch_mem_domain_thread_remove(struct k_thread *thread)
{
if (_current != thread) {
return;
}
z_arch_mem_domain_destroy(thread->mem_domain_info.mem_domain);
} }
/* Reset/destroy one partition specified in the argument of the API. */ /* Reset/destroy one partition specified in the argument of the API. */
@ -444,6 +459,10 @@ void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
{ {
struct k_mem_partition *partition; struct k_mem_partition *partition;
if (_current->mem_domain_info.mem_domain != domain) {
return;
}
__ASSERT_NO_MSG(domain != NULL); __ASSERT_NO_MSG(domain != NULL);
__ASSERT(partition_id <= domain->num_partitions, __ASSERT(partition_id <= domain->num_partitions,
"invalid partitions"); "invalid partitions");
@ -458,6 +477,10 @@ void z_arch_mem_domain_partition_add(struct k_mem_domain *domain,
{ {
struct k_mem_partition *partition; struct k_mem_partition *partition;
if (_current->mem_domain_info.mem_domain != domain) {
return;
}
__ASSERT_NO_MSG(domain != NULL); __ASSERT_NO_MSG(domain != NULL);
__ASSERT(partition_id <= domain->num_partitions, __ASSERT(partition_id <= domain->num_partitions,
"invalid partitions"); "invalid partitions");

View file

@ -81,6 +81,13 @@ extern FUNC_NORETURN void z_x86_userspace_enter(k_thread_entry_t user_entry,
u32_t stack_end, u32_t stack_end,
u32_t stack_start); u32_t stack_start);
/* Helper macros needed to be passed to x86_update_mem_domain_pages */
#define X86_MEM_DOMAIN_SET_PAGES (0U)
#define X86_MEM_DOMAIN_RESET_PAGES (1U)
extern void z_x86_mem_domain_pages_update(struct k_mem_domain *mem_domain,
u32_t page_conf);
#include <stddef.h> /* For size_t */ #include <stddef.h> /* For size_t */
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -73,40 +73,48 @@ extern int z_arch_float_disable(struct k_thread *thread);
/** /**
* @brief Get the maximum number of partitions for a memory domain * @brief Get the maximum number of partitions for a memory domain
* *
* A memory domain is a container data structure containing some number of * @return Max number of partitions, or -1 if there is no limit
* memory partitions, where each partition represents a memory range with
* access policies.
*
* MMU-based systems don't have a limit here, but MPU-based systems will
* have an upper bound on how many different regions they can manage
* simultaneously.
*
* @return Max number of free regions, or -1 if there is no limit
*/ */
extern int z_arch_mem_domain_max_partitions_get(void); extern int z_arch_mem_domain_max_partitions_get(void);
/** /**
* @brief Configure the memory domain of the thread. * @brief Add a thread to a memory domain (arch-specific)
* *
* A memory domain is a container data structure containing some number of * Architecture-specific hook to manage internal data structures or hardware
* memory partitions, where each partition represents a memory range with * state when the provided thread has been added to a memory domain.
* access policies. This api will configure the appropriate hardware *
* registers to make it work. * The thread's memory domain pointer will be set to the domain to be added
* to.
* *
* @param thread Thread which needs to be configured. * @param thread Thread which needs to be configured.
*/ */
extern void z_arch_mem_domain_configure(struct k_thread *thread); extern void z_arch_mem_domain_thread_add(struct k_thread *thread);
/** /**
* @brief Remove a partition from the memory domain * @brief Remove a thread from a memory domain (arch-specific)
* *
* A memory domain contains multiple partitions and this API provides the * Architecture-specific hook to manage internal data structures or hardware
* freedom to remove a particular partition while keeping others intact. * state when the provided thread has been removed from a memory domain.
* This API will handle any arch/HW specific changes that needs to be done. *
* Only called if the active thread's domain was modified. * The thread's memory domain pointer will be the domain that the thread
* is being removed from.
*
* @param thread Thread being removed from its memory domain
*/
extern void z_arch_mem_domain_thread_remove(struct k_thread *thread);
/**
* @brief Remove a partition from the memory domain (arch-specific)
*
* Architecture-specific hook to manage internal data structures or hardware
* state when a memory domain has had a partition removed.
*
* The partition index data, and the number of partitions configured, are not
* respectively cleared and decremented in the domain until after this function
* runs.
* *
* @param domain The memory domain structure * @param domain The memory domain structure
* @param partition_id The partition that needs to be deleted * @param partition_id The partition index that needs to be deleted
*/ */
extern void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain, extern void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
u32_t partition_id); u32_t partition_id);
@ -114,10 +122,8 @@ extern void z_arch_mem_domain_partition_remove(struct k_mem_domain *domain,
/** /**
* @brief Add a partition to the memory domain * @brief Add a partition to the memory domain
* *
* A memory domain contains multiple partitions and this API provides the * Architecture-specific hook to manage internal data structures or hardware
* freedom to add an additional partition to a memory domain. * state when a memory domain has a partition added.
* This API will handle any arch/HW specific changes that needs to be done.
* Only called if the active thread's domain was modified.
* *
* @param domain The memory domain structure * @param domain The memory domain structure
* @param partition_id The partition that needs to be added * @param partition_id The partition that needs to be added
@ -128,9 +134,11 @@ extern void z_arch_mem_domain_partition_add(struct k_mem_domain *domain,
/** /**
* @brief Remove the memory domain * @brief Remove the memory domain
* *
* A memory domain contains multiple partitions and this API will traverse * Architecture-specific hook to manage internal data structures or hardware
* all these to reset them back to default setting. * state when a memory domain has been destroyed.
* This API will handle any arch/HW specific changes that needs to be done. *
* Thread assignments to the memory domain are only cleared after this function
* runs.
* *
* @param domain The memory domain structure which needs to be deleted. * @param domain The memory domain structure which needs to be deleted.
*/ */

View file

@ -129,12 +129,7 @@ void k_mem_domain_destroy(struct k_mem_domain *domain)
key = k_spin_lock(&lock); key = k_spin_lock(&lock);
/* Handle architecture-specific destroy
* only if it is the current thread.
*/
if (_current->mem_domain_info.mem_domain == domain) {
z_arch_mem_domain_destroy(domain); z_arch_mem_domain_destroy(domain);
}
SYS_DLIST_FOR_EACH_NODE_SAFE(&domain->mem_domain_q, node, next_node) { SYS_DLIST_FOR_EACH_NODE_SAFE(&domain->mem_domain_q, node, next_node) {
struct k_thread *thread = struct k_thread *thread =
@ -181,13 +176,7 @@ void k_mem_domain_add_partition(struct k_mem_domain *domain,
domain->num_partitions++; domain->num_partitions++;
/* Handle architecture-specific add
* only if it is the current thread.
*/
if (_current->mem_domain_info.mem_domain == domain) {
z_arch_mem_domain_partition_add(domain, p_idx); z_arch_mem_domain_partition_add(domain, p_idx);
}
k_spin_unlock(&lock, key); k_spin_unlock(&lock, key);
} }
@ -213,12 +202,7 @@ void k_mem_domain_remove_partition(struct k_mem_domain *domain,
/* Assert if not found */ /* Assert if not found */
__ASSERT(p_idx < max_partitions, "no matching partition found"); __ASSERT(p_idx < max_partitions, "no matching partition found");
/* Handle architecture-specific remove
* only if it is the current thread.
*/
if (_current->mem_domain_info.mem_domain == domain) {
z_arch_mem_domain_partition_remove(domain, p_idx); z_arch_mem_domain_partition_remove(domain, p_idx);
}
/* A zero-sized partition denotes it's a free partition */ /* A zero-sized partition denotes it's a free partition */
domain->partitions[p_idx].size = 0U; domain->partitions[p_idx].size = 0U;
@ -243,9 +227,7 @@ void k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread)
&thread->mem_domain_info.mem_domain_q_node); &thread->mem_domain_info.mem_domain_q_node);
thread->mem_domain_info.mem_domain = domain; thread->mem_domain_info.mem_domain = domain;
if (_current == thread) { z_arch_mem_domain_thread_add(thread);
z_arch_mem_domain_configure(thread);
}
k_spin_unlock(&lock, key); k_spin_unlock(&lock, key);
} }
@ -258,13 +240,10 @@ void k_mem_domain_remove_thread(k_tid_t thread)
__ASSERT(thread->mem_domain_info.mem_domain != NULL, "mem domain set"); __ASSERT(thread->mem_domain_info.mem_domain != NULL, "mem domain set");
key = k_spin_lock(&lock); key = k_spin_lock(&lock);
if (_current == thread) { z_arch_mem_domain_thread_remove(thread);
z_arch_mem_domain_destroy(thread->mem_domain_info.mem_domain);
}
sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node); sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node);
thread->mem_domain_info.mem_domain = NULL; thread->mem_domain_info.mem_domain = NULL;
k_spin_unlock(&lock, key); k_spin_unlock(&lock, key);
} }