blob: 369779f4d14a8561936f0877afc65e1dd13ebc4f [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 Scull11a4a0c2018-12-29 11:38:31 +000022#include "hf/arch/console.h"
23
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;
43
44/**
45 * Enables the lock protecting the serial device.
46 */
47void dlog_enable_lock(void)
48{
49 dlog_lock_enabled = true;
50}
51
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010052/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010053 * Prints a raw string to the debug log and returns its length.
54 */
55static size_t print_raw_string(const char *str)
56{
57 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000058
Andrew Scull7364a8e2018-07-19 15:39:29 +010059 while (*c != '\0') {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010060 arch_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010061 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000062
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010063 return c - str;
64}
65
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010066/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010067 * Prints a formatted string to the debug log. The format includes a minimum
68 * width, the fill character, and flags (whether to align to left or right).
69 *
70 * str is the full string, while suffix is a pointer within str that indicates
71 * where the suffix begins. This is used when printing right-aligned numbers
72 * with a zero fill; for example, -10 with width 4 should be padded to -010,
73 * so suffix would point to index one of the "-10" string .
74 */
75static void print_string(const char *str, const char *suffix, size_t width,
76 int flags, char fill)
77{
78 size_t len = suffix - str;
79
80 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010081 while (str != suffix) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010082 arch_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010083 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010084
85 if (flags & FLAG_MINUS) {
86 /* Left-aligned. Print suffix, then print padding if needed. */
87 len += print_raw_string(suffix);
88 while (len < width) {
89 arch_putchar(' ');
90 len++;
91 }
92 return;
93 }
94
95 /* Fill until we reach the desired length. */
Andrew Scull4a867bc2019-04-08 10:15:11 +010096 len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010097 while (len < width) {
98 arch_putchar(fill);
99 len++;
100 }
101
102 /* Now print the rest of the string. */
103 print_raw_string(suffix);
104}
105
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100106/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100107 * Prints a number to the debug log. The caller specifies the base, its minimum
108 * width and printf-style flags.
109 */
110static void print_num(size_t v, size_t base, size_t width, int flags)
111{
112 static const char *digits_lower = "0123456789abcdefx";
113 static const char *digits_upper = "0123456789ABCDEFX";
114 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
Andrew Scull4a867bc2019-04-08 10:15:11 +0100115 char buf[DLOG_MAX_STRING_LENGTH];
Andrew Scullf3d45592018-09-20 14:30:22 +0100116 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100117 char *num;
118 *ptr = '\0';
119 do {
120 --ptr;
121 *ptr = d[v % base];
122 v /= base;
123 } while (v);
124
125 /* Num stores where the actual number begins. */
126 num = ptr;
127
128 /* Add prefix if requested. */
129 if (flags & FLAG_ALT) {
130 switch (base) {
131 case 16:
132 ptr -= 2;
133 ptr[0] = '0';
134 ptr[1] = d[16];
135 break;
136
137 case 8:
138 ptr--;
139 *ptr = '0';
140 break;
141 }
142 }
143
144 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100145 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100146 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100147 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100148 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100149 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100150 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100151 }
152 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100153 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100154 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100155 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100156 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100157}
158
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100159/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100160 * Parses the optional flags field of a printf-style format. It returns the spot
161 * on the string where a non-flag character was found.
162 */
163static const char *parse_flags(const char *p, int *flags)
164{
165 for (;;) {
166 switch (*p) {
167 case ' ':
168 *flags |= FLAG_SPACE;
169 break;
170
171 case '0':
172 *flags |= FLAG_ZERO;
173 break;
174
175 case '-':
176 *flags |= FLAG_MINUS;
177 break;
178
179 case '+':
180 *flags |= FLAG_PLUS;
181
182 case '#':
183 *flags |= FLAG_ALT;
184 break;
185
186 default:
187 return p;
188 }
189 p++;
190 }
191}
192
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100193/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000194 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100195 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000196void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100197{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000198 static struct spinlock sl = SPINLOCK_INIT;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100199 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100200 size_t w;
201 int flags;
202 char buf[2];
203
Andrew Scullfdd716e2018-12-20 05:37:31 +0000204 if (dlog_lock_enabled) {
205 sl_lock(&sl);
206 }
207
Andrew Scull020ae692018-07-19 16:20:14 +0100208 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100209 switch (*p) {
210 default:
211 arch_putchar(*p);
212 break;
213
214 case '%':
215 /* Read optional flags. */
216 flags = 0;
217 p = parse_flags(p + 1, &flags) - 1;
218
219 /* Read the minimum width, if one is specified. */
220 w = 0;
221 while (p[1] >= '0' && p[1] <= '9') {
222 w = (w * 10) + (p[1] - '0');
223 p++;
224 }
225
226 /* Read minimum width from arguments. */
227 if (w == 0 && p[1] == '*') {
228 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000229
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100230 if (v >= 0) {
231 w = v;
232 } else {
233 w = -v;
234 flags |= FLAG_MINUS;
235 }
236 p++;
237 }
238
239 /* Handle the format specifier. */
240 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100241 case 's': {
242 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000243
Andrew Scull4f170f52018-07-19 12:58:20 +0100244 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100245 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100246 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100247
248 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100249 case 'i': {
250 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000251
Andrew Scull4f170f52018-07-19 12:58:20 +0100252 if (v < 0) {
253 flags |= FLAG_NEG;
254 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100255 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100256
257 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100258 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100259 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100260
261 case 'X':
262 flags |= FLAG_UPPER;
263 print_num(va_arg(args, size_t), 16, w, flags);
Jose Marinho7d4cd532019-02-18 15:13:53 +0000264 p++;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100265 break;
266
267 case 'p':
268 print_num(va_arg(args, size_t), 16,
269 sizeof(size_t) * 2, FLAG_ZERO);
270 p++;
271 break;
272
273 case 'x':
274 print_num(va_arg(args, size_t), 16, w, flags);
275 p++;
276 break;
277
278 case 'u':
279 print_num(va_arg(args, size_t), 10, w, flags);
280 p++;
281 break;
282
283 case 'o':
284 print_num(va_arg(args, size_t), 8, w, flags);
285 p++;
286 break;
287
288 case 'c':
289 buf[1] = 0;
290 buf[0] = va_arg(args, int);
291 print_string(buf, buf, w, flags, ' ');
292 p++;
293 break;
294
295 case '%':
296 break;
297
298 default:
299 arch_putchar('%');
300 }
301
302 break;
303 }
304 }
305
Andrew Scullfdd716e2018-12-20 05:37:31 +0000306 if (dlog_lock_enabled) {
307 sl_unlock(&sl);
308 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100309}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100310
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100311/**
312 * Prints the given format string to the debug log.
313 */
314void dlog(const char *fmt, ...)
315{
316 va_list args;
317
318 va_start(args, fmt);
319 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100320 va_end(args);
321}