/* * Copyright (c) 2017 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); struct k_spinlock z_mem_domain_lock; static uint8_t max_partitions; struct k_mem_domain k_mem_domain_default; #if __ASSERT_ON static bool check_add_partition(struct k_mem_domain *domain, struct k_mem_partition *part) { int i; uintptr_t pstart, pend, dstart, dend; if (part == NULL) { LOG_ERR("NULL k_mem_partition provided"); return false; } #ifdef CONFIG_EXECUTE_XOR_WRITE /* Arches where execution cannot be disabled should always return * false to this check */ if (K_MEM_PARTITION_IS_EXECUTABLE(part->attr) && K_MEM_PARTITION_IS_WRITABLE(part->attr)) { LOG_ERR("partition is writable and executable ", part->start); return false; } #endif if (part->size == 0U) { LOG_ERR("zero sized partition at %p with base 0x%lx", part, part->start); return false; } pstart = part->start; pend = part->start + part->size; if (pend <= pstart) { LOG_ERR("invalid partition %p, wraparound detected. base 0x%lx size %zu", part, part->start, part->size); return false; } /* Check that this partition doesn't overlap any existing ones already * in the domain */ for (i = 0; i < domain->num_partitions; i++) { struct k_mem_partition *dpart = &domain->partitions[i]; if (dpart->size == 0U) { /* Unused slot */ continue; } dstart = dpart->start; dend = dstart + dpart->size; if (pend > dstart && dend > pstart) { LOG_ERR("partition %p base %lx (size %zu) overlaps existing base %lx (size %zu)", part, part->start, part->size, dpart->start, dpart->size); return false; } } return true; } #endif void k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts, struct k_mem_partition *parts[]) { k_spinlock_key_t key; __ASSERT_NO_MSG(domain != NULL); __ASSERT(num_parts == 0U || parts != NULL, "parts array is NULL and num_parts is nonzero"); __ASSERT(num_parts <= max_partitions, "num_parts of %d exceeds maximum allowable partitions (%d)", num_parts, max_partitions); key = k_spin_lock(&z_mem_domain_lock); domain->num_partitions = 0U; (void)memset(domain->partitions, 0, sizeof(domain->partitions)); sys_dlist_init(&domain->mem_domain_q); #ifdef CONFIG_ARCH_MEM_DOMAIN_DATA int ret = arch_mem_domain_init(domain); /* TODO propagate return values, see #24609. * * Not using an assertion here as this is a memory allocation error */ if (ret != 0) { LOG_ERR("architecture-specific initialization failed for domain %p with %d", domain, ret); k_panic(); } #endif if (num_parts != 0U) { uint32_t i; for (i = 0U; i < num_parts; i++) { __ASSERT(check_add_partition(domain, parts[i]), "invalid partition index %d (%p)", i, parts[i]); domain->partitions[i] = *parts[i]; domain->num_partitions++; #ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API arch_mem_domain_partition_add(domain, i); #endif } } k_spin_unlock(&z_mem_domain_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_NO_MSG(domain != NULL); __ASSERT(check_add_partition(domain, part), "invalid partition %p", part); key = k_spin_lock(&z_mem_domain_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 == 0U) { break; } } __ASSERT(p_idx < max_partitions, "no free partition slots available"); LOG_DBG("add partition base %lx size %zu to domain %p\n", part->start, part->size, domain); domain->partitions[p_idx].start = part->start; domain->partitions[p_idx].size = part->size; domain->partitions[p_idx].attr = part->attr; domain->num_partitions++; #ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API arch_mem_domain_partition_add(domain, p_idx); #endif k_spin_unlock(&z_mem_domain_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_NO_MSG(domain != NULL); __ASSERT_NO_MSG(part != NULL); key = k_spin_lock(&z_mem_domain_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(p_idx < max_partitions, "no matching partition found"); LOG_DBG("remove partition base %lx size %zu from domain %p\n", part->start, part->size, domain); #ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API arch_mem_domain_partition_remove(domain, p_idx); #endif /* A zero-sized partition denotes it's a free partition */ domain->partitions[p_idx].size = 0U; domain->num_partitions--; k_spin_unlock(&z_mem_domain_lock, key); } static void add_thread_locked(struct k_mem_domain *domain, k_tid_t thread) { __ASSERT_NO_MSG(domain != NULL); __ASSERT_NO_MSG(thread != NULL); LOG_DBG("add thread %p to domain %p\n", thread, domain); sys_dlist_append(&domain->mem_domain_q, &thread->mem_domain_info.mem_domain_q_node); thread->mem_domain_info.mem_domain = domain; #ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API arch_mem_domain_thread_add(thread); #endif } static void remove_thread_locked(struct k_thread *thread) { __ASSERT_NO_MSG(thread != NULL); LOG_DBG("remove thread %p from memory domain %p\n", thread, thread->mem_domain_info.mem_domain); sys_dlist_remove(&thread->mem_domain_info.mem_domain_q_node); #ifdef CONFIG_ARCH_MEM_DOMAIN_SYNCHRONOUS_API arch_mem_domain_thread_remove(thread); #endif } /* Called from thread object initialization */ void z_mem_domain_init_thread(struct k_thread *thread) { k_spinlock_key_t key = k_spin_lock(&z_mem_domain_lock); /* New threads inherit memory domain configuration from parent */ add_thread_locked(_current->mem_domain_info.mem_domain, thread); k_spin_unlock(&z_mem_domain_lock, key); } /* Called when thread aborts during teardown tasks. sched_spinlock is held */ void z_mem_domain_exit_thread(struct k_thread *thread) { k_spinlock_key_t key = k_spin_lock(&z_mem_domain_lock); remove_thread_locked(thread); k_spin_unlock(&z_mem_domain_lock, key); } void k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread) { k_spinlock_key_t key; key = k_spin_lock(&z_mem_domain_lock); if (thread->mem_domain_info.mem_domain != domain) { remove_thread_locked(thread); add_thread_locked(domain, thread); } k_spin_unlock(&z_mem_domain_lock, key); } static int init_mem_domain_module(const 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, ""); k_mem_domain_init(&k_mem_domain_default, 0, NULL); #ifdef Z_LIBC_PARTITION_EXISTS k_mem_domain_add_partition(&k_mem_domain_default, &z_libc_partition); #endif /* Z_LIBC_PARTITION_EXISTS */ return 0; } SYS_INIT(init_mem_domain_module, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);