Add service locator component

Change-Id: I58ad588deff3c0904b3ba8b9162b20440e9ff7d3
Signed-off-by: Julian Hall <julian.hall@arm.com>
diff --git a/components/service/locator/component.cmake b/components/service/locator/component.cmake
new file mode 100644
index 0000000..fe7d137
--- /dev/null
+++ b/components/service/locator/component.cmake
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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}/service_locator.c"
+	"${CMAKE_CURRENT_LIST_DIR}/service_name.c"
+	)
+
diff --git a/components/service/locator/interface/component.cmake b/components/service/locator/interface/component.cmake
new file mode 100644
index 0000000..b5aefa3
--- /dev/null
+++ b/components/service/locator/interface/component.cmake
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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()
+
+set_property(TARGET ${TGT} PROPERTY SERVICE_LOCATOR_PUBLIC_HEADER_FILES
+	"${CMAKE_CURRENT_LIST_DIR}/service_locator.h"
+	)
+
+target_include_directories(${TGT} PUBLIC
+	"${CMAKE_CURRENT_LIST_DIR}"
+	)
diff --git a/components/service/locator/interface/service_locator.h b/components/service/locator/interface/service_locator.h
new file mode 100644
index 0000000..3e1404b
--- /dev/null
+++ b/components/service/locator/interface/service_locator.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SERVICE_LOCATOR_H
+#define SERVICE_LOCATOR_H
+
+#include <rpc_caller.h>
+
+/*
+ * The service_locator puplic interface may be exported as a public interface to
+ * a shared library.
+ */
+#ifdef EXPORT_PUBLIC_INTERFACE_SERVICE_LOCATOR
+#define SERVICE_LOCATOR_EXPORTED __attribute__((__visibility__("default")))
+#else
+#define SERVICE_LOCATOR_EXPORTED
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The service_locator provides an interface for locating trusted service
+ * provider instances and establishing RPC sessions for using the service.
+ * The service_locator decouples clients from the details of any particular
+ * service deployment.  By accessing trusted services using the service_locator,
+ * client code may be reused accross different service deployment scenarios.
+ */
+struct service_location_strategy;
+typedef void* rpc_session_handle;
+
+/*
+ * Initialises the singleton service locator.  Must be called once
+ * but may be called more than once.
+ */
+SERVICE_LOCATOR_EXPORTED void service_locator_init(void);
+
+/*
+ * An environment specific implementation of this function is
+ * needed in order to initialise and register the set of
+ * concrete service_location_strategy objects to use when
+ * building a deployment for a particular environment.
+ */
+void service_locator_envinit(void);
+
+/*
+ * Register a service_location_strategy for locating service
+ * instances.  When attempting to locate a service, the set
+ * of registered service_location_strategy objects will be tried
+ * in the order of registration.
+ */
+void service_locator_register_strategy(const struct service_location_strategy *strategy);
+
+/*
+ * Query to locate a service instance.  If the given service name
+ * corresponds to an available service instance, a service_context
+ * is returned.  The client should hang onto this until it has
+ * finished using the service.  When the service is no longer needed,
+ * the client should call the relinquish method.  Returns NULL
+ * if no service is located that corresponds to the service name.
+ */
+SERVICE_LOCATOR_EXPORTED struct service_context *service_locator_query(const char *sn, int *status);
+
+/*
+ * The service_context struct represents a service instance to a client
+ * after having located the service instance using the service locator.  A
+ * service_context object allows a client to open and close RPC sessions
+ * associated with the service instance, wherever it happens to be deployed.
+ */
+struct service_context
+{
+    void *context;
+
+    rpc_session_handle (*open)(void *context, struct rpc_caller **caller);
+    void (*close)(void *context, rpc_session_handle session_handle);
+    void (*relinquish)(void *context);
+};
+
+/*
+ * Provides an abstract interface for a strategy that locates services
+ * in a particular way.  The set of registered strategies forms a
+ * chain of responsibility for resolving a query made by a clisnt.
+ */
+struct service_location_strategy
+{
+    struct service_context *(*query)(const char *sn, int *status);
+};
+
+/*
+ * Open an RPC session in order to use the service associated with this
+ * service_context.
+ */
+SERVICE_LOCATOR_EXPORTED rpc_session_handle service_context_open(struct service_context *s, struct rpc_caller **caller);
+
+/*
+ * Close an RPC session.
+ */
+SERVICE_LOCATOR_EXPORTED void service_context_close(struct service_context *s, rpc_session_handle session_handle);
+
+/*
+ * Called by a client when it has finished using a service context.
+ */
+SERVICE_LOCATOR_EXPORTED void service_context_relinquish(struct service_context *context);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SERVICE_LOCATOR_H */
diff --git a/components/service/locator/linux/component.cmake b/components/service/locator/linux/component.cmake
new file mode 100644
index 0000000..4247c69
--- /dev/null
+++ b/components/service/locator/linux/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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}/linux_env.c"
+	)
+
diff --git a/components/service/locator/linux/ffa/component.cmake b/components/service/locator/linux/ffa/component.cmake
new file mode 100644
index 0000000..1e5a287
--- /dev/null
+++ b/components/service/locator/linux/ffa/component.cmake
@@ -0,0 +1,15 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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}/linuxffa_location_strategy.c"
+	"${CMAKE_CURRENT_LIST_DIR}/linuxffa_service_context.c"
+	)
+
diff --git a/components/service/locator/linux/ffa/linuxffa_location_strategy.c b/components/service/locator/linux/ffa/linuxffa_location_strategy.c
new file mode 100644
index 0000000..8c1cb64
--- /dev/null
+++ b/components/service/locator/linux/ffa/linuxffa_location_strategy.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "linuxffa_location_strategy.h"
+#include "linuxffa_service_context.h"
+#include <common/uuid/uuid.h>
+#include <service/locator/service_name.h>
+#include <rpc/ffarpc/caller/linux/ffarpc_caller.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/*
+ * There is the potential for there to be alternative deployment possibilities
+ * for a service instance.  This will depend on deployment decisions such as
+ * running one service instance per SP, or multiple services per SP.  The FFA
+ * location strategy accommodates this by allowing for more than one suggestion
+ * for a service to partition mapping.
+ */
+#define MAX_PARTITION_SUGGESTIONS           (4)
+
+/*
+ * The maximum number of partition instances, identified by a particular UUID,
+ * that may be discovered.
+ */
+#define MAX_PARTITION_INSTANCES             (4)
+
+static struct service_context *query(const char *sn, int *status);
+static size_t suggest_tf_org_partition_uuids(const char *sn, struct uuid_canonical *uuids, size_t uuid_limit);
+static size_t use_ffa_partition_uuid(const char *sn, struct uuid_canonical *uuids, size_t uuid_limit);
+static bool discover_partition(const char *sn, struct uuid_canonical *uuid, const char *dev_path, uint16_t *partition_id);
+
+const struct service_location_strategy *linuxffa_location_strategy(void)
+{
+    static const struct service_location_strategy strategy = { query };
+    return &strategy;
+}
+
+static struct service_context *query(const char *sn, int *status)
+{
+    struct service_context *result = NULL;
+    struct uuid_canonical uuids[MAX_PARTITION_SUGGESTIONS];
+    size_t num_suggestons = 0;
+    size_t suggeston_index;
+
+    /* Determine one or more candidate partition UUIDs from the specified service name. */
+    if (sn_check_authority(sn, "trustedfirmware.org")) {
+        num_suggestons = suggest_tf_org_partition_uuids(sn, uuids, MAX_PARTITION_SUGGESTIONS);
+    }
+    else if (sn_check_authority(sn, "ffa")) {
+        num_suggestons = use_ffa_partition_uuid(sn, uuids, MAX_PARTITION_SUGGESTIONS);
+    }
+
+    /* Attempt to discover suitable partitions */
+    for (suggeston_index = 0; suggeston_index < num_suggestons; ++suggeston_index) {
+
+        uint16_t partition_id;
+        const char *dev_path = "/sys/kernel/debug/buf";
+
+        if (discover_partition(sn, &uuids[suggeston_index], dev_path, &partition_id)) {
+
+            struct linuxffa_service_context *new_context = linuxffa_service_context_create(dev_path, partition_id);
+
+            if (new_context) result = &new_context->service_context;
+            break;
+        }
+    }
+
+    return result;
+}
+
+/*
+ * Returns a list of partition UUIDs to identify partitions that could potentially host the
+ * requested service.  This mapping is based trustedfirmware.org ffa partition UUIDs. There
+ * may be multiple UUIDs because of different depeloyment decisions such as dedicated SP,
+ * SP hosting multple services.
+ */
+static size_t suggest_tf_org_partition_uuids(const char *sn, struct uuid_canonical *uuids, size_t uuid_limit)
+{
+    const struct service_to_uuid
+    {
+        const char *service;
+        const char *uuid;
+    }
+    partition_lookup[] =
+    {
+        {"crypto",              "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0"},
+        {"secure-storage",      "dc1eef48-b17a-4ccf-ac8b-dfcff7711b14"},
+        {NULL,                  NULL}
+    };
+
+    const struct service_to_uuid *entry = &partition_lookup[0];
+    size_t num_suggestions = 0;
+
+    while (entry->service && (num_suggestions < uuid_limit)) {
+
+        if (sn_check_service(sn, entry->service)) {
+
+            memcpy(uuids[num_suggestions].characters, entry->uuid, UUID_CANONICAL_FORM_LEN + 1);
+            ++num_suggestions;
+        }
+
+        ++entry;
+    }
+
+    return num_suggestions;
+}
+
+/*
+ * When an ffa service name where the service field is an explicit UUID is used, the UUID
+ * is used directly for partition discovery.
+ */
+static size_t use_ffa_partition_uuid(const char *sn, struct uuid_canonical *uuids, size_t uuid_limit)
+{
+    size_t num_suggestions = 0;
+
+    if ((num_suggestions < uuid_limit) &&
+        (sn_read_service(sn, uuids[num_suggestions].characters, UUID_CANONICAL_FORM_LEN + 1) == UUID_CANONICAL_FORM_LEN)) {
+
+        ++num_suggestions;
+    }
+
+    return num_suggestions;
+}
+
+/*
+ * Attempt to discover the partition that hosts the requested service instance.
+ */
+static bool discover_partition(const char *sn, struct uuid_canonical *uuid,
+                            const char *dev_path, uint16_t *partition_id)
+{
+    bool discovered = false;
+    struct uuid_octets uuid_bytes;
+
+    if (uuid_parse_to_octets_reversed(uuid->characters, uuid_bytes.octets, UUID_OCTETS_LEN) == UUID_CANONICAL_FORM_LEN) {
+
+        struct ffarpc_caller ffarpc_caller;
+        unsigned int required_instance = sn_get_service_instance(sn);
+
+        ffarpc_caller_init(&ffarpc_caller, dev_path);
+
+        uint16_t discovered_partitions[MAX_PARTITION_INSTANCES];
+        size_t discovered_count;
+
+        discovered_count = ffarpc_caller_discover(&ffarpc_caller, uuid_bytes.octets,
+                                        discovered_partitions, MAX_PARTITION_INSTANCES);
+
+        if ((discovered_count > 0) && (required_instance < discovered_count)) {
+
+            *partition_id = discovered_partitions[required_instance];
+            discovered = true;
+        }
+
+        ffarpc_caller_deinit(&ffarpc_caller);
+    }
+
+    return discovered;
+}
diff --git a/components/service/locator/linux/ffa/linuxffa_location_strategy.h b/components/service/locator/linux/ffa/linuxffa_location_strategy.h
new file mode 100644
index 0000000..6bb7eeb
--- /dev/null
+++ b/components/service/locator/linux/ffa/linuxffa_location_strategy.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LINUXFFA_LOCATION_STRATEGY_H
+#define LINUXFFA_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 FFA from Linux userspace.
+ * Relies on an FFA Linux kernel driver.
+ */
+const struct service_location_strategy *linuxffa_location_strategy(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINUXFFA_LOCATION_STRATEGY_H */
\ No newline at end of file
diff --git a/components/service/locator/linux/ffa/linuxffa_service_context.c b/components/service/locator/linux/ffa/linuxffa_service_context.c
new file mode 100644
index 0000000..2af7c60
--- /dev/null
+++ b/components/service/locator/linux/ffa/linuxffa_service_context.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "linuxffa_service_context.h"
+#include <rpc/ffarpc/caller/linux/ffarpc_caller.h>
+#include <stdlib.h>
+
+/* Concrete service_context methods */
+static rpc_session_handle linuxffa_service_context_open(void *context, struct rpc_caller **caller);
+static void linuxffa_service_context_close(void *context, rpc_session_handle session_handle);
+static void linuxffa_service_context_relinquish(void *context);
+
+
+struct linuxffa_service_context *linuxffa_service_context_create(const char *dev_path, uint16_t partition_id)
+{
+    struct linuxffa_service_context *new_context =
+        (struct linuxffa_service_context*)malloc(sizeof(struct linuxffa_service_context));
+
+    if (new_context) {
+        new_context->ffa_dev_path = dev_path;
+        new_context->partition_id = partition_id;
+
+        new_context->service_context.context = new_context;
+        new_context->service_context.open = linuxffa_service_context_open;
+        new_context->service_context.close = linuxffa_service_context_close;
+        new_context->service_context.relinquish = linuxffa_service_context_relinquish;
+    }
+
+    return new_context;
+}
+
+static rpc_session_handle linuxffa_service_context_open(void *context, struct rpc_caller **caller)
+{
+    struct linuxffa_service_context *this_context = (struct linuxffa_service_context*)context;
+    rpc_session_handle session_handle = NULL;
+    struct ffarpc_caller *ffarpc_caller = (struct ffarpc_caller*)malloc(sizeof(struct ffarpc_caller));
+
+    if (ffarpc_caller) {
+
+        int status;
+        *caller = ffarpc_caller_init(ffarpc_caller, this_context->ffa_dev_path);
+        status = ffarpc_caller_open(ffarpc_caller, this_context->partition_id);
+
+		if (status == 0) {
+            /* Successfully opened session */
+            session_handle = ffarpc_caller;
+		}
+		else {
+            /* Failed to open session */
+			ffarpc_caller_close(ffarpc_caller);
+            ffarpc_caller_deinit(ffarpc_caller);
+            free(ffarpc_caller);
+		}
+    }
+
+    return session_handle;
+}
+
+static void linuxffa_service_context_close(void *context, rpc_session_handle session_handle)
+{
+    struct ffarpc_caller *ffarpc_caller = (struct ffarpc_caller*)session_handle;
+
+    (void)context;
+
+    if (ffarpc_caller) {
+
+		ffarpc_caller_close(ffarpc_caller);
+        ffarpc_caller_deinit(ffarpc_caller);
+        free(ffarpc_caller);
+    }
+}
+
+static void linuxffa_service_context_relinquish(void *context)
+{
+    struct linuxffa_service_context *this_context = (struct linuxffa_service_context*)context;
+    free(this_context);
+}
diff --git a/components/service/locator/linux/ffa/linuxffa_service_context.h b/components/service/locator/linux/ffa/linuxffa_service_context.h
new file mode 100644
index 0000000..a16fe13
--- /dev/null
+++ b/components/service/locator/linux/ffa/linuxffa_service_context.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef LINUXFFA_SERVICE_CONTEXT_H
+#define LINUXFFA_SERVICE_CONTEXT_H
+
+#include <service_locator.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * A service_context that represents a service instance located in
+ * a partition, accessed via FFA.  This service_context is suitable
+ * for use by client applications running in Linux userspace.
+ */
+struct linuxffa_service_context
+{
+    struct service_context service_context;
+    const char *ffa_dev_path;
+    uint16_t partition_id;
+};
+
+/*
+ * Factory method to create a service context associated with theh specified
+ * partition id.
+ */
+struct linuxffa_service_context *linuxffa_service_context_create(const char *dev_path, uint16_t partition_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LINUXFFA_SERVICE_CONTEXT_H */
diff --git a/components/service/locator/linux/linux_env.c b/components/service/locator/linux/linux_env.c
new file mode 100644
index 0000000..cb940f4
--- /dev/null
+++ b/components/service/locator/linux/linux_env.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2020, 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>
+
+
+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
diff --git a/components/service/locator/service_locator.c b/components/service/locator/service_locator.c
new file mode 100644
index 0000000..90cb457
--- /dev/null
+++ b/components/service/locator/service_locator.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <service_locator.h>
+#include "service_name.h"
+#include <assert.h>
+#include <stddef.h>
+
+#define SERVICE_LOCATOR_MAX_STATEGIES       (8)
+
+/*
+ * The singleton service_locator object.
+ */
+static struct service_locator
+{
+    unsigned int num_strategies;
+    const struct service_location_strategy *strategies[SERVICE_LOCATOR_MAX_STATEGIES];
+
+} service_locator_instance = { .num_strategies = 0 };
+
+
+void service_locator_init(void)
+{
+    if (service_locator_instance.num_strategies == 0) service_locator_envinit();
+}
+
+void service_locator_register_strategy(const struct service_location_strategy *strategy)
+{
+    assert(service_locator_instance.num_strategies < SERVICE_LOCATOR_MAX_STATEGIES);
+
+    if (service_locator_instance.num_strategies < SERVICE_LOCATOR_MAX_STATEGIES) {
+
+        service_locator_instance.strategies[service_locator_instance.num_strategies] = strategy;
+        ++service_locator_instance.num_strategies;
+    }
+}
+
+struct service_context *service_locator_query(const char *sn, int *status)
+{
+    struct service_context *located_context = NULL;
+    unsigned int index = 0;
+
+    if (sn_is_valid(sn)) {
+
+        while (!located_context && (index < service_locator_instance.num_strategies)) {
+
+            located_context = service_locator_instance.strategies[index]->query(sn, status);
+            ++index;
+        }
+    }
+
+    return located_context;
+}
+
+rpc_session_handle service_context_open(struct service_context *s, struct rpc_caller **caller)
+{
+    return s->open(s->context, caller);
+}
+
+void service_context_close(struct service_context *s, rpc_session_handle session_handle)
+{
+    s->close(s->context, session_handle);
+}
+
+void service_context_relinquish(struct service_context *s)
+{
+    s->relinquish(s->context);
+}
diff --git a/components/service/locator/service_name.c b/components/service/locator/service_name.c
new file mode 100644
index 0000000..878b84b
--- /dev/null
+++ b/components/service/locator/service_name.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "service_name.h"
+
+/*
+ * Structure to hold result of parsing the service name.  Sets
+ * indices and length of the following fields.  Lengths are set to zero
+ * if field is not present.
+ */
+struct sn_parsed_fields
+{
+    size_t authority_pos;
+    size_t authority_len;
+    size_t service_pos;
+    size_t service_len;
+    size_t version_pos;
+    size_t version_len;
+    size_t instance_pos;
+    size_t instance_len;
+};
+
+/*
+ * Find the lengthof the current field, up to the specified separator or end
+ * of the service name.
+ */
+static size_t field_len(const char *sn, size_t from, size_t len, char separator)
+{
+    size_t field_len = 0;
+    size_t pos = from;
+
+    while (pos < len) {
+
+        if (sn[pos] == separator) break;
+
+        ++pos;
+        ++field_len;
+    }
+
+    return field_len;
+}
+
+/*
+ * Find the next field in the service name after the specified separator from the
+ * specified position.
+ */
+static bool find_next(const char *sn, size_t from, size_t len, char separator, size_t *next)
+{
+    bool found = false;
+    size_t pos = from;
+
+    while (pos < len) {
+
+        if (sn[pos] == separator) {
+            *next = pos + 1;
+            found = (*next < len);
+            break;
+        }
+
+        ++pos;
+    }
+
+    return found;
+}
+
+/*
+ * Parse the service name.  Returns true if is a legal service
+ * name and mandatory fields are present;
+ */
+static bool sn_parse(const char *sn, struct sn_parsed_fields *fields)
+{
+    size_t sn_len = strlen(sn);
+    size_t field_pos = 0;
+
+    fields->authority_pos = 0;
+    fields->authority_len = 0;
+    fields->service_pos = 0;
+    fields->service_len = 0;
+    fields->version_pos = 0;
+    fields->version_len = 0;
+    fields->instance_pos = 0;
+    fields->instance_len = 0;
+
+    /* Absorb urn: if present */
+    if (memcmp("urn", &sn[field_pos], strlen("urn")) == 0) {
+        if (!find_next(sn, field_pos, sn_len, ':', &field_pos)) return false;
+    }
+
+    /* Check it is a service name */
+    if (memcmp("sn", &sn[field_pos], strlen("sn")) != 0) {
+        /* Not a service name */
+         return false;
+    }
+
+    /* Expect the authority field */
+    if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
+        fields->authority_pos = field_pos;
+        fields->authority_len = field_len(sn, field_pos, sn_len, ':');
+    }
+    else {
+        /* Missing mandatory authority field */
+        return false;
+    }
+
+    /* Expect the service field */
+    if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
+        fields->service_pos = field_pos;
+        fields->service_len = field_len(sn, field_pos, sn_len, ':');
+
+        /* Check for the optional version */
+        if (find_next(sn, field_pos, field_pos + fields->service_len, '.', &field_pos)) {
+            fields->version_pos = field_pos;
+            fields->version_len = field_len(sn, field_pos, sn_len, ':');
+        }
+    }
+    else {
+        /* Missing mandatory service field */
+        return false;
+    }
+
+    /* Check for optional instance */
+    if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
+        fields->instance_pos = field_pos;
+        fields->instance_len = sn_len - field_pos;
+    }
+
+    return true;
+}
+
+bool sn_is_valid(const char *sn)
+{
+    struct sn_parsed_fields fields;
+    return sn_parse(sn, &fields);
+}
+
+bool sn_check_authority(const char *sn, const char *auth)
+{
+    bool matched = false;
+    struct sn_parsed_fields fields;
+
+    if (sn_parse(sn, &fields) && fields.authority_len) {
+
+        matched = (memcmp(auth, &sn[fields.authority_pos], strlen(auth)) == 0);
+    }
+
+    return matched;
+}
+
+bool sn_check_service(const char *sn, const char *service)
+{
+    bool matched = false;
+    struct sn_parsed_fields fields;
+
+    if (sn_parse(sn, &fields) && fields.service_len) {
+
+        matched = (memcmp(service, &sn[fields.service_pos], strlen(service)) == 0);
+    }
+
+    return matched;
+}
+
+unsigned int sn_get_service_instance(const char *sn)
+{
+    unsigned int instance = 0;
+
+    struct sn_parsed_fields fields;
+
+    if (sn_parse(sn, &fields) && fields.instance_len) {
+
+        sscanf(&sn[fields.instance_pos], "%d", &instance);
+    }
+
+    return instance;
+}
+
+size_t sn_read_service(const char *sn, char *buf, size_t buf_len)
+{
+    size_t field_len = 0;
+
+    memset(buf, 0, buf_len);
+    struct sn_parsed_fields fields;
+
+    if (sn_parse(sn, &fields) && fields.service_len && (fields.service_len < buf_len)) {
+
+        field_len = fields.service_len;
+        memcpy(buf, &sn[fields.service_pos], field_len);
+        buf[field_len] = '\0';
+    }
+
+    return field_len;
+}
diff --git a/components/service/locator/service_name.h b/components/service/locator/service_name.h
new file mode 100644
index 0000000..441dea4
--- /dev/null
+++ b/components/service/locator/service_name.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SERVICE_NAME_H
+#define SERVICE_NAME_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Check if it is valid service name with all the mandatory sections.
+ */
+bool sn_is_valid(const char *sn);
+
+/*
+ * Check naming authority. Returns true for match.
+ */
+bool sn_check_authority(const char *sn, const char *auth);
+
+/*
+ * Check the service portion of the service name. Returns true for match.
+ */
+bool sn_check_service(const char *sn, const char *service);
+
+/*
+ * Get the service instance number
+ */
+unsigned int sn_get_service_instance(const char *sn);
+
+/*
+ * Read the service field and copy the null terminated string
+ * to the provided buffer.  Returns the copied string length,
+ * minus the zero terminator.
+ */
+size_t sn_read_service(const char *sn, char *buf, size_t buf_len);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SERVICE_NAME_H */
diff --git a/components/service/locator/standalone/component.cmake b/components/service/locator/standalone/component.cmake
new file mode 100644
index 0000000..cb75c82
--- /dev/null
+++ b/components/service/locator/standalone/component.cmake
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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}/standalone_env.cpp"
+	"${CMAKE_CURRENT_LIST_DIR}/standalone_service_context.cpp"
+	"${CMAKE_CURRENT_LIST_DIR}/standalone_service_registry.cpp"
+	"${CMAKE_CURRENT_LIST_DIR}/standalone_location_strategy.cpp"
+	)
+
diff --git a/components/service/locator/standalone/services/crypto/component.cmake b/components/service/locator/standalone/services/crypto/component.cmake
new file mode 100644
index 0000000..0e5529b
--- /dev/null
+++ b/components/service/locator/standalone/services/crypto/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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}/crypto_service_context.cpp"
+	)
+
diff --git a/components/service/locator/standalone/services/crypto/crypto_service_context.cpp b/components/service/locator/standalone/services/crypto/crypto_service_context.cpp
new file mode 100644
index 0000000..e3bc2e8
--- /dev/null
+++ b/components/service/locator/standalone/services/crypto/crypto_service_context.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "crypto_service_context.h"
+
+crypto_service_context::crypto_service_context(const char *sn) :
+    standalone_service_context(sn),
+    m_crypto_provider(),
+    m_storage_provider(),
+    m_storage_caller()
+{
+
+}
+
+crypto_service_context::~crypto_service_context()
+{
+
+}
+
+void crypto_service_context::do_init()
+{
+    struct call_ep *storage_ep = sfs_provider_init(&m_storage_provider);
+    struct rpc_caller *storage_caller = direct_caller_init_default(&m_storage_caller, storage_ep);
+    struct call_ep *crypto_ep = mbed_crypto_provider_init(&m_crypto_provider, storage_caller);
+
+    standalone_service_context::set_call_ep(crypto_ep);
+}
+
+void crypto_service_context::do_deinit()
+{
+    mbed_crypto_provider_deinit(&m_crypto_provider);
+    direct_caller_deinit(&m_storage_caller);
+}
diff --git a/components/service/locator/standalone/services/crypto/crypto_service_context.h b/components/service/locator/standalone/services/crypto/crypto_service_context.h
new file mode 100644
index 0000000..6e79163
--- /dev/null
+++ b/components/service/locator/standalone/services/crypto/crypto_service_context.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STANDALONE_CRYPTO_SERVICE_CONTEXT_H
+#define STANDALONE_CRYPTO_SERVICE_CONTEXT_H
+
+#include <service/locator/standalone/standalone_service_context.h>
+#include <rpc/direct/direct_caller.h>
+#include <service/crypto/provider/mbedcrypto/crypto_provider.h>
+#include <service/secure_storage/provider/secure_flash_store/sfs_provider.h>
+
+class crypto_service_context : public standalone_service_context
+{
+public:
+    crypto_service_context(const char *sn);
+    virtual ~crypto_service_context();
+
+private:
+
+    void do_init();
+    void do_deinit();
+
+    struct mbed_crypto_provider m_crypto_provider;
+    struct sfs_provider m_storage_provider;
+    struct direct_caller m_storage_caller;
+};
+
+#endif /* STANDALONE_CRYPTO_SERVICE_CONTEXT_H */
diff --git a/components/service/locator/standalone/standalone_env.cpp b/components/service/locator/standalone/standalone_env.cpp
new file mode 100644
index 0000000..80d1777
--- /dev/null
+++ b/components/service/locator/standalone/standalone_env.cpp
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <service_locator.h>
+#include <service/locator/standalone/services/crypto/crypto_service_context.h>
+#include "standalone_location_strategy.h"
+#include "standalone_service_registry.h"
+
+void service_locator_envinit(void)
+{
+    static crypto_service_context crypto_context("sn:trustedfirmware.org:crypto:0");
+    standalone_service_registry::instance()->regsiter_service_instance(&crypto_context);
+    service_locator_register_strategy(standalone_location_strategy());
+}
\ No newline at end of file
diff --git a/components/service/locator/standalone/standalone_location_strategy.cpp b/components/service/locator/standalone/standalone_location_strategy.cpp
new file mode 100644
index 0000000..57e370c
--- /dev/null
+++ b/components/service/locator/standalone/standalone_location_strategy.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "standalone_location_strategy.h"
+#include "standalone_service_registry.h"
+#include "standalone_service_context.h"
+
+static struct service_context *query(const char *sn, int *status)
+{
+    struct service_context *result = NULL;
+    standalone_service_registry *registry = standalone_service_registry::instance();
+    standalone_service_context *query_result = registry->query(sn, status);
+
+    if (query_result) {
+
+        result = query_result->get_service_context();
+    }
+
+    return result;
+}
+
+const struct service_location_strategy *standalone_location_strategy(void)
+{
+    static const struct service_location_strategy strategy = { query };
+    return &strategy;
+}
diff --git a/components/service/locator/standalone/standalone_location_strategy.h b/components/service/locator/standalone/standalone_location_strategy.h
new file mode 100644
index 0000000..7ea57ea
--- /dev/null
+++ b/components/service/locator/standalone/standalone_location_strategy.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STANDALONE_LOCATION_STRATEGY_H
+#define STANDALONE_LOCATION_STRATEGY_H
+
+#include <service_locator.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Returns a service_location_strategy for locating a service instance
+ * from a bundle of standalone service instances that are part of the
+ * client execution context.
+ */
+const struct service_location_strategy *standalone_location_strategy(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STANDALONE_LOCATION_STRATEGY_H */
\ No newline at end of file
diff --git a/components/service/locator/standalone/standalone_service_context.cpp b/components/service/locator/standalone/standalone_service_context.cpp
new file mode 100644
index 0000000..5e53ebe
--- /dev/null
+++ b/components/service/locator/standalone/standalone_service_context.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "standalone_service_context.h"
+#include <cassert>
+
+/* Concrete C service_context methods */
+static rpc_session_handle standalone_service_context_open(void *context, struct rpc_caller **caller);
+static void standalone_service_context_close(void *context, rpc_session_handle session_handle);
+static void standalone_service_context_relinquish(void *context);
+
+
+standalone_service_context::standalone_service_context(const char *sn) :
+    m_sn(sn),
+    m_ref_count(0),
+    m_service_context(),
+    m_call_ep(NULL)
+{
+    m_service_context.context = this;
+    m_service_context.open = standalone_service_context_open;
+    m_service_context.close = standalone_service_context_close;
+    m_service_context.relinquish = standalone_service_context_relinquish;
+}
+
+standalone_service_context::~standalone_service_context()
+{
+
+}
+
+void standalone_service_context::init()
+{
+    assert(m_ref_count >= 0);
+
+    if (!m_ref_count) do_init();
+    ++m_ref_count;
+}
+
+void standalone_service_context::deinit()
+{
+    assert(m_ref_count > 0);
+
+    --m_ref_count;
+    if (!m_ref_count) do_deinit();
+}
+
+rpc_session_handle standalone_service_context::open(struct rpc_caller **caller)
+{
+    struct rpc_session *session = new rpc_session(m_call_ep);
+    *caller = session->m_rpc_caller;
+    return static_cast<rpc_session_handle>(session);
+}
+
+void standalone_service_context::close(rpc_session_handle session_handle)
+{
+    struct rpc_session *session = reinterpret_cast<struct rpc_session*>(session_handle);
+    delete session;
+}
+
+const std::string &standalone_service_context::get_service_name() const
+{
+    return m_sn;
+}
+
+struct service_context *standalone_service_context::get_service_context()
+{
+    return &m_service_context;
+}
+
+void standalone_service_context::set_call_ep(call_ep *ep)
+{
+    m_call_ep = ep;
+}
+
+standalone_service_context::rpc_session::rpc_session(struct call_ep *call_ep) :
+    m_direct_caller(),
+    m_rpc_caller()
+{
+    m_rpc_caller = direct_caller_init_default(&m_direct_caller, call_ep);
+}
+
+standalone_service_context::rpc_session::~rpc_session()
+{
+    direct_caller_deinit(&m_direct_caller);
+}
+
+static rpc_session_handle standalone_service_context_open(void *context, struct rpc_caller **caller)
+{
+    rpc_session_handle handle = NULL;
+    standalone_service_context *this_context = reinterpret_cast<standalone_service_context*>(context);
+
+    if (this_context) {
+        handle = this_context->open(caller);
+    }
+
+    return handle;
+}
+
+static void standalone_service_context_close(void *context, rpc_session_handle session_handle)
+{
+    standalone_service_context *this_context = reinterpret_cast<standalone_service_context*>(context);
+
+    if (this_context) {
+        this_context->close(session_handle);
+    }
+}
+
+static void standalone_service_context_relinquish(void *context)
+{
+    standalone_service_context *this_context = reinterpret_cast<standalone_service_context*>(context);
+
+    if (this_context) {
+        this_context->deinit();
+    }
+}
diff --git a/components/service/locator/standalone/standalone_service_context.h b/components/service/locator/standalone/standalone_service_context.h
new file mode 100644
index 0000000..d8ebb1f
--- /dev/null
+++ b/components/service/locator/standalone/standalone_service_context.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STANDALONE_SERVICE_CONTEXT_H
+#define STANDALONE_SERVICE_CONTEXT_H
+
+#include <service_locator.h>
+#include <rpc/common/endpoint/call_ep.h>
+#include <rpc/direct/direct_caller.h>
+#include <string>
+
+class standalone_service_context
+{
+public:
+    standalone_service_context(const char *sn);
+    virtual ~standalone_service_context();
+
+    void init();
+    void deinit();
+
+    rpc_session_handle open(struct rpc_caller **caller);
+    void close(rpc_session_handle session_handle);
+
+    const std::string &get_service_name() const;
+    struct service_context *get_service_context();
+
+protected:
+    void set_call_ep(call_ep *ep);
+
+    virtual void do_init() {}
+    virtual void do_deinit() {}
+
+private:
+
+    struct rpc_session
+    {
+        rpc_session(struct call_ep *call_ep);
+        ~rpc_session();
+
+        struct direct_caller m_direct_caller;
+        struct rpc_caller *m_rpc_caller;
+    };
+
+    std::string m_sn;
+    int m_ref_count;
+    struct service_context m_service_context;
+    struct call_ep *m_call_ep;
+};
+
+#endif /* STANDALONE_SERVICE_CONTEXT_H */
diff --git a/components/service/locator/standalone/standalone_service_registry.cpp b/components/service/locator/standalone/standalone_service_registry.cpp
new file mode 100644
index 0000000..5625bc0
--- /dev/null
+++ b/components/service/locator/standalone/standalone_service_registry.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "standalone_service_registry.h"
+
+standalone_service_registry::standalone_service_registry() :
+    m_service_instances()
+{
+
+}
+
+standalone_service_registry::~standalone_service_registry()
+{
+
+}
+
+standalone_service_registry *standalone_service_registry::instance()
+{
+    static standalone_service_registry singleton_instance;
+    return &singleton_instance;
+}
+
+void standalone_service_registry::regsiter_service_instance(standalone_service_context *service_context)
+{
+    size_t index;
+
+    if (find_context_index(service_context->get_service_name().c_str(), &index)) {
+
+        m_service_instances[index] = service_context;
+    }
+    else {
+
+        m_service_instances.push_back(service_context);
+    }
+}
+
+standalone_service_context *standalone_service_registry::deregsiter_service_instance(const char *sn)
+{
+    size_t index;
+    standalone_service_context *context = NULL;
+
+    if (find_context_index(sn, &index)) {
+
+        context = m_service_instances[index];
+        m_service_instances.erase(m_service_instances.begin() + index);
+    }
+
+    return context;
+}
+
+standalone_service_context *standalone_service_registry::query(const char *sn, int *status)
+{
+    size_t index;
+    standalone_service_context *context = NULL;
+    (void)status;
+
+    if (find_context_index(sn, &index)) {
+
+        context = m_service_instances[index];
+        context->init();
+    }
+
+    return context;
+}
+
+bool standalone_service_registry::find_context_index(const char *sn, size_t *index) const
+{
+    bool found = false;
+
+    for (size_t i = 0; !found && i < m_service_instances.size(); i++) {
+
+        if (m_service_instances[i]->get_service_name().compare(sn) == 0) {
+
+            *index = i;
+            found = true;
+        }
+    }
+
+    return found;
+}
\ No newline at end of file
diff --git a/components/service/locator/standalone/standalone_service_registry.h b/components/service/locator/standalone/standalone_service_registry.h
new file mode 100644
index 0000000..7a76aeb
--- /dev/null
+++ b/components/service/locator/standalone/standalone_service_registry.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STANDALONE_SERVICE_REGISTRY_H
+#define STANDALONE_SERVICE_REGISTRY_H
+
+#include "standalone_service_context.h"
+#include <vector>
+
+/*
+ * The standalone_service_registry supports a set of standalone
+ * service instances that are deployed as part of a standalone exectable
+ * or library.  A standalone deployment of service providers should only
+ * be used for test and development.  Because service provider instances
+ * are running within the same execution context as the client, a
+ * standalone deployment offers no isolation from the client.
+ */
+class standalone_service_registry
+{
+public:
+    standalone_service_registry();
+    virtual ~standalone_service_registry();
+
+    static standalone_service_registry *instance();
+
+    void regsiter_service_instance(standalone_service_context *service_context);
+    standalone_service_context *deregsiter_service_instance(const char *sn);
+
+    standalone_service_context *query(const char *sn, int *status);
+
+private:
+    bool find_context_index(const char *sn, size_t *index) const;
+
+    std::vector<standalone_service_context*> m_service_instances;
+};
+
+#endif /* STANDALONE_SERVICE_REGISTRY_H */
\ No newline at end of file
diff --git a/components/service/locator/test/component.cmake b/components/service/locator/test/component.cmake
new file mode 100644
index 0000000..04e98f3
--- /dev/null
+++ b/components/service/locator/test/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, 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}/sn_tests.cpp"
+	)
diff --git a/components/service/locator/test/sn_tests.cpp b/components/service/locator/test/sn_tests.cpp
new file mode 100644
index 0000000..4f8630a
--- /dev/null
+++ b/components/service/locator/test/sn_tests.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <service/locator/service_name.h>
+#include <CppUTest/TestHarness.h>
+#include <common/uuid/uuid.h>
+#include <string.h>
+
+TEST_GROUP(ServiceNameTests) {
+
+};
+
+TEST(ServiceNameTests, checkValidServiceNames) {
+
+    const char *sn1 = "sn:trustedfirmware.org:crypto:0";
+    CHECK(sn_is_valid(sn1));
+
+    const char *sn2 = "sn:trustedfirmware.org:secure-storage.1.0:0";
+    CHECK(sn_is_valid(sn2));
+
+    const char *sn3 = "urn:sn:trustedfirmware.org:tpm:3";
+    CHECK(sn_is_valid(sn3));
+
+    const char *sn4 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0:0";
+    CHECK(sn_is_valid(sn4));
+
+    const char *sn5 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
+    CHECK(sn_is_valid(sn5));
+}
+
+TEST(ServiceNameTests, checkInvalidServiceNames) {
+
+    const char *sn1 = "sn:trustedfirmware.org";
+    CHECK(!sn_is_valid(sn1));
+
+    const char *sn2 = "trustedfirmware.org:secure-storage.1.0:0";
+    CHECK(!sn_is_valid(sn2));
+}
+
+TEST(ServiceNameTests, checkFields) {
+
+    const char *sn1 = "sn:trustedfirmware.org:crypto:2";
+    CHECK(sn_check_authority(sn1, "trustedfirmware.org"));
+    CHECK(!sn_check_authority(sn1, "ffa"));
+    CHECK(sn_check_service(sn1, "crypto"));
+    CHECK_EQUAL(2, sn_get_service_instance(sn1));
+
+    const char *sn2 = "sn:trustedfirmware.org:secure-storage.1.0:0";
+    CHECK(sn_check_authority(sn2, "trustedfirmware.org"));
+    CHECK(sn_check_service(sn2, "secure-storage.1.0"));
+    CHECK(sn_check_service(sn2, "secure-storage"));
+    CHECK_EQUAL(0, sn_get_service_instance(sn2));
+
+    const char *sn3 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0:7";
+    CHECK(sn_check_authority(sn3, "ffa"));
+    CHECK(sn_check_service(sn3, "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0"));
+    CHECK_EQUAL(7, sn_get_service_instance(sn3));
+
+    /* Check instance defaults to zero */
+    const char *sn4 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
+    CHECK(sn_is_valid(sn4));
+    CHECK_EQUAL(0, sn_get_service_instance(sn4));
+}
+
+TEST(ServiceNameTests, readService) {
+
+    char buf[UUID_CANONICAL_FORM_LEN + 1];
+
+    const char *sn1 = "sn:trustedfirmware.org:crypto:2";
+    CHECK_EQUAL(strlen("crypto"), sn_read_service(sn1, buf, sizeof(buf)));
+    CHECK(memcmp(buf, "crypto", strlen("crypto") + 1) == 0);
+    CHECK_EQUAL(strlen("crypto"), strlen(buf));
+
+    const char *sn2 = "sn:trustedfirmware.org:crypto.1.7.0:2";
+    CHECK_EQUAL(strlen("crypto.1.7.0"), sn_read_service(sn2, buf, sizeof(buf)));
+    CHECK(memcmp(buf, "crypto.1.7.0", strlen("crypto.1.7.0") + 1) == 0);
+    CHECK_EQUAL(strlen("crypto.1.7.0"), strlen(buf));
+
+    const char *sn3 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0:7";
+    CHECK_EQUAL(UUID_CANONICAL_FORM_LEN, sn_read_service(sn3, buf, sizeof(buf)));
+    CHECK(memcmp(buf, "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0", UUID_CANONICAL_FORM_LEN + 1) == 0);
+    CHECK_EQUAL(UUID_CANONICAL_FORM_LEN, strlen(buf));
+
+    const char *sn4 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
+    CHECK_EQUAL(UUID_CANONICAL_FORM_LEN, sn_read_service(sn4, buf, sizeof(buf)));
+    CHECK(memcmp(buf, "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0", UUID_CANONICAL_FORM_LEN + 1) == 0);
+    CHECK_EQUAL(UUID_CANONICAL_FORM_LEN, strlen(buf));
+}
\ No newline at end of file