blob: d14ef70c890cbc0cfa35ac9720239585788c4426 [file] [log] [blame]
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <init.h>
#include <kernel.h>
#include <kernel_structs.h>
#include <kernel_internal.h>
#include <misc/__assert.h>
static u8_t max_partitions;
#if defined(CONFIG_EXECUTE_XOR_WRITE) && __ASSERT_ON
static bool sane_partition(const struct k_mem_partition *part,
const struct k_mem_partition *parts,
u32_t num_parts)
{
bool exec, write;
u32_t end;
u32_t i;
end = part->start + part->size;
exec = K_MEM_PARTITION_IS_EXECUTABLE(part->attr);
write = K_MEM_PARTITION_IS_WRITABLE(part->attr);
if (exec && write) {
__ASSERT(0, "partition is writable and executable <start %x>",
part->start);
return false;
}
for (i = 0; i < num_parts; i++) {
bool cur_write, cur_exec;
u32_t cur_end;
cur_end = parts[i].start + parts[i].size;
if (end < parts[i].start || cur_end < part->start) {
continue;
}
cur_write = K_MEM_PARTITION_IS_WRITABLE(parts[i].attr);
cur_exec = K_MEM_PARTITION_IS_EXECUTABLE(parts[i].attr);
if ((cur_write && exec) || (cur_exec && write)) {
__ASSERT(0, "overlapping partitions are "
"writable and executable "
"<%x...%x>, <%x...%x>",
part->start, end,
parts[i].start, cur_end);
return false;
}
}
return true;
}
static inline bool sane_partition_domain(const struct k_mem_domain *domain,
const struct k_mem_partition *part)
{
return sane_partition(part, domain->partitions,
domain->num_partitions);
}
#else
#define sane_partition(...) (true)
#define sane_partition_domain(...) (true)
#endif
void k_mem_domain_init(struct k_mem_domain *domain, u8_t num_parts,
struct k_mem_partition *parts[])
{
unsigned int key;
__ASSERT(domain != NULL, "");
__ASSERT(num_parts == 0 || parts != NULL, "");
__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], "");
__ASSERT((parts[i]->start + parts[i]->size) >
parts[i]->start, "");
domain->partitions[i] = *parts[i];
}
#if defined(CONFIG_EXECUTE_XOR_WRITE)
for (i = 0; i < num_parts; i++) {
__ASSERT(sane_partition_domain(domain,
&domain->partitions[i]),
"");
}
#endif
}
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 != NULL, "");
key = irq_lock();
/* Handle architecture specifc destroy only if it is the current thread*/
if (_current->mem_domain_info.mem_domain == domain) {
_arch_mem_domain_destroy(domain);
}
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 != NULL, "");
__ASSERT(part != NULL, "");
__ASSERT((part->start + part->size) > part->start, "");
#if defined(CONFIG_EXECUTE_XOR_WRITE)
__ASSERT(sane_partition_domain(domain, part), "");
#endif
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 != NULL, "");
__ASSERT(part != NULL, "");
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, "");
/* Handle architecture specifc remove only if it is the current thread*/
if (_current->mem_domain_info.mem_domain == domain) {
_arch_mem_domain_partition_remove(domain, p_idx);
}
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 != NULL, "");
__ASSERT(thread != NULL, "");
__ASSERT(thread->mem_domain_info.mem_domain == NULL,
"mem domain unset");
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;
if (_current == thread) {
_arch_mem_domain_configure(thread);
}
irq_unlock(key);
}
void k_mem_domain_remove_thread(k_tid_t thread)
{
unsigned int key;
__ASSERT(thread != NULL, "");
__ASSERT(thread->mem_domain_info.mem_domain != NULL, "mem domain set");
key = irq_lock();
if (_current == thread) {
_arch_mem_domain_destroy(thread->mem_domain_info.mem_domain);
}
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);