blob: 3fa0c8222f378afe893af717afd6b380572dedf6 [file] [log] [blame]
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +01001/*
Mudit Sharmab99254f2025-03-06 12:31:49 +00002 * Copyright (c) 2017-2025, Arm Limited. All rights reserved.
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
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
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +000043static void output_str(tfm_log_output_str output_func, void *priv,
44 const char *str, uint32_t len, bool calculate_length)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010045{
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010046 const char *str_ptr = str;
47
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +000048 if (calculate_length) {
49 len = 0;
50 while (*str_ptr++ != '\0') {
51 len++;
52 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010053 }
54
Antonio de Angelis4f021312024-11-09 21:02:33 +000055 output_func(priv, str, len);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010056}
57
58static void output_val(tfm_log_output_str output_func, void *priv, uint32_t val,
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000059 uint16_t num_padding, bool zero_padding, uint8_t base, bool signed_specifier)
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010060{
61 uint8_t digit, chars_to_print;
62 uint16_t i;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000063 /* uint32_t has maximum value of 4,294,967,295. Require enough space in buffer
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +000064 * for 10 digits + null terminator + '-' */
Mudit Sharmab99254f2025-03-06 12:31:49 +000065 char buf[12] = { 0 };
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010066 char *const buf_end = &buf[sizeof(buf) - 1];
Mudit Sharmab99254f2025-03-06 12:31:49 +000067 /* Leaving space for NULL terminator */
68 char *buf_ptr = buf_end - 1;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010069 const char pad_char = zero_padding ? '0' : ' ';
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000070 const char negative_char = '-';
71 bool negative = false;
72
73 if (signed_specifier && ((int32_t)val < 0)) {
74 val = -val;
75 negative = true;
76 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010077
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010078 do {
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000079 digit = val % base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010080
81 if (digit < 10) {
82 *buf_ptr-- = '0' + digit;
83 } else {
84 *buf_ptr-- = 'a' + digit - 10;
85 }
86
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +000087 val /= base;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010088 } while (val);
89
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +000090 if (negative) {
91 if (!zero_padding) {
92 *buf_ptr-- = negative_char;
93 } else {
94 output_char(output_func, priv, negative_char);
95 }
96 }
97
98 chars_to_print = (buf_end - 1) - buf_ptr;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +010099 if (num_padding > chars_to_print) {
100 num_padding -= chars_to_print;
101 } else {
102 num_padding = 0;
103 }
104
105 for (i = 0; i < num_padding; i++) {
106 output_char(output_func, priv, pad_char);
107 }
108
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +0000109 output_str(output_func, priv, buf_ptr + 1, chars_to_print, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100110}
111
112/* Basic vprintf, understands:
113 * %s: output string
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000114 * %u: output uint32_t in decimal
115 * %d: output int32_t in decimal
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100116 * %x: output uint32_t in hex
117 */
118static void tfm_vprintf_internal(tfm_log_output_str output_func,
119 void *priv, const char *fmt, va_list args)
120{
121 char c;
122 bool formatting = false;
123 uint16_t num_padding = 0;
124 bool zero_padding = false;
125
126 while ((c = *fmt++) != '\0') {
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100127 if (!formatting) {
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000128 if (c == '%') {
129 zero_padding = false;
130 num_padding = 0;
131 formatting = true;
132 } else {
133 if (c == '\n') {
134 output_char(output_func, priv, '\r');
135 }
136 output_char(output_func, priv, c);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100137 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100138 continue;
139 }
140
141 switch (c) {
142 case 'l':
143 continue;
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000144 case 'u':
145 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding, 10, false);
146 break;
147 case 'd':
148 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding, 10, true);
149 break;
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100150 case 'x':
Jackson Cooper-Driverd6ddcb82025-01-21 11:39:28 +0000151 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding, 16, false);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100152 break;
153 case 's':
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +0000154 output_str(output_func, priv, va_arg(args, char *), 0, true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100155 break;
156 case '0':
157 if (num_padding == 0) {
158 zero_padding = true;
159 continue;
160 }
161 /* fallthrough */
162 case '1':
163 case '2':
164 case '3':
165 case '4':
166 case '5':
167 case '6':
168 case '7':
169 case '8':
170 case '9':
171 num_padding *= 10;
172 num_padding += c - '0';
173 continue;
174 case '%':
175 output_char(output_func, priv, '%');
176 break;
177 default:
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +0000178 output_str(output_func, priv, "[Unsupported]", 0, true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100179 }
180
181 formatting = false;
182 }
183}
184
185void tfm_vprintf(tfm_log_output_str output_func, void *priv, const char *fmt, va_list args)
186{
187 uint8_t log_level;
188 const char spacer = ' ';
189
190 /* We expect the LOG_MARKER_* macro as the first character */
191 log_level = fmt[0];
192 fmt++;
193
Jackson Cooper-Driver9db08832025-01-24 11:24:20 +0000194 output_str(output_func, priv, get_log_prefix(log_level), 0, true);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100195 output_char(output_func, priv, spacer);
196
197 tfm_vprintf_internal(output_func, priv, fmt, args);
198}