libc: Adapt to TFTF

Add support for functions used in TFTF but not in TF-A.

Replaced calls to plat_panic_handler with calls to panic, since there is no
implementation of the former in TFTF.

Change-Id: Ic10de2c6e749db97b932cd7ffbb6067b5befe914
Signed-off-by: Ambroise Vincent <ambroise.vincent@arm.com>
diff --git a/lib/libc/assert.c b/lib/libc/assert.c
index 60f1a86..dbf8507 100644
--- a/lib/libc/assert.c
+++ b/lib/libc/assert.c
@@ -9,35 +9,9 @@
 #include <stdio.h>
 
 #include <common/debug.h>
-#include <drivers/console.h>
-#include <plat/common/platform.h>
 
-/*
- * Only print the output if PLAT_LOG_LEVEL_ASSERT is higher or equal to
- * LOG_LEVEL_INFO, which is the default value for builds with DEBUG=1.
- */
-
-#if PLAT_LOG_LEVEL_ASSERT >= LOG_LEVEL_VERBOSE
 void __assert(const char *file, unsigned int line, const char *assertion)
 {
 	printf("ASSERT: %s:%d:%s\n", file, line, assertion);
-	backtrace("assert");
-	(void)console_flush();
-	plat_panic_handler();
+	panic();
 }
-#elif PLAT_LOG_LEVEL_ASSERT >= LOG_LEVEL_INFO
-void __assert(const char *file, unsigned int line)
-{
-	printf("ASSERT: %s:%d\n", file, line);
-	backtrace("assert");
-	(void)console_flush();
-	plat_panic_handler();
-}
-#else
-void __assert(void)
-{
-	backtrace("assert");
-	(void)console_flush();
-	plat_panic_handler();
-}
-#endif
diff --git a/lib/libc/libc.mk b/lib/libc/libc.mk
index e1b5560..729b91c 100644
--- a/lib/libc/libc.mk
+++ b/lib/libc/libc.mk
@@ -16,12 +16,14 @@
 			printf.c			\
 			putchar.c			\
 			puts.c				\
+			rand.c				\
 			snprintf.c			\
 			strchr.c			\
 			strcmp.c			\
 			strlcpy.c			\
 			strlen.c			\
 			strncmp.c			\
+			strncpy.c			\
 			strnlen.c			\
 			strrchr.c)
 
diff --git a/lib/libc/printf.c b/lib/libc/printf.c
index 2715a72..60203fb 100644
--- a/lib/libc/printf.c
+++ b/lib/libc/printf.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -21,17 +21,36 @@
 	(((_lcount) == 1) ? va_arg(_args, unsigned long int) :		\
 			    va_arg(_args, unsigned int)))
 
-static int string_print(const char *str)
+static int string_print(const char *str, char padc, int padn)
 {
-	int count = 0;
+	int i = 0, count = 0;
 
 	assert(str != NULL);
 
+	while (str[i] != '\0')
+		i++;
+
+	if (padn > 0) {
+		while (i < padn) {
+			(void)putchar(padc);
+			count++;
+			padn--;
+		}
+	}
+
 	for ( ; *str != '\0'; str++) {
 		(void)putchar(*str);
 		count++;
 	}
 
+	if (padn < 0) {
+		while (i < -padn) {
+			(void)putchar(padc);
+			count++;
+			padn++;
+		}
+	}
+
 	return count;
 }
 
@@ -41,6 +60,7 @@
 	/* Just need enough space to store 64 bit decimal integer */
 	char num_buf[20];
 	int i = 0, count = 0;
+	int width;
 	unsigned int rem;
 
 	do {
@@ -53,8 +73,10 @@
 		unum /= radix;
 	} while (unum > 0U);
 
+	width = i;
+
 	if (padn > 0) {
-		while (i < padn) {
+		while (width < padn) {
 			(void)putchar(padc);
 			count++;
 			padn--;
@@ -66,11 +88,19 @@
 		count++;
 	}
 
+	if (padn < 0) {
+		while (width < -padn) {
+			(void)putchar(padc);
+			count++;
+			padn++;
+		}
+	}
+
 	return count;
 }
 
 /*******************************************************************
- * Reduced format print for Trusted firmware.
+ * Simplified version of printf() with smaller memory footprint.
  * The following type specifiers are supported by this print
  * %x - hexadecimal format
  * %s - string format
@@ -85,6 +115,8 @@
  *
  * The following padding specifiers are supported by this print
  * %0NN - Left-pad the number with 0s (NN is a decimal number)
+ * %NN - Left-pad the number or string with spaces (NN is a decimal number)
+ * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
  *
  * The print exits on all other formats specifiers other than valid
  * combinations of the above specifiers.
@@ -92,15 +124,18 @@
 int vprintf(const char *fmt, va_list args)
 {
 	int l_count;
+	int left;
 	long long int num;
 	unsigned long long int unum;
 	char *str;
-	char padc = '\0'; /* Padding character */
+	char padc; /* Padding character */
 	int padn; /* Number of characters to pad */
 	int count = 0; /* Number of printed characters */
 
 	while (*fmt != '\0') {
 		l_count = 0;
+		left = 0;
+		padc = '\0';
 		padn = 0;
 
 		if (*fmt == '%') {
@@ -108,6 +143,25 @@
 			/* Check the format specifier */
 loop:
 			switch (*fmt) {
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				padc = ' ';
+				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
+					padn = (padn * 10) + (*fmt - '0');
+				if (left)
+					padn = -padn;
+				goto loop;
+			case '-':
+				left = 1;
+				fmt++;
+				goto loop;
 			case 'i': /* Fall through to next one */
 			case 'd':
 				num = get_num_va_args(args, l_count);
@@ -123,12 +177,12 @@
 				break;
 			case 's':
 				str = va_arg(args, char *);
-				count += string_print(str);
+				count += string_print(str, padc, padn);
 				break;
 			case 'p':
 				unum = (uintptr_t)va_arg(args, void *);
 				if (unum > 0U) {
-					count += string_print("0x");
+					count += string_print("0x", padc, 0);
 					padn -= 2;
 				}
 
diff --git a/lib/libc/snprintf.c b/lib/libc/snprintf.c
index 38ad1c7..29c50df 100644
--- a/lib/libc/snprintf.c
+++ b/lib/libc/snprintf.c
@@ -1,15 +1,26 @@
 /*
- * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <assert.h>
 #include <stdarg.h>
+#include <stdlib.h>
 
 #include <common/debug.h>
 #include <plat/common/platform.h>
 
+#define get_num_va_args(_args, _lcount)				\
+	(((_lcount) > 1)  ? va_arg(_args, long long int) :	\
+	(((_lcount) == 1) ? va_arg(_args, long int) :		\
+			    va_arg(_args, int)))
+
+#define get_unum_va_args(_args, _lcount)				\
+	(((_lcount) > 1)  ? va_arg(_args, unsigned long long int) :	\
+	(((_lcount) == 1) ? va_arg(_args, unsigned long int) :		\
+			    va_arg(_args, unsigned int)))
+
 static void string_print(char **s, size_t n, size_t *chars_printed,
 			 const char *str)
 {
@@ -24,28 +35,192 @@
 	}
 }
 
-static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed,
-			       unsigned int unum)
+static void unsigned_num_print(char **s, size_t n, size_t *count,
+			      unsigned long long int unum, unsigned int radix,
+			      char padc, int padn)
 {
-	/* Enough for a 32-bit unsigned decimal integer (4294967295). */
-	char num_buf[10];
+	/* Just need enough space to store 64 bit decimal integer */
+	char num_buf[20];
 	int i = 0;
+	int width;
 	unsigned int rem;
 
 	do {
-		rem = unum % 10U;
-		num_buf[i++] = '0' + rem;
-		unum /= 10U;
+		rem = unum % radix;
+		if (rem < 0xa)
+			num_buf[i] = '0' + rem;
+		else
+			num_buf[i] = 'a' + (rem - 0xa);
+		i++;
+		unum /= radix;
 	} while (unum > 0U);
 
+	width = i;
+
+	if (padn > 0) {
+		while (width < padn) {
+			if (*count < n) {
+				*(*s) = padc;
+				(*s)++;
+			}
+			(*count)++;
+			padn--;
+		}
+	}
+
 	while (--i >= 0) {
-		if (*chars_printed < n) {
+		if (*count < n) {
 			*(*s) = num_buf[i];
 			(*s)++;
 		}
-
-		(*chars_printed)++;
+		(*count)++;
 	}
+
+	if (padn < 0) {
+		while (width < -padn) {
+			if (*count < n) {
+				*(*s) = padc;
+				(*s)++;
+			}
+			(*count)++;
+			padn++;
+		}
+	}
+}
+
+/*
+ * Scaled down version of vsnprintf(3).
+ */
+int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
+{
+	int l_count;
+	int left;
+	char *str;
+	int num;
+	unsigned long long int unum;
+	char padc; /* Padding character */
+	int padn; /* Number of characters to pad */
+	size_t count = 0U;
+
+	if (n == 0U) {
+		/* There isn't space for anything. */
+	} else if (n == 1U) {
+		/* Buffer is too small to actually write anything else. */
+		*s = '\0';
+		n = 0U;
+	} else {
+		/* Reserve space for the terminator character. */
+		n--;
+	}
+
+	while (*fmt != '\0') {
+		l_count = 0;
+		left = 0;
+		padc = '\0';
+		padn = 0;
+
+		if (*fmt == '%') {
+			fmt++;
+			/* Check the format specifier. */
+loop:
+			switch (*fmt) {
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				padc = ' ';
+				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
+					padn = (padn * 10) + (*fmt - '0');
+				if (left)
+					padn = -padn;
+				goto loop;
+			case '-':
+				left = 1;
+				fmt++;
+				goto loop;
+			case 'i':
+			case 'd':
+				num = get_num_va_args(args, l_count);
+
+				if (num < 0) {
+					if (count < n) {
+						*s = '-';
+						s++;
+					}
+					count++;
+
+					unum = (unsigned int)-num;
+				} else {
+					unum = (unsigned int)num;
+				}
+
+				unsigned_num_print(&s, n, &count, unum, 10,
+						   padc, padn);
+				break;
+			case 'l':
+				l_count++;
+				fmt++;
+				goto loop;
+			case 's':
+				str = va_arg(args, char *);
+				string_print(&s, n, &count, str);
+				break;
+			case 'u':
+				unum = get_unum_va_args(args, l_count);
+				unsigned_num_print(&s, n, &count, unum, 10,
+						   padc, padn);
+				break;
+			case 'x':
+				unum = get_unum_va_args(args, l_count);
+				unsigned_num_print(&s, n, &count, unum, 16,
+						   padc, padn);
+				break;
+			case '0':
+				padc = '0';
+				padn = 0;
+				fmt++;
+
+				for (;;) {
+					char ch = *fmt;
+					if ((ch < '0') || (ch > '9')) {
+						goto loop;
+					}
+					padn = (padn * 10) + (ch - '0');
+					fmt++;
+				}
+				assert(0); /* Unreachable */
+			default:
+				/*
+				 * Exit on any other format specifier and abort
+				 * when in debug mode.
+				 */
+				WARN("snprintf: specifier with ASCII code '%d' not supported.\n",
+				     *fmt);
+				assert(0);
+				return -1;
+			}
+			fmt++;
+			continue;
+		}
+
+		if (count < n) {
+			*s = *fmt;
+			s++;
+		}
+
+		fmt++;
+		count++;
+	}
+
+	if (n > 0U)
+		*s = '\0';
+
+	return (int)count;
 }
 
 /*******************************************************************
@@ -65,79 +240,11 @@
 int snprintf(char *s, size_t n, const char *fmt, ...)
 {
 	va_list args;
-	int num;
-	unsigned int unum;
-	char *str;
-	size_t chars_printed = 0U;
-
-	if (n == 0U) {
-		/* There isn't space for anything. */
-	} else if (n == 1U) {
-		/* Buffer is too small to actually write anything else. */
-		*s = '\0';
-		n = 0U;
-	} else {
-		/* Reserve space for the terminator character. */
-		n--;
-	}
+	int chars_printed;
 
 	va_start(args, fmt);
-	while (*fmt != '\0') {
-
-		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 = '-';
-						s++;
-					}
-					chars_printed++;
-
-					unum = (unsigned int)-num;
-				} else {
-					unum = (unsigned int)num;
-				}
-
-				unsigned_dec_print(&s, n, &chars_printed, unum);
-				break;
-			case 's':
-				str = va_arg(args, char *);
-				string_print(&s, n, &chars_printed, str);
-				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("snprintf: specifier with ASCII code '%d' not supported.",
-				      *fmt);
-				plat_panic_handler();
-				assert(0); /* Unreachable */
-			}
-			fmt++;
-			continue;
-		}
-
-		if (chars_printed < n) {
-			*s = *fmt;
-			s++;
-		}
-
-		fmt++;
-		chars_printed++;
-	}
-
+	chars_printed = vsnprintf(s, n, fmt, args);
 	va_end(args);
 
-	if (n > 0U)
-		*s = '\0';
-
-	return (int)chars_printed;
+	return chars_printed;
 }