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"
19
20#include "spi_device_regs.h" // Generated.
21
22// This is declared as an enum to force the values to be
23// compile-time constants, but the type is otherwise not
24// used for anything.
25enum {
26 // Standard format specifiers.
27 kPercent = '%',
28 kCharacter = 'c',
29 kString = 's',
30 kSignedDec1 = 'd',
31 kSignedDec2 = 'i',
32 kUnsignedOct = 'o',
33 kUnsignedHexLow = 'x',
34 kUnsignedHexHigh = 'X',
35 kUnsignedDec = 'u',
36 kPointer = 'p',
37
38 // Verilog-style format specifiers.
39 kSvBinary = 'b',
40 kSvHexLow = 'h',
41 kSvHexHigh = 'H',
42
43 // Other non-standard specifiers.
44 kHexLeLow = 'y',
45 kHexLeHigh = 'Y',
46 kStatusResult = 'r',
47 kFourCC = 'C',
48};
49
50// NOTE: all of the lengths of the strings below are given so that the NUL
51// terminator is left off; that way, `sizeof(kConst)` does not include it.
52OT_NONSTRING static const char kDigitsLow[16] = "0123456789abcdef";
53OT_NONSTRING static const char kDigitsHigh[16] = "0123456789ABCDEF";
54
55OT_NONSTRING static const char kErrorNul[17] = "%<unexpected nul>";
56OT_NONSTRING static const char kUnknownSpec[15] = "%<unknown spec>";
57OT_NONSTRING static const char kErrorTooWide[12] = "%<bad width>";
58
59static size_t base_dev_null(void *data, const char *buf, size_t len) {
60 return len;
61}
62static buffer_sink_t base_stdout = {
63 .data = NULL,
64 // Note: Using `&base_dev_null` causes this variable to be placed in the
65 // .data section and triggers the assertion in rom.ld.
66 .sink = NULL,
67};
68
69// The GPIO TX indicator pin that can be used with the SPI console.
70static dif_gpio_t *spi_console_gpio = NULL;
71static dif_gpio_pin_t spi_console_tx_ready_gpio = UINT32_MAX;
72
73void base_set_stdout(buffer_sink_t out) {
74 if (out.sink == NULL) {
75 out.sink = &base_dev_null;
76 }
77 base_stdout = out;
78}
79
80static const size_t kSpiDeviceReadBufferSizeBytes =
81 SPI_DEVICE_PARAM_SRAM_READ_BUFFER_DEPTH * sizeof(uint32_t);
82static const size_t kSpiDeviceFrameHeaderSizeBytes = 12;
83static const size_t kSpiDeviceBufferPreservedSizeBytes =
84 kSpiDeviceFrameHeaderSizeBytes;
85static const size_t kSpiDeviceMaxFramePayloadSizeBytes =
86 kSpiDeviceReadBufferSizeBytes - kSpiDeviceFrameHeaderSizeBytes -
87 kSpiDeviceBufferPreservedSizeBytes - 4;
88static const uint32_t kSpiDeviceFrameMagicNumber = 0xa5a5beef;
89static uint32_t spi_device_frame_num = 0;
90
91static status_t spi_device_send_data(dif_spi_device_handle_t *spi_device,
92 const uint8_t *buf, size_t len,
93 size_t address) {
94 if (len == 0) {
95 return OK_STATUS();
96 }
97
98 size_t space_to_end_of_buffer = kSpiDeviceReadBufferSizeBytes - address;
99 size_t first_part_size =
100 space_to_end_of_buffer < len ? space_to_end_of_buffer : len;
101
102 TRY(dif_spi_device_write_flash_buffer(spi_device,
104 address, first_part_size, buf));
105
106 // Handle wrap-around.
107 if (first_part_size < len) {
108 size_t second_part_size = len - first_part_size;
109 TRY(dif_spi_device_write_flash_buffer(
110 spi_device, kDifSpiDeviceFlashBufferTypeEFlash, 0, second_part_size,
111 (uint8_t *)(buf + first_part_size)));
112 }
113
114 return OK_STATUS();
115}
116
117/**
118 * Sends data out of the SPI device.
119 *
120 * Data is packaged into a frame that is described below.
121 * The host side reads the header first, then decides how many words
122 * to read from the data section.
123 *
124 * -----------------------------------------------
125 * | Magic Number | 4-bytes | |
126 * -----------------------------------| |
127 * | Frame Number | 4-bytes | Header |
128 * -----------------------------------| |
129 * | Data Length (bytes) | 4-bytes | |
130 * -----------------------------------|----------|
131 * | Data (word aligned) | |
132 * -----------------------------------| Data |
133 * | 0xFF Pad Bytes | <4-bytes | |
134 * -----------------------------------|----------|
135 */
136static size_t spi_device_send_frame(void *data, const char *buf, size_t len) {
138 const size_t data_packet_size_bytes = ((len + 3u) & ~3u);
139 const size_t frame_size_bytes =
140 kSpiDeviceFrameHeaderSizeBytes + data_packet_size_bytes;
141 uint8_t frame_header_bytes[kSpiDeviceFrameHeaderSizeBytes];
142
143 static uint32_t next_write_address = 0;
144
145 if (frame_size_bytes >= kSpiDeviceReadBufferSizeBytes) {
146 return 0;
147 }
148
149 // Add the magic bytes.
150 for (size_t i = 0; i < 4; ++i) {
151 frame_header_bytes[i] = (kSpiDeviceFrameMagicNumber >> (i * 8)) & 0xff;
152 }
153 // Add the frame number.
154 for (size_t i = 0; i < 4; ++i) {
155 frame_header_bytes[i + 4] = (spi_device_frame_num >> (i * 8)) & 0xff;
156 }
157 // Add the data length.
158 for (size_t i = 0; i < 4; ++i) {
159 frame_header_bytes[i + 8] = (len >> (i * 8)) & 0xff;
160 }
161
162 // Wait for enough space to free up in the SPI flash buffer if we are in
163 // operating in polling mode.
164 if (spi_console_gpio == NULL) {
165 uint32_t available_buffer_size = 0;
166 uint32_t last_read_address = 0;
167 do {
168 if (dif_spi_device_get_last_read_address(spi_device,
169 &last_read_address) != kDifOk) {
170 return 0;
171 }
172
173 // If we are not using the GPIO TX-ready indicator pin (which is the
174 // default) the host SPI console is constantly polling the spi_device to
175 // see if data is available to be read out. In this case, we need to
176 // adjust the last read address.
177 //
178 // Specifically, when the host is continuously reading from the read
179 // buffer, it is unaware of whether it is going to find a valid new frame
180 // (marked by a magic number in the frame header), an frame header all
181 // zeros, or garbage, since it is operating in polling mode. This could
182 // result in the reported last_read_address being one header size ahead of
183 // the actual address of the last valid frame if all the frames in the
184 // read buffer has been consumed by the host. While it's harmless to use
185 // the last read address even if the reported value is a frame header
186 // ahead, doing so might temporarily underestimate the available buffer
187 // size by the size of a frame header (or 12 bytes to be specific).
188 //
189 // However, if we are using the GPIO TX-ready indicator pin, the host will
190 // only ever attempt to read out data if it was signaled to do so by the
191 // device. In which case the next write address will always be 0, i.e.,
192 // the beginning of the buffer.
193 uint32_t adjusted_last_read_address =
194 (kSpiDeviceReadBufferSizeBytes + last_read_address -
195 kSpiDeviceFrameHeaderSizeBytes) %
196 kSpiDeviceReadBufferSizeBytes;
197
198 // Frames are always word aligned, so ensure the last read address is word
199 // aligned too.
200 uint32_t next_read_address = ((adjusted_last_read_address + 1) & ~3u) %
201 kSpiDeviceReadBufferSizeBytes;
202
203 // Compute the remaining free space in the SPI flash buffer.
204 if (next_read_address > next_write_address) {
205 available_buffer_size = next_read_address - next_write_address - 1;
206 } else {
207 available_buffer_size =
208 next_read_address +
209 (kSpiDeviceReadBufferSizeBytes - next_write_address) - 1;
210 }
211 } while ((frame_size_bytes + kSpiDeviceBufferPreservedSizeBytes) >
212 available_buffer_size);
213 }
214
215 // Send aligned data.
216 size_t data_write_address =
217 (next_write_address + kSpiDeviceFrameHeaderSizeBytes) %
218 kSpiDeviceReadBufferSizeBytes;
219 size_t aligned_data_len = len & (~3u);
220 if (!status_ok(spi_device_send_data(spi_device, (uint8_t *)buf,
221 aligned_data_len, data_write_address))) {
222 return 0;
223 }
224
225 // Send unaligned data.
226 if (len != aligned_data_len) {
227 uint8_t pad_bytes[4] = {0xff, 0xff, 0xff, 0xff};
228 size_t pad_write_address =
229 (data_write_address + aligned_data_len) % kSpiDeviceReadBufferSizeBytes;
230
231 for (size_t i = 0; i + aligned_data_len < len; i++) {
232 pad_bytes[i] = buf[aligned_data_len + i];
233 }
234 if (!status_ok(spi_device_send_data(spi_device, pad_bytes, 4,
235 pad_write_address))) {
236 return 0;
237 }
238 }
239
240 // Send frame header.
241 if (!status_ok(spi_device_send_data(spi_device, frame_header_bytes,
242 kSpiDeviceFrameHeaderSizeBytes,
243 next_write_address))) {
244 return 0;
245 }
246
247 // Update the next write address and frame number.
248 next_write_address =
249 (next_write_address + frame_size_bytes) % kSpiDeviceReadBufferSizeBytes;
250 spi_device_frame_num++;
251
252 // Block until the host to reads out the frame by toggling the GPIO TX-ready
253 // indicator pin to signal to the host to clock out data from the spi_device
254 // egress buffer.
255 if (spi_console_gpio != NULL) {
257 dif_gpio_write(spi_console_gpio, spi_console_tx_ready_gpio, true));
258 bool cs_state = true;
259 bool target_cs_state = false;
260 // There will be two bulk transfers that can be synchronized by the
261 // chip-select action. First the host will read out the 12-byte frame
262 // header, followed by the N-byte payload. Each transfer can be observed by
263 // the chip-select toggling low then high. After the first toggle low, when
264 // the host begins reading out the frame header, we can deassert the
265 // TX-ready pin as the host has already initiated the two SPI transactions.
266 for (size_t i = 0; i < 4; ++i) {
267 do {
268 if (dif_spi_device_get_csb_status(spi_device, &cs_state) != kDifOk) {
269 return 0;
270 }
271 } while (cs_state != target_cs_state);
272 if (i == 0) {
274 dif_gpio_write(spi_console_gpio, spi_console_tx_ready_gpio, false));
275 }
276 target_cs_state = !target_cs_state;
277 }
278 next_write_address = 0;
279 }
280
281 return len;
282}
283
284static size_t base_dev_spi_device(void *data, const char *buf, size_t len) {
285 size_t write_data_len = 0;
286
287 while (write_data_len < len) {
288 size_t payload_len = len - write_data_len;
289 if (payload_len > kSpiDeviceMaxFramePayloadSizeBytes) {
290 payload_len = kSpiDeviceMaxFramePayloadSizeBytes;
291 }
292 if (spi_device_send_frame(data, buf + write_data_len, payload_len) ==
293 payload_len) {
294 write_data_len += payload_len;
295 }
296 }
297
298 return write_data_len;
299}
300
301sink_func_ptr get_spi_device_sink(void) { return &base_dev_spi_device; }
302
303static size_t base_dev_uart(void *data, const char *buf, size_t len) {
304 const dif_uart_t *uart = (const dif_uart_t *)data;
305 for (size_t i = 0; i < len; ++i) {
306 if (dif_uart_byte_send_polled(uart, (uint8_t)buf[i]) != kDifOk) {
307 return i;
308 }
309 }
310 return len;
311}
312
313sink_func_ptr get_uart_sink(void) { return &base_dev_uart; }
314
315void base_spi_device_set_gpio_tx_indicator(dif_gpio_t *gpio,
316 dif_gpio_pin_t tx_indicator_pin) {
317 spi_console_gpio = gpio;
318 spi_console_tx_ready_gpio = tx_indicator_pin;
319}
320
321void base_spi_device_stdout(const dif_spi_device_handle_t *spi_device) {
322 // Reset the frame counter.
323 spi_device_frame_num = 0;
324 base_set_stdout((buffer_sink_t){.data = (void *)spi_device,
325 .sink = &base_dev_spi_device});
326}
327
328void base_uart_stdout(const dif_uart_t *uart) {
329 base_set_stdout(
330 (buffer_sink_t){.data = (void *)uart, .sink = &base_dev_uart});
331}
332
333size_t base_printf(const char *format, ...) {
334 va_list args;
335 va_start(args, format);
336 size_t bytes_left = base_vprintf(format, args);
337 va_end(args);
338 return bytes_left;
339}
340
341size_t base_vprintf(const char *format, va_list args) {
342 return base_vfprintf(base_stdout, format, args);
343}
344
345typedef struct snprintf_captures_t {
346 char *buf;
347 size_t bytes_left;
349
350static size_t snprintf_sink(void *data, const char *buf, size_t len) {
351 snprintf_captures_t *captures = (snprintf_captures_t *)data;
352 if (captures->bytes_left == 0) {
353 return 0;
354 }
355
356 if (len > captures->bytes_left) {
357 len = captures->bytes_left;
358 }
359 memcpy(captures->buf, buf, len);
360 captures->buf += len;
361 captures->bytes_left -= len;
362 return len;
363}
364
365size_t base_snprintf(char *buf, size_t len, const char *format, ...) {
366 va_list args;
367 va_start(args, format);
368
369 snprintf_captures_t data = {
370 .buf = buf,
371 .bytes_left = len,
372 };
373 buffer_sink_t out = {
374 .data = &data,
375 .sink = &snprintf_sink,
376 };
377 size_t bytes_left = base_vfprintf(out, format, args);
378 va_end(args);
379 return bytes_left;
380}
381
382size_t base_fprintf(buffer_sink_t out, const char *format, ...) {
383 va_list args;
384 va_start(args, format);
385 size_t bytes_left = base_vfprintf(out, format, args);
386 va_end(args);
387 return bytes_left;
388}
389
390/**
391 * Consumes characters from `format` until a '%' or NUL is reached. All
392 * characters seen before that are then sinked into `out`.
393 *
394 * @param out the sink to write bytes to.
395 * @param format a pointer to the format string to consume a prefix of.
396 * @param[out] bytes_written out param for the number of bytes writen to `out`.
397 * @return true if an unprocessed '%' was found.
398 */
399static bool consume_until_percent(buffer_sink_t out, const char **format,
400 size_t *bytes_written) {
401 size_t text_len = 0;
402 while (true) {
403 char c = (*format)[text_len];
404 if (c == '\0' || c == kPercent) {
405 if (text_len > 0) {
406 *bytes_written += out.sink(out.data, *format, text_len);
407 }
408 *format += text_len;
409 return c != '\0';
410 }
411 ++text_len;
412 }
413}
414
415/**
416 * Represents a parsed format specifier.
417 */
418typedef struct format_specifier {
419 char type;
420 size_t width;
421 char padding;
422 bool is_nonstd;
423} format_specifier_t;
424
425/**
426 * Consumes characters from `format` until a complete format specifier is
427 * parsed. See the documentation in `print.h` for full syntax.
428 *
429 * @param out the sink to write bytes to.
430 * @param format a pointer to the format string to consume a prefix of.
431 * @param[out] spec out param for the specifier.
432 * @return whether the parse succeeded.
433 */
434static bool consume_format_specifier(buffer_sink_t out, const char **format,
435 size_t *bytes_written,
436 format_specifier_t *spec) {
437 *spec = (format_specifier_t){0};
438
439 // Consume the percent sign.
440 ++(*format);
441
442 // Parse a ! to detect an OpenTitan-specific extension (that isn't one
443 // of the Verilog ones...).
444 if ((*format)[0] == '!') {
445 spec->is_nonstd = true;
446 ++(*format);
447 }
448
449 // Attempt to parse out an unsigned, decimal number, a "width",
450 // after the percent sign; the format specifier is the character
451 // immediately after this width.
452 //
453 // `spec->padding` is used to indicate whether we've seen a width;
454 // if nonzero, we have an actual width.
455 size_t spec_len = 0;
456 while (true) {
457 char c = (*format)[spec_len];
458 if (c == '\0') {
459 *bytes_written += out.sink(out.data, kErrorNul, sizeof(kErrorNul));
460 return false;
461 }
462 if (c < '0' || c > '9') {
463 break;
464 }
465 if (spec->padding == 0) {
466 if (c == '0') {
467 spec->padding = '0';
468 ++spec_len;
469 continue;
470 }
471 spec->padding = ' ';
472 }
473 spec->width *= 10;
474 spec->width += (c - '0');
475 ++spec_len;
476 }
477
478 if ((spec->width == 0 && spec->padding != 0) || spec->width > 32) {
479 *bytes_written += out.sink(out.data, kErrorTooWide, sizeof(kErrorTooWide));
480 return false;
481 }
482
483 spec->type = (*format)[spec_len];
484 *format += spec_len + 1;
485 return true;
486}
487
488/**
489 * Write the digits of `value` onto `out`.
490 *
491 * @param out the sink to write bytes to.
492 * @param value the value to "stringify".
493 * @param width the minimum width to print; going below will result in writing
494 * out zeroes.
495 * @param base the base to express `value` in.
496 * @param glyphs an array of characters to use as the digits of a number, which
497 * should be at least ast long as `base`.
498 * @return the number of bytes written.
499 */
500static size_t write_digits(buffer_sink_t out, uint32_t value, uint32_t width,
501 char padding, uint32_t base, const char *glyphs) {
502 // All allocations are done relative to a buffer that could hold the longest
503 // textual representation of a number: ~0x0 in base 2, i.e., 32 ones.
504 static const int kWordBits = sizeof(uint32_t) * 8;
505 char buffer[kWordBits];
506
507 size_t len = 0;
508 if (value == 0) {
509 buffer[kWordBits - 1] = glyphs[0];
510 ++len;
511 }
512 while (value > 0) {
513 uint32_t digit = value % base;
514 value /= base;
515 buffer[kWordBits - 1 - len] = glyphs[digit];
516 ++len;
517 }
518 width = width == 0 ? 1 : width;
519 width = width > kWordBits ? kWordBits : width;
520 while (len < width) {
521 buffer[kWordBits - len - 1] = padding;
522 ++len;
523 }
524 return out.sink(out.data, buffer + (kWordBits - len), len);
525}
526
527static size_t write_status(buffer_sink_t out, status_t value, bool as_json) {
528 // The module id is defined to be 3 chars long.
529 char mod[] = {0, 0, 0};
530 int32_t arg;
531 const char *start;
532 bool err = status_extract(value, &start, &arg, mod);
533
534 // strlen of the status code.
535 const char *end = start;
536 while (*end)
537 end++;
538 size_t len = 0;
539
540 len += out.sink(out.data, "{\"", as_json ? 2 : 0);
541 len += out.sink(out.data, start, (size_t)(end - start));
542 len += out.sink(out.data, "\"", as_json ? 1 : 0);
543
544 len += out.sink(out.data, ":", 1);
545 if (err) {
546 // All error codes include the module identifier.
547 len += out.sink(out.data, "[\"", 2);
548 for (size_t i = 0; i < 3; i++) {
549 if (mod[i] == '\\') {
550 len += out.sink(out.data, "\\\\", 2);
551 } else {
552 len += out.sink(out.data, &mod[i], 1);
553 }
554 }
555 len += out.sink(out.data, "\",", 2);
556 len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
557 len += out.sink(out.data, "]", 1);
558 } else {
559 // Ok codes include only the arg
560 len += write_digits(out, (uint32_t)arg, 0, 0, 10, kDigitsLow);
561 }
562 len += out.sink(out.data, "}", as_json ? 1 : 0);
563 return len;
564}
565
566/**
567 * Hexdumps `bytes` onto `out`.
568 *
569 * @param out the sink to write bytes to.
570 * @param bytes the bytes to dump.
571 * @param len the number of bytes to dump.
572 * @param width the minimum width to print; going below will result in writing
573 * out zeroes.
574 * @param padding the character to use for padding.
575 * @param big_endian whether to print in big-endian order (i.e. as %x would).
576 * @param glyphs an array of characters to use as the digits of a number, which
577 * should be at least ast long as `base`.
578 * @return the number of bytes written.
579 */
580static size_t hex_dump(buffer_sink_t out, const char *bytes, size_t len,
581 uint32_t width, char padding, bool big_endian,
582 const char *glyphs) {
583 size_t bytes_written = 0;
584 char buf[32];
585 size_t buffered = 0;
586 if (len < width) {
587 width -= len;
588 memset(buf, padding, sizeof(buf));
589 while (width > 0) {
590 size_t to_write = width > ARRAYSIZE(buf) ? 32 : width;
591 bytes_written += out.sink(out.data, buf, to_write);
592 width -= to_write;
593 }
594 }
595
596 for (size_t i = 0; i < len; ++i) {
597 size_t idx = big_endian ? len - i - 1 : i;
598 buf[buffered] = glyphs[(bytes[idx] >> 4) & 0xf];
599 buf[buffered + 1] = glyphs[bytes[idx] & 0xf];
600 buffered += 2;
601
602 if (buffered == ARRAYSIZE(buf)) {
603 bytes_written += out.sink(out.data, buf, buffered);
604 buffered = 0;
605 }
606 }
607
608 if (buffered != 0) {
609 bytes_written += out.sink(out.data, buf, buffered);
610 }
611 return bytes_written;
612}
613
614/**
615 * Prints out the next entry in `args` according to `spec`.
616 *
617 * This function assumes that `spec` accurately describes the next entry in
618 * `args`.
619 *
620 * @param out the sink to write bytes to.
621 * @param spec the specifier to use for stringifying.
622 * @param[out] bytes_written out param for the number of bytes writen to `out`.
623 * @param args the list to pull an entry from.
624 */
625static void process_specifier(buffer_sink_t out, format_specifier_t spec,
626 size_t *bytes_written, va_list *args) {
627 // Switch on the specifier. At this point, we assert that there is
628 // an initialized value of correct type in the VA list; if it is
629 // missing, the caller has caused UB.
630 switch (spec.type) {
631 case kPercent: {
632 if (spec.is_nonstd) {
633 goto bad_spec;
634 }
635 *bytes_written += out.sink(out.data, "%", 1);
636 break;
637 }
638 case kCharacter: {
639 if (spec.is_nonstd) {
640 goto bad_spec;
641 }
642 char value = (char)va_arg(*args, uint32_t);
643 *bytes_written += out.sink(out.data, &value, 1);
644 break;
645 }
646 case kFourCC: {
647 uint32_t value = va_arg(*args, uint32_t);
648 for (size_t i = 0; i < sizeof(uint32_t); ++i, value >>= 8) {
649 uint8_t ch = (uint8_t)value;
650 if (ch >= 32 && ch < 127) {
651 *bytes_written += out.sink(out.data, (const char *)&ch, 1);
652 } else {
653 *bytes_written += out.sink(out.data, "\\x", 2);
654 *bytes_written += out.sink(out.data, &kDigitsLow[ch >> 4], 1);
655 *bytes_written += out.sink(out.data, &kDigitsLow[ch & 15], 1);
656 }
657 }
658 break;
659 }
660 case kString: {
661 size_t len = 0;
662 if (spec.is_nonstd) {
663 // This implements %!s.
664 len = va_arg(*args, size_t);
665 }
666
667 char *value = va_arg(*args, char *);
668 while (!spec.is_nonstd && value[len] != '\0') {
669 // This implements %s.
670 ++len;
671 }
672
673 *bytes_written += out.sink(out.data, value, len);
674 break;
675 }
676 case kSignedDec1:
677 case kSignedDec2: {
678 if (spec.is_nonstd) {
679 goto bad_spec;
680 }
681 uint32_t value = va_arg(*args, uint32_t);
682 if (((int32_t)value) < 0) {
683 *bytes_written += out.sink(out.data, "-", 1);
684 value = -value;
685 }
686 *bytes_written +=
687 write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
688 break;
689 }
690 case kUnsignedOct: {
691 if (spec.is_nonstd) {
692 goto bad_spec;
693 }
694 uint32_t value = va_arg(*args, uint32_t);
695 *bytes_written +=
696 write_digits(out, value, spec.width, spec.padding, 8, kDigitsLow);
697 break;
698 }
699 case kPointer: {
700 if (spec.is_nonstd) {
701 goto bad_spec;
702 }
703 // Pointers are formatted as 0x<hex digits>, where the width is always
704 // set to the number necessary to represent a pointer on the current
705 // platform, that is, the size of uintptr_t in nybbles. For example, on
706 // different architecutres the null pointer prints as
707 // - rv32imc: 0x00000000 (four bytes, eight nybbles).
708 // - amd64: 0x0000000000000000 (eight bytes, sixteen nybbles).
709 *bytes_written += out.sink(out.data, "0x", 2);
710 uintptr_t value = va_arg(*args, uintptr_t);
711 *bytes_written +=
712 write_digits(out, value, sizeof(uintptr_t) * 2, '0', 16, kDigitsLow);
713 break;
714 }
715 case kUnsignedHexLow:
716 if (spec.is_nonstd) {
717 size_t len = va_arg(*args, size_t);
718 char *value = va_arg(*args, char *);
719 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
720 /*big_endian=*/true, kDigitsLow);
721 break;
722 }
724 case kSvHexLow: {
725 uint32_t value = va_arg(*args, uint32_t);
726 *bytes_written +=
727 write_digits(out, value, spec.width, spec.padding, 16, kDigitsLow);
728 break;
729 }
730 case kUnsignedHexHigh:
731 if (spec.is_nonstd) {
732 size_t len = va_arg(*args, size_t);
733 char *value = va_arg(*args, char *);
734 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
735 /*big_endian=*/true, kDigitsHigh);
736 break;
737 }
739 case kSvHexHigh: {
740 uint32_t value = va_arg(*args, uint32_t);
741 *bytes_written +=
742 write_digits(out, value, spec.width, spec.padding, 16, kDigitsHigh);
743 break;
744 }
745 case kHexLeLow: {
746 if (!spec.is_nonstd) {
747 goto bad_spec;
748 }
749 size_t len = va_arg(*args, size_t);
750 char *value = va_arg(*args, char *);
751 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
752 /*big_endian=*/false, kDigitsLow);
753 break;
754 }
755 case kHexLeHigh: {
756 if (!spec.is_nonstd) {
757 goto bad_spec;
758 }
759 size_t len = va_arg(*args, size_t);
760 char *value = va_arg(*args, char *);
761 *bytes_written += hex_dump(out, value, len, spec.width, spec.padding,
762 /*big_endian=*/false, kDigitsHigh);
763 break;
764 }
765 case kUnsignedDec: {
766 if (spec.is_nonstd) {
767 goto bad_spec;
768 }
769 uint32_t value = va_arg(*args, uint32_t);
770 *bytes_written +=
771 write_digits(out, value, spec.width, spec.padding, 10, kDigitsLow);
772 break;
773 }
774 case kSvBinary: {
775 if (spec.is_nonstd) {
776 // Bools passed into a ... function will be automatically promoted
777 // to int; va_arg(..., bool) is actually UB!
778 if (va_arg(*args, int) != 0) {
779 *bytes_written += out.sink(out.data, "true", 4);
780 } else {
781 *bytes_written += out.sink(out.data, "false", 5);
782 }
783 break;
784 }
785 uint32_t value = va_arg(*args, uint32_t);
786 *bytes_written +=
787 write_digits(out, value, spec.width, spec.padding, 2, kDigitsLow);
788 break;
789 }
790 case kStatusResult: {
791 status_t value = va_arg(*args, status_t);
792 *bytes_written += write_status(out, value, spec.is_nonstd);
793 break;
794 }
795 bad_spec: // Used with `goto` to bail out early.
796 default: {
797 *bytes_written += out.sink(out.data, kUnknownSpec, sizeof(kUnknownSpec));
798 }
799 }
800}
801
802size_t base_vfprintf(buffer_sink_t out, const char *format, va_list args) {
803 if (out.sink == NULL) {
804 out.sink = &base_dev_null;
805 }
806
807 // NOTE: This copy is necessary on amd64 and other platforms, where
808 // `va_list` is a fixed array type (and, as such, decays to a pointer in
809 // an argument list). On PSABI RV32IMC, however, `va_list` is a `void*`, so
810 // this is a copy of the pointer, not the array.
811 va_list args_copy;
812 va_copy(args_copy, args);
813
814 size_t bytes_written = 0;
815 while (format[0] != '\0') {
816 if (!consume_until_percent(out, &format, &bytes_written)) {
817 break;
818 }
819 format_specifier_t spec;
820 if (!consume_format_specifier(out, &format, &bytes_written, &spec)) {
821 break;
822 }
823
824 process_specifier(out, spec, &bytes_written, &args_copy);
825 }
826
827 va_end(args_copy);
828 return bytes_written;
829}
830
831OT_NONSTRING const char kBaseHexdumpDefaultFmtAlphabet[256] =
832 // clang-format off
833 // First 32 characters are not printable.
834 "................................"
835 // Printable ASCII.
836 " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
837 // The rest of the range is also not printable (129 characters).
838 "................................................................"
839 "................................................................"
840 ".";
841// clang-format on
842
843static const base_hexdump_fmt_t kBaseHexdumpDefaultFmt = {
844 .bytes_per_word = 2,
845 .words_per_line = 8,
846 .alphabet = &kBaseHexdumpDefaultFmtAlphabet,
847};
848
849size_t base_hexdump(const char *buf, size_t len) {
850 return base_hexdump_with(kBaseHexdumpDefaultFmt, buf, len);
851}
852
853size_t base_snhexdump(char *out, size_t out_len, const char *buf, size_t len) {
854 return base_snhexdump_with(out, out_len, kBaseHexdumpDefaultFmt, buf, len);
855}
856
857size_t base_fhexdump(buffer_sink_t out, const char *buf, size_t len) {
858 return base_fhexdump_with(out, kBaseHexdumpDefaultFmt, buf, len);
859}
860
861size_t base_hexdump_with(base_hexdump_fmt_t fmt, const char *buf, size_t len) {
862 return base_fhexdump_with(base_stdout, fmt, buf, len);
863}
864
865size_t base_snhexdump_with(char *out, size_t out_len, base_hexdump_fmt_t fmt,
866 const char *buf, size_t len) {
867 snprintf_captures_t data = {
868 .buf = out,
869 .bytes_left = out_len,
870 };
871 buffer_sink_t sink = {
872 .data = &data,
873 .sink = &snprintf_sink,
874 };
875 return base_fhexdump_with(sink, fmt, buf, len);
876}
877
878size_t base_fhexdump_with(buffer_sink_t out, base_hexdump_fmt_t fmt,
879 const char *buf, size_t len) {
880 if (out.sink == NULL) {
881 out.sink = &base_dev_null;
882 }
883
884 size_t bytes_written = 0;
885 size_t bytes_per_line = fmt.bytes_per_word * fmt.words_per_line;
886
887 for (size_t line = 0; line < len; line += bytes_per_line) {
888 bytes_written += base_fprintf(out, "%08x:", line);
889
890 size_t chars_per_line = bytes_per_line * 2 + fmt.words_per_line;
891 size_t line_bytes_written = 0;
892 for (size_t word = 0; word < bytes_per_line; word += fmt.bytes_per_word) {
893 if (len < line + word) {
894 OT_NONSTRING char spaces[16] = " ";
895 while (line_bytes_written < chars_per_line) {
896 size_t to_print = chars_per_line - line_bytes_written;
897 if (to_print > sizeof(spaces)) {
898 to_print = sizeof(spaces);
899 }
900 line_bytes_written += base_fprintf(out, "%!s", to_print, spaces);
901 }
902 break;
903 }
904
905 size_t bytes_left = len - line - word;
906 if (bytes_left > fmt.bytes_per_word) {
907 bytes_left = fmt.bytes_per_word;
908 }
909 line_bytes_written += base_fprintf(out, " ");
910 line_bytes_written +=
911 hex_dump(out, buf + line + word, bytes_left, bytes_left, '0',
912 /*big_endian=*/false, kDigitsLow);
913 }
914 bytes_written += line_bytes_written;
915
916 bytes_written += base_fprintf(out, " ");
917 size_t buffered = 0;
918 char glyph_buffer[16];
919 for (size_t byte = 0; byte < bytes_per_line; ++byte) {
920 if (buffered == sizeof(glyph_buffer)) {
921 bytes_written += base_fprintf(out, "%!s", buffered, glyph_buffer);
922 buffered = 0;
923 }
924 if (line + byte >= len) {
925 break;
926 }
927 glyph_buffer[buffered++] =
928 (*fmt.alphabet)[(size_t)(uint8_t)buf[line + byte]];
929 }
930 if (buffered > 0) {
931 bytes_written += base_fprintf(out, "%!s", buffered, glyph_buffer);
932 }
933 bytes_written += base_fprintf(out, "\n");
934 }
935
936 return bytes_written;
937}