Software APIs
print.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include <stdarg.h>
8#include <stdbool.h>
9#include <stddef.h>
10#include <stdint.h>
11
14#include "sw/device/lib/base/status.h"
16
17// This is declared as an enum to force the values to be
18// compile-time constants, but the type is otherwise not
19// used for anything.
20enum {
21 // Standard format specifiers.
22 kPercent = '%',
23 kCharacter = 'c',
24 kString = 's',
25 kSignedDec1 = 'd',
26 kSignedDec2 = 'i',
27 kUnsignedOct = 'o',
28 kUnsignedHexLow = 'x',
29 kUnsignedHexHigh = 'X',
30 kUnsignedDec = 'u',
31 kPointer = 'p',
32
33 // Verilog-style format specifiers.
34 kSvBinary = 'b',
35 kSvHexLow = 'h',
36 kSvHexHigh = 'H',
37
38 // Other non-standard specifiers.
39 kHexLeLow = 'y',
40 kHexLeHigh = 'Y',
41 kStatusResult = 'r',
42 kFourCC = 'C',
43};
44
45// NOTE: all of the lengths of the strings below are given so that the NUL
46// terminator is left off; that way, `sizeof(kConst)` does not include it.
47OT_NONSTRING static const char kDigitsLow[16] = "0123456789abcdef";
48OT_NONSTRING static const char kDigitsHigh[16] = "0123456789ABCDEF";
49
50OT_NONSTRING static const char kErrorNul[17] = "%<unexpected nul>";
51OT_NONSTRING static const char kUnknownSpec[15] = "%<unknown spec>";
52OT_NONSTRING static const char kErrorTooWide[12] = "%<bad width>";
53
54static size_t base_dev_null(void *data, const char *buf, size_t len) {
55 return len;
56}
57static buffer_sink_t base_stdout = {
58 .data = NULL,
59 // Note: Using `&base_dev_null` causes this variable to be placed in the
60 // .data section and triggers the assertion in rom.ld.
61 .sink = NULL,
62};
63
64void base_set_stdout(buffer_sink_t out) {
65 if (out.sink == NULL) {
66 out.sink = &base_dev_null;
67 }
68 base_stdout = out;
69}
70
71static size_t base_dev_uart(void *data, const char *buf, size_t len) {
72 const dif_uart_t *uart = (const dif_uart_t *)data;
73 for (size_t i = 0; i < len; ++i) {
74 if (dif_uart_byte_send_polled(uart, (uint8_t)buf[i]) != kDifOk) {
75 return i;
76 }
77 }
78 return len;
79}
80
81void base_uart_stdout(const dif_uart_t *uart) {
82 base_set_stdout(
83 (buffer_sink_t){.data = (void *)uart, .sink = &base_dev_uart});
84}
85
86size_t base_printf(const char *format, ...) {
87 va_list args;
88 va_start(args, format);
89 size_t bytes_left = base_vprintf(format, args);
90 va_end(args);
91 return bytes_left;
92}
93
94size_t base_vprintf(const char *format, va_list args) {
95 return base_vfprintf(base_stdout, format, args);
96}
97
98typedef struct snprintf_captures_t {
99 char *buf;
100 size_t bytes_left;
102
103static size_t snprintf_sink(void *data, const char *buf, size_t len) {
104 snprintf_captures_t *captures = (snprintf_captures_t *)data;
105 if (captures->bytes_left == 0) {
106 return 0;
107 }
108
109 if (len > captures->bytes_left) {
110 len = captures->bytes_left;
111 }
112 memcpy(captures->buf, buf, len);
113 captures->buf += len;
114 captures->bytes_left -= len;
115 return len;
116}
117
118size_t base_snprintf(char *buf, size_t len, const char *format, ...) {
119 va_list args;
120 va_start(args, format);
121
122 snprintf_captures_t data = {
123 .buf = buf,
124 .bytes_left = len,
125 };
126 buffer_sink_t out = {
127 .data = &data,
128 .sink = &snprintf_sink,
129 };
130 size_t bytes_left = base_vfprintf(out, format, args);
131 va_end(args);
132 return bytes_left;
133}
134
135size_t base_fprintf(buffer_sink_t out, const char *format, ...) {
136 va_list args;
137 va_start(args, format);
138 size_t bytes_left = base_vfprintf(out, format, args);
139 va_end(args);
140 return bytes_left;
141}
142
143/**
144 * Consumes characters from `format` until a '%' or NUL is reached. All
145 * characters seen before that are then sinked into `out`.
146 *
147 * @param out the sink to write bytes to.
148 * @param format a pointer to the format string to consume a prefix of.
149 * @param[out] bytes_written out param for the number of bytes writen to `out`.
150 * @return true if an unprocessed '%' was found.
151 */
152static bool consume_until_percent(buffer_sink_t out, const char **format,
153 size_t *bytes_written) {
154 size_t text_len = 0;
155 while (true) {
156 char c = (*format)[text_len];
157 if (c == '\0' || c == kPercent) {
158 if (text_len > 0) {
159 *bytes_written += out.sink(out.data, *format, text_len);
160 }
161 *format += text_len;
162 return c != '\0';
163 }
164 ++text_len;
165 }
166}
167
168/**
169 * Represents a parsed format specifier.
170 */
171typedef struct format_specifier {
172 char type;
173 size_t width;
174 char padding;
175 bool is_nonstd;
176} format_specifier_t;
177
178/**
179 * Consumes characters from `format` until a complete format specifier is
180 * parsed. See the documentation in `print.h` for full syntax.
181 *
182 * @param out the sink to write bytes to.
183 * @param format a pointer to the format string to consume a prefix of.
184 * @param[out] spec out param for the specifier.
185 * @return whether the parse succeeded.
186 */
187static bool consume_format_specifier(buffer_sink_t out, const char **format,
188 size_t *bytes_written,
189 format_specifier_t *spec) {
190 *spec = (format_specifier_t){0};
191
192 // Consume the percent sign.
193 ++(*format);
194
195 // Parse a ! to detect an OpenTitan-specific extension (that isn't one
196 // of the Verilog ones...).
197 if ((*format)[0] == '!') {
198 spec->is_nonstd = true;
199 ++(*format);
200 }
201
202 // Attempt to parse out an unsigned, decimal number, a "width",
203 // after the percent sign; the format specifier is the character
204 // immediately after this width.
205 //
206 // `spec->padding` is used to indicate whether we've seen a width;
207 // if nonzero, we have an actual width.
208 size_t spec_len = 0;
209 while (true) {
210 char c = (*format)[spec_len];
211 if (c == '\0') {
212 *bytes_written += out.sink(out.data, kErrorNul, sizeof(kErrorNul));
213 return false;
214 }
215 if (c < '0' || c > '9') {
216 break;
217 }
218 if (spec->padding == 0) {
219 if (c == '0') {
220 spec->padding = '0';
221 ++spec_len;
222 continue;
223 }
224 spec->padding = ' ';
225 }
226 spec->width *= 10;
227 spec->width += (c - '0');
228 ++spec_len;
229 }
230
231 if ((spec->width == 0 && spec->padding != 0) || spec->width > 32) {
232 *bytes_written += out.sink(out.data, kErrorTooWide, sizeof(kErrorTooWide));
233 return false;
234 }
235
236 spec->type = (*format)[spec_len];
237 *format += spec_len + 1;
238 return true;
239}
240
241/**
242 * Write the digits of `value` onto `out`.
243 *
244 * @param out the sink to write bytes to.
245 * @param value the value to "stringify".
246 * @param width the minimum width to print; going below will result in writing
247 * out zeroes.
248 * @param base the base to express `value` in.
249 * @param glyphs an array of characters to use as the digits of a number, which
250 * should be at least ast long as `base`.
251 * @return the number of bytes written.
252 */
253static size_t write_digits(buffer_sink_t out, uint32_t value, uint32_t width,
254 char padding, uint32_t base, const char *glyphs) {
255 // All allocations are done relative to a buffer that could hold the longest
256 // textual representation of a number: ~0x0 in base 2, i.e., 32 ones.
257 static const int kWordBits = sizeof(uint32_t) * 8;
258 char buffer[kWordBits];
259
260 size_t len = 0;
261 if (value == 0) {
262 buffer[kWordBits - 1] = glyphs[0];
263 ++len;
264 }
265 while (value > 0) {
266 uint32_t digit = value % base;
267 value /= base;
268 buffer[kWordBits - 1 - len] = glyphs[digit];
269 ++len;
270 }
271 width = width == 0 ? 1 : width;
272 width = width > kWordBits ? kWordBits : width;
273 while (len < width) {
274 buffer[kWordBits - len - 1] = padding;
275 ++len;
276 }
277 return out.sink(out.data, buffer + (kWordBits - len), len);
278}
279
280static size_t write_status(buffer_sink_t out, status_t value, bool as_json) {
281 // The module id is defined to be 3 chars long.
282 char mod[] = {0, 0, 0};
283 int32_t arg;
284 const char *start;
285 bool err = status_extract(value, &start, &arg, mod);
286
287 // strlen of the status code.
288 const char *end = start;
289 while (*end)
290 end++;
291 size_t len = 0;
292
293 len += out.sink(out.data, "{\"", as_json ? 2 : 0);
294 len += out.sink(out.data, start, (size_t)(end - start));
295 len += out.sink(out.data, "\"", as_json ? 1 : 0);
296
297 len += out.sink(out.data, ":", 1);
298 if (err) {
299 // All error codes include the module identifier.
300 len += out.sink(out.data, "[\"", 2);
301 for (size_t i = 0; i < 3; i++) {
302 if (mod[i] == '\\') {
303 len += out.sink(out.data, "\\\\", 2);
304 } else {
305 len += out.sink(out.data, &mod[i], 1);
306 }
307 }
308 len += out.sink(out.data, "\",", 2);
309 len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
310 len += out.sink(out.data, "]", 1);
311 } else {
312 // Ok codes include only the arg
313 len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
314 }
315 len += out.sink(out.data, "}", as_json ? 1 : 0);
316 return len;
317}
318
319/**
320 * Hexdumps `bytes` onto `out`.
321 *
322 * @param out the sink to write bytes to.
323 * @param bytes the bytes to dump.
324 * @param len the number of bytes to dump.
325 * @param width the minimum width to print; going below will result in writing
326 * out zeroes.
327 * @param padding the character to use for padding.
328 * @param big_endian whether to print in big-endian order (i.e. as %x would).
329 * @param glyphs an array of characters to use as the digits of a number, which
330 * should be at least ast long as `base`.
331 * @return the number of bytes written.
332 */
333static size_t hex_dump(buffer_sink_t out, const char *bytes, size_t len,
334 uint32_t width, char padding, bool big_endian,
335 const char *glyphs) {
336 size_t bytes_written = 0;
337 char buf[32];
338 size_t buffered = 0;
339 if (len < width) {
340 width -= len;
341 memset(buf, padding, sizeof(buf));
342 while (width > 0) {
343 size_t to_write = width > ARRAYSIZE(buf) ? 32 : width;
344 bytes_written += out.sink(out.data, buf, to_write);
345 width -= to_write;
346 }
347 }
348
349 for (size_t i = 0; i < len; ++i) {
350 size_t idx = big_endian ? len - i - 1 : i;
351 buf[buffered] = glyphs[(bytes[idx] >> 4) & 0xf];
352 buf[buffered + 1] = glyphs[bytes[idx] & 0xf];
353 buffered += 2;
354
355 if (buffered == ARRAYSIZE(buf)) {
356 bytes_written += out.sink(out.data, buf, buffered);
357 buffered = 0;
358 }
359 }
360
361 if (buffered != 0) {
362 bytes_written += out.sink(out.data, buf, buffered);
363 }
364 return bytes_written;
365}
366
367/**
368 * Prints out the next entry in `args` according to `spec`.
369 *
370 * This function assumes that `spec` accurately describes the next entry in
371 * `args`.
372 *
373 * @param out the sink to write bytes to.
374 * @param spec the specifier to use for stringifying.
375 * @param[out] bytes_written out param for the number of bytes writen to `out`.
376 * @param args the list to pull an entry from.
377 */
378static void process_specifier(buffer_sink_t out, format_specifier_t spec,
379 size_t *bytes_written, va_list *args) {
380 // Switch on the specifier. At this point, we assert that there is
381 // an initialized value of correct type in the VA list; if it is
382 // missing, the caller has caused UB.
383 switch (spec.type) {
384 case kPercent: {
385 if (spec.is_nonstd) {
386 goto bad_spec;
387 }
388 *bytes_written += out.sink(out.data, "%", 1);
389 break;
390 }
391 case kCharacter: {
392 if (spec.is_nonstd) {
393 goto bad_spec;
394 }
395 char value = (char)va_arg(*args, uint32_t);
396 *bytes_written += out.sink(out.data, &value, 1);
397 break;
398 }
399 case kFourCC: {
400 uint32_t value = va_arg(*args, uint32_t);
401 for (size_t i = 0; i < sizeof(uint32_t); ++i, value >>= 8) {
402 uint8_t ch = (uint8_t)value;
403 if (ch >= 32 && ch < 127) {
404 *bytes_written += out.sink(out.data, (const char *)&ch, 1);
405 } else {
406 *bytes_written += out.sink(out.data, "\\x", 2);
407 *bytes_written += out.sink(out.data, &kDigitsLow[ch >> 4], 1);
408 *bytes_written += out.sink(out.data, &kDigitsLow[ch & 15], 1);
409 }
410 }
411 break;
412 }
413 case kString: {
414 size_t len = 0;
415 if (spec.is_nonstd) {
416 // This implements %!s.
417 len = va_arg(*args, size_t);
418 }
419
420 char *value = va_arg(*args, char *);
421 while (!spec.is_nonstd && value[len] != '\0') {
422 // This implements %s.
423 ++len;
424 }
425
426 *bytes_written += out.sink(out.data, value, len);
427 break;
428 }
429 case kSignedDec1:
430 case kSignedDec2: {
431 if (spec.is_nonstd) {
432 goto bad_spec;
433 }
434 uint32_t value = va_arg(*args, uint32_t);
435 if (((int32_t)value) < 0) {
436 *bytes_written += out.sink(out.data, "-", 1);
437 value = -value;
438 }
439 *bytes_written +=
440 write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
441 break;
442 }
443 case kUnsignedOct: {
444 if (spec.is_nonstd) {
445 goto bad_spec;
446 }
447 uint32_t value = va_arg(*args, uint32_t);
448 *bytes_written +=
449 write_digits(out, value, spec.width, spec.padding, 8, kDigitsLow);
450 break;
451 }
452 case kPointer: {
453 if (spec.is_nonstd) {
454 goto bad_spec;
455 }
456 // Pointers are formatted as 0x<hex digits>, where the width is always
457 // set to the number necessary to represent a pointer on the current
458 // platform, that is, the size of uintptr_t in nybbles. For example, on
459 // different architecutres the null pointer prints as
460 // - rv32imc: 0x00000000 (four bytes, eight nybbles).
461 // - amd64: 0x0000000000000000 (eight bytes, sixteen nybbles).
462 *bytes_written += out.sink(out.data, "0x", 2);
463 uintptr_t value = va_arg(*args, uintptr_t);
464 *bytes_written +=
465 write_digits(out, value, sizeof(uintptr_t) * 2, '0', 16, kDigitsLow);
466 break;
467 }
468 case kUnsignedHexLow:
469 if (spec.is_nonstd) {
470 size_t len = va_arg(*args, size_t);
471 char *value = va_arg(*args, char *);
472 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
473 /*big_endian=*/true, kDigitsLow);
474 break;
475 }
477 case kSvHexLow: {
478 uint32_t value = va_arg(*args, uint32_t);
479 *bytes_written +=
480 write_digits(out, value, spec.width, spec.padding, 16, kDigitsLow);
481 break;
482 }
483 case kUnsignedHexHigh:
484 if (spec.is_nonstd) {
485 size_t len = va_arg(*args, size_t);
486 char *value = va_arg(*args, char *);
487 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
488 /*big_endian=*/true, kDigitsHigh);
489 break;
490 }
492 case kSvHexHigh: {
493 uint32_t value = va_arg(*args, uint32_t);
494 *bytes_written +=
495 write_digits(out, value, spec.width, spec.padding, 16, kDigitsHigh);
496 break;
497 }
498 case kHexLeLow: {
499 if (!spec.is_nonstd) {
500 goto bad_spec;
501 }
502 size_t len = va_arg(*args, size_t);
503 char *value = va_arg(*args, char *);
504 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
505 /*big_endian=*/false, kDigitsLow);
506 break;
507 }
508 case kHexLeHigh: {
509 if (!spec.is_nonstd) {
510 goto bad_spec;
511 }
512 size_t len = va_arg(*args, size_t);
513 char *value = va_arg(*args, char *);
514 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
515 /*big_endian=*/false, kDigitsHigh);
516 break;
517 }
518 case kUnsignedDec: {
519 if (spec.is_nonstd) {
520 goto bad_spec;
521 }
522 uint32_t value = va_arg(*args, uint32_t);
523 *bytes_written +=
524 write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
525 break;
526 }
527 case kSvBinary: {
528 if (spec.is_nonstd) {
529 // Bools passed into a ... function will be automatically promoted
530 // to int; va_arg(..., bool) is actually UB!
531 if (va_arg(*args, int) != 0) {
532 *bytes_written += out.sink(out.data, "true", 4);
533 } else {
534 *bytes_written += out.sink(out.data, "false", 5);
535 }
536 break;
537 }
538 uint32_t value = va_arg(*args, uint32_t);
539 *bytes_written +=
540 write_digits(out, value, spec.width, spec.padding, 2, kDigitsLow);
541 break;
542 }
543 case kStatusResult: {
544 status_t value = va_arg(*args, status_t);
545 *bytes_written += write_status(out, value, spec.is_nonstd);
546 break;
547 }
548 bad_spec: // Used with `goto` to bail out early.
549 default: {
550 *bytes_written += out.sink(out.data, kUnknownSpec, sizeof(kUnknownSpec));
551 }
552 }
553}
554
555size_t base_vfprintf(buffer_sink_t out, const char *format, va_list args) {
556 if (out.sink == NULL) {
557 out.sink = &base_dev_null;
558 }
559
560 // NOTE: This copy is necessary on amd64 and other platforms, where
561 // `va_list` is a fixed array type (and, as such, decays to a pointer in
562 // an argument list). On PSABI RV32IMC, however, `va_list` is a `void*`, so
563 // this is a copy of the pointer, not the array.
564 va_list args_copy;
565 va_copy(args_copy, args);
566
567 size_t bytes_written = 0;
568 while (format[0] != '\0') {
569 if (!consume_until_percent(out, &format, &bytes_written)) {
570 break;
571 }
572 format_specifier_t spec;
573 if (!consume_format_specifier(out, &format, &bytes_written, &spec)) {
574 break;
575 }
576
577 process_specifier(out, spec, &bytes_written, &args_copy);
578 }
579
580 va_end(args_copy);
581 return bytes_written;
582}
583
584OT_NONSTRING const char kBaseHexdumpDefaultFmtAlphabet[256] =
585 // clang-format off
586 // First 32 characters are not printable.
587 "................................"
588 // Printable ASCII.
589 " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
590 // The rest of the range is also not printable (129 characters).
591 "................................................................"
592 "................................................................"
593 ".";
594// clang-format on
595
596static const base_hexdump_fmt_t kBaseHexdumpDefaultFmt = {
597 .bytes_per_word = 2,
598 .words_per_line = 8,
599 .alphabet = &kBaseHexdumpDefaultFmtAlphabet,
600};
601
602size_t base_hexdump(const char *buf, size_t len) {
603 return base_hexdump_with(kBaseHexdumpDefaultFmt, buf, len);
604}
605
606size_t base_snhexdump(char *out, size_t out_len, const char *buf, size_t len) {
607 return base_snhexdump_with(out, out_len, kBaseHexdumpDefaultFmt, buf, len);
608}
609
610size_t base_fhexdump(buffer_sink_t out, const char *buf, size_t len) {
611 return base_fhexdump_with(out, kBaseHexdumpDefaultFmt, buf, len);
612}
613
614size_t base_hexdump_with(base_hexdump_fmt_t fmt, const char *buf, size_t len) {
615 return base_fhexdump_with(base_stdout, fmt, buf, len);
616}
617
618size_t base_snhexdump_with(char *out, size_t out_len, base_hexdump_fmt_t fmt,
619 const char *buf, size_t len) {
620 snprintf_captures_t data = {
621 .buf = out,
622 .bytes_left = out_len,
623 };
624 buffer_sink_t sink = {
625 .data = &data,
626 .sink = &snprintf_sink,
627 };
628 return base_fhexdump_with(sink, fmt, buf, len);
629}
630
631size_t base_fhexdump_with(buffer_sink_t out, base_hexdump_fmt_t fmt,
632 const char *buf, size_t len) {
633 if (out.sink == NULL) {
634 out.sink = &base_dev_null;
635 }
636
637 size_t bytes_written = 0;
638 size_t bytes_per_line = fmt.bytes_per_word * fmt.words_per_line;
639
640 for (size_t line = 0; line < len; line += bytes_per_line) {
641 bytes_written += base_fprintf(out, "%08x:", line);
642
643 size_t chars_per_line = bytes_per_line * 2 + fmt.words_per_line;
644 size_t line_bytes_written = 0;
645 for (size_t word = 0; word < bytes_per_line; word += fmt.bytes_per_word) {
646 if (len < line + word) {
647 OT_NONSTRING char spaces[16] = " ";
648 while (line_bytes_written < chars_per_line) {
649 size_t to_print = chars_per_line - line_bytes_written;
650 if (to_print > sizeof(spaces)) {
651 to_print = sizeof(spaces);
652 }
653 line_bytes_written += base_fprintf(out, "%!s", to_print, spaces);
654 }
655 break;
656 }
657
658 size_t bytes_left = len - line - word;
659 if (bytes_left > fmt.bytes_per_word) {
660 bytes_left = fmt.bytes_per_word;
661 }
662 line_bytes_written += base_fprintf(out, " ");
663 line_bytes_written +=
664 hex_dump(out, buf + line + word, bytes_left, bytes_left, '0',
665 /*big_endian=*/false, kDigitsLow);
666 }
667 bytes_written += line_bytes_written;
668
669 bytes_written += base_fprintf(out, " ");
670 size_t buffered = 0;
671 char glyph_buffer[16];
672 for (size_t byte = 0; byte < bytes_per_line; ++byte) {
673 if (buffered == sizeof(glyph_buffer)) {
674 bytes_written += base_fprintf(out, "%!s", buffered, glyph_buffer);
675 buffered = 0;
676 }
677 if (line + byte >= len) {
678 break;
679 }
680 glyph_buffer[buffered++] =
681 (*fmt.alphabet)[(size_t)(uint8_t)buf[line + byte]];
682 }
683 if (buffered > 0) {
684 bytes_written += base_fprintf(out, "%!s", buffered, glyph_buffer);
685 }
686 bytes_written += base_fprintf(out, "\n");
687 }
688
689 return bytes_written;
690}