From 4b4f7734848fff525dd38fc15d5e74876baca0ac Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Fri, 1 Feb 2019 12:18:31 -0800 Subject: [PATCH] libc: set up memory partitions * Newlib now defines a special z_newlib_partition containing all globals relevant to newlib. Most of these are in libc.a with a heap tracking variable in newlib's hooks. * Both C libraries now expose a k_mem_partition containing the bounds of the malloc heap arena. Threads that want to use libc malloc() will need to add this to their memory domain. * z_newlib_get_heap_bounds has been removed, in favor of the memory partition for the heap arena * ztest now includes the C library partitions in its memory domain. * The mem_alloc test now runs in user mode to prove that this all works for both C libraries. Signed-off-by: Andrew Boie --- CMakeLists.txt | 4 +++ include/misc/libc-hooks.h | 13 +++++++- kernel/include/kernel_internal.h | 15 --------- lib/libc/minimal/source/stdlib/malloc.c | 13 +++++++- lib/libc/newlib/libc-hooks.c | 44 ++++++++++++++++++++----- tests/lib/mem_alloc/prj_newlib.conf | 1 + tests/lib/mem_alloc/src/main.c | 34 +++++++------------ tests/lib/mem_alloc/testcase.yaml | 4 +-- tests/ztest/src/ztest.c | 12 ++++++- 9 files changed, 90 insertions(+), 50 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cce540f6604..6638a96a844 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1228,12 +1228,16 @@ if(CONFIG_APP_SHARED_MEM AND CONFIG_USERSPACE) kernel ${ZEPHYR_LIBS_PROPERTY} ) + if(CONFIG_NEWLIB_LIBC) + set(NEWLIB_PART -l libc.a z_newlib_partition) + endif() add_custom_command( TARGET ${APP_SMEM_DEP} COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/gen_app_partitions.py -d ${OBJ_FILE_DIR} -o ${APP_SMEM_LD} + ${NEWLIB_PART} $<$:--verbose> WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/ COMMENT "Generating app_smem linker section" diff --git a/include/misc/libc-hooks.h b/include/misc/libc-hooks.h index 216e8d966c3..2056bc99edf 100644 --- a/include/misc/libc-hooks.h +++ b/include/misc/libc-hooks.h @@ -27,6 +27,13 @@ __syscall int _zephyr_read(char *buf, int nbytes); __syscall int _zephyr_write(const void *buf, int nbytes); +#ifdef CONFIG_APP_SHARED_MEM +/* Memory partition containing newlib's globals. This includes all the globals + * within libc.a and the supporting zephyr hooks, but not the malloc arena. + */ +extern struct k_mem_partition z_newlib_partition; +#endif /* CONFIG_APP_SHARED_MEM */ + #else /* Minimal libc */ @@ -34,9 +41,13 @@ __syscall int _zephyr_fputc(int c, FILE *stream); __syscall size_t _zephyr_fwrite(const void *_MLIBC_RESTRICT ptr, size_t size, size_t nitems, FILE *_MLIBC_RESTRICT stream); - #endif /* CONFIG_NEWLIB_LIBC */ +#ifdef CONFIG_APP_SHARED_MEM +/* Memory partition containing the libc malloc arena */ +extern struct k_mem_partition z_malloc_partition; +#endif + #include #endif /* ZEPHYR_INCLUDE_MISC_LIBC_HOOKS_H_ */ diff --git a/kernel/include/kernel_internal.h b/kernel/include/kernel_internal.h index 6ab76322969..aa8ff206df7 100644 --- a/kernel/include/kernel_internal.h +++ b/kernel/include/kernel_internal.h @@ -206,21 +206,6 @@ extern void smp_init(void); extern void smp_timer_init(void); -#ifdef CONFIG_NEWLIB_LIBC -/** - * @brief Fetch dimentions of newlib heap area for _sbrk() - * - * This memory region is used for heap allocations by the newlib C library. - * If user threads need to have access to this, the results returned can be - * used to program memory protection hardware appropriately. - * - * @param base Pointer to void pointer, filled in with the heap starting - * address - * @param size Pointer to a size_y, filled in with the maximum heap size - */ -extern void z_newlib_get_heap_bounds(void **base, size_t *size); -#endif - extern u32_t z_early_boot_rand32_get(void); #if CONFIG_STACK_POINTER_RANDOM diff --git a/lib/libc/minimal/source/stdlib/malloc.c b/lib/libc/minimal/source/stdlib/malloc.c index da7412b83a5..696ae2dd6b1 100644 --- a/lib/libc/minimal/source/stdlib/malloc.c +++ b/lib/libc/minimal/source/stdlib/malloc.c @@ -10,15 +10,26 @@ #include #include #include +#include #define LOG_LEVEL CONFIG_KERNEL_LOG_LEVEL #include LOG_MODULE_DECLARE(os); +#ifdef CONFIG_APP_SHARED_MEM +K_APPMEM_PARTITION_DEFINE(z_malloc_partition); +#endif + #if (CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE > 0) +#ifdef CONFIG_APP_SHARED_MEM +#define POOL_SECTION K_APP_DMEM_SECTION(z_malloc_partition) +#else +#define POOL_SECTION .data +#endif /* CONFIG_APP_SHARED_MEM */ + K_MUTEX_DEFINE(malloc_mutex); SYS_MEM_POOL_DEFINE(z_malloc_mem_pool, &malloc_mutex, 16, - CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE, 1, 4, .data); + CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE, 1, 4, POOL_SECTION); void *malloc(size_t size) { diff --git a/lib/libc/newlib/libc-hooks.c b/lib/libc/newlib/libc-hooks.c index 545035bee2a..09f5bd13de0 100644 --- a/lib/libc/newlib/libc-hooks.c +++ b/lib/libc/newlib/libc-hooks.c @@ -14,12 +14,30 @@ #include #include #include +#include +#include + +#ifdef CONFIG_APP_SHARED_MEM +K_APPMEM_PARTITION_DEFINE(z_newlib_partition); +#define LIBC_BSS K_APP_BMEM(z_newlib_partition) +#define LIBC_DATA K_APP_DMEM(z_newlib_partition) + +#if CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE +K_APPMEM_PARTITION_DEFINE(z_malloc_partition); +#define MALLOC_BSS K_APP_BMEM(z_malloc_partition) +#endif /* CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE */ + +#else +#define LIBC_BSS +#define LIBC_DATA +#define MALLOC_BSS +#endif /* CONFIG_APP_SHARED_MEM */ #define USED_RAM_END_ADDR POINTER_TO_UINT(&_end) #if CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE /* Compiler will throw an error if the provided value isn't a power of two */ -static unsigned char __kernel __aligned(CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE) +MALLOC_BSS static unsigned char __kernel __aligned(CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE) heap_base[CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE]; #define MAX_HEAP_SIZE CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE #else @@ -48,9 +66,25 @@ extern void *_heap_sentry; #endif static unsigned char *heap_base = UINT_TO_POINTER(USED_RAM_END_ADDR); + +#ifdef CONFIG_APP_SHARED_MEM +struct k_mem_partition z_malloc_partition; + +static int malloc_prepare(struct device *unused) +{ + ARG_UNUSED(unused); + + z_malloc_partition.start = (u32_t)heap_base; + z_malloc_partition.size = MAX_HEAP_SIZE; + z_malloc_partition.attr = K_MEM_PARTITION_P_RW_U_RW; + return 0; +} + +SYS_INIT(malloc_prepare, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#endif #endif /* CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE */ -static unsigned int heap_sz; +LIBC_BSS static unsigned int heap_sz; static int _stdout_hook_default(int c) { @@ -207,12 +241,6 @@ void *_sbrk(int count) } FUNC_ALIAS(_sbrk, sbrk, void *); -void z_newlib_get_heap_bounds(void **base, size_t *size) -{ - *base = heap_base; - *size = MAX_HEAP_SIZE; -} - int *__errno(void) { return z_errno(); diff --git a/tests/lib/mem_alloc/prj_newlib.conf b/tests/lib/mem_alloc/prj_newlib.conf index 913fe6fcade..91f4ca40561 100644 --- a/tests/lib/mem_alloc/prj_newlib.conf +++ b/tests/lib/mem_alloc/prj_newlib.conf @@ -1,3 +1,4 @@ CONFIG_ZTEST=y CONFIG_NEWLIB_LIBC=y CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE=512 diff --git a/tests/lib/mem_alloc/src/main.c b/tests/lib/mem_alloc/src/main.c index 354a4f999ee..51835554db2 100644 --- a/tests/lib/mem_alloc/src/main.c +++ b/tests/lib/mem_alloc/src/main.c @@ -24,10 +24,6 @@ #define BUF_LEN 10 -#ifdef CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE -#define MAX_HEAP_SIZE CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE -#endif - /** * @brief Test dynamic memory allocation using malloc * @@ -40,6 +36,7 @@ void test_malloc(void) iptr = malloc(BUF_LEN * sizeof(int)); zassert_not_null((iptr), "malloc failed, errno: %d", errno); + memset(iptr, 'p', BUF_LEN * sizeof(int)); free(iptr); iptr = NULL; } @@ -64,7 +61,7 @@ void test_free(void) * @see calloc(), free() */ #define CALLOC_BUFLEN (200) -static unsigned char zerobuf[CALLOC_BUFLEN]; +ZTEST_BMEM static unsigned char zerobuf[CALLOC_BUFLEN]; void test_calloc(void) { @@ -74,6 +71,7 @@ void test_calloc(void) zassert_not_null((cptr), "calloc failed, errno: %d", errno); zassert_true(((memcmp(cptr, zerobuf, CALLOC_BUFLEN)) == 0), "calloc failed to set zero value, errno: %d", errno); + memset(cptr, 'p', CALLOC_BUFLEN); free(cptr); cptr = NULL; } @@ -83,7 +81,7 @@ void test_calloc(void) * * @see malloc(), realloc(), free() */ -unsigned char filled_buf[BUF_LEN]; +ZTEST_BMEM unsigned char filled_buf[BUF_LEN]; void test_realloc(void) { @@ -191,17 +189,9 @@ void test_memalloc_all(void) void test_memalloc_max(void) { - size_t max_heap_size = 0; char *ptr = NULL; -#ifdef CONFIG_NEWLIB_LIBC - void *heap_base = NULL; - - z_newlib_get_heap_bounds(&heap_base, &max_heap_size); -#else - max_heap_size = MAX_HEAP_SIZE; -#endif - ptr = malloc(max_heap_size + 1); + ptr = malloc(0xF0000000); zassert_is_null(ptr, "malloc passed unexpectedly"); free(ptr); ptr = NULL; @@ -210,13 +200,13 @@ void test_memalloc_max(void) void test_main(void) { ztest_test_suite(test_c_lib_dynamic_memalloc, - ztest_unit_test(test_malloc), - ztest_unit_test(test_free), - ztest_unit_test(test_calloc), - ztest_unit_test(test_realloc), - ztest_unit_test(test_reallocarray), - ztest_unit_test(test_memalloc_all), - ztest_unit_test(test_memalloc_max) + ztest_user_unit_test(test_malloc), + ztest_user_unit_test(test_free), + ztest_user_unit_test(test_calloc), + ztest_user_unit_test(test_realloc), + ztest_user_unit_test(test_reallocarray), + ztest_user_unit_test(test_memalloc_all), + ztest_user_unit_test(test_memalloc_max) ); ztest_run_test_suite(test_c_lib_dynamic_memalloc); } diff --git a/tests/lib/mem_alloc/testcase.yaml b/tests/lib/mem_alloc/testcase.yaml index b537c23dece..8befc7b31ea 100644 --- a/tests/lib/mem_alloc/testcase.yaml +++ b/tests/lib/mem_alloc/testcase.yaml @@ -2,9 +2,9 @@ tests: libraries.libc.minimal: extra_args: CONF_FILE=prj.conf arch_exclude: posix - tags: clib minimal_libc + tags: clib minimal_libc userspace libraries.libc.newlib: extra_args: CONF_FILE=prj_newlib.conf arch_exclude: posix filter: TOOLCHAIN_HAS_NEWLIB == 1 - tags: clib newlib + tags: clib newlib userspace diff --git a/tests/ztest/src/ztest.c b/tests/ztest/src/ztest.c index 284c3d97984..49ec3d5c9a2 100644 --- a/tests/ztest/src/ztest.c +++ b/tests/ztest/src/ztest.c @@ -7,6 +7,10 @@ #include #include #include +#ifdef CONFIG_APP_SHARED_MEM +#include +#endif + #ifdef KERNEL __kernel static struct k_thread ztest_thread; #endif @@ -297,7 +301,13 @@ void main(void) { #ifdef CONFIG_APP_SHARED_MEM struct k_mem_partition *parts[] = { - &ztest_mem_partition + &ztest_mem_partition, +#ifdef CONFIG_NEWLIB_LIBC + /* Newlib libc.a library and hooks globals */ + &z_newlib_partition, +#endif + /* Both minimal and newlib libc expose this for malloc arena */ + &z_malloc_partition }; /* Ztests just have one memory domain with one partition.