kernel: mem_domain: init function to return error values

This changes k_mem_domain_init() to return error values
instead of asserting when errors are encountered.
This gives applications a chance to recover if needed.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2021-11-11 12:49:58 -08:00 committed by Anas Nashif
commit fb91ce2e21
13 changed files with 122 additions and 53 deletions

View file

@ -121,9 +121,13 @@ struct k_mem_partition;
* @param num_parts The number of array items of "parts" parameter.
* @param parts An array of pointers to the memory partitions. Can be NULL
* if num_parts is zero.
*
* @retval 0 if successful
* @retval -EINVAL if invalid parameters supplied
* @retval -ENOMEM if insufficient memory
*/
extern void k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
struct k_mem_partition *parts[]);
extern int k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
struct k_mem_partition *parts[]);
/**
* @brief Add a memory partition into a memory domain.

View file

@ -11,6 +11,7 @@
#include <sys/__assert.h>
#include <stdbool.h>
#include <spinlock.h>
#include <sys/check.h>
#include <sys/libc-hooks.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
@ -20,7 +21,6 @@ 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)
{
@ -84,19 +84,30 @@ static bool check_add_partition(struct k_mem_domain *domain,
return true;
}
#endif
void k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
struct k_mem_partition *parts[])
int k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
struct k_mem_partition *parts[])
{
k_spinlock_key_t key;
int ret = 0;
__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);
CHECKIF(domain == NULL) {
ret = -EINVAL;
goto out;
}
CHECKIF(!(num_parts == 0U || parts != NULL)) {
LOG_ERR("parts array is NULL and num_parts is nonzero");
ret = -EINVAL;
goto out;
}
CHECKIF(!(num_parts <= max_partitions)) {
LOG_ERR("num_parts of %d exceeds maximum allowable partitions (%d)",
num_parts, max_partitions);
ret = -EINVAL;
goto out;
}
key = k_spin_lock(&z_mem_domain_lock);
@ -105,25 +116,25 @@ void k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
sys_dlist_init(&domain->mem_domain_q);
#ifdef CONFIG_ARCH_MEM_DOMAIN_DATA
int ret = arch_mem_domain_init(domain);
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();
ret = -ENOMEM;
goto unlock_out;
}
#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]);
CHECKIF(!check_add_partition(domain, parts[i])) {
LOG_ERR("invalid partition index %d (%p)",
i, parts[i]);
ret = -EINVAL;
goto unlock_out;
}
domain->partitions[i] = *parts[i];
domain->num_partitions++;
@ -133,7 +144,11 @@ void k_mem_domain_init(struct k_mem_domain *domain, uint8_t num_parts,
}
}
unlock_out:
k_spin_unlock(&z_mem_domain_lock, key);
out:
return ret;
}
void k_mem_domain_add_partition(struct k_mem_domain *domain,
@ -269,7 +284,10 @@ void k_mem_domain_add_thread(struct k_mem_domain *domain, k_tid_t thread)
static int init_mem_domain_module(const struct device *arg)
{
int ret;
ARG_UNUSED(arg);
ARG_UNUSED(ret);
max_partitions = arch_mem_domain_max_partitions_get();
/*
@ -279,7 +297,9 @@ static int init_mem_domain_module(const struct device *arg)
*/
__ASSERT(max_partitions <= CONFIG_MAX_DOMAIN_PARTITIONS, "");
k_mem_domain_init(&k_mem_domain_default, 0, NULL);
ret = k_mem_domain_init(&k_mem_domain_default, 0, NULL);
__ASSERT(ret == 0, "failed to init default mem domain");
#ifdef Z_LIBC_PARTITION_EXISTS
k_mem_domain_add_partition(&k_mem_domain_default, &z_libc_partition);
#endif /* Z_LIBC_PARTITION_EXISTS */

View file

@ -524,6 +524,8 @@ void main(void)
#endif
#if defined(CONFIG_USERSPACE)
int ret;
struct k_mem_partition *parts[] = {
#if Z_LIBC_PARTITION_EXISTS
&z_libc_partition,
@ -531,7 +533,10 @@ void main(void)
&app_partition
};
k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
ret = k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
__ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
ARG_UNUSED(ret);
k_mem_domain_add_thread(&app_domain, app_thread);
k_thread_heap_assign(app_thread, &app_mem_pool);

View file

@ -233,7 +233,10 @@ static void init_app(void)
&app_partition
};
k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
int ret = k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
__ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
ARG_UNUSED(ret);
#endif
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)

View file

@ -131,7 +131,10 @@ static void init_app(void)
&app_partition
};
k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
int ret = k_mem_domain_init(&app_domain, ARRAY_SIZE(parts), parts);
__ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
ARG_UNUSED(ret);
#endif
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) || \

View file

@ -335,7 +335,11 @@ static void log_demo_supervisor(void *p1, void *p2, void *p3)
log_demo_thread(p1, p2, p3);
#ifdef CONFIG_USERSPACE
k_mem_domain_init(&app_domain, ARRAY_SIZE(app_parts), app_parts);
int ret = k_mem_domain_init(&app_domain, ARRAY_SIZE(app_parts), app_parts);
__ASSERT(ret == 0, "k_mem_domain_init() failed %d\n", ret);
ARG_UNUSED(ret);
k_mem_domain_add_thread(&app_domain, k_current_get());
k_thread_user_mode_enter(log_demo_thread, p1, p2, p3);
#endif

View file

@ -189,6 +189,7 @@ static void writeback_entry(void *p1, void *p2, void *p3)
/* Supervisor mode setup function for application A */
void app_a_entry(void *p1, void *p2, void *p3)
{
int ret;
struct k_mem_partition *parts[] = {
#if Z_LIBC_PARTITION_EXISTS
&z_libc_partition,
@ -207,7 +208,10 @@ void app_a_entry(void *p1, void *p2, void *p3)
* partition, the shared partition, and any common libc partition
* if it exists.
*/
k_mem_domain_init(&app_a_domain, ARRAY_SIZE(parts), parts);
ret = k_mem_domain_init(&app_a_domain, ARRAY_SIZE(parts), parts);
__ASSERT(ret == 0, "k_mem_domain_init failed %d", ret);
ARG_UNUSED(ret);
k_mem_domain_add_thread(&app_a_domain, k_current_get());
/* Assign a resource pool to serve for kernel-side allocations on

View file

@ -13,6 +13,9 @@
* DO NOT USE THIS CODE FOR SECURITY
*
*/
#include <sys/__assert.h>
#include "main.h"
#include "enc.h"
/* the following definition name prefix is to avoid a conflict */
@ -101,6 +104,7 @@ void main(void)
struct k_mem_partition *enc_parts[] = {&enc_part, &red_part, &blk_part};
struct k_mem_partition *pt_parts[] = {&user_part, &red_part};
k_tid_t tPT, tENC, tCT;
int ret;
fBUFIN = 0; /* clear flags */
fBUFOUT = 0;
@ -124,7 +128,11 @@ void main(void)
k_thread_access_grant(tENC, &allforone);
/* use K_FOREVER followed by k_thread_start*/
printk("ENC Thread Created %p\n", tENC);
k_mem_domain_init(&enc_domain, 3, enc_parts);
ret = k_mem_domain_init(&enc_domain, 3, enc_parts);
__ASSERT(ret == 0, "k_mem_domain_init() on enc_domain failed %d", ret);
ARG_UNUSED(ret);
printk("Partitions added to enc_domain\n");
k_mem_domain_add_thread(&enc_domain, tENC);
printk("enc_domain Created\n");
@ -136,7 +144,10 @@ void main(void)
K_FOREVER);
k_thread_access_grant(tPT, &allforone);
printk("PT Thread Created %p\n", tPT);
k_mem_domain_init(&pt_domain, 2, pt_parts);
ret = k_mem_domain_init(&pt_domain, 2, pt_parts);
__ASSERT(ret == 0, "k_mem_domain_init() on pt_domain failed %d", ret);
k_mem_domain_add_thread(&pt_domain, tPT);
printk("pt_domain Created\n");

View file

@ -42,12 +42,15 @@ void main(void)
printk("Hello from %s!\n", CONFIG_BOARD);
#ifdef CONFIG_USERSPACE
int ret;
struct k_mem_partition *mem_parts[] = {
&footprint_mem_partition
};
k_mem_domain_init(&footprint_mem_domain,
ARRAY_SIZE(mem_parts), mem_parts);
ret = k_mem_domain_init(&footprint_mem_domain,
ARRAY_SIZE(mem_parts), mem_parts);
__ASSERT_NO_MSG(ret == 0);
ARG_UNUSED(ret);
#endif /* CONFIG_USERSPACE */
run_thread_system();

View file

@ -100,9 +100,14 @@ static void test_thread_1_for_SU(void *p1, void *p2, void *p3)
*/
void test_permission_inheritance(void)
{
k_mem_domain_init(&inherit_mem_domain,
ARRAY_SIZE(inherit_memory_partition_array),
inherit_memory_partition_array);
int ret;
ret = k_mem_domain_init(&inherit_mem_domain,
ARRAY_SIZE(inherit_memory_partition_array),
inherit_memory_partition_array);
if (ret != 0) {
ztest_test_fail();
}
parent_tid = k_current_get();
k_mem_domain_add_thread(&inherit_mem_domain, parent_tid);

View file

@ -53,7 +53,9 @@ void test_mem_domain_setup(void)
CONFIG_MAX_DOMAIN_PARTITIONS, max_parts);
zassert_true(num_rw_parts > 0, "no free memory partitions");
k_mem_domain_init(&test_domain, ARRAY_SIZE(parts), parts);
zassert_equal(
k_mem_domain_init(&test_domain, ARRAY_SIZE(parts), parts),
0, "failed to initialize memory domain");
for (unsigned int i = 0; i < num_rw_parts; i++) {
rw_parts[i].start = (uintptr_t)&rw_bufs[i];
@ -207,7 +209,9 @@ K_MEM_PARTITION_DEFINE(no_access_part, no_access_buf, sizeof(no_access_buf),
static void mem_domain_init_entry(void *p1, void *p2, void *p3)
{
k_mem_domain_init(&no_access_domain, 0, NULL);
zassert_equal(
k_mem_domain_init(&no_access_domain, 0, NULL),
0, "failed to initialize memory domain");
}
static void mem_domain_add_partition_entry(void *p1, void *p2, void *p3)
@ -469,10 +473,8 @@ void test_mem_domain_remove_part_fail(void)
/**
* @brief Test error case of initializing memory domain fail
*
* @details Try to initialize a domain with invalid partition, then see
* if an expected fatal error happens.
* And while the fatal error happened, the memory domain spinlock
* is held, we need to release them to make other follow test case.
* @details Try to initialize a domain with invalid partition.
* k_mem_domain_init() should return non-zero.
*
* @ingroup kernel_memprotect_tests
*/
@ -480,18 +482,14 @@ void test_mem_domain_init_fail(void)
{
struct k_mem_partition *no_parts[] = {&ro_part, 0};
/* init another domain fail, expected fault happened */
need_recover_spinlock = true;
set_fault_valid(true);
k_mem_domain_init(&test_domain_fail, ARRAY_SIZE(no_parts),
no_parts);
/* init another domain fail */
need_recover_spinlock = false;
set_fault_valid(false);
/* For acrh which not CONFIG_ARCH_MEM_DOMAIN_DATA, if assert is off,
* it will reach here.
*/
#if !defined(CONFIG_ASSERT) && defined(CONFIG_ARCH_MEM_DOMAIN_DATA)
zassert_unreachable("should not be here");
#endif
zassert_not_equal(
k_mem_domain_init(&test_domain_fail, ARRAY_SIZE(no_parts),
no_parts),
0, "should fail to initialize memory domain");
}
/**

View file

@ -681,7 +681,11 @@ static void drop_user(volatile bool *to_modify)
static void test_init_and_access_other_memdomain(void)
{
struct k_mem_partition *parts[] = { &ztest_mem_partition, &alt_part };
k_mem_domain_init(&alternate_domain, ARRAY_SIZE(parts), parts);
zassert_equal(
k_mem_domain_init(&alternate_domain, ARRAY_SIZE(parts), parts),
0, "failed to initialize memory domain");
/* Switch to alternate_domain which does not have default_part that
* contains default_bool. This should fault when we try to write it.
*/

View file

@ -209,6 +209,7 @@ void test_tls_userspace(void)
void test_main(void)
{
#ifdef CONFIG_USERSPACE
int ret;
unsigned int i;
struct k_mem_partition *parts[] = {
@ -220,7 +221,11 @@ void test_main(void)
};
parts[0] = &part_common;
k_mem_domain_init(&dom_common, ARRAY_SIZE(parts), parts);
ret = k_mem_domain_init(&dom_common, ARRAY_SIZE(parts), parts);
__ASSERT(ret == 0, "k_mem_domain_init() failed %d", ret);
ARG_UNUSED(ret);
k_mem_domain_add_thread(&dom_common, k_current_get());
for (i = 0; i < NUM_THREADS; i++) {