userspace: fix x86 issue with adding partitions

On x86, if a supervisor thread belonging to a memory domain
adds a new partition to that domain, subsequent context switches
to another thread in the same domain, or dropping itself to user
mode, does not have the correct setup in the page tables.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2019-03-01 15:58:14 -08:00 committed by Anas Nashif
commit 6dc3fd8e50
5 changed files with 79 additions and 33 deletions

View file

@ -143,6 +143,12 @@ void _arch_mem_domain_destroy(struct k_mem_domain *domain)
arc_core_mpu_enable();
}
void _arch_mem_domain_partition_add(struct k_mem_domain *domain,
u32_t partition_id)
{
/* No-op on this architecture */
}
/*
* Validate the given buffer is user accessible or not
*/

View file

@ -314,6 +314,12 @@ void _arch_mem_domain_partition_remove(struct k_mem_domain *domain,
&domain->partitions[partition_id], &reset_attr);
}
void _arch_mem_domain_partition_add(struct k_mem_domain *domain,
u32_t partition_id)
{
/* No-op on this architecture */
}
/*
* Validate the given buffer is user accessible or not
*/

View file

@ -236,6 +236,24 @@ void z_x86_reset_pages(void *start, size_t size)
#endif /* CONFIG_X86_KPTI */
}
static inline void activate_partition(struct k_mem_partition *partition)
{
/* Set the partition attributes */
u64_t attr, mask;
#if CONFIG_X86_KPTI
attr = partition->attr | MMU_ENTRY_PRESENT;
mask = K_MEM_PARTITION_PERM_MASK | MMU_PTE_P_MASK;
#else
attr = partition->attr;
mask = K_MEM_PARTITION_PERM_MASK;
#endif /* CONFIG_X86_KPTI */
_x86_mmu_set_flags(&USER_PDPT,
(void *)partition->start,
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)
@ -245,7 +263,7 @@ static inline void _x86_mem_domain_pages_update(struct k_mem_domain *mem_domain,
{
u32_t partition_index;
u32_t total_partitions;
struct k_mem_partition partition;
struct k_mem_partition *partition;
u32_t partitions_count;
/* If mem_domain doesn't point to a valid location return.*/
@ -266,32 +284,19 @@ static inline void _x86_mem_domain_pages_update(struct k_mem_domain *mem_domain,
partition_index++) {
/* Get the partition info */
partition = mem_domain->partitions[partition_index];
if (partition.size == 0) {
partition = &mem_domain->partitions[partition_index];
if (partition->size == 0) {
continue;
}
partitions_count++;
if (page_conf == X86_MEM_DOMAIN_SET_PAGES) {
/* Set the partition attributes */
u64_t attr, mask;
#if CONFIG_X86_KPTI
attr = partition.attr | MMU_ENTRY_PRESENT;
mask = K_MEM_PARTITION_PERM_MASK | MMU_PTE_P_MASK;
#else
attr = partition.attr;
mask = K_MEM_PARTITION_PERM_MASK;
#endif /* CONFIG_X86_KPTI */
_x86_mmu_set_flags(&USER_PDPT,
(void *)partition.start,
partition.size, attr, mask);
activate_partition(partition);
} else {
z_x86_reset_pages((void *)partition.start,
partition.size);
z_x86_reset_pages((void *)partition->start,
partition->size);
}
}
out:
out:
return;
}
@ -312,21 +317,30 @@ void _arch_mem_domain_destroy(struct k_mem_domain *domain)
/* Reset/destroy one partition spcified in the argument of the API. */
void _arch_mem_domain_partition_remove(struct k_mem_domain *domain,
u32_t partition_id)
u32_t partition_id)
{
struct k_mem_partition partition;
if (domain == NULL) {
goto out;
}
struct k_mem_partition *partition;
__ASSERT_NO_MSG(domain != NULL);
__ASSERT(partition_id <= domain->num_partitions,
"invalid partitions");
partition = domain->partitions[partition_id];
z_x86_reset_pages((void *)partition.start, partition.size);
out:
return;
partition = &domain->partitions[partition_id];
z_x86_reset_pages((void *)partition->start, partition->size);
}
/* Reset/destroy one partition spcified in the argument of the API. */
void _arch_mem_domain_partition_add(struct k_mem_domain *domain,
u32_t partition_id)
{
struct k_mem_partition *partition;
__ASSERT_NO_MSG(domain != NULL);
__ASSERT(partition_id <= domain->num_partitions,
"invalid partitions");
partition = &domain->partitions[partition_id];
activate_partition(partition);
}
int _arch_mem_domain_max_partitions_get(void)

View file

@ -85,12 +85,27 @@ extern void _arch_mem_domain_configure(struct k_thread *thread);
* A memory domain contains multiple partitions and this API provides the
* freedom to remove a particular partition while keeping others intact.
* 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 partition_id The partition that needs to be deleted
*/
extern void _arch_mem_domain_partition_remove(struct k_mem_domain *domain,
u32_t partition_id);
u32_t partition_id);
/**
* @brief Remove a partition from the memory domain
*
* A memory domain contains multiple partitions and this API provides the
* freedom to add an additional partition to 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.
*
* @param domain The memory domain structure
* @param partition_id The partition that needs to be added
*/
extern void _arch_mem_domain_partition_add(struct k_mem_domain *domain,
u32_t partition_id);
/**
* @brief Remove the memory domain
@ -102,9 +117,7 @@ extern void _arch_mem_domain_partition_remove(struct k_mem_domain *domain,
* @param domain The memory domain structure which needs to be deleted.
*/
extern void _arch_mem_domain_destroy(struct k_mem_domain *domain);
#endif
#ifdef CONFIG_USERSPACE
/**
* @brief Check memory region permissions
*

View file

@ -178,6 +178,13 @@ void k_mem_domain_add_partition(struct k_mem_domain *domain,
domain->num_partitions++;
/* Handle architecture-specific remove
* only if it is the current thread.
*/
if (_current->mem_domain_info.mem_domain == domain) {
_arch_mem_domain_partition_add(domain, p_idx);
}
k_spin_unlock(&lock, key);
}