1 /*
2  * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <stdarg.h>
9 #include <stdint.h>
10 
11 #include <common/debug.h>
12 #include <plat/common/platform.h>
13 
14 #define get_num_va_args(_args, _lcount)				\
15 	(((_lcount) > 1)  ? va_arg(_args, long long int) :	\
16 	(((_lcount) == 1) ? va_arg(_args, long int) :		\
17 			    va_arg(_args, int)))
18 
19 #define get_unum_va_args(_args, _lcount)				\
20 	(((_lcount) > 1)  ? va_arg(_args, unsigned long long int) :	\
21 	(((_lcount) == 1) ? va_arg(_args, unsigned long int) :		\
22 			    va_arg(_args, unsigned int)))
23 
24 #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch)	\
25 	do {						\
26 		if ((chars_printed) < (size)) {		\
27 			*(buf) = (ch);			\
28 			(buf)++;			\
29 		}					\
30 		(chars_printed)++;			\
31 	} while (false)
32 
string_print(char ** s,size_t n,size_t * chars_printed,const char * str)33 static void string_print(char **s, size_t n, size_t *chars_printed,
34 			 const char *str)
35 {
36 	while (*str != '\0') {
37 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
38 		str++;
39 	}
40 }
41 
unsigned_num_print(char ** s,size_t n,size_t * chars_printed,unsigned long long int unum,unsigned int radix,char padc,int padn,bool capitalise)42 static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
43 			      unsigned long long int unum,
44 			      unsigned int radix, char padc, int padn,
45 			      bool capitalise)
46 {
47 	/* Just need enough space to store 64 bit decimal integer */
48 	char num_buf[20];
49 	int i = 0;
50 	int width;
51 	unsigned int rem;
52 	char ascii_a = capitalise ? 'A' : 'a';
53 
54 	if (radix < 10) {
55 		ERROR("snprintf: unsupported radix '%u'.", radix);
56 		plat_panic_handler();
57 		assert(0); /* Unreachable */
58 	}
59 
60 	do {
61 		rem = unum % radix;
62 		if (rem < 10U) {
63 			num_buf[i] = '0' + rem;
64 		} else {
65 			num_buf[i] = ascii_a + (rem - 10U);
66 		}
67 		i++;
68 		unum /= radix;
69 	} while (unum > 0U);
70 
71 	width = i;
72 	for (i = padn - width; i > 0; i--) {
73 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
74 	}
75 	for (i = width; i > 0; i--) {
76 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
77 	}
78 	for (i = width + padn; i < 0; i++) {
79 		CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
80 	}
81 }
82 
83 /*******************************************************************
84  * Reduced vsnprintf to be used for Trusted firmware.
85  * The following type specifiers are supported:
86  *
87  * %x (or %X) - hexadecimal format
88  * %d or %i - signed decimal format
89  * %s - string format
90  * %u - unsigned decimal format
91  * %p - pointer format
92  *
93  * The following length specifiers are supported by this print
94  * %l - long int
95  * %ll - long long int
96  * %z - size_t sized integer formats
97  *
98  * The following padding specifiers are supported by this print
99  * %0NN - Left-pad the number with 0s (NN is a decimal number)
100  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
101  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
102  *
103  * The function panics on all other formats specifiers.
104  *
105  * It returns the number of characters that would be written if the
106  * buffer was big enough. If it returns a value lower than n, the
107  * whole string has been written.
108  *******************************************************************/
vsnprintf(char * s,size_t n,const char * fmt,va_list args)109 int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
110 {
111 	int num;
112 	unsigned long long int unum;
113 	char *str;
114 	char padc;		/* Padding character */
115 	int padn;		/* Number of characters to pad */
116 	bool left;
117 	bool capitalise;
118 	size_t chars_printed = 0U;
119 	unsigned int l_count;
120 
121 	if (n == 0U) {
122 		/* There isn't space for anything. */
123 	} else if (n == 1U) {
124 		/* Buffer is too small to actually write anything else. */
125 		*s = '\0';
126 		n = 0U;
127 	} else {
128 		/* Reserve space for the terminator character. */
129 		n--;
130 	}
131 
132 	while (*fmt != '\0') {
133 		left = false;
134 		padc ='\0';
135 		padn = 0;
136 		capitalise = false;
137 		l_count = 0;
138 
139 		if (*fmt == '%') {
140 			fmt++;
141 			/* Check the format specifier. */
142 loop:
143 			switch (*fmt) {
144 			case '%':
145 				CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
146 				break;
147 			case '0':
148 			case '1':
149 			case '2':
150 			case '3':
151 			case '4':
152 			case '5':
153 			case '6':
154 			case '7':
155 			case '8':
156 			case '9':
157 				padc = (*fmt == '0') ? '0' : ' ';
158 				for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
159 					padn = (padn * 10) + (*fmt - '0');
160 				}
161 				if (left) {
162 					padn = -padn;
163 				}
164 				goto loop;
165 			case '-':
166 				left = true;
167 				fmt++;
168 				goto loop;
169 
170 			case 'i':
171 			case 'd':
172 				num = get_num_va_args(args, l_count);
173 
174 				if (num < 0) {
175 					CHECK_AND_PUT_CHAR(s, n, chars_printed,
176 						'-');
177 					unum = (unsigned int)-num;
178 				} else {
179 					unum = (unsigned int)num;
180 				}
181 
182 				unsigned_num_print(&s, n, &chars_printed,
183 						   unum, 10, padc, padn, false);
184 				break;
185 			case 's':
186 				str = va_arg(args, char *);
187 				string_print(&s, n, &chars_printed, str);
188 				break;
189 			case 'u':
190 				unum = get_unum_va_args(args, l_count);
191 				unsigned_num_print(&s, n, &chars_printed,
192 						   unum, 10, padc, padn, false);
193 				break;
194 			case 'z':
195 				l_count = 1;
196 				fmt++;
197 				goto loop;
198 			case 'l':
199 				l_count++;
200 				fmt++;
201 				goto loop;
202 			case 'p':
203 				unum = (uintptr_t)va_arg(args, void *);
204 				if (unum > 0U) {
205 					string_print(&s, n, &chars_printed, "0x");
206 					padn -= 2;
207 				}
208 				unsigned_num_print(&s, n, &chars_printed,
209 						   unum, 16, padc, padn, false);
210 				break;
211 			case 'X':
212 				capitalise = true;
213 			case 'x':
214 				unum = get_unum_va_args(args, l_count);
215 				unsigned_num_print(&s, n, &chars_printed,
216 						   unum, 16, padc, padn,
217 						   capitalise);
218 				break;
219 
220 			default:
221 				/* Panic on any other format specifier. */
222 				ERROR("snprintf: specifier with ASCII code '%d' not supported.",
223 				      *fmt);
224 				plat_panic_handler();
225 				assert(0); /* Unreachable */
226 			}
227 			fmt++;
228 			continue;
229 		}
230 
231 		CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
232 
233 		fmt++;
234 	}
235 
236 	if (n > 0U) {
237 		*s = '\0';
238 	}
239 
240 	return (int)chars_printed;
241 }
242 
243 /*******************************************************************
244  * Reduced snprintf to be used for Trusted firmware.
245  * The following type specifiers are supported:
246  *
247  * %x (or %X) - hexadecimal format
248  * %d or %i - signed decimal format
249  * %s - string format
250  * %u - unsigned decimal format
251  * %p - pointer format
252  *
253  * The following padding specifiers are supported by this print
254  * %0NN - Left-pad the number with 0s (NN is a decimal number)
255  * %NN - Left-pad the number or string with spaces (NN is a decimal number)
256  * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
257  *
258  * The function panics on all other formats specifiers.
259  *
260  * It returns the number of characters that would be written if the
261  * buffer was big enough. If it returns a value lower than n, the
262  * whole string has been written.
263  *******************************************************************/
snprintf(char * s,size_t n,const char * fmt,...)264 int snprintf(char *s, size_t n, const char *fmt, ...)
265 {
266 	int count;
267 	va_list all_args;
268 
269 	va_start(all_args, fmt);
270 	count = vsnprintf(s, n, fmt, all_args);
271 	va_end(all_args);
272 
273 	return count;
274 }
275