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