blob: ef10bc0271ee02fe19024c98982e1ebb9e126b8c [file] [log] [blame]
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +01001/*
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +00002 * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +01003 *
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"
Jackson Cooper-Driverccf42f22025-03-31 11:24:01 +010017#include "tfm_vprintf_priv.h"
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010018
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +000019#define LOG_RAW_VALUE UINT8_C(60)
20
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010021static inline const char *get_log_prefix(uint8_t log_level)
22{
23 switch(log_level) {
24 case LOG_LEVEL_ERROR:
25 return "[ERR]";
26 case LOG_LEVEL_NOTICE:
27 return "[NOT]";
28 case LOG_LEVEL_WARNING:
29 return "[WAR]";
30 case LOG_LEVEL_INFO:
31 return "[INF]";
32 case LOG_LEVEL_VERBOSE:
33 return "[VER]";
34 default:
35 /* String must start with LOG_MARKER_* */
36 assert(0);
Jackson Cooper-Driver57a13c62024-09-04 16:01:45 +000037 return NULL;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010038 }
39}
40
41static void output_char(tfm_log_output_str output_func, void *priv, char c)
42{
Antonio de Angelis4f021312024-11-09 21:02:33 +000043 output_func(priv, &c, 1);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010044}
45
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000046static inline void output_padding_chars(tfm_log_output_str output_func, void *priv,
47 uint16_t num_padding, char pad_char)
48{
49 uint16_t i;
50
51 for (i = 0; i < num_padding; i++) {
52 output_char(output_func, priv, pad_char);
53 }
54}
55
56static void output_str(tfm_log_output_str output_func, void *priv, const char *str, uint32_t len,
57 uint16_t num_padding, bool left_aligned, char pad_char,
58 bool calculate_length)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010059{
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010060 const char *str_ptr = str;
61
Jackson Cooper-Driver3e369bd2025-05-08 09:32:25 +010062 if (str_ptr == NULL) {
63 assert(false);
64 return;
65 }
66
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +000067 if (calculate_length) {
68 len = 0;
69 while (*str_ptr++ != '\0') {
70 len++;
71 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010072 }
73
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000074 if (num_padding > len) {
75 num_padding -= len;
76 } else {
77 num_padding = 0;
78 }
79
80 if (!left_aligned) {
81 output_padding_chars(output_func, priv, num_padding, pad_char);
82 }
83
Antonio de Angelis4f021312024-11-09 21:02:33 +000084 output_func(priv, str, len);
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000085
86 if (left_aligned) {
87 output_padding_chars(output_func, priv, num_padding, pad_char);
88 }
89}
90
91static void output_str_not_formatted(tfm_log_output_str output_func, void *priv, const char *str)
92{
93 output_str(output_func, priv, str, 0, 0, false, 0, true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010094}
95
96static void output_val(tfm_log_output_str output_func, void *priv, uint32_t val,
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000097 uint16_t num_padding, bool zero_padding, bool left_aligned, uint8_t base,
98 bool signed_specifier)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010099{
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000100 uint8_t digit;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000101 /* uint32_t has maximum value of 4,294,967,295. Require enough space in buffer
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000102 * for 10 digits + '-'. Note that the buffer does not need to be NULL terminated
103 * as we pass the string length to output_str */
104 char buf[11] = { 0 };
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100105 char *const buf_end = &buf[sizeof(buf) - 1];
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000106 char *buf_ptr = buf_end;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100107 const char pad_char = zero_padding ? '0' : ' ';
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000108 const char negative_char = '-';
109 bool negative = false;
110
111 if (signed_specifier && ((int32_t)val < 0)) {
112 val = -val;
113 negative = true;
114 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100115
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100116 do {
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000117 digit = val % base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100118
119 if (digit < 10) {
120 *buf_ptr-- = '0' + digit;
121 } else {
122 *buf_ptr-- = 'a' + digit - 10;
123 }
124
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000125 val /= base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100126 } while (val);
127
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +0000128 if (negative) {
129 if (!zero_padding) {
130 *buf_ptr-- = negative_char;
131 } else {
132 output_char(output_func, priv, negative_char);
133 }
134 }
135
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000136 output_str(output_func, priv, buf_ptr + 1, buf_end - buf_ptr, num_padding, left_aligned,
137 pad_char, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100138}
139
140/* Basic vprintf, understands:
141 * %s: output string
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000142 * %u: output uint32_t in decimal
143 * %d: output int32_t in decimal
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100144 * %x: output uint32_t in hex
145 */
146static void tfm_vprintf_internal(tfm_log_output_str output_func,
147 void *priv, const char *fmt, va_list args)
148{
149 char c;
150 bool formatting = false;
151 uint16_t num_padding = 0;
152 bool zero_padding = false;
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000153 bool left_aligned = false;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100154
155 while ((c = *fmt++) != '\0') {
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100156 if (!formatting) {
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000157 if (c == '%') {
158 zero_padding = false;
159 num_padding = 0;
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000160 left_aligned = false;
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000161 formatting = true;
162 } else {
163 if (c == '\n') {
164 output_char(output_func, priv, '\r');
165 }
166 output_char(output_func, priv, c);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100167 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100168 continue;
169 }
170
171 switch (c) {
172 case 'l':
173 continue;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000174 case 'u':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000175 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding,
176 left_aligned, 10, false);
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000177 break;
178 case 'd':
Nicola Mazzucato18c84d72025-03-28 19:58:09 +0000179 case 'i':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000180 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding,
181 left_aligned, 10, true);
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000182 break;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100183 case 'x':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000184 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding,
185 left_aligned, 16, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100186 break;
187 case 's':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000188 output_str(output_func, priv, va_arg(args, char *), 0, num_padding, left_aligned, ' ',
189 true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100190 break;
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000191 case '-':
192 left_aligned = true;
193 continue;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100194 case '0':
195 if (num_padding == 0) {
196 zero_padding = true;
197 continue;
198 }
199 /* fallthrough */
200 case '1':
201 case '2':
202 case '3':
203 case '4':
204 case '5':
205 case '6':
206 case '7':
207 case '8':
208 case '9':
209 num_padding *= 10;
210 num_padding += c - '0';
211 continue;
212 case '%':
213 output_char(output_func, priv, '%');
214 break;
215 default:
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000216 output_str_not_formatted(output_func, priv, "[Unsupported]");
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100217 }
218
219 formatting = false;
220 }
221}
222
Jackson Cooper-Driverb59d3fb2025-03-31 11:43:12 +0100223void tfm_vprintf(tfm_log_output_str output_func, void *priv, const char *fmt, va_list args,
224 bool with_marker)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100225{
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +0000226 uint8_t log_marker;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100227 const char spacer = ' ';
228
Jackson Cooper-Driverb59d3fb2025-03-31 11:43:12 +0100229 if (with_marker) {
230 log_marker = fmt[0];
231 fmt++;
232 if (log_marker != LOG_RAW_VALUE) {
233 output_str_not_formatted(output_func, priv, get_log_prefix(log_marker));
234 output_char(output_func, priv, spacer);
235 }
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +0000236 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100237
238 tfm_vprintf_internal(output_func, priv, fmt, args);
239}