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