Add HVC/SMC call for debug logging.

Bug: 115484857
Change-Id: I253adf03ebde97d4b620be9d3f2cc05f5265f45d
diff --git a/inc/hf/api.h b/inc/hf/api.h
index af21d00..a808e5d 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -39,6 +39,7 @@
 int64_t api_mailbox_waiter_get(spci_vm_id_t vm_id, const struct vcpu *current);
 int64_t api_share_memory(spci_vm_id_t vm_id, ipaddr_t addr, size_t size,
 			 enum hf_share share, struct vcpu *current);
+int64_t api_debug_log(char c, struct vcpu *current);
 
 struct vcpu *api_preempt(struct vcpu *current);
 struct vcpu *api_wait_for_interrupt(struct vcpu *current);
diff --git a/inc/hf/dlog.h b/inc/hf/dlog.h
index 3e83624..01dddd9 100644
--- a/inc/hf/dlog.h
+++ b/inc/hf/dlog.h
@@ -18,6 +18,8 @@
 
 #include <stdarg.h>
 
+#include "vm.h"
+
 #if DEBUG
 void dlog_enable_lock(void);
 void dlog(const char *fmt, ...);
@@ -27,3 +29,5 @@
 #define dlog(...)
 #define vdlog(fmt, args)
 #endif
+
+void dlog_flush_vm_buffer(struct vm_locked vm);
diff --git a/inc/hf/vm.h b/inc/hf/vm.h
index 8930c11..4abff00 100644
--- a/inc/hf/vm.h
+++ b/inc/hf/vm.h
@@ -27,6 +27,8 @@
 
 #include "vmapi/hf/spci.h"
 
+#define LOG_BUFFER_SIZE 256
+
 enum mailbox_state {
 	/** There is no message in the mailbox. */
 	MAILBOX_STATE_EMPTY,
@@ -83,6 +85,8 @@
 	struct vcpu vcpus[MAX_CPUS];
 	struct mm_ptable ptable;
 	struct mailbox mailbox;
+	char log_buffer[LOG_BUFFER_SIZE];
+	size_t log_buffer_length;
 
 	/** Wait entries to be used when waiting on other VM mailboxes. */
 	struct wait_entry wait_entries[MAX_VMS];
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 6e68a50..d04b914 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -37,6 +37,9 @@
 #define HF_INTERRUPT_INJECT     0xff0d
 #define HF_SHARE_MEMORY         0xff0e
 
+/* This matches what Trusty and its ATF module currently use. */
+#define HF_DEBUG_LOG            0xbd000000
+
 /* clang-format on */
 
 /**
@@ -244,6 +247,16 @@
 		       size);
 }
 
+/**
+ * Sends a character to the debug log for the VM.
+ *
+ * Returns 0 on success, or -1 if it failed for some reason.
+ */
+static inline int64_t hf_debug_log(char c)
+{
+	return hf_call(HF_DEBUG_LOG, c, 0, 0);
+}
+
 /** Obtains the Hafnium's version of the implemented SPCI specification. */
 static inline int64_t spci_version(void)
 {
diff --git a/src/api.c b/src/api.c
index c32446f..c8778cb 100644
--- a/src/api.c
+++ b/src/api.c
@@ -22,6 +22,7 @@
 #include "hf/check.h"
 #include "hf/dlog.h"
 #include "hf/mm.h"
+#include "hf/plat/console.h"
 #include "hf/spci_internal.h"
 #include "hf/spinlock.h"
 #include "hf/static_assert.h"
@@ -36,7 +37,7 @@
  * acquisition of locks held concurrently by the same physical CPU. Our current
  * ordering requirements are as follows:
  *
- * vm::lock -> vcpu::lock -> mm_stage1_lock
+ * vm::lock -> vcpu::lock -> mm_stage1_lock -> dlog sl
  *
  * Locks of the same kind require the lock of lowest address to be locked first,
  * see `sl_lock_both()`.
@@ -1655,3 +1656,20 @@
 	return (SPCI_VERSION_MAJOR << SPCI_VERSION_MAJOR_OFFSET) |
 	       SPCI_VERSION_MINOR;
 }
+
+int64_t api_debug_log(char c, struct vcpu *current)
+{
+	struct vm *vm = current->vm;
+	struct vm_locked vm_locked = vm_lock(vm);
+
+	if (c == '\n' || c == '\0' ||
+	    vm->log_buffer_length == sizeof(vm->log_buffer)) {
+		dlog_flush_vm_buffer(vm_locked);
+	} else {
+		vm->log_buffer[vm->log_buffer_length++] = c;
+	}
+
+	vm_unlock(&vm_locked);
+
+	return 0;
+}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index d84f7f1..c8e2d90 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -247,6 +247,23 @@
 	write_msr(hcr_el2, hcr_el2);
 }
 
+static bool smc_handler(struct vcpu *vcpu, uint32_t func, uintreg_t arg0,
+			uintreg_t arg1, uintreg_t arg2, uintreg_t *ret,
+			struct vcpu **next)
+{
+	if (psci_handler(vcpu, func, arg0, arg1, arg2, ret, next)) {
+		return true;
+	}
+
+	switch (func & ~SMCCC_CONVENTION_MASK) {
+	case HF_DEBUG_LOG:
+		*ret = api_debug_log(arg0, vcpu);
+		return true;
+	}
+
+	return false;
+}
+
 struct hvc_handler_return hvc_handler(uintreg_t arg0, uintreg_t arg1,
 				      uintreg_t arg2, uintreg_t arg3)
 {
@@ -329,6 +346,10 @@
 					 arg1 & 0xffffffff, current());
 		break;
 
+	case HF_DEBUG_LOG:
+		ret.user_ret = api_debug_log(arg1, current());
+		break;
+
 	default:
 		ret.user_ret = -1;
 	}
@@ -453,9 +474,9 @@
 		uintreg_t ret;
 		struct vcpu *next = NULL;
 
-		if (!psci_handler(vcpu, vcpu->regs.r[0], vcpu->regs.r[1],
-				  vcpu->regs.r[2], vcpu->regs.r[3], &ret,
-				  &next)) {
+		if (!smc_handler(vcpu, vcpu->regs.r[0], vcpu->regs.r[1],
+				 vcpu->regs.r[2], vcpu->regs.r[3], &ret,
+				 &next)) {
 			dlog("Unsupported SMC call: 0x%x\n", vcpu->regs.r[0]);
 			ret = PSCI_ERROR_NOT_SUPPORTED;
 		}
diff --git a/src/dlog.c b/src/dlog.c
index 080485e..453a597 100644
--- a/src/dlog.c
+++ b/src/dlog.c
@@ -22,6 +22,7 @@
 #include "hf/plat/console.h"
 #include "hf/spinlock.h"
 #include "hf/std.h"
+#include "hf/vm.h"
 
 /* Keep macro alignment */
 /* clang-format off */
@@ -39,6 +40,27 @@
 /* clang-format on */
 
 static bool dlog_lock_enabled = false;
+static struct spinlock sl = SPINLOCK_INIT;
+
+/**
+ * Takes the lock, if it is enabled.
+ */
+static void lock(void)
+{
+	if (dlog_lock_enabled) {
+		sl_lock(&sl);
+	}
+}
+
+/**
+ * Releases the lock, if it is enabled.
+ */
+static void unlock(void)
+{
+	if (dlog_lock_enabled) {
+		sl_unlock(&sl);
+	}
+}
 
 /**
  * Enables the lock protecting the serial device.
@@ -190,19 +212,38 @@
 }
 
 /**
+ * Send the contents of the given VM's log buffer to the log, preceded by the VM
+ * ID and followed by a newline.
+ */
+void dlog_flush_vm_buffer(struct vm_locked vm)
+{
+	lock();
+
+	print_raw_string("VM ");
+	print_num(vm.vm->id, 10, 0, 0);
+	print_raw_string(": ");
+
+	for (size_t i = 0; i < vm.vm->log_buffer_length; ++i) {
+		plat_console_putchar(vm.vm->log_buffer[i]);
+		vm.vm->log_buffer[i] = '\0';
+	}
+	vm.vm->log_buffer_length = 0;
+	plat_console_putchar('\n');
+
+	unlock();
+}
+
+/**
  * Same as "dlog", except that arguments are passed as a va_list
  */
 void vdlog(const char *fmt, va_list args)
 {
-	static struct spinlock sl = SPINLOCK_INIT;
 	const char *p;
 	size_t w;
 	int flags;
 	char buf[2];
 
-	if (dlog_lock_enabled) {
-		sl_lock(&sl);
-	}
+	lock();
 
 	for (p = fmt; *p; p++) {
 		switch (*p) {
@@ -302,9 +343,7 @@
 		}
 	}
 
-	if (dlog_lock_enabled) {
-		sl_unlock(&sl);
-	}
+	unlock();
 }
 
 /**