1 /*
2  * Copyright 2018 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/dlog.h"
10 
11 #include <stddef.h>
12 
13 #include "hf/spinlock.h"
14 #include "hf/std.h"
15 #include "hf/stdout.h"
16 
17 enum { DLOG_MAX_STRING_LENGTH = 64 };
18 
19 /* Keep fields aligned */
20 /* clang-format off */
21 struct format_flags {
22 	bool minus	: 1;
23 	bool plus	: 1;
24 	bool space	: 1;
25 	bool alt	: 1;
26 	bool zero	: 1;
27 	bool upper	: 1;
28 	bool neg	: 1;
29 };
30 /* clang-format on */
31 
32 enum format_base {
33 	base2 = 2,
34 	base8 = 8,
35 	base10 = 10,
36 	base16 = 16,
37 };
38 
39 enum format_length {
40 	length8 = 8,
41 	length16 = 16,
42 	length32 = 32,
43 	length64 = 64,
44 };
45 
46 static_assert(sizeof(char) == sizeof(uint8_t),
47 	      "dlog expects char to be 8 bits wide");
48 static_assert(sizeof(short) == sizeof(uint16_t),
49 	      "dlog expects short to be 16 bits wide");
50 static_assert(sizeof(int) == sizeof(uint32_t),
51 	      "dlog expects int to be 32 bits wide");
52 static_assert(sizeof(long) == sizeof(uint64_t),
53 	      "dlog expects long to be 64 bits wide");
54 static_assert(sizeof(long long) == sizeof(uint64_t),
55 	      "dlog expects long long to be 64 bits wide");
56 static_assert(sizeof(intmax_t) == sizeof(uint64_t),
57 	      "dlog expects intmax_t to be 64 bits wide");
58 static_assert(sizeof(size_t) == sizeof(uint64_t),
59 	      "dlog expects size_t to be 64 bits wide");
60 static_assert(sizeof(ptrdiff_t) == sizeof(uint64_t),
61 	      "dlog expects ptrdiff_t to be 64 bits wide");
62 
63 static bool dlog_lock_enabled = false;
64 static struct spinlock sl = SPINLOCK_INIT;
65 
66 /*
67  * These global variables for the log buffer are not static because a test needs
68  * to access them directly.
69  */
70 size_t dlog_buffer_offset;
71 char dlog_buffer[DLOG_BUFFER_SIZE];
72 
73 /**
74  * Takes the lock, if it is enabled.
75  */
lock(void)76 static void lock(void)
77 {
78 	if (dlog_lock_enabled) {
79 		sl_lock(&sl);
80 	}
81 }
82 
83 /**
84  * Releases the lock, if it is enabled.
85  */
unlock(void)86 static void unlock(void)
87 {
88 	if (dlog_lock_enabled) {
89 		sl_unlock(&sl);
90 	}
91 }
92 
93 /**
94  * Enables the lock protecting the serial device.
95  */
dlog_enable_lock(void)96 void dlog_enable_lock(void)
97 {
98 	dlog_lock_enabled = true;
99 }
100 
dlog_putchar(char c)101 static void dlog_putchar(char c)
102 {
103 	dlog_buffer[dlog_buffer_offset] = c;
104 	dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
105 	stdout_putchar(c);
106 }
107 
108 /**
109  * Prints a literal string (i.e. '%' is not interpreted specially) to the debug
110  * log.
111  *
112  * Returns number of characters written.
113  */
print_raw_string(const char * str)114 static size_t print_raw_string(const char *str)
115 {
116 	const char *c = str;
117 
118 	for (; *c != '\0'; c++) {
119 		dlog_putchar(*c);
120 	}
121 
122 	return c - str;
123 }
124 
125 /**
126  * Prints a formatted string to the debug log. The format includes a minimum
127  * width, the fill character, and flags (whether to align to left or right).
128  *
129  * str is the full string, while suffix is a pointer within str that indicates
130  * where the suffix begins. This is used when printing right-aligned numbers
131  * with a zero fill; for example, -10 with width 4 should be padded to -010,
132  * so suffix would point to index one of the "-10" string .
133  *
134  * Returns number of characters written.
135  */
print_string(const char * str,const char * suffix,size_t min_width,struct format_flags flags,char fill)136 static size_t print_string(const char *str, const char *suffix,
137 			   size_t min_width, struct format_flags flags,
138 			   char fill)
139 {
140 	size_t chars_written = 0;
141 	size_t len = suffix - str;
142 
143 	/* Print the string up to the beginning of the suffix. */
144 	while (str != suffix) {
145 		chars_written++;
146 		dlog_putchar(*str++);
147 	}
148 
149 	if (flags.minus) {
150 		/* Left-aligned. Print suffix, then print padding if needed. */
151 		len += print_raw_string(suffix);
152 		while (len < min_width) {
153 			chars_written++;
154 			dlog_putchar(' ');
155 			len++;
156 		}
157 		return chars_written;
158 	}
159 
160 	/* Fill until we reach the desired length. */
161 	len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
162 	while (len < min_width) {
163 		chars_written++;
164 		dlog_putchar(fill);
165 		len++;
166 	}
167 
168 	/* Now print the rest of the string. */
169 	chars_written += print_raw_string(suffix);
170 	return chars_written;
171 }
172 
173 /**
174  * Prints an integer to the debug log. The caller specifies the base, its
175  * minimum width and printf-style flags.
176  *
177  * Returns number of characters written.
178  */
print_int(size_t value,enum format_base base,size_t min_width,struct format_flags flags)179 static size_t print_int(size_t value, enum format_base base, size_t min_width,
180 			struct format_flags flags)
181 {
182 	static const char *digits_lower = "0123456789abcdefxb";
183 	static const char *digits_upper = "0123456789ABCDEFXB";
184 	const char *digits = flags.upper ? digits_upper : digits_lower;
185 	char buf[DLOG_MAX_STRING_LENGTH];
186 	char *ptr = &buf[sizeof(buf) - 1];
187 	char *num;
188 	*ptr = '\0';
189 	do {
190 		--ptr;
191 		*ptr = digits[value % base];
192 		value /= base;
193 	} while (value);
194 
195 	/* Num stores where the actual number begins. */
196 	num = ptr;
197 
198 	/* Add prefix if requested. */
199 	if (flags.alt) {
200 		switch (base) {
201 		case base16:
202 			ptr -= 2;
203 			ptr[0] = '0';
204 			ptr[1] = digits[16];
205 			break;
206 
207 		case base2:
208 			ptr -= 2;
209 			ptr[0] = '0';
210 			ptr[1] = digits[17];
211 			break;
212 
213 		case base8:
214 			ptr--;
215 			*ptr = '0';
216 			break;
217 
218 		case base10:
219 			/* do nothing */
220 			break;
221 		}
222 	}
223 
224 	/* Add sign if requested. */
225 	if (flags.neg) {
226 		*--ptr = '-';
227 	} else if (flags.plus) {
228 		*--ptr = '+';
229 	} else if (flags.space) {
230 		*--ptr = ' ';
231 	}
232 	return print_string(ptr, num, min_width, flags, flags.zero ? '0' : ' ');
233 }
234 
235 /**
236  * Parses the optional flags field of a printf-style format. Returns a pointer
237  * to the first non-flag character in the string.
238  */
parse_flags(const char * fmt,struct format_flags * flags)239 static const char *parse_flags(const char *fmt, struct format_flags *flags)
240 {
241 	for (;; fmt++) {
242 		switch (*fmt) {
243 		case '-':
244 			flags->minus = true;
245 			break;
246 
247 		case '+':
248 			flags->plus = true;
249 			break;
250 
251 		case ' ':
252 			flags->space = true;
253 			break;
254 
255 		case '#':
256 			flags->alt = true;
257 			break;
258 
259 		case '0':
260 			flags->zero = true;
261 			break;
262 
263 		default:
264 			return fmt;
265 		}
266 	}
267 }
268 
269 /**
270  * Parses the optional length modifier field of a printf-style format.
271  *
272  * Returns a pointer to the first non-length modifier character in the string.
273  */
parse_length_modifier(const char * fmt,enum format_length * length)274 static const char *parse_length_modifier(const char *fmt,
275 					 enum format_length *length)
276 {
277 	switch (*fmt) {
278 	case 'h':
279 		fmt++;
280 		if (*fmt == 'h') {
281 			fmt++;
282 			*length = length8;
283 		} else {
284 			*length = length16;
285 		}
286 		break;
287 	case 'l':
288 		fmt++;
289 		if (*fmt == 'l') {
290 			fmt++;
291 			*length = length64;
292 		} else {
293 			*length = length64;
294 		}
295 		break;
296 
297 	case 'j':
298 	case 'z':
299 	case 't':
300 		fmt++;
301 		*length = length64;
302 		break;
303 
304 	default:
305 		*length = length32;
306 		break;
307 	}
308 
309 	return fmt;
310 }
311 
312 /**
313  * Parses the optional minimum width field of a printf-style format.
314  * If the width is negative, `flags.minus` is set.
315  *
316  * Returns a pointer to the first non-digit character in the string.
317  */
parse_min_width(const char * fmt,struct va_list_wrapper * args,struct format_flags * flags,int * min_width)318 static const char *parse_min_width(const char *fmt,
319 				   struct va_list_wrapper *args,
320 				   struct format_flags *flags, int *min_width)
321 {
322 	int width = 0;
323 
324 	/* Read minimum width from arguments. */
325 	if (*fmt == '*') {
326 		fmt++;
327 		width = va_arg(args->va, int);
328 		if (width < 0) {
329 			width = -width;
330 			flags->minus = true;
331 		}
332 	} else {
333 		for (; *fmt >= '0' && *fmt <= '9'; fmt++) {
334 			width = (width * 10) + (*fmt - '0');
335 		}
336 	}
337 
338 	*min_width = width;
339 
340 	return fmt;
341 }
342 
343 /**
344  * Reinterpret an unsigned 64-bit integer as a potentially shorter unsigned
345  * integer according to the length modifier.
346  * Returns an unsigned integer suitable for passing to `print_int`.
347  */
reinterpret_unsigned_int(enum format_length length,uint64_t value)348 uint64_t reinterpret_unsigned_int(enum format_length length, uint64_t value)
349 {
350 	switch (length) {
351 	case length8:
352 		return (uint8_t)value;
353 	case length16:
354 		return (uint16_t)value;
355 	case length32:
356 		return (uint32_t)value;
357 	case length64:
358 		return value;
359 	}
360 }
361 
362 /**
363  * Reinterpret an unsigned 64-bit integer as a potentially shorter signed
364  * integer according to the length modifier.
365  *
366  * Returns an *unsigned* integer suitable for passing to `print_int`. If the
367  * reinterpreted value is negative, `flags.neg` is set and the absolute value is
368  * returned.
369  */
reinterpret_signed_int(enum format_length length,uint64_t value,struct format_flags * flags)370 uint64_t reinterpret_signed_int(enum format_length length, uint64_t value,
371 				struct format_flags *flags)
372 {
373 	int64_t signed_value = (int64_t)reinterpret_unsigned_int(length, value);
374 
375 	switch (length) {
376 	case length8:
377 		if ((int8_t)signed_value < 0) {
378 			flags->neg = true;
379 			signed_value = (-signed_value) & 0xFF;
380 		}
381 		break;
382 	case length16:
383 		if ((int16_t)signed_value < 0) {
384 			flags->neg = true;
385 			signed_value = (-signed_value) & 0xFFFF;
386 		}
387 		break;
388 	case length32:
389 		if ((int32_t)signed_value < 0) {
390 			flags->neg = true;
391 			signed_value = (-signed_value) & 0xFFFFFFFF;
392 		}
393 		break;
394 	case length64:
395 		if (signed_value < 0) {
396 			flags->neg = true;
397 			signed_value = -signed_value;
398 		}
399 		break;
400 	}
401 
402 	return signed_value;
403 }
404 
405 /**
406  * Same as "dlog", except that arguments are passed as a va_list
407  *
408  * Returns number of characters written, or `-1` if format string is invalid.
409  */
vdlog(const char * fmt,struct va_list_wrapper * args)410 size_t vdlog(const char *fmt, struct va_list_wrapper *args)
411 {
412 	size_t chars_written = 0;
413 
414 	lock();
415 
416 	while (*fmt != '\0') {
417 		switch (*fmt) {
418 		default:
419 			chars_written++;
420 			dlog_putchar(*fmt);
421 			fmt++;
422 			break;
423 
424 		case '%': {
425 			struct format_flags flags = {0};
426 			int min_width = 0;
427 			enum format_length length = length32;
428 			uint64_t value;
429 
430 			fmt++;
431 			fmt = parse_flags(fmt, &flags);
432 			fmt = parse_min_width(fmt, args, &flags, &min_width);
433 			fmt = parse_length_modifier(fmt, &length);
434 
435 			/* Handle the format specifier. */
436 			switch (*fmt) {
437 			case '%':
438 				fmt++;
439 				chars_written++;
440 				dlog_putchar('%');
441 				break;
442 
443 			case 'c': {
444 				char str[2] = {va_arg(args->va, int), '\0'};
445 
446 				fmt++;
447 				chars_written += print_string(
448 					str, str, min_width, flags, ' ');
449 				break;
450 			}
451 
452 			case 's': {
453 				char *str = va_arg(args->va, char *);
454 
455 				fmt++;
456 				chars_written += print_string(
457 					str, str, min_width, flags, ' ');
458 				break;
459 			}
460 
461 			case 'd':
462 			case 'i': {
463 				fmt++;
464 				value = va_arg(args->va, uint64_t);
465 				value = reinterpret_signed_int(length, value,
466 							       &flags);
467 
468 				chars_written += print_int(value, base10,
469 							   min_width, flags);
470 				break;
471 			}
472 
473 			case 'b':
474 				fmt++;
475 				value = va_arg(args->va, uint64_t);
476 				value = reinterpret_unsigned_int(length, value);
477 
478 				chars_written += print_int(value, base2,
479 							   min_width, flags);
480 				break;
481 
482 			case 'B':
483 				fmt++;
484 				flags.upper = true;
485 				value = va_arg(args->va, uint64_t);
486 				value = reinterpret_unsigned_int(length, value);
487 
488 				chars_written += print_int(value, base2,
489 							   min_width, flags);
490 				break;
491 
492 			case 'o':
493 				fmt++;
494 				value = va_arg(args->va, uint64_t);
495 				value = reinterpret_unsigned_int(length, value);
496 
497 				chars_written += print_int(value, base8,
498 							   min_width, flags);
499 				break;
500 
501 			case 'x':
502 				fmt++;
503 				value = va_arg(args->va, uint64_t);
504 				value = reinterpret_unsigned_int(length, value);
505 
506 				chars_written += print_int(value, base16,
507 							   min_width, flags);
508 				break;
509 
510 			case 'X':
511 				fmt++;
512 				flags.upper = true;
513 				value = va_arg(args->va, uint64_t);
514 				value = reinterpret_unsigned_int(length, value);
515 
516 				chars_written += print_int(value, base16,
517 							   min_width, flags);
518 				break;
519 
520 			case 'u':
521 				fmt++;
522 				value = va_arg(args->va, uint64_t);
523 				value = reinterpret_unsigned_int(length, value);
524 
525 				chars_written += print_int(value, base10,
526 							   min_width, flags);
527 				break;
528 
529 			case 'p':
530 				fmt++;
531 				value = va_arg(args->va, uint64_t);
532 				min_width = sizeof(size_t) * 2 + 2;
533 				flags.zero = true;
534 				flags.alt = true;
535 
536 				chars_written += print_int(value, base16,
537 							   min_width, flags);
538 				break;
539 
540 			default:
541 				chars_written = -1;
542 				goto out;
543 			}
544 		}
545 		}
546 	}
547 
548 out:
549 	stdout_flush();
550 	unlock();
551 	return chars_written;
552 }
553 
554 /**
555  * Prints the given format string to the debug log.
556  *
557  * The format string supported is the same as described in
558  * https://en.cppreference.com/w/c/io/fprintf, with the following exceptions:
559  * - Floating-point formatters (`%f`, `%F`, `%e`, `%E`, `%a`, `%A`, `%g`, `%G`,
560  *   `%L`) are not supported because floats are not used in Hafnium and
561  *   formatting them is too complicated.
562  * - `%n` is not supported because it is rarely used and potentially dangerous.
563  * - Precision modifiers (`%.*` and `%.` followed by an integer) are not
564  *   supported.
565  *
566  * Returns number of characters written, or `-1` if format string is invalid.
567  */
dlog(const char * fmt,...)568 size_t dlog(const char *fmt, ...)
569 {
570 	size_t chars_written = 0;
571 	struct va_list_wrapper args;
572 
573 	va_start(args.va, fmt);
574 	chars_written = vdlog(fmt, &args);
575 	va_end(args.va);
576 	return chars_written;
577 }
578