blob: 1bda56836686c448a6b1e01abdf81aa790bd8ee0 [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 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/dlog.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018
19#include <stdbool.h>
20#include <stddef.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010021
Andrew Walbran48699362019-05-20 14:38:00 +010022#include "hf/plat/console.h"
Andrew Walbran7f904bf2019-07-12 16:38:38 +010023#include "hf/spci.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010024#include "hf/spinlock.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010025#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010026
Andrew Scull4f170f52018-07-19 12:58:20 +010027/* Keep macro alignment */
28/* clang-format off */
29
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010030#define FLAG_SPACE 0x01
31#define FLAG_ZERO 0x02
32#define FLAG_MINUS 0x04
33#define FLAG_PLUS 0x08
34#define FLAG_ALT 0x10
35#define FLAG_UPPER 0x20
36#define FLAG_NEG 0x40
37
Andrew Scull4a867bc2019-04-08 10:15:11 +010038#define DLOG_MAX_STRING_LENGTH 64
39
Andrew Scull4f170f52018-07-19 12:58:20 +010040/* clang-format on */
41
Andrew Scullfdd716e2018-12-20 05:37:31 +000042static bool dlog_lock_enabled = false;
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010043static struct spinlock sl = SPINLOCK_INIT;
44
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010045/*
46 * These global variables for the log buffer are not static because a test needs
47 * to access them directly.
48 */
49size_t dlog_buffer_offset;
50char dlog_buffer[DLOG_BUFFER_SIZE];
51
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010052/**
53 * Takes the lock, if it is enabled.
54 */
55static void lock(void)
56{
57 if (dlog_lock_enabled) {
58 sl_lock(&sl);
59 }
60}
61
62/**
63 * Releases the lock, if it is enabled.
64 */
65static void unlock(void)
66{
67 if (dlog_lock_enabled) {
68 sl_unlock(&sl);
69 }
70}
Andrew Scullfdd716e2018-12-20 05:37:31 +000071
72/**
73 * Enables the lock protecting the serial device.
74 */
75void dlog_enable_lock(void)
76{
77 dlog_lock_enabled = true;
78}
79
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010080static void dlog_putchar(char c)
81{
82 dlog_buffer[dlog_buffer_offset] = c;
83 dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
84 plat_console_putchar(c);
85}
86
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010087/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010088 * Prints a raw string to the debug log and returns its length.
89 */
90static size_t print_raw_string(const char *str)
91{
92 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000093
Andrew Scull7364a8e2018-07-19 15:39:29 +010094 while (*c != '\0') {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +010095 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 .
109 */
110static void print_string(const char *str, const char *suffix, size_t width,
111 int flags, char fill)
112{
113 size_t len = suffix - str;
114
115 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100116 while (str != suffix) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100117 dlog_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100118 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100119
120 if (flags & FLAG_MINUS) {
121 /* Left-aligned. Print suffix, then print padding if needed. */
122 len += print_raw_string(suffix);
123 while (len < width) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100124 dlog_putchar(' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100125 len++;
126 }
127 return;
128 }
129
130 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +0100131 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100132 while (len < width) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100133 dlog_putchar(fill);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100134 len++;
135 }
136
137 /* Now print the rest of the string. */
138 print_raw_string(suffix);
139}
140
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100141/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100142 * Prints a number to the debug log. The caller specifies the base, its minimum
143 * width and printf-style flags.
144 */
145static void print_num(size_t v, size_t base, size_t width, int flags)
146{
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) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100188 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100189 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100190 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 */
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100232void dlog_flush_vm_buffer(spci_vm_id_t id, char buffer[], size_t length)
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100233{
234 lock();
235
236 print_raw_string("VM ");
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100237 print_num(id, 10, 0, 0);
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100238 print_raw_string(": ");
239
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100240 for (size_t i = 0; i < length; ++i) {
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100241 dlog_putchar(buffer[i]);
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100242 buffer[i] = '\0';
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100243 }
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100244 dlog_putchar('\n');
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100245
246 unlock();
247}
248
249/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000250 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000252void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100253{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100255 size_t w;
256 int flags;
257 char buf[2];
258
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100259 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000260
Andrew Scull020ae692018-07-19 16:20:14 +0100261 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100262 switch (*p) {
263 default:
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100264 dlog_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100265 break;
266
267 case '%':
268 /* Read optional flags. */
269 flags = 0;
270 p = parse_flags(p + 1, &flags) - 1;
271
272 /* Read the minimum width, if one is specified. */
273 w = 0;
274 while (p[1] >= '0' && p[1] <= '9') {
275 w = (w * 10) + (p[1] - '0');
276 p++;
277 }
278
279 /* Read minimum width from arguments. */
280 if (w == 0 && p[1] == '*') {
281 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000282
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100283 if (v >= 0) {
284 w = v;
285 } else {
286 w = -v;
287 flags |= FLAG_MINUS;
288 }
289 p++;
290 }
291
292 /* Handle the format specifier. */
293 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100294 case 's': {
295 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000296
Andrew Scull4f170f52018-07-19 12:58:20 +0100297 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100298 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100299 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100300
301 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100302 case 'i': {
303 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000304
Andrew Scull4f170f52018-07-19 12:58:20 +0100305 if (v < 0) {
306 flags |= FLAG_NEG;
307 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100308 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100309
310 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100311 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100312 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100313
314 case 'X':
315 flags |= FLAG_UPPER;
316 print_num(va_arg(args, size_t), 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000317 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100318 break;
319
320 case 'p':
321 print_num(va_arg(args, size_t), 16,
322 sizeof(size_t) * 2, FLAG_ZERO);
323 p++;
324 break;
325
326 case 'x':
327 print_num(va_arg(args, size_t), 16, w, flags);
328 p++;
329 break;
330
331 case 'u':
332 print_num(va_arg(args, size_t), 10, w, flags);
333 p++;
334 break;
335
336 case 'o':
337 print_num(va_arg(args, size_t), 8, w, flags);
338 p++;
339 break;
340
341 case 'c':
342 buf[1] = 0;
343 buf[0] = va_arg(args, int);
344 print_string(buf, buf, w, flags, ' ');
345 p++;
346 break;
347
348 case '%':
349 break;
350
351 default:
Andrew Walbranf3ac84d2019-07-26 16:10:02 +0100352 dlog_putchar('%');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100353 }
354
355 break;
356 }
357 }
358
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100359 unlock();
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100360}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100361
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100362/**
363 * Prints the given format string to the debug log.
364 */
365void dlog(const char *fmt, ...)
366{
367 va_list args;
368
369 va_start(args, fmt);
370 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100371 va_end(args);
372}