Skip to content

Commit bc234fb

Browse files
keith-packardcarlescufi
authored andcommitted
libc/picolibc: Rework malloc arena setup
Picolibc inherited its malloc arena configuration from newlib instead of from minimal libc. This ended up making it a bit too fragile to run the full set of zephyr tests. In particular: * Z_MALLOC_PARTITION_EXISTS would get set when not used * Setting an arena size depended on a bunch of other values, including whether the system had an MMU or MPU, and whether the MPU required power-of-two alignment or not. This patch cleans things up so that there is a single heap size specifier, PICOLIBC_HEAP_SIZE. * If PICOLIBC_HEAP_SIZE is positive, this sets the size of the heap. On MMU systems, picolibc will only use the remaining memory if that's smaller. * If PICOLIBC_HEAP_SIZE is zero, then there is no heap available and malloc will always fail. This also disables Z_MALLOC_PARTITION_EXISTS. * If PICOLIBC_HEAP_SIZE is negative, then picolibc uses all remaining memory for the malloc heap. The defaults are designed to allow tests to work without requiring additional settings. * For MMU enabled systems, the default value is 1048576. It would be nice to have this use 'all available memory', but that's difficult to manage as the API which returns free memory (k_mem_free_get) doesn't take into account the amount of free virtual address space. * For MPU enabled systems which require power-of-two aligned MPU regions, the default value is 64kB. * For other systems, the default value is -1, indicating that all available memory be used for the malloc arena. Signed-off-by: Keith Packard <[email protected]>
1 parent 34473f6 commit bc234fb

File tree

3 files changed

+128
-73
lines changed

3 files changed

+128
-73
lines changed

include/zephyr/sys/libc-hooks.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,17 @@ extern struct k_mem_partition z_malloc_partition;
5757
*/
5858
#define Z_MALLOC_PARTITION_EXISTS 1
5959
#endif /* CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE > 0 */
60+
6061
#elif defined(CONFIG_PICOLIBC)
61-
/* If we are using picolibc, the heap arena is in one of two areas:
62-
* - If we have an MPU that requires power of two alignment, the heap bounds
63-
* must be specified in Kconfig via CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE.
64-
* - Otherwise, the heap arena on most arches starts at a suitably
65-
* aligned base addreess after the `_end` linker symbol, through to the end
66-
* of system RAM.
62+
/*
63+
* When using picolibc, we need z_malloc_partition whenever
64+
* the heap size is not zero and there is an mpu or an mmu
6765
*/
68-
#if (!defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) || \
69-
(defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) && \
70-
CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE))
66+
#if CONFIG_PICOLIBC_HEAP_SIZE != 0 && \
67+
(defined(CONFIG_MPU) || defined(CONFIG_MMU))
7168
#define Z_MALLOC_PARTITION_EXISTS 1
72-
extern struct k_mem_partition z_malloc_partition;
7369
#endif
70+
7471
#endif /* CONFIG_PICOLIBC */
7572

7673
#ifdef Z_MALLOC_PARTITION_EXISTS

lib/libc/picolibc/Kconfig

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,24 @@ config PICOLIBC_USE_MODULE
88
help
99
Use picolibc module instead of picolibc included with toolchain
1010

11-
config PICOLIBC_ALIGNED_HEAP_SIZE
12-
int "Picolibc aligned heap size (bytes)"
13-
depends on MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
14-
depends on USERSPACE
15-
default 0
11+
config PICOLIBC_HEAP_SIZE
12+
int "Picolibc heap size (bytes)"
13+
default 1048576 if MMU
14+
default 65536 if USERSPACE && MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
15+
default -1
1616
help
17+
Indicates the amount of memory which will be used by the picolibc
18+
malloc() heap.
19+
1720
If user mode is enabled, and MPU hardware has requirements that
1821
regions be sized to a power of two and aligned to their size,
19-
and user mode threads need to access this heap, then this is necessary
20-
to properly define an MPU region for the heap.
22+
then this must be defined as a power of two or a compile error
23+
will result.
2124

22-
If this is left at 0, then remaining system RAM will be used for this
23-
area and it may not be possible to program it as an MPU region.
25+
If set to zero, then no malloc() heap will be available.
2426

25-
config PICOLIBC_LIBC_MAX_MAPPED_REGION_SIZE
26-
int "Maximum memory mapped for picolibc heap"
27-
depends on MMU
28-
default 1048576
29-
help
30-
On MMU-based systems, indicates the maximum amount of memory which
31-
will be used for the picolibc malloc() heap. The actual amount of
32-
memory used will be the minimum of this value and the amount of
33-
free physical memory at kernel boot.
27+
If set to -1, then all remaining system RAM will be used for this
28+
area.
3429

3530
config PICOLIBC_IO_LONG_LONG
3631
bool "support for long long in integer-only printf/scanf"

lib/libc/picolibc/libc-hooks.c

Lines changed: 108 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,77 +27,138 @@
2727
#define LIBC_DATA K_APP_DMEM(z_libc_partition)
2828

2929
#ifdef CONFIG_MMU
30-
#ifdef CONFIG_USERSPACE
30+
31+
/* When there is an MMU, allocate the heap at startup time */
32+
33+
# if Z_MALLOC_PARTITION_EXISTS
3134
struct k_mem_partition z_malloc_partition;
32-
#endif
35+
# endif
3336

3437
LIBC_BSS static unsigned char *heap_base;
3538
LIBC_BSS static size_t max_heap_size;
3639

37-
#define HEAP_BASE ((uintptr_t) heap_base)
38-
#define MAX_HEAP_SIZE max_heap_size
39-
#define USE_MALLOC_PREPARE 1
40+
# define HEAP_BASE ((uintptr_t) heap_base)
41+
# define MAX_HEAP_SIZE max_heap_size
42+
43+
# define USE_MALLOC_PREPARE 1
44+
45+
#elif CONFIG_PICOLIBC_HEAP_SIZE == 0
46+
47+
/* No heap at all */
48+
# define HEAP_BASE 0
49+
# define MAX_HEAP_SIZE 0
50+
51+
#else /* CONFIG_PICOLIBC_HEAP_SIZE != 0 */
52+
53+
/* Figure out alignment requirement */
54+
# ifdef Z_MALLOC_PARTITION_EXISTS
55+
56+
# if defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT)
57+
# if CONFIG_PICOLIBC_HEAP_SIZE < 0
58+
# error CONFIG_PICOLIBC_HEAP_SIZE must be defined on this target
59+
# endif
60+
# if (CONFIG_PICOLIBC_HEAP_SIZE & (CONFIG_PICOLIBC_HEAP_SIZE - 1)) != 0
61+
# error CONFIG_PICOLIBC_HEAP_SIZE must be power of two on this target
62+
# endif
63+
# define HEAP_ALIGN CONFIG_PICOLIBC_HEAP_SIZE
64+
# elif defined(CONFIG_ARM) || defined(CONFIG_ARM64)
65+
# define HEAP_ALIGN CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE
66+
# elif defined(CONFIG_ARC)
67+
# define HEAP_ALIGN Z_ARC_MPU_ALIGN
68+
# elif defined(CONFIG_RISCV)
69+
# define HEAP_ALIGN Z_RISCV_STACK_GUARD_SIZE
70+
# else
71+
/*
72+
* Default to 64-bytes; we'll get a run-time error if this doesn't work.
73+
*/
74+
# define HEAP_ALIGN 64
75+
# endif /* CONFIG_<arch> */
76+
77+
# else /* Z_MALLOC_PARTITION_EXISTS */
78+
79+
# define HEAP_ALIGN sizeof(double)
4080

41-
#elif CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE
81+
# endif /* else Z_MALLOC_PARTITION_EXISTS */
82+
83+
# if CONFIG_PICOLIBC_HEAP_SIZE > 0
84+
85+
/* Static allocation of heap in BSS */
86+
87+
# ifdef Z_MALLOC_PARTITION_EXISTS
4288
K_APPMEM_PARTITION_DEFINE(z_malloc_partition);
43-
#define MALLOC_BSS K_APP_BMEM(z_malloc_partition)
89+
# define MALLOC_BSS K_APP_BMEM(z_malloc_partition)
90+
# else
91+
# define MALLOC_BSS
92+
# endif
93+
94+
MALLOC_BSS static unsigned char __aligned(HEAP_ALIGN)
95+
heap_base[CONFIG_PICOLIBC_HEAP_SIZE];
4496

45-
/* Compiler will throw an error if the provided value isn't a power of two */
46-
MALLOC_BSS static unsigned char __aligned(CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE)
47-
heap_base[CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE];
97+
# define HEAP_BASE ((uintptr_t) heap_base)
98+
# define MAX_HEAP_SIZE CONFIG_PICOLIBC_HEAP_SIZE
4899

49-
#define HEAP_BASE ((uintptr_t) heap_base)
50-
#define MAX_HEAP_SIZE CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE
100+
# else /* CONFIG_PICOLIBC_HEAP_SIZE > 0 */
51101

52-
#else /* Not MMU or CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE */
53-
/* Heap base and size are determined based on the available unused SRAM,
54-
* in the interval from a properly aligned address after the linker symbol
55-
* `_end`, to the end of SRAM
102+
/*
103+
* Heap base and size are determined based on the available unused SRAM, in the
104+
* interval from a properly aligned address after the linker symbol `_end`, to
105+
* the end of SRAM
56106
*/
57-
#define USED_RAM_END_ADDR POINTER_TO_UINT(&_end)
58107

59-
#ifdef Z_MALLOC_PARTITION_EXISTS
60-
/* Need to be able to program a memory protection region from HEAP_BASE
61-
* to the end of RAM so that user threads can get at it.
62-
* Implies that the base address needs to be suitably aligned since the
63-
* bounds have to go in a k_mem_partition.
108+
# ifdef Z_MALLOC_PARTITION_EXISTS
109+
/*
110+
* Need to be able to program a memory protection region from HEAP_BASE to the
111+
* end of RAM so that user threads can get at it. Implies that the base address
112+
* needs to be suitably aligned since the bounds have to go in a
113+
* k_mem_partition.
64114
*/
65115
struct k_mem_partition z_malloc_partition;
66-
/* TODO: Need a generic Kconfig for the MPU region granularity */
67-
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
68-
#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, \
69-
CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE)
70-
#elif defined(CONFIG_ARC)
71-
#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, Z_ARC_MPU_ALIGN)
72-
#elif defined(CONFIG_RISCV)
73-
#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, Z_RISCV_STACK_GUARD_SIZE)
74-
#else
75-
#error "Unsupported platform"
76-
#endif /* CONFIG_<arch> */
77-
#else /* !Z_MALLOC_PARTITION_EXISTS */
78-
/* No partition, heap can just start wherever _end is */
79-
#define HEAP_BASE USED_RAM_END_ADDR
80-
#endif /* Z_MALLOC_PARTITION_EXISTS */
81-
82-
#ifdef CONFIG_XTENSA
116+
117+
# define USE_MALLOC_PREPARE 1
118+
119+
# endif /* Z_MALLOC_PARTITION_EXISTS */
120+
121+
# define USED_RAM_END_ADDR POINTER_TO_UINT(&_end)
122+
123+
/*
124+
* No partition, heap can just start wherever _end is, with
125+
* suitable alignment
126+
*/
127+
128+
# define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, HEAP_ALIGN)
129+
130+
# ifdef CONFIG_XTENSA
83131
extern char _heap_sentry[];
84-
#define MAX_HEAP_SIZE (POINTER_TO_UINT(_heap_sentry) - HEAP_BASE)
85-
#else
86-
#define MAX_HEAP_SIZE (KB(CONFIG_SRAM_SIZE) - \
132+
# define MAX_HEAP_SIZE (POINTER_TO_UINT(_heap_sentry) - HEAP_BASE)
133+
# else
134+
# define MAX_HEAP_SIZE (KB(CONFIG_SRAM_SIZE) - \
87135
(HEAP_BASE - CONFIG_SRAM_BASE_ADDRESS))
88-
#endif
136+
# endif /* CONFIG_XTENSA */
89137

90-
#endif /* CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE */
138+
# endif /* CONFIG_PICOLIBC_HEAP_SIZE < 0 */
91139

140+
#endif /* CONFIG_PICOLIBC_HEAP_SIZE == 0 */
141+
142+
#ifdef USE_MALLOC_PREPARE
92143

93144
static int malloc_prepare(const struct device *unused)
94145
{
95146
ARG_UNUSED(unused);
96147

97148
#ifdef CONFIG_MMU
98-
max_heap_size = MIN(CONFIG_PICOLIBC_LIBC_MAX_MAPPED_REGION_SIZE,
149+
150+
/* With an MMU, the heap is allocated at runtime */
151+
152+
# if CONFIG_PICOLIBC_HEAP_SIZE < 0
153+
# define MMU_MAX_HEAP_SIZE PTRDIFF_MAX
154+
# else
155+
# define MMU_MAX_HEAP_SIZE CONFIG_PICOLIBC_HEAP_SIZE
156+
# endif
157+
max_heap_size = MIN(MMU_MAX_HEAP_SIZE,
99158
k_mem_free_get());
100159

160+
max_heap_size &= ~(CONFIG_MMU_PAGE_SIZE-1);
161+
101162
if (max_heap_size != 0) {
102163
heap_base = k_mem_map(max_heap_size, K_MEM_PERM_RW);
103164
__ASSERT(heap_base != NULL,
@@ -116,6 +177,8 @@ static int malloc_prepare(const struct device *unused)
116177

117178
SYS_INIT(malloc_prepare, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
118179

180+
#endif /* USE_MALLOC_PREPARE */
181+
119182
LIBC_BSS static uintptr_t heap_sz;
120183

121184
static int (*_stdout_hook)(int);

0 commit comments

Comments
 (0)