blob: 1b0a79336f354b8d80736294bc41b6ed55b0d138 [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
45/**
46 * Takes the lock, if it is enabled.
47 */
48static void lock(void)
49{
50 if (dlog_lock_enabled) {
51 sl_lock(&sl);
52 }
53}
54
55/**
56 * Releases the lock, if it is enabled.
57 */
58static void unlock(void)
59{
60 if (dlog_lock_enabled) {
61 sl_unlock(&sl);
62 }
63}
Andrew Scullfdd716e2018-12-20 05:37:31 +000064
65/**
66 * Enables the lock protecting the serial device.
67 */
68void dlog_enable_lock(void)
69{
70 dlog_lock_enabled = true;
71}
72
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010073/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010074 * Prints a raw string to the debug log and returns its length.
75 */
76static size_t print_raw_string(const char *str)
77{
78 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000079
Andrew Scull7364a8e2018-07-19 15:39:29 +010080 while (*c != '\0') {
Andrew Walbran48699362019-05-20 14:38:00 +010081 plat_console_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010082 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000083
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010084 return c - str;
85}
86
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010087/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010088 * Prints a formatted string to the debug log. The format includes a minimum
89 * width, the fill character, and flags (whether to align to left or right).
90 *
91 * str is the full string, while suffix is a pointer within str that indicates
92 * where the suffix begins. This is used when printing right-aligned numbers
93 * with a zero fill; for example, -10 with width 4 should be padded to -010,
94 * so suffix would point to index one of the "-10" string .
95 */
96static void print_string(const char *str, const char *suffix, size_t width,
97 int flags, char fill)
98{
99 size_t len = suffix - str;
100
101 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100102 while (str != suffix) {
Andrew Walbran48699362019-05-20 14:38:00 +0100103 plat_console_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100104 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100105
106 if (flags & FLAG_MINUS) {
107 /* Left-aligned. Print suffix, then print padding if needed. */
108 len += print_raw_string(suffix);
109 while (len < width) {
Andrew Walbran48699362019-05-20 14:38:00 +0100110 plat_console_putchar(' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100111 len++;
112 }
113 return;
114 }
115
116 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +0100117 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100118 while (len < width) {
Andrew Walbran48699362019-05-20 14:38:00 +0100119 plat_console_putchar(fill);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100120 len++;
121 }
122
123 /* Now print the rest of the string. */
124 print_raw_string(suffix);
125}
126
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100127/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100128 * Prints a number to the debug log. The caller specifies the base, its minimum
129 * width and printf-style flags.
130 */
131static void print_num(size_t v, size_t base, size_t width, int flags)
132{
133 static const char *digits_lower = "0123456789abcdefx";
134 static const char *digits_upper = "0123456789ABCDEFX";
135 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
Andrew Scull4a867bc2019-04-08 10:15:11 +0100136 char buf[DLOG_MAX_STRING_LENGTH];
Andrew Scullf3d45592018-09-20 14:30:22 +0100137 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100138 char *num;
139 *ptr = '\0';
140 do {
141 --ptr;
142 *ptr = d[v % base];
143 v /= base;
144 } while (v);
145
146 /* Num stores where the actual number begins. */
147 num = ptr;
148
149 /* Add prefix if requested. */
150 if (flags & FLAG_ALT) {
151 switch (base) {
152 case 16:
153 ptr -= 2;
154 ptr[0] = '0';
155 ptr[1] = d[16];
156 break;
157
158 case 8:
159 ptr--;
160 *ptr = '0';
161 break;
162 }
163 }
164
165 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100166 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100167 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100168 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100169 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100170 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100171 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100172 }
173 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100174 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100175 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100176 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100177 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100178}
179
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100180/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100181 * Parses the optional flags field of a printf-style format. It returns the spot
182 * on the string where a non-flag character was found.
183 */
184static const char *parse_flags(const char *p, int *flags)
185{
186 for (;;) {
187 switch (*p) {
188 case ' ':
189 *flags |= FLAG_SPACE;
190 break;
191
192 case '0':
193 *flags |= FLAG_ZERO;
194 break;
195
196 case '-':
197 *flags |= FLAG_MINUS;
198 break;
199
200 case '+':
201 *flags |= FLAG_PLUS;
202
203 case '#':
204 *flags |= FLAG_ALT;
205 break;
206
207 default:
208 return p;
209 }
210 p++;
211 }
212}
213
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100214/**
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100215 * Send the contents of the given VM's log buffer to the log, preceded by the VM
216 * ID and followed by a newline.
217 */
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100218void dlog_flush_vm_buffer(spci_vm_id_t id, char buffer[], size_t length)
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100219{
220 lock();
221
222 print_raw_string("VM ");
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100223 print_num(id, 10, 0, 0);
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100224 print_raw_string(": ");
225
Andrew Walbran7f904bf2019-07-12 16:38:38 +0100226 for (size_t i = 0; i < length; ++i) {
227 plat_console_putchar(buffer[i]);
228 buffer[i] = '\0';
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100229 }
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100230 plat_console_putchar('\n');
231
232 unlock();
233}
234
235/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000236 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100237 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000238void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100239{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100241 size_t w;
242 int flags;
243 char buf[2];
244
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100245 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000246
Andrew Scull020ae692018-07-19 16:20:14 +0100247 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100248 switch (*p) {
249 default:
Andrew Walbran48699362019-05-20 14:38:00 +0100250 plat_console_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251 break;
252
253 case '%':
254 /* Read optional flags. */
255 flags = 0;
256 p = parse_flags(p + 1, &flags) - 1;
257
258 /* Read the minimum width, if one is specified. */
259 w = 0;
260 while (p[1] >= '0' && p[1] <= '9') {
261 w = (w * 10) + (p[1] - '0');
262 p++;
263 }
264
265 /* Read minimum width from arguments. */
266 if (w == 0 && p[1] == '*') {
267 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000268
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100269 if (v >= 0) {
270 w = v;
271 } else {
272 w = -v;
273 flags |= FLAG_MINUS;
274 }
275 p++;
276 }
277
278 /* Handle the format specifier. */
279 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100280 case 's': {
281 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000282
Andrew Scull4f170f52018-07-19 12:58:20 +0100283 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100284 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100285 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100286
287 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100288 case 'i': {
289 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000290
Andrew Scull4f170f52018-07-19 12:58:20 +0100291 if (v < 0) {
292 flags |= FLAG_NEG;
293 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100294 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100295
296 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100297 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100298 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100299
300 case 'X':
301 flags |= FLAG_UPPER;
302 print_num(va_arg(args, size_t), 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':
307 print_num(va_arg(args, size_t), 16,
308 sizeof(size_t) * 2, FLAG_ZERO);
309 p++;
310 break;
311
312 case 'x':
313 print_num(va_arg(args, size_t), 16, w, flags);
314 p++;
315 break;
316
317 case 'u':
318 print_num(va_arg(args, size_t), 10, w, flags);
319 p++;
320 break;
321
322 case 'o':
323 print_num(va_arg(args, size_t), 8, w, flags);
324 p++;
325 break;
326
327 case 'c':
328 buf[1] = 0;
329 buf[0] = va_arg(args, int);
330 print_string(buf, buf, w, flags, ' ');
331 p++;
332 break;
333
334 case '%':
335 break;
336
337 default:
Andrew Walbran48699362019-05-20 14:38:00 +0100338 plat_console_putchar('%');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100339 }
340
341 break;
342 }
343 }
344
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100345 unlock();
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100346}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100347
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100348/**
349 * Prints the given format string to the debug log.
350 */
351void dlog(const char *fmt, ...)
352{
353 va_list args;
354
355 va_start(args, fmt);
356 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100357 va_end(args);
358}