blob: 1b5f2cdfc5a043fe9d88ae26dbf00856f13c9a81 [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 Scullfdd716e2018-12-20 05:37:31 +0000229 * Same as "dlog", except that arguments are passed as a va_list
Karl Meakind2ef6182024-05-22 11:23:12 +0100230 *
231 * Returns number of characters written, or `-1` if format string is invalid.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100232 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100233size_t vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100234{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100235 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100236 size_t w;
Karl Meakind2ef6182024-05-22 11:23:12 +0100237 size_t chars_written = 0;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100238 int flags;
239 char buf[2];
240
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100241 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000242
Andrew Scull020ae692018-07-19 16:20:14 +0100243 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100244 switch (*p) {
245 default:
Karl Meakind2ef6182024-05-22 11:23:12 +0100246 chars_written++;
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100247 dlog_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100248 break;
249
250 case '%':
251 /* Read optional flags. */
252 flags = 0;
253 p = parse_flags(p + 1, &flags) - 1;
254
255 /* Read the minimum width, if one is specified. */
256 w = 0;
257 while (p[1] >= '0' && p[1] <= '9') {
258 w = (w * 10) + (p[1] - '0');
259 p++;
260 }
261
262 /* Read minimum width from arguments. */
263 if (w == 0 && p[1] == '*') {
264 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000265
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100266 if (v >= 0) {
267 w = v;
268 } else {
269 w = -v;
270 flags |= FLAG_MINUS;
271 }
272 p++;
273 }
274
275 /* Handle the format specifier. */
276 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100277 case 's': {
278 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000279
Karl Meakind2ef6182024-05-22 11:23:12 +0100280 chars_written +=
281 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100282 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100283 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100284
285 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100286 case 'i': {
287 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000288
Andrew Scull4f170f52018-07-19 12:58:20 +0100289 if (v < 0) {
290 flags |= FLAG_NEG;
291 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100292 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100293
Karl Meakind2ef6182024-05-22 11:23:12 +0100294 chars_written +=
295 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100296 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100297 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100298
299 case 'X':
300 flags |= FLAG_UPPER;
Karl Meakind2ef6182024-05-22 11:23:12 +0100301 chars_written += print_num(va_arg(args, size_t),
302 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000303 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100304 break;
305
306 case 'p':
Karl Meakind2ef6182024-05-22 11:23:12 +0100307 chars_written += print_num(
308 va_arg(args, size_t), 16,
309 sizeof(size_t) * 2, FLAG_ZERO);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100310 p++;
311 break;
312
313 case 'x':
Karl Meakind2ef6182024-05-22 11:23:12 +0100314 chars_written += print_num(va_arg(args, size_t),
315 16, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100316 p++;
317 break;
318
319 case 'u':
Karl Meakind2ef6182024-05-22 11:23:12 +0100320 chars_written += print_num(va_arg(args, size_t),
321 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100322 p++;
323 break;
324
325 case 'o':
Karl Meakind2ef6182024-05-22 11:23:12 +0100326 chars_written += print_num(va_arg(args, size_t),
327 8, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100328 p++;
329 break;
330
331 case 'c':
332 buf[1] = 0;
333 buf[0] = va_arg(args, int);
Karl Meakind2ef6182024-05-22 11:23:12 +0100334 chars_written +=
335 print_string(buf, buf, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100336 p++;
337 break;
338
339 case '%':
Karl Meakind2ef6182024-05-22 11:23:12 +0100340 chars_written++;
341 dlog_putchar('%');
342 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100343 break;
344
345 default:
Karl Meakind2ef6182024-05-22 11:23:12 +0100346 chars_written = -1;
347 goto out;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100348 }
349
350 break;
351 }
352 }
Karl Meakind2ef6182024-05-22 11:23:12 +0100353out:
Kathleen Capellafd306bb2023-03-17 18:14:01 -0400354 stdout_flush();
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100355 unlock();
Karl Meakind2ef6182024-05-22 11:23:12 +0100356 return chars_written;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100357}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100358
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100359/**
360 * Prints the given format string to the debug log.
Karl Meakind2ef6182024-05-22 11:23:12 +0100361 *
362 * Returns number of characters written, or `-1` if format string is invalid.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100363 */
Karl Meakind2ef6182024-05-22 11:23:12 +0100364size_t dlog(const char *fmt, ...)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100365{
Karl Meakind2ef6182024-05-22 11:23:12 +0100366 size_t chars_written = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100367 va_list args;
368
369 va_start(args, fmt);
Karl Meakind2ef6182024-05-22 11:23:12 +0100370 chars_written = vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100371 va_end(args);
Karl Meakind2ef6182024-05-22 11:23:12 +0100372 return chars_written;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100373}