Add hw TRNG from SEL0 SP

Intgrates the FVP TRNG into the crypto sp to provide a hw
entropy source.  Includes tests that check SP device
region configuration loading and MMIO access within the
region.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Ia9af8b044596e1c7d194f039fdf64c2468bb3221
diff --git a/components/app/remote-test-runner/remote_test_runner.cpp b/components/app/remote-test-runner/remote_test_runner.cpp
index 681c746..b819779 100644
--- a/components/app/remote-test-runner/remote_test_runner.cpp
+++ b/components/app/remote-test-runner/remote_test_runner.cpp
@@ -136,6 +136,8 @@
         else if (results[i].run_state == TEST_RUN_STATE_FAILED) {
 
             printf("error\n");
+            printf("\tline number: %d\n", results[i].failure.line_num);
+            printf("\tinfo: 0x%016lx\n", results[i].failure.info);
         }
         else {
 
diff --git a/components/config/interface/platform_config.h b/components/config/interface/platform_config.h
index d11f0eb..3f7eb94 100644
--- a/components/config/interface/platform_config.h
+++ b/components/config/interface/platform_config.h
@@ -46,6 +46,13 @@
  */
 int platform_config_device_add(const struct device_region *device_region);
 
+/**
+ * \brief Returns a count of the number of device regions
+ *
+ * \return          0 if successful
+ */
+unsigned int platform_config_device_region_count(void);
+
 
 #ifdef __cplusplus
 }
diff --git a/components/config/loader/sp/component.cmake b/components/config/loader/sp/component.cmake
new file mode 100644
index 0000000..82b9fbd
--- /dev/null
+++ b/components/config/loader/sp/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}/sp_config_loader.c"
+	)
+
diff --git a/components/config/loader/sp/sp_config_loader.c b/components/config/loader/sp/sp_config_loader.c
new file mode 100644
index 0000000..a1c8804
--- /dev/null
+++ b/components/config/loader/sp/sp_config_loader.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <string.h>
+#include <config/interface/platform_config.h>
+#include <platform/interface/device_region.h>
+#include "sp_config_loader.h"
+
+
+struct sp_param_device_region
+{
+	char name[16];
+	uintptr_t location;
+	size_t size;
+};
+
+/**
+ * Loads externally provided configuration data originating from
+ * theh SP manifest.
+ */
+void sp_config_load(struct ffa_init_info *init_info)
+{
+	/* Load deployment specific configuration */
+	for (size_t param_index = 0; param_index < init_info->count; param_index++) {
+
+   		if (!strcmp((const char *)init_info->nvp[param_index].name,"DEVICE_REGIONS")) {
+
+			struct sp_param_device_region *d = (struct sp_param_device_region *)init_info->nvp[param_index].value;
+
+            /*Iterate over the device regions*/
+			while ((uintptr_t)d < (init_info->nvp[param_index].value + init_info->nvp[param_index].size)) {
+
+				struct device_region device_region;
+
+				strcpy(device_region.dev_class, d->name);
+				device_region.dev_instance = 0;
+				device_region.base_addr = d->location;
+				device_region.io_region_size = d->size;
+
+				platform_config_device_add(&device_region);
+
+				++d;
+			}
+		}
+	}
+}
diff --git a/components/config/loader/sp/sp_config_loader.h b/components/config/loader/sp/sp_config_loader.h
new file mode 100644
index 0000000..a1c8bf0
--- /dev/null
+++ b/components/config/loader/sp/sp_config_loader.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SP_CONFIG_LOADER_H
+#define SP_CONFIG_LOADER_H
+
+#include <ffa_api.h>
+
+/**
+ * Loads the secure partition specific configuration passed as
+ * SP initialization parameters.
+ */
+void sp_config_load(struct ffa_init_info *init_info);
+
+
+#endif /* SP_CONFIG_LOADER_H */
diff --git a/components/config/ramstore/config_ramstore.c b/components/config/ramstore/config_ramstore.c
index 548ba4b..2e24645 100644
--- a/components/config/ramstore/config_ramstore.c
+++ b/components/config/ramstore/config_ramstore.c
@@ -114,3 +114,18 @@
 {
 	free(device_region);
 }
+
+unsigned int platform_config_device_region_count(void)
+{
+	unsigned int count = 0;
+
+	const struct config_container *container = ramstore.device_region_list;
+
+	while (container) {
+
+		++count;
+		container = container->next;
+	}
+
+	return count;
+}
diff --git a/components/config/ramstore/test/ramstore_tests.cpp b/components/config/ramstore/test/ramstore_tests.cpp
index c597b57..aadb62b 100644
--- a/components/config/ramstore/test/ramstore_tests.cpp
+++ b/components/config/ramstore/test/ramstore_tests.cpp
@@ -38,7 +38,7 @@
     /* This would be external configuration, obtained say from device tree */
     strcpy(config.dev_class, "fs");
     config.dev_instance = 2;
-    config.base_addr = (uint8_t*)0x0f000010;
+    config.base_addr = (uintptr_t)0x0f000010;
     config.io_region_size = 0x100;
 
     /* Add the configuration object */
@@ -65,7 +65,7 @@
 
     strcpy(config1.dev_class, "flash");
     config1.dev_instance = 0;
-    config1.base_addr = (uint8_t*)0x0f000010;
+    config1.base_addr = (uintptr_t)0x0f000010;
     config1.io_region_size = 0x100;
 
     status = platform_config_device_add(&config1);
@@ -76,12 +76,14 @@
 
     strcpy(config2.dev_class, "flash");
     config2.dev_instance = 1;
-    config2.base_addr = (uint8_t*)0x0f000010;
+    config2.base_addr = (uintptr_t)0x0f000010;
     config2.io_region_size = 0x100;
 
     status = platform_config_device_add(&config2);
     CHECK_EQUAL(0, status);
 
+    CHECK_EQUAL(2, platform_config_device_region_count());
+
     /* Expect queries for both objects to work */
     struct device_region *query1_result = platform_config_device_query(config1.dev_class, config1.dev_instance);
     CHECK(query1_result);
diff --git a/components/config/test/sp/component.cmake b/components/config/test/sp/component.cmake
new file mode 100644
index 0000000..9869e73
--- /dev/null
+++ b/components/config/test/sp/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}/sp_config_tests.c"
+	)
+
diff --git a/components/config/test/sp/sp_config_tests.c b/components/config/test/sp/sp_config_tests.c
new file mode 100644
index 0000000..94ba268
--- /dev/null
+++ b/components/config/test/sp/sp_config_tests.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <service/test_runner/provider/backend/simple_c/simple_c_test_runner.h>
+#include <config/interface/platform_config.h>
+#include <stdint.h>
+
+/**
+ * Secure Partition configuration tests for checking configuartion
+ * data passed to an SP at initialisation.  These tests assume
+ * use of the FFA manifest for any SP deployments of
+ * deployments/env_test.
+ */
+
+/*
+ * Check that the loaded configuration includes one or more
+ * device regions.
+ */
+static bool check_device_region_loaded(struct test_failure *failure)
+{
+    return platform_config_device_region_count() > 0;
+}
+
+/*
+ * Check that a device region for a 'trng' device has been loaded
+ * and that values are as expected.
+ */
+static bool check_trng_device_region_loaded(struct test_failure *failure)
+{
+    bool passed = false;
+    struct device_region *dev_region = platform_config_device_query("trng", 0);
+
+    if (dev_region) {
+
+        passed =
+            (dev_region->dev_instance == 0) &&
+            (dev_region->io_region_size == 0x1000);
+    }
+
+    platform_config_device_query_free(dev_region);
+
+    return passed;
+}
+
+/*
+ * Check access to some trng registers
+ */
+static bool check_trng_register_access(struct test_failure *failure)
+{
+    bool passed = false;
+
+    struct device_region *dev_region = platform_config_device_query("trng", 0);
+
+    if (dev_region) {
+
+        /* Expect reset values to be read from a selection of TRNG registers */
+        uint32_t reg_val;
+        passed = true;
+
+        /* PID4 */
+        if (passed) {
+            reg_val = *((volatile uint32_t*)((uint8_t*)dev_region->base_addr + 0xfd0));
+            passed = (reg_val == 0x00000004);
+            failure->line_num = __LINE__;
+            failure->info = reg_val;
+        }
+
+        /* PID0 */
+        if (passed) {
+            reg_val = *((volatile uint32_t*)((uint8_t*)dev_region->base_addr + 0xfe0));
+            passed = (reg_val == 0x000000aa);
+            failure->line_num = __LINE__;
+            failure->info = reg_val;
+        }
+    }
+
+    return passed;
+}
+
+
+/**
+ * Define an register test group
+ */
+void sp_config_tests_register(void)
+{
+    static const struct simple_c_test_case sp_config_tests[] = {
+        {.name = "DevRegionLoaded", .test_func = check_device_region_loaded},
+        {.name = "TrngDevRegionLoaded", .test_func = check_trng_device_region_loaded},
+        {.name = "TrngRegAccess", .test_func = check_trng_register_access}
+    };
+
+    static const struct simple_c_test_group sp_config_test_group =
+    {
+        .group = "SpConfigTests",
+        .num_test_cases = sizeof(sp_config_tests)/sizeof(struct simple_c_test_case),
+        .test_cases = sp_config_tests
+    };
+
+    simple_c_test_runner_register_group(&sp_config_test_group);
+}
\ No newline at end of file
diff --git a/components/config/test/sp/sp_config_tests.h b/components/config/test/sp/sp_config_tests.h
new file mode 100644
index 0000000..516d2b1
--- /dev/null
+++ b/components/config/test/sp/sp_config_tests.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SP_CONFIG_TESTS_H
+#define SP_CONFIG_TESTS_H
+
+void sp_config_tests_register(void);
+
+#endif /* SP_CONFIG_TESTS_H */
diff --git a/components/service/crypto/provider/mbedcrypto/trng_adapter/platform/platform_trng_adapter.c b/components/service/crypto/provider/mbedcrypto/trng_adapter/platform/platform_trng_adapter.c
index 29628d1..1b9f1d2 100644
--- a/components/service/crypto/provider/mbedcrypto/trng_adapter/platform/platform_trng_adapter.c
+++ b/components/service/crypto/provider/mbedcrypto/trng_adapter/platform/platform_trng_adapter.c
@@ -4,7 +4,6 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 #include <mbedtls/entropy.h>
-#include <mbedtls/entropy_poll.h>
 #include <platform/interface/trng.h>
 #include <service/crypto/provider/mbedcrypto/trng_adapter/trng_adapter.h>
 #include <config/interface/platform_config.h>
diff --git a/components/service/crypto/provider/mbedcrypto/trng_adapter/test/component.cmake b/components/service/crypto/provider/mbedcrypto/trng_adapter/test/component.cmake
new file mode 100644
index 0000000..4aa4321
--- /dev/null
+++ b/components/service/crypto/provider/mbedcrypto/trng_adapter/test/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}/trng_env_tests.c"
+	)
+
diff --git a/components/service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.c b/components/service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.c
new file mode 100644
index 0000000..d297821
--- /dev/null
+++ b/components/service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <service/test_runner/provider/backend/simple_c/simple_c_test_runner.h>
+#include <service/crypto/provider/mbedcrypto/trng_adapter/trng_adapter.h>
+#include <string.h>
+#include <stddef.h>
+
+/**
+ * Tests the hw TRNG provided by the environment or underlying platform.
+ */
+
+/* Declaration for mbedtls function to avoid complicated conditional
+ * compilation problems in mbedtls header files.
+ */
+int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen);
+
+/*
+ * Check the trng create/destroy lifecycle
+ */
+static bool check_trng_lifecycle(struct test_failure *failure)
+{
+    int status = trng_adapter_init(0);
+    failure->info = status;
+    trng_adapter_deinit();
+
+    return (status == 0);
+}
+
+/*
+ * Check the trng hardware poll function used by mbedcrypto to
+ * access a hardware entropy source,
+ */
+static bool check_hw_poll(struct test_failure *failure)
+{
+    bool passed;
+    uint8_t output[2][10];
+    size_t expected_len = 10;
+
+    passed = (trng_adapter_init(0) == 0);
+
+    memset(output, 0, sizeof(output));
+
+    for (int round = 0; passed && round < 5; ++round) {
+
+        size_t olen;
+        int status = mbedtls_hardware_poll(NULL, &output[round & 1][0], expected_len, &olen);
+
+        /* Check results of call - expect a different byte stream on each call */
+        passed = passed && (status == 0);
+        passed = passed && (olen == expected_len);
+        passed = passed && (memcmp(&output[round & 1][0], &output[!(round & 1)][0], expected_len) != 0);
+
+        /* capture most recent output in case of failure */
+        memcpy(&failure->info, &output[round & 1][0], sizeof(failure->info));
+    }
+
+    trng_adapter_deinit();
+
+    return passed;
+}
+
+/**
+ * Define an register test group
+ */
+void trng_env_tests_register(void)
+{
+    static const struct simple_c_test_case trng_env_tests[] = {
+        {.name = "TrngLifecycle", .test_func = check_trng_lifecycle},
+        {.name = "TrngHwPoll", .test_func = check_hw_poll}
+    };
+
+    static const struct simple_c_test_group trng_env_test_group =
+    {
+        .group = "TrngEnvTests",
+        .num_test_cases = sizeof(trng_env_tests)/sizeof(struct simple_c_test_case),
+        .test_cases = trng_env_tests
+    };
+
+    simple_c_test_runner_register_group(&trng_env_test_group);
+}
\ No newline at end of file
diff --git a/components/service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.h b/components/service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.h
new file mode 100644
index 0000000..36c4103
--- /dev/null
+++ b/components/service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TRNG_ENV_TESTS_H
+#define TRNG_ENV_TESTS_H
+
+void trng_env_tests_register(void);
+
+#endif /* TRNG_ENV_TESTS_H */
diff --git a/components/service/test_runner/client/cpp/test_runner_client.cpp b/components/service/test_runner/client/cpp/test_runner_client.cpp
index a780002..b1b2784 100644
--- a/components/service/test_runner/client/cpp/test_runner_client.cpp
+++ b/components/service/test_runner/client/cpp/test_runner_client.cpp
@@ -211,9 +211,9 @@
         memcpy(&packed_result, value_buf, fixed_size);
 
         result.run_state = (enum test_run_state)packed_result.run_state;
-        result.fail_line = packed_result.fail_line;
         result.name[0] = 0;
         result.group[0] = 0;
+        result.failure = {0};
 
         /* Deserialize name and group if present */
         struct tlv_const_iterator req_iter;
@@ -238,6 +238,17 @@
                 result.group[decoded_record.length] = 0;
             }
         }
+
+        if (tlv_find_decode(&req_iter, TS_TEST_RUNNER_TEST_RESULT_TAG_FAILURE, &decoded_record)) {
+
+            if (decoded_record.length == sizeof(ts_test_runner_test_failure)) {
+
+                struct ts_test_runner_test_failure deserialized_failure;
+                memcpy(&deserialized_failure, decoded_record.value, decoded_record.length);
+                result.failure.line_num = deserialized_failure.line_num;
+                result.failure.info = deserialized_failure.info;
+            }
+        }
     }
     else {
         /* Invalid test result */
diff --git a/components/service/test_runner/common/test_runner.h b/components/service/test_runner/common/test_runner.h
index c891918..ffc6243 100644
--- a/components/service/test_runner/common/test_runner.h
+++ b/components/service/test_runner/common/test_runner.h
@@ -4,10 +4,11 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
-#ifndef CPPUTEST_TEST_RUNNER_H
-#define CPPUTEST_TEST_RUNNER_H
+#ifndef TEST_RUNNER_H
+#define TEST_RUNNER_H
 
 #include <protocols/service/test_runner/packed-c/test_result.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -33,10 +34,10 @@
  */
 struct test_summary
 {
-	int num_tests;		/* Number of qualifying tests */
-	int num_results;	/* Number of available test result objects */
-	int num_passed;		/* Number that ran and passed */
-	int num_failed;		/* Number that ran and failed */
+	unsigned int num_tests;		/* Number of qualifying tests */
+	unsigned int num_results;	/* Number of available test result objects */
+	unsigned int num_passed;	/* Number that ran and passed */
+	unsigned int num_failed;	/* Number that ran and failed */
 };
 
 /**
@@ -50,6 +51,15 @@
 };
 
 /**
+ * Test failue to describe a failure
+ */
+struct test_failure
+{
+	unsigned int line_num;		/* Source line where test assertion failed */
+	uint64_t info;				/* Provides test specific information about the failure */
+};
+
+/**
  * The result for a particular test case.
  */
 struct test_result
@@ -57,11 +67,11 @@
 	char name[TEST_NAME_MAX_LEN];
 	char group[TEST_GROUP_MAX_LEN];
 	enum test_run_state run_state;
-	unsigned int fail_line;
+	struct test_failure failure;
 };
 
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
 
-#endif /* CPPUTEST_TEST_RUNNER_H */
+#endif /* TEST_RUNNER_H */
diff --git a/components/service/test_runner/provider/backend/mock/mock_test_runner.c b/components/service/test_runner/provider/backend/mock/mock_test_runner.c
index 4b6cde8..275457d 100644
--- a/components/service/test_runner/provider/backend/mock/mock_test_runner.c
+++ b/components/service/test_runner/provider/backend/mock/mock_test_runner.c
@@ -8,8 +8,18 @@
 #include <string.h>
 
 /* Mock test test functions */
-static bool test_that_passes(void)  { return true; }
-static bool test_that_fails(void)   { return false; }
+static bool test_that_passes(struct test_failure *failure)
+{
+    (void)failure;
+    return true;
+}
+
+static bool test_that_fails(struct test_failure *failure)
+{
+    failure->line_num = __LINE__;
+    failure->info = 27;
+    return false;
+}
 
 /**
  * The mock backend is a test_runner that provides some mock test cases
diff --git a/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.c b/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.c
index 0ab190c..fb66d75 100644
--- a/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.c
+++ b/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.c
@@ -80,11 +80,12 @@
                 if (does_qualify(spec->name, test_case->name)) {
 
                     enum test_run_state run_state = TEST_RUN_STATE_NOT_RUN;
+                    struct test_failure failure = {0};
 
                     /* Run the qualifying test case if we're not just listing tests */
                     if (!list_only) {
 
-                        if (test_case->test_func()) {
+                        if (test_case->test_func(&failure)) {
 
                             run_state = TEST_RUN_STATE_PASSED;
                             ++summary->num_passed;
@@ -102,7 +103,7 @@
                         struct test_result *new_result = &results[summary->num_results];
 
                         new_result->run_state = run_state;
-                        new_result->fail_line = 0;
+                        new_result->failure = failure;
                         strcpy(new_result->group, test_group->group);
                         strcpy(new_result->name, test_case->name);
 
diff --git a/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.h b/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.h
index 3875c42..1292547 100644
--- a/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.h
+++ b/components/service/test_runner/provider/backend/simple_c/simple_c_test_runner.h
@@ -7,6 +7,7 @@
 #ifndef SIMPLE_C_TEST_RUNNER_H
 #define SIMPLE_C_TEST_RUNNER_H
 
+#include <service/test_runner/common/test_runner.h>
 #include <stdbool.h>
 #include <stddef.h>
 
@@ -30,7 +31,7 @@
 	const char *name;
 
 	/* The test function that results true for a pass, false for a fail. */
-	bool (*test_func)(void);
+	bool (*test_func)(struct test_failure *test_failure);
 };
 
 /**
diff --git a/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c b/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c
index 577bfdc..5892502 100644
--- a/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c
+++ b/components/service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.c
@@ -55,6 +55,8 @@
 
     if (name_len)   required_space += tlv_required_space(name_len);
     if (group_len)  required_space += tlv_required_space(group_len);
+    if (result->run_state == TEST_RUN_STATE_FAILED)
+        required_space += tlv_required_space(sizeof(struct ts_test_runner_test_failure));
 
     *serialized_len = required_space;
 
@@ -64,7 +66,6 @@
 
         struct ts_test_runner_test_result result_msg;
         result_msg.run_state = result->run_state;
-        result_msg.fail_line = result->fail_line;
 
         memcpy(out_buf, &result_msg, fixed_len);
 
@@ -88,6 +89,19 @@
             record.value = result->group;
             tlv_encode(&tlv_iter, &record);
         }
+
+        if (result->run_state == TEST_RUN_STATE_FAILED) {
+
+            struct ts_test_runner_test_failure serialized_failure;
+            serialized_failure.line_num = result->failure.line_num;
+            serialized_failure.info = result->failure.info;
+
+            struct tlv_record record;
+            record.tag = TS_TEST_RUNNER_TEST_RESULT_TAG_FAILURE;
+            record.length = sizeof(serialized_failure);
+            record.value = (const uint8_t*)&serialized_failure;
+            tlv_encode(&tlv_iter, &record);
+        }
     }
 
     return out_buf;
diff --git a/components/service/test_runner/test/service/test_runner_service_tests.cpp b/components/service/test_runner/test/service/test_runner_service_tests.cpp
index c4ba0e0..8d68bca 100644
--- a/components/service/test_runner/test/service/test_runner_service_tests.cpp
+++ b/components/service/test_runner/test/service/test_runner_service_tests.cpp
@@ -128,6 +128,7 @@
     CHECK(strcmp(results[2].group, "ConfigTests") == 0);
     CHECK(strcmp(results[2].name, "ValidateConfig") == 0);
     CHECK_EQUAL(TEST_RUN_STATE_FAILED, results[2].run_state);
+    CHECK_EQUAL(27, results[2].failure.info);
 
     CHECK(strcmp(results[3].group, "ConfigTests") == 0);
     CHECK(strcmp(results[3].name, "ApplyConfig") == 0);
diff --git a/deployments/crypto/opteesp/CMakeLists.txt b/deployments/crypto/opteesp/CMakeLists.txt
index 356d0d3..9441ecf 100644
--- a/deployments/crypto/opteesp/CMakeLists.txt
+++ b/deployments/crypto/opteesp/CMakeLists.txt
@@ -36,6 +36,7 @@
 	COMPONENTS
 		"components/common/tlv"
 		"components/config/ramstore"
+		"components/config/loader/sp"
 		"components/messaging/ffa/libsp"
 		"components/rpc/ffarpc/endpoint"
 		"components/rpc/ffarpc/caller/sp"
diff --git a/deployments/crypto/opteesp/crypto_sp.c b/deployments/crypto/opteesp/crypto_sp.c
index 6b376b7..594fa89 100644
--- a/deployments/crypto/opteesp/crypto_sp.c
+++ b/deployments/crypto/opteesp/crypto_sp.c
@@ -3,7 +3,6 @@
  * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
  */
 
-#include <config/ramstore/config_ramstore.h>
 #include <rpc/ffarpc/caller/sp/ffarpc_caller.h>
 #include <rpc/ffarpc/endpoint/ffarpc_call_ep.h>
 #include <rpc/dummy/dummy_caller.h>
@@ -12,6 +11,8 @@
 #include <service/crypto/provider/serializer/protobuf/pb_crypto_provider_serializer.h>
 #include <service/crypto/provider/serializer/packed-c/packedc_crypto_provider_serializer.h>
 #include <protocols/rpc/common/packed-c/status.h>
+#include <config/ramstore/config_ramstore.h>
+#include <config/loader/sp/sp_config_loader.h>
 #include <ffa_api.h>
 #include <sp_api.h>
 #include <sp_rxtx.h>
@@ -44,9 +45,8 @@
 
 	if (sp_init(&own_id) != 0) goto fatal_error;
 
-	/* Read config data */
 	config_ramstore_init();
-	// ~ read here
+	sp_config_load(init_info);
 
 	/* Establish RPC session with secure storage SP */
 	storage_caller = ffarpc_caller_init(&ffarpc_caller);
diff --git a/deployments/env-test/env_test.cmake b/deployments/env-test/env_test.cmake
new file mode 100644
index 0000000..bf2b509
--- /dev/null
+++ b/deployments/env-test/env_test.cmake
@@ -0,0 +1,46 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+#  The base build file shared between deployments of 'env-test' for
+#  different environments.  Used for running tests that validate hardwarw
+#  backed services available from within a secure execution environment.
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+#  Components that are common accross all deployments
+#
+#-------------------------------------------------------------------------------
+add_components(
+	TARGET "env_test"
+	BASE_DIR ${TS_ROOT}
+	COMPONENTS
+	"components/common/tlv"
+	"components/config/ramstore"
+	"components/rpc/common/interface"
+	"components/rpc/common/caller"
+	"components/service/common"
+	"components/service/common/provider"
+	"components/service/test_runner/provider"
+	"components/service/test_runner/provider/serializer/packed-c"
+	"components/service/test_runner/provider/backend/null"
+	"components/service/test_runner/provider/backend/simple_c"
+	"components/service/crypto/provider/mbedcrypto"
+	"components/service/crypto/provider/mbedcrypto/trng_adapter/platform"
+	"components/service/crypto/provider/mbedcrypto/trng_adapter/test"
+	"components/service/secure_storage/client/psa"
+	"protocols/rpc/common/packed-c"
+)
+
+#-------------------------------------------------------------------------------
+#  Components used from external projects
+#
+#-------------------------------------------------------------------------------
+
+# Mbedcrypto
+include(${TS_ROOT}/external/mbed-crypto/mbedcrypto.cmake)
+target_link_libraries(env_test PRIVATE mbedcrypto)
diff --git a/deployments/env-test/opteesp/CMakeLists.txt b/deployments/env-test/opteesp/CMakeLists.txt
index bf735a6..3149ecd 100644
--- a/deployments/env-test/opteesp/CMakeLists.txt
+++ b/deployments/env-test/opteesp/CMakeLists.txt
@@ -15,7 +15,7 @@
 #  test cases.
 #-------------------------------------------------------------------------------
 include(${TS_ROOT}/environments/opteesp/env.cmake)
-project(trusted-services LANGUAGES C CXX ASM)
+project(trusted-services LANGUAGES C ASM)
 add_executable(env_test)
 target_include_directories(env_test PRIVATE "${TOP_LEVEL_INCLUDE_DIRS}")
 set(SP_UUID "33c75baf-ac6a-4fe4-8ac7-e9909bee2d17")
@@ -35,23 +35,26 @@
 add_components(TARGET "env_test"
 	BASE_DIR ${TS_ROOT}
 	COMPONENTS
-		"components/common/tlv"
-		"components/config/ramstore"
+		"components/config/loader/sp"
 		"components/messaging/ffa/libsp"
 		"components/rpc/ffarpc/endpoint"
-		"components/rpc/common/interface"
-		"components/service/common"
-		"components/service/common/provider"
-		"components/service/test_runner/provider"
-		"components/service/test_runner/provider/serializer/packed-c"
-		"components/service/test_runner/provider/backend/null"
-		"protocols/rpc/common/packed-c"
+		"components/config/test/sp"
 		"environments/opteesp"
 )
 
+#-------------------------------------------------------------------------------
+#  Extend with components that are common across all deployments of
+#  env_test
+#
+#-------------------------------------------------------------------------------
+include(../env_test.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+#  Deployment specific source files
+#-------------------------------------------------------------------------------
 target_sources(env_test PRIVATE
 	env_test.c
-	env_test_config.c
+	env_test_tests.c
 )
 
 #-------------------------------------------------------------------------------
@@ -63,11 +66,6 @@
 
 add_platform(TARGET "env_test")
 
-#-------------------------------------------------------------------------------
-#  Components used from external projects
-#
-#-------------------------------------------------------------------------------
-
 if(CMAKE_CROSSCOMPILING)
 	target_link_libraries(env_test PRIVATE stdc++ gcc m)
 endif()
diff --git a/deployments/env-test/opteesp/env_test.c b/deployments/env-test/opteesp/env_test.c
index 0bb523f..3df4c1a 100644
--- a/deployments/env-test/opteesp/env_test.c
+++ b/deployments/env-test/opteesp/env_test.c
@@ -8,11 +8,13 @@
 #include <service/test_runner/provider/test_runner_provider.h>
 #include <service/test_runner/provider/serializer/packed-c/packedc_test_runner_provider_serializer.h>
 #include <protocols/rpc/common/packed-c/status.h>
+#include <config/ramstore/config_ramstore.h>
+#include <config/loader/sp/sp_config_loader.h>
 #include <ffa_api.h>
 #include <sp_api.h>
 #include <sp_rxtx.h>
 #include <trace.h>
-#include "env_test_config.h"
+#include "env_test_tests.h"
 
 
 uint16_t own_id = 0; /* !!Needs refactoring as parameter to ffarpc_caller_init */
@@ -33,7 +35,8 @@
 
 	if (sp_init(&own_id) != 0) goto fatal_error;
 
-	load_sp_config(init_info);
+	config_ramstore_init();
+	sp_config_load(init_info);
 
 	/* Initialize the test_runner service */
 	test_runner_iface = test_runner_provider_init(&test_runner_provider);
@@ -41,6 +44,8 @@
 	test_runner_provider_register_serializer(&test_runner_provider,
             TS_RPC_ENCODING_PACKED_C, packedc_test_runner_provider_serializer_instance());
 
+	env_test_register_tests(&test_runner_provider);
+
 	ffa_call_ep_init(&ffarpc_call_ep, test_runner_iface);
 
  	/* End of boot phase */
diff --git a/deployments/env-test/opteesp/env_test_config.c b/deployments/env-test/opteesp/env_test_config.c
deleted file mode 100644
index 14a1e53..0000000
--- a/deployments/env-test/opteesp/env_test_config.c
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-/*
- * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
- */
-
-#include <config/ramstore/config_ramstore.h>
-#include "env_test_config.h"
-
-
-void load_sp_config(struct ffa_init_info *init_info)
-{
-	config_ramstore_init();
-
-	/* Load deployment specific configuration */
-	(void)init_info;
-}
\ No newline at end of file
diff --git a/deployments/env-test/opteesp/env_test_config.h b/deployments/env-test/opteesp/env_test_config.h
deleted file mode 100644
index 8ed4a7e..0000000
--- a/deployments/env-test/opteesp/env_test_config.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-#ifndef ENV_TEST_CONFIG_H
-#define ENV_TEST_CONFIG_H
-
-#include <ffa_api.h>
-
-/**
- * Loads the SP specific configuration passed as SP initialization parameters.
- */
-void load_sp_config(struct ffa_init_info *init_info);
-
-
-#endif /* ENV_TEST_CONFIG_H */
diff --git a/deployments/env-test/opteesp/env_test_tests.c b/deployments/env-test/opteesp/env_test_tests.c
new file mode 100644
index 0000000..beb9cbf
--- /dev/null
+++ b/deployments/env-test/opteesp/env_test_tests.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <service/test_runner/provider/backend/simple_c/simple_c_test_runner.h>
+#include <config/test/sp/sp_config_tests.h>
+#include <service/crypto/provider/mbedcrypto/trng_adapter/test/trng_env_tests.h>
+
+void env_test_register_tests(struct test_runner_provider *context)
+{
+	simple_c_test_runner_init(context);
+
+	sp_config_tests_register();
+	trng_env_tests_register();
+}
\ No newline at end of file
diff --git a/deployments/env-test/opteesp/env_test_tests.h b/deployments/env-test/opteesp/env_test_tests.h
new file mode 100644
index 0000000..305fc45
--- /dev/null
+++ b/deployments/env-test/opteesp/env_test_tests.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ENV_TEST_TESTS_H
+#define ENV_TEST_TESTS_H
+
+struct test_runner_provider;
+
+/**
+ * Registers test cases for this deployment
+ */
+void env_test_register_tests(struct test_runner_provider *context);
+
+
+#endif /* ENV_TEST_TESTS_H */
diff --git a/platform/drivers/arm/juno_trng/driver.cmake b/platform/drivers/arm/juno_trng/driver.cmake
new file mode 100644
index 0000000..6d51009
--- /dev/null
+++ b/platform/drivers/arm/juno_trng/driver.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+# Add source files for using juno_trng and adapting it to the platform
+# trng interface.
+target_sources(${TGT} PRIVATE
+	"${CMAKE_CURRENT_LIST_DIR}/juno_trng.c"
+	"${CMAKE_CURRENT_LIST_DIR}/juno_trng_adapter.c"
+)
diff --git a/platform/drivers/arm/juno_trng/juno_decl.h b/platform/drivers/arm/juno_trng/juno_decl.h
new file mode 100644
index 0000000..07f2e19
--- /dev/null
+++ b/platform/drivers/arm/juno_trng/juno_decl.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * This is a modified version of the juno trng platform driver from trusted-firmware-a.
+ * The code has been modified to allow the peripheral to be accessed by S-EL0 at
+ * an arbitrary virtual address.
+ */
+
+#ifndef JUNO_DECL_H
+#define JUNO_DECL_H
+
+#include <stdint.h>
+
+void juno_trng_set_base_addr(uintptr_t addr);
+int juno_getentropy(void *buf, size_t len);
+
+#endif /* JUNO_DECL_H */
diff --git a/platform/drivers/arm/juno_trng/juno_def.h b/platform/drivers/arm/juno_trng/juno_def.h
new file mode 100644
index 0000000..97d1e3c
--- /dev/null
+++ b/platform/drivers/arm/juno_trng/juno_def.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ *
+ * Based on the the trustedfirmware-a file with the same name.  All but the
+ * TRNG defines are stripped out.
+ */
+
+#ifndef JUNO_DEF_H
+#define JUNO_DEF_H
+
+/*******************************************************************************
+ * TRNG related constants
+ ******************************************************************************/
+#define TRNG_NOUTPUTS	4
+#define TRNG_STATUS	UL(0x10)
+#define TRNG_INTMASK	UL(0x14)
+#define TRNG_CONFIG	UL(0x18)
+#define TRNG_CONTROL	UL(0x1C)
+#define TRNG_NBYTES	16	/* Number of bytes generated per round. */
+
+
+
+#endif /* JUNO_DEF_H */
diff --git a/platform/drivers/arm/juno_trng/juno_trng.c b/platform/drivers/arm/juno_trng/juno_trng.c
new file mode 100644
index 0000000..b520dba
--- /dev/null
+++ b/platform/drivers/arm/juno_trng/juno_trng.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * This is a modified version of the juno trng platform driver from trusted-firmware-a.
+ * The code has been modified to allow the peripheral to be accessed by S-EL0 at
+ * an arbitrary virtual address.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <util.h>
+#include "mmio.h"
+#include "juno_def.h"
+#include "juno_decl.h"
+
+/* The TRNG base address */
+static uintptr_t trng_base = 0;
+
+#define TRNG_BASE	trng_base
+#define NSAMPLE_CLOCKS	1 /* min 1 cycle, max 231 cycles */
+#define NRETRIES	5
+
+/*
+ * Sets the base address used to access the peripheral.  For
+ * S-EL0 operation, this will be a virtual address.
+ */
+void juno_trng_set_base_addr(uintptr_t addr)
+{
+	trng_base = addr;
+}
+
+static inline int output_valid(void)
+{
+	int i;
+
+	for (i = 0; i < NRETRIES; i++) {
+		uint32_t val;
+
+		val = mmio_read_32(TRNG_BASE + TRNG_STATUS);
+		if (val & 1U)
+			break;
+	}
+	if (i >= NRETRIES)
+		return 0; /* No output data available. */
+	return 1;
+}
+
+/*
+ * This function fills `buf` with `len` bytes of entropy.
+ * It uses the Trusted Entropy Source peripheral on Juno.
+ * Returns 0 when the buffer has been filled with entropy
+ * successfully and -1 otherwise.
+ */
+int juno_getentropy(void *buf, size_t len)
+{
+	uint8_t *bp = buf;
+
+	assert(buf);
+	assert(len);
+
+	/* Check for a valid base address */
+	if (!TRNG_BASE) return -1;
+
+	/* Disable interrupt mode. */
+	mmio_write_32(TRNG_BASE + TRNG_INTMASK, 0);
+	/* Program TRNG to sample for `NSAMPLE_CLOCKS`. */
+	mmio_write_32(TRNG_BASE + TRNG_CONFIG, NSAMPLE_CLOCKS);
+
+	while (len > 0) {
+		int i;
+
+		/* Start TRNG. */
+		mmio_write_32(TRNG_BASE + TRNG_CONTROL, 1);
+
+		/* Check if output is valid. */
+		if (!output_valid())
+			return -1;
+
+		/* Fill entropy buffer. */
+		for (i = 0; i < TRNG_NOUTPUTS; i++) {
+			size_t n;
+			uint32_t val;
+
+			val = mmio_read_32(TRNG_BASE + i * sizeof(uint32_t));
+			n = MIN(len, sizeof(uint32_t));
+			memcpy(bp, &val, n);
+			bp += n;
+			len -= n;
+			if (len == 0)
+				break;
+		}
+
+		/* Reset TRNG outputs. */
+		mmio_write_32(TRNG_BASE + TRNG_STATUS, 1);
+	}
+
+	return 0;
+}
diff --git a/platform/drivers/arm/juno_trng/juno_trng_adapter.c b/platform/drivers/arm/juno_trng/juno_trng_adapter.c
new file mode 100644
index 0000000..3ad8b04
--- /dev/null
+++ b/platform/drivers/arm/juno_trng/juno_trng_adapter.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <platform/interface/trng.h>
+#include <platform/interface/device_region.h>
+#include "juno_decl.h"
+
+/*
+ * A platform trng driver that uses the juno_trng driver from TF-A to provide a
+ * hardware entropy source.
+ */
+
+static int trng_poll(void *context, unsigned char *output, size_t nbyte, size_t *len)
+{
+     int status = 0;
+
+    *len = 0;
+
+    if (nbyte >= sizeof(unsigned char)) {
+
+        status = juno_getentropy(output, nbyte);
+
+        if (status == 0) {
+            *len = nbyte;
+        }
+        else {
+            /* Mbedtls currently crashes when a failure status is returned.
+             * This workaround prevents the crash for cases where no
+             * configuration has been provided for the trng.
+             */
+            status = 0;
+            *len = sizeof(unsigned char);
+        }
+    }
+
+    return status;
+}
+
+int platform_trng_create(struct platform_trng_driver *driver,
+                            const struct device_region *device_region)
+{
+    static const struct platform_trng_iface iface =  { .poll = trng_poll };
+
+    /*
+     * Default to leaving the driver in a safe but inoperable state.
+     */
+    driver->iface = &iface;
+    driver->context = NULL;
+
+    if (device_region) {
+
+        /*
+         * A device region has been provided, possibly from an external configuation.
+         */
+        juno_trng_set_base_addr(device_region->base_addr);
+    }
+
+    return 0;
+}
+
+void platform_trng_destroy(struct platform_trng_driver *driver)
+{
+    (void)driver;
+    juno_trng_set_base_addr(0);
+}
diff --git a/platform/drivers/arm/juno_trng/mmio.h b/platform/drivers/arm/juno_trng/mmio.h
new file mode 100644
index 0000000..da8d82f
--- /dev/null
+++ b/platform/drivers/arm/juno_trng/mmio.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copied from trustedfirmware-a and stripped down to only provide functions
+ * used by juno_trng.c.
+ */
+
+#ifndef MMIO_H
+#define MMIO_H
+
+#include <stdint.h>
+
+
+static inline void mmio_write_32(uintptr_t addr, uint32_t value)
+{
+	*(volatile uint32_t*)addr = value;
+}
+
+static inline uint32_t mmio_read_32(uintptr_t addr)
+{
+	return *(volatile uint32_t*)addr;
+}
+
+#endif /* MMIO_H */
diff --git a/platform/drivers/arm/tztrng/driver.cmake b/platform/drivers/arm/tztrng/driver.cmake
index 58d98c8..6a7f936 100644
--- a/platform/drivers/arm/tztrng/driver.cmake
+++ b/platform/drivers/arm/tztrng/driver.cmake
@@ -65,4 +65,4 @@
 add_dependencies(${TGT} libcc_tztrng)
 
 # Add adapter to map platform trng interface to tz-trng driver
-target_sources(${TGT} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/tztrng_trng.c")
+target_sources(${TGT} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/tztrng_adapter.c")
diff --git a/platform/drivers/arm/tztrng/tztrng_trng.c b/platform/drivers/arm/tztrng/tztrng_adapter.c
similarity index 100%
rename from platform/drivers/arm/tztrng/tztrng_trng.c
rename to platform/drivers/arm/tztrng/tztrng_adapter.c
diff --git a/platform/interface/device_region.h b/platform/interface/device_region.h
index 1ad1721..8bbf203 100644
--- a/platform/interface/device_region.h
+++ b/platform/interface/device_region.h
@@ -23,7 +23,7 @@
 {
     char dev_class[16];     /**< Identifier for class of device e.g. 'trng' */
     int dev_instance;       /**< Instance of the class of device on a platform */
-    uint8_t *base_addr;     /**< Base address or region */
+    uintptr_t base_addr;    /**< Base address or region */
     size_t io_region_size;  /**< Size of I/O region in bytes */
 };
 
diff --git a/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake b/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake
index cb01389..ff7c049 100644
--- a/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake
+++ b/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake
@@ -18,5 +18,5 @@
 #
 #-------------------------------------------------------------------------------
 if ("trng" IN_LIST _platform_driver_dependencies)
-	include(${TS_ROOT}/platform/drivers/arm/tztrng/driver.cmake)
+	include(${TS_ROOT}/platform/drivers/arm/juno_trng/driver.cmake)
 endif()
diff --git a/protocols/service/test_runner/packed-c/test_result.h b/protocols/service/test_runner/packed-c/test_result.h
index 888d2ec..7fe9d26 100644
--- a/protocols/service/test_runner/packed-c/test_result.h
+++ b/protocols/service/test_runner/packed-c/test_result.h
@@ -42,17 +42,27 @@
 struct __attribute__ ((__packed__)) ts_test_runner_test_result
 {
   uint32_t run_state;
-  uint32_t fail_line;
 };
 
 /* Variable length output parameter tags */
 enum
 {
     /* The name of the test */
-    TS_TEST_RUNNER_TEST_RESULT_TAG_NAME  = 1,
+    TS_TEST_RUNNER_TEST_RESULT_TAG_NAME = 1,
 
     /* The group the test belongs to */
-    TS_TEST_RUNNER_TEST_RESULT_TAG_GROUP  = 2
+    TS_TEST_RUNNER_TEST_RESULT_TAG_GROUP = 2,
+
+    /* Test failure recorded, optionally included on failure */
+    TS_TEST_RUNNER_TEST_RESULT_TAG_FAILURE = 3
 };
 
+/* Test failure fixed sized structure */
+struct __attribute__ ((__packed__)) ts_test_runner_test_failure
+{
+  uint32_t line_num;
+  uint64_t info;
+};
+
+
 #endif /* TS_TEST_RUNNER_TEST_RESULT */