blob: 080485efd1272cb1c2fd77713ced415e0a045ad1 [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"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025
Andrew Scull4f170f52018-07-19 12:58:20 +010026/* Keep macro alignment */
27/* clang-format off */
28
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010029#define FLAG_SPACE 0x01
30#define FLAG_ZERO 0x02
31#define FLAG_MINUS 0x04
32#define FLAG_PLUS 0x08
33#define FLAG_ALT 0x10
34#define FLAG_UPPER 0x20
35#define FLAG_NEG 0x40
36
Andrew Scull4a867bc2019-04-08 10:15:11 +010037#define DLOG_MAX_STRING_LENGTH 64
38
Andrew Scull4f170f52018-07-19 12:58:20 +010039/* clang-format on */
40
Andrew Scullfdd716e2018-12-20 05:37:31 +000041static bool dlog_lock_enabled = false;
42
43/**
44 * Enables the lock protecting the serial device.
45 */
46void dlog_enable_lock(void)
47{
48 dlog_lock_enabled = true;
49}
50
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010051/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010052 * Prints a raw string to the debug log and returns its length.
53 */
54static size_t print_raw_string(const char *str)
55{
56 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000057
Andrew Scull7364a8e2018-07-19 15:39:29 +010058 while (*c != '\0') {
Andrew Walbran48699362019-05-20 14:38:00 +010059 plat_console_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010060 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000061
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010062 return c - str;
63}
64
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010065/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010066 * Prints a formatted string to the debug log. The format includes a minimum
67 * width, the fill character, and flags (whether to align to left or right).
68 *
69 * str is the full string, while suffix is a pointer within str that indicates
70 * where the suffix begins. This is used when printing right-aligned numbers
71 * with a zero fill; for example, -10 with width 4 should be padded to -010,
72 * so suffix would point to index one of the "-10" string .
73 */
74static void print_string(const char *str, const char *suffix, size_t width,
75 int flags, char fill)
76{
77 size_t len = suffix - str;
78
79 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010080 while (str != suffix) {
Andrew Walbran48699362019-05-20 14:38:00 +010081 plat_console_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010082 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010083
84 if (flags & FLAG_MINUS) {
85 /* Left-aligned. Print suffix, then print padding if needed. */
86 len += print_raw_string(suffix);
87 while (len < width) {
Andrew Walbran48699362019-05-20 14:38:00 +010088 plat_console_putchar(' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010089 len++;
90 }
91 return;
92 }
93
94 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +010095 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010096 while (len < width) {
Andrew Walbran48699362019-05-20 14:38:00 +010097 plat_console_putchar(fill);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010098 len++;
99 }
100
101 /* Now print the rest of the string. */
102 print_raw_string(suffix);
103}
104
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100105/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100106 * Prints a number to the debug log. The caller specifies the base, its minimum
107 * width and printf-style flags.
108 */
109static void print_num(size_t v, size_t base, size_t width, int flags)
110{
111 static const char *digits_lower = "0123456789abcdefx";
112 static const char *digits_upper = "0123456789ABCDEFX";
113 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
Andrew Scull4a867bc2019-04-08 10:15:11 +0100114 char buf[DLOG_MAX_STRING_LENGTH];
Andrew Scullf3d45592018-09-20 14:30:22 +0100115 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100116 char *num;
117 *ptr = '\0';
118 do {
119 --ptr;
120 *ptr = d[v % base];
121 v /= base;
122 } while (v);
123
124 /* Num stores where the actual number begins. */
125 num = ptr;
126
127 /* Add prefix if requested. */
128 if (flags & FLAG_ALT) {
129 switch (base) {
130 case 16:
131 ptr -= 2;
132 ptr[0] = '0';
133 ptr[1] = d[16];
134 break;
135
136 case 8:
137 ptr--;
138 *ptr = '0';
139 break;
140 }
141 }
142
143 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100144 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100145 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100146 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100147 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100148 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100149 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100150 }
151 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100152 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100153 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100154 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100155 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100156}
157
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100158/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100159 * Parses the optional flags field of a printf-style format. It returns the spot
160 * on the string where a non-flag character was found.
161 */
162static const char *parse_flags(const char *p, int *flags)
163{
164 for (;;) {
165 switch (*p) {
166 case ' ':
167 *flags |= FLAG_SPACE;
168 break;
169
170 case '0':
171 *flags |= FLAG_ZERO;
172 break;
173
174 case '-':
175 *flags |= FLAG_MINUS;
176 break;
177
178 case '+':
179 *flags |= FLAG_PLUS;
180
181 case '#':
182 *flags |= FLAG_ALT;
183 break;
184
185 default:
186 return p;
187 }
188 p++;
189 }
190}
191
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100192/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000193 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100194 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000195void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100196{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000197 static struct spinlock sl = SPINLOCK_INIT;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100198 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100199 size_t w;
200 int flags;
201 char buf[2];
202
Andrew Scullfdd716e2018-12-20 05:37:31 +0000203 if (dlog_lock_enabled) {
204 sl_lock(&sl);
205 }
206
Andrew Scull020ae692018-07-19 16:20:14 +0100207 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100208 switch (*p) {
209 default:
Andrew Walbran48699362019-05-20 14:38:00 +0100210 plat_console_putchar(*p);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100211 break;
212
213 case '%':
214 /* Read optional flags. */
215 flags = 0;
216 p = parse_flags(p + 1, &flags) - 1;
217
218 /* Read the minimum width, if one is specified. */
219 w = 0;
220 while (p[1] >= '0' && p[1] <= '9') {
221 w = (w * 10) + (p[1] - '0');
222 p++;
223 }
224
225 /* Read minimum width from arguments. */
226 if (w == 0 && p[1] == '*') {
227 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000228
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100229 if (v >= 0) {
230 w = v;
231 } else {
232 w = -v;
233 flags |= FLAG_MINUS;
234 }
235 p++;
236 }
237
238 /* Handle the format specifier. */
239 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100240 case 's': {
241 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000242
Andrew Scull4f170f52018-07-19 12:58:20 +0100243 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100244 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100245 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100246
247 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100248 case 'i': {
249 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000250
Andrew Scull4f170f52018-07-19 12:58:20 +0100251 if (v < 0) {
252 flags |= FLAG_NEG;
253 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100255
256 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100257 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100258 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100259
260 case 'X':
261 flags |= FLAG_UPPER;
262 print_num(va_arg(args, size_t), 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000263 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100264 break;
265
266 case 'p':
267 print_num(va_arg(args, size_t), 16,
268 sizeof(size_t) * 2, FLAG_ZERO);
269 p++;
270 break;
271
272 case 'x':
273 print_num(va_arg(args, size_t), 16, w, flags);
274 p++;
275 break;
276
277 case 'u':
278 print_num(va_arg(args, size_t), 10, w, flags);
279 p++;
280 break;
281
282 case 'o':
283 print_num(va_arg(args, size_t), 8, w, flags);
284 p++;
285 break;
286
287 case 'c':
288 buf[1] = 0;
289 buf[0] = va_arg(args, int);
290 print_string(buf, buf, w, flags, ' ');
291 p++;
292 break;
293
294 case '%':
295 break;
296
297 default:
Andrew Walbran48699362019-05-20 14:38:00 +0100298 plat_console_putchar('%');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100299 }
300
301 break;
302 }
303 }
304
Andrew Scullfdd716e2018-12-20 05:37:31 +0000305 if (dlog_lock_enabled) {
306 sl_unlock(&sl);
307 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100308}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100309
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100310/**
311 * Prints the given format string to the debug log.
312 */
313void dlog(const char *fmt, ...)
314{
315 va_list args;
316
317 va_start(args, fmt);
318 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100319 va_end(args);
320}