blob: c0e4eda9453e30bbb41ae904a0140049d6b887b8 [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 */
Andrew Walbranb5ab43c2020-04-30 11:32:54 +0100224void dlog_flush_vm_buffer(ffa_vm_id_t id, char buffer[], size_t length)
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100225{
226 lock();
227
228 print_raw_string("VM ");
Olivier Deprez2a8ee342020-08-03 15:10:44 +0200229 print_num(id, 16, 0, 0);
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100230 print_raw_string(": ");
231
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100232 for (size_t i = 0; i < length; ++i) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100233 dlog_putchar(buffer[i]);
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100234 buffer[i] = '\0';
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100235 }
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100236 dlog_putchar('\n');
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100237
238 unlock();
239}
240
241/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000242 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100243 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000244void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100245{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100246 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247 size_t w;
248 int flags;
249 char buf[2];
250
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100251 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000252
Andrew Scull020ae692018-07-19 16:20:14 +0100253 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 switch (*p) {
255 default:
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100256 dlog_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100257 break;
258
259 case '%':
260 /* Read optional flags. */
261 flags = 0;
262 p = parse_flags(p + 1, &flags) - 1;
263
264 /* Read the minimum width, if one is specified. */
265 w = 0;
266 while (p[1] >= '0' && p[1] <= '9') {
267 w = (w * 10) + (p[1] - '0');
268 p++;
269 }
270
271 /* Read minimum width from arguments. */
272 if (w == 0 && p[1] == '*') {
273 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000274
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100275 if (v >= 0) {
276 w = v;
277 } else {
278 w = -v;
279 flags |= FLAG_MINUS;
280 }
281 p++;
282 }
283
284 /* Handle the format specifier. */
285 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100286 case 's': {
287 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000288
Andrew Scull4f170f52018-07-19 12:58:20 +0100289 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100290 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100291 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100292
293 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100294 case 'i': {
295 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000296
Andrew Scull4f170f52018-07-19 12:58:20 +0100297 if (v < 0) {
298 flags |= FLAG_NEG;
299 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100300 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100301
302 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100303 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100304 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100305
306 case 'X':
307 flags |= FLAG_UPPER;
308 print_num(va_arg(args, size_t), 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000309 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100310 break;
311
312 case 'p':
313 print_num(va_arg(args, size_t), 16,
314 sizeof(size_t) * 2, FLAG_ZERO);
315 p++;
316 break;
317
318 case 'x':
319 print_num(va_arg(args, size_t), 16, w, flags);
320 p++;
321 break;
322
323 case 'u':
324 print_num(va_arg(args, size_t), 10, w, flags);
325 p++;
326 break;
327
328 case 'o':
329 print_num(va_arg(args, size_t), 8, w, flags);
330 p++;
331 break;
332
333 case 'c':
334 buf[1] = 0;
335 buf[0] = va_arg(args, int);
336 print_string(buf, buf, w, flags, ' ');
337 p++;
338 break;
339
340 case '%':
341 break;
342
343 default:
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100344 dlog_putchar('%');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100345 }
346
347 break;
348 }
349 }
350
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100351 unlock();
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100352}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100353
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100354/**
355 * Prints the given format string to the debug log.
356 */
357void dlog(const char *fmt, ...)
358{
359 va_list args;
360
361 va_start(args, fmt);
362 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100363 va_end(args);
364}