Test: Change test services to use iovec API
Changes:
- Special linking of tfm_secure_tests library is removed from
secure_fw/CMakeLists.txt if SST test partition is not used because it
doesn't contain veneers anymore.
- Disable the invert testsuite as it became deprecated.
- Remove the veneers c and h files for the test services.
- Modify the interface of the test service implementations, and the
calls to them accordingly.
Change-Id: I9c684277662cee57aa1e426c8117bedc59903c99
Signed-off-by: Mate Toth-Pal <mate.toth-pal@arm.com>
diff --git a/secure_fw/CMakeLists.txt b/secure_fw/CMakeLists.txt
index 7bd047e..4698911 100644
--- a/secure_fw/CMakeLists.txt
+++ b/secure_fw/CMakeLists.txt
@@ -142,14 +142,25 @@
embedded_set_target_compile_defines(TARGET ${PROJECT_OBJ_LIB} LANGUAGE C DEFINES __thumb2__ __ARM_FEATURE_CMSE=3 TFM_LVL=${TFM_LVL} DAUTH_CHIP_DEFAULT APPEND)
if (REGRESSION OR CORE_TEST)
- #The test service veneers may not be referenced in the secure binary so the
- #veneer objects are explicitly loaded from the secure tests library.
- if(${COMPILER} STREQUAL "ARMCLANG")
- target_link_libraries(${EXE_NAME} tfm_crypto tfm_storage tfm_audit tfm_platform tfm_attest $<TARGET_LINKER_FILE:tfm_secure_tests>\(*veneers.o\) tfm_secure_tests)
- elseif(${COMPILER} STREQUAL "GNUARM")
- target_link_libraries(${EXE_NAME} tfm_secure_tests tfm_crypto tfm_storage tfm_audit tfm_platform tfm_attest)
+ if (DEFINED TFM_PARTITION_TEST_SST AND TFM_PARTITION_TEST_SST)
+ #The test service veneers in the tfm_secure_tests library may not be
+ #referenced in the secure binary so the veneer objects are explicitly loaded
+ #from the secure tests library. However by generating the veneer files from
+ #the manifests, all the iovec interfaced veneers are in a single file in the
+ #secure_fw directory. The core test partitions use the veneers with the
+ #iovec API, so we only need the explicit load in case the SST test partition
+ #is present.
+ #FIXME Remove the explicit load and the above comment once the SST test
+ #partition uses the generated veneers.
+ if(${COMPILER} STREQUAL "ARMCLANG")
+ target_link_libraries(${EXE_NAME} tfm_crypto tfm_storage tfm_audit tfm_platform tfm_attest $<TARGET_LINKER_FILE:tfm_secure_tests>\(*veneers.o\) tfm_secure_tests)
+ elseif(${COMPILER} STREQUAL "GNUARM")
+ target_link_libraries(${EXE_NAME} tfm_secure_tests tfm_crypto tfm_storage tfm_audit tfm_platform tfm_attest)
+ else()
+ message(FATAL_ERROR "unknown compiler" )
+ endif()
else()
- message(FATAL_ERROR "unknown compiler" )
+ target_link_libraries(${EXE_NAME} tfm_crypto tfm_storage tfm_audit tfm_platform tfm_attest tfm_secure_tests)
endif()
else()
target_link_libraries(${EXE_NAME} tfm_crypto tfm_storage tfm_audit tfm_platform tfm_attest)
@@ -222,7 +233,7 @@
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "Default install location for secure_fw." FORCE)
endif()
- #Export files needed to interface external applications at: <build_dir>/install/export/tfm/
+ #Export files needed to interface external applications at: <build_dir>/install/export/tfm/
install(DIRECTORY ${TFM_ROOT_DIR}/interface/include/
DESTINATION export/tfm/inc)
diff --git a/test/suites/core/non_secure/core_ns_positive_testsuite.c b/test/suites/core/non_secure/core_ns_positive_testsuite.c
index 2a288dc..b9ee390 100644
--- a/test/suites/core/non_secure/core_ns_positive_testsuite.c
+++ b/test/suites/core/non_secure/core_ns_positive_testsuite.c
@@ -7,8 +7,7 @@
#include "core_ns_tests.h"
#include "tfm_api.h"
-#include "test/test_services/tfm_core_test/tfm_ss_core_test_veneers.h"
-#include "test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h"
+#include "tfm_veneers.h"
#include "test/suites/core/non_secure/core_test_api.h"
#include "test/test_services/tfm_core_test/core_test_defs.h"
@@ -79,12 +78,13 @@
core_tests, list_size, p_test_suite);
}
-/* Args that may or may not be used for service calls */
-int32_t args[] = {0};
-
static void tfm_core_test_ns_thread(struct test_result_t *ret)
{
- int32_t err = tfm_core_test_sfn(CORE_TEST_ID_NS_THREAD, 0, 0, 0);
+ int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_NS_THREAD;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+
+ err = tfm_spm_core_test_sfn_veneer(in_vec, 1, NULL, 0);
if (err != TFM_SUCCESS) {
TEST_FAIL("Secure function call from thread mode should be successful");
@@ -96,9 +96,12 @@
static void tfm_core_test_peripheral_access(struct test_result_t *ret)
{
- args[0] = CORE_TEST_ID_PERIPHERAL_ACCESS;
+ int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_PERIPHERAL_ACCESS;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
- int32_t err = tfm_core_test_call(tfm_core_test_sfn, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if ((err != TFM_SUCCESS) && (err < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
TEST_FAIL("TFM Core returned error.");
@@ -125,8 +128,9 @@
static void tfm_core_test_check_init(struct test_result_t *ret)
{
int32_t err;
+ struct tfm_core_test_call_args_t args = {NULL, 0, NULL, 0};
- err = tfm_core_test_call(tfm_core_test_sfn_init_success, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_init_success_veneer, &args);
if ((err != TFM_SUCCESS) && (err < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
TEST_FAIL("TFM Core returned error.");
@@ -165,15 +169,17 @@
static void tfm_core_test_mpu_access(struct test_result_t *ret)
{
int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_MPU_ACCESS;
+ uint32_t data[4] = {0};
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)},
+ {data, sizeof(data)},
+ {(void *)((int32_t)tfm_core_test_mpu_access &
+ (~(0x3))),
+ sizeof(uint32_t)} };
+ psa_outvec outvec[] = { {data, sizeof(data)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 3, outvec, 1};
- int32_t args[] = {
- CORE_TEST_ID_MPU_ACCESS,
- (int32_t)args,
- /* Make sure the pointer in code segment is word-aligned */
- (int32_t)tfm_core_test_mpu_access & (~(0x3)),
- (int32_t)args};
-
- err = tfm_core_test_call(tfm_core_test_sfn, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if (err != TFM_SUCCESS && err < TFM_PARTITION_SPECIFIC_ERROR_MIN) {
TEST_FAIL("TFM Core returned error.");
@@ -193,15 +199,17 @@
static void tfm_core_test_permissions(struct test_result_t *ret)
{
int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_MEMORY_PERMISSIONS;
+ uint32_t data[4] = {0};
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)},
+ {data, sizeof(data)},
+ {(void *)((int32_t)tfm_core_test_mpu_access &
+ (~(0x3))),
+ sizeof(uint32_t)} };
+ psa_outvec outvec[] = { {data, sizeof(data)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 3, outvec, 1};
- int32_t args[] = {
- CORE_TEST_ID_MEMORY_PERMISSIONS,
- (int32_t)args,
- /* Make sure the pointer in code segment is word-aligned */
- (int32_t)tfm_core_test_mpu_access & (~(0x3)),
- (int32_t)args};
-
- err = tfm_core_test_call(tfm_core_test_sfn, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if (err != TFM_SUCCESS && err < TFM_PARTITION_SPECIFIC_ERROR_MIN) {
TEST_FAIL("TFM Core returned error.");
@@ -225,10 +233,12 @@
uint32_t inbuf[] = {1, 2, 3, 4, 0xAAAFFF, 0xFFFFFFFF};
uint32_t outbuf[16] = {0};
int32_t result;
- int32_t args[] = {(int32_t)&result, (int32_t)inbuf,
- (int32_t)outbuf, (int32_t)sizeof(inbuf) >> 2};
+ psa_invec in_vec[] = { {inbuf, sizeof(inbuf)} };
+ psa_outvec outvec[] = { {outbuf, sizeof(outbuf)},
+ {&result, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, outvec, 2};
- res = tfm_core_test_call(tfm_core_test_2_sfn_invert, args);
+ res = tfm_core_test_call(tfm_spm_core_test_2_sfn_invert_veneer, &args);
if ((res != TFM_SUCCESS) && (res < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
TEST_FAIL("Call to secure service should be successful.");
return;
@@ -258,8 +268,11 @@
{
int32_t err;
- args[0] = CORE_TEST_ID_SS_TO_SS;
- err = tfm_core_test_call(tfm_core_test_sfn, args);
+ int32_t test_case_id = CORE_TEST_ID_SS_TO_SS;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
+
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if (err != TFM_SUCCESS && err < TFM_PARTITION_SPECIFIC_ERROR_MIN) {
TEST_FAIL("Call to secure service should be successful.");
@@ -277,9 +290,11 @@
static void tfm_core_test_share_change(struct test_result_t *ret)
{
int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_SHARE_REDIRECTION;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
- args[0] = CORE_TEST_ID_SHARE_REDIRECTION;
- err = tfm_core_test_call(tfm_core_test_sfn, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if ((err != TFM_SUCCESS) && (err < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
TEST_FAIL("TFM Core returned error.");
@@ -300,10 +315,15 @@
uint32_t inbuf[] = {1, 2, 3, 4, 0xAAAFFF, 0xFFFFFFFF};
uint32_t outbuf[16] = {0};
- int32_t args[] = {CORE_TEST_ID_SS_TO_SS_BUFFER, (int32_t)inbuf,
- (int32_t)outbuf, (int32_t)sizeof(inbuf) >> 2};
+ int32_t len = (int32_t)sizeof(inbuf) >> 2;
+ int32_t test_case_id = CORE_TEST_ID_SS_TO_SS_BUFFER;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)},
+ {inbuf, sizeof(inbuf)},
+ {&len, sizeof(int32_t)} };
+ psa_outvec outvec[] = { {outbuf, sizeof(outbuf)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 3, outvec, 1};
- res = tfm_core_test_call(tfm_core_test_sfn, args);
+ res = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if ((res != TFM_SUCCESS) && (res < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
TEST_FAIL("Call to secure service should be successful.");
return;
@@ -342,9 +362,11 @@
static void tfm_core_test_get_caller_client_id(struct test_result_t *ret)
{
int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_GET_CALLER_CLIENT_ID;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
- args[0] = CORE_TEST_ID_GET_CALLER_CLIENT_ID;
- err = tfm_core_test_call(tfm_core_test_sfn, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if (err != TFM_SUCCESS && err < TFM_PARTITION_SPECIFIC_ERROR_MIN) {
TEST_FAIL("Call to secure service should be successful.");
@@ -362,9 +384,11 @@
static void tfm_core_test_spm_request(struct test_result_t *ret)
{
int32_t err;
+ int32_t test_case_id = CORE_TEST_ID_SPM_REQUEST;
+ psa_invec in_vec[] = { {&test_case_id, sizeof(int32_t)} };
+ struct tfm_core_test_call_args_t args = {in_vec, 1, NULL, 0};
- args[0] = CORE_TEST_ID_SPM_REQUEST;
- err = tfm_core_test_call(tfm_core_test_sfn, args);
+ err = tfm_core_test_call(tfm_spm_core_test_sfn_veneer, &args);
if (err != TFM_SUCCESS && err < TFM_PARTITION_SPECIFIC_ERROR_MIN) {
TEST_FAIL("Call to secure service should be successful.");
diff --git a/test/suites/core/non_secure/core_test_api.c b/test/suites/core/non_secure/core_test_api.c
index 5729c15..05c9a77 100644
--- a/test/suites/core/non_secure/core_test_api.c
+++ b/test/suites/core/non_secure/core_test_api.c
@@ -8,8 +8,10 @@
#include "core_test_api.h"
#include "test/test_services/tfm_core_test/core_test_defs.h"
-int32_t tfm_core_test_call(void *fn_ptr, int32_t args[])
+int32_t tfm_core_test_call(void *fn_ptr, struct tfm_core_test_call_args_t *args)
{
- int32_t (*fn_ptr_to_call)(int32_t, int32_t, int32_t, int32_t) = fn_ptr;
- return fn_ptr_to_call(args[0], args[1], args[2], args[3]);
+ int32_t (*fn_ptr_to_call)(struct psa_invec*, size_t,
+ struct psa_outvec*, size_t) = fn_ptr;
+ return fn_ptr_to_call(args->in_vec, args->in_len,
+ args->out_vec, args->out_len);
}
diff --git a/test/suites/core/non_secure/core_test_api.h b/test/suites/core/non_secure/core_test_api.h
index 0d601b5..7371915 100644
--- a/test/suites/core/non_secure/core_test_api.h
+++ b/test/suites/core/non_secure/core_test_api.h
@@ -16,6 +16,17 @@
#endif
/**
+ * \brief This structure is to pass iovec arguments to the tfm_core_test_call
+ * function.
+ */
+struct tfm_core_test_call_args_t {
+ struct psa_invec *in_vec; /*!< Array of psa_invec objects */
+ size_t in_len; /*!< Number psa_invec objects in in_vec */
+ struct psa_outvec *out_vec; /*!< Array of psa_outvec objects */
+ size_t out_len; /*!< Number psa_outvec objects in out_vec */
+};
+
+/**
* \brief Calls the secure function provided in \ref fn_ptr
*
* \param[in] fn_ptr Secure function to be called.
@@ -23,7 +34,8 @@
*
* \return Returns value depending on fn_ptr.
*/
-int32_t tfm_core_test_call(void *fn_ptr, int32_t args[]);
+int32_t tfm_core_test_call(void *fn_ptr,
+ struct tfm_core_test_call_args_t *args);
#ifdef __cplusplus
}
diff --git a/test/suites/invert/non_secure/invert_ns_interface_testsuite.c b/test/suites/invert/non_secure/invert_ns_interface_testsuite.c
index fb02753..089d89e 100644
--- a/test/suites/invert/non_secure/invert_ns_interface_testsuite.c
+++ b/test/suites/invert/non_secure/invert_ns_interface_testsuite.c
@@ -7,7 +7,6 @@
#include "invert_ns_tests.h"
#include "tfm_api.h"
-#include "test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h"
#include "test/suites/core/non_secure/core_test_api.h"
#include <stdio.h>
@@ -20,9 +19,9 @@
static struct test_t invert_veeners_tests[] = {
{&tfm_invert_test_1001, "TFM_INVERT_TEST_1001",
- "Invert with valid buffer", {0} },
+ "Invert with valid buffer (DEPRECATED)", {0} },
{&tfm_invert_test_1002, "TFM_INVERT_TEST_1002",
- "Invert with invalid buffer", {0} },
+ "Invert with invalid buffer (DEPRECATED)", {0} },
};
void register_testsuite_ns_invert_interface(struct test_suite_t *p_test_suite)
@@ -41,40 +40,7 @@
*/
static void tfm_invert_test_1001(struct test_result_t *ret)
{
- int32_t err;
- int32_t res;
- int32_t len;
- int32_t i;
-
- uint32_t in_ptr[] = {0xBADC0FFEU, 0xDEADBEEFU};
- uint32_t out_ptr[sizeof(in_ptr) / sizeof(in_ptr[0])];
-
- memset(out_ptr, 0, sizeof(out_ptr));
-
- len = sizeof(in_ptr)/sizeof(in_ptr[0]);
-
- int32_t args[] = {(int32_t)&res, (int32_t)in_ptr,
- (int32_t)out_ptr, (int32_t)len};
-
- err = tfm_core_test_call(tfm_core_test_2_sfn_invert, args);
-
- if (err != TFM_SUCCESS) {
- TEST_FAIL("Call to secure service should be successful");
- return;
- }
-
- if (res != 0) {
- TEST_FAIL("Invert should succeed on small buffer");
- return;
- }
-
- for (i = 0; i < len; ++i) {
- if (~in_ptr[i] != out_ptr[i]) {
- TEST_FAIL("Output should be the inverted version of input");
- return;
- }
- }
-
+ TEST_LOG("This test is DEPRECATED and the test execution was SKIPPED\r\n");
ret->val = TEST_PASSED;
}
@@ -83,27 +49,6 @@
*/
static void tfm_invert_test_1002(struct test_result_t *ret)
{
- int32_t err;
- int32_t res;
- uint32_t *in_ptr = (uint32_t *)0xFFFFFFFFU;
- uint32_t *out_ptr = in_ptr;
-
- int32_t len = 1024;
-
- int32_t args[] = {(int32_t)&res, (int32_t)in_ptr,
- (int32_t)out_ptr, (int32_t)len};
-
- err = tfm_core_test_call(tfm_core_test_2_sfn_invert, args);
-
- if (err == TFM_SUCCESS) {
- TEST_FAIL("Call to secure service should not be successful");
- return;
- }
-
- if (res != -1) {
- TEST_FAIL("Invert should fail with large buffer. Unexpected result");
- return;
- }
-
+ TEST_LOG("This test is DEPRECATED and the test execution was SKIPPED\r\n");
ret->val = TEST_PASSED;
}
diff --git a/test/suites/invert/secure/invert_s_interface_testsuite.c b/test/suites/invert/secure/invert_s_interface_testsuite.c
index 6d074d5..5e01800 100644
--- a/test/suites/invert/secure/invert_s_interface_testsuite.c
+++ b/test/suites/invert/secure/invert_s_interface_testsuite.c
@@ -7,7 +7,6 @@
#include "invert_s_tests.h"
#include "tfm_api.h"
-#include "test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h"
#include <stdio.h>
#include <string.h>
@@ -39,30 +38,7 @@
*/
static void tfm_invert_test_1001(struct test_result_t *ret)
{
- int32_t res;
- int32_t len;
- int32_t i;
- uint32_t in_ptr[] = {0xBADC0FFEU, 0xDEADBEEFU};
- uint32_t out_ptr[sizeof(in_ptr) / sizeof(in_ptr[0])];
-
- memset(out_ptr, 0, sizeof(out_ptr));
-
- len = sizeof(in_ptr)/sizeof(in_ptr[0]);
-
- tfm_core_test_2_sfn_invert(&res, in_ptr, out_ptr, len);
-
- if (res != 0) {
- TEST_FAIL("Invert should succeed on small buffer");
- return;
- }
-
- for (i = 0; i < len; ++i) {
- if (~in_ptr[i] != out_ptr[i]) {
- TEST_FAIL("Output should be the inverted version of input");
- return;
- }
- }
-
+ TEST_LOG("This test is DEPRECATED and the test execution was SKIPPED\r\n");
ret->val = TEST_PASSED;
}
@@ -71,18 +47,6 @@
*/
static void tfm_invert_test_1002(struct test_result_t *ret)
{
- int32_t res;
- uint32_t *in_ptr = (uint32_t *)0xFFFFFFFFU;
- uint32_t *out_ptr = in_ptr;
-
- int32_t len = 1024;
-
- tfm_core_test_2_sfn_invert(&res, in_ptr, out_ptr, len);
-
- if (res != -1) {
- TEST_FAIL("Invert should fail with large buffer");
- return;
- }
-
+ TEST_LOG("This test is DEPRECATED and the test execution was SKIPPED\r\n");
ret->val = TEST_PASSED;
}
diff --git a/test/test_services/CMakeLists.inc b/test/test_services/CMakeLists.inc
index 42c309c..0fe8c4b 100644
--- a/test/test_services/CMakeLists.inc
+++ b/test/test_services/CMakeLists.inc
@@ -29,17 +29,13 @@
if (NOT DEFINED ENABLE_CORE_TESTS)
message(FATAL_ERROR "Incomplete build configuration: ENABLE_CORE_TESTS is undefined. ")
elseif(ENABLE_CORE_TESTS)
- list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test/tfm_ss_core_test.c"
- "${CORE_TEST_DIR}/tfm_core_test/tfm_ss_core_test_veneers.c"
- )
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test/tfm_ss_core_test.c")
endif()
if (NOT DEFINED ENABLE_CORE_TESTS_2)
message(FATAL_ERROR "Incomplete build configuration: ENABLE_CORE_TESTS_2 is undefined. ")
elseif(ENABLE_CORE_TESTS_2)
- list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test_2/tfm_ss_core_test_2_veneers.c"
- "${CORE_TEST_DIR}/tfm_core_test_2/tfm_ss_core_test_2.c"
- )
+ list(APPEND ALL_SRC_C_S "${CORE_TEST_DIR}/tfm_core_test_2/tfm_ss_core_test_2.c")
endif()
if (NOT DEFINED TFM_PARTITION_TEST_SST)
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.c b/test/test_services/tfm_core_test/tfm_ss_core_test.c
index 30cc172..cbdb92c 100644
--- a/test/test_services/tfm_core_test/tfm_ss_core_test.c
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.c
@@ -9,8 +9,7 @@
#include "tfm_ss_core_test.h"
#include "tfm_api.h"
#include "core_test_defs.h"
-#include "tfm_ss_core_test_veneers.h"
-#include "test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h"
+#include "tfm_veneers.h"
#include "secure_fw/core/secure_utilities.h"
#include "secure_fw/core/tfm_secure_api.h"
#include "secure_fw/include/tfm_spm_services_api.h"
@@ -41,9 +40,13 @@
return TFM_SUCCESS;
}
-
-int32_t spm_core_test_sfn_init_success(void)
+int32_t spm_core_test_sfn_init_success(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
{
+ if ((in_len != 0) || (out_len != 0)) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+
if (partition_init_done) {
return CORE_TEST_ERRNO_SUCCESS;
} else {
@@ -51,14 +54,28 @@
}
}
-int32_t spm_core_test_sfn_direct_recursion(int32_t depth)
+int32_t spm_core_test_sfn_direct_recursion(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
{
+ uint32_t depth;
+ struct psa_invec new_vec = {NULL, sizeof(uint32_t)};
+
+ if ((in_len != 1) || (out_len != 0) || (in_vec[0].len != sizeof(uint32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+
+ depth = *((uint32_t *)in_vec[0].base);
+
if (depth != 0) {
/* Protect against scenario where TF-M core fails to block recursion */
return CORE_TEST_ERRNO_SP_RECURSION_NOT_REJECTED;
}
/* Call to the same service again, should be rejected */
- int32_t ret = tfm_core_test_sfn_direct_recursion(++depth);
+ depth += 1;
+ new_vec.base = &depth;
+ int32_t ret = tfm_spm_core_test_sfn_direct_recursion_veneer(&new_vec,
+ 1, NULL, 0);
if (ret == TFM_SUCCESS) {
/* This is an unexpected return value */
@@ -205,6 +222,10 @@
uint32_t *slave_buffer = (uint32_t *)tfm_scratch_area;
/* Store result at end of scratch area to test entire range for RW access */
int32_t *result_ptr = (int32_t *)&tfm_scratch_area[tfm_scratch_area_size-4];
+ int32_t res;
+ psa_invec in_vec[] = { {slave_buffer, len*sizeof(uint32_t)} };
+ psa_outvec outvec[] = { {slave_buffer, len*sizeof(uint32_t)},
+ {result_ptr, sizeof(int32_t)} };
if (len > SS_BUFFER_LEN) {
return CORE_TEST_ERRNO_TEST_FAULT;
@@ -231,8 +252,7 @@
}
/* Call internal service with buffer handling */
- int32_t res = tfm_core_test_2_sfn_invert(result_ptr, slave_buffer,
- slave_buffer, len);
+ res = tfm_spm_core_test_2_sfn_invert_veneer(in_vec, 1, outvec, 2);
if (res != TFM_SUCCESS) {
return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
@@ -256,11 +276,10 @@
return TFM_SUCCESS;
}
-
static int32_t test_ss_to_ss(void)
{
- /* Call to a different service, should be sucessful */
- int32_t ret = tfm_core_test_2_veneer_slave_service();
+ /* Call to a different service, should be successful */
+ int32_t ret = tfm_spm_core_test_2_slave_service_veneer(NULL, 0, NULL, 0);
if (ret == TFM_SUCCESS) {
return CORE_TEST_ERRNO_SUCCESS;
@@ -278,7 +297,7 @@
caller_client_id_zi = INVALID_NS_CLIENT_ID;
- ret = tfm_core_test_2_check_caller_client_id();
+ ret = tfm_spm_core_test_2_check_caller_client_id_veneer(NULL, 0, NULL, 0);
if (ret != TFM_SUCCESS) {
return CORE_TEST_ERRNO_SLAVE_SP_CALL_FAILURE;
}
@@ -312,14 +331,9 @@
return TFM_SUCCESS;
}
-static int32_t test_spm_request(int32_t arg1, int32_t arg2, int32_t arg3)
+static int32_t test_spm_request(void)
{
- /* Arguments not used yet */
- (void)arg1;
- (void)arg2;
- (void)arg3;
-
- /* Call to a different service, should be sucessful */
+ /* Request a reset vote, should be successful */
int32_t ret = tfm_spm_request_reset_vote();
if (ret != TFM_SUCCESS) {
@@ -364,7 +378,7 @@
}
#endif
-static int32_t test_block(int32_t arg1, int32_t arg2, int32_t arg3)
+static int32_t test_block(void)
{
#ifdef CORE_TEST_INTERACTIVE
/* Only block if interactive test is turned on */
@@ -375,13 +389,42 @@
#endif /* CORE_TEST_INTERACTIVE */
}
-int32_t spm_core_test_sfn(int32_t tc, int32_t arg1, int32_t arg2, int32_t arg3)
+int32_t spm_core_test_sfn(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
{
+ uint32_t tc;
+ int32_t arg1;
+ int32_t arg2;
+ int32_t arg3;
+
+ if ((in_len < 1) || (in_vec[0].len != sizeof(uint32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+ tc = *((uint32_t *)in_vec[0].base);
+
switch (tc) {
case CORE_TEST_ID_MPU_ACCESS:
+ if ((in_len != 3) || (out_len != 1) ||
+ (in_vec[1].len < sizeof(int32_t)) ||
+ (in_vec[2].len < sizeof(int32_t)) ||
+ (out_vec[0].len < 3*sizeof(int32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+ arg1 = (int32_t)in_vec[1].base;
+ arg2 = (int32_t)in_vec[2].base;
+ arg3 = (int32_t)out_vec[0].base;
return test_mpu_access(
(uint32_t *)arg1, (uint32_t *)arg2, (uint32_t *)arg3);
case CORE_TEST_ID_MEMORY_PERMISSIONS:
+ if ((in_len != 3) || (out_len != 1) ||
+ (in_vec[1].len < sizeof(int32_t)) ||
+ (in_vec[2].len < sizeof(int32_t)) ||
+ (out_vec[0].len < sizeof(int32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+ arg1 = (int32_t)in_vec[1].base;
+ arg2 = (int32_t)in_vec[2].base;
+ arg3 = (int32_t)out_vec[0].base;
return test_memory_permissions(
(uint32_t *)arg1, (uint32_t *)arg2, (uint32_t *)arg3);
case CORE_TEST_ID_SHARE_REDIRECTION:
@@ -389,15 +432,26 @@
case CORE_TEST_ID_SS_TO_SS:
return test_ss_to_ss();
case CORE_TEST_ID_SS_TO_SS_BUFFER:
+ if ((in_len != 3) || (out_len != 1) ||
+ (in_vec[2].len != sizeof(int32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+ arg3 = *((int32_t *)in_vec[2].base);
+ if ((in_vec[1].len < arg3*sizeof(int32_t)) ||
+ (out_vec[0].len < arg3*sizeof(int32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+ arg1 = (int32_t)in_vec[1].base;
+ arg2 = (int32_t)out_vec[0].base;
return test_ss_to_ss_buffer((uint32_t *)arg1, (uint32_t *)arg2, arg3);
case CORE_TEST_ID_PERIPHERAL_ACCESS:
return test_peripheral_access();
case CORE_TEST_ID_GET_CALLER_CLIENT_ID:
return test_get_caller_client_id();
case CORE_TEST_ID_SPM_REQUEST:
- return test_spm_request(arg1, arg2, arg3);
+ return test_spm_request();
case CORE_TEST_ID_BLOCK:
- return test_block(arg1, arg2, arg3);
+ return test_block();
case CORE_TEST_ID_NS_THREAD:
/* dummy service call is enough */
return CORE_TEST_ERRNO_SUCCESS;
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test.h b/test/test_services/tfm_core_test/tfm_ss_core_test.h
index ea88850..2b84193 100644
--- a/test/test_services/tfm_core_test/tfm_ss_core_test.h
+++ b/test/test_services/tfm_core_test/tfm_ss_core_test.h
@@ -14,35 +14,74 @@
#include <inttypes.h>
#include <limits.h>
+#include "tfm_api.h"
/**
* \brief Tests whether the initialisation of the service was successful.
*
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
* \return Returns \ref CORE_TEST_ERRNO_SUCCESS on success, and
* \ref CORE_TEST_ERRNO_SP_NOT_INITED on failure.
*/
-int32_t spm_core_test_sfn_init_success(void);
+int32_t spm_core_test_sfn_init_success(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
/**
* \brief Tests what happens when a service calls itself directly.
*
- * \param[in] depth The current depth of the call (0 when first called).
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 1 in_vec object:
+ * in_vec[0].base: A buffer containing a pointer to an uint32_t value
+ * containing the current depth of the call (the value of the
+ * depth is 0 when first called).
+ * in_vec[0].len: The size of a pointer in bytes.
+
+ * The function expects 0 out_vec objects.
*
* \return Returns \ref CORE_TEST_ERRNO_SUCCESS.
*/
-int32_t spm_core_test_sfn_direct_recursion(int32_t depth);
+int32_t spm_core_test_sfn_direct_recursion(
+ struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
/**
* \brief Entry point for multiple test cases to be executed on the secure side.
*
- * \param[in] a The id of the testcase.
- * \param[in] b First parameter for testcase.
- * \param[in] c Second parameter for testcase.
- * \param[in] d Third parameter for testcase.
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects at least 1 in_vec object:
+ *
+ * in_vec[0].base: A buffer containing a pointer to an uint32_t value
+ * containing the testcase id to be executed.
+ * in_vec[0].len: The size of a pointer in bytes.
+ *
+ * The number of expected additional in_vecs and out_vecs is dependent on the id
+ * of the test case. For details see the function implementation.
*
* \return Can return various error codes.
*/
-int32_t spm_core_test_sfn(int32_t tc, int32_t arg1, int32_t arg2, int32_t arg3);
+int32_t spm_core_test_sfn(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
#ifdef __cplusplus
}
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test_veneers.c b/test/test_services/tfm_core_test/tfm_ss_core_test_veneers.c
deleted file mode 100644
index f104424..0000000
--- a/test/test_services/tfm_core_test/tfm_ss_core_test_veneers.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- */
-
-#include "tfm_ss_core_test_veneers.h"
-#include "tfm_secure_api.h"
-#include "tfm_ss_core_test.h"
-#include "secure_fw/spm/spm_api.h"
-
-__tfm_secure_gateway_attributes__
-int32_t tfm_core_test_sfn_init_success(void)
-{
- TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_ID,
- spm_core_test_sfn_init_success,
- 0, 0, 0, 0);
-}
-
-__tfm_secure_gateway_attributes__
-int32_t tfm_core_test_sfn_direct_recursion(int32_t depth)
-{
- TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_ID,
- spm_core_test_sfn_direct_recursion,
- depth, 0, 0, 0);
-}
-
-__tfm_secure_gateway_attributes__
-int32_t tfm_core_test_sfn(int32_t a, int32_t b, int32_t c, int32_t d)
-{
- TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_ID, spm_core_test_sfn,
- a, b, c, d);
-}
diff --git a/test/test_services/tfm_core_test/tfm_ss_core_test_veneers.h b/test/test_services/tfm_core_test/tfm_ss_core_test_veneers.h
deleted file mode 100644
index 293835d..0000000
--- a/test/test_services/tfm_core_test/tfm_ss_core_test_veneers.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- */
-
-#ifndef __TFM_SS_CORE_TEST_VENEERS_H__
-#define __TFM_SS_CORE_TEST_VENEERS_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-#include <cmsis_compiler.h>
-
-/**
- * \brief Tests whether the initialisation of the service was successful.
- *
- * \return Returns \ref CORE_TEST_ERRNO_SUCCESS on success, and
- * \ref CORE_TEST_ERRNO_SP_NOT_INITED on failure.
- */
-int32_t tfm_core_test_sfn_init_success(void);
-
-/**
- * \brief Tests what happens when a service calls itself directly.
- *
- * \param[in] depth The current depth of the call (0 when first called).
- *
- * \return Returns \ref CORE_TEST_ERRNO_SUCCESS on succes, error othervise.
- */
-int32_t tfm_core_test_sfn_direct_recursion(int32_t depth);
-
-/**
- * \brief Entry point for multiple test cases to be executed on the secure side.
- *
- * \param[in] a The id of the testcase.
- * \param[in] b First parameter for testcase.
- * \param[in] c Second parameter for testcase.
- * \param[in] d Third parameter for testcase.
- *
- * \return Can return various error codes.
- */
-int32_t tfm_core_test_sfn(int32_t a, int32_t b, int32_t c, int32_t d);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __TFM_SS_CORE_TEST_VENEERS_H__ */
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
index 052c0b0..dfd3d2c 100644
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.c
@@ -35,8 +35,13 @@
return TFM_SUCCESS;
}
-int32_t spm_core_test_2_slave_service(void)
+int32_t spm_core_test_2_slave_service(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
{
+ if ((in_len != 0) || (out_len != 0)) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+
return TFM_SUCCESS;
}
@@ -80,11 +85,30 @@
/* Invert function */
#define SFN_INVERT_MAX_LEN 128
-int32_t spm_core_test_2_sfn_invert(int32_t *res_ptr, uint32_t *in_ptr,
- uint32_t *out_ptr, int32_t len)
+int32_t spm_core_test_2_sfn_invert(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len)
{
int32_t i;
static uint32_t invert_buffer[SFN_INVERT_MAX_LEN];
+ int32_t len;
+ uint32_t *in_ptr;
+ uint32_t *out_ptr;
+ int32_t *res_ptr;
+
+ if (in_len != 1 || out_len != 2) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+
+ if ((out_vec[0].len < in_vec[0].len) || (in_vec[0].len%4 != 0) ||
+ (out_vec[1].len < sizeof(int32_t))) {
+ return TFM_ERROR_INVALID_PARAMETER;
+ }
+
+ len = in_vec[0].len / 4;
+
+ in_ptr = (uint32_t *)in_vec[0].base;
+ out_ptr = (uint32_t *)out_vec[0].base;
+ res_ptr = (int32_t *)out_vec[1].base;
if (tfm_core_memory_permission_check(res_ptr, sizeof(int32_t),
TFM_MEMORY_ACCESS_RW) != TFM_SUCCESS) {
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
index e1d7bc0..0ae9068 100644
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
+++ b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2.h
@@ -15,32 +15,61 @@
#include <inttypes.h>
#include <limits.h>
+#include "tfm_api.h"
+
/**
* \brief A minimal test service to be called from another service.
*
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
* \return Returns \ref TFM_SUCCESS.
*/
-int32_t spm_core_test_2_slave_service(void);
+int32_t spm_core_test_2_slave_service(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
/**
* \brief Bitwise inverts the buffer received as input.
*
- * \param[out] res_ptr Result, 0 on success, -1 othervise.
- * \param[in] in_ptr Buffer containing data to be inverted.
- * \param[out] out_ptr Buffer to store the inverted data.
- * \param[in] len Number of bytes to be inverted.
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 2 in_vec objects:
+ * in_vec[0].base: A pointer to the buffer containing the data to be inverted
+ * in_vec[0].len: The length of the buffer
+ * in_vec[1].base: A pointer to an int32_t object
+ * in_vec[1].len: The size of int32_t object
+ *
+ * The function expects 1 out_vec object:
+ * out_vec[0].base: A pointer to the buffer to put the result to
+ * out_vec[0].len: The length of the buffer
*
* \return Returns \ref TFM_SUCCESS on success, TFM_PARTITION_BUSY otherwise.
*/
-int32_t spm_core_test_2_sfn_invert(
- int32_t *res_ptr, uint32_t *in_ptr, uint32_t *out_ptr, int32_t len);
+int32_t spm_core_test_2_sfn_invert(struct psa_invec *in_vec, size_t in_len,
+ struct psa_outvec *out_vec, size_t out_len);
/**
* \brief A minimal test secure function to be called from another partition.
*
* Checks the functionality of querying the client ID of the caller service.
*
+ * \param[in] in_vec Array of psa_invec objects
+ * \param[in] in_len Number psa_invec objects in in_vec
+ * \param[in] out_vec Array of psa_outvec objects
+ * \param[in] out_len Number psa_outvec objects in out_vec
+ *
+ * The function expects 0 in_vec objects.
+ * The function expects 0 out_vec objects.
+ *
* \return Returns \ref TFM_SUCCESS on success, \ref CORE_TEST_ERRNO_TEST_FAULT
* othervise.
*/
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c
deleted file mode 100644
index 3ec7e38..0000000
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- */
-
-#include "tfm_ss_core_test_2_veneers.h"
-#include "tfm_secure_api.h"
-#include "tfm_ss_core_test_2.h"
-#include "secure_fw/spm/spm_api.h"
-
-/* Add functions to the service*/
-__tfm_secure_gateway_attributes__
-int32_t tfm_core_test_2_veneer_slave_service(void)
-{
- TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_2_ID,
- spm_core_test_2_slave_service,
- 0, 0, 0, 0);
-}
-
-__tfm_secure_gateway_attributes__
-int32_t tfm_core_test_2_sfn_invert(int32_t *res_ptr, uint32_t *in_ptr,
- uint32_t *out_ptr, int32_t len)
-{
- TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_2_ID,
- spm_core_test_2_sfn_invert,
- res_ptr, in_ptr, out_ptr, len);
-}
-
-__tfm_secure_gateway_attributes__
-int32_t tfm_core_test_2_check_caller_client_id(void)
-{
- TFM_CORE_SFN_REQUEST(TFM_SP_CORE_TEST_2_ID,
- spm_core_test_2_check_caller_client_id,
- 0, 0, 0, 0);
-}
diff --git a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h b/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h
deleted file mode 100644
index b556bdd..0000000
--- a/test/test_services/tfm_core_test_2/tfm_ss_core_test_2_veneers.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2017-2018, Arm Limited. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- */
-
-#ifndef __TFM_SS_CORE_TEST_2_VENEERS_H__
-#define __TFM_SS_CORE_TEST_2_VENEERS_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-#include <cmsis_compiler.h>
-
-/**
- * \brief A minimal test service to be called from another service.
- *
- * \return Returns \ref TFM_SUCCESS.
- */
-int32_t tfm_core_test_2_veneer_slave_service(void);
-
-/**
- * \brief Bitwise inverts the buffer received as input.
- *
- * \param[out] res_ptr Result, 0 on success, -1 othervise.
- * \param[in] in_ptr Buffer containing data to be inverted.
- * \param[out] out_ptr Buffer to store the inverted data.
- * \param[in] len Number of bytes to be inverted.
- *
- * \return Returns \ref TFM_SUCCESS on success, TFM_PARTITION_BUSY otherwise.
- */
-int32_t tfm_core_test_2_sfn_invert(int32_t *res_ptr,
- uint32_t *in_ptr,
- uint32_t *out_ptr,
- int32_t len);
-
-/**
- * \brief A minimal test secure function to be called from another partition.
- *
- * Checks the functionality of querying the client ID of the caller service.
- *
- * \return Returns \ref TFM_SUCCESS on success, \ref CORE_TEST_ERRNO_TEST_FAULT
- * othervise.
- */
-int32_t tfm_core_test_2_check_caller_client_id(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __TFM_SS_CORE_TEST_2_VENEERS_H__ */