diff --git a/include/display_options.h b/include/display_options.h index 049688e39e..43810cbe22 100644 --- a/include/display_options.h +++ b/include/display_options.h @@ -47,6 +47,31 @@ void print_freq(uint64_t freq, const char *suffix); int print_buffer(ulong addr, const void *data, uint width, uint count, uint linelen); +/* + * Maximum length of an output line is when width == 1 + * 9 for address, + * a space, two hex digits and an ASCII character for each byte + * 2 spaces between the hex and ASCII + * \0 terminator + */ +#define HEXDUMP_MAX_BUF_LENGTH(bytes) (9 + (bytes) * 4 + 3) + +/** + * hexdump_line() - Print out a single line of a hex dump + * + * @addr: Starting address to display at start of line + * @data: pointer to data buffer + * @width: data value width. May be 1, 2, or 4. + * @count: number of values to display + * @linelen: Number of values to print per line; specify 0 for default length + * @out: Output buffer to hold the dump + * @size: Size of output buffer in bytes + * @return number of bytes processed, if OK, -ENOSPC if buffer too small + * + */ +int hexdump_line(ulong addr, const void *data, uint width, uint count, + uint linelen, char *out, int size); + /** * display_options() - display the version string / build tag * diff --git a/lib/display_options.c b/lib/display_options.c index 7752baba2b..c08a87e316 100644 --- a/lib/display_options.c +++ b/lib/display_options.c @@ -131,10 +131,11 @@ void print_size(uint64_t size, const char *s) printf (" %ciB%s", c, s); } -#define MAX_LINE_LENGTH_BYTES (64) -#define DEFAULT_LINE_LENGTH_BYTES (16) -int print_buffer(ulong addr, const void *data, uint width, uint count, - uint linelen) +#define MAX_LINE_LENGTH_BYTES 64 +#define DEFAULT_LINE_LENGTH_BYTES 16 + +int hexdump_line(ulong addr, const void *data, uint width, uint count, + uint linelen, char *out, int size) { /* linebuf as a union causes proper alignment */ union linebuf { @@ -143,62 +144,86 @@ int print_buffer(ulong addr, const void *data, uint width, uint count, uint16_t us[MAX_LINE_LENGTH_BYTES/sizeof(uint16_t) + 1]; uint8_t uc[MAX_LINE_LENGTH_BYTES/sizeof(uint8_t) + 1]; } lb; + uint thislinelen; int i; ulong x; + if (linelen * width > MAX_LINE_LENGTH_BYTES) + linelen = MAX_LINE_LENGTH_BYTES / width; + if (linelen < 1) + linelen = DEFAULT_LINE_LENGTH_BYTES / width; + + /* + * Check the size here so that we don't need to use snprintf(). This + * helps to reduce code size + */ + if (size < HEXDUMP_MAX_BUF_LENGTH(linelen * width)) + return -ENOSPC; + + thislinelen = linelen; + out += sprintf(out, "%08lx:", addr); + + /* check for overflow condition */ + if (count < thislinelen) + thislinelen = count; + + /* Copy from memory into linebuf and print hex values */ + for (i = 0; i < thislinelen; i++) { + if (width == 4) + x = lb.ui[i] = *(volatile uint32_t *)data; + else if (MEM_SUPPORT_64BIT_DATA && width == 8) + x = lb.uq[i] = *(volatile ulong *)data; + else if (width == 2) + x = lb.us[i] = *(volatile uint16_t *)data; + else + x = lb.uc[i] = *(volatile uint8_t *)data; + if (CONFIG_IS_ENABLED(USE_TINY_PRINTF)) + out += sprintf(out, " %x", (uint)x); + else + out += sprintf(out, " %0*lx", width * 2, x); + data += width; + } + + /* fill line with whitespace for nice ASCII print */ + for (i = 0; i < (linelen - thislinelen) * (width * 2 + 1); i++) + *out++ = ' '; + + /* Print data in ASCII characters */ + for (i = 0; i < thislinelen * width; i++) { + if (!isprint(lb.uc[i]) || lb.uc[i] >= 0x80) + lb.uc[i] = '.'; + } + lb.uc[i] = '\0'; + out += sprintf(out, " %s", lb.uc); + + return thislinelen; +} + +int print_buffer(ulong addr, const void *data, uint width, uint count, + uint linelen) +{ if (linelen*width > MAX_LINE_LENGTH_BYTES) linelen = MAX_LINE_LENGTH_BYTES / width; if (linelen < 1) linelen = DEFAULT_LINE_LENGTH_BYTES / width; while (count) { - uint thislinelen = linelen; - printf("%08lx:", addr); + uint thislinelen; + char buf[HEXDUMP_MAX_BUF_LENGTH(width * linelen)]; - /* check for overflow condition */ - if (count < thislinelen) - thislinelen = count; - - /* Copy from memory into linebuf and print hex values */ - for (i = 0; i < thislinelen; i++) { - if (width == 4) - x = lb.ui[i] = *(volatile uint32_t *)data; - else if (MEM_SUPPORT_64BIT_DATA && width == 8) - x = lb.uq[i] = *(volatile ulong *)data; - else if (width == 2) - x = lb.us[i] = *(volatile uint16_t *)data; - else - x = lb.uc[i] = *(volatile uint8_t *)data; - if (CONFIG_IS_ENABLED(USE_TINY_PRINTF)) - printf(" %x", (uint)x); - else - printf(" %0*lx", width * 2, x); - data += width; - } - - while (thislinelen < linelen) { - /* fill line with whitespace for nice ASCII print */ - for (i=0; i= 0x80) - lb.uc[i] = '.'; - } - lb.uc[i] = '\0'; - printf(" %s\n", lb.uc); + thislinelen = hexdump_line(addr, data, width, count, linelen, + buf, sizeof(buf)); + assert(thislinelen >= 0); + puts(buf); + putc('\n'); /* update references */ + data += thislinelen * width; addr += thislinelen * width; count -= thislinelen; -#ifndef CONFIG_SPL_BUILD - if (ctrlc()) - return -1; -#endif + if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc()) + return -EINTR; } return 0; diff --git a/test/print_ut.c b/test/print_ut.c index 86b1a5477e..e2bcfbef00 100644 --- a/test/print_ut.c +++ b/test/print_ut.c @@ -227,6 +227,32 @@ static int print_display_buffer(struct unit_test_state *uts) } PRINT_TEST(print_display_buffer, UT_TESTF_CONSOLE_REC); +static int print_hexdump_line(struct unit_test_state *uts) +{ + char *linebuf; + u8 *buf; + int i; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\0', BUF_SIZE); + for (i = 0; i < 0x11; i++) + buf[i] = i * 0x11; + + /* Check buffer size calculations */ + linebuf = map_sysmem(0x400, BUF_SIZE); + memset(linebuf, '\xff', BUF_SIZE); + ut_asserteq(-ENOSPC, hexdump_line(0, buf, 1, 0x10, 0, linebuf, 75)); + ut_asserteq(-1, linebuf[0]); + ut_asserteq(0x10, hexdump_line(0, buf, 1, 0x10, 0, linebuf, 76)); + ut_asserteq(0, linebuf[75]); + ut_asserteq(-1, linebuf[76]); + + unmap_sysmem(buf); + + return 0; +} +PRINT_TEST(print_hexdump_line, UT_TESTF_CONSOLE_REC); + static int print_do_hex_dump(struct unit_test_state *uts) { u8 *buf;