blob: 4120f102e78162a3896ad91eeb1ccdddc4133df1 [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);
34 }
35}
36
37static void output_char(tfm_log_output_str output_func, void *priv, char c)
38{
39 output_func(priv, (const unsigned char *)&c, 1);
40}
41
42static void output_str(tfm_log_output_str output_func, void *priv, const char *str)
43{
44 uint32_t len = 0;
45 const char *str_ptr = str;
46
47 while (*str_ptr++ != '\0') {
48 len++;
49 }
50
51 output_func(priv, (const unsigned char *)str, len);
52}
53
54static void output_val(tfm_log_output_str output_func, void *priv, uint32_t val,
55 uint16_t num_padding, bool zero_padding)
56{
57 uint8_t digit, chars_to_print;
58 uint16_t i;
59 char buf[9];
60 char *const buf_end = &buf[sizeof(buf) - 1];
61 char *buf_ptr = buf_end;
62 const char pad_char = zero_padding ? '0' : ' ';
63
64 /* Ensure buffer ends with NULL character */
65 *buf_ptr-- = '\0';
66
67 do {
68 digit = val & 0xf;
69
70 if (digit < 10) {
71 *buf_ptr-- = '0' + digit;
72 } else {
73 *buf_ptr-- = 'a' + digit - 10;
74 }
75
76 val >>= 4;
77 } while (val);
78
79 chars_to_print = (buf_end - 1) - buf_ptr;
80 if (num_padding > chars_to_print) {
81 num_padding -= chars_to_print;
82 } else {
83 num_padding = 0;
84 }
85
86 for (i = 0; i < num_padding; i++) {
87 output_char(output_func, priv, pad_char);
88 }
89
90 output_str(output_func, priv, buf_ptr + 1);
91}
92
93/* Basic vprintf, understands:
94 * %s: output string
95 * %x: output uint32_t in hex
96 */
97static void tfm_vprintf_internal(tfm_log_output_str output_func,
98 void *priv, const char *fmt, va_list args)
99{
100 char c;
101 bool formatting = false;
102 uint16_t num_padding = 0;
103 bool zero_padding = false;
104
105 while ((c = *fmt++) != '\0') {
106 if (c == '%') {
107 zero_padding = false;
108 num_padding = 0;
109 formatting = true;
110 continue;
111 }
112
113 if (!formatting) {
114 if (c == '\n') {
115 output_char(output_func, priv, '\r');
116 }
117 output_char(output_func, priv, c);
118 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}