1 /*
2  * Copyright (c) 2014-2021, 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 <stdbool.h>
10 #include <stddef.h>
11 #include <stdint.h>
12 #include <stdio.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 
string_print(const char * str)24 static int string_print(const char *str)
25 {
26 	int count = 0;
27 
28 	assert(str != NULL);
29 
30 	for ( ; *str != '\0'; str++) {
31 		(void)putchar(*str);
32 		count++;
33 	}
34 
35 	return count;
36 }
37 
unsigned_num_print(unsigned long long int unum,unsigned int radix,char padc,int padn)38 static int unsigned_num_print(unsigned long long int unum, unsigned int radix,
39 			      char padc, int padn)
40 {
41 	/* Just need enough space to store 64 bit decimal integer */
42 	char num_buf[20];
43 	int i = 0, count = 0;
44 	unsigned int rem;
45 
46 	/* num_buf is only large enough for radix >= 10 */
47 	if (radix < 10) {
48 		assert(0);
49 		return 0;
50 	}
51 
52 	do {
53 		rem = unum % radix;
54 		if (rem < 0xa)
55 			num_buf[i] = '0' + rem;
56 		else
57 			num_buf[i] = 'a' + (rem - 0xa);
58 		i++;
59 		unum /= radix;
60 	} while (unum > 0U);
61 
62 	if (padn > 0) {
63 		while (i < padn) {
64 			(void)putchar(padc);
65 			count++;
66 			padn--;
67 		}
68 	}
69 
70 	while (--i >= 0) {
71 		(void)putchar(num_buf[i]);
72 		count++;
73 	}
74 
75 	return count;
76 }
77 
78 /*******************************************************************
79  * Reduced format print for Trusted firmware.
80  * The following type specifiers are supported by this print
81  * %x - hexadecimal format
82  * %s - string format
83  * %d or %i - signed decimal format
84  * %u - unsigned decimal format
85  * %p - pointer format
86  *
87  * The following length specifiers are supported by this print
88  * %l - long int (64-bit on AArch64)
89  * %ll - long long int (64-bit on AArch64)
90  * %z - size_t sized integer formats (64 bit on AArch64)
91  *
92  * The following padding specifiers are supported by this print
93  * %0NN - Left-pad the number with 0s (NN is a decimal number)
94  *
95  * The print exits on all other formats specifiers other than valid
96  * combinations of the above specifiers.
97  *******************************************************************/
vprintf(const char * fmt,va_list args)98 int vprintf(const char *fmt, va_list args)
99 {
100 	int l_count;
101 	long long int num;
102 	unsigned long long int unum;
103 	char *str;
104 	char padc = '\0'; /* Padding character */
105 	int padn; /* Number of characters to pad */
106 	int count = 0; /* Number of printed characters */
107 
108 	while (*fmt != '\0') {
109 		l_count = 0;
110 		padn = 0;
111 
112 		if (*fmt == '%') {
113 			fmt++;
114 			/* Check the format specifier */
115 loop:
116 			switch (*fmt) {
117 			case '%':
118 				(void)putchar('%');
119 				break;
120 			case 'i': /* Fall through to next one */
121 			case 'd':
122 				num = get_num_va_args(args, l_count);
123 				if (num < 0) {
124 					(void)putchar('-');
125 					unum = (unsigned long long int)-num;
126 					padn--;
127 				} else
128 					unum = (unsigned long long int)num;
129 
130 				count += unsigned_num_print(unum, 10,
131 							    padc, padn);
132 				break;
133 			case 's':
134 				str = va_arg(args, char *);
135 				count += string_print(str);
136 				break;
137 			case 'p':
138 				unum = (uintptr_t)va_arg(args, void *);
139 				if (unum > 0U) {
140 					count += string_print("0x");
141 					padn -= 2;
142 				}
143 
144 				count += unsigned_num_print(unum, 16,
145 							    padc, padn);
146 				break;
147 			case 'x':
148 				unum = get_unum_va_args(args, l_count);
149 				count += unsigned_num_print(unum, 16,
150 							    padc, padn);
151 				break;
152 			case 'z':
153 				if (sizeof(size_t) == 8U)
154 					l_count = 2;
155 
156 				fmt++;
157 				goto loop;
158 			case 'l':
159 				l_count++;
160 				fmt++;
161 				goto loop;
162 			case 'u':
163 				unum = get_unum_va_args(args, l_count);
164 				count += unsigned_num_print(unum, 10,
165 							    padc, padn);
166 				break;
167 			case '0':
168 				padc = '0';
169 				padn = 0;
170 				fmt++;
171 
172 				for (;;) {
173 					char ch = *fmt;
174 					if ((ch < '0') || (ch > '9')) {
175 						goto loop;
176 					}
177 					padn = (padn * 10) + (ch - '0');
178 					fmt++;
179 				}
180 				assert(0); /* Unreachable */
181 			default:
182 				/* Exit on any other format specifier */
183 				return -1;
184 			}
185 			fmt++;
186 			continue;
187 		}
188 		(void)putchar(*fmt);
189 		fmt++;
190 		count++;
191 	}
192 
193 	return count;
194 }
195 
printf(const char * fmt,...)196 int printf(const char *fmt, ...)
197 {
198 	int count;
199 	va_list va;
200 
201 	va_start(va, fmt);
202 	count = vprintf(fmt, va);
203 	va_end(va);
204 
205 	return count;
206 }
207