blob: 83e325bb26807c2a1dcc3459bbe4911afc59dc22 [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
Karl Meakine980e612024-03-28 17:46:32 +000018enum { DLOG_MAX_STRING_LENGTH = 64 };
19
20/* Keep fields aligned */
Andrew Scull4f170f52018-07-19 12:58:20 +010021/* clang-format off */
Karl Meakine980e612024-03-28 17:46:32 +000022struct format_flags {
23 bool minus : 1;
24 bool plus : 1;
25 bool space : 1;
26 bool alt : 1;
27 bool zero : 1;
28 bool upper : 1;
29 bool neg : 1;
30};
Andrew Scull4f170f52018-07-19 12:58:20 +010031/* clang-format on */
32
Karl Meakine980e612024-03-28 17:46:32 +000033enum format_base {
34 base8 = 8,
35 base10 = 10,
36 base16 = 16,
37};
38
Andrew Scullfdd716e2018-12-20 05:37:31 +000039static bool dlog_lock_enabled = false;
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010040static struct spinlock sl = SPINLOCK_INIT;
41
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010042/*
43 * These global variables for the log buffer are not static because a test needs
44 * to access them directly.
45 */
46size_t dlog_buffer_offset;
47char dlog_buffer[DLOG_BUFFER_SIZE];
48
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010049/**
50 * Takes the lock, if it is enabled.
51 */
52static void lock(void)
53{
54 if (dlog_lock_enabled) {
55 sl_lock(&sl);
56 }
57}
58
59/**
60 * Releases the lock, if it is enabled.
61 */
62static void unlock(void)
63{
64 if (dlog_lock_enabled) {
65 sl_unlock(&sl);
66 }
67}
Andrew Scullfdd716e2018-12-20 05:37:31 +000068
69/**
70 * Enables the lock protecting the serial device.
71 */
72void dlog_enable_lock(void)
73{
74 dlog_lock_enabled = true;
75}
76
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010077static void dlog_putchar(char c)
78{
79 dlog_buffer[dlog_buffer_offset] = c;
80 dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
David Brazdil52230432020-01-27 15:08:34 +000081 stdout_putchar(c);
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010082}
83
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010084/**
Karl Meakine980e612024-03-28 17:46:32 +000085 * Prints a literal string (i.e. '%' is not interpreted specially) to the debug
86 * log.
87 *
88 * Returns number of characters written.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010089 */
90static size_t print_raw_string(const char *str)
91{
92 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000093
Karl Meakine980e612024-03-28 17:46:32 +000094 for (; *c != '\0'; c++) {
95 dlog_putchar(*c);
Andrew Scull7364a8e2018-07-19 15:39:29 +010096 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000097
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010098 return c - str;
99}
100
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100101/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100102 * Prints a formatted string to the debug log. The format includes a minimum
103 * width, the fill character, and flags (whether to align to left or right).
104 *
105 * str is the full string, while suffix is a pointer within str that indicates
106 * where the suffix begins. This is used when printing right-aligned numbers
107 * with a zero fill; for example, -10 with width 4 should be padded to -010,
108 * so suffix would point to index one of the "-10" string .
Karl Meakind2ef6182024-05-22 11:23:12 +0100109 *
110 * Returns number of characters written.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100111 */
Karl Meakine980e612024-03-28 17:46:32 +0000112static size_t print_string(const char *str, const char *suffix,
113 size_t min_width, struct format_flags flags,
114 char fill)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100115{
Karl Meakind2ef6182024-05-22 11:23:12 +0100116 size_t chars_written = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100117 size_t len = suffix - str;
118
119 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100120 while (str != suffix) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100121 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100122 dlog_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100123 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100124
Karl Meakine980e612024-03-28 17:46:32 +0000125 if (flags.minus) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100126 /* Left-aligned. Print suffix, then print padding if needed. */
127 len += print_raw_string(suffix);
Karl Meakine980e612024-03-28 17:46:32 +0000128 while (len < min_width) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100129 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100130 dlog_putchar(' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100131 len++;
132 }
Karl Meakind2ef6182024-05-22 11:23:12 +0100133 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100134 }
135
136 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +0100137 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Karl Meakine980e612024-03-28 17:46:32 +0000138 while (len < min_width) {
Karl Meakind2ef6182024-05-22 11:23:12 +0100139 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100140 dlog_putchar(fill);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100141 len++;
142 }
143
144 /* Now print the rest of the string. */
Karl Meakind2ef6182024-05-22 11:23:12 +0100145 chars_written += print_raw_string(suffix);
146 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100147}
148
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100149/**
Karl Meakine980e612024-03-28 17:46:32 +0000150 * Prints an integer to the debug log. The caller specifies the base, its
151 * minimum width and printf-style flags.
Karl Meakind2ef6182024-05-22 11:23:12 +0100152 *
153 * Returns number of characters written.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100154 */
Karl Meakine980e612024-03-28 17:46:32 +0000155static size_t print_int(size_t value, enum format_base base, size_t min_width,
156 struct format_flags flags)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100157{
158 static const char *digits_lower = "0123456789abcdefx";
159 static const char *digits_upper = "0123456789ABCDEFX";
Karl Meakine980e612024-03-28 17:46:32 +0000160 const char *digits = flags.upper ? digits_upper : digits_lower;
Andrew Scull4a867bc2019-04-08 10:15:11 +0100161 char buf[DLOG_MAX_STRING_LENGTH];
Andrew Scullf3d45592018-09-20 14:30:22 +0100162 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100163 char *num;
164 *ptr = '\0';
165 do {
166 --ptr;
Karl Meakine980e612024-03-28 17:46:32 +0000167 *ptr = digits[value % base];
168 value /= base;
169 } while (value);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100170
171 /* Num stores where the actual number begins. */
172 num = ptr;
173
174 /* Add prefix if requested. */
Karl Meakine980e612024-03-28 17:46:32 +0000175 if (flags.alt) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100176 switch (base) {
Karl Meakine980e612024-03-28 17:46:32 +0000177 case base16:
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100178 ptr -= 2;
179 ptr[0] = '0';
Karl Meakine980e612024-03-28 17:46:32 +0000180 ptr[1] = digits[16];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100181 break;
182
Karl Meakine980e612024-03-28 17:46:32 +0000183 case base8:
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100184 ptr--;
185 *ptr = '0';
186 break;
Karl Meakine980e612024-03-28 17:46:32 +0000187
188 case base10:
189 /* do nothing */
190 break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100191 }
192 }
193
194 /* Add sign if requested. */
Karl Meakine980e612024-03-28 17:46:32 +0000195 if (flags.neg) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100196 *--ptr = '-';
Karl Meakine980e612024-03-28 17:46:32 +0000197 } else if (flags.plus) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100198 *--ptr = '+';
Karl Meakine980e612024-03-28 17:46:32 +0000199 } else if (flags.space) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100200 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100201 }
Karl Meakine980e612024-03-28 17:46:32 +0000202 return print_string(ptr, num, min_width, flags, flags.zero ? '0' : ' ');
203}
204
205/**
206 * Parses the optional flags field of a printf-style format. Returns a pointer
207 * to the first non-flag character in the string.
208 */
209static const char *parse_flags(const char *fmt, struct format_flags *flags)
210{
211 for (;; fmt++) {
212 switch (*fmt) {
213 case '-':
214 flags->minus = true;
215 break;
216
217 case '+':
218 flags->plus = true;
219 break;
220
221 case ' ':
222 flags->space = true;
223 break;
224
225 case '#':
226 flags->alt = true;
227 break;
228
229 case '0':
230 flags->zero = true;
231 break;
232
233 default:
234 return fmt;
235 }
Andrew Scull7364a8e2018-07-19 15:39:29 +0100236 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100237}
238
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100239/**
Karl Meakine980e612024-03-28 17:46:32 +0000240 * Parses the optional minimum width field of a printf-style format.
241 * If the width is negative, `flags.minus` is set.
242 *
243 * Returns a pointer to the first non-digit character in the string.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100244 */
Karl Meakine980e612024-03-28 17:46:32 +0000245static const char *parse_min_width(const char *fmt, va_list args,
246 struct format_flags *flags, int *min_width)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247{
Karl Meakine980e612024-03-28 17:46:32 +0000248 int width = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100249
Karl Meakine980e612024-03-28 17:46:32 +0000250 /* Read minimum width from arguments. */
251 if (*fmt == '*') {
252 fmt++;
253 width = va_arg(args, int);
254 if (width < 0) {
255 width = -width;
256 flags->minus = true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100257 }
Karl Meakine980e612024-03-28 17:46:32 +0000258 } else {
259 for (; *fmt >= '0' && *fmt <= '9'; fmt++) {
260 width = (width * 10) + (*fmt - '0');
261 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100262 }
Karl Meakine980e612024-03-28 17:46:32 +0000263
264 *min_width = width;
265
266 return fmt;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100267}
268
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100269/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000270 * Same as "dlog", except that arguments are passed as a va_list
Karl Meakind2ef6182024-05-22 11:23:12 +0100271 *
272 * Returns number of characters written, or `-1` if format string is invalid.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100273 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100274size_t vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100275{
Karl Meakind2ef6182024-05-22 11:23:12 +0100276 size_t chars_written = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100277
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100278 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000279
Karl Meakine980e612024-03-28 17:46:32 +0000280 while (*fmt != '\0') {
281 switch (*fmt) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100282 default:
Karl Meakind2ef6182024-05-22 11:23:12 +0100283 chars_written++;
Karl Meakine980e612024-03-28 17:46:32 +0000284 dlog_putchar(*fmt);
285 fmt++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100286 break;
287
Karl Meakine980e612024-03-28 17:46:32 +0000288 case '%': {
289 struct format_flags flags = {0};
290 int min_width = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100291
Karl Meakine980e612024-03-28 17:46:32 +0000292 fmt++;
293 fmt = parse_flags(fmt, &flags);
294 fmt = parse_min_width(fmt, args, &flags, &min_width);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100295
296 /* Handle the format specifier. */
Karl Meakine980e612024-03-28 17:46:32 +0000297 switch (*fmt) {
298 case '%':
299 fmt++;
300 chars_written++;
301 dlog_putchar('%');
302 break;
303
304 case 'c': {
305 char str[2] = {va_arg(args, int), 0};
306
307 fmt++;
308 chars_written += print_string(
309 str, str, min_width, flags, ' ');
310 break;
311 }
312
Andrew Scull4f170f52018-07-19 12:58:20 +0100313 case 's': {
314 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000315
Karl Meakine980e612024-03-28 17:46:32 +0000316 fmt++;
317 chars_written += print_string(
318 str, str, min_width, flags, ' ');
319 break;
320 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100321
322 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100323 case 'i': {
324 int v = va_arg(args, int);
Karl Meakine980e612024-03-28 17:46:32 +0000325 fmt++;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000326
Andrew Scull4f170f52018-07-19 12:58:20 +0100327 if (v < 0) {
Karl Meakine980e612024-03-28 17:46:32 +0000328 flags.neg = true;
Andrew Scull4f170f52018-07-19 12:58:20 +0100329 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100330 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100331
Karl Meakine980e612024-03-28 17:46:32 +0000332 chars_written += print_int((size_t)v, base10,
333 min_width, flags);
334 break;
335 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100336
337 case 'X':
Karl Meakine980e612024-03-28 17:46:32 +0000338 fmt++;
339 flags.upper = true;
340 chars_written +=
341 print_int(va_arg(args, size_t), base16,
342 min_width, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100343 break;
344
345 case 'p':
Karl Meakine980e612024-03-28 17:46:32 +0000346 fmt++;
347 min_width = sizeof(size_t) * 2 + 2;
348 flags.zero = true;
349 flags.alt = true;
350
351 chars_written +=
352 print_int(va_arg(args, uintptr_t),
353 base16, min_width, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100354 break;
355
356 case 'x':
Karl Meakine980e612024-03-28 17:46:32 +0000357 fmt++;
358 chars_written +=
359 print_int(va_arg(args, size_t), base16,
360 min_width, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100361 break;
362
363 case 'u':
Karl Meakine980e612024-03-28 17:46:32 +0000364 fmt++;
365 chars_written +=
366 print_int(va_arg(args, size_t), base10,
367 min_width, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100368 break;
369
370 case 'o':
Karl Meakine980e612024-03-28 17:46:32 +0000371 fmt++;
Karl Meakind2ef6182024-05-22 11:23:12 +0100372 chars_written +=
Karl Meakine980e612024-03-28 17:46:32 +0000373 print_int(va_arg(args, size_t), base8,
374 min_width, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100375 break;
376
377 default:
Karl Meakind2ef6182024-05-22 11:23:12 +0100378 chars_written = -1;
379 goto out;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100380 }
Karl Meakine980e612024-03-28 17:46:32 +0000381 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100382 }
383 }
Karl Meakine980e612024-03-28 17:46:32 +0000384
Karl Meakind2ef6182024-05-22 11:23:12 +0100385out:
Kathleen Capellafd306bb2023-03-17 18:14:01 -0400386 stdout_flush();
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100387 unlock();
Karl Meakind2ef6182024-05-22 11:23:12 +0100388 return chars_written;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100389}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100390
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100391/**
392 * Prints the given format string to the debug log.
Karl Meakind2ef6182024-05-22 11:23:12 +0100393 *
Karl Meakine980e612024-03-28 17:46:32 +0000394 * The format string supported is the same as described in
395 * https://en.cppreference.com/w/c/io/fprintf, with the following exceptions:
396 * - Floating-point formatters (`%f`, `%F`, `%e`, `%E`, `%a`, `%A`, `%g`, `%G`,
397 * `%L`) are not supported because floats are not used in Hafnium and
398 * formatting them is too complicated.
399 * - `%n` is not supported because it is rarely used and potentially dangerous.
400 * - Precision modifiers (`%.*` and `%.` followed by an integer) are not
401 * supported.
402 *
Karl Meakind2ef6182024-05-22 11:23:12 +0100403 * Returns number of characters written, or `-1` if format string is invalid.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100404 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100405size_t dlog(const char *fmt, ...)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100406{
Karl Meakind2ef6182024-05-22 11:23:12 +0100407 size_t chars_written = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100408 va_list args;
409
410 va_start(args, fmt);
Karl Meakind2ef6182024-05-22 11:23:12 +0100411 chars_written = vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100412 va_end(args);
Karl Meakind2ef6182024-05-22 11:23:12 +0100413 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100414}