Keep logs in a circular buffer so they can be extracted from a RAM dump.

Bug: 115484857, 132429213
Change-Id: I7ae3fd6c24ca8b42f581d9d4417fc6c49b23b43d
diff --git a/inc/hf/dlog.h b/inc/hf/dlog.h
index f36c428..f8c0080 100644
--- a/inc/hf/dlog.h
+++ b/inc/hf/dlog.h
@@ -21,6 +21,11 @@
 
 #include "hf/spci.h"
 
+#define DLOG_BUFFER_SIZE 8192
+
+extern size_t dlog_buffer_offset;
+extern char dlog_buffer[];
+
 #if DEBUG
 void dlog_enable_lock(void);
 void dlog(const char *fmt, ...);
diff --git a/src/dlog.c b/src/dlog.c
index 1b0a793..1bda568 100644
--- a/src/dlog.c
+++ b/src/dlog.c
@@ -42,6 +42,13 @@
 static bool dlog_lock_enabled = false;
 static struct spinlock sl = SPINLOCK_INIT;
 
+/*
+ * These global variables for the log buffer are not static because a test needs
+ * to access them directly.
+ */
+size_t dlog_buffer_offset;
+char dlog_buffer[DLOG_BUFFER_SIZE];
+
 /**
  * Takes the lock, if it is enabled.
  */
@@ -70,6 +77,13 @@
 	dlog_lock_enabled = true;
 }
 
+static void dlog_putchar(char c)
+{
+	dlog_buffer[dlog_buffer_offset] = c;
+	dlog_buffer_offset = (dlog_buffer_offset + 1) % DLOG_BUFFER_SIZE;
+	plat_console_putchar(c);
+}
+
 /**
  * Prints a raw string to the debug log and returns its length.
  */
@@ -78,7 +92,7 @@
 	const char *c = str;
 
 	while (*c != '\0') {
-		plat_console_putchar(*c++);
+		dlog_putchar(*c++);
 	}
 
 	return c - str;
@@ -100,14 +114,14 @@
 
 	/* Print the string up to the beginning of the suffix. */
 	while (str != suffix) {
-		plat_console_putchar(*str++);
+		dlog_putchar(*str++);
 	}
 
 	if (flags & FLAG_MINUS) {
 		/* Left-aligned. Print suffix, then print padding if needed. */
 		len += print_raw_string(suffix);
 		while (len < width) {
-			plat_console_putchar(' ');
+			dlog_putchar(' ');
 			len++;
 		}
 		return;
@@ -116,7 +130,7 @@
 	/* Fill until we reach the desired length. */
 	len += strnlen_s(suffix, DLOG_MAX_STRING_LENGTH);
 	while (len < width) {
-		plat_console_putchar(fill);
+		dlog_putchar(fill);
 		len++;
 	}
 
@@ -224,10 +238,10 @@
 	print_raw_string(": ");
 
 	for (size_t i = 0; i < length; ++i) {
-		plat_console_putchar(buffer[i]);
+		dlog_putchar(buffer[i]);
 		buffer[i] = '\0';
 	}
-	plat_console_putchar('\n');
+	dlog_putchar('\n');
 
 	unlock();
 }
@@ -247,7 +261,7 @@
 	for (p = fmt; *p; p++) {
 		switch (*p) {
 		default:
-			plat_console_putchar(*p);
+			dlog_putchar(*p);
 			break;
 
 		case '%':
@@ -335,7 +349,7 @@
 				break;
 
 			default:
-				plat_console_putchar('%');
+				dlog_putchar('%');
 			}
 
 			break;
diff --git a/test/arch/BUILD.gn b/test/arch/BUILD.gn
index d99a667..c770dee 100644
--- a/test/arch/BUILD.gn
+++ b/test/arch/BUILD.gn
@@ -27,6 +27,7 @@
   testonly = true
 
   sources = [
+    "dlog_test.c",
     "mm_test.c",
   ]
 
diff --git a/test/arch/dlog_test.c b/test/arch/dlog_test.c
new file mode 100644
index 0000000..49403e1
--- /dev/null
+++ b/test/arch/dlog_test.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hf/dlog.h"
+
+#include "hftest.h"
+
+/**
+ * Test that logs are written to the buffer, and the rest is empty.
+ */
+TEST(dlog, log_buffer)
+{
+	const char test_string[] = "Test string\n";
+
+	dlog(test_string);
+	ASSERT_EQ(strcmp(test_string, dlog_buffer), 0);
+	/* The \0 at the end shouldn't be counted. */
+	ASSERT_EQ(dlog_buffer_offset, sizeof(test_string) - 1);
+	for (int i = sizeof(test_string) - 1; i < DLOG_BUFFER_SIZE; ++i) {
+		EXPECT_EQ(dlog_buffer[i], '\0');
+	}
+}