Support modular extension of service provider capabilities

To allow a service provider to be scalable in a modular way, the
existing chain-of-resopnsibility facilty provided by the base
service_provider is exploited to allow a set of sub providers
to extend the capabilities of the base provider.  The first
use of this will be to support modular extension of the crypto
service provider.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I4369d7a9cb99e54403768a9e3459b0ba1ac8f812
diff --git a/components/service/common/provider/service_provider.c b/components/service/common/provider/service_provider.c
index f11c303..19bd088 100644
--- a/components/service/common/provider/service_provider.c
+++ b/components/service/common/provider/service_provider.c
@@ -64,3 +64,10 @@
 
 	sp->successor = NULL;
 }
+
+void service_provider_extend(struct service_provider *context,
+                    struct service_provider *sub_provider)
+{
+	sub_provider->successor = context->successor;
+	context->successor = &sub_provider->iface;
+}
diff --git a/components/service/common/provider/service_provider.h b/components/service/common/provider/service_provider.h
index 508a93f..a4f15e4 100644
--- a/components/service/common/provider/service_provider.h
+++ b/components/service/common/provider/service_provider.h
@@ -59,6 +59,21 @@
 				 	const struct service_handler *handlers,
 				 	size_t num_handlers);
 
+/*
+ * Extend the core set of operations provided by a service provider by
+ * adding a sub provider that will add a capability.  This facility
+ * allows a deployment to customize the set of operations
+ * supported to meet requirements by only extending the core service
+ * provider if needed.
+ */
+void service_provider_extend(struct service_provider *context,
+                    struct service_provider *sub_provider);
+
+/*
+ * Link a successor to this service provider to extend the chain of responsibility
+ * to allow call handling to be delegated to different components.  Used to support
+ * modular configuration of service capabilities.
+ */
 static inline void service_provider_link_successor(struct service_provider *sp,
 					struct rpc_interface *successor)
 {
diff --git a/components/service/common/provider/test/service_framework_tests.cpp b/components/service/common/provider/test/service_framework_tests.cpp
index 6483c4b..49ec163 100644
--- a/components/service/common/provider/test/service_framework_tests.cpp
+++ b/components/service/common/provider/test/service_framework_tests.cpp
@@ -78,14 +78,14 @@
     int opstatus;
 
     handle = rpc_caller_begin(caller, &req_buf, req_len);
-    CHECK(handle);
+    CHECK_TRUE(handle);
 
     rpc_status_t rpc_status = rpc_caller_invoke(caller, handle, SOME_ARBITRARY_OPCODE,
                                     &opstatus, &resp_buf, &resp_len);
 
     rpc_caller_end(caller, handle);
 
-    CHECK_EQUAL(TS_RPC_ERROR_INVALID_OPCODE, rpc_status);
+    LONGS_EQUAL(TS_RPC_ERROR_INVALID_OPCODE, rpc_status);
 }
 
 TEST(ServiceFrameworkTests, serviceWithOps)
@@ -114,7 +114,7 @@
 
     /* Expect this call transaction to succeed */
     handle = rpc_caller_begin(caller, &req_buf, req_len);
-    CHECK(handle);
+    CHECK_TRUE(handle);
 
     rpc_status = rpc_caller_invoke(caller, handle, SOME_ARBITRARY_OPCODE,
                                     &opstatus, &resp_buf, &resp_len);
@@ -123,13 +123,13 @@
 
     rpc_caller_end(caller, handle);
 
-    CHECK_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
-    CHECK_EQUAL(SERVICE_SPECIFIC_SUCCESS_CODE, opstatus);
-    CHECK(respString == "Yay!");
+    LONGS_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
+    LONGS_EQUAL(SERVICE_SPECIFIC_SUCCESS_CODE, opstatus);
+    STRCMP_EQUAL("Yay!", respString.c_str());
 
     /* Expect this call transaction to fail */
     handle = rpc_caller_begin(caller, &req_buf, req_len);
-    CHECK(handle);
+    CHECK_TRUE(handle);
 
     rpc_status = rpc_caller_invoke(caller, handle, ANOTHER_ARBITRARY_OPCODE,
         &opstatus, &resp_buf, &resp_len);
@@ -138,18 +138,116 @@
 
     rpc_caller_end(caller, handle);
 
-    CHECK_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
-    CHECK_EQUAL(SERVICE_SPECIFIC_ERROR_CODE, opstatus);
-    CHECK(respString == "Ehh!");
+    LONGS_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
+    LONGS_EQUAL(SERVICE_SPECIFIC_ERROR_CODE, opstatus);
+    STRCMP_EQUAL("Ehh!", respString.c_str());
 
     /* Try an unsupported opcode */
     handle = rpc_caller_begin(caller, &req_buf, req_len);
-    CHECK(handle);
+    CHECK_TRUE(handle);
 
     rpc_status = rpc_caller_invoke(caller, handle, YET_ANOTHER_ARBITRARY_OPCODE,
         &opstatus, &resp_buf, &resp_len);
 
     rpc_caller_end(caller, handle);
 
-    CHECK_EQUAL(TS_RPC_ERROR_INVALID_OPCODE, rpc_status);
+    LONGS_EQUAL(TS_RPC_ERROR_INVALID_OPCODE, rpc_status);
+}
+
+TEST(ServiceFrameworkTests, serviceProviderChain)
+{
+     /* Construct the base service provider */
+   struct service_handler base_handlers[0];
+    base_handlers[0].opcode = 100;
+    base_handlers[0].invoke = handlerThatSucceeds;
+
+    struct service_provider base_provider;
+    service_provider_init(&base_provider, &base_provider, base_handlers, 1);
+
+    /* Construct a sub provider and extend the base */
+    struct service_handler sub0_handlers[0];
+    sub0_handlers[0].opcode = 200;
+    sub0_handlers[0].invoke = handlerThatSucceeds;
+
+    struct service_provider sub0_provider;
+    service_provider_init(&sub0_provider, &sub0_provider, sub0_handlers, 1);
+    service_provider_extend(&base_provider, &sub0_provider);
+
+    /* Construct another sub provider and extend the base */
+    struct service_handler sub1_handlers[0];
+    sub1_handlers[0].opcode = 300;
+    sub1_handlers[0].invoke = handlerThatSucceeds;
+
+    struct service_provider sub1_provider;
+    service_provider_init(&sub1_provider, &sub1_provider, sub1_handlers, 1);
+    service_provider_extend(&base_provider, &sub1_provider);
+
+
+    struct rpc_caller *caller = direct_caller_init_default(&m_direct_caller,
+                                    service_provider_get_rpc_interface(&base_provider));
+
+    rpc_call_handle handle;
+    rpc_status_t rpc_status;
+    uint8_t *req_buf;
+    uint8_t *resp_buf;
+    size_t req_len = 100;
+    size_t resp_len;
+    int opstatus;
+    std::string respString;
+
+    /* Expect calls that will be handled by all three chained service providers to succeed */
+    handle = rpc_caller_begin(caller, &req_buf, req_len);
+    CHECK_TRUE(handle);
+
+    rpc_status = rpc_caller_invoke(caller, handle, 100,
+                                    &opstatus, &resp_buf, &resp_len);
+
+    respString = std::string((const char*)resp_buf, resp_len);
+
+    rpc_caller_end(caller, handle);
+
+    LONGS_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
+    LONGS_EQUAL(SERVICE_SPECIFIC_SUCCESS_CODE, opstatus);
+    STRCMP_EQUAL("Yay!", respString.c_str());
+
+    /* This one should beb handled by sub0 */
+    handle = rpc_caller_begin(caller, &req_buf, req_len);
+    CHECK_TRUE(handle);
+
+    rpc_status = rpc_caller_invoke(caller, handle, 200,
+                                    &opstatus, &resp_buf, &resp_len);
+
+    respString = std::string((const char*)resp_buf, resp_len);
+
+    rpc_caller_end(caller, handle);
+
+    LONGS_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
+    LONGS_EQUAL(SERVICE_SPECIFIC_SUCCESS_CODE, opstatus);
+    STRCMP_EQUAL("Yay!", respString.c_str());
+
+    /* This one should beb handled by sub1 */
+    handle = rpc_caller_begin(caller, &req_buf, req_len);
+    CHECK_TRUE(handle);
+
+    rpc_status = rpc_caller_invoke(caller, handle, 300,
+                                    &opstatus, &resp_buf, &resp_len);
+
+    respString = std::string((const char*)resp_buf, resp_len);
+
+    rpc_caller_end(caller, handle);
+
+    LONGS_EQUAL(TS_RPC_CALL_ACCEPTED, rpc_status);
+    LONGS_EQUAL(SERVICE_SPECIFIC_SUCCESS_CODE, opstatus);
+    STRCMP_EQUAL("Yay!", respString.c_str());
+
+    /* Try an unsupported opcode */
+    handle = rpc_caller_begin(caller, &req_buf, req_len);
+    CHECK_TRUE(handle);
+
+    rpc_status = rpc_caller_invoke(caller, handle, 400,
+        &opstatus, &resp_buf, &resp_len);
+
+    rpc_caller_end(caller, handle);
+
+    LONGS_EQUAL(TS_RPC_ERROR_INVALID_OPCODE, rpc_status);
 }
diff --git a/components/service/crypto/provider/crypto_provider.c b/components/service/crypto/provider/crypto_provider.c
index c3d55b5..6777fd2 100644
--- a/components/service/crypto/provider/crypto_provider.c
+++ b/components/service/crypto/provider/crypto_provider.c
@@ -75,6 +75,12 @@
 		context->serializers[encoding] = serializer;
 }
 
+void crypto_provider_extend(struct crypto_provider *context,
+                    struct service_provider *sub_provider)
+{
+	service_provider_extend(&context->base_provider, sub_provider);
+}
+
 static const struct crypto_provider_serializer* get_crypto_serializer(void *context,
 														const struct call_req *req)
 {
diff --git a/components/service/crypto/provider/crypto_provider.h b/components/service/crypto/provider/crypto_provider.h
index a45b14c..7cd91a9 100644
--- a/components/service/crypto/provider/crypto_provider.h
+++ b/components/service/crypto/provider/crypto_provider.h
@@ -46,6 +46,12 @@
 void crypto_provider_register_serializer(struct crypto_provider *context,
                     unsigned int encoding, const struct crypto_provider_serializer *serializer);
 
+/*
+ * Extend the core set of operations provided by the crypto provider.
+ */
+void crypto_provider_extend(struct crypto_provider *context,
+                    struct service_provider *sub_provider);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif