Implement RPMB client

Implement RPMB service client for accessing remote RPMB backends via the
RPC layer.

Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I4b4c6fd808864903653ed36620b51267c352d3d8
diff --git a/components/service/rpmb/client/component.cmake b/components/service/rpmb/client/component.cmake
new file mode 100644
index 0000000..2a04a3b
--- /dev/null
+++ b/components/service/rpmb/client/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2023, 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}/rpmb_client.c"
+)
diff --git a/components/service/rpmb/client/rpmb_client.c b/components/service/rpmb/client/rpmb_client.c
new file mode 100644
index 0000000..56bc961
--- /dev/null
+++ b/components/service/rpmb/client/rpmb_client.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "rpmb_client.h"
+#include "protocols/service/rpmb/packed-c/rpmb_proto.h"
+#include "util.h"
+#include <string.h>
+
+static psa_status_t rpmb_client_get_dev_info(void *context, uint32_t dev_id,
+					     struct rpmb_dev_info *dev_info)
+{
+	struct rpmb_client *this_context = (struct rpmb_client *)context;
+	struct rpmb_request_get_dev_info *request_desc = NULL;
+	struct rpmb_response_get_dev_info *response_desc = NULL;
+	size_t response_length = 0;
+	rpc_call_handle handle = 0;
+	rpc_status_t rpc_status = RPC_ERROR_INTERNAL;
+	service_status_t service_status = 0;
+	psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR;
+
+	handle = rpc_caller_session_begin(this_context->session, (uint8_t **)&request_desc,
+					  sizeof(*request_desc), sizeof(*response_desc));
+	if (!handle)
+		return PSA_ERROR_GENERIC_ERROR;
+
+	request_desc->dev_id = dev_id;
+
+	rpc_status = rpc_caller_session_invoke(handle, TS_RPMB_OPCODE_GET_DEV_INFO,
+					       (uint8_t **)&response_desc, &response_length,
+					       &service_status);
+	if (rpc_status != RPC_SUCCESS || response_length != sizeof(*response_desc))
+		goto session_end;
+
+	psa_status = service_status;
+
+	if (psa_status == PSA_SUCCESS)
+		*dev_info = response_desc->dev_info;
+
+session_end:
+	rpc_status = rpc_caller_session_end(handle);
+	if (psa_status == PSA_SUCCESS && rpc_status != RPC_SUCCESS)
+		psa_status = PSA_ERROR_GENERIC_ERROR;
+
+	return psa_status;
+}
+
+static bool calculate_size(size_t header_size, uint32_t frame_count, size_t *frames_size,
+			   size_t *total_size)
+{
+	/* Calculating [data frame count] * [data frame size] + [header size] */
+
+	if (MUL_OVERFLOW(frame_count, sizeof(struct rpmb_data_frame), frames_size))
+		return false;
+
+	if (ADD_OVERFLOW(*frames_size, header_size, total_size))
+		return false;
+
+	return true;
+}
+
+static psa_status_t rpmb_client_data_request(void *context, uint32_t dev_id,
+					     const struct rpmb_data_frame *request_frames,
+					     size_t request_frame_count,
+					     struct rpmb_data_frame *response_frames,
+					     size_t *response_frame_count)
+{
+	struct rpmb_client *this_context = (struct rpmb_client *)context;
+	/* Request */
+	struct rpmb_request_data_request  *request_desc = NULL;
+	size_t request_frames_length = 0;
+	size_t request_length = 0;
+	/* Response */
+	struct rpmb_response_data_request *response_desc = NULL;
+	size_t response_frames_max_length = 0;
+	size_t response_frames_length = 0;
+	size_t response_max_length = 0;
+	size_t response_length = 0;
+	rpc_call_handle handle = 0;
+	/* Status */
+	rpc_status_t rpc_status = RPC_ERROR_INTERNAL;
+	service_status_t service_status = 0;
+	psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR;
+
+	/* Calculating request lengths */
+	if (!calculate_size(sizeof(*request_desc), request_frame_count,
+			    &request_frames_length, &request_length))
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	/* Calculating response lengths */
+	if (!calculate_size(sizeof(*response_desc), *response_frame_count,
+			    &response_frames_max_length, &response_max_length))
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	handle = rpc_caller_session_begin(this_context->session, (uint8_t **)&request_desc,
+					  request_length, response_max_length);
+	if (!handle)
+		goto out;
+
+	request_desc->dev_id = dev_id;
+	request_desc->request_frame_count = request_frame_count;
+	request_desc->max_response_frame_count = *response_frame_count;
+	memcpy(request_desc->request_frames, request_frames, request_frames_length);
+
+	rpc_status = rpc_caller_session_invoke(handle, TS_RPMB_OPCODE_DATA_REQUEST,
+					       (uint8_t **)&response_desc, &response_length,
+					       &service_status);
+	if (rpc_status != RPC_SUCCESS)
+		goto session_end;
+
+	/* Checking if the response length is too small or large */
+	if (response_length < sizeof(*response_desc) || response_length > response_max_length)
+		goto session_end;
+
+	/* Checking if the response data frames have the expected length */
+	if (!calculate_size(sizeof(*response_desc), response_desc->response_frame_count,
+			    &response_frames_length, &response_max_length))
+		goto session_end;
+
+	if (response_length != response_max_length)
+		goto session_end;
+
+	psa_status = service_status;
+
+	if (psa_status == PSA_SUCCESS) {
+		memcpy(response_frames, response_desc->response_frames, response_frames_length);
+		*response_frame_count = response_desc->response_frame_count;
+	} else {
+		*response_frame_count = 0;
+	}
+
+session_end:
+	rpc_status = rpc_caller_session_end(handle);
+	if (psa_status == PSA_SUCCESS && rpc_status != RPC_SUCCESS)
+		psa_status = PSA_ERROR_GENERIC_ERROR;
+
+out:
+	return psa_status;
+}
+
+struct rpmb_backend *rpmb_client_init(struct rpmb_client *context,
+				      struct rpc_caller_session *session)
+{
+	static const struct rpmb_backend_interface interface = {
+		rpmb_client_get_dev_info,
+		rpmb_client_data_request
+	};
+
+	if (!context || !session)
+		return NULL;
+
+	context->backend.context = context;
+	context->backend.interface = &interface;
+	context->session = session;
+
+	return &context->backend;
+}
+
+void rpmb_client_deinit(struct rpmb_client *context)
+{
+	(void)context;
+}
diff --git a/components/service/rpmb/client/rpmb_client.h b/components/service/rpmb/client/rpmb_client.h
new file mode 100644
index 0000000..466ba78
--- /dev/null
+++ b/components/service/rpmb/client/rpmb_client.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2023, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RPMB_CLIENT_H_
+#define RPMB_CLIENT_H_
+
+#include "components/service/rpmb/backend/rpmb_backend.h"
+#include "components/rpc/common/caller/rpc_caller_session.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief RPMB client
+ *
+ * The RPMB client provides and RPMB backend via accessing a remove backend through RPC.
+ */
+struct rpmb_client {
+	struct rpmb_backend backend;
+	struct rpc_caller_session *session;
+};
+
+/**
+ * \brief Initialize RPMB client
+ *
+ * \param context[in]	RPMB client context
+ * \param session[in]	RPC caller session
+ * \return struct rpmb_backend* RPMB backend or NULL on error
+ */
+struct rpmb_backend *rpmb_client_init(struct rpmb_client *context,
+				      struct rpc_caller_session *session);
+
+/**
+ * \brief Deinitialize RPMB client
+ *
+ * \param context[in]	RPMB client context
+ */
+void rpmb_client_deinit(struct rpmb_client *context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RPMB_CLIENT_H_ */