blob: 7878fb8c19a7b0b7166519181e12e18c660b697a [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"
17
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +000018#define LOG_RAW_VALUE UINT8_C(60)
19
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010020static inline const char *get_log_prefix(uint8_t log_level)
21{
22 switch(log_level) {
23 case LOG_LEVEL_ERROR:
24 return "[ERR]";
25 case LOG_LEVEL_NOTICE:
26 return "[NOT]";
27 case LOG_LEVEL_WARNING:
28 return "[WAR]";
29 case LOG_LEVEL_INFO:
30 return "[INF]";
31 case LOG_LEVEL_VERBOSE:
32 return "[VER]";
33 default:
34 /* String must start with LOG_MARKER_* */
35 assert(0);
Jackson Cooper-Driver57a13c62024-09-04 16:01:45 +000036 return NULL;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010037 }
38}
39
40static void output_char(tfm_log_output_str output_func, void *priv, char c)
41{
Antonio de Angelis4f021312024-11-09 21:02:33 +000042 output_func(priv, &c, 1);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010043}
44
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000045static inline void output_padding_chars(tfm_log_output_str output_func, void *priv,
46 uint16_t num_padding, char pad_char)
47{
48 uint16_t i;
49
50 for (i = 0; i < num_padding; i++) {
51 output_char(output_func, priv, pad_char);
52 }
53}
54
55static void output_str(tfm_log_output_str output_func, void *priv, const char *str, uint32_t len,
56 uint16_t num_padding, bool left_aligned, char pad_char,
57 bool calculate_length)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010058{
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010059 const char *str_ptr = str;
60
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +000061 if (calculate_length) {
62 len = 0;
63 while (*str_ptr++ != '\0') {
64 len++;
65 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010066 }
67
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000068 if (num_padding > len) {
69 num_padding -= len;
70 } else {
71 num_padding = 0;
72 }
73
74 if (!left_aligned) {
75 output_padding_chars(output_func, priv, num_padding, pad_char);
76 }
77
Antonio de Angelis4f021312024-11-09 21:02:33 +000078 output_func(priv, str, len);
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000079
80 if (left_aligned) {
81 output_padding_chars(output_func, priv, num_padding, pad_char);
82 }
83}
84
85static void output_str_not_formatted(tfm_log_output_str output_func, void *priv, const char *str)
86{
87 output_str(output_func, priv, str, 0, 0, false, 0, true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010088}
89
90static void output_val(tfm_log_output_str output_func, void *priv, uint32_t val,
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000091 uint16_t num_padding, bool zero_padding, bool left_aligned, uint8_t base,
92 bool signed_specifier)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010093{
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000094 uint8_t digit;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000095 /* uint32_t has maximum value of 4,294,967,295. Require enough space in buffer
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +000096 * for 10 digits + '-'. Note that the buffer does not need to be NULL terminated
97 * as we pass the string length to output_str */
98 char buf[11] = { 0 };
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010099 char *const buf_end = &buf[sizeof(buf) - 1];
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000100 char *buf_ptr = buf_end;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100101 const char pad_char = zero_padding ? '0' : ' ';
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000102 const char negative_char = '-';
103 bool negative = false;
104
105 if (signed_specifier && ((int32_t)val < 0)) {
106 val = -val;
107 negative = true;
108 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100109
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100110 do {
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000111 digit = val % base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100112
113 if (digit < 10) {
114 *buf_ptr-- = '0' + digit;
115 } else {
116 *buf_ptr-- = 'a' + digit - 10;
117 }
118
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000119 val /= base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100120 } while (val);
121
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +0000122 if (negative) {
123 if (!zero_padding) {
124 *buf_ptr-- = negative_char;
125 } else {
126 output_char(output_func, priv, negative_char);
127 }
128 }
129
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000130 output_str(output_func, priv, buf_ptr + 1, buf_end - buf_ptr, num_padding, left_aligned,
131 pad_char, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100132}
133
134/* Basic vprintf, understands:
135 * %s: output string
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000136 * %u: output uint32_t in decimal
137 * %d: output int32_t in decimal
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100138 * %x: output uint32_t in hex
139 */
140static void tfm_vprintf_internal(tfm_log_output_str output_func,
141 void *priv, const char *fmt, va_list args)
142{
143 char c;
144 bool formatting = false;
145 uint16_t num_padding = 0;
146 bool zero_padding = false;
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000147 bool left_aligned = false;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100148
149 while ((c = *fmt++) != '\0') {
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100150 if (!formatting) {
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000151 if (c == '%') {
152 zero_padding = false;
153 num_padding = 0;
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000154 left_aligned = false;
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000155 formatting = true;
156 } else {
157 if (c == '\n') {
158 output_char(output_func, priv, '\r');
159 }
160 output_char(output_func, priv, c);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100161 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100162 continue;
163 }
164
165 switch (c) {
166 case 'l':
167 continue;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000168 case 'u':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000169 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding,
170 left_aligned, 10, false);
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000171 break;
172 case 'd':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000173 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding,
174 left_aligned, 10, true);
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000175 break;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100176 case 'x':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000177 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding,
178 left_aligned, 16, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100179 break;
180 case 's':
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000181 output_str(output_func, priv, va_arg(args, char *), 0, num_padding, left_aligned, ' ',
182 true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100183 break;
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000184 case '-':
185 left_aligned = true;
186 continue;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100187 case '0':
188 if (num_padding == 0) {
189 zero_padding = true;
190 continue;
191 }
192 /* fallthrough */
193 case '1':
194 case '2':
195 case '3':
196 case '4':
197 case '5':
198 case '6':
199 case '7':
200 case '8':
201 case '9':
202 num_padding *= 10;
203 num_padding += c - '0';
204 continue;
205 case '%':
206 output_char(output_func, priv, '%');
207 break;
208 default:
Jackson Cooper-Driver44a51532025-03-14 10:24:22 +0000209 output_str_not_formatted(output_func, priv, "[Unsupported]");
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100210 }
211
212 formatting = false;
213 }
214}
215
216void tfm_vprintf(tfm_log_output_str output_func, void *priv, const char *fmt, va_list args)
217{
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +0000218 uint8_t log_marker;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100219 const char spacer = ' ';
220
221 /* We expect the LOG_MARKER_* macro as the first character */
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +0000222 log_marker = fmt[0];
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100223 fmt++;
224
Jackson Cooper-Driver5188b1e2025-03-03 13:55:45 +0000225 if (log_marker != LOG_RAW_VALUE) {
226 output_str_not_formatted(output_func, priv, get_log_prefix(log_marker));
227 output_char(output_func, priv, spacer);
228 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100229
230 tfm_vprintf_internal(output_func, priv, fmt, args);
231}