Add RPC caller and RPC caller session components
Change rpc_caller_interface to follow the new RPC protocol's primitives.
Introduce rpc_caller_session as a session layer built on top of the
rpc_caller_interface. An opened session is tied to an interface of an
RPC endpoint.
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: Ibd75998d6a9d511b4adc79274cce34b59d9730f8
diff --git a/components/rpc/common/caller/component.cmake b/components/rpc/common/caller/component.cmake
index 9cb5138..0c8934a 100644
--- a/components/rpc/common/caller/component.cmake
+++ b/components/rpc/common/caller/component.cmake
@@ -8,6 +8,16 @@
message(FATAL_ERROR "mandatory parameter TGT is not defined.")
endif()
+target_include_directories(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}"
+)
+
+set_property(TARGET ${TGT} APPEND PROPERTY PUBLIC_HEADER
+ "${CMAKE_CURRENT_LIST_DIR}/rpc_caller_session.h"
+ "${CMAKE_CURRENT_LIST_DIR}/rpc_caller.h"
+ )
+
target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/rpc_caller_session.c"
"${CMAKE_CURRENT_LIST_DIR}/rpc_caller.c"
)
diff --git a/components/rpc/common/caller/rpc_caller.c b/components/rpc/common/caller/rpc_caller.c
index 2dceabe..4af9d8c 100644
--- a/components/rpc/common/caller/rpc_caller.c
+++ b/components/rpc/common/caller/rpc_caller.c
@@ -1,39 +1,63 @@
/*
- * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
-#include <rpc_caller.h>
-#include <stdint.h>
-#include <protocols/rpc/common/packed-c/encoding.h>
+#include "rpc_caller.h"
-void rpc_caller_init(struct rpc_caller *s, void *context)
+rpc_status_t rpc_caller_open_session(struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid,
+ uint16_t endpoint_id)
{
- s->context = context;
+ if (!caller)
+ return RPC_ERROR_INVALID_VALUE;
- /* The default encoding scheme - may be overridden by a client */
- s->encoding = TS_RPC_ENCODING_PACKED_C;
+ return caller->open_session(caller->context, service_uuid, endpoint_id);
}
-void rpc_caller_set_encoding_scheme(struct rpc_caller *s, uint32_t encoding)
+rpc_status_t rpc_caller_find_and_open_session(struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid)
{
- s->encoding = encoding;
+ if (!caller)
+ return RPC_ERROR_INVALID_VALUE;
+
+ return caller->find_and_open_session(caller->context, service_uuid);
}
-rpc_call_handle rpc_caller_begin(struct rpc_caller *s,
- uint8_t **req_buf, size_t req_len)
+rpc_status_t rpc_caller_close_session(struct rpc_caller_interface *caller)
{
- return s->call_begin(s->context, req_buf, req_len);
+ if (!caller)
+ return RPC_ERROR_INVALID_VALUE;
+
+ return caller->close_session(caller->context);
}
-rpc_status_t rpc_caller_invoke(struct rpc_caller *s, rpc_call_handle handle,
- uint32_t opcode, rpc_opstatus_t *opstatus, uint8_t **resp_buf, size_t *resp_len)
+rpc_status_t rpc_caller_create_shared_memory(struct rpc_caller_interface *caller, size_t length,
+ struct rpc_caller_shared_memory *shared_memory)
{
- return s->call_invoke(s->context, handle, opcode, opstatus, resp_buf, resp_len);
+ if (!caller)
+ return RPC_ERROR_INVALID_VALUE;
+
+ return caller->create_shared_memory(caller->context, length, shared_memory);
}
-void rpc_caller_end(struct rpc_caller *s, rpc_call_handle handle)
+rpc_status_t rpc_caller_release_shared_memory(struct rpc_caller_interface *caller,
+ struct rpc_caller_shared_memory *shared_memory)
{
- s->call_end(s->context, handle);
+ if (!caller)
+ return RPC_ERROR_INVALID_VALUE;
+
+ return caller->release_shared_memory(caller->context, shared_memory);
+}
+
+rpc_status_t rpc_caller_call(struct rpc_caller_interface *caller, uint16_t opcode,
+ struct rpc_caller_shared_memory *shared_memory, size_t request_length,
+ size_t *response_length, service_status_t *service_status)
+{
+ if (!caller)
+ return RPC_ERROR_INVALID_VALUE;
+
+ return caller->call(caller->context, opcode, shared_memory, request_length,
+ response_length, service_status);
}
diff --git a/components/rpc/common/caller/rpc_caller.h b/components/rpc/common/caller/rpc_caller.h
new file mode 100644
index 0000000..9581fd0
--- /dev/null
+++ b/components/rpc/common/caller/rpc_caller.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RPC_CALLER_H
+#define RPC_CALLER_H
+
+#include "rpc_status.h"
+#include "rpc_uuid.h"
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * The rpc_caller public interface may be exported as a public interface to
+ * a shared library.
+ */
+#ifdef EXPORT_PUBLIC_INTERFACE_RPC_CALLER
+#define RPC_CALLER_EXPORTED __attribute__((__visibility__("default")))
+#else
+#define RPC_CALLER_EXPORTED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Shared memory descriptor of an RPC caller
+ *
+ */
+struct rpc_caller_shared_memory {
+ uint64_t id; /** Implementation defined ID of the shared memory */
+ void *buffer; /** Address of the shared memory (virtual) */
+ size_t size; /** Size of the shared memory */
+};
+
+struct rpc_caller_interface {
+ void *context;
+
+ rpc_status_t (*open_session)(void *context, const struct rpc_uuid *service_uuid,
+ uint16_t endpoint_id);
+ rpc_status_t (*find_and_open_session)(void *context, const struct rpc_uuid *service_uuid);
+ rpc_status_t (*close_session)(void *context);
+
+ rpc_status_t (*create_shared_memory)(void *context, size_t size,
+ struct rpc_caller_shared_memory *shared_memory);
+ rpc_status_t (*release_shared_memory)(void *context,
+ struct rpc_caller_shared_memory *shared_memory);
+
+ rpc_status_t (*call)(void *context, uint16_t opcode,
+ struct rpc_caller_shared_memory *shared_memory, size_t request_length,
+ size_t *response_length, service_status_t *service_status);
+};
+
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_open_session(struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid,
+ uint16_t endpoint_id);
+
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_find_and_open_session(struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid);
+
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_close_session(struct rpc_caller_interface *caller);
+
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_create_shared_memory(struct rpc_caller_interface *caller, size_t size,
+ struct rpc_caller_shared_memory *shared_memory);
+
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_release_shared_memory(struct rpc_caller_interface *caller,
+ struct rpc_caller_shared_memory *shared_memory);
+
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_call(struct rpc_caller_interface *caller, uint16_t opcode,
+ struct rpc_caller_shared_memory *shared_memory, size_t request_length,
+ size_t *response_length, service_status_t *service_status);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RPC_CALLER_H */
diff --git a/components/rpc/common/caller/rpc_caller_session.c b/components/rpc/common/caller/rpc_caller_session.c
new file mode 100644
index 0000000..d72a318
--- /dev/null
+++ b/components/rpc/common/caller/rpc_caller_session.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "rpc_caller_session.h"
+#include "util.h"
+#include <string.h>
+
+static rpc_status_t initalize_shared_memory(struct rpc_caller_session *session,
+ struct rpc_caller_interface *caller,
+ size_t shared_memory_size)
+{
+ if (shared_memory_size) {
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+
+ status = rpc_caller_create_shared_memory(caller, shared_memory_size,
+ &session->shared_memory);
+ if (status) {
+ rpc_caller_close_session(caller);
+ return status;
+ }
+
+ session->shared_memory_policy = alloc_for_session;
+ } else {
+ session->shared_memory = (struct rpc_caller_shared_memory){ 0 };
+ session->shared_memory_policy = alloc_for_each_call;
+ }
+
+ return RPC_SUCCESS;
+}
+
+rpc_status_t rpc_caller_session_open(struct rpc_caller_session *session,
+ struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid,
+ uint16_t endpoint_id,
+ size_t shared_memory_size)
+{
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+
+ if (!session || !caller || !service_uuid)
+ return RPC_ERROR_INVALID_VALUE;
+
+ status = rpc_caller_open_session(caller, service_uuid, endpoint_id);
+ if (status)
+ return status;
+
+ status = initalize_shared_memory(session, caller, shared_memory_size);
+ if (status)
+ return status;
+
+ session->caller = caller;
+ session->is_call_transaction_in_progress = false;
+ session->request_length = 0;
+
+ return status;
+}
+
+rpc_status_t rpc_caller_session_find_and_open(struct rpc_caller_session *session,
+ struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid,
+ size_t shared_memory_size)
+{
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+
+ if (!session || !caller || !service_uuid)
+ return RPC_ERROR_INVALID_VALUE;
+
+ status = rpc_caller_find_and_open_session(caller, service_uuid);
+ if (status)
+ return status;
+
+ status = initalize_shared_memory(session, caller, shared_memory_size);
+ if (status)
+ return status;
+
+ session->caller = caller;
+ session->is_call_transaction_in_progress = false;
+ session->request_length = 0;
+
+ return status;
+}
+
+rpc_status_t rpc_caller_session_close(struct rpc_caller_session *session)
+{
+ if (!session)
+ return RPC_ERROR_INVALID_VALUE;
+
+ if (session->is_call_transaction_in_progress)
+ return RPC_ERROR_INVALID_STATE;
+
+ if (session->shared_memory_policy == alloc_for_session) {
+ rpc_status_t rpc_status = RPC_ERROR_INTERNAL;
+
+ rpc_status = rpc_caller_release_shared_memory(session->caller,
+ &session->shared_memory);
+ if (rpc_status != RPC_SUCCESS)
+ return rpc_status;
+ }
+
+ return rpc_caller_close_session(session->caller);
+}
+
+rpc_call_handle rpc_caller_session_begin(struct rpc_caller_session *session,
+ uint8_t **request_buffer, size_t request_length,
+ size_t response_max_length)
+{
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+ size_t required_buffer_length = MAX(request_length, response_max_length);
+
+ if (required_buffer_length > UINT32_MAX)
+ return NULL;
+
+ if (!session || !request_buffer || session->is_call_transaction_in_progress)
+ return NULL;
+
+ switch (session->shared_memory_policy) {
+ case alloc_for_each_call:
+ if (session->shared_memory.buffer || session->shared_memory.size)
+ return NULL; /* There's already a shared memory */
+
+ status = rpc_caller_create_shared_memory(session->caller, required_buffer_length,
+ &session->shared_memory);
+ if (status)
+ return NULL; /* Failed to create shared memory */
+ break;
+
+ case alloc_for_session:
+ if (!session->shared_memory.buffer || !session->shared_memory.size)
+ return NULL; /* There's no shared memory */
+
+ if (session->shared_memory.size < required_buffer_length)
+ return NULL; /* The allocated shared memory is too small */
+ break;
+
+ default:
+ /* Invalid shared memory policy */
+ return NULL;
+ }
+
+ *request_buffer = session->shared_memory.buffer;
+
+ session->is_call_transaction_in_progress = true;
+ session->request_length = request_length;
+
+ return (rpc_call_handle)session;
+}
+
+rpc_status_t rpc_caller_session_invoke(rpc_call_handle handle, uint32_t opcode,
+ uint8_t **response_buffer, size_t *response_length,
+ service_status_t *service_status)
+{
+ struct rpc_caller_session *session = (struct rpc_caller_session *)handle;
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+
+ if (!handle || !response_buffer || !response_length)
+ return RPC_ERROR_INVALID_VALUE;
+
+ if (!session->is_call_transaction_in_progress)
+ return RPC_ERROR_INVALID_STATE;
+
+ if (session->request_length &&
+ (!session->shared_memory.buffer || !session->shared_memory.size))
+ return RPC_ERROR_INVALID_STATE;
+
+ status = rpc_caller_call(session->caller, opcode, &session->shared_memory,
+ session->request_length, response_length, service_status);
+ if (status || *response_length > session->shared_memory.size) {
+ *response_buffer = NULL;
+ *response_length = 0;
+ return status;
+ }
+
+ *response_buffer = session->shared_memory.buffer;
+
+ return status;
+}
+
+rpc_status_t rpc_caller_session_end(rpc_call_handle handle)
+{
+ struct rpc_caller_session *session = (struct rpc_caller_session *)handle;
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+
+ if (!handle)
+ return RPC_ERROR_INVALID_VALUE;
+
+ if (!session->is_call_transaction_in_progress)
+ return RPC_ERROR_INVALID_STATE;
+
+ if (session->request_length &&
+ (!session->shared_memory.buffer || !session->shared_memory.size))
+ return RPC_ERROR_INVALID_STATE; /* There's no shared memory */
+
+ switch (session->shared_memory_policy) {
+ case alloc_for_each_call:
+ status = rpc_caller_release_shared_memory(session->caller,
+ &session->shared_memory);
+ if (status)
+ return status; /* Failed to release shared memory */
+
+ session->shared_memory = (struct rpc_caller_shared_memory){ 0 };
+ break;
+
+ case alloc_for_session:
+ /* Nothing to do */
+ break;
+
+ default:
+ return RPC_ERROR_INVALID_STATE;
+ }
+
+ session->is_call_transaction_in_progress = false;
+ session->request_length = 0;
+
+ return RPC_SUCCESS;
+}
diff --git a/components/rpc/common/caller/rpc_caller_session.h b/components/rpc/common/caller/rpc_caller_session.h
new file mode 100644
index 0000000..debcf19
--- /dev/null
+++ b/components/rpc/common/caller/rpc_caller_session.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RPC_CALLER_SESSION_H
+#define RPC_CALLER_SESSION_H
+
+#include "rpc_caller.h"
+#include "rpc_status.h"
+#include "rpc_uuid.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *rpc_call_handle;
+
+enum rpc_caller_memory_policy {
+ alloc_for_each_call = 0,
+ alloc_for_session,
+};
+
+/**
+ * @brief RPC caller session
+ *
+ * Builds a session on top of the rpc_caller_interface. It provides high level functions for service
+ * caller implementations and for prior service discovery.
+ */
+struct rpc_caller_session {
+ /** Caller interface */
+ struct rpc_caller_interface *caller;
+
+ /** Shared memory instance for the exchanging of RPC request and response parameters. */
+ struct rpc_caller_shared_memory shared_memory;
+
+ /** Controls how and when the shared memory is allocated for the RPC calls. */
+ enum rpc_caller_memory_policy shared_memory_policy;
+
+ /**
+ * Indicates if a call transaction has been started by the begin function but was not
+ * finished yet (i.e. end was not called).
+ */
+ bool is_call_transaction_in_progress;
+
+ /**
+ * Stores the request length of the current transaction. Its value is set by the begin
+ * function and then used in the invoke step.
+ */
+ size_t request_length;
+};
+
+/**
+ * @brief
+ *
+ * @param session
+ * @param caller
+ * @param service_uuid
+ * @param endpoint_id
+ * @param shared_memory_size
+ * @return RPC_CALLER_EXPORTED
+ */
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_session_open(struct rpc_caller_session *session,
+ struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid,
+ uint16_t endpoint_id,
+ size_t shared_memory_size);
+
+/**
+ * @brief
+ *
+ * @param session
+ * @param caller
+ * @param service_uuid
+ * @param shared_memory_size
+ * @return RPC_CALLER_EXPORTED
+ */
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_session_find_and_open(struct rpc_caller_session *session,
+ struct rpc_caller_interface *caller,
+ const struct rpc_uuid *service_uuid,
+ size_t shared_memory_size);
+
+/**
+ * @brief Closes the RPC caller session
+ *
+ * @param session Caller session instance
+ * @return RPC_CALLER_EXPORTED
+ */
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_session_close(struct rpc_caller_session *session);
+
+/**
+ * @brief Begins an RPC call
+ *
+ * The function returns a buffer where the service caller can build the request.
+ *
+ * @param session Caller session instance
+ * @param request_buffer Pointer of the request buffer
+ * @param request_length Request length
+ * @param response_max_length Expected maximal length of the response
+ * @return rpc_call_handle Handle of the started call
+ */
+RPC_CALLER_EXPORTED
+rpc_call_handle rpc_caller_session_begin(struct rpc_caller_session *session,
+ uint8_t **request_buffer,
+ size_t request_length,
+ size_t response_max_length);
+
+/**
+ * @brief Invoke phase of the RPC call
+ *
+ * Invokes the call on the remote side and returns the response buffer and service status. The
+ * service caller can parse the response from the response buffer.
+ * After this call the request buffer is not available for the service caller.
+ *
+ * @param handle RPC call handle
+ * @param opcode The opcode of the remote function
+ * @param response_buffer Pointer of the response buffer
+ * @param response_length Length of the response buffer
+ * @param service_status Service specific status code
+ * @return RPC_CALLER_EXPORTED
+ */
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_session_invoke(rpc_call_handle handle, uint32_t opcode,
+ uint8_t **response_buffer,
+ size_t *response_length,
+ service_status_t *service_status);
+
+/**
+ * @brief Ends the RPC call
+ *
+ * Indicates if the response has been parsed by the service calls and the RPC session can free the
+ * response buffer.
+ *
+ * @param handle RPC call handle
+ * @return RPC_CALLER_EXPORTED
+ */
+RPC_CALLER_EXPORTED
+rpc_status_t rpc_caller_session_end(rpc_call_handle handle);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RPC_CALLER_SESSION_H */
diff --git a/components/rpc/common/interface/component.cmake b/components/rpc/common/interface/component.cmake
index 51f1f24..ae219d6 100644
--- a/components/rpc/common/interface/component.cmake
+++ b/components/rpc/common/interface/component.cmake
@@ -9,7 +9,6 @@
endif()
set_property(TARGET ${TGT} APPEND PROPERTY PUBLIC_HEADER
- "${CMAKE_CURRENT_LIST_DIR}/rpc_caller.h"
"${CMAKE_CURRENT_LIST_DIR}/rpc_status.h"
"${CMAKE_CURRENT_LIST_DIR}/rpc_uuid.h"
)
diff --git a/components/rpc/common/interface/rpc_caller.h b/components/rpc/common/interface/rpc_caller.h
deleted file mode 100644
index 387489c..0000000
--- a/components/rpc/common/interface/rpc_caller.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef RPC_CALLER_H
-#define RPC_CALLER_H
-
-#include <stddef.h>
-#include <stdint.h>
-#include "rpc_status.h"
-
-/*
- * The rpc_caller puplic interface may be exported as a public interface to
- * a shared library.
- */
-#ifdef EXPORT_PUBLIC_INTERFACE_RPC_CALLER
-#define RPC_CALLER_EXPORTED __attribute__((__visibility__("default")))
-#else
-#define RPC_CALLER_EXPORTED
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Defines an abstract interface for calling operations provided by an rpc endpoint.
- * Concrete specializations will map the an RPC or direct calling mechanism to
- * suite the deployment.
- */
-
-typedef void *rpc_call_handle;
-
-struct rpc_caller
-{
- void *context;
- uint32_t encoding;
-
- /* A concrete rpc_caller implements these methods */
- rpc_call_handle (*call_begin)(void *context, uint8_t **req_buf, size_t req_len);
-
- rpc_status_t (*call_invoke)(void *context, rpc_call_handle handle, uint32_t opcode,
- rpc_opstatus_t *opstatus, uint8_t **resp_buf, size_t *resp_len);
-
- void (*call_end)(void *context, rpc_call_handle handle);
-};
-
-/*
- * Called by a concrete rpc_caller to initialise the base rpc_caller.
- */
-void rpc_caller_init(struct rpc_caller *s, void *context);
-
-/*
- * Allows a client to specify the parameter encoding scheme that the client
- * intends to use during an RPC session. It is the client's responsiblity
- * to choose an encoding scheme that is supported by the remote interface.
- */
-RPC_CALLER_EXPORTED void rpc_caller_set_encoding_scheme(struct rpc_caller *s,
- uint32_t encoding);
-
-/*
- * Starts a call transaction. The returned handle is an identifier for the
- * transaction and must be passed as a parameter to call_invoke() and
- * call_end(). A concrete rpc_caller may perform resource allocation during
- * this call. This will include a buffer for the request message parameters.
- * Returns a NULL handle on failure.
- */
-RPC_CALLER_EXPORTED rpc_call_handle rpc_caller_begin(struct rpc_caller *s,
- uint8_t **req_buf, size_t req_len);
-
-/*
- * Invokes the operation identified by the opcode. This method blocks
- * until the operation completes. The status of the call is returned. An
- * additional endpoint specific status value is also returned. If a response
- * message was received, the concrete rpc_caller will have allocated a
- * buffer for the reponse. This buffer will hold valid data until the point when
- * call_end() is called for the transaction.
- */
-RPC_CALLER_EXPORTED rpc_status_t rpc_caller_invoke(struct rpc_caller *s, rpc_call_handle handle,
- uint32_t opcode, rpc_opstatus_t *opstatus, uint8_t **resp_buf, size_t *resp_len);
-
-/*
- * Ends the call transaction, allowing any resource associated with the
- * transaction to be freed.
- */
-RPC_CALLER_EXPORTED void rpc_caller_end(struct rpc_caller *s, rpc_call_handle handle);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* RPC_CALLER_H */