tf_printf.c 5.11 KB
Newer Older
1
/*
2
 * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved.
3
 *
dp-arm's avatar
dp-arm committed
4
 * SPDX-License-Identifier: BSD-3-Clause
5
 */
Soby Mathew's avatar
Soby Mathew committed
6
7
8
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
9
#include <debug.h>
Soby Mathew's avatar
Soby Mathew committed
10
#include <limits.h>
11
12
13
14
15
16
#include <stdarg.h>
#include <stdint.h>

/***********************************************************
 * The tf_printf implementation for all BL stages
 ***********************************************************/
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

#define get_num_va_args(args, lcount) \
	(((lcount) > 1) ? va_arg(args, long long int) :	\
	((lcount) ? va_arg(args, long int) : va_arg(args, int)))

#define get_unum_va_args(args, lcount) \
	(((lcount) > 1) ? va_arg(args, unsigned long long int) :	\
	((lcount) ? va_arg(args, unsigned long int) : va_arg(args, unsigned int)))

static void string_print(const char *str)
{
	while (*str)
		putchar(*str++);
}

Soby Mathew's avatar
Soby Mathew committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#ifdef AARCH32
#define unsigned_num_print(unum, radix)			\
	do {						\
		if ((radix) == 16)			\
			unsigned_hex_print(unum);	\
		else if ((radix) == 10)			\
			unsigned_dec_print(unum);	\
		else					\
			string_print("tf_printf : Unsupported radix");\
	} while (0);

/*
 * Utility function to print an unsigned number in decimal format for AArch32.
 * The function doesn't support printing decimal integers higher than 32 bits
 * to avoid having to implement 64-bit integer compiler library functions.
 */
static void unsigned_dec_print(unsigned long long int unum)
{
	unsigned int local_num;
	/* Just need enough space to store 32 bit decimal integer */
	unsigned char num_buf[10];
	int i = 0, rem;

	if (unum > UINT_MAX) {
		string_print("tf_printf : decimal numbers higher than 32 bits"
				" not supported\n");
		return;
	}

	local_num = (unsigned int)unum;

	do {
		rem = local_num % 10;
		num_buf[i++] = '0' + rem;
	} while (local_num /= 10);

	while (--i >= 0)
		putchar(num_buf[i]);
}

/*
 * Utility function to print an unsigned number in hexadecimal format for
 * AArch32. The function doesn't use 64-bit integer arithmetic to avoid
 * having to implement 64-bit compiler library functions. It splits the
 * 64 bit number into two 32 bit numbers and converts them into equivalent
 * ASCII characters.
 */
static void unsigned_hex_print(unsigned long long int unum)
{
	/* Just need enough space to store 16 characters */
	unsigned char num_buf[16];
	int i = 0, rem;
	uint32_t num_local = 0, num_msb = 0;

	/* Get the LSB of 64 bit unum */
	num_local = (uint32_t)unum;
	/* Get the MSB of 64 bit unum. This works only on Little Endian */
	assert((read_sctlr() & SCTLR_EE_BIT) == 0);
	num_msb = *(((uint32_t *) &unum) + 1);

	do {
		do {
			rem = (num_local & 0xf);
			if (rem < 0xa)
				num_buf[i++] = '0' + rem;
			else
				num_buf[i++] = 'a' + (rem - 0xa);
		} while (num_local >>= 4);

		num_local = num_msb;
		num_msb = 0;
	} while (num_local);

	while (--i >= 0)
		putchar(num_buf[i]);
}

#else

111
static void unsigned_num_print(unsigned long long int unum, unsigned int radix)
112
113
114
{
	/* Just need enough space to store 64 bit decimal integer */
	unsigned char num_buf[20];
115
	int i = 0, rem;
116
117
118
119
120
121
122
123
124
125
126
127

	do {
		rem = unum % radix;
		if (rem < 0xa)
			num_buf[i++] = '0' + rem;
		else
			num_buf[i++] = 'a' + (rem - 0xa);
	} while (unum /= radix);

	while (--i >= 0)
		putchar(num_buf[i]);
}
Soby Mathew's avatar
Soby Mathew committed
128
#endif /* AARCH32 */
129
130
131

/*******************************************************************
 * Reduced format print for Trusted firmware.
132
133
 * The following type specifiers are supported by this print
 * %x - hexadecimal format
134
 * %s - string format
135
136
 * %d or %i - signed decimal format
 * %u - unsigned decimal format
137
 * %p - pointer format
138
139
140
141
142
143
144
145
 *
 * The following length specifiers are supported by this print
 * %l - long int (64-bit on AArch64)
 * %ll - long long int (64-bit on AArch64)
 * %z - size_t sized integer formats (64 bit on AArch64)
 *
 * The print exits on all other formats specifiers other than valid
 * combinations of the above specifiers.
146
147
148
149
 *******************************************************************/
void tf_printf(const char *fmt, ...)
{
	va_list args;
150
151
152
	int l_count;
	long long int num;
	unsigned long long int unum;
153
154
155
156
	char *str;

	va_start(args, fmt);
	while (*fmt) {
157
		l_count = 0;
158
159
160
161
162
163
164
165

		if (*fmt == '%') {
			fmt++;
			/* Check the format specifier */
loop:
			switch (*fmt) {
			case 'i': /* Fall through to next one */
			case 'd':
166
				num = get_num_va_args(args, l_count);
167
168
				if (num < 0) {
					putchar('-');
169
					unum = (unsigned long long int)-num;
170
				} else
171
					unum = (unsigned long long int)num;
172
173
174
175
176
177
178

				unsigned_num_print(unum, 10);
				break;
			case 's':
				str = va_arg(args, char *);
				string_print(str);
				break;
179
			case 'p':
180
				unum = (uintptr_t)va_arg(args, void *);
181
182
183
184
185
				if (unum)
					string_print("0x");

				unsigned_num_print(unum, 16);
				break;
186
			case 'x':
187
				unum = get_unum_va_args(args, l_count);
188
189
				unsigned_num_print(unum, 16);
				break;
190
191
			case 'z':
				if (sizeof(size_t) == 8)
192
193
					l_count = 2;

194
195
				fmt++;
				goto loop;
196
			case 'l':
197
				l_count++;
198
199
200
				fmt++;
				goto loop;
			case 'u':
201
				unum = get_unum_va_args(args, l_count);
202
203
204
205
206
207
208
209
210
211
212
213
214
215
				unsigned_num_print(unum, 10);
				break;
			default:
				/* Exit on any other format specifier */
				goto exit;
			}
			fmt++;
			continue;
		}
		putchar(*fmt++);
	}
exit:
	va_end(args);
}