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