Add MM Communicate RPC caller
To enable service level testing from Linux userspace, an RPC
caller that implements the MM Communicate protocol has been added.
For compatibility with StMM, the same carve-out buffer scheme is
used. Service level tests will be used for end-to-end testing of
the smm-gateway.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: If5dc4e799d6461878c126cf3f73e8a2625f98f1d
diff --git a/components/rpc/mm_communicate/caller/linux/carveout.c b/components/rpc/mm_communicate/caller/linux/carveout.c
new file mode 100644
index 0000000..e3cdf16
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/carveout.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "carveout.h"
+
+/* Need to be aligned with carve-out used by StMM or smm-gateway. */
+static const off_t carveout_pa = 0x0000000881000000;
+static const size_t carveout_len = 0x8000;
+
+int carveout_claim(uint8_t **buf, size_t *buf_size)
+{
+ int status = -1;
+ int fd = open("/dev/mem", O_RDWR | O_SYNC);
+
+ if (fd >= 0) {
+
+ uint8_t *mem = mmap(NULL, carveout_len,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, carveout_pa);
+
+ if (mem != MAP_FAILED) {
+
+ *buf = mem;
+ *buf_size = carveout_len;
+
+ status = 0;
+ }
+
+ close(fd);
+ }
+
+ return status;
+}
+
+void carveout_relinquish(uint8_t *buf, size_t buf_size)
+{
+ munmap(buf, buf_size);
+}
diff --git a/components/rpc/mm_communicate/caller/linux/carveout.h b/components/rpc/mm_communicate/caller/linux/carveout.h
new file mode 100644
index 0000000..dce3988
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/carveout.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MM_COMMUNICATE_CARVEOUT_H
+#define MM_COMMUNICATE_CARVEOUT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * For compatibility with StMM, MM Communicate messages are passed
+ * in a pre-allocated buffer, referred to as the 'carveout'. Because
+ * there can only be one of these, the carveout is represented by
+ * a singleton that is responsible for one-time setup and teardown,
+ * independent of the number of rpc callers.
+ */
+
+/**
+ * \brief Claim use of the carveout
+ *
+ * \param[out] buf The address of the carveout buffer
+ * \param[out] buf_size The size of the buffer
+ *
+ * \return Status (0 for success)
+ */
+int carveout_claim(uint8_t **buf, size_t *buf_size);
+
+/**
+ * \brief Relinquish use of the carveout
+ *
+ * \param[in] buf The address of the carveout buffer
+ * \param[in] buf_size The size of the buffer
+ *
+ * \return Status (0 for success)
+ */
+void carveout_relinquish(uint8_t *buf, size_t buf_size);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MM_COMMUNICATE_CARVEOUT_H */
diff --git a/components/rpc/mm_communicate/caller/linux/component.cmake b/components/rpc/mm_communicate/caller/linux/component.cmake
new file mode 100644
index 0000000..9435599
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/component.cmake
@@ -0,0 +1,21 @@
+#-------------------------------------------------------------------------------
+# 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()
+
+include(${TS_ROOT}/external/LinuxFFAUserShim/LinuxFFAUserShim.cmake)
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/mm_communicate_caller.c"
+ "${CMAKE_CURRENT_LIST_DIR}/mm_communicate_serializer.c"
+ "${CMAKE_CURRENT_LIST_DIR}/carveout.c"
+)
+
+target_include_directories(${TGT} PRIVATE
+ "${LINUX_FFA_USER_SHIM_INCLUDE_DIR}"
+)
diff --git a/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.c b/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.c
new file mode 100644
index 0000000..dda796a
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "mm_communicate_caller.h"
+#include "carveout.h"
+#include <arm_ffa_user.h>
+#include <components/rpc/mm_communicate/common/mm_communicate_call_args.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <protocols/common/mm/mm_smc.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define KERNEL_MOD_REQ_VER_MAJOR 2
+#define KERNEL_MOD_REQ_VER_MINOR 0
+#define KERNEL_MOD_REQ_VER_PATCH 0
+
+static rpc_call_handle call_begin(
+ void *context,
+ uint8_t **req_buf,
+ size_t req_len);
+
+static 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);
+
+static void call_end(
+ void *context,
+ rpc_call_handle handle);
+
+static rpc_status_t mm_return_code_to_rpc_status(
+ int32_t return_code);
+
+bool mm_communicate_caller_check_version(void)
+{
+ FILE *f;
+ char mod_name[64];
+ int ver_major, ver_minor, ver_patch;
+ bool mod_loaded = false;
+
+ f = fopen("/proc/modules", "r");
+ if (!f) {
+ printf("error: cannot open /proc/modules\n");
+ return false;
+ }
+
+ while (fscanf(f, "%64s %*[^\n]\n", mod_name) != EOF) {
+ if (!strcmp(mod_name, "arm_ffa_user")) {
+ mod_loaded = true;
+ break;
+ }
+ }
+
+ fclose(f);
+
+ if (!mod_loaded) {
+ printf("error: kernel module not loaded\n");
+ return false;
+ }
+
+ f = fopen("/sys/module/arm_ffa_user/version", "r");
+ if (f) {
+ fscanf(f, "%d.%d.%d", &ver_major, &ver_minor, &ver_patch);
+ fclose(f);
+ } else {
+ /*
+ * Fallback for the initial release of the kernel module, where
+ * the version definition was missing.
+ */
+ ver_major = 1;
+ ver_minor = 0;
+ ver_patch = 0;
+ }
+
+ if (ver_major != KERNEL_MOD_REQ_VER_MAJOR)
+ goto err;
+
+ if (ver_minor < KERNEL_MOD_REQ_VER_MINOR)
+ goto err;
+
+ if (ver_minor == KERNEL_MOD_REQ_VER_MINOR)
+ if (ver_patch < KERNEL_MOD_REQ_VER_PATCH)
+ goto err;
+
+ return true;
+
+err:
+ printf("error: kernel module is v%d.%d.%d but required v%d.%d.%d\n",
+ ver_major, ver_minor, ver_patch, KERNEL_MOD_REQ_VER_MAJOR,
+ KERNEL_MOD_REQ_VER_MINOR, KERNEL_MOD_REQ_VER_PATCH);
+
+ return false;
+}
+
+struct rpc_caller *mm_communicate_caller_init(
+ struct mm_communicate_caller *s,
+ const char *ffa_device_path)
+{
+ struct rpc_caller *base = &s->rpc_caller;
+
+ rpc_caller_init(base, s);
+ base->call_begin = call_begin;
+ base->call_invoke = call_invoke;
+ base->call_end = call_end;
+
+ s->ffa_fd = -1;
+ s->ffa_device_path = ffa_device_path;
+ s->dest_partition_id = 0;
+ s->comm_buffer = NULL;
+ s->comm_buffer_size = 0;
+ s->scrub_len = 0;
+ s->req_len = 0;
+ s->is_call_transaction_in_progess = false;
+ s->serializer = NULL;
+
+ return base;
+}
+
+void mm_communicate_caller_deinit(
+ struct mm_communicate_caller *s)
+{
+ s->rpc_caller.context = NULL;
+ s->rpc_caller.call_begin = NULL;
+ s->rpc_caller.call_invoke = NULL;
+ s->rpc_caller.call_end = NULL;
+
+ call_end(s, s);
+ mm_communicate_caller_close(s);
+}
+
+size_t mm_communicate_caller_discover(
+ const struct mm_communicate_caller *s,
+ const struct uuid_canonical *uuid,
+ uint16_t *partition_ids,
+ size_t discover_limit)
+{
+ size_t discover_count = 0;
+
+ if (uuid && partition_ids && s->ffa_device_path) {
+ int fd;
+
+ fd = open(s->ffa_device_path, O_RDWR);
+
+ if (fd >= 0) {
+ int ioctl_status;
+ struct ffa_ioctl_ep_desc discovered_partition;
+
+ discovered_partition.uuid_ptr = (uintptr_t)&uuid->characters;
+ discovered_partition.id = 0;
+
+ ioctl_status = ioctl(fd, FFA_IOC_GET_PART_ID, &discovered_partition);
+
+ if ((ioctl_status == 0) && (discover_count < discover_limit)) {
+ partition_ids[discover_count] = discovered_partition.id;
+ ++discover_count;
+ }
+
+ close(fd);
+ }
+ }
+
+ return discover_count;
+}
+
+int mm_communicate_caller_open(
+ struct mm_communicate_caller *s,
+ uint16_t dest_partition_id,
+ const EFI_GUID *svc_guid)
+{
+ int status = -1;
+
+ s->serializer = mm_communicate_serializer_find(svc_guid);
+
+ if (s->serializer && s->ffa_device_path) {
+
+ s->ffa_fd = open(s->ffa_device_path, O_RDWR);
+
+ if ((s->ffa_fd >= 0) && !s->comm_buffer) {
+
+ status = carveout_claim(
+ &s->comm_buffer,
+ &s->comm_buffer_size);
+
+ if (status == 0) {
+
+ s->dest_partition_id = dest_partition_id;
+ }
+ else {
+ /* Failed to claim carveout */
+ s->comm_buffer = NULL;
+ s->comm_buffer_size = 0;
+
+ mm_communicate_caller_close(s);
+ }
+ }
+ }
+
+ if (status != 0) {
+
+ s->serializer = NULL;
+ }
+
+ return status;
+}
+
+int mm_communicate_caller_close(
+ struct mm_communicate_caller *s)
+{
+ if (s->ffa_fd >= 0) {
+
+ close(s->ffa_fd);
+ s->ffa_fd = -1;
+ s->dest_partition_id = 0;
+ }
+
+ if (s->comm_buffer) {
+
+ carveout_relinquish(s->comm_buffer, s->comm_buffer_size);
+ s->comm_buffer = NULL;
+ s->comm_buffer_size = 0;
+ }
+
+ s->serializer = NULL;
+
+ s->is_call_transaction_in_progess = false;
+
+ return ((s->ffa_fd < 0) && !s->comm_buffer) ? 0 : -1;
+}
+
+static rpc_call_handle call_begin(
+ void *context,
+ uint8_t **req_buf,
+ size_t req_len)
+{
+ rpc_call_handle handle = NULL;
+ struct mm_communicate_caller *s = (struct mm_communicate_caller*)context;
+ size_t hdr_size = mm_communicate_serializer_header_size(s->serializer);
+ *req_buf = NULL;
+
+ if (!s->is_call_transaction_in_progess && hdr_size) {
+
+ if (req_len + hdr_size <= s->comm_buffer_size) {
+
+ s->is_call_transaction_in_progess = true;
+ handle = s;
+
+ s->req_len = req_len;
+ *req_buf = &s->comm_buffer[hdr_size];
+
+ s->scrub_len = hdr_size + req_len;
+ }
+ else {
+
+ s->req_len = 0;
+ }
+ }
+
+ return handle;
+}
+
+static 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)
+{
+ struct mm_communicate_caller *s = (struct mm_communicate_caller*)context;
+
+ rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+ *resp_len = 0;
+
+ if ((handle == s) && s->is_call_transaction_in_progess) {
+
+ mm_communicate_serializer_header_encode(s->serializer,
+ s->comm_buffer, opcode, s->req_len);
+
+ /* Make direct call to send the request */
+ struct ffa_ioctl_msg_args direct_msg;
+ memset(&direct_msg, 0, sizeof(direct_msg));
+
+ direct_msg.dst_id = s->dest_partition_id;
+
+ direct_msg.args[MM_COMMUNICATE_CALL_ARGS_COMM_BUFFER_ADDRESS] = (uintptr_t)s->comm_buffer;
+ direct_msg.args[MM_COMMUNICATE_CALL_ARGS_COMM_BUFFER_SIZE] = s->comm_buffer_size;
+
+ int kernel_op_status = ioctl(s->ffa_fd, FFA_IOC_MSG_SEND, &direct_msg);
+
+ if (kernel_op_status == 0) {
+
+ /* Kernel send operation completed normally */
+ uint32_t mm_return_id = direct_msg.args[MM_COMMUNICATE_CALL_ARGS_RETURN_ID];
+ int32_t mm_return_code = direct_msg.args[MM_COMMUNICATE_CALL_ARGS_RETURN_CODE];
+
+ if (mm_return_id == ARM_SVC_ID_SP_EVENT_COMPLETE) {
+
+ if (mm_return_code == MM_RETURN_CODE_SUCCESS) {
+
+ mm_communicate_serializer_header_decode(s->serializer,
+ s->comm_buffer, opstatus, resp_buf, resp_len);
+
+ if (*resp_len > s->req_len) {
+
+ s->scrub_len =
+ mm_communicate_serializer_header_size(s->serializer) +
+ *resp_len;
+ }
+
+ rpc_status = TS_RPC_CALL_ACCEPTED;
+ }
+ else {
+
+ rpc_status = mm_return_code_to_rpc_status(mm_return_code);
+ }
+ }
+ }
+ }
+
+ return rpc_status;
+}
+
+static void call_end(void *context, rpc_call_handle handle)
+{
+ struct mm_communicate_caller *s = (struct mm_communicate_caller*)context;
+
+ if ((handle == s) && s->is_call_transaction_in_progess) {
+
+ /* Call transaction complete */
+ s->req_len = 0;
+ s->is_call_transaction_in_progess = false;
+
+ /* Scrub the comms buffer */
+ memset(s->comm_buffer, 0, s->scrub_len);
+ s->scrub_len = 0;
+ }
+}
+
+static rpc_status_t mm_return_code_to_rpc_status(int32_t return_code)
+{
+ rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
+
+ switch (return_code)
+ {
+ case MM_RETURN_CODE_NOT_SUPPORTED:
+ rpc_status = TS_RPC_ERROR_INTERFACE_DOES_NOT_EXIST;
+ break;
+ case MM_RETURN_CODE_INVALID_PARAMETER:
+ rpc_status = TS_RPC_ERROR_INVALID_PARAMETER;
+ break;
+ case MM_RETURN_CODE_DENIED:
+ rpc_status = TS_RPC_ERROR_ACCESS_DENIED;
+ break;
+ case MM_RETURN_CODE_NO_MEMORY:
+ rpc_status = TS_RPC_ERROR_RESOURCE_FAILURE;
+ break;
+ default:
+ break;
+ }
+
+ return rpc_status;
+}
diff --git a/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h b/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h
new file mode 100644
index 0000000..ba15864
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MM_COMMUNICATE_CALLER_H
+#define MM_COMMUNICATE_CALLER_H
+
+#include <stdbool.h>
+#include <common/uuid/uuid.h>
+#include <protocols/common/efi/efi_types.h>
+#include <rpc_caller.h>
+#include "mm_communicate_serializer.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * An RPC caller for Linux user-space clients that uses the MM_COMMUNICATE
+ * protocol for calling UEFI SMM service endpoints.
+ */
+struct mm_communicate_caller
+{
+ struct rpc_caller rpc_caller;
+ int ffa_fd;
+ const char *ffa_device_path;
+ uint16_t dest_partition_id;
+ uint16_t dest_iface_id;
+ uint8_t *comm_buffer;
+ size_t comm_buffer_size;
+ size_t req_len;
+ size_t scrub_len;
+ bool is_call_transaction_in_progess;
+ const struct mm_communicate_serializer *serializer;
+};
+
+bool mm_communicate_caller_check_version(void);
+
+struct rpc_caller *mm_communicate_caller_init(
+ struct mm_communicate_caller *s,
+ const char *ffa_device_path);
+
+void mm_communicate_caller_deinit(
+ struct mm_communicate_caller *s);
+
+size_t mm_communicate_caller_discover(
+ const struct mm_communicate_caller *s,
+ const struct uuid_canonical *uuid,
+ uint16_t *partition_ids,
+ size_t discover_limit);
+
+int mm_communicate_caller_open(
+ struct mm_communicate_caller *s,
+ uint16_t dest_partition_id,
+ uint16_t dest_iface_id,
+ const EFI_GUID *svc_guid);
+
+int mm_communicate_caller_close(
+ struct mm_communicate_caller *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MM_COMMUNICATE_CALLER_H */
diff --git a/components/rpc/mm_communicate/caller/linux/mm_communicate_serializer.c b/components/rpc/mm_communicate/caller/linux/mm_communicate_serializer.c
new file mode 100644
index 0000000..2be74eb
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/mm_communicate_serializer.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <protocols/service/smm_variable/smm_variable_proto.h>
+#include <protocols/common/efi/efi_types.h>
+#include "mm_communicate_serializer.h"
+
+/* Concrete service header serialization functions */
+static void smm_variable_header_encode(const struct mm_communicate_serializer *serializer,
+ uint8_t *buf, uint32_t opcode);
+static void smm_variable_header_decode(const struct mm_communicate_serializer *serializer,
+ uint8_t *buf, efi_status_t *efi_status);
+
+/**
+ * Rather than have a generic RPC header, header parameters are
+ * split between the generic MM Communicate header and an SMM
+ * service specific header that carries the function ID to
+ * call and the return status. To accommodate different
+ * service specific headers, concrete header encode/decode
+ * functions are provided, keyed off the service guid
+ * carried in the MM Communicate header.
+ */
+struct mm_communicate_serializer
+{
+ /**
+ * \brief The service GUID
+ */
+ EFI_GUID svc_guid;
+
+ /**
+ * \brief Service header size
+ */
+ size_t svc_header_size;
+
+ /**
+ * \brief Service header encode function
+ *
+ * \param[in] serializer Concrete serializer
+ * \param[in] buf Encode to this buffer
+ * \param[in] opcode Service opcode
+ */
+ void (*header_encode)(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ uint32_t opcode);
+
+ /**
+ * \brief Header decode function
+ *
+ * \param[in] serializer Concrete serializer
+ * \param[in] buf Encode to this buffer
+ * \param[out] efi_status EFI status code
+ */
+ void (*header_decode)(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ efi_status_t *efi_status);
+};
+
+const struct mm_communicate_serializer *mm_communicate_serializer_find(const EFI_GUID *svc_guid)
+{
+ /* Lookup to map service guid to a concrete serializer */
+ static const struct mm_communicate_serializer lookup[] =
+ {
+ /* SMM Variable mapping */
+ {
+ SMM_VARIABLE_GUID,
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE,
+ smm_variable_header_encode,
+ smm_variable_header_decode
+ }
+ };
+
+ /* Find a concrete mapping for the requested service */
+ for (size_t i = 0; i < sizeof(lookup)/sizeof(struct mm_communicate_serializer); ++i) {
+
+ const struct mm_communicate_serializer *serializer = &lookup[i];
+
+ if ((svc_guid->Data1 == serializer->svc_guid.Data1) &&
+ (svc_guid->Data2 == serializer->svc_guid.Data2) &&
+ (svc_guid->Data3 == serializer->svc_guid.Data3) &&
+ (memcmp(svc_guid->Data4, serializer->svc_guid.Data4, sizeof(svc_guid->Data4)) == 0)) {
+
+ return serializer;
+ }
+ }
+
+ /* Failed to find serializer */
+ return NULL;
+}
+
+size_t mm_communicate_serializer_header_size(
+ const struct mm_communicate_serializer *serializer)
+{
+ return EFI_MM_COMMUNICATE_HEADER_SIZE + serializer->svc_header_size;
+}
+
+void mm_communicate_serializer_header_encode(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ uint32_t opcode,
+ size_t req_len)
+{
+ EFI_MM_COMMUNICATE_HEADER *hdr = (EFI_MM_COMMUNICATE_HEADER*)buf;
+ hdr->HeaderGuid = serializer->svc_guid;
+ hdr->MessageLength = serializer->svc_header_size + req_len;
+
+ /* Encode the service specific header */
+ serializer->header_encode(serializer, hdr->Data, opcode);
+}
+
+void mm_communicate_serializer_header_decode(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ efi_status_t *efi_status,
+ uint8_t **resp_buf,
+ size_t *resp_len)
+{
+ EFI_MM_COMMUNICATE_HEADER *hdr = (EFI_MM_COMMUNICATE_HEADER*)buf;
+
+ *efi_status = EFI_PROTOCOL_ERROR;
+ *resp_len = 0;
+
+ if (hdr->MessageLength >= serializer->svc_header_size) {
+
+ *resp_len = hdr->MessageLength - serializer->svc_header_size;
+ *resp_buf = &hdr->Data[serializer->svc_header_size];
+
+ /* Deserialize the service specific header */
+ serializer->header_decode(serializer, hdr->Data, efi_status);
+ }
+}
+
+static void smm_variable_header_encode(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ uint32_t opcode)
+{
+ SMM_VARIABLE_COMMUNICATE_HEADER *hdr = (SMM_VARIABLE_COMMUNICATE_HEADER*)buf;
+ hdr->Function = opcode;
+ hdr->ReturnStatus = EFI_SUCCESS;
+}
+
+static void smm_variable_header_decode(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ efi_status_t *efi_status)
+{
+ SMM_VARIABLE_COMMUNICATE_HEADER *hdr = (SMM_VARIABLE_COMMUNICATE_HEADER*)buf;
+ *efi_status = hdr->ReturnStatus;
+}
diff --git a/components/rpc/mm_communicate/caller/linux/mm_communicate_serializer.h b/components/rpc/mm_communicate/caller/linux/mm_communicate_serializer.h
new file mode 100644
index 0000000..b0c541d
--- /dev/null
+++ b/components/rpc/mm_communicate/caller/linux/mm_communicate_serializer.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MM_COMMUNICATE_SERIALIZER_H
+#define MM_COMMUNICATE_SERIALIZER_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <protocols/common/efi/efi_status.h>
+#include "protocols/common/efi/efi_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Provides an interface for encoding/decoding the MM Communicate and
+ * MM service specific headers. Based on the TS view of an RPC layer
+ * RPC related parameters are distributed between the generic
+ * MM Communicate header and the SMM service specific header.
+ * Concrete functions are determined by the requests SMM service GUID.
+ */
+struct mm_communicate_serializer;
+
+/**
+ * \brief Find a MM Communicate serializer
+ *
+ * Find a serializer for the specified service GUID.
+ *
+ * \param[in] svc_guid The SMM service GUID
+ *
+ * \return Pointer to serializer or NULL if not found
+ */
+const struct mm_communicate_serializer *mm_communicate_serializer_find(
+ const EFI_GUID *svc_guid);
+
+/**
+ * \brief Return the header size for the specified serializer
+ *
+ * \param[in] serializer The concrete serializer
+ *
+ * \return Header size comprising MM Communicate header + SMM service header
+ */
+size_t mm_communicate_serializer_header_size(
+ const struct mm_communicate_serializer *serializer);
+
+/**
+ * \brief Encode the MM Communicate + SMM service header
+ *
+ * \param[in] serializer Concrete serializer
+ * \param[in] buf Encode to this buffer
+ * \param[in] opcode Service opcode
+ * \param[in] req_len Length of the request
+ */
+void mm_communicate_serializer_header_encode(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ uint32_t opcode,
+ size_t req_len);
+
+/**
+ * \brief Header decode function
+ *
+ * \param[in] serializer Concrete serializer
+ * \param[in] buf Encode to this buffer
+ * \param[out] efi_status EFI status code
+ * \param[out] resp_buf Response buffer
+ * \param[out] resp_len Length of the response
+ */
+void mm_communicate_serializer_header_decode(
+ const struct mm_communicate_serializer *serializer,
+ uint8_t *buf,
+ efi_status_t *efi_status,
+ uint8_t **resp_buf,
+ size_t *resp_len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MM_COMMUNICATE_SERIALIZER_H */
diff --git a/components/service/smm_variable/frontend/mm_communicate/smm_variable_mm_service.h b/components/service/smm_variable/frontend/mm_communicate/smm_variable_mm_service.h
index 7e1f40e..420419f 100644
--- a/components/service/smm_variable/frontend/mm_communicate/smm_variable_mm_service.h
+++ b/components/service/smm_variable/frontend/mm_communicate/smm_variable_mm_service.h
@@ -8,6 +8,7 @@
#include "components/rpc/common/endpoint/rpc_interface.h"
#include "components/rpc/mm_communicate/endpoint/sp/mm_communicate_call_ep.h"
+#include "protocols/service/smm_variable/smm_variable_proto.h"
#ifdef __cplusplus
extern "C" {
@@ -17,10 +18,6 @@
* MM service interface implementation for parsing SMM variable requests and
* forwarding them to an RPC interface.
*/
-
-#define SMM_VARIABLE_GUID \
- {0xed32d533, 0x99e6, 0x4209, { 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7 }}
-
struct smm_variable_mm_service {
struct mm_service_interface mm_service;
struct rpc_interface *iface;
diff --git a/deployments/libts/arm-linux/CMakeLists.txt b/deployments/libts/arm-linux/CMakeLists.txt
index ab512d6..b85a5d5 100644
--- a/deployments/libts/arm-linux/CMakeLists.txt
+++ b/deployments/libts/arm-linux/CMakeLists.txt
@@ -1,5 +1,5 @@
#-------------------------------------------------------------------------------
-# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -27,7 +27,9 @@
TARGET "ts"
BASE_DIR ${TS_ROOT}
COMPONENTS
+ "components/common/utils"
"components/rpc/ffarpc/caller/linux"
+ "components/rpc/mm_communicate/caller/linux"
"components/service/locator/linux"
"components/service/locator/linux/ffa"
"components/common/uuid"
@@ -45,4 +47,4 @@
#
#-------------------------------------------------------------------------------
env_set_link_options(TGT ts)
-target_link_libraries(ts PRIVATE gcc)
\ No newline at end of file
+target_link_libraries(ts PRIVATE gcc)
diff --git a/protocols/rpc/common/packed-c/status.h b/protocols/rpc/common/packed-c/status.h
index 534f0df..7dace1b 100644
--- a/protocols/rpc/common/packed-c/status.h
+++ b/protocols/rpc/common/packed-c/status.h
@@ -15,18 +15,19 @@
*/
enum
{
- TS_RPC_CALL_ACCEPTED = 0,
- TS_RPC_ERROR_EP_DOES_NOT_EXIT = -1,
- TS_RPC_ERROR_INVALID_OPCODE = -2,
- TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED = -3,
- TS_RPC_ERROR_INVALID_REQ_BODY = -4,
- TS_RPC_ERROR_INVALID_RESP_BODY = -5,
- TS_RPC_ERROR_RESOURCE_FAILURE = -6,
- TS_RPC_ERROR_NOT_READY = -7,
- TS_RPC_ERROR_INVALID_TRANSACTION = -8,
- TS_RPC_ERROR_INTERNAL = -9,
- TS_RPC_ERROR_INVALID_PARAMETER = -10,
- TS_RPC_ERROR_INTERFACE_DOES_NOT_EXIST = -11
+ TS_RPC_CALL_ACCEPTED = 0,
+ TS_RPC_ERROR_EP_DOES_NOT_EXIT = -1,
+ TS_RPC_ERROR_INVALID_OPCODE = -2,
+ TS_RPC_ERROR_SERIALIZATION_NOT_SUPPORTED = -3,
+ TS_RPC_ERROR_INVALID_REQ_BODY = -4,
+ TS_RPC_ERROR_INVALID_RESP_BODY = -5,
+ TS_RPC_ERROR_RESOURCE_FAILURE = -6,
+ TS_RPC_ERROR_NOT_READY = -7,
+ TS_RPC_ERROR_INVALID_TRANSACTION = -8,
+ TS_RPC_ERROR_INTERNAL = -9,
+ TS_RPC_ERROR_INVALID_PARAMETER = -10,
+ TS_RPC_ERROR_INTERFACE_DOES_NOT_EXIST = -11,
+ TS_RPC_ERROR_ACCESS_DENIED = -12
};
#endif /* PROTOCOLS_RPC_COMMON_STATUS_H */
diff --git a/protocols/service/smm_variable/smm_variable_proto.h b/protocols/service/smm_variable/smm_variable_proto.h
index 9e15bc1..a47be10 100644
--- a/protocols/service/smm_variable/smm_variable_proto.h
+++ b/protocols/service/smm_variable/smm_variable_proto.h
@@ -10,4 +10,8 @@
#include <protocols/service/smm_variable/opcodes.h>
#include <protocols/service/smm_variable/parameters.h>
+/* The GUID used to identify the SMM Variable service */
+#define SMM_VARIABLE_GUID \
+ {0xed32d533, 0x99e6, 0x4209, { 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7 }}
+
#endif /* TS_SMM_VARIABLE_PROTO_H */