blob: 4c77b287b9d2e12aa86ad7e92886a632293cc065 [file] [log] [blame]
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +01001/*
2 * Copyright (c) 2017-2024, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7/*
8 * Based on TF-A common/tf_log.c
9 */
10
11#include <assert.h>
12#include <stdarg.h>
13#include <stdint.h>
14#include <stddef.h>
15
16#include "tfm_vprintf.h"
17
18static inline const char *get_log_prefix(uint8_t log_level)
19{
20 switch(log_level) {
21 case LOG_LEVEL_ERROR:
22 return "[ERR]";
23 case LOG_LEVEL_NOTICE:
24 return "[NOT]";
25 case LOG_LEVEL_WARNING:
26 return "[WAR]";
27 case LOG_LEVEL_INFO:
28 return "[INF]";
29 case LOG_LEVEL_VERBOSE:
30 return "[VER]";
31 default:
32 /* String must start with LOG_MARKER_* */
33 assert(0);
Jackson Cooper-Driver57a13c62024-09-04 16:01:45 +000034 return NULL;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010035 }
36}
37
38static void output_char(tfm_log_output_str output_func, void *priv, char c)
39{
Antonio de Angelis4f021312024-11-09 21:02:33 +000040 output_func(priv, &c, 1);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010041}
42
43static void output_str(tfm_log_output_str output_func, void *priv, const char *str)
44{
45 uint32_t len = 0;
46 const char *str_ptr = str;
47
48 while (*str_ptr++ != '\0') {
49 len++;
50 }
51
Antonio de Angelis4f021312024-11-09 21:02:33 +000052 output_func(priv, str, len);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010053}
54
55static void output_val(tfm_log_output_str output_func, void *priv, uint32_t val,
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000056 uint16_t num_padding, bool zero_padding, uint8_t base, bool signed_specifier)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010057{
58 uint8_t digit, chars_to_print;
59 uint16_t i;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000060 /* uint32_t has maximum value of 4,294,967,295. Require enough space in buffer
61 * for 10 digits + null terminator */
62 char buf[11];
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010063 char *const buf_end = &buf[sizeof(buf) - 1];
64 char *buf_ptr = buf_end;
65 const char pad_char = zero_padding ? '0' : ' ';
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000066 const char negative_char = '-';
67 bool negative = false;
68
69 if (signed_specifier && ((int32_t)val < 0)) {
70 val = -val;
71 negative = true;
72 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010073
74 /* Ensure buffer ends with NULL character */
75 *buf_ptr-- = '\0';
76
77 do {
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000078 digit = val % base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010079
80 if (digit < 10) {
81 *buf_ptr-- = '0' + digit;
82 } else {
83 *buf_ptr-- = 'a' + digit - 10;
84 }
85
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000086 val /= base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010087 } while (val);
88
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000089 chars_to_print = (uint8_t)negative + (buf_end - 1) - buf_ptr;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010090 if (num_padding > chars_to_print) {
91 num_padding -= chars_to_print;
92 } else {
93 num_padding = 0;
94 }
95
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000096 /* - before 0 */
97 if (negative && zero_padding) {
98 output_char(output_func, priv, negative_char);
99 }
100
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100101 for (i = 0; i < num_padding; i++) {
102 output_char(output_func, priv, pad_char);
103 }
104
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000105 /* - after ' ' */
106 if (negative && !zero_padding) {
107 output_char(output_func, priv, negative_char);
108 }
109
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100110 output_str(output_func, priv, buf_ptr + 1);
111}
112
113/* Basic vprintf, understands:
114 * %s: output string
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000115 * %u: output uint32_t in decimal
116 * %d: output int32_t in decimal
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100117 * %x: output uint32_t in hex
118 */
119static void tfm_vprintf_internal(tfm_log_output_str output_func,
120 void *priv, const char *fmt, va_list args)
121{
122 char c;
123 bool formatting = false;
124 uint16_t num_padding = 0;
125 bool zero_padding = false;
126
127 while ((c = *fmt++) != '\0') {
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100128 if (!formatting) {
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000129 if (c == '%') {
130 zero_padding = false;
131 num_padding = 0;
132 formatting = true;
133 } else {
134 if (c == '\n') {
135 output_char(output_func, priv, '\r');
136 }
137 output_char(output_func, priv, c);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100138 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100139 continue;
140 }
141
142 switch (c) {
143 case 'l':
144 continue;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000145 case 'u':
146 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding, 10, false);
147 break;
148 case 'd':
149 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding, 10, true);
150 break;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100151 case 'x':
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000152 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding, 16, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100153 break;
154 case 's':
155 output_str(output_func, priv, va_arg(args, char *));
156 break;
157 case '0':
158 if (num_padding == 0) {
159 zero_padding = true;
160 continue;
161 }
162 /* fallthrough */
163 case '1':
164 case '2':
165 case '3':
166 case '4':
167 case '5':
168 case '6':
169 case '7':
170 case '8':
171 case '9':
172 num_padding *= 10;
173 num_padding += c - '0';
174 continue;
175 case '%':
176 output_char(output_func, priv, '%');
177 break;
178 default:
179 output_str(output_func, priv, "[Unsupported]");
180 }
181
182 formatting = false;
183 }
184}
185
186void tfm_vprintf(tfm_log_output_str output_func, void *priv, const char *fmt, va_list args)
187{
188 uint8_t log_level;
189 const char spacer = ' ';
190
191 /* We expect the LOG_MARKER_* macro as the first character */
192 log_level = fmt[0];
193 fmt++;
194
195 output_str(output_func, priv, get_log_prefix(log_level));
196 output_char(output_func, priv, spacer);
197
198 tfm_vprintf_internal(output_func, priv, fmt, args);
199}