Add log client component

Adding a client and a related factory component that will be used
by the SPs to use the logging SP.
Extending trace functions with the configurable option to log using
the logging SP. In case logging with the logging SP fails,
the FFA_CONSOLE_LOG mechanism will be used.

Signed-off-by: Gabor Toth <gabor.toth2@arm.com>
Signed-off-by: Gabor Ambrus <gabor.ambrus@arm.com>
Change-Id: I1c03d145faa8f27c3e5878de0f6d28bec23ca9af
diff --git a/components/common/trace/include/trace.h b/components/common/trace/include/trace.h
index f89411b..d9b6c07 100644
--- a/components/common/trace/include/trace.h
+++ b/components/common/trace/include/trace.h
@@ -28,6 +28,7 @@
 		}								\
 	} while (0)
 
+extern void (*trace_puts_interface)(const char *str);
 void trace_puts(const char *str);
 void trace_printf(const char *func, int line, int level, const char *fmt, ...) __printf(4, 5);
 
diff --git a/components/common/trace/trace.c b/components/common/trace/trace.c
index 0b8cb53..372226b 100644
--- a/components/common/trace/trace.c
+++ b/components/common/trace/trace.c
@@ -12,6 +12,8 @@
 #error TRACE_PREFIX must be defined
 #endif /* TRACE_PREFIX */
 
+void (*trace_puts_interface)(const char *str) = &trace_puts;
+
 void trace_printf(const char *func, int line, int level, const char *fmt, ...)
 {
 	char buffer[256];
@@ -42,6 +44,6 @@
 		buffer[sizeof(buffer) - 1] = '\0';
 	}
 
-	trace_puts(buffer);
+	(*trace_puts_interface)(buffer);
 }
 #endif
diff --git a/components/service/log/client/component.cmake b/components/service/log/client/component.cmake
new file mode 100644
index 0000000..5d2ad62
--- /dev/null
+++ b/components/service/log/client/component.cmake
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/log_client.c"
+	)
+
+
+target_include_directories(${TGT}
+	 PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}"
+	)
diff --git a/components/service/log/client/log_client.c b/components/service/log/client/log_client.c
new file mode 100644
index 0000000..21a52bf
--- /dev/null
+++ b/components/service/log/client/log_client.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include "log_client.h"
+
+#include <string.h>
+
+#include "components/service/log/factory/log_factory.h"
+#include "protocols/rpc/common/packed-c/status.h"
+#include "protocols/service/log/packed-c/log_proto.h"
+#include "rpc_caller_session.h"
+#include "util.h"
+#include "trace.h"
+#include "ffa_api.h"
+
+/*
+ * Custom logging function, which sends the log message to
+ * the logging backend, but can fall back to the default
+ * logging interface of the system.
+ */
+static void log_client_trace_puts(const char *str)
+{
+	struct log_backend *log_backend;
+	log_status_t log_status = LOG_STATUS_GENERIC_ERROR;
+
+	/* log message using logging sp */
+	log_backend = log_factory_get_backend_instance();
+
+	/* in case logging to sp fails we still want to log it with FFA */
+	if (log_backend) {
+		log_status = log_backend->interface->puts(log_backend->context, str);
+		if (log_status == LOG_STATUS_SUCCESS)
+			return;
+	}
+
+	/* log message using FFA CONSOLE LOG */
+	trace_puts(str);
+}
+
+/*
+ * Client function for sending string to Logging SP.
+ */
+log_status_t log_client_puts(void *context, const char *msg)
+{
+	struct log_client *log_client = (struct log_client *)context;
+	uint8_t *request = NULL;
+	uint8_t *response = NULL;
+	size_t request_length = 0;
+	size_t response_length = 0;
+	size_t msg_length = 0;
+	struct log_request *request_desc = NULL;
+	rpc_call_handle handle = 0;
+	rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+	log_status_t log_status = LOG_STATUS_GENERIC_ERROR;
+
+	service_status_t service_status = LOG_STATUS_SUCCESS;
+
+	if (log_client->client.session == NULL)
+		return LOG_STATUS_GENERIC_ERROR;
+
+	/* Validating input parameters */
+	if (msg == NULL)
+		return LOG_STATUS_INVALID_PARAMETER;
+
+	msg_length = strlen(msg);
+
+	/* Add one for null termination */
+	if (ADD_OVERFLOW(msg_length, 1, &msg_length))
+		return LOG_STATUS_INVALID_PARAMETER;
+
+	if (ADD_OVERFLOW(sizeof(*request_desc), msg_length, &request_length))
+		return LOG_STATUS_INVALID_PARAMETER;
+
+	/* RPC call */
+	handle = rpc_caller_session_begin(log_client->client.session, &request, request_length, 0);
+	if (handle) {
+		request_desc = (struct log_request *)request;
+		memcpy(&request_desc->msg, msg, msg_length);
+		request_desc->msg_length = msg_length;
+
+		rpc_status = rpc_caller_session_invoke(handle, TS_LOG_OPCODE_PUTS, &response,
+						       &response_length, &service_status);
+
+		if (rpc_status == PSA_SUCCESS)
+			log_status = service_status;
+
+		rpc_caller_session_end(handle);
+	}
+
+	return log_status;
+}
+
+/*
+ * Client initialization function.
+ */
+struct log_backend *log_client_init(struct log_client *context, struct rpc_caller_session *session)
+{
+	service_client_init(&context->client, session);
+
+	static const struct log_backend_interface interface = { log_client_puts };
+
+	context->backend.context = context;
+	context->backend.interface = &interface;
+
+	trace_puts_interface = &log_client_trace_puts;
+
+	return &context->backend;
+}
diff --git a/components/service/log/client/log_client.h b/components/service/log/client/log_client.h
new file mode 100644
index 0000000..aa867a8
--- /dev/null
+++ b/components/service/log/client/log_client.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef LOG_CLIENT_H
+#define LOG_CLIENT_H
+
+#include <service/common/client/service_client.h>
+#include <stdbool.h>
+
+#include "components/service/log/backend/log_backend.h"
+#include "components/service/log/common/log_status.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief	Log client instance
+ */
+struct log_client {
+	struct log_backend backend;
+	struct service_client client;
+};
+
+/**
+ * @brief	Initialize a log client
+ *
+ * A log client is a log backend that makes RPC calls
+ * to a remote log provider.
+ *
+ * @param[in]  context	Instance data
+ * @param[in]  rpc_caller RPC caller instance
+ *
+ *
+ * @return	Pointer to inialized log backend or NULL on failure
+ */
+struct log_backend *log_client_init(struct log_client *context, struct rpc_caller_session *session);
+
+/**
+ * @brief	  Deinitialize a log client
+ *
+ * @param[in]  context   Instance data
+ */
+void log_client_deinit(struct log_client *context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LOG_CLIENT_H */
diff --git a/components/service/log/factory/component.cmake b/components/service/log/factory/component.cmake
new file mode 100644
index 0000000..372cd4d
--- /dev/null
+++ b/components/service/log/factory/component.cmake
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+	message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/log_factory.c"
+	)
+
+
+target_include_directories(${TGT}
+	 PRIVATE
+		"${CMAKE_CURRENT_LIST_DIR}"
+	)
diff --git a/components/service/log/factory/log_factory.c b/components/service/log/factory/log_factory.c
new file mode 100644
index 0000000..56ad1a0
--- /dev/null
+++ b/components/service/log/factory/log_factory.c
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include "service/log/factory/log_factory.h"
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "components/common/trace/include/trace.h"
+#include "components/rpc/common/caller/rpc_caller.h"
+#include "components/rpc/common/caller/rpc_caller_session.h"
+#include "components/rpc/ts_rpc/caller/sp/ts_rpc_caller_sp.h"
+#include "components/service/log/provider/log_uuid.h"
+#include "protocols/rpc/common/packed-c/status.h"
+#include "service/log/client/log_client.h"
+#include "sp_discovery.h"
+
+/**
+ * A log factory that creates log backends that is used
+ * to access logging SP from a separate SP.
+ */
+struct logger {
+	struct log_client client;
+	struct rpc_caller_interface caller;
+	struct rpc_caller_session session;
+	bool in_use;
+};
+
+/* Only supports construction of a single instance */
+static struct logger backend_instance = { .in_use = false };
+
+static const struct rpc_uuid logging_service_uuid = { .uuid = TS_LOG_SERVICE_UUID };
+
+/*
+ * Log factory create that is included in the code for other SP's
+ */
+struct log_backend *log_factory_create(void)
+{
+	struct logger *new_backend = &backend_instance;
+	struct log_backend *result = NULL;
+	rpc_status_t rpc_status = RPC_ERROR_INTERNAL;
+
+	if (new_backend->in_use)
+		return NULL;
+
+	rpc_status = ts_rpc_caller_sp_init(&new_backend->caller);
+	if (rpc_status != RPC_SUCCESS)
+		return NULL;
+
+	rpc_status = rpc_caller_session_find_and_open(&new_backend->session, &new_backend->caller,
+						      &logging_service_uuid, 4096);
+	if (rpc_status != RPC_SUCCESS) {
+		(void)ts_rpc_caller_sp_deinit(&new_backend->caller);
+		return NULL;
+	}
+
+	result = log_client_init(&new_backend->client, &new_backend->session);
+	if (!result) {
+		(void)ts_rpc_caller_sp_deinit(&new_backend->caller);
+		return NULL;
+	}
+
+	new_backend->in_use = (result != NULL);
+
+	return result;
+}
+
+/*
+ * Returns the log_backend instance if log_factory_create
+ * run successfully, otherwise NULL.
+ */
+struct log_backend *log_factory_get_backend_instance(void)
+{
+	if (backend_instance.in_use)
+		return &backend_instance.client.backend;
+
+	return NULL;
+}
+
+/*
+ * Remove the log factory
+ */
+void log_factory_destroy(struct logger *backend)
+{
+	if (backend) {
+		rpc_caller_session_close(&backend_instance.session);
+		ts_rpc_caller_sp_deinit(&backend_instance.caller);
+		backend_instance.in_use = false;
+	}
+}
diff --git a/components/service/log/factory/log_factory.h b/components/service/log/factory/log_factory.h
new file mode 100644
index 0000000..5456993
--- /dev/null
+++ b/components/service/log/factory/log_factory.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef LOG_FACTORY_H
+#define LOG_FACTORY_H
+
+#include <stdbool.h>
+
+#include "components/service/log/backend/log_backend.h"
+#include "components/service/log/common/log_status.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct logger;
+
+struct log_backend *log_factory_create(void);
+struct log_backend *log_factory_get_backend_instance(void);
+void log_factory_destroy(struct logger *backend);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LOG_FACTORY_H */