blob: 4901c3d48f4a8054448cfcc025843dcbda41c241 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
Andrew Scull18834872018-10-12 11:48:09 +01007 */
8
Andrew Scull18c78fc2018-08-20 12:57:41 +01009#include "hf/dlog.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010010
11#include <stdbool.h>
12#include <stddef.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010013
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010014#include "hf/ffa.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010015#include "hf/spinlock.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010016#include "hf/std.h"
David Brazdil52230432020-01-27 15:08:34 +000017#include "hf/stdout.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018
Andrew Scull4f170f52018-07-19 12:58:20 +010019/* Keep macro alignment */
20/* clang-format off */
21
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010022#define FLAG_SPACE 0x01
23#define FLAG_ZERO 0x02
24#define FLAG_MINUS 0x04
25#define FLAG_PLUS 0x08
26#define FLAG_ALT 0x10
27#define FLAG_UPPER 0x20
28#define FLAG_NEG 0x40
29
Andrew Scull4a867bc2019-04-08 10:15:11 +010030#define DLOG_MAX_STRING_LENGTH 64
31
Andrew Scull4f170f52018-07-19 12:58:20 +010032/* clang-format on */
33
Andrew Scullfdd716e2018-12-20 05:37:31 +000034static bool dlog_lock_enabled = false;
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010035static struct spinlock sl = SPINLOCK_INIT;
36
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010037/*
38 * These global variables for the log buffer are not static because a test needs
39 * to access them directly.
40 */
41size_t dlog_buffer_offset;
42char dlog_buffer[DLOG_BUFFER_SIZE];
43
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010044/**
45 * Takes the lock, if it is enabled.
46 */
47static void lock(void)
48{
49 if (dlog_lock_enabled) {
50 sl_lock(&sl);
51 }
52}
53
54/**
55 * Releases the lock, if it is enabled.
56 */
57static void unlock(void)
58{
59 if (dlog_lock_enabled) {
60 sl_unlock(&sl);
61 }
62}
Andrew Scullfdd716e2018-12-20 05:37:31 +000063
64/**
65 * Enables the lock protecting the serial device.
66 */
67void dlog_enable_lock(void)
68{
69 dlog_lock_enabled = true;
70}
71
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010072static void dlog_putchar(char c)
73{
74 dlog_buffer[dlog_buffer_offset] = c;
75 dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
David Brazdil52230432020-01-27 15:08:34 +000076 stdout_putchar(c);
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010077}
78
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010079/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010080 * Prints a raw string to the debug log and returns its length.
81 */
82static size_t print_raw_string(const char *str)
83{
84 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000085
Andrew Scull7364a8e2018-07-19 15:39:29 +010086 while (*c != '\0') {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010087 dlog_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010088 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000089
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010090 return c - str;
91}
92
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010093/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010094 * Prints a formatted string to the debug log. The format includes a minimum
95 * width, the fill character, and flags (whether to align to left or right).
96 *
97 * str is the full string, while suffix is a pointer within str that indicates
98 * where the suffix begins. This is used when printing right-aligned numbers
99 * with a zero fill; for example, -10 with width 4 should be padded to -010,
100 * so suffix would point to index one of the "-10" string .
101 */
102static void print_string(const char *str, const char *suffix, size_t width,
103 int flags, char fill)
104{
105 size_t len = suffix - str;
106
107 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100108 while (str != suffix) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100109 dlog_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100110 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100111
112 if (flags & FLAG_MINUS) {
113 /* Left-aligned. Print suffix, then print padding if needed. */
114 len += print_raw_string(suffix);
115 while (len < width) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100116 dlog_putchar(' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100117 len++;
118 }
119 return;
120 }
121
122 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +0100123 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100124 while (len < width) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100125 dlog_putchar(fill);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100126 len++;
127 }
128
129 /* Now print the rest of the string. */
130 print_raw_string(suffix);
131}
132
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100133/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100134 * Prints a number to the debug log. The caller specifies the base, its minimum
135 * width and printf-style flags.
136 */
137static void print_num(size_t v, size_t base, size_t width, int flags)
138{
139 static const char *digits_lower = "0123456789abcdefx";
140 static const char *digits_upper = "0123456789ABCDEFX";
141 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
Andrew Scull4a867bc2019-04-08 10:15:11 +0100142 char buf[DLOG_MAX_STRING_LENGTH];
Andrew Scullf3d45592018-09-20 14:30:22 +0100143 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100144 char *num;
145 *ptr = '\0';
146 do {
147 --ptr;
148 *ptr = d[v % base];
149 v /= base;
150 } while (v);
151
152 /* Num stores where the actual number begins. */
153 num = ptr;
154
155 /* Add prefix if requested. */
156 if (flags & FLAG_ALT) {
157 switch (base) {
158 case 16:
159 ptr -= 2;
160 ptr[0] = '0';
161 ptr[1] = d[16];
162 break;
163
164 case 8:
165 ptr--;
166 *ptr = '0';
167 break;
168 }
169 }
170
171 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100172 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100173 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100174 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100175 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100176 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100177 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100178 }
179 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100180 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100181 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100182 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100183 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100184}
185
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100186/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100187 * Parses the optional flags field of a printf-style format. It returns the spot
188 * on the string where a non-flag character was found.
189 */
190static const char *parse_flags(const char *p, int *flags)
191{
192 for (;;) {
193 switch (*p) {
194 case ' ':
195 *flags |= FLAG_SPACE;
196 break;
197
198 case '0':
199 *flags |= FLAG_ZERO;
200 break;
201
202 case '-':
203 *flags |= FLAG_MINUS;
204 break;
205
206 case '+':
207 *flags |= FLAG_PLUS;
208
209 case '#':
210 *flags |= FLAG_ALT;
211 break;
212
213 default:
214 return p;
215 }
216 p++;
217 }
218}
219
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100220/**
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100221 * Send the contents of the given VM's log buffer to the log, preceded by the VM
222 * ID and followed by a newline.
223 */
J-Alves19e20cf2023-08-02 12:48:55 +0100224void dlog_flush_vm_buffer(ffa_id_t id, char buffer[], size_t length)
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100225{
226 lock();
227
J-Alvesb882db92023-08-02 13:40:07 +0100228 if (ffa_is_vm_id(id)) {
229 print_raw_string("VM ");
230 } else {
231 print_raw_string("SP ");
232 }
Olivier Deprez2a8ee342020-08-03 15:10:44 +0200233 print_num(id, 16, 0, 0);
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100234 print_raw_string(": ");
235
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100236 for (size_t i = 0; i < length; ++i) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100237 dlog_putchar(buffer[i]);
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100238 buffer[i] = '\0';
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100239 }
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100240 dlog_putchar('\n');
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100241
242 unlock();
243}
244
245/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000246 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000248void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100249{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100250 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251 size_t w;
252 int flags;
253 char buf[2];
254
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100255 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000256
Andrew Scull020ae692018-07-19 16:20:14 +0100257 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100258 switch (*p) {
259 default:
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100260 dlog_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100261 break;
262
263 case '%':
264 /* Read optional flags. */
265 flags = 0;
266 p = parse_flags(p + 1, &flags) - 1;
267
268 /* Read the minimum width, if one is specified. */
269 w = 0;
270 while (p[1] >= '0' && p[1] <= '9') {
271 w = (w * 10) + (p[1] - '0');
272 p++;
273 }
274
275 /* Read minimum width from arguments. */
276 if (w == 0 && p[1] == '*') {
277 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000278
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100279 if (v >= 0) {
280 w = v;
281 } else {
282 w = -v;
283 flags |= FLAG_MINUS;
284 }
285 p++;
286 }
287
288 /* Handle the format specifier. */
289 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100290 case 's': {
291 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000292
Andrew Scull4f170f52018-07-19 12:58:20 +0100293 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100294 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100295 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100296
297 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100298 case 'i': {
299 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000300
Andrew Scull4f170f52018-07-19 12:58:20 +0100301 if (v < 0) {
302 flags |= FLAG_NEG;
303 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100304 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100305
306 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100307 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100308 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100309
310 case 'X':
311 flags |= FLAG_UPPER;
312 print_num(va_arg(args, size_t), 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000313 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100314 break;
315
316 case 'p':
317 print_num(va_arg(args, size_t), 16,
318 sizeof(size_t) * 2, FLAG_ZERO);
319 p++;
320 break;
321
322 case 'x':
323 print_num(va_arg(args, size_t), 16, w, flags);
324 p++;
325 break;
326
327 case 'u':
328 print_num(va_arg(args, size_t), 10, w, flags);
329 p++;
330 break;
331
332 case 'o':
333 print_num(va_arg(args, size_t), 8, w, flags);
334 p++;
335 break;
336
337 case 'c':
338 buf[1] = 0;
339 buf[0] = va_arg(args, int);
340 print_string(buf, buf, w, flags, ' ');
341 p++;
342 break;
343
344 case '%':
345 break;
346
347 default:
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100348 dlog_putchar('%');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100349 }
350
351 break;
352 }
353 }
Kathleen Capellafd306bb2023-03-17 18:14:01 -0400354 stdout_flush();
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100355 unlock();
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100356}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100357
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100358/**
359 * Prints the given format string to the debug log.
360 */
361void dlog(const char *fmt, ...)
362{
363 va_list args;
364
365 va_start(args, fmt);
366 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100367 va_end(args);
368}