Add HVC/SMC call for debug logging.
Bug: 115484857
Change-Id: I253adf03ebde97d4b620be9d3f2cc05f5265f45d
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();
}
/**