zephyr/kernel/mem_domain.c
Chunlin Han e9c9702818 kernel: add memory domain APIs
Add the following application-facing memory domain APIs:

k_mem_domain_init() - to initialize a memory domain
k_mem_domain_destroy() - to destroy a memory domain
k_mem_domain_add_partition() - to add a partition into a domain
k_mem_domain_remove_partition() - to remove a partition from a domain
k_mem_domain_add_thread() - to add a thread into a domain
k_mem_domain_remove_thread() - to remove a thread from a domain

A memory domain would contain some number of memory partitions.
A memory partition is a memory region (might be RAM, peripheral
registers, flash...) with specific attributes (access permission,
e.g. privileged read/write, unprivileged read-only, execute never...).
Memory partitions would be defined by set of MPU regions or MMU tables
underneath.
A thread could only belong to a single memory domain any point in time
but a memory domain could contain multiple threads.
Threads in the same memory domain would have the same access permission
to the memory partitions belong to the memory domain.

The memory domain APIs are used by unprivileged threads to share data
to the threads in the same memory and protect sensitive data from
threads outside their domain. It is not only for improving the security
but also useful for debugging (unexpected access would cause exception).

Jira: ZEP-2281

Signed-off-by: Chunlin Han <chunlin.han@linaro.org>
2017-09-29 16:48:53 -07:00

169 lines
3.5 KiB
C

/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <init.h>
#include <kernel.h>
#include <kernel_structs.h>
#include <nano_internal.h>
static u8_t max_partitions;
void k_mem_domain_init(struct k_mem_domain *domain, u32_t num_parts,
struct k_mem_partition *parts[])
{
unsigned int key;
__ASSERT(domain && !num_parts || parts, "");
__ASSERT(num_parts <= max_partitions, "");
key = irq_lock();
domain->num_partitions = num_parts;
memset(domain->partitions, 0, sizeof(domain->partitions));
if (num_parts) {
u32_t i;
for (i = 0; i < num_parts; i++) {
__ASSERT(parts[i], "");
domain->partitions[i] = *parts[i];
}
}
sys_dlist_init(&domain->mem_domain_q);
irq_unlock(key);
}
void k_mem_domain_destroy(struct k_mem_domain *domain)
{
unsigned int key;
sys_dnode_t *node, *next_node;
__ASSERT(domain, "");
key = irq_lock();
SYS_DLIST_FOR_EACH_NODE_SAFE(&domain->mem_domain_q, node, next_node) {
struct k_thread *thread =
CONTAINER_OF(node, struct k_thread, mem_domain_info);
sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node);
thread->mem_domain_info.mem_domain = NULL;
}
irq_unlock(key);
}
void k_mem_domain_add_partition(struct k_mem_domain *domain,
struct k_mem_partition *part)
{
int p_idx;
unsigned int key;
__ASSERT(domain && part, "");
__ASSERT(part->start + part->size > part->start, "");
key = irq_lock();
for (p_idx = 0; p_idx < max_partitions; p_idx++) {
/* A zero-sized partition denotes it's a free partition */
if (domain->partitions[p_idx].size == 0) {
break;
}
}
/* Assert if there is no free partition */
__ASSERT(p_idx < max_partitions, "");
domain->partitions[p_idx].start = part->start;
domain->partitions[p_idx].size = part->size;
domain->partitions[p_idx].attr = part->attr;
domain->num_partitions++;
irq_unlock(key);
}
void k_mem_domain_remove_partition(struct k_mem_domain *domain,
struct k_mem_partition *part)
{
int p_idx;
unsigned int key;
__ASSERT(domain && part, "");
key = irq_lock();
/* find a partition that matches the given start and size */
for (p_idx = 0; p_idx < max_partitions; p_idx++) {
if (domain->partitions[p_idx].start == part->start &&
domain->partitions[p_idx].size == part->size) {
break;
}
}
/* Assert if not found */
__ASSERT(p_idx < max_partitions, "");
domain->partitions[p_idx].start = 0;
domain->partitions[p_idx].size = 0;
domain->partitions[p_idx].attr = 0;
domain->num_partitions--;
irq_unlock(key);
}
void k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread)
{
unsigned int key;
__ASSERT(domain && thread && !thread->mem_domain_info.mem_domain, "");
key = irq_lock();
sys_dlist_append(&domain->mem_domain_q,
&thread->mem_domain_info.mem_domain_q_node);
thread->mem_domain_info.mem_domain = domain;
irq_unlock(key);
}
void k_mem_domain_remove_thread(k_tid_t thread)
{
unsigned int key;
__ASSERT(thread && thread->mem_domain_info.mem_domain, "");
key = irq_lock();
sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node);
thread->mem_domain_info.mem_domain = NULL;
irq_unlock(key);
}
static int init_mem_domain_module(struct device *arg)
{
ARG_UNUSED(arg);
max_partitions = _arch_mem_domain_max_partitions_get();
/*
* max_partitions must be less than or equal to
* CONFIG_MAX_DOMAIN_PARTITIONS, or would encounter array index
* out of bounds error.
*/
__ASSERT(max_partitions <= CONFIG_MAX_DOMAIN_PARTITIONS, "");
return 0;
}
SYS_INIT(init_mem_domain_module, PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);