Add mm communicate service location strategy

To support service discovery and RPC session extablishment
for SMM services reached via MM Communicate, a new service
location strategy and service context has been added to the
service locator. Client applications that need to use say the
smm-variable service can now use the service locator to
set-up a session.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I4a97af4763c6a48b9dbe2dcf87ec2c79654131de
diff --git a/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h b/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h
index ba15864..8420681 100644
--- a/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h
+++ b/components/rpc/mm_communicate/caller/linux/mm_communicate_caller.h
@@ -28,7 +28,6 @@
 	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;
@@ -55,7 +54,6 @@
 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(
diff --git a/components/service/locator/linux/ffa/linuxffa_location_strategy.c b/components/service/locator/linux/ffa/linuxffa_location_strategy.c
index 0c0b16c..a6ec465 100644
--- a/components/service/locator/linux/ffa/linuxffa_location_strategy.c
+++ b/components/service/locator/linux/ffa/linuxffa_location_strategy.c
@@ -116,7 +116,6 @@
 		{"protected-storage",           "751bf801-3dde-4768-a514-0f10aeed1790",	0},
 		{"test-runner",                 "33c75baf-ac6a-4fe4-8ac7-e9909bee2d17",	0},
 		{"attestation",                 "a1baf155-8876-4695-8f7c-54955e8db974",	0},
-		{"smm-variable",                "ed32d533-99e6-4209-9cc0-2d72cdd998a7",	0},
 
 		/* Secure Enclave proxy accessed services */
 		{"crypto",                      "46bb39d1-b4d9-45b5-88ff-040027dab249",	SE_PROXY_INTERFACE_ID_CRYPTO},
diff --git a/components/service/locator/linux/linux_env.c b/components/service/locator/linux/linux_env.c
index cb940f4..4bef301 100644
--- a/components/service/locator/linux/linux_env.c
+++ b/components/service/locator/linux/linux_env.c
@@ -1,18 +1,19 @@
 /*
- * 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
  */
 
 #include <service_locator.h>
 #include <service/locator/linux/ffa/linuxffa_location_strategy.h>
-
+#include <service/locator/linux/mm_communicate/mm_communicate_location_strategy.h>
 
 void service_locator_envinit(void)
 {
-    /*
-     * Register all service location strategies that could be used
-     * to locate services from Linux userspace.
-     */
-    service_locator_register_strategy(linuxffa_location_strategy());
-}
\ No newline at end of file
+	/*
+	 * Register all service location strategies that could be used
+	 * to locate services from Linux userspace.
+	 */
+	service_locator_register_strategy(linuxffa_location_strategy());
+	service_locator_register_strategy(mm_communicate_location_strategy());
+}
diff --git a/components/service/locator/linux/mm_communicate/component.cmake b/components/service/locator/linux/mm_communicate/component.cmake
new file mode 100644
index 0000000..13bc961
--- /dev/null
+++ b/components/service/locator/linux/mm_communicate/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# 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}/mm_communicate_location_strategy.c"
+	"${CMAKE_CURRENT_LIST_DIR}/mm_communicate_service_context.c"
+	)
diff --git a/components/service/locator/linux/mm_communicate/mm_communicate_location_strategy.c b/components/service/locator/linux/mm_communicate/mm_communicate_location_strategy.c
new file mode 100644
index 0000000..632c682
--- /dev/null
+++ b/components/service/locator/linux/mm_communicate/mm_communicate_location_strategy.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+#include "mm_communicate_location_strategy.h"
+#include "mm_communicate_service_context.h"
+#include <common/uuid/uuid.h>
+#include <service/locator/service_name.h>
+#include <rpc/mm_communicate/caller/linux/mm_communicate_caller.h>
+#include <protocols/service/smm_variable/smm_variable_proto.h>
+
+#define MAX_PARTITION_INSTANCES		(1)
+
+/* Structure to define the location of an smm service */
+struct smm_service_location
+{
+	struct uuid_canonical uuid;
+	uint16_t partition_id;
+	EFI_GUID svc_guid;
+};
+
+/* Structure to map a service name to FFA and MM Communicate labels */
+struct smm_service_label
+{
+	const char *service;
+	const char *uuid;
+	EFI_GUID svc_guid;
+};
+
+bool find_candidate_location(const char *sn, struct smm_service_location *location)
+{
+	static const struct smm_service_label service_lookup[] =
+	{
+		{
+			.service = "smm-variable",
+			.uuid = "ed32d533-99e6-4209-9cc0-2d72cdd998a7",
+			.svc_guid = SMM_VARIABLE_GUID
+		},
+		{
+			/* Terminator */
+			.service = NULL
+		}
+	};
+
+	bool found = false;
+	const struct smm_service_label *entry = &service_lookup[0];
+
+	while (entry->service) {
+
+		if (sn_check_service(sn, entry->service)) {
+
+			/* Found a match */
+			memcpy(location->uuid.characters,
+				entry->uuid,
+				UUID_CANONICAL_FORM_LEN + 1);
+
+			location->svc_guid = entry->svc_guid;
+
+			found = true;
+			break;
+		}
+
+		++entry;
+	}
+
+	return found;
+}
+
+bool discover_partition(const char *dev_path, struct smm_service_location *location)
+{
+	bool discovered = false;
+
+	if (uuid_is_valid(location->uuid.characters) == UUID_CANONICAL_FORM_LEN) {
+
+		struct mm_communicate_caller mm_communicate_caller;
+
+		mm_communicate_caller_init(&mm_communicate_caller, dev_path);
+
+		uint16_t discovered_partitions[MAX_PARTITION_INSTANCES];
+		size_t discovered_count;
+
+		discovered_count = mm_communicate_caller_discover(
+			&mm_communicate_caller,
+			&location->uuid,
+			discovered_partitions,
+			MAX_PARTITION_INSTANCES);
+
+		if (discovered_count > 0) {
+
+			location->partition_id = discovered_partitions[0];
+			discovered = true;
+		}
+
+		mm_communicate_caller_deinit(&mm_communicate_caller);
+	}
+
+	return discovered;
+}
+
+static struct service_context *query(const char *sn, int *status)
+{
+	struct service_context *result = NULL;
+
+	if (!sn_check_authority(sn, "trustedfirmware.org")) return NULL;
+
+	struct smm_service_location location;
+	const char *dev_path = "/sys/kernel/debug/arm_ffa_user";
+
+	if (find_candidate_location(sn, &location) &&
+		discover_partition(dev_path, &location)) {
+
+		struct mm_communicate_service_context *new_context =
+			mm_communicate_service_context_create(
+				dev_path,
+				location.partition_id,
+				&location.svc_guid);
+
+		if (new_context) result = &new_context->service_context;
+	}
+
+	return result;
+}
+
+const struct service_location_strategy *mm_communicate_location_strategy(void)
+{
+	static const struct service_location_strategy strategy = { query };
+	return &strategy;
+}
diff --git a/components/service/locator/linux/mm_communicate/mm_communicate_location_strategy.h b/components/service/locator/linux/mm_communicate/mm_communicate_location_strategy.h
new file mode 100644
index 0000000..324ad36
--- /dev/null
+++ b/components/service/locator/linux/mm_communicate/mm_communicate_location_strategy.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MM_COMMUNICATE_LOCATION_STRATEGY_H
+#define MM_COMMUNICATE_LOCATION_STRATEGY_H
+
+#include <service_locator.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Returns a service_location_strategy for locating a service instance
+ * hosted in a secure partition, accessed using MM Communicate from Linux userspace.
+ * Relies on an FFA Linux kernel driver.
+ */
+const struct service_location_strategy *mm_communicate_location_strategy(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MM_COMMUNICATE_LOCATION_STRATEGY_H */
diff --git a/components/service/locator/linux/mm_communicate/mm_communicate_service_context.c b/components/service/locator/linux/mm_communicate/mm_communicate_service_context.c
new file mode 100644
index 0000000..7e6330d
--- /dev/null
+++ b/components/service/locator/linux/mm_communicate/mm_communicate_service_context.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdlib.h>
+#include <rpc/mm_communicate/caller/linux/mm_communicate_caller.h>
+#include "mm_communicate_service_context.h"
+
+/* Concrete service_context methods */
+static rpc_session_handle mm_communicate_service_context_open(void *context,
+	struct rpc_caller **caller);
+static void mm_communicate_service_context_close(void *context,
+	rpc_session_handle session_handle);
+static void mm_communicate_service_context_relinquish(void *context);
+
+
+struct mm_communicate_service_context *mm_communicate_service_context_create(
+	const char *dev_path,
+	uint16_t partition_id,
+	const EFI_GUID *svc_guid)
+{
+	struct mm_communicate_service_context *new_context = (struct mm_communicate_service_context*)
+		malloc(sizeof(struct mm_communicate_service_context));
+
+	if (new_context) {
+
+		new_context->ffa_dev_path = dev_path;
+		new_context->partition_id = partition_id;
+		new_context->svc_guid = *svc_guid;
+
+		new_context->service_context.context = new_context;
+		new_context->service_context.open = mm_communicate_service_context_open;
+		new_context->service_context.close = mm_communicate_service_context_close;
+		new_context->service_context.relinquish = mm_communicate_service_context_relinquish;
+	}
+
+	return new_context;
+}
+
+static rpc_session_handle mm_communicate_service_context_open(
+	void *context,
+	struct rpc_caller **caller)
+{
+	struct mm_communicate_service_context *this_context =
+		(struct mm_communicate_service_context*)context;
+
+	rpc_session_handle session_handle = NULL;
+
+	struct mm_communicate_caller *mm_communicate_caller =
+		(struct mm_communicate_caller*)malloc(sizeof(struct mm_communicate_caller));
+
+	if (mm_communicate_caller) {
+
+		*caller = mm_communicate_caller_init(mm_communicate_caller,
+			this_context->ffa_dev_path);
+		int status = mm_communicate_caller_open(mm_communicate_caller,
+			this_context->partition_id, &this_context->svc_guid);
+
+		if (status == 0) {
+			/* Successfully opened session */
+			session_handle = mm_communicate_caller;
+		}
+		else {
+			/* Failed to open session */
+			mm_communicate_caller_close(mm_communicate_caller);
+			mm_communicate_caller_deinit(mm_communicate_caller);
+			free(mm_communicate_caller);
+		}
+	}
+
+	return session_handle;
+}
+
+static void mm_communicate_service_context_close(
+	void *context,
+	rpc_session_handle session_handle)
+{
+	struct mm_communicate_caller *mm_communicate_caller =
+		(struct mm_communicate_caller*)session_handle;
+
+	(void)context;
+
+	if (mm_communicate_caller) {
+
+		mm_communicate_caller_close(mm_communicate_caller);
+		mm_communicate_caller_deinit(mm_communicate_caller);
+		free(mm_communicate_caller);
+	}
+}
+
+static void mm_communicate_service_context_relinquish(
+	void *context)
+{
+	struct mm_communicate_service_context *this_context =
+		(struct mm_communicate_service_context*)context;
+
+	free(this_context);
+}
diff --git a/components/service/locator/linux/mm_communicate/mm_communicate_service_context.h b/components/service/locator/linux/mm_communicate/mm_communicate_service_context.h
new file mode 100644
index 0000000..6771c31
--- /dev/null
+++ b/components/service/locator/linux/mm_communicate/mm_communicate_service_context.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MM_COMMUNICATE_SERVICE_CONTEXT_H
+#define MM_COMMUNICATE_SERVICE_CONTEXT_H
+
+#include <service_locator.h>
+#include <protocols/common/efi/efi_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * A service_context that represents a service instance located in
+ * a partition, accessed using the MM Communicate protocol over
+ * FFA.
+ */
+struct mm_communicate_service_context
+{
+	struct service_context service_context;
+	const char *ffa_dev_path;
+	uint16_t partition_id;
+	EFI_GUID svc_guid;
+};
+
+/*
+ * Factory method to create a service context associated with the specified
+ * partition id and service GUID.
+ */
+struct mm_communicate_service_context *mm_communicate_service_context_create(
+	const char *dev_path,
+	uint16_t partition_id,
+	const EFI_GUID *svc_guid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MM_COMMUNICATE_SERVICE_CONTEXT_H */