/* * Copyright (c) 2017 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include static struct k_spinlock lock; static u8_t max_partitions; #if (defined(CONFIG_EXECUTE_XOR_WRITE) || \ defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS)) && __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 last; u32_t i; last = part->start + part->size - 1; exec = K_MEM_PARTITION_IS_EXECUTABLE(part->attr); write = K_MEM_PARTITION_IS_WRITABLE(part->attr); if (exec && write) { __ASSERT(false, "partition is writable and executable ", part->start); return false; } for (i = 0U; i < num_parts; i++) { bool cur_write, cur_exec; u32_t cur_last; cur_last = parts[i].start + parts[i].size - 1; if (last < parts[i].start || cur_last < part->start) { continue; } #if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) /* Partitions overlap */ __ASSERT(false, "overlapping partitions <%x...%x>, <%x...%x>", part->start, last, parts[i].start, cur_last); return false; #endif 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(false, "overlapping partitions are " "writable and executable " "<%x...%x>, <%x...%x>", part->start, last, parts[i].start, cur_last); 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[]) { k_spinlock_key_t key; __ASSERT(domain != NULL, ""); __ASSERT(num_parts == 0 || parts != NULL, ""); __ASSERT(num_parts <= max_partitions, ""); key = k_spin_lock(&lock); domain->num_partitions = 0; (void)memset(domain->partitions, 0, sizeof(domain->partitions)); if (num_parts != 0) { u32_t i; for (i = 0U; i < num_parts; i++) { __ASSERT(parts[i] != NULL, ""); __ASSERT((parts[i]->start + parts[i]->size) > parts[i]->start, ""); #if defined(CONFIG_EXECUTE_XOR_WRITE) || \ defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) __ASSERT(sane_partition_domain(domain, parts[i]), ""); #endif domain->partitions[i] = *parts[i]; domain->num_partitions++; } } sys_dlist_init(&domain->mem_domain_q); k_spin_unlock(&lock, key); } void k_mem_domain_destroy(struct k_mem_domain *domain) { k_spinlock_key_t key; sys_dnode_t *node, *next_node; __ASSERT(domain != NULL, ""); key = k_spin_lock(&lock); /* Handle architecture-specific 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; } k_spin_unlock(&lock, key); } void k_mem_domain_add_partition(struct k_mem_domain *domain, struct k_mem_partition *part) { int p_idx; k_spinlock_key_t key; __ASSERT(domain != NULL, ""); __ASSERT(part != NULL, ""); __ASSERT((part->start + part->size) > part->start, ""); #if defined(CONFIG_EXECUTE_XOR_WRITE) || \ defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) __ASSERT(sane_partition_domain(domain, part), ""); #endif key = k_spin_lock(&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++; k_spin_unlock(&lock, key); } void k_mem_domain_remove_partition(struct k_mem_domain *domain, struct k_mem_partition *part) { int p_idx; k_spinlock_key_t key; __ASSERT(domain != NULL, ""); __ASSERT(part != NULL, ""); key = k_spin_lock(&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-specific remove * only if it is the current thread. */ if (_current->mem_domain_info.mem_domain == domain) { _arch_mem_domain_partition_remove(domain, p_idx); } /* A zero-sized partition denotes it's a free partition */ domain->partitions[p_idx].size = 0; domain->num_partitions--; k_spin_unlock(&lock, key); } void k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread) { k_spinlock_key_t key; __ASSERT(domain != NULL, ""); __ASSERT(thread != NULL, ""); __ASSERT(thread->mem_domain_info.mem_domain == NULL, "mem domain unset"); key = k_spin_lock(&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); } k_spin_unlock(&lock, key); } void k_mem_domain_remove_thread(k_tid_t thread) { k_spinlock_key_t key; __ASSERT(thread != NULL, ""); __ASSERT(thread->mem_domain_info.mem_domain != NULL, "mem domain set"); key = k_spin_lock(&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; k_spin_unlock(&lock, 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);