blob: 453a597d7916b53e75b8f2a969395ebd27eeb2db [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 Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/spinlock.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010024#include "hf/std.h"
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +010025#include "hf/vm.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 */
218void dlog_flush_vm_buffer(struct vm_locked vm)
219{
220 lock();
221
222 print_raw_string("VM ");
223 print_num(vm.vm->id, 10, 0, 0);
224 print_raw_string(": ");
225
226 for (size_t i = 0; i < vm.vm->log_buffer_length; ++i) {
227 plat_console_putchar(vm.vm->log_buffer[i]);
228 vm.vm->log_buffer[i] = '\0';
229 }
230 vm.vm->log_buffer_length = 0;
231 plat_console_putchar('\n');
232
233 unlock();
234}
235
236/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000237 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100238 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000239void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100241 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100242 size_t w;
243 int flags;
244 char buf[2];
245
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100246 lock();
Andrew Scullfdd716e2018-12-20 05:37:31 +0000247
Andrew Scull020ae692018-07-19 16:20:14 +0100248 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100249 switch (*p) {
250 default:
Andrew Walbran48699362019-05-20 14:38:00 +0100251 plat_console_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100252 break;
253
254 case '%':
255 /* Read optional flags. */
256 flags = 0;
257 p = parse_flags(p + 1, &flags) - 1;
258
259 /* Read the minimum width, if one is specified. */
260 w = 0;
261 while (p[1] >= '0' && p[1] <= '9') {
262 w = (w * 10) + (p[1] - '0');
263 p++;
264 }
265
266 /* Read minimum width from arguments. */
267 if (w == 0 && p[1] == '*') {
268 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000269
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100270 if (v >= 0) {
271 w = v;
272 } else {
273 w = -v;
274 flags |= FLAG_MINUS;
275 }
276 p++;
277 }
278
279 /* Handle the format specifier. */
280 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100281 case 's': {
282 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000283
Andrew Scull4f170f52018-07-19 12:58:20 +0100284 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100285 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100286 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100287
288 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100289 case 'i': {
290 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000291
Andrew Scull4f170f52018-07-19 12:58:20 +0100292 if (v < 0) {
293 flags |= FLAG_NEG;
294 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100295 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100296
297 print_num((size_t)v, 10, 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 'X':
302 flags |= FLAG_UPPER;
303 print_num(va_arg(args, size_t), 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000304 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100305 break;
306
307 case 'p':
308 print_num(va_arg(args, size_t), 16,
309 sizeof(size_t) * 2, FLAG_ZERO);
310 p++;
311 break;
312
313 case 'x':
314 print_num(va_arg(args, size_t), 16, w, flags);
315 p++;
316 break;
317
318 case 'u':
319 print_num(va_arg(args, size_t), 10, w, flags);
320 p++;
321 break;
322
323 case 'o':
324 print_num(va_arg(args, size_t), 8, w, flags);
325 p++;
326 break;
327
328 case 'c':
329 buf[1] = 0;
330 buf[0] = va_arg(args, int);
331 print_string(buf, buf, w, flags, ' ');
332 p++;
333 break;
334
335 case '%':
336 break;
337
338 default:
Andrew Walbran48699362019-05-20 14:38:00 +0100339 plat_console_putchar('%');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100340 }
341
342 break;
343 }
344 }
345
Andrew Walbranc1ad4ce2019-05-09 11:41:39 +0100346 unlock();
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100347}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100348
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100349/**
350 * Prints the given format string to the debug log.
351 */
352void dlog(const char *fmt, ...)
353{
354 va_list args;
355
356 va_start(args, fmt);
357 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100358 va_end(args);
359}