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