TF-RMM Release v0.1.0
This is the first external release of TF-RMM and provides a reference
implementation of Realm Management Monitor (RMM) as specified by the
RMM Beta0 specification[1].
The `docs/readme.rst` has more details about the project and
`docs/getting_started/getting-started.rst` has details on how to get
started with TF-RMM.
[1] https://developer.arm.com/documentation/den0137/1-0bet0/?lang=en
Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: I205ef14c015e4a37ae9ae1a64e4cd22eb8da746e
diff --git a/lib/libc/CMakeLists.txt b/lib/libc/CMakeLists.txt
new file mode 100644
index 0000000..1631332
--- /dev/null
+++ b/lib/libc/CMakeLists.txt
@@ -0,0 +1,38 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+if(NOT RMM_ARCH STREQUAL fake_host)
+ add_library(rmm-lib-libc)
+
+ target_link_libraries(rmm-lib-libc
+ PUBLIC rmm-lib-arch
+ rmm-lib-common
+ rmm-lib-debug)
+
+ target_include_directories(rmm-lib-libc SYSTEM
+ PUBLIC "include")
+
+ target_sources(rmm-lib-libc
+ PRIVATE "src/abort.c"
+ "src/assert.c"
+ "src/memcmp.c"
+ "src/memcpy.c"
+ "src/memmove.c"
+ "src/printf.c"
+ "src/strlen.c"
+ "src/strcmp.c"
+ "src/strlcpy.c"
+ "src/strncmp.c"
+ "src/strnlen.c")
+
+ target_sources(rmm-lib-libc
+ PRIVATE
+ "src/aarch64/memset.S")
+
+target_compile_definitions(rmm-lib-libc
+ PRIVATE "PRINTF_DISABLE_SUPPORT_FLOAT")
+else()
+ add_library(rmm-lib-libc INTERFACE)
+endif()
diff --git a/lib/libc/include/stdio.h b/lib/libc/include/stdio.h
new file mode 100644
index 0000000..e7f2355
--- /dev/null
+++ b/lib/libc/include/stdio.h
@@ -0,0 +1,117 @@
+/*
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Copyright Marco Paland (info@paland.com), PALANDesign Hannover, Germany
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * n the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * \brief Tiny printf, sprintf and snprintf implementation, optimized for
+ * speed on
+ * embedded systems with a very limited resources.
+ * Use this instead of bloated standard/newlib printf.
+ * These routines are thread safe and reentrant.
+ */
+
+#ifndef STDIO_H
+#define STDIO_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Output a character to a custom device like UART, used by the printf() function
+ * This function is declared here only. You have to write your custom implementation somewhere
+ * \param character: Character to output
+ */
+void _putchar(char character);
+
+
+/**
+ * Tiny printf implementation
+ * You have to implement _putchar if you use printf()
+ * To avoid conflicts with the regular printf() API it is overridden by macro defines
+ * and internal underscore-appended functions like printf_() are used
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are written into the array, not counting the terminating null character
+ */
+#define printf printf_
+int printf_(const char *format, ...);
+
+
+/**
+ * Tiny sprintf implementation
+ * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
+ * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define sprintf sprintf_
+int sprintf_(char *buffer, const char *format, ...);
+
+
+/**
+ * Tiny snprintf/vsnprintf implementation
+ * \param buffer A pointer to the buffer where to store the formatted string
+ * \param count The maximum number of characters to store in the buffer, including a terminating null character
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that COULD have been written into the buffer, not counting the terminating
+ * null character. A value equal or larger than count indicates truncation. Only when the returned value
+ * is non-negative and less than count, the string has been completely written.
+ */
+#define snprintf snprintf_
+#define vsnprintf vsnprintf_
+int snprintf_(char *buffer, size_t count, const char *format, ...);
+int vsnprintf_(char *buffer, size_t count, const char *format, va_list va);
+
+
+/**
+ * Tiny vprintf implementation
+ * \param format A string that specifies the format of the output
+ * \param va A value identifying a variable arguments list
+ * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
+ */
+#define vprintf vprintf_
+int vprintf_(const char *format, va_list va);
+
+
+/**
+ * printf with output function
+ * You may use this as dynamic alternative to printf() with its fixed _putchar() output
+ * \param out An output function which takes one character and an argument pointer
+ * \param arg An argument pointer for user data passed to output function
+ * \param format A string that specifies the format of the output
+ * \return The number of characters that are sent to the output function, not counting the terminating null character
+ */
+int fctprintf(void (*out)(char character, void *arg), void *arg,
+ const char *format, ...);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* STDIO_H */
diff --git a/lib/libc/include/string.h b/lib/libc/include/string.h
new file mode 100644
index 0000000..8772708
--- /dev/null
+++ b/lib/libc/include/string.h
@@ -0,0 +1,22 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+int memcmp(const void *s1, const void *s2, size_t n);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+
+int strcmp(const char *s1, const char *s2);
+size_t strlen(const char *s);
+size_t strlcpy(char *dst, const char *src, size_t dsize);
+int strncmp(const char *s1, const char *s2, size_t n);
+size_t strnlen(const char *s, size_t maxlen);
+
+#endif
diff --git a/lib/libc/src/aarch64/memset.S b/lib/libc/src/aarch64/memset.S
new file mode 100644
index 0000000..32c04e0
--- /dev/null
+++ b/lib/libc/src/aarch64/memset.S
@@ -0,0 +1,63 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <asm_macros.S>
+
+ .global memset
+
+/* -----------------------------------------------------------------------
+ * void *memset(void *dst, int val, size_t count)
+ *
+ * Copy the value of 'val' (converted to an unsigned char) into
+ * each of the first 'count' characters of the object pointed to by 'dst'.
+ *
+ * Returns the value of 'dst'.
+ * -----------------------------------------------------------------------
+ */
+func memset
+ cbz x2, exit /* exit if 'count' = 0 */
+ mov x3, x0 /* keep x0 */
+ tst x0, #7
+ b.eq aligned /* 8-bytes aligned */
+
+ /* Unaligned 'dst' */
+unaligned:
+ strb w1, [x3], #1
+ subs x2, x2, #1
+ b.eq exit /* exit if 0 */
+ tst x3, #7
+ b.ne unaligned /* continue while unaligned */
+
+ /* 8-bytes aligned */
+aligned:cbz x1, x1_zero
+ bfi w1, w1, #8, #8 /* propagate 'val' */
+ bfi w1, w1, #16, #16
+ bfi x1, x1, #32, #32
+
+x1_zero:ands x4, x2, #~0x3f
+ b.eq less_64
+
+write_64:
+ .rept 4
+ stp x1, x1, [x3], #16 /* write 64 bytes in a loop */
+ .endr
+ subs x4, x4, #64
+ b.ne write_64
+less_64:tbz w2, #5, less_32 /* < 32 bytes */
+ stp x1, x1, [x3], #16 /* write 32 bytes */
+ stp x1, x1, [x3], #16
+less_32:tbz w2, #4, less_16 /* < 16 bytes */
+ stp x1, x1, [x3], #16 /* write 16 bytes */
+less_16:tbz w2, #3, less_8 /* < 8 bytes */
+ str x1, [x3], #8 /* write 8 bytes */
+less_8: tbz w2, #2, less_4 /* < 4 bytes */
+ str w1, [x3], #4 /* write 4 bytes */
+less_4: tbz w2, #1, less_2 /* < 2 bytes */
+ strh w1, [x3], #2 /* write 2 bytes */
+less_2: tbz w2, #0, exit
+ strb w1, [x3] /* write 1 byte */
+exit: ret
+
+endfunc memset
diff --git a/lib/libc/src/abort.c b/lib/libc/src/abort.c
new file mode 100644
index 0000000..ad4dd4e
--- /dev/null
+++ b/lib/libc/src/abort.c
@@ -0,0 +1,12 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <debug.h>
+
+void abort(void)
+{
+ ERROR("ABORT\n");
+ panic();
+}
diff --git a/lib/libc/src/assert.c b/lib/libc/src/assert.c
new file mode 100644
index 0000000..563da4f
--- /dev/null
+++ b/lib/libc/src/assert.c
@@ -0,0 +1,26 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+
+void __assert(const char *file, int line, const char *expression)
+{
+ ERROR("Assertion \"%s\" failed %s:%d\n", expression, file, line);
+ while (true) {
+ wfe();
+ }
+}
+
+void __assert_func(const char *file, int line, const char *func, const char *expression)
+{
+ ERROR("Assertion \"%s\" failed %s:%d, %s\n", expression, file, line, func);
+ while (true) {
+ wfe();
+ }
+}
+
+
diff --git a/lib/libc/src/memcmp.c b/lib/libc/src/memcmp.c
new file mode 100644
index 0000000..9ef89e6
--- /dev/null
+++ b/lib/libc/src/memcmp.c
@@ -0,0 +1,25 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+int memcmp(const void *s1, const void *s2, size_t len)
+{
+ const unsigned char *s = s1;
+ const unsigned char *d = s2;
+ unsigned char sc;
+ unsigned char dc;
+
+ while (len--) {
+ sc = *s++;
+ dc = *d++;
+ if (sc - dc) {
+ return (sc - dc);
+ }
+ }
+
+ return 0;
+}
diff --git a/lib/libc/src/memcpy.c b/lib/libc/src/memcpy.c
new file mode 100644
index 0000000..47fee23
--- /dev/null
+++ b/lib/libc/src/memcpy.c
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+void *memcpy(void *dst, const void *src, size_t len)
+{
+ const char *s = src;
+ char *d = dst;
+
+ while (len--) {
+ *d++ = *s++;
+ }
+
+ return dst;
+}
diff --git a/lib/libc/src/memmove.c b/lib/libc/src/memmove.c
new file mode 100644
index 0000000..b8acb65
--- /dev/null
+++ b/lib/libc/src/memmove.c
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Jon Medhurst <tixy@linaro.org>
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+void *memmove(void *dst, const void *src, size_t len)
+{
+ /*
+ * The following test makes use of unsigned arithmetic overflow to
+ * more efficiently test the condition !(src <= dst && dst < str+len).
+ * It also avoids the situation where the more explicit test would give
+ * incorrect results were the calculation str+len to overflow (though
+ * that issue is probably moot as such usage is probably undefined
+ * behaviour and a bug anyway.
+ */
+ if ((size_t)dst - (size_t)src >= len) {
+ /* destination not in source data, so can safely use memcpy */
+ return memcpy(dst, src, len);
+ }
+
+ /* copy backwards... */
+ const char *end = dst;
+ const char *s = (const char *)src + len;
+ char *d = (char *)dst + len;
+
+ while (d != end) {
+ *--d = *--s;
+ }
+ return dst;
+}
diff --git a/lib/libc/src/printf.c b/lib/libc/src/printf.c
new file mode 100644
index 0000000..d700426
--- /dev/null
+++ b/lib/libc/src/printf.c
@@ -0,0 +1,1043 @@
+/*
+ * SPDX-License-Identifier: MIT
+ * SPDX-FileCopyrightText: Copyright Marco Paland (info@paland.com), PALANDesign Hannover, Germany
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for
+ * speed on
+ * embedded systems with a very limited resources. These routines are thread
+ * safe and reentrant!
+ * Use this instead of the bloated standard/newlib printf cause these use
+ * malloc for printf (and may not be thread safe).
+ */
+
+/*
+ * fine this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
+ * printf_config.h header file
+ * default: undefined
+ */
+/*
+ * support for the floating point type (%f)
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
+#define PRINTF_SUPPORT_FLOAT
+#endif
+
+/* import float.h for DBL_MAX */
+#if defined(PRINTF_SUPPORT_FLOAT)
+#include <float.h>
+#endif
+
+#ifdef PRINTF_INCLUDE_CONFIG_H
+#include "printf_config.h"
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/*
+ * 'ntoa' conversion buffer size, this must be big enough to hold one converted
+ * numeric number including padded zeros (dynamically created on stack)
+ * default: 32 byte
+ */
+#ifndef PRINTF_NTOA_BUFFER_SIZE
+#define PRINTF_NTOA_BUFFER_SIZE 32U
+#endif
+
+/*
+ *'ftoa' conversion buffer size, this must be big enough to hold one converted
+ * float number including padded zeros (dynamically created on stack)
+ * default: 32 byte
+ */
+#ifndef PRINTF_FTOA_BUFFER_SIZE
+#define PRINTF_FTOA_BUFFER_SIZE 32U
+#endif
+
+/*
+ * support for exponential floating point notation (%e/%g)
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
+#define PRINTF_SUPPORT_EXPONENTIAL
+#endif
+
+/*
+ * define the default floating point precision
+ * default: 6 digits
+ */
+#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
+#define PRINTF_DEFAULT_FLOAT_PRECISION 6U
+#endif
+
+/*
+ * define the largest float suitable to print with %f
+ * default: 1e9
+ */
+#ifndef PRINTF_MAX_FLOAT
+#define PRINTF_MAX_FLOAT 1e9
+#endif
+
+/*
+ * support for the long long types (%llu or %p)
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
+#define PRINTF_SUPPORT_LONG_LONG
+#endif
+
+/*
+ * support for the ptrdiff_t type (%t)
+ * ptrdiff_t is normally defined in <stddef.h> as long or long long type
+ * default: activated
+ */
+#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
+#define PRINTF_SUPPORT_PTRDIFF_T
+#endif
+
+/******************************************************************************/
+
+/* internal flag definitions */
+#define FLAGS_ZEROPAD (1U << 0U)
+#define FLAGS_LEFT (1U << 1U)
+#define FLAGS_PLUS (1U << 2U)
+#define FLAGS_SPACE (1U << 3U)
+#define FLAGS_HASH (1U << 4U)
+#define FLAGS_UPPERCASE (1U << 5U)
+#define FLAGS_CHAR (1U << 6U)
+#define FLAGS_SHORT (1U << 7U)
+#define FLAGS_LONG (1U << 8U)
+#define FLAGS_LONG_LONG (1U << 9U)
+#define FLAGS_PRECISION (1U << 10U)
+#define FLAGS_ADAPT_EXP (1U << 11U)
+
+/* output function type */
+typedef void (*out_fct_type)(char character, void *buffer, size_t idx,
+ size_t maxlen);
+
+
+/* wrapper (used as buffer) for output function type */
+typedef struct {
+ void (*fct)(char character, void *arg);
+ void *arg;
+} out_fct_wrap_type;
+
+
+/* internal buffer output */
+static inline void _out_buffer(char character, void *buffer, size_t idx,
+ size_t maxlen)
+{
+ if (idx < maxlen) {
+ ((char *)buffer)[idx] = character;
+ }
+}
+
+
+/* internal null output */
+static inline void _out_null(char character, void *buffer, size_t idx,
+ size_t maxlen)
+{
+ (void)character; (void)buffer; (void)idx; (void)maxlen;
+}
+
+
+/* internal _putchar wrapper */
+static inline void _out_char(char character, void *buffer, size_t idx,
+ size_t maxlen)
+{
+ (void)buffer; (void)idx; (void)maxlen;
+ if (character) {
+ _putchar(character);
+ }
+}
+
+
+/* internal output function wrapper */
+static inline void _out_fct(char character, void *buffer, size_t idx,
+ size_t maxlen)
+{
+ (void)idx; (void)maxlen;
+ if (character) {
+ /* buffer is the output fct pointer */
+ ((out_fct_wrap_type *)buffer)->fct(character,
+ ((out_fct_wrap_type *)buffer)->arg);
+ }
+}
+
+
+/*
+ * internal secure strlen
+ * \return The length of the string (excluding the terminating 0) limited by
+ * 'maxsize'
+ */
+static inline unsigned int _strnlen_s(const char *str, size_t maxsize)
+{
+ const char *s;
+
+ for (s = str; *s && maxsize--; ++s) {
+ ;
+ }
+
+ return (unsigned int)(s - str);
+}
+
+/*
+ * internal test if char is a digit (0-9)
+ * \return true if char is a digit
+ */
+static inline bool _is_digit(char ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+
+/* internal ASCII string to unsigned int conversion */
+static unsigned int _atoi(const char **str)
+{
+ unsigned int i = 0U;
+
+ while (_is_digit(**str)) {
+ i = i * 10U + (unsigned int)(*((*str)++) - '0');
+ }
+ return i;
+}
+
+
+/* output the specified string in reverse, taking care of any zero-padding */
+static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags)
+{
+ const size_t start_idx = idx;
+
+ /* pad spaces up to given width */
+ if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
+ for (size_t i = len; i < width; i++) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ /* reverse string */
+ while (len) {
+ out(buf[--len], buffer, idx++, maxlen);
+ }
+
+ /* append pad spaces up to given width */
+ if (flags & FLAGS_LEFT) {
+ while (idx - start_idx < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+
+ return idx;
+}
+
+
+/* internal itoa format */
+static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)
+{
+ /* pad leading zeros */
+ if (!(flags & FLAGS_LEFT)) {
+ if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ /* handle hash */
+ if (flags & FLAGS_HASH) {
+ if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
+ len--;
+ if (len && (base == 16U)) {
+ len--;
+ }
+ }
+ if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'x';
+ } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'X';
+ } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
+ buf[len++] = 'b';
+ }
+ if (len < PRINTF_NTOA_BUFFER_SIZE) {
+ buf[len++] = '0';
+ }
+ }
+
+ if (len < PRINTF_NTOA_BUFFER_SIZE) {
+ if (negative) {
+ buf[len++] = '-';
+ } else if (flags & FLAGS_PLUS) {
+ /* ignore the space if the '+' exists */
+ buf[len++] = '+';
+ } else if (flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+/* internal itoa for 'long' type */
+static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx,
+ size_t maxlen, unsigned long value, bool negative,
+ unsigned long base, unsigned int prec,
+ unsigned int width, unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ /* no hash for 0 values */
+ if (!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ /* write if precision != 0 and value is != 0 */
+ if (!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative,
+ (unsigned int)base, prec, width, flags);
+}
+
+
+/* internal itoa for 'long long' type */
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx,
+ size_t maxlen, unsigned long long value,
+ bool negative, unsigned long long base,
+ unsigned int prec, unsigned int width,
+ unsigned int flags)
+{
+ char buf[PRINTF_NTOA_BUFFER_SIZE];
+ size_t len = 0U;
+
+ /* no hash for 0 values */
+ if (!value) {
+ flags &= ~FLAGS_HASH;
+ }
+
+ /* write if precision != 0 and value is != 0 */
+ if (!(flags & FLAGS_PRECISION) || value) {
+ do {
+ const char digit = (char)(value % base);
+
+ buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
+ value /= base;
+ } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
+ }
+
+ return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative,
+ (unsigned int)base, prec, width, flags);
+}
+#endif /* PRINTF_SUPPORT_LONG_LONG */
+
+
+#if defined(PRINTF_SUPPORT_FLOAT)
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+/*
+ * forward declaration so that _ftoa can switch to exp notation for
+ * values > PRINTF_MAX_FLOAT
+ */
+static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
+ double value, unsigned int prec, unsigned int width,
+ unsigned int flags);
+#endif
+
+
+/* internal ftoa for fixed decimal floating point */
+static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
+ double value, unsigned int prec, unsigned int width,
+ unsigned int flags)
+{
+ char buf[PRINTF_FTOA_BUFFER_SIZE];
+ size_t len = 0U;
+ double diff = 0.0;
+
+ /* powers of 10 */
+ static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000,
+ 1000000, 10000000, 100000000, 1000000000 };
+
+ /* test for special values */
+ if (value != value) {
+ return _out_rev(out, buffer, idx, maxlen, "nan", 3, width,
+ flags);
+ }
+
+ if (value < -DBL_MAX) {
+ return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width,
+ flags);
+ }
+
+ if (value > DBL_MAX) {
+ return _out_rev(out, buffer, idx, maxlen,
+ (flags & FLAGS_PLUS) ? "fni+" : "fni",
+ (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
+ }
+
+ /*
+ * test for very large values
+ * standard printf behavior is to print EVERY whole number digit --
+ * which could be 100s of characters overflowing your buffers == bad
+ */
+ if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ return _etoa(out, buffer, idx, maxlen, value, prec, width,
+ flags);
+#else
+ return 0U;
+#endif
+ }
+
+ /* test for negative */
+ bool negative = false;
+
+ if (value < 0) {
+ negative = true;
+ value = 0 - value;
+ }
+
+ /* set default precision, if not set explicitly */
+ if (!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+ /*
+ * limit precision to 9, cause a prec >= 10 can lead to overflow
+ * errors
+ */
+ while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
+ buf[len++] = '0';
+ prec--;
+ }
+
+ int whole = (int)value;
+ double tmp = (value - whole) * pow10[prec];
+ unsigned long frac = (unsigned long)tmp;
+
+ diff = tmp - frac;
+
+ if (diff > 0.5) {
+ ++frac;
+ /* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
+ if (frac >= pow10[prec]) {
+ frac = 0;
+ ++whole;
+ }
+ } else if (diff < 0.5) {
+ /* skip */
+ } else if ((frac == 0U) || (frac & 1U)) {
+ /* if halfway, round up if odd OR if last digit is 0 */
+ ++frac;
+ }
+
+ if (prec == 0U) {
+ diff = value - (double)whole;
+ if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
+ /*
+ * exactly 0.5 and ODD, then round up
+ * 1.5 -> 2, but 2.5 -> 2
+ */
+ ++whole;
+ }
+ } else {
+ unsigned int count = prec;
+
+ /* now do fractional part, as an unsigned number */
+ while (len < PRINTF_FTOA_BUFFER_SIZE) {
+ --count;
+ buf[len++] = (char)(48U + (frac % 10U));
+ frac /= 10U;
+ if (!frac) {
+ break;
+ }
+ }
+ /* add extra 0s */
+ while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
+ buf[len++] = '0';
+ }
+ if (len < PRINTF_FTOA_BUFFER_SIZE) {
+ /* add decimal */
+ buf[len++] = '.';
+ }
+ }
+
+ /* do whole part, number is reversed */
+ while (len < PRINTF_FTOA_BUFFER_SIZE) {
+ buf[len++] = (char)(48 + (whole % 10));
+ whole /= 10;
+ if (!whole) {
+ break;
+ }
+ }
+
+ /* pad leading zeros */
+ if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
+ if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
+ width--;
+ }
+ while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
+ buf[len++] = '0';
+ }
+ }
+
+ if (len < PRINTF_FTOA_BUFFER_SIZE) {
+ if (negative) {
+ buf[len++] = '-';
+ } else if (flags & FLAGS_PLUS) {
+ /* ignore the space if the '+' exists */
+ buf[len++] = '+';
+ } else if (flags & FLAGS_SPACE) {
+ buf[len++] = ' ';
+ }
+ }
+
+ return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
+}
+
+
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+/*
+ * internal ftoa variant for exponential floating-point type, contributed by
+ * Martijn Jasperse <m.jasperse@gmail.com>
+ */
+static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen,
+ double value, unsigned int prec, unsigned int width,
+ unsigned int flags)
+{
+ /* check for NaN and special values */
+ if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
+ return _ftoa(out, buffer, idx, maxlen, value, prec, width,
+ flags);
+ }
+
+ /* determine the sign */
+ const bool negative = value < 0;
+
+ if (negative) {
+ value = -value;
+ }
+
+ /* default precision */
+ if (!(flags & FLAGS_PRECISION)) {
+ prec = PRINTF_DEFAULT_FLOAT_PRECISION;
+ }
+
+ /*
+ * determine the decimal exponent
+ * based on the algorithm by David Gay
+ * (https://www.ampl.com/netlib/fp/dtoa.c)
+ */
+ union {
+ uint64_t U;
+ double F;
+ } conv;
+
+ conv.F = value;
+ /* effectively log2 */
+ int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023;
+
+ /* drop the exponent so conv.F is now in [1,2) */
+ conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U);
+
+ /*
+ * now approximate log10 from the log2 integer part and an expansion
+ * of ln around 1.5
+ */
+ int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
+ /*
+ * now we want to compute 10^expval but we want to be sure it won't
+ * overflow
+ */
+ exp2 = (int)(expval * 3.321928094887362 + 0.5);
+ const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
+ const double z2 = z * z;
+
+ conv.U = (uint64_t)(exp2 + 1023) << 52U;
+ /*
+ * compute exp(z) using continued fractions, see
+ * https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
+ */
+ conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
+ /* correct for rounding errors */
+ if (value < conv.F) {
+ expval--;
+ conv.F /= 10;
+ }
+
+ /*
+ * the exponent format is "%+03d" and largest value is "307", so set
+ * aside 4-5 characters
+ */
+ unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
+
+ /*
+ * in "%g" mode, "prec" is the number of *significant figures* not
+ * decimals
+ */
+ if (flags & FLAGS_ADAPT_EXP) {
+ /* do we want to fall-back to "%f" mode? */
+ if ((value >= 1e-4) && (value < 1e6)) {
+ if ((int)prec > expval) {
+ prec = (unsigned int)((int)prec - expval - 1);
+ } else {
+ prec = 0;
+ }
+ /* make sure _ftoa respects precision */
+ flags |= FLAGS_PRECISION;
+ /* no characters in exponent */
+ minwidth = 0U;
+ expval = 0;
+ } else {
+ /* we use one sigfig for the whole part */
+ if ((prec > 0) && (flags & FLAGS_PRECISION)) {
+ --prec;
+ }
+ }
+ }
+
+ /* will everything fit? */
+ unsigned int fwidth = width;
+
+ if (width > minwidth) {
+ /*
+ * we didn't fall-back so subtract the characters required for
+ * the exponent
+ */
+ fwidth -= minwidth;
+ } else {
+ /* not enough characters, so go back to default sizing */
+ fwidth = 0U;
+ }
+ if ((flags & FLAGS_LEFT) && minwidth) {
+ /* if we're padding on the right, DON'T pad the floating part */
+ fwidth = 0U;
+ }
+
+ /* rescale the float value */
+ if (expval) {
+ value /= conv.F;
+ }
+
+ /* output the floating part */
+ const size_t start_idx = idx;
+
+ idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
+
+ /* output the exponent part */
+ if (minwidth) {
+ /* output the exponential symbol */
+ out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++,
+ maxlen);
+ /* output the exponent value */
+ idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
+ /* might need to right-pad spaces */
+ if (flags & FLAGS_LEFT) {
+ while (idx - start_idx < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ }
+ return idx;
+}
+#endif /* PRINTF_SUPPORT_EXPONENTIAL */
+#endif /* PRINTF_SUPPORT_FLOAT */
+
+
+/* internal vsnprintf */
+static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen,
+ const char *format, va_list va)
+{
+ unsigned int flags, width, precision, n;
+ size_t idx = 0U;
+
+ if (!buffer) {
+ /* use null output function */
+ out = _out_null;
+ }
+
+ while (*format) {
+ /* format specifier? %[flags][width][.precision][length] */
+ if (*format != '%') {
+ /* no */
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ continue;
+ } else {
+ /* yes, evaluate it */
+ format++;
+ }
+
+ /* evaluate flags */
+ flags = 0U;
+ do {
+ switch (*format) {
+ case '0':
+ flags |= FLAGS_ZEROPAD; format++; n = 1U;
+ break;
+ case '-':
+ flags |= FLAGS_LEFT; format++; n = 1U;
+ break;
+ case '+':
+ flags |= FLAGS_PLUS; format++; n = 1U;
+ break;
+ case ' ':
+ flags |= FLAGS_SPACE; format++; n = 1U;
+ break;
+ case '#':
+ flags |= FLAGS_HASH; format++; n = 1U;
+ break;
+ default:
+ n = 0U;
+ break;
+ }
+ } while (n);
+
+ /* evaluate width field */
+ width = 0U;
+ if (_is_digit(*format)) {
+ width = _atoi(&format);
+ } else if (*format == '*') {
+ const int w = va_arg(va, int);
+
+ if (w < 0) {
+ flags |= FLAGS_LEFT; /* reverse padding */
+ width = (unsigned int)-w;
+ } else {
+ width = (unsigned int)w;
+ }
+ format++;
+ }
+
+ /* evaluate precision field */
+ precision = 0U;
+ if (*format == '.') {
+ flags |= FLAGS_PRECISION;
+ format++;
+ if (_is_digit(*format)) {
+ precision = _atoi(&format);
+ } else if (*format == '*') {
+ const int prec = (int)va_arg(va, int);
+
+ precision = prec > 0 ? (unsigned int)prec : 0U;
+ format++;
+ }
+ }
+
+ /* evaluate length field */
+ switch (*format) {
+ case 'l':
+ flags |= FLAGS_LONG;
+ format++;
+ if (*format == 'l') {
+ flags |= FLAGS_LONG_LONG;
+ format++;
+ }
+ break;
+ case 'h':
+ flags |= FLAGS_SHORT;
+ format++;
+ if (*format == 'h') {
+ flags |= FLAGS_CHAR;
+ format++;
+ }
+ break;
+#if defined(PRINTF_SUPPORT_PTRDIFF_T)
+ case 't':
+ flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+#endif
+ case 'j':
+ flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ case 'z':
+ flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
+ format++;
+ break;
+ default:
+ break;
+ }
+
+ /* evaluate specifier */
+ switch (*format) {
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'o':
+ case 'b': {
+ /* set the base */
+ unsigned int base;
+
+ if (*format == 'x' || *format == 'X') {
+ base = 16U;
+ } else if (*format == 'o') {
+ base = 8U;
+ } else if (*format == 'b') {
+ base = 2U;
+ } else {
+ base = 10U;
+ /* no hash for dec format */
+ flags &= ~FLAGS_HASH;
+ }
+ /* uppercase */
+ if (*format == 'X') {
+ flags |= FLAGS_UPPERCASE;
+ }
+
+ /* no plus or space flag for u, x, X, o, b */
+ if ((*format != 'i') && (*format != 'd')) {
+ flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
+ }
+
+ /* ignore '0' flag when precision is given */
+ if (flags & FLAGS_PRECISION) {
+ flags &= ~FLAGS_ZEROPAD;
+ }
+
+ /* convert the integer */
+ if ((*format == 'i') || (*format == 'd')) {
+ /* signed */
+ if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const long long value = va_arg(va, long long);
+
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+#endif
+ } else if (flags & FLAGS_LONG) {
+ const long value = va_arg(va, long);
+
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+ } else {
+ const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
+
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
+ }
+ } else {
+ /* unsigned */
+ if (flags & FLAGS_LONG_LONG) {
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
+#endif
+ } else if (flags & FLAGS_LONG) {
+ idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
+ } else {
+ const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
+
+ idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
+ }
+ }
+ format++;
+ break;
+ }
+#if defined(PRINTF_SUPPORT_FLOAT)
+ case 'f':
+ case 'F':
+ if (*format == 'F') {
+ flags |= FLAGS_UPPERCASE;
+ }
+
+ idx = _ftoa(out, buffer, idx, maxlen,
+ va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#if defined(PRINTF_SUPPORT_EXPONENTIAL)
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ if ((*format == 'g') || (*format == 'G')) {
+ flags |= FLAGS_ADAPT_EXP;
+ }
+
+ if ((*format == 'E') || (*format == 'G')) {
+ flags |= FLAGS_UPPERCASE;
+ }
+
+ idx = _etoa(out, buffer, idx, maxlen,
+ va_arg(va, double), precision, width, flags);
+ format++;
+ break;
+#endif /* PRINTF_SUPPORT_EXPONENTIAL */
+#endif /* PRINTF_SUPPORT_FLOAT */
+ case 'c': {
+ unsigned int l = 1U;
+ /* pre padding */
+ if (!(flags & FLAGS_LEFT)) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ /* char output */
+ out((char)va_arg(va, int), buffer, idx++, maxlen);
+ /* post padding */
+ if (flags & FLAGS_LEFT) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 's': {
+ const char *p = va_arg(va, char*);
+ unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
+ /* pre padding */
+ if (flags & FLAGS_PRECISION) {
+ l = (l < precision ? l : precision);
+ }
+ if (!(flags & FLAGS_LEFT)) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ /* string output */
+ while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
+ out(*(p++), buffer, idx++, maxlen);
+ }
+ /* post padding */
+ if (flags & FLAGS_LEFT) {
+ while (l++ < width) {
+ out(' ', buffer, idx++, maxlen);
+ }
+ }
+ format++;
+ break;
+ }
+
+ case 'p': {
+ width = sizeof(void *) * 2U;
+ flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
+
+ if (is_ll) {
+ idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags);
+ } else {
+#endif
+ idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags);
+#if defined(PRINTF_SUPPORT_LONG_LONG)
+ }
+#endif
+ format++;
+ break;
+ }
+
+ case '%':
+ out('%', buffer, idx++, maxlen);
+ format++;
+ break;
+
+ default:
+ out(*format, buffer, idx++, maxlen);
+ format++;
+ break;
+ }
+ }
+
+ /* termination */
+ out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
+
+ /* return written chars without terminating \0 */
+ return (int)idx;
+}
+
+
+/*****************************************************************************/
+
+int printf_(const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ char buffer[1];
+ const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
+
+ va_end(va);
+ return ret;
+}
+
+
+int sprintf_(char *buffer, const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
+
+ va_end(va);
+ return ret;
+}
+
+
+int snprintf_(char *buffer, size_t count, const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
+
+ va_end(va);
+ return ret;
+}
+
+
+int vprintf_(const char *format, va_list va)
+{
+ char buffer[1];
+
+ return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
+}
+
+
+int vsnprintf_(char *buffer, size_t count, const char *format, va_list va)
+{
+ return _vsnprintf(_out_buffer, buffer, count, format, va);
+}
+
+
+int fctprintf(void (*out)(char character, void *arg), void *arg,
+ const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ const out_fct_wrap_type out_fct_wrap = { out, arg };
+ const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
+
+ va_end(va);
+ return ret;
+}
diff --git a/lib/libc/src/strcmp.c b/lib/libc/src/strcmp.c
new file mode 100644
index 0000000..3719125
--- /dev/null
+++ b/lib/libc/src/strcmp.c
@@ -0,0 +1,48 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The Regents of the University of California.
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+/*
+ * Compare strings.
+ */
+int strcmp(const char *s1, const char *s2)
+{
+ while (*s1 == *s2++) {
+ if (*s1++ == '\0') {
+ return 0;
+ }
+ }
+
+ return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
+}
diff --git a/lib/libc/src/strlcpy.c b/lib/libc/src/strlcpy.c
new file mode 100644
index 0000000..dfc5965
--- /dev/null
+++ b/lib/libc/src/strlcpy.c
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: ISC
+ * SPDX-FileCopyrightText: Copyright Todd C. Miller <Todd.Miller@courtesan.com>
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+/*
+ * Copy string src to buffer dst of size dsize. At most dsize-1
+ * chars will be copied. Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
+ */
+size_t strlcpy(char *dst, const char *src, size_t dsize)
+{
+ const char *osrc = src;
+ size_t nleft = dsize;
+
+ /* Copy as many bytes as will fit. */
+ if (nleft != 0UL) {
+ while (--nleft != 0UL) {
+ *dst++ = *src++;
+ if (*(dst - 1) == '\0') {
+ break;
+ }
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src. */
+ if (nleft == 0UL) {
+ if (dsize != 0UL) {
+ *dst = '\0'; /* NUL-terminate dst */
+ }
+ while (*src++) {
+ ;
+ }
+ }
+
+ return (src - osrc - 1); /* count does not include NUL */
+}
diff --git a/lib/libc/src/strlen.c b/lib/libc/src/strlen.c
new file mode 100644
index 0000000..1d6f7bd
--- /dev/null
+++ b/lib/libc/src/strlen.c
@@ -0,0 +1,17 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <string.h>
+
+size_t strlen(const char *s)
+{
+ const char *cursor = s;
+
+ while (*cursor) {
+ cursor++;
+ }
+
+ return cursor - s;
+}
diff --git a/lib/libc/src/strncmp.c b/lib/libc/src/strncmp.c
new file mode 100644
index 0000000..f5a3723
--- /dev/null
+++ b/lib/libc/src/strncmp.c
@@ -0,0 +1,50 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright The Regents of the University of California.
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+int strncmp(const char *s1, const char *s2, size_t n)
+{
+ if (n == 0UL) {
+ return 0;
+ }
+
+ do {
+ if (*s1 != *s2++) {
+ return (*(const unsigned char *)s1 -
+ *(const unsigned char *)(s2 - 1));
+ }
+ if (*s1++ == '\0') {
+ break;
+ }
+ } while (--n != 0UL);
+
+ return 0;
+}
diff --git a/lib/libc/src/strnlen.c b/lib/libc/src/strnlen.c
new file mode 100644
index 0000000..e1a975b
--- /dev/null
+++ b/lib/libc/src/strnlen.c
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ * SPDX-FileCopyrightText: Copyright David Schultz <das@FreeBSD.org
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+size_t strnlen(const char *s, size_t maxlen)
+{
+ size_t len;
+
+ for (len = 0UL; len < maxlen; len++, s++) {
+ if (!*s) {
+ break;
+ }
+ }
+ return len;
+}