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 <andrew.p.boie@intel.com>
This commit is contained in:
Andrew Boie 2019-02-01 12:18:31 -08:00 committed by Anas Nashif
commit 4b4f773484
9 changed files with 90 additions and 50 deletions

View file

@ -1228,12 +1228,16 @@ if(CONFIG_APP_SHARED_MEM AND CONFIG_USERSPACE)
kernel ${ZEPHYR_LIBS_PROPERTY} kernel ${ZEPHYR_LIBS_PROPERTY}
) )
if(CONFIG_NEWLIB_LIBC)
set(NEWLIB_PART -l libc.a z_newlib_partition)
endif()
add_custom_command( add_custom_command(
TARGET ${APP_SMEM_DEP} TARGET ${APP_SMEM_DEP}
COMMAND ${PYTHON_EXECUTABLE} COMMAND ${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/gen_app_partitions.py ${ZEPHYR_BASE}/scripts/gen_app_partitions.py
-d ${OBJ_FILE_DIR} -d ${OBJ_FILE_DIR}
-o ${APP_SMEM_LD} -o ${APP_SMEM_LD}
${NEWLIB_PART}
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose> $<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose>
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/
COMMENT "Generating app_smem linker section" COMMENT "Generating app_smem linker section"

View file

@ -27,6 +27,13 @@ __syscall int _zephyr_read(char *buf, int nbytes);
__syscall int _zephyr_write(const void *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 #else
/* Minimal libc */ /* 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, __syscall size_t _zephyr_fwrite(const void *_MLIBC_RESTRICT ptr, size_t size,
size_t nitems, FILE *_MLIBC_RESTRICT stream); size_t nitems, FILE *_MLIBC_RESTRICT stream);
#endif /* CONFIG_NEWLIB_LIBC */ #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 <syscalls/libc-hooks.h> #include <syscalls/libc-hooks.h>
#endif /* ZEPHYR_INCLUDE_MISC_LIBC_HOOKS_H_ */ #endif /* ZEPHYR_INCLUDE_MISC_LIBC_HOOKS_H_ */

View file

@ -206,21 +206,6 @@ extern void smp_init(void);
extern void smp_timer_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); extern u32_t z_early_boot_rand32_get(void);
#if CONFIG_STACK_POINTER_RANDOM #if CONFIG_STACK_POINTER_RANDOM

View file

@ -10,15 +10,26 @@
#include <errno.h> #include <errno.h>
#include <misc/mempool.h> #include <misc/mempool.h>
#include <string.h> #include <string.h>
#include <app_memory/app_memdomain.h>
#define LOG_LEVEL CONFIG_KERNEL_LOG_LEVEL #define LOG_LEVEL CONFIG_KERNEL_LOG_LEVEL
#include <logging/log.h> #include <logging/log.h>
LOG_MODULE_DECLARE(os); 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) #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); K_MUTEX_DEFINE(malloc_mutex);
SYS_MEM_POOL_DEFINE(z_malloc_mem_pool, &malloc_mutex, 16, 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) void *malloc(size_t size)
{ {

View file

@ -14,12 +14,30 @@
#include <misc/errno_private.h> #include <misc/errno_private.h>
#include <misc/libc-hooks.h> #include <misc/libc-hooks.h>
#include <syscall_handler.h> #include <syscall_handler.h>
#include <app_memory/app_memdomain.h>
#include <init.h>
#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) #define USED_RAM_END_ADDR POINTER_TO_UINT(&_end)
#if CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE #if CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE
/* Compiler will throw an error if the provided value isn't a power of two */ /* 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]; heap_base[CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE];
#define MAX_HEAP_SIZE CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE #define MAX_HEAP_SIZE CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE
#else #else
@ -48,9 +66,25 @@ extern void *_heap_sentry;
#endif #endif
static unsigned char *heap_base = UINT_TO_POINTER(USED_RAM_END_ADDR); 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 */ #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) static int _stdout_hook_default(int c)
{ {
@ -207,12 +241,6 @@ void *_sbrk(int count)
} }
FUNC_ALIAS(_sbrk, sbrk, void *); 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) int *__errno(void)
{ {
return z_errno(); return z_errno();

View file

@ -1,3 +1,4 @@
CONFIG_ZTEST=y CONFIG_ZTEST=y
CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC=y
CONFIG_MAIN_STACK_SIZE=1024 CONFIG_MAIN_STACK_SIZE=1024
CONFIG_NEWLIB_LIBC_ALIGNED_HEAP_SIZE=512

View file

@ -24,10 +24,6 @@
#define BUF_LEN 10 #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 * @brief Test dynamic memory allocation using malloc
* *
@ -40,6 +36,7 @@ void test_malloc(void)
iptr = malloc(BUF_LEN * sizeof(int)); iptr = malloc(BUF_LEN * sizeof(int));
zassert_not_null((iptr), "malloc failed, errno: %d", errno); zassert_not_null((iptr), "malloc failed, errno: %d", errno);
memset(iptr, 'p', BUF_LEN * sizeof(int));
free(iptr); free(iptr);
iptr = NULL; iptr = NULL;
} }
@ -64,7 +61,7 @@ void test_free(void)
* @see calloc(), free() * @see calloc(), free()
*/ */
#define CALLOC_BUFLEN (200) #define CALLOC_BUFLEN (200)
static unsigned char zerobuf[CALLOC_BUFLEN]; ZTEST_BMEM static unsigned char zerobuf[CALLOC_BUFLEN];
void test_calloc(void) void test_calloc(void)
{ {
@ -74,6 +71,7 @@ void test_calloc(void)
zassert_not_null((cptr), "calloc failed, errno: %d", errno); zassert_not_null((cptr), "calloc failed, errno: %d", errno);
zassert_true(((memcmp(cptr, zerobuf, CALLOC_BUFLEN)) == 0), zassert_true(((memcmp(cptr, zerobuf, CALLOC_BUFLEN)) == 0),
"calloc failed to set zero value, errno: %d", errno); "calloc failed to set zero value, errno: %d", errno);
memset(cptr, 'p', CALLOC_BUFLEN);
free(cptr); free(cptr);
cptr = NULL; cptr = NULL;
} }
@ -83,7 +81,7 @@ void test_calloc(void)
* *
* @see malloc(), realloc(), free() * @see malloc(), realloc(), free()
*/ */
unsigned char filled_buf[BUF_LEN]; ZTEST_BMEM unsigned char filled_buf[BUF_LEN];
void test_realloc(void) void test_realloc(void)
{ {
@ -191,17 +189,9 @@ void test_memalloc_all(void)
void test_memalloc_max(void) void test_memalloc_max(void)
{ {
size_t max_heap_size = 0;
char *ptr = NULL; char *ptr = NULL;
#ifdef CONFIG_NEWLIB_LIBC ptr = malloc(0xF0000000);
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);
zassert_is_null(ptr, "malloc passed unexpectedly"); zassert_is_null(ptr, "malloc passed unexpectedly");
free(ptr); free(ptr);
ptr = NULL; ptr = NULL;
@ -210,13 +200,13 @@ void test_memalloc_max(void)
void test_main(void) void test_main(void)
{ {
ztest_test_suite(test_c_lib_dynamic_memalloc, ztest_test_suite(test_c_lib_dynamic_memalloc,
ztest_unit_test(test_malloc), ztest_user_unit_test(test_malloc),
ztest_unit_test(test_free), ztest_user_unit_test(test_free),
ztest_unit_test(test_calloc), ztest_user_unit_test(test_calloc),
ztest_unit_test(test_realloc), ztest_user_unit_test(test_realloc),
ztest_unit_test(test_reallocarray), ztest_user_unit_test(test_reallocarray),
ztest_unit_test(test_memalloc_all), ztest_user_unit_test(test_memalloc_all),
ztest_unit_test(test_memalloc_max) ztest_user_unit_test(test_memalloc_max)
); );
ztest_run_test_suite(test_c_lib_dynamic_memalloc); ztest_run_test_suite(test_c_lib_dynamic_memalloc);
} }

View file

@ -2,9 +2,9 @@ tests:
libraries.libc.minimal: libraries.libc.minimal:
extra_args: CONF_FILE=prj.conf extra_args: CONF_FILE=prj.conf
arch_exclude: posix arch_exclude: posix
tags: clib minimal_libc tags: clib minimal_libc userspace
libraries.libc.newlib: libraries.libc.newlib:
extra_args: CONF_FILE=prj_newlib.conf extra_args: CONF_FILE=prj_newlib.conf
arch_exclude: posix arch_exclude: posix
filter: TOOLCHAIN_HAS_NEWLIB == 1 filter: TOOLCHAIN_HAS_NEWLIB == 1
tags: clib newlib tags: clib newlib userspace

View file

@ -7,6 +7,10 @@
#include <ztest.h> #include <ztest.h>
#include <stdio.h> #include <stdio.h>
#include <app_memory/app_memdomain.h> #include <app_memory/app_memdomain.h>
#ifdef CONFIG_APP_SHARED_MEM
#include <misc/libc-hooks.h>
#endif
#ifdef KERNEL #ifdef KERNEL
__kernel static struct k_thread ztest_thread; __kernel static struct k_thread ztest_thread;
#endif #endif
@ -297,7 +301,13 @@ void main(void)
{ {
#ifdef CONFIG_APP_SHARED_MEM #ifdef CONFIG_APP_SHARED_MEM
struct k_mem_partition *parts[] = { 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. /* Ztests just have one memory domain with one partition.