From 7fe9b3f2e1b7917ff8d19fc62e1086364f290bc2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 20 Dec 2021 14:56:39 -0800 Subject: [PATCH 1/4] arch/arm: Use TPIDRURO on cortex-a too V7-A also supports TPIDRURO, so go ahead and use that for TLS, enabling thread local storage for the other ARM architectures. Add __aeabi_read_tp function in case code was compiled to use that. Signed-off-by: Keith Packard --- arch/Kconfig | 2 +- arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt | 1 + .../core/aarch32/cortex_a_r/__aeabi_read_tp.S | 17 +++++++++++++++++ arch/arm/core/aarch32/swap_helper.S | 2 +- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S diff --git a/arch/Kconfig b/arch/Kconfig index a679897889e154..c67e59600b7e3f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -33,7 +33,7 @@ config ARM # FIXME: current state of the code for all ARM requires this, but # is really only necessary for Cortex-M with ARM MPU! select GEN_PRIV_STACKS - select ARCH_HAS_THREAD_LOCAL_STORAGE if CPU_AARCH32_CORTEX_R || CPU_CORTEX_M + select ARCH_HAS_THREAD_LOCAL_STORAGE if CPU_AARCH32_CORTEX_R || CPU_CORTEX_M || CPU_AARCH32_CORTEX_A help ARM architecture diff --git a/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt b/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt index a044a2b20bb85f..d03d53680fc781 100644 --- a/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt +++ b/arch/arm/core/aarch32/cortex_a_r/CMakeLists.txt @@ -15,3 +15,4 @@ zephyr_library_sources( ) zephyr_library_sources_ifdef(CONFIG_USERSPACE thread.c) +zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE __aeabi_read_tp.S) diff --git a/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S new file mode 100644 index 00000000000000..48bc5cf7c0e4b9 --- /dev/null +++ b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +_ASM_FILE_PROLOGUE + +GTEXT(__aeabi_read_tp) + +GDATA(z_arm_tls_ptr) + +SECTION_FUNC(text, __aeabi_read_tp) + mrc 15, 0, r0, c13, c0, 3 + bx lr diff --git a/arch/arm/core/aarch32/swap_helper.S b/arch/arm/core/aarch32/swap_helper.S index 90452186e46c5a..2f9518af26573b 100644 --- a/arch/arm/core/aarch32/swap_helper.S +++ b/arch/arm/core/aarch32/swap_helper.S @@ -186,7 +186,7 @@ out_fp_endif: adds r4, r2, r4 ldr r0, [r4] -#if defined(CONFIG_CPU_AARCH32_CORTEX_R) +#if defined(CONFIG_CPU_AARCH32_CORTEX_R) || defined(CONFIG_CPU_AARCH32_CORTEX_A) /* Store TLS pointer in the "Process ID" register. * This register is used as a base pointer to all * thread variables with offsets added by toolchain. From 96c1017f565c4a809f18a72fde4a8e12e07c545d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Oct 2020 19:07:50 -0700 Subject: [PATCH 2/4] lib/libc: Add picolibc support (aarch32, aarch64 and RISC-V) [v15] Picolibc is a fork of newlib designed and tested on embedded systems. It offers a smaller memory footprint (both ROM and RAM), and native TLS support, which uses the Zephyr TLS support. By default, the full printf version is included in the executable, which includes exact floating point and long long input and output. A configuration option has been added to switch to the integer-only version (which also omits long long support). Here are some size comparisons using qemu-cortex-m3 and this application (parameters passed to printf to avoid GCC optimizing it into puts): void main(void) { printf("Hello World! %s %d\n", CONFIG_BOARD, 12); } FLASH SRAM minimal 8696 3952 picolibc int 7600 3960 picolibc float 12304 3960 newlib-nano int 11696 4128 newlib-nano float 30516 4496 newlib 34800 6112 --- v2: Include picolibc-tls.ld v3: Document usage in guides/c_library.rst and getting_started/toolchain_other_x_compilers.rst v4: Lost the lib/libc/picolibc directory somehow! v5: Add PICOLIBC_ALIGNED_HEAP_SIZE configuration option. Delete PICOLIBC_SEMIHOST option support code v6: Don't allocate static RAM for TLS values; TLS values only need to be allocated for each thread. v7: Use arm coprocessor for TLS pointer storage where supported for compatibility with the -mtp=cp15 compiler option (or when the target cpu type selects this option) Add a bunch of tests Round TLS segment up to stack alignment so that overall stack remains correctly aligned Add aarch64 support Rebase to upstream head v8: Share NEWLIB, NEWLIB_NANO and PICOLIBC library configuration variables in a single LIBC_PARTITIONS variable instead of having separate PICOLIBC_PART and NEWLIB_PART variables. v9: Update docs to reference pending sdk-ng support for picolibc v10: Support memory protection by creating a partition for picolibc shared data and any pre-defined picolibc heap. v11: Fix formatting in arch/arm/core/aarch64/switch.S v12: Remove TLS support from this patch now that TLS is upstream Require THREAD_LOCAL_STORAGE when using PICOLIBC for architectures that support it. v13: Merge errno changes as they're only needed for picolibc. Adapt cmake changes suggested by Torsten Tejlmand Rasmussen v14: Update to picolibc 1.7 and newer (new stdin/stdout/stderr ABI) v15: Respond to comments from dcpleung: * switch kernel/errno to use CONFIG_LIBC_ERRNO instead of CONFIG_PICOLIBC * Add comment to test/lib/sprintf as to why the %n test was disabled for picolibc. Signed-off-by: Keith Packard --- CMakeLists.txt | 3 + .../core/aarch32/cortex_a_r/__aeabi_read_tp.S | 7 +- cmake/toolchain/gnuarmemb/generic.cmake | 1 + cmake/toolchain/xtools/generic.cmake | 1 + include/sys/libc-hooks.h | 20 +- kernel/Kconfig | 8 + kernel/errno.c | 3 + lib/libc/CMakeLists.txt | 2 + lib/libc/Kconfig | 33 +++ lib/libc/picolibc/CMakeLists.txt | 41 +++ lib/libc/picolibc/libc-hooks.c | 271 ++++++++++++++++++ tests/lib/c_lib/testcase.yaml | 6 + tests/lib/mem_alloc/prj_picolibc.conf | 4 + tests/lib/mem_alloc/testcase.yaml | 6 + tests/lib/sprintf/prj_picolibc.conf | 3 + tests/lib/sprintf/src/main.c | 8 + tests/lib/sprintf/testcase.yaml | 4 + tests/posix/common/testcase.yaml | 5 + 18 files changed, 421 insertions(+), 5 deletions(-) create mode 100644 lib/libc/picolibc/CMakeLists.txt create mode 100644 lib/libc/picolibc/libc-hooks.c create mode 100644 tests/lib/mem_alloc/prj_picolibc.conf create mode 100644 tests/lib/sprintf/prj_picolibc.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index d754ff98eac31a..282fbb91158d2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -917,6 +917,9 @@ if(CONFIG_USERSPACE) if(CONFIG_NEWLIB_LIBC_NANO) set(NEWLIB_PART -l libc_nano.a z_libc_partition -l libm_nano.a z_libc_partition) endif() + if(CONFIG_PICOLIBC) + set(NEWLIB_PART -l libc.a z_libc_partition) + endif() add_custom_command( OUTPUT ${APP_SMEM_UNALIGNED_LD} ${APP_SMEM_PINNED_UNALIGNED_LD} diff --git a/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S index 48bc5cf7c0e4b9..7b5bb5c1bf61b0 100644 --- a/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S +++ b/arch/arm/core/aarch32/cortex_a_r/__aeabi_read_tp.S @@ -10,8 +10,11 @@ _ASM_FILE_PROLOGUE GTEXT(__aeabi_read_tp) -GDATA(z_arm_tls_ptr) - SECTION_FUNC(text, __aeabi_read_tp) + /* + * Load TLS base address, which is stored in the TPIDRURO register, also + * known as the "Process ID" register. Refer to the code in z_arm_pendsv + * to see where this register is set. + */ mrc 15, 0, r0, c13, c0, 3 bx lr diff --git a/cmake/toolchain/gnuarmemb/generic.cmake b/cmake/toolchain/gnuarmemb/generic.cmake index 1ea30ab70debcd..97bff4272f6b17 100644 --- a/cmake/toolchain/gnuarmemb/generic.cmake +++ b/cmake/toolchain/gnuarmemb/generic.cmake @@ -28,5 +28,6 @@ set(SYSROOT_TARGET arm-none-eabi) set(CROSS_COMPILE ${TOOLCHAIN_HOME}/bin/${CROSS_COMPILE_TARGET}-) set(SYSROOT_DIR ${TOOLCHAIN_HOME}/${SYSROOT_TARGET}) set(TOOLCHAIN_HAS_NEWLIB ON CACHE BOOL "True if toolchain supports newlib") +set(TOOLCHAIN_HAS_PICOLIBC ON CACHE BOOL "True if toolchain supports picolibc") message(STATUS "Found toolchain: gnuarmemb (${GNUARMEMB_TOOLCHAIN_PATH})") diff --git a/cmake/toolchain/xtools/generic.cmake b/cmake/toolchain/xtools/generic.cmake index f4836eb5775de4..b6ff678c6722db 100644 --- a/cmake/toolchain/xtools/generic.cmake +++ b/cmake/toolchain/xtools/generic.cmake @@ -33,6 +33,7 @@ set(SYSROOT_TARGET ${CROSS_COMPILE_TARGET}) set(CROSS_COMPILE ${some_toolchain_root}/${CROSS_COMPILE_TARGET}/bin/${CROSS_COMPILE_TARGET}-) set(SYSROOT_DIR ${some_toolchain_root}/${SYSROOT_TARGET}/${SYSROOT_TARGET}) set(TOOLCHAIN_HAS_NEWLIB ON CACHE BOOL "True if toolchain supports newlib") +set(TOOLCHAIN_HAS_PICOLIBC ON CACHE BOOL "True if toolchain supports picolibc") unset(some_toolchain_root) unset(some_toolchain) diff --git a/include/sys/libc-hooks.h b/include/sys/libc-hooks.h index afa3554ed87c09..6bfd379151cc3b 100644 --- a/include/sys/libc-hooks.h +++ b/include/sys/libc-hooks.h @@ -16,7 +16,7 @@ * that need to call into the kernel as system calls */ -#if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_ARCMWDT_LIBC) +#if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_ARCMWDT_LIBC) || defined(CONFIG_PICOLIBC) /* syscall generation ignores preprocessor, ensure this is defined to ensure * we don't have compile errors @@ -56,7 +56,21 @@ extern struct k_mem_partition z_malloc_partition; */ #define Z_MALLOC_PARTITION_EXISTS 1 #endif /* CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE > 0 */ -#endif /* CONFIG_MINIMAL_LIBC */ +#elif defined(CONFIG_PICOLIBC) +/* If we are using picolibc, the heap arena is in one of two areas: + * - If we have an MPU that requires power of two alignment, the heap bounds + * must be specified in Kconfig via CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE. + * - Otherwise, the heap arena on most arches starts at a suitably + * aligned base addreess after the `_end` linker symbol, through to the end + * of system RAM. + */ +#if (!defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) || \ +(defined(CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT) && \ +CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE)) +#define Z_MALLOC_PARTITION_EXISTS 1 +extern struct k_mem_partition z_malloc_partition; +#endif +#endif /* CONFIG_PICOLIBC */ #ifdef Z_MALLOC_PARTITION_EXISTS /* Memory partition containing the libc malloc arena. Configuration controls @@ -66,7 +80,7 @@ extern struct k_mem_partition z_malloc_partition; #endif #if defined(CONFIG_NEWLIB_LIBC) || defined(CONFIG_STACK_CANARIES) || \ - defined(CONFIG_NEED_LIBC_MEM_PARTITION) +defined(CONFIG_PICOLIBC) || defined(CONFIG_NEED_LIBC_MEM_PARTITION) /* - All newlib globals will be placed into z_libc_partition. * - Minimal C library globals, if any, will be placed into * z_libc_partition. diff --git a/kernel/Kconfig b/kernel/Kconfig index 559cc05f37c8c7..4db82ffcc0213e 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -202,9 +202,17 @@ config THREAD_USERSPACE_LOCAL_DATA depends on USERSPACE default y if ERRNO && !ERRNO_IN_TLS +config LIBC_ERRNO + bool + help + Use external libc errno, not the internal one. This makes sure that + ERRNO is not set for libc configurations that provide their own + implementation. + config ERRNO bool "Errno support" default y + depends on !LIBC_ERRNO help Enable per-thread errno in the kernel. Application and library code must include errno.h provided by the C library (libc) to use the errno diff --git a/kernel/errno.c b/kernel/errno.c index 84f78593dc1763..07a8684ddd6c8c 100644 --- a/kernel/errno.c +++ b/kernel/errno.c @@ -44,10 +44,13 @@ static inline int *z_vrfy_z_errno(void) #include #else +#ifndef CONFIG_LIBC_ERRNO int *z_impl_z_errno(void) { return &_current->errno_var; } +#endif /* CONFIG_PICOLIBC */ + #endif /* CONFIG_USERSPACE */ #endif /* CONFIG_ERRNO_IN_TLS */ diff --git a/lib/libc/CMakeLists.txt b/lib/libc/CMakeLists.txt index fe72c26f7aa7dc..f8600b62c45e7c 100644 --- a/lib/libc/CMakeLists.txt +++ b/lib/libc/CMakeLists.txt @@ -8,4 +8,6 @@ elseif(CONFIG_MINIMAL_LIBC) add_subdirectory(minimal) elseif(CONFIG_ARMCLANG_STD_LIBC) add_subdirectory(armstdc) +elseif(CONFIG_PICOLIBC) + add_subdirectory(picolibc) endif() diff --git a/lib/libc/Kconfig b/lib/libc/Kconfig index 1bb1f87e747ef7..1098b52d24866f 100644 --- a/lib/libc/Kconfig +++ b/lib/libc/Kconfig @@ -29,6 +29,15 @@ config MINIMAL_LIBC help Build with minimal C library. +config PICOLIBC + bool "Picolibc library" + depends on !NATIVE_APPLICATION + select THREAD_LOCAL_STORAGE if ARCH_HAS_THREAD_LOCAL_STORAGE + select LIBC_ERRNO + help + Build with picolibc library. The picolibc library is expected to be + part of the SDK in this case. + config NEWLIB_LIBC bool "Newlib C library" depends on !NATIVE_APPLICATION @@ -53,6 +62,30 @@ endchoice # LIBC_IMPLEMENTATION config HAS_NEWLIB_LIBC_NANO bool +if PICOLIBC + +config PICOLIBC_INTEGER_PRINTF + bool "Build with picolibc integer-only printf" + help + Build with floating point printf disabled. This will reduce the size + of the image. + +config PICOLIBC_ALIGNED_HEAP_SIZE + int "Picolibc aligned heap size (bytes)" + depends on MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT + depends on USERSPACE + default 0 + help + If user mode is enabled, and MPU hardware has requirements that + regions be sized to a power of two and aligned to their size, + and user mode threads need to access this heap, then this is necessary + to properly define an MPU region for the heap. + + If this is left at 0, then remaining system RAM will be used for this + area and it may not be possible to program it as an MPU region. + +endif # PICOLIBC + if NEWLIB_LIBC config NEWLIB_LIBC_NANO diff --git a/lib/libc/picolibc/CMakeLists.txt b/lib/libc/picolibc/CMakeLists.txt new file mode 100644 index 00000000000000..ff57fcfde31862 --- /dev/null +++ b/lib/libc/picolibc/CMakeLists.txt @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(libc-hooks.c) + +# Zephyr normally uses -ffreestanding, which with current GNU toolchains +# means that the flag macros used by picolibc to signal +# support for PRI.64 macros are not present. To make them available we +# need to hook into the include path before the system files and +# explicitly include the picolibc header that provides those macros. +zephyr_include_directories(include) + +# define __LINUX_ERRNO_EXTENSIONS__ so we get errno defines like -ESHUTDOWN +# used by the network stack +zephyr_compile_definitions(__LINUX_ERRNO_EXTENSIONS__) + +zephyr_link_libraries( + m + c + gcc # Lib C depends on libgcc. + ) + +# The -T/dev/null avoids pulling in picolibc.ld +zephyr_link_libraries( + --specs=picolibc.specs + -T/dev/null + ) + +zephyr_compile_options( + --specs=picolibc.specs + -D_GNU_SOURCE + ) + +if(CONFIG_PICOLIBC_INTEGER_PRINTF) + zephyr_compile_options( + -DPICOLIBC_INTEGER_PRINTF_SCANF + ) + zephyr_link_libraries( + -DPICOLIBC_INTEGER_PRINTF_SCANF + ) +endif() diff --git a/lib/libc/picolibc/libc-hooks.c b/lib/libc/picolibc/libc-hooks.c new file mode 100644 index 00000000000000..4367c1a53f6bfc --- /dev/null +++ b/lib/libc/picolibc/libc-hooks.c @@ -0,0 +1,271 @@ +/* + * Copyright © 2021, Keith Packard + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIBC_BSS K_APP_BMEM(z_libc_partition) +#define LIBC_DATA K_APP_DMEM(z_libc_partition) + +#if CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE +K_APPMEM_PARTITION_DEFINE(z_malloc_partition); +#define MALLOC_BSS K_APP_BMEM(z_malloc_partition) + +/* Compiler will throw an error if the provided value isn't a power of two */ +MALLOC_BSS static unsigned char __aligned(CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE) + heap_base[CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE]; +#define MAX_HEAP_SIZE CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE + +#else /* CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE */ +/* Heap base and size are determined based on the available unused SRAM, + * in the interval from a properly aligned address after the linker symbol + * `_end`, to the end of SRAM + */ +#define USED_RAM_END_ADDR POINTER_TO_UINT(&_end) + +#ifdef Z_MALLOC_PARTITION_EXISTS +/* Need to be able to program a memory protection region from HEAP_BASE + * to the end of RAM so that user threads can get at it. + * Implies that the base address needs to be suitably aligned since the + * bounds have to go in a k_mem_partition. + */ +#ifdef CONFIG_MMU +/* Linker script may already have done this, but just to be safe */ +#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, CONFIG_MMU_PAGE_SIZE) +#else /* MPU-based systems */ +/* TODO: Need a generic Kconfig for the MPU region granularity */ +#if defined(CONFIG_ARM) +#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, \ + CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE) +#elif defined(CONFIG_ARC) +#define HEAP_BASE ROUND_UP(USED_RAM_END_ADDR, Z_ARC_MPU_ALIGN) +#else +#error "Unsupported platform" +#endif /* CONFIG_ */ +#endif /* !CONFIG_MMU */ +#else /* !Z_MALLOC_PARTITION_EXISTS */ +/* No partition, heap can just start wherever _end is */ +#define HEAP_BASE USED_RAM_END_ADDR +#endif /* Z_MALLOC_PARTITION_EXISTS */ + +#ifdef CONFIG_XTENSA +extern void *_heap_sentry; +#define MAX_HEAP_SIZE (POINTER_TO_UINT(&_heap_sentry) - HEAP_BASE) +#else +#define MAX_HEAP_SIZE (KB(CONFIG_SRAM_SIZE) - \ + (HEAP_BASE - CONFIG_SRAM_BASE_ADDRESS)) +#endif + +#if Z_MALLOC_PARTITION_EXISTS +struct k_mem_partition z_malloc_partition; + +static int malloc_prepare(const struct device *unused) +{ + ARG_UNUSED(unused); + + z_malloc_partition.start = 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 /* CONFIG_USERSPACE */ +#endif /* CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE */ + +LIBC_BSS static unsigned int heap_sz; + +static int (*put_hook)(int); + +static int picolibc_put(char a, FILE *out) +{ + (*put_hook)(a); + return 0; +} + +static FILE __stdio = FDEV_SETUP_STREAM(picolibc_put, NULL, NULL, 0); + +#ifdef __strong_reference +#define STDIO_ALIAS(x) __strong_reference(stdin, x); +#else +#define STDIO_ALIAS(x) FILE *const x = &__stdio; +#endif + +FILE *const stdin = &__stdio; +STDIO_ALIAS(stdout); +STDIO_ALIAS(stderr); + +void __stdout_hook_install(int (*hook)(int)) +{ + put_hook = hook; + __stdio.flags |= _FDEV_SETUP_WRITE; +} + +void __stdin_hook_install(unsigned char (*hook)(void)) +{ + __stdio.get = (int (*)(FILE *)) hook; + __stdio.flags |= _FDEV_SETUP_READ; +} + +int z_impl_zephyr_read_stdin(char *buf, int nbytes) +{ + int i = 0; + + for (i = 0; i < nbytes; i++) { + *(buf + i) = getchar(); + if ((*(buf + i) == '\n') || (*(buf + i) == '\r')) { + i++; + break; + } + } + return i; +} + +int z_impl_zephyr_write_stdout(const void *buffer, int nbytes) +{ + const char *buf = buffer; + int i; + + for (i = 0; i < nbytes; i++) { + if (*(buf + i) == '\n') { + putchar('\r'); + } + putchar(*(buf + i)); + } + return nbytes; +} + +#ifndef CONFIG_POSIX_API +int _read(int fd, char *buf, int nbytes) +{ + ARG_UNUSED(fd); + + return z_impl_zephyr_read_stdin(buf, nbytes); +} +__weak FUNC_ALIAS(_read, read, int); + +int _write(int fd, const void *buf, int nbytes) +{ + ARG_UNUSED(fd); + + return z_impl_zephyr_write_stdout(buf, nbytes); +} +__weak FUNC_ALIAS(_write, write, int); + +int _open(const char *name, int mode) +{ + return -1; +} +__weak FUNC_ALIAS(_open, open, int); + +int _close(int file) +{ + return -1; +} +__weak FUNC_ALIAS(_close, close, int); + +int _lseek(int file, int ptr, int dir) +{ + return 0; +} +__weak FUNC_ALIAS(_lseek, lseek, int); +#else +extern ssize_t write(int file, const char *buffer, size_t count); +#define _write write +#endif + +int _isatty(int file) +{ + return 1; +} +__weak FUNC_ALIAS(_isatty, isatty, int); + +int _kill(int i, int j) +{ + return 0; +} +__weak FUNC_ALIAS(_kill, kill, int); + +int _getpid(void) +{ + return 0; +} +__weak FUNC_ALIAS(_getpid, getpid, int); + +int _fstat(int file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} +__weak FUNC_ALIAS(_fstat, fstat, int); + +__weak void _exit(int status) +{ + _write(1, "exit\n", 5); + while (1) { + ; + } +} + +static LIBC_DATA SYS_SEM_DEFINE(heap_sem, 1, 1); + +void *_sbrk(int count) +{ + void *ret, *ptr; + + sys_sem_take(&heap_sem, K_FOREVER); + +#if CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE + ptr = heap_base + heap_sz; +#else + ptr = ((char *)HEAP_BASE) + heap_sz; +#endif + + if ((heap_sz + count) < MAX_HEAP_SIZE) { + heap_sz += count; + ret = ptr; + } else { + ret = (void *)-1; + } + + /* coverity[CHECKED_RETURN] */ + sys_sem_give(&heap_sem); + + return ret; +} +__weak FUNC_ALIAS(_sbrk, sbrk, void *); + +/* This function gets called if static buffer overflow detection is enabled on + * stdlib side (Picolibc here), in case such an overflow is detected. Picolibc + * provides an implementation not suitable for us, so we override it here. + */ +__weak FUNC_NORETURN void __chk_fail(void) +{ + static const char chk_fail_msg[] = "* buffer overflow detected *\n"; + + _write(2, chk_fail_msg, sizeof(chk_fail_msg) - 1); + k_oops(); + CODE_UNREACHABLE; +} + +int _gettimeofday(struct timeval *__tp, void *__tzp) +{ + ARG_UNUSED(__tp); + ARG_UNUSED(__tzp); + + return -1; +} diff --git a/tests/lib/c_lib/testcase.yaml b/tests/lib/c_lib/testcase.yaml index 33dfe0983a9b64..f63cb476533151 100644 --- a/tests/lib/c_lib/testcase.yaml +++ b/tests/lib/c_lib/testcase.yaml @@ -2,3 +2,9 @@ tests: libraries.libc: tags: clib ignore_faults platform_exclude: native_posix native_posix_64 nrf52_bsim + libraries.picolibc: + tags: clib picolibc ignore_faults + platform_exclude: native_posix native_posix_64 nrf52_bsim + extra_configs: + - CONFIG_PICOLIBC=y + - CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 diff --git a/tests/lib/mem_alloc/prj_picolibc.conf b/tests/lib/mem_alloc/prj_picolibc.conf new file mode 100644 index 00000000000000..bca686c990bd6f --- /dev/null +++ b/tests/lib/mem_alloc/prj_picolibc.conf @@ -0,0 +1,4 @@ +CONFIG_ZTEST=y +CONFIG_PICOLIBC=y +CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 +CONFIG_TEST_USERSPACE=y diff --git a/tests/lib/mem_alloc/testcase.yaml b/tests/lib/mem_alloc/testcase.yaml index 8738372cae9047..32d1619501c31b 100644 --- a/tests/lib/mem_alloc/testcase.yaml +++ b/tests/lib/mem_alloc/testcase.yaml @@ -20,3 +20,9 @@ tests: arch_exclude: posix platform_exclude: twr_ke18f native_posix_64 nrf52_bsim tags: clib minimal_libc userspace + libraries.libc.picolibc: + extra_args: CONF_FILE=prj_picolibc.conf + arch_exclude: posix + platform_exclude: twr_ke18f native_posix_64 nrf52_bsim + tags: clib picolibc userspace + filter: TOOLCHAIN_HAS_PICOLIBC == 1 diff --git a/tests/lib/sprintf/prj_picolibc.conf b/tests/lib/sprintf/prj_picolibc.conf new file mode 100644 index 00000000000000..fd34803103ff01 --- /dev/null +++ b/tests/lib/sprintf/prj_picolibc.conf @@ -0,0 +1,3 @@ +CONFIG_ZTEST=y +CONFIG_PICOLIBC=y +CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 diff --git a/tests/lib/sprintf/src/main.c b/tests/lib/sprintf/src/main.c index aaa1e7ceafa895..21b094db58927c 100644 --- a/tests/lib/sprintf/src/main.c +++ b/tests/lib/sprintf/src/main.c @@ -537,6 +537,7 @@ void test_sprintf_misc(void) if (IS_MINIMAL_LIBC_NANO) { TC_PRINT(" MINIMAL_LIBC+CPBPRINTF skipped tests\n"); } else { +#ifndef CONFIG_PICOLIBC sprintf(buffer, "test data %n test data", &count); zassert_false((count != 10), "sprintf(%%n). Expected count to be %d, not %d", @@ -545,6 +546,13 @@ void test_sprintf_misc(void) zassert_false((strcmp(buffer, "test data test data") != 0), "sprintf(%%p). Expected '%s', got '%s'", "test data test data", buffer); +#else + /* + * Picolibc doesn't include %n support as it makes format string + * bugs a more serious security issue + */ + (void) count; +#endif /*******************/ diff --git a/tests/lib/sprintf/testcase.yaml b/tests/lib/sprintf/testcase.yaml index aff5053617bf0c..2e841ccb4b7964 100644 --- a/tests/lib/sprintf/testcase.yaml +++ b/tests/lib/sprintf/testcase.yaml @@ -10,3 +10,7 @@ tests: extra_args: CONF_FILE=prj_new.conf tags: libc platform_exclude: native_posix native_posix_64 nrf52_bsim + libraries.picolibc.sprintf: + extra_args: CONF_FILE=prj_picolibc.conf + tags: clib ignore_faults picolibc + platform_exclude: native_posix native_posix_64 nrf52_bsim diff --git a/tests/posix/common/testcase.yaml b/tests/posix/common/testcase.yaml index 9d875d4a38c6c5..ceab1c307eba60 100644 --- a/tests/posix/common/testcase.yaml +++ b/tests/posix/common/testcase.yaml @@ -43,3 +43,8 @@ tests: extra_configs: - CONFIG_NEWLIB_LIBC=y - CONFIG_TEST_HW_STACK_PROTECTION=n + portability.posix.common.picolibc: + platform_exclude: nsim_sem_mpu_stack_guard nsim_em_mpu_stack_guard + extra_configs: + - CONFIG_PICOLIBC=y + - CONFIG_PICOLIBC_ALIGNED_HEAP_SIZE=8192 From 16ea72e5df7d54d9eb7fc9910df1126274aae5e6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 19 Oct 2021 21:44:52 -0700 Subject: [PATCH 3/4] lib/os: Use picolibc printf as a replacement for printk Picolibc printf is smaller than the built-in printk while offering a full POSIX implementation. Signed-off-by: Keith Packard --- include/sys/printk.h | 11 +++++++++++ lib/libc/picolibc/libc-hooks.c | 22 ++++++++++++++++++++++ lib/os/CMakeLists.txt | 5 ++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/sys/printk.h b/include/sys/printk.h index 34529f877de1fb..cd1b162d6d4ab4 100644 --- a/include/sys/printk.h +++ b/include/sys/printk.h @@ -60,11 +60,22 @@ static inline __printf_like(1, 0) void vprintk(const char *fmt, va_list ap) } #endif +#ifdef _PICOLIBC__ + +#include + +#define snprintk(...) snprintf(__VA_ARGS__) +#define vsnprintk(str, size, fmt, ap) vsnprintf(str, size, fmt, ap) + +#else + extern __printf_like(3, 4) int snprintk(char *str, size_t size, const char *fmt, ...); extern __printf_like(3, 0) int vsnprintk(char *str, size_t size, const char *fmt, va_list ap); +#endif + #ifdef __cplusplus } #endif diff --git a/lib/libc/picolibc/libc-hooks.c b/lib/libc/picolibc/libc-hooks.c index 4367c1a53f6bfc..41b31ee11c53fb 100644 --- a/lib/libc/picolibc/libc-hooks.c +++ b/lib/libc/picolibc/libc-hooks.c @@ -149,6 +149,28 @@ int z_impl_zephyr_write_stdout(const void *buffer, int nbytes) return nbytes; } +static FILE __console = FDEV_SETUP_STREAM(NULL, NULL, NULL, 0); + +void __printk_hook_install(int (*fn)(int)) +{ + __console.put = (int(*)(char, FILE *)) fn; + __console.flags |= _FDEV_SETUP_WRITE; +} + +void vprintk(const char *fmt, va_list ap) +{ + vfprintf(&__console, fmt, ap); +} + +void printk(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(&__console, fmt, ap); + va_end(ap); +} + #ifndef CONFIG_POSIX_API int _read(int fd, char *buf, int nbytes) { diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index 78202bbb7b4f06..4ed26d404d5ce8 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -14,7 +14,6 @@ zephyr_sources( fdtable.c hex.c notify.c - printk.c onoff.c rb.c sem.c @@ -26,6 +25,10 @@ zephyr_sources( multi_heap.c ) +if(NOT CONFIG_PICOLIBC) + zephyr_sources(printk.c) +endif() + zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c) zephyr_sources_ifdef(CONFIG_CBPRINTF_NANO cbprintf_nano.c) From a0d98b33ffc92c16b4a6283502f6fdf264efd1fe Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 19 Oct 2021 21:49:15 -0700 Subject: [PATCH 4/4] lib/os: Replace cbvprintf when using picolibc [v2] Picolibc already provides the functionality offered by cbprintf, so there's no reason to use the larger and less functional version included in zephyr. Signed-off-by: Keith Packard --- v2: Add Kconfig changes to make relevant cbprintf options depend on !PICOLIBC --- lib/libc/picolibc/libc-hooks.c | 26 ++++++++++++++++++++++++++ lib/os/CMakeLists.txt | 5 ++--- lib/os/Kconfig.cbprintf | 4 ++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/libc/picolibc/libc-hooks.c b/lib/libc/picolibc/libc-hooks.c index 41b31ee11c53fb..2a57e613976d35 100644 --- a/lib/libc/picolibc/libc-hooks.c +++ b/lib/libc/picolibc/libc-hooks.c @@ -171,6 +171,32 @@ void printk(const char *fmt, ...) va_end(ap); } +#include + +struct cb_bits { + FILE f; + cbprintf_cb out; + void *ctx; +}; + +static int cbputc(char c, FILE *_s) +{ + struct cb_bits *s = (struct cb_bits *) _s; + + (*s->out) (c, s->ctx); + return 0; +} + +int cbvprintf(cbprintf_cb out, void *ctx, const char *fp, va_list ap) +{ + struct cb_bits s = { + .f = FDEV_SETUP_STREAM(cbputc, NULL, NULL, _FDEV_SETUP_WRITE), + .out = out, + .ctx = ctx, + }; + return vfprintf(&s.f, fp, ap); +} + #ifndef CONFIG_POSIX_API int _read(int fd, char *buf, int nbytes) { diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index 4ed26d404d5ce8..207e17a84de589 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -27,11 +27,10 @@ zephyr_sources( if(NOT CONFIG_PICOLIBC) zephyr_sources(printk.c) + zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c) + zephyr_sources_ifdef(CONFIG_CBPRINTF_NANO cbprintf_nano.c) endif() -zephyr_sources_ifdef(CONFIG_CBPRINTF_COMPLETE cbprintf_complete.c) -zephyr_sources_ifdef(CONFIG_CBPRINTF_NANO cbprintf_nano.c) - zephyr_sources_ifdef(CONFIG_JSON_LIBRARY json.c) zephyr_sources_ifdef(CONFIG_RING_BUFFER ring_buffer.c) diff --git a/lib/os/Kconfig.cbprintf b/lib/os/Kconfig.cbprintf index 4a4296c038d0ed..dd8a11d1172e04 100644 --- a/lib/os/Kconfig.cbprintf +++ b/lib/os/Kconfig.cbprintf @@ -3,6 +3,7 @@ choice CBPRINTF_IMPLEMENTATION prompt "Capabilities of cbprintf implementation" + depends on !PICOLIBC default CBPRINTF_COMPLETE config CBPRINTF_COMPLETE @@ -26,6 +27,7 @@ endchoice # CBPRINTF_IMPLEMENTATION choice CBPRINTF_INTEGRAL_CONV prompt "Control range of convertible integer values" + depends on !PICOLIBC default CBPRINTF_FULL_INTEGRAL # 01: 0% / 0 B (01 / 00) @@ -85,6 +87,7 @@ config CBPRINTF_FP_A_SUPPORT # 40: -15% / -508 B (46 / 06) config CBPRINTF_FP_ALWAYS_A bool "Select %a format for all floating point specifications" + depends on !PICOLIBC select CBPRINTF_FP_A_SUPPORT help The %a format for floats requires significantly less code than the @@ -107,6 +110,7 @@ config CBPRINTF_N_SPECIFIER # 180: 18% / 138 B (180 / 80) [NANO] config CBPRINTF_LIBC_SUBSTS bool "Generate C-library compatible functions using cbprintf" + depends on !PICOLIBC help If selected wrappers are generated for various C library functions using the cbprintf formatter underneath. The wrappers use the C