1 /*
2  * Copyright (c) 2014-2023, 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  * %c - character format
85  * %u - unsigned decimal format
86  * %p - pointer format
87  *
88  * The following length specifiers are supported by this print
89  * %l - long int (64-bit on AArch64)
90  * %ll - long long int (64-bit on AArch64)
91  * %z - size_t sized integer formats (64 bit on AArch64)
92  *
93  * The following padding specifiers are supported by this print
94  * %0NN - Left-pad the number with 0s (NN is a decimal number)
95  *
96  * The print exits on all other formats specifiers other than valid
97  * combinations of the above specifiers.
98  *******************************************************************/
vprintf(const char * fmt,va_list args)99 int vprintf(const char *fmt, va_list args)
100 {
101 	int l_count;
102 	long long int num;
103 	unsigned long long int unum;
104 	char *str;
105 	char padc = '\0'; /* Padding character */
106 	int padn; /* Number of characters to pad */
107 	int count = 0; /* Number of printed characters */
108 
109 	while (*fmt != '\0') {
110 		l_count = 0;
111 		padn = 0;
112 
113 		if (*fmt == '%') {
114 			fmt++;
115 			/* Check the format specifier */
116 loop:
117 			switch (*fmt) {
118 			case '%':
119 				(void)putchar('%');
120 				break;
121 			case 'i': /* Fall through to next one */
122 			case 'd':
123 				num = get_num_va_args(args, l_count);
124 				if (num < 0) {
125 					(void)putchar('-');
126 					unum = (unsigned long long int)-num;
127 					padn--;
128 				} else
129 					unum = (unsigned long long int)num;
130 
131 				count += unsigned_num_print(unum, 10,
132 							    padc, padn);
133 				break;
134 			case 'c':
135 				(void)putchar(va_arg(args, int));
136 				count++;
137 				break;
138 			case 's':
139 				str = va_arg(args, char *);
140 				count += string_print(str);
141 				break;
142 			case 'p':
143 				unum = (uintptr_t)va_arg(args, void *);
144 				if (unum > 0U) {
145 					count += string_print("0x");
146 					padn -= 2;
147 				}
148 
149 				count += unsigned_num_print(unum, 16,
150 							    padc, padn);
151 				break;
152 			case 'x':
153 				unum = get_unum_va_args(args, l_count);
154 				count += unsigned_num_print(unum, 16,
155 							    padc, padn);
156 				break;
157 			case 'z':
158 				if (sizeof(size_t) == 8U)
159 					l_count = 2;
160 
161 				fmt++;
162 				goto loop;
163 			case 'l':
164 				l_count++;
165 				fmt++;
166 				goto loop;
167 			case 'u':
168 				unum = get_unum_va_args(args, l_count);
169 				count += unsigned_num_print(unum, 10,
170 							    padc, padn);
171 				break;
172 			case '0':
173 				padc = '0';
174 				padn = 0;
175 				fmt++;
176 
177 				for (;;) {
178 					char ch = *fmt;
179 					if ((ch < '0') || (ch > '9')) {
180 						goto loop;
181 					}
182 					padn = (padn * 10) + (ch - '0');
183 					fmt++;
184 				}
185 				assert(0); /* Unreachable */
186 			default:
187 				/* Exit on any other format specifier */
188 				return -1;
189 			}
190 			fmt++;
191 			continue;
192 		}
193 		(void)putchar(*fmt);
194 		fmt++;
195 		count++;
196 	}
197 
198 	return count;
199 }
200 
printf(const char * fmt,...)201 int printf(const char *fmt, ...)
202 {
203 	int count;
204 	va_list va;
205 
206 	va_start(va, fmt);
207 	count = vprintf(fmt, va);
208 	va_end(va);
209 
210 	return count;
211 }
212