blob: d8fa4c65c79bfd700c8de23977f495802b19d36b [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,
56 uint16_t num_padding, bool zero_padding)
57{
58 uint8_t digit, chars_to_print;
59 uint16_t i;
60 char buf[9];
61 char *const buf_end = &buf[sizeof(buf) - 1];
62 char *buf_ptr = buf_end;
63 const char pad_char = zero_padding ? '0' : ' ';
64
65 /* Ensure buffer ends with NULL character */
66 *buf_ptr-- = '\0';
67
68 do {
69 digit = val & 0xf;
70
71 if (digit < 10) {
72 *buf_ptr-- = '0' + digit;
73 } else {
74 *buf_ptr-- = 'a' + digit - 10;
75 }
76
77 val >>= 4;
78 } while (val);
79
80 chars_to_print = (buf_end - 1) - buf_ptr;
81 if (num_padding > chars_to_print) {
82 num_padding -= chars_to_print;
83 } else {
84 num_padding = 0;
85 }
86
87 for (i = 0; i < num_padding; i++) {
88 output_char(output_func, priv, pad_char);
89 }
90
91 output_str(output_func, priv, buf_ptr + 1);
92}
93
94/* Basic vprintf, understands:
95 * %s: output string
96 * %x: output uint32_t in hex
97 */
98static void tfm_vprintf_internal(tfm_log_output_str output_func,
99 void *priv, const char *fmt, va_list args)
100{
101 char c;
102 bool formatting = false;
103 uint16_t num_padding = 0;
104 bool zero_padding = false;
105
106 while ((c = *fmt++) != '\0') {
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100107 if (!formatting) {
Jackson Cooper-Driver65940702024-09-04 15:57:12 +0000108 if (c == '%') {
109 zero_padding = false;
110 num_padding = 0;
111 formatting = true;
112 } else {
113 if (c == '\n') {
114 output_char(output_func, priv, '\r');
115 }
116 output_char(output_func, priv, c);
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100117 }
Jackson Cooper-Drivered984c92024-08-14 16:12:40 +0100118 continue;
119 }
120
121 switch (c) {
122 case 'l':
123 continue;
124 case 'x':
125 output_val(output_func, priv, va_arg(args, uint32_t), num_padding, zero_padding);
126 break;
127 case 's':
128 output_str(output_func, priv, va_arg(args, char *));
129 break;
130 case '0':
131 if (num_padding == 0) {
132 zero_padding = true;
133 continue;
134 }
135 /* fallthrough */
136 case '1':
137 case '2':
138 case '3':
139 case '4':
140 case '5':
141 case '6':
142 case '7':
143 case '8':
144 case '9':
145 num_padding *= 10;
146 num_padding += c - '0';
147 continue;
148 case '%':
149 output_char(output_func, priv, '%');
150 break;
151 default:
152 output_str(output_func, priv, "[Unsupported]");
153 }
154
155 formatting = false;
156 }
157}
158
159void tfm_vprintf(tfm_log_output_str output_func, void *priv, const char *fmt, va_list args)
160{
161 uint8_t log_level;
162 const char spacer = ' ';
163
164 /* We expect the LOG_MARKER_* macro as the first character */
165 log_level = fmt[0];
166 fmt++;
167
168 output_str(output_func, priv, get_log_prefix(log_level));
169 output_char(output_func, priv, spacer);
170
171 tfm_vprintf_internal(output_func, priv, fmt, args);
172}