blob: a0c1d15027e273842404a95a4805367355266214 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
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"
Andrew Walbran4a53ba62019-03-05 17:26:12 +000023#include "hf/arch/std.h"
Andrew Scull11a4a0c2018-12-29 11:38:31 +000024
Andrew Scull18c78fc2018-08-20 12:57:41 +010025#include "hf/spinlock.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 Scull4f170f52018-07-19 12:58:20 +010038/* clang-format on */
39
Andrew Scullfdd716e2018-12-20 05:37:31 +000040static bool dlog_lock_enabled = false;
41
42/**
43 * Enables the lock protecting the serial device.
44 */
45void dlog_enable_lock(void)
46{
47 dlog_lock_enabled = true;
48}
49
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010050/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010051 * Prints a raw string to the debug log and returns its length.
52 */
53static size_t print_raw_string(const char *str)
54{
55 const char *c = str;
Andrew Scullcbefbdb2019-01-11 16:36:26 +000056
Andrew Scull7364a8e2018-07-19 15:39:29 +010057 while (*c != '\0') {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010058 arch_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010059 }
Andrew Scullcbefbdb2019-01-11 16:36:26 +000060
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010061 return c - str;
62}
63
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010064/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010065 * Prints a formatted string to the debug log. The format includes a minimum
66 * width, the fill character, and flags (whether to align to left or right).
67 *
68 * str is the full string, while suffix is a pointer within str that indicates
69 * where the suffix begins. This is used when printing right-aligned numbers
70 * with a zero fill; for example, -10 with width 4 should be padded to -010,
71 * so suffix would point to index one of the "-10" string .
72 */
73static void print_string(const char *str, const char *suffix, size_t width,
74 int flags, char fill)
75{
76 size_t len = suffix - str;
77
78 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010079 while (str != suffix) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010080 arch_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010081 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010082
83 if (flags & FLAG_MINUS) {
84 /* Left-aligned. Print suffix, then print padding if needed. */
85 len += print_raw_string(suffix);
86 while (len < width) {
87 arch_putchar(' ');
88 len++;
89 }
90 return;
91 }
92
93 /* Fill until we reach the desired length. */
94 len += strlen(suffix);
95 while (len < width) {
96 arch_putchar(fill);
97 len++;
98 }
99
100 /* Now print the rest of the string. */
101 print_raw_string(suffix);
102}
103
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100104/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100105 * Prints a number to the debug log. The caller specifies the base, its minimum
106 * width and printf-style flags.
107 */
108static void print_num(size_t v, size_t base, size_t width, int flags)
109{
110 static const char *digits_lower = "0123456789abcdefx";
111 static const char *digits_upper = "0123456789ABCDEFX";
112 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
113 char buf[51];
Andrew Scullf3d45592018-09-20 14:30:22 +0100114 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100115 char *num;
116 *ptr = '\0';
117 do {
118 --ptr;
119 *ptr = d[v % base];
120 v /= base;
121 } while (v);
122
123 /* Num stores where the actual number begins. */
124 num = ptr;
125
126 /* Add prefix if requested. */
127 if (flags & FLAG_ALT) {
128 switch (base) {
129 case 16:
130 ptr -= 2;
131 ptr[0] = '0';
132 ptr[1] = d[16];
133 break;
134
135 case 8:
136 ptr--;
137 *ptr = '0';
138 break;
139 }
140 }
141
142 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100143 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100144 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100145 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100146 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100147 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100148 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100149 }
150 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100151 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100152 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100153 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100154 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100155}
156
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100157/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100158 * Parses the optional flags field of a printf-style format. It returns the spot
159 * on the string where a non-flag character was found.
160 */
161static const char *parse_flags(const char *p, int *flags)
162{
163 for (;;) {
164 switch (*p) {
165 case ' ':
166 *flags |= FLAG_SPACE;
167 break;
168
169 case '0':
170 *flags |= FLAG_ZERO;
171 break;
172
173 case '-':
174 *flags |= FLAG_MINUS;
175 break;
176
177 case '+':
178 *flags |= FLAG_PLUS;
179
180 case '#':
181 *flags |= FLAG_ALT;
182 break;
183
184 default:
185 return p;
186 }
187 p++;
188 }
189}
190
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100191/**
Andrew Scullfdd716e2018-12-20 05:37:31 +0000192 * Same as "dlog", except that arguments are passed as a va_list
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100193 */
Andrew Scullfdd716e2018-12-20 05:37:31 +0000194void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100195{
Andrew Scullfdd716e2018-12-20 05:37:31 +0000196 static struct spinlock sl = SPINLOCK_INIT;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100197 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100198 size_t w;
199 int flags;
200 char buf[2];
201
Andrew Scullfdd716e2018-12-20 05:37:31 +0000202 if (dlog_lock_enabled) {
203 sl_lock(&sl);
204 }
205
Andrew Scull020ae692018-07-19 16:20:14 +0100206 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100207 switch (*p) {
208 default:
209 arch_putchar(*p);
210 break;
211
212 case '%':
213 /* Read optional flags. */
214 flags = 0;
215 p = parse_flags(p + 1, &flags) - 1;
216
217 /* Read the minimum width, if one is specified. */
218 w = 0;
219 while (p[1] >= '0' && p[1] <= '9') {
220 w = (w * 10) + (p[1] - '0');
221 p++;
222 }
223
224 /* Read minimum width from arguments. */
225 if (w == 0 && p[1] == '*') {
226 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000227
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100228 if (v >= 0) {
229 w = v;
230 } else {
231 w = -v;
232 flags |= FLAG_MINUS;
233 }
234 p++;
235 }
236
237 /* Handle the format specifier. */
238 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100239 case 's': {
240 char *str = va_arg(args, char *);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000241
Andrew Scull4f170f52018-07-19 12:58:20 +0100242 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100243 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100244 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100245
246 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100247 case 'i': {
248 int v = va_arg(args, int);
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000249
Andrew Scull4f170f52018-07-19 12:58:20 +0100250 if (v < 0) {
251 flags |= FLAG_NEG;
252 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100253 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100254
255 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100256 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100257 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100258
259 case 'X':
260 flags |= FLAG_UPPER;
261 print_num(va_arg(args, size_t), 16, w, flags);
262 break;
263
264 case 'p':
265 print_num(va_arg(args, size_t), 16,
266 sizeof(size_t) * 2, FLAG_ZERO);
267 p++;
268 break;
269
270 case 'x':
271 print_num(va_arg(args, size_t), 16, w, flags);
272 p++;
273 break;
274
275 case 'u':
276 print_num(va_arg(args, size_t), 10, w, flags);
277 p++;
278 break;
279
280 case 'o':
281 print_num(va_arg(args, size_t), 8, w, flags);
282 p++;
283 break;
284
285 case 'c':
286 buf[1] = 0;
287 buf[0] = va_arg(args, int);
288 print_string(buf, buf, w, flags, ' ');
289 p++;
290 break;
291
292 case '%':
293 break;
294
295 default:
296 arch_putchar('%');
297 }
298
299 break;
300 }
301 }
302
Andrew Scullfdd716e2018-12-20 05:37:31 +0000303 if (dlog_lock_enabled) {
304 sl_unlock(&sl);
305 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100306}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100307
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100308/**
309 * Prints the given format string to the debug log.
310 */
311void dlog(const char *fmt, ...)
312{
313 va_list args;
314
315 va_start(args, fmt);
316 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100317 va_end(args);
318}