blob: 10a7ed229309e1ae47529f02774dcd4363817339 [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 Scull18c78fc2018-08-20 12:57:41 +010014#include "hf/spinlock.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010015#include "hf/std.h"
David Brazdil52230432020-01-27 15:08:34 +000016#include "hf/stdout.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010017
Andrew Scull4f170f52018-07-19 12:58:20 +010018/* Keep macro alignment */
19/* clang-format off */
20
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010021#define FLAG_SPACE 0x01
22#define FLAG_ZERO 0x02
23#define FLAG_MINUS 0x04
24#define FLAG_PLUS 0x08
25#define FLAG_ALT 0x10
26#define FLAG_UPPER 0x20
27#define FLAG_NEG 0x40
28
Andrew Scull4a867bc2019-04-08 10:15:11 +010029#define DLOG_MAX_STRING_LENGTH 64
30
Andrew Scull4f170f52018-07-19 12:58:20 +010031/* clang-format on */
32
Andrew Scullfdd716e2018-12-20 05:37:31 +000033static bool dlog_lock_enabled = false;
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010034static struct spinlock sl = SPINLOCK_INIT;
35
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010036/*
37 * These global variables for the log buffer are not static because a test needs
38 * to access them directly.
39 */
40size_t dlog_buffer_offset;
41char dlog_buffer[DLOG_BUFFER_SIZE];
42
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010043/**
44 * Takes the lock, if it is enabled.
45 */
46static void lock(void)
47{
48 if (dlog_lock_enabled) {
49 sl_lock(&sl);
50 }
51}
52
53/**
54 * Releases the lock, if it is enabled.
55 */
56static void unlock(void)
57{
58 if (dlog_lock_enabled) {
59 sl_unlock(&sl);
60 }
61}
Andrew Scullfdd716e2018-12-20 05:37:31 +000062
63/**
64 * Enables the lock protecting the serial device.
65 */
66void dlog_enable_lock(void)
67{
68 dlog_lock_enabled = true;
69}
70
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010071static void dlog_putchar(char c)
72{
73 dlog_buffer[dlog_buffer_offset] = c;
74 dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
David Brazdil52230432020-01-27 15:08:34 +000075 stdout_putchar(c);
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010076}
77
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010078/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010079 * Prints a raw string to the debug log and returns its length.
80 */
81static size_t print_raw_string(const char *str)
82{
83 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000084
Andrew Scull7364a8e2018-07-19 15:39:29 +010085 while (*c != '\0') {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010086 dlog_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010087 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000088
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010089 return c - str;
90}
91
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010092/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010093 * Prints a formatted string to the debug log. The format includes a minimum
94 * width, the fill character, and flags (whether to align to left or right).
95 *
96 * str is the full string, while suffix is a pointer within str that indicates
97 * where the suffix begins. This is used when printing right-aligned numbers
98 * with a zero fill; for example, -10 with width 4 should be padded to -010,
99 * so suffix would point to index one of the "-10" string .
Karl Meakind2ef6182024-05-22 11:23:12 +0100100 *
101 * Returns number of characters written.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100102 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100103static size_t print_string(const char *str, const char *suffix, size_t width,
104 int flags, char fill)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100105{
Karl Meakind2ef6182024-05-22 11:23:12 +0100106 size_t chars_written = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100107 size_t len = suffix - str;
108
109 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100110 while (str != suffix) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100111 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100112 dlog_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100113 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100114
115 if (flags & FLAG_MINUS) {
116 /* Left-aligned. Print suffix, then print padding if needed. */
117 len += print_raw_string(suffix);
118 while (len < width) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100119 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100120 dlog_putchar(' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100121 len++;
122 }
Karl Meakind2ef6182024-05-22 11:23:12 +0100123 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100124 }
125
126 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +0100127 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100128 while (len < width) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100129 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100130 dlog_putchar(fill);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100131 len++;
132 }
133
134 /* Now print the rest of the string. */
Karl Meakind2ef6182024-05-22 11:23:12 +0100135 chars_written += print_raw_string(suffix);
136 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100137}
138
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100139/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100140 * Prints a number to the debug log. The caller specifies the base, its minimum
141 * width and printf-style flags.
Karl Meakind2ef6182024-05-22 11:23:12 +0100142 *
143 * Returns number of characters written.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100144 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100145static size_t print_num(size_t v, size_t base, size_t width, int flags)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100146{
147 static const char *digits_lower = "0123456789abcdefx";
148 static const char *digits_upper = "0123456789ABCDEFX";
149 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
Andrew Scull4a867bc2019-04-08 10:15:11 +0100150 char buf[DLOG_MAX_STRING_LENGTH];
Andrew Scullf3d45592018-09-20 14:30:22 +0100151 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100152 char *num;
153 *ptr = '\0';
154 do {
155 --ptr;
156 *ptr = d[v % base];
157 v /= base;
158 } while (v);
159
160 /* Num stores where the actual number begins. */
161 num = ptr;
162
163 /* Add prefix if requested. */
164 if (flags & FLAG_ALT) {
165 switch (base) {
166 case 16:
167 ptr -= 2;
168 ptr[0] = '0';
169 ptr[1] = d[16];
170 break;
171
172 case 8:
173 ptr--;
174 *ptr = '0';
175 break;
176 }
177 }
178
179 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100180 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100181 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100182 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100183 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100184 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100185 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100186 }
187 if (flags & FLAG_ZERO) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100188 return print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100189 } else {
Karl Meakind2ef6182024-05-22 11:23:12 +0100190 return print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100191 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100192}
193
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100194/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100195 * Parses the optional flags field of a printf-style format. It returns the spot
196 * on the string where a non-flag character was found.
197 */
198static const char *parse_flags(const char *p, int *flags)
199{
200 for (;;) {
201 switch (*p) {
202 case ' ':
203 *flags |= FLAG_SPACE;
204 break;
205
206 case '0':
207 *flags |= FLAG_ZERO;
208 break;
209
210 case '-':
211 *flags |= FLAG_MINUS;
212 break;
213
214 case '+':
215 *flags |= FLAG_PLUS;
216
217 case '#':
218 *flags |= FLAG_ALT;
219 break;
220
221 default:
222 return p;
223 }
224 p++;
225 }
226}
227
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100228/**
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100229 * Send the contents of the given VM's log buffer to the log, preceded by the VM
230 * ID and followed by a newline.
231 */
J-Alves19e20cf2023-08-02 12:48:55 +0100232void dlog_flush_vm_buffer(ffa_id_t id, char buffer[], size_t length)
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100233{
234 lock();
235
J-Alvesb882db92023-08-02 13:40:07 +0100236 if (ffa_is_vm_id(id)) {
237 print_raw_string("VM ");
238 } else {
239 print_raw_string("SP ");
240 }
Olivier Deprez2a8ee342020-08-03 15:10:44 +0200241 print_num(id, 16, 0, 0);
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100242 print_raw_string(": ");
243
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100244 for (size_t i = 0; i < length; ++i) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100245 dlog_putchar(buffer[i]);
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100246 buffer[i] = '\0';
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100247 }
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100248 dlog_putchar('\n');
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100249
250 unlock();
251}
252
253/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000254 * Same as "dlog", except that arguments are passed as a va_list
Karl Meakind2ef6182024-05-22 11:23:12 +0100255 *
256 * Returns number of characters written, or `-1` if format string is invalid.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100257 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100258size_t vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100259{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100260 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100261 size_t w;
Karl Meakind2ef6182024-05-22 11:23:12 +0100262 size_t chars_written = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100263 int flags;
264 char buf[2];
265
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100266 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000267
Andrew Scull020ae692018-07-19 16:20:14 +0100268 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100269 switch (*p) {
270 default:
Karl Meakind2ef6182024-05-22 11:23:12 +0100271 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100272 dlog_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100273 break;
274
275 case '%':
276 /* Read optional flags. */
277 flags = 0;
278 p = parse_flags(p + 1, &flags) - 1;
279
280 /* Read the minimum width, if one is specified. */
281 w = 0;
282 while (p[1] >= '0' && p[1] <= '9') {
283 w = (w * 10) + (p[1] - '0');
284 p++;
285 }
286
287 /* Read minimum width from arguments. */
288 if (w == 0 && p[1] == '*') {
289 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000290
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100291 if (v >= 0) {
292 w = v;
293 } else {
294 w = -v;
295 flags |= FLAG_MINUS;
296 }
297 p++;
298 }
299
300 /* Handle the format specifier. */
301 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100302 case 's': {
303 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000304
Karl Meakind2ef6182024-05-22 11:23:12 +0100305 chars_written +=
306 print_string(str, str, 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 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100311 case 'i': {
312 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000313
Andrew Scull4f170f52018-07-19 12:58:20 +0100314 if (v < 0) {
315 flags |= FLAG_NEG;
316 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100317 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100318
Karl Meakind2ef6182024-05-22 11:23:12 +0100319 chars_written +=
320 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100321 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100322 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100323
324 case 'X':
325 flags |= FLAG_UPPER;
Karl Meakind2ef6182024-05-22 11:23:12 +0100326 chars_written += print_num(va_arg(args, size_t),
327 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000328 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100329 break;
330
331 case 'p':
Karl Meakind2ef6182024-05-22 11:23:12 +0100332 chars_written += print_num(
333 va_arg(args, size_t), 16,
334 sizeof(size_t) * 2, FLAG_ZERO);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100335 p++;
336 break;
337
338 case 'x':
Karl Meakind2ef6182024-05-22 11:23:12 +0100339 chars_written += print_num(va_arg(args, size_t),
340 16, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100341 p++;
342 break;
343
344 case 'u':
Karl Meakind2ef6182024-05-22 11:23:12 +0100345 chars_written += print_num(va_arg(args, size_t),
346 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100347 p++;
348 break;
349
350 case 'o':
Karl Meakind2ef6182024-05-22 11:23:12 +0100351 chars_written += print_num(va_arg(args, size_t),
352 8, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100353 p++;
354 break;
355
356 case 'c':
357 buf[1] = 0;
358 buf[0] = va_arg(args, int);
Karl Meakind2ef6182024-05-22 11:23:12 +0100359 chars_written +=
360 print_string(buf, buf, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100361 p++;
362 break;
363
364 case '%':
Karl Meakind2ef6182024-05-22 11:23:12 +0100365 chars_written++;
366 dlog_putchar('%');
367 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100368 break;
369
370 default:
Karl Meakind2ef6182024-05-22 11:23:12 +0100371 chars_written = -1;
372 goto out;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100373 }
374
375 break;
376 }
377 }
Karl Meakind2ef6182024-05-22 11:23:12 +0100378out:
Kathleen Capellafd306bb2023-03-17 18:14:01 -0400379 stdout_flush();
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100380 unlock();
Karl Meakind2ef6182024-05-22 11:23:12 +0100381 return chars_written;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100382}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100383
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100384/**
385 * Prints the given format string to the debug log.
Karl Meakind2ef6182024-05-22 11:23:12 +0100386 *
387 * Returns number of characters written, or `-1` if format string is invalid.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100388 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100389size_t dlog(const char *fmt, ...)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100390{
Karl Meakind2ef6182024-05-22 11:23:12 +0100391 size_t chars_written = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100392 va_list args;
393
394 va_start(args, fmt);
Karl Meakind2ef6182024-05-22 11:23:12 +0100395 chars_written = vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100396 va_end(args);
Karl Meakind2ef6182024-05-22 11:23:12 +0100397 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100398}