From da5241cb1591416d6aeba9f30ce395b58c97cb5d Mon Sep 17 00:00:00 2001 From: Antonio Nino Diaz Date: Wed, 17 May 2017 15:34:22 +0100 Subject: Introduce `tf_snprintf` This is a reduced version of `snprintf` that only supports formats '%d', '%i' and '%u'. It can be used when the full `snprintf` is not needed in order to save memory. If it finds an unknown format specifier, it prints an error message and panics. Change-Id: I2cb06fcdf74cda2c43caf73ae0762a91499fc04e Signed-off-by: Antonio Nino Diaz --- Makefile | 1 + common/tf_snprintf.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++ include/common/debug.h | 1 + 3 files changed, 110 insertions(+) create mode 100644 common/tf_snprintf.c diff --git a/Makefile b/Makefile index ddf87561..9c0ccc08 100644 --- a/Makefile +++ b/Makefile @@ -150,6 +150,7 @@ include lib/stdlib/stdlib.mk BL_COMMON_SOURCES += common/bl_common.c \ common/tf_printf.c \ + common/tf_snprintf.c \ common/${ARCH}/debug.S \ lib/${ARCH}/cache_helpers.S \ lib/${ARCH}/misc_helpers.S \ diff --git a/common/tf_snprintf.c b/common/tf_snprintf.c new file mode 100644 index 00000000..a99ab7ab --- /dev/null +++ b/common/tf_snprintf.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed, + unsigned int unum) +{ + /* Enough for a 32-bit unsigned decimal integer (4294967295). */ + unsigned char num_buf[10]; + int i = 0, rem; + + do { + rem = unum % 10; + num_buf[i++] = '0' + rem; + } while (unum /= 10); + + while (--i >= 0) { + if (*chars_printed < n) + *(*s)++ = num_buf[i]; + (*chars_printed)++; + } +} + +/******************************************************************* + * Reduced snprintf to be used for Trusted firmware. + * The following type specifiers are supported: + * + * %d or %i - signed decimal format + * %u - unsigned decimal format + * + * The function panics on all other formats specifiers. + * + * It returns the number of characters that would be written if the + * buffer was big enough. If it returns a value lower than n, the + * whole string has been written. + *******************************************************************/ +int tf_snprintf(char *s, size_t n, const char *fmt, ...) +{ + va_list args; + int num; + unsigned int unum; + size_t chars_printed = 0; + + if (n == 1) { + /* Buffer is too small to actually write anything else. */ + *s = '\0'; + n = 0; + } else if (n >= 2) { + /* Reserve space for the terminator character. */ + n--; + } + + va_start(args, fmt); + while (*fmt) { + + if (*fmt == '%') { + fmt++; + /* Check the format specifier. */ + switch (*fmt) { + case 'i': + case 'd': + num = va_arg(args, int); + + if (num < 0) { + if (chars_printed < n) + *s++ = '-'; + chars_printed++; + + unum = (unsigned int)-num; + } else { + unum = (unsigned int)num; + } + + unsigned_dec_print(&s, n, &chars_printed, unum); + break; + case 'u': + unum = va_arg(args, unsigned int); + unsigned_dec_print(&s, n, &chars_printed, unum); + break; + default: + /* Panic on any other format specifier. */ + ERROR("tf_snprintf: specifier with ASCII code '%d' not supported.", + *fmt); + plat_panic_handler(); + } + fmt++; + continue; + } + + if (chars_printed < n) + *s++ = *fmt; + fmt++; + chars_printed++; + } + + va_end(args); + + if (n > 0) + *s = '\0'; + + return chars_printed; +} diff --git a/include/common/debug.h b/include/common/debug.h index 646dabaf..814cf840 100644 --- a/include/common/debug.h +++ b/include/common/debug.h @@ -64,6 +64,7 @@ void __dead2 do_panic(void); void __dead2 __stack_chk_fail(void); void tf_printf(const char *fmt, ...) __printflike(1, 2); +int tf_snprintf(char *s, size_t n, const char *fmt, ...) __printflike(3, 4); #endif /* __ASSEMBLY__ */ #endif /* __DEBUG_H__ */ -- cgit v1.2.3