Implement FFA_CONSOLE_LOG interface

Implement FFA_CONSOLE_LOG interface for sending debug logs to the SPMC.
The commit also replaces the proprietary trace call by FFA_CONSOLE_LOG.

Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I5275470683e2674fdd6f5a61f9c5ab8dd71dcb11
diff --git a/components/common/trace/trace.c b/components/common/trace/trace.c
index bc7f0a9..0b8cb53 100644
--- a/components/common/trace/trace.c
+++ b/components/common/trace/trace.c
@@ -30,10 +30,18 @@
 
 	if (offset < sizeof(buffer)) {
 		va_start(ap, fmt);
-		vsnprintf(buffer + offset, sizeof(buffer) - offset, fmt, ap);
+		offset += vsnprintf(buffer + offset, sizeof(buffer) - offset, fmt, ap);
 		va_end(ap);
 	}
 
+	if (offset < sizeof(buffer) - 2) {
+		buffer[offset] = '\n';
+		buffer[offset + 1] = '\0';
+	} else {
+		buffer[sizeof(buffer) - 2] = '\n';
+		buffer[sizeof(buffer) - 1] = '\0';
+	}
+
 	trace_puts(buffer);
 }
 #endif
diff --git a/components/messaging/ffa/libsp/ffa.c b/components/messaging/ffa/libsp/ffa.c
index caacc79..6a51379 100644
--- a/components/messaging/ffa/libsp/ffa.c
+++ b/components/messaging/ffa/libsp/ffa.c
@@ -6,6 +6,7 @@
 #include <assert.h>            // for assert
 #include <stddef.h>            // for size_t
 #include <stdint.h>            // for uint32_t, uint16_t, uintptr_t, U
+#include <string.h>            // for memcpy
 #include "ffa_api.h"           // for FFA_OK, ffa_interrupt_handler, ffa_fea...
 #include "ffa_api_defines.h"   // for FFA_PARAM_MBZ, FFA_OK, FFA_ERROR, FFA_...
 #include "ffa_api_types.h"     // for ffa_result, ffa_direct_msg, ffa_uuid
@@ -525,3 +526,43 @@
 	assert(result.a0 == FFA_SUCCESS_32);
 	return FFA_OK;
 }
+
+ffa_result ffa_console_log_32(const char *message, size_t length)
+{
+	struct ffa_params result = {0};
+	uint32_t char_lists[6] = {0};
+
+	assert(length > 0 && length <= sizeof(char_lists));
+
+	memcpy(char_lists, message, MIN(length, sizeof(char_lists)));
+
+	ffa_svc(FFA_CONSOLE_LOG_32, length, char_lists[0], char_lists[1],
+		char_lists[2], char_lists[3], char_lists[4], char_lists[5],
+		&result);
+
+	if (result.a0 == FFA_ERROR)
+		return ffa_get_errorcode(&result);
+
+	assert(result.a0 == FFA_SUCCESS_32);
+	return FFA_OK;
+}
+
+ffa_result ffa_console_log_64(const char *message, size_t length)
+{
+	struct ffa_params result = {0};
+	uint64_t char_lists[6] = {0};
+
+	assert(length > 0 && length <= sizeof(char_lists));
+
+	memcpy(char_lists, message, MIN(length, sizeof(char_lists)));
+
+	ffa_svc(FFA_CONSOLE_LOG_64, length, char_lists[0], char_lists[1],
+		char_lists[2], char_lists[3], char_lists[4], char_lists[5],
+		&result);
+
+	if (result.a0 == FFA_ERROR)
+		return ffa_get_errorcode(&result);
+
+	assert(result.a0 == FFA_SUCCESS_32);
+	return FFA_OK;
+}
diff --git a/components/messaging/ffa/libsp/include/ffa_api.h b/components/messaging/ffa/libsp/include/ffa_api.h
index 4b7073b..e18e381 100644
--- a/components/messaging/ffa/libsp/include/ffa_api.h
+++ b/components/messaging/ffa/libsp/include/ffa_api.h
@@ -362,6 +362,26 @@
 			    uint32_t mem_perm);
 
 /**
+ * @brief 	Allow an entity to provide debug logging to the console. Uses
+ * 		32 bit registers to pass characters.
+ *
+ * @param message	Message characters
+ * @param length	Message length, max FFA_CONSOLE_LOG_32_MAX_LENGTH
+ * @return 		The FF-A error status code
+ */
+ffa_result ffa_console_log_32(const char *message, size_t length);
+
+/**
+ * @brief 	Allow an entity to provide debug logging to the console. Uses
+ * 		64 bit registers to pass characters.
+ *
+ * @param message	Message characters
+ * @param length	Message length, max FFA_CONSOLE_LOG_64_MAX_LENGTH
+ * @return 		The FF-A error status code
+ */
+ffa_result ffa_console_log_64(const char *message, size_t length);
+
+/**
  * @brief      Interrupt handler prototype. Must be implemented by another
  *             component.
  *
diff --git a/components/messaging/ffa/libsp/include/ffa_api_defines.h b/components/messaging/ffa/libsp/include/ffa_api_defines.h
index 17a5eda..f6e0212 100644
--- a/components/messaging/ffa/libsp/include/ffa_api_defines.h
+++ b/components/messaging/ffa/libsp/include/ffa_api_defines.h
@@ -57,6 +57,8 @@
 #define FFA_MEM_FRAG_TX			UINT32_C(0x8400007B)
 #define FFA_MEM_PERM_GET		UINT32_C(0x84000088)
 #define FFA_MEM_PERM_SET		UINT32_C(0x84000089)
+#define FFA_CONSOLE_LOG_32		UINT32_C(0x8400008A)
+#define FFA_CONSOLE_LOG_64		UINT32_C(0xC400008A)
 
 /* Utility macros */
 #define FFA_TO_32_BIT_FUNC(x)		((x) & (~UINT32_C(0x40000000)))
@@ -273,4 +275,8 @@
 
 #define FFA_MEM_PERM_RESERVED_MASK			GENMASK_32(31, 3)
 
+/* FFA_CONSOLE_LOG */
+#define FFA_CONSOLE_LOG_32_MAX_LENGTH			UINT32_C(24)
+#define FFA_CONSOLE_LOG_64_MAX_LENGTH			UINT32_C(48)
+
 #endif /* LIBSP_INCLUDE_FFA_API_DEFINES_H_ */
diff --git a/components/messaging/ffa/libsp/mock/mock_ffa_api.cpp b/components/messaging/ffa/libsp/mock/mock_ffa_api.cpp
index a01848c..38637cf 100644
--- a/components/messaging/ffa/libsp/mock/mock_ffa_api.cpp
+++ b/components/messaging/ffa/libsp/mock/mock_ffa_api.cpp
@@ -561,3 +561,39 @@
 		.withUnsignedIntParameter("mem_perm", mem_perm)
 		.returnIntValue();
 }
+
+void expect_ffa_console_log_32(const char *message, size_t length,
+			       ffa_result result)
+{
+	mock().expectOneCall("ffa_console_log_32")
+		.withStringParameter("message", message)
+		.withUnsignedIntParameter("length", length)
+		.andReturnValue(result);
+}
+
+ffa_result ffa_console_log_32(const char *message, size_t length)
+{
+	return mock()
+		.actualCall("ffa_console_log_32")
+		.withStringParameter("message", message)
+		.withUnsignedIntParameter("length", length)
+		.returnIntValue();
+}
+
+void expect_ffa_console_log_64(const char *message, size_t length,
+			       ffa_result result)
+{
+	mock().expectOneCall("ffa_console_log_64")
+		.withStringParameter("message", message)
+		.withUnsignedIntParameter("length", length)
+		.andReturnValue(result);
+}
+
+ffa_result ffa_console_log_64(const char *message, size_t length)
+{
+	return mock()
+		.actualCall("ffa_console_log_64")
+		.withStringParameter("message", message)
+		.withUnsignedIntParameter("length", length)
+		.returnIntValue();
+}
diff --git a/components/messaging/ffa/libsp/mock/mock_ffa_api.h b/components/messaging/ffa/libsp/mock/mock_ffa_api.h
index 4213ccb..9c3dbc8 100644
--- a/components/messaging/ffa/libsp/mock/mock_ffa_api.h
+++ b/components/messaging/ffa/libsp/mock/mock_ffa_api.h
@@ -98,4 +98,10 @@
 void expect_ffa_mem_perm_set(const void *base_address, uint32_t page_count,
 			     uint32_t mem_perm, ffa_result result);
 
+void expect_ffa_console_log_32(const char *message, size_t length,
+			       ffa_result result);
+
+void expect_ffa_console_log_64(const char *message, size_t length,
+			       ffa_result result);
+
 #endif /* FFA_LIBSP_TEST_MOCK_FFA_API_H_ */
diff --git a/components/messaging/ffa/libsp/mock/test/test_mock_ffa_api.cpp b/components/messaging/ffa/libsp/mock/test/test_mock_ffa_api.cpp
index c1cbdd6..8b793d3 100644
--- a/components/messaging/ffa/libsp/mock/test/test_mock_ffa_api.cpp
+++ b/components/messaging/ffa/libsp/mock/test/test_mock_ffa_api.cpp
@@ -345,3 +345,21 @@
 	expect_ffa_mem_perm_set(base_address, page_count, mem_perm, result);
 	LONGS_EQUAL(result, ffa_mem_perm_set(base_address, page_count, mem_perm));
 }
+
+TEST(mock_ffa_api, ffa_console_log_32)
+{
+	const char *message = "log message";
+	const size_t length = 11;
+
+	expect_ffa_console_log_32(message, length, result);
+	LONGS_EQUAL(result, ffa_console_log_32(message, length));
+}
+
+TEST(mock_ffa_api, ffa_console_log_64)
+{
+	const char *message = "log message";
+	const size_t length = 11;
+
+	expect_ffa_console_log_64(message, length, result);
+	LONGS_EQUAL(result, ffa_console_log_64(message, length));
+}
diff --git a/components/messaging/ffa/libsp/test/test_ffa_api.cpp b/components/messaging/ffa/libsp/test/test_ffa_api.cpp
index 6cca085..8395662 100644
--- a/components/messaging/ffa/libsp/test/test_ffa_api.cpp
+++ b/components/messaging/ffa/libsp/test/test_ffa_api.cpp
@@ -1507,4 +1507,115 @@
 	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
 		ffa_mem_perm_set(base_addr, page_count, mem_perm);
 	}
-}
\ No newline at end of file
+}
+
+TEST(ffa_api, ffa_console_log_32_zero)
+{
+	assert_environment_t assert_env;
+
+	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
+		ffa_console_log_32("", 0);
+	}
+}
+
+TEST(ffa_api, ffa_console_log_32_too_long)
+{
+	assert_environment_t assert_env;
+
+	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
+		ffa_console_log_32("", 25);
+	}
+}
+
+TEST(ffa_api, ffa_console_log_32_unknown_response)
+{
+	assert_environment_t assert_env;
+	const char *message = "0";
+	const size_t length = 1;
+
+	svc_result.a0 = 0x12345678;
+	expect_ffa_svc(0x8400008A, length, 0x30, 0, 0, 0, 0, 0, &svc_result);
+
+	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
+		ffa_console_log_32(message, length);
+	}
+}
+
+TEST(ffa_api, ffa_console_log_32_error)
+{
+	const char *message = "0";
+	const size_t length = 1;
+
+	setup_error_response(-1);
+	expect_ffa_svc(0x8400008A, length, 0x30, 0, 0, 0, 0, 0, &svc_result);
+	ffa_result result = ffa_console_log_32(message, length);
+	LONGS_EQUAL(-1, result);
+}
+
+TEST(ffa_api, ffa_console_log_32)
+{
+	const char *message = "0123456789abcdefghijklmn";
+	const size_t length = 24;
+
+	svc_result.a0 = 0x84000061;
+	expect_ffa_svc(0x8400008A, length, 0x33323130, 0x37363534, 0x62613938,
+		       0x66656463, 0x6A696867, 0x6E6D6C6B, &svc_result);
+	ffa_result result = ffa_console_log_32(message, length);
+	LONGS_EQUAL(0, result);
+}
+
+TEST(ffa_api, ffa_console_log_64_zero)
+{
+	assert_environment_t assert_env;
+
+	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
+		ffa_console_log_64("", 0);
+	}
+}
+
+TEST(ffa_api, ffa_console_log_64_too_long)
+{
+	assert_environment_t assert_env;
+
+	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
+		ffa_console_log_64("", 49);
+	}
+}
+
+TEST(ffa_api, ffa_console_log_64_unknown_response)
+{
+	assert_environment_t assert_env;
+	const char *message = "0";
+	const size_t length = 1;
+
+	svc_result.a0 = 0x12345678;
+	expect_ffa_svc(0xC400008A, length, 0x30, 0, 0, 0, 0, 0, &svc_result);
+
+	if (SETUP_ASSERT_ENVIRONMENT(assert_env)) {
+		ffa_console_log_64(message, length);
+	}
+}
+
+TEST(ffa_api, ffa_console_log_64_error)
+{
+	const char *message = "0";
+	const size_t length = 1;
+
+	setup_error_response(-1);
+	expect_ffa_svc(0xC400008A, length, 0x30, 0, 0, 0, 0, 0, &svc_result);
+	ffa_result result = ffa_console_log_64(message, length);
+	LONGS_EQUAL(-1, result);
+}
+
+TEST(ffa_api, ffa_console_log_64)
+{
+	const char *message = "0123456789abcdefghijklmnopqrstuvwxyz0123456789ab";
+	const size_t length = 48;
+
+	svc_result.a0 = 0x84000061;
+	expect_ffa_svc(0xC400008A, length,  0x3736353433323130,
+		       0x6665646362613938, 0x6E6D6C6B6A696867,
+		       0x767574737271706F, 0x333231307A797877,
+		       0x6261393837363534, &svc_result);
+	ffa_console_log_64(message, length);
+}
diff --git a/environments/opteesp/sp_trace.c b/environments/opteesp/sp_trace.c
index 6ac0ddc..586b41c 100644
--- a/environments/opteesp/sp_trace.c
+++ b/environments/opteesp/sp_trace.c
@@ -4,15 +4,20 @@
  */
 
 #include "trace.h"
-#include "ffa_internal_api.h"
+#include "ffa_api.h"
+#include <string.h>
 
 #if TRACE_LEVEL >= TRACE_LEVEL_ERROR
 
 void trace_puts(const char *str)
 {
-	struct ffa_params resp;
+	size_t length = strlen(str);
+	size_t i = 0;
 
-	ffa_svc(0xdeadbeef, (uintptr_t)str, 0, 0, 0, 0, 0, 0, &resp);
+	for (i = 0; i < length; i += FFA_CONSOLE_LOG_64_MAX_LENGTH) {
+		ffa_console_log_64(&str[i], MIN(FFA_CONSOLE_LOG_64_MAX_LENGTH,
+						length - i));
+	}
 }
 
 #endif  /* TRACE_LEVEL >= TRACE_LEVEL_ERROR */
diff --git a/environments/sp/sp_trace.c b/environments/sp/sp_trace.c
index 606e45d..7e8cb9c 100644
--- a/environments/sp/sp_trace.c
+++ b/environments/sp/sp_trace.c
@@ -4,15 +4,20 @@
  */
 
 #include "trace.h"
-#include "ffa_internal_api.h"
+#include "ffa_api.h"
+#include <string.h>
 
 #if TRACE_LEVEL >= TRACE_LEVEL_ERROR
 
 void trace_puts(const char *str)
 {
-	struct ffa_params resp;
+	size_t length = strlen(str);
+	size_t i = 0;
 
-	ffa_svc(0xdeadbeef, (uintptr_t)str, 0, 0, 0, 0, 0, 0, &resp);
+	for (i = 0; i < length; i += FFA_CONSOLE_LOG_64_MAX_LENGTH) {
+		ffa_console_log_64(&str[i], MIN(FFA_CONSOLE_LOG_64_MAX_LENGTH,
+						length - i));
+	}
 }
 
 #endif  /* TRACE_LEVEL >= TRACE_LEVEL_ERROR */