feat(console_log): add FFA_CONSOLE_LOG ABI

Add implementation-defined FFA_CONSOLE_LOG_32/64 ABIs.

Input parameters:
w0/x0 - FFA_CONSOLE_LOG_32/64
w1/x1 - Character count
w2/x2-w7/x7 - 24 or 48 characters depending upon whether a SMC32 or
SMC64 FID was used.

Output parameters in case of success:
w0/x0 - FFA_SUCCESS

Output parameters in case of error:
w0/x0 - FFA_ERROR
w2/x2 - NOT_SUPPORTED: ABI is not implemented
        INVALID_PARAMETERS: Parameters are incorrectly encoded

Signed-off-by: Maksims Svecovs <maksims.svecovs@arm.com>
Change-Id: Ic38d354d736cc7691010ab6d7dbcc42a69ef7c61
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 7883434..84bd003 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -129,3 +129,6 @@
 struct ffa_value api_ffa_mem_perm_get(vaddr_t base_addr, struct vcpu *current);
 struct ffa_value api_ffa_mem_perm_set(vaddr_t base_addr, uint32_t page_count,
 				      uint32_t mem_perm, struct vcpu *current);
+
+struct ffa_value api_ffa_console_log(const struct ffa_value args,
+				     struct vcpu *current);
diff --git a/inc/vmapi/hf/ffa.h b/inc/vmapi/hf/ffa.h
index 50abec8..539b489 100644
--- a/inc/vmapi/hf/ffa.h
+++ b/inc/vmapi/hf/ffa.h
@@ -80,6 +80,10 @@
 #define FFA_MEM_PERM_GET_64                 0xC4000088
 #define FFA_MEM_PERM_SET_64                 0xC4000089
 
+/* Implementation-defined ABIs. */
+#define FFA_CONSOLE_LOG_32                  0x8400008A
+#define FFA_CONSOLE_LOG_64                  0xC400008A
+
 /* FF-A error codes. */
 #define FFA_NOT_SUPPORTED      INT32_C(-1)
 #define FFA_INVALID_PARAMETERS INT32_C(-2)
diff --git a/src/api.c b/src/api.c
index 7424b24..20bdd17 100644
--- a/src/api.c
+++ b/src/api.c
@@ -3562,3 +3562,79 @@
 
 	return ret;
 }
+
+/**
+ * Helper function for FFA_CONSOLE_LOG ABI.
+ * Writes number of characters to a given VM buffer.
+ */
+static rsize_t arg_to_char_helper(struct vm_locked from_locked,
+				  const uint64_t src, rsize_t src_size,
+				  rsize_t to_write)
+{
+	bool flush = false;
+	char c;
+	rsize_t size = src_size < to_write ? src_size : to_write;
+	rsize_t written = 0;
+
+	if (size == 0) {
+		return 0;
+	}
+
+	while (written < size) {
+		c = ((char *)&src)[written++];
+		if (c == '\n' || c == '\0') {
+			flush = true;
+		} else {
+			from_locked.vm->log_buffer
+				[from_locked.vm->log_buffer_length++] = c;
+			flush = (from_locked.vm->log_buffer_length ==
+				 LOG_BUFFER_SIZE);
+		}
+
+		if (flush) {
+			dlog_flush_vm_buffer(from_locked.vm->id,
+					     from_locked.vm->log_buffer,
+					     from_locked.vm->log_buffer_length);
+			from_locked.vm->log_buffer_length = 0;
+		}
+	}
+
+	return written;
+}
+
+/**
+ * Implements FFA_CONSOLE_LOG buffered logging.
+ */
+struct ffa_value api_ffa_console_log(const struct ffa_value args,
+				     struct vcpu *current)
+{
+	struct vm *vm = current->vm;
+	struct vm_locked vm_locked;
+	size_t chars_in_param = args.func == FFA_CONSOLE_LOG_32
+					? sizeof(uint32_t)
+					: sizeof(uint64_t);
+	size_t total_to_write = args.arg1;
+
+	if (total_to_write == 0 || total_to_write > chars_in_param * 6) {
+		return ffa_error(FFA_INVALID_PARAMETERS);
+	}
+
+	vm_locked = vm_lock(vm);
+
+	total_to_write -= arg_to_char_helper(vm_locked, args.arg2,
+					     chars_in_param, total_to_write);
+	total_to_write -= arg_to_char_helper(vm_locked, args.arg3,
+					     chars_in_param, total_to_write);
+	total_to_write -= arg_to_char_helper(vm_locked, args.arg4,
+					     chars_in_param, total_to_write);
+	total_to_write -= arg_to_char_helper(vm_locked, args.arg5,
+					     chars_in_param, total_to_write);
+	total_to_write -= arg_to_char_helper(vm_locked, args.arg6,
+					     chars_in_param, total_to_write);
+	arg_to_char_helper(vm_locked, args.arg7, chars_in_param,
+			   total_to_write);
+
+	vm_unlock(&vm_locked);
+
+	return (struct ffa_value){.func = FFA_SUCCESS_32};
+}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 4bd8a3b..fc3c324 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -631,6 +631,10 @@
 	case FFA_INTERRUPT_32:
 		*args = plat_ffa_delegate_ffa_interrupt(current, next);
 		return true;
+	case FFA_CONSOLE_LOG_32:
+	case FFA_CONSOLE_LOG_64:
+		*args = api_ffa_console_log(*args, current);
+		return true;
 	}
 
 	return false;