diff --git a/include/display_options.h b/include/display_options.h
index 049688e39e8..43810cbe22f 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 7752baba2bd..c08a87e3162 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);
-
-		/* 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;
-		}
+		uint thislinelen;
+		char buf[HEXDUMP_MAX_BUF_LENGTH(width * linelen)];
 
-		while (thislinelen < linelen) {
-			/* fill line with whitespace for nice ASCII print */
-			for (i=0; i<width*2+1; i++)
-				puts(" ");
-			linelen--;
-		}
-
-		/* 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';
-		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 3a3cdf323e1..079d3f7cb08 100644
--- a/test/print_ut.c
+++ b/test/print_ut.c
@@ -229,7 +229,33 @@ static int print_display_buffer(struct unit_test_state *uts)
 }
 PRINT_TEST(print_display_buffer, UT_TESTF_CONSOLE_REC);
 
-static int print_hex_dump(struct unit_test_state *uts)
+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_hexdump(struct unit_test_state *uts)
 {
 	u8 *buf;
 	int i;
@@ -302,7 +328,7 @@ static int print_hex_dump(struct unit_test_state *uts)
 
 	return 0;
 }
-PRINT_TEST(print_hex_dump, UT_TESTF_CONSOLE_REC);
+PRINT_TEST(print_hexdump, UT_TESTF_CONSOLE_REC);
 
 int do_ut_print(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
