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/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};
+}