feat(unittests): add a unit test framework.
This patch adds all the infrastructure needed to run unit tests for
RMM, including a new variant for platform host, called `host_test`.
To build and run the tests:
cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_test \
-DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
cmake --build ${RMM_BUILD_DIR} -- run-unittests
Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: If16686e111d91c563f8e7281d4ee7ca2864125ae
diff --git a/.gitmodules b/.gitmodules
index b994ee1..091b707 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -8,3 +8,6 @@
[submodule "ext/t_cose"]
path = ext/t_cose
url = https://github.com/matetothpal/t_cose.git
+[submodule "ext/cpputest"]
+ path = ext/cpputest
+ url = https://github.com/cpputest/cpputest.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80229dc..a793590 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,7 +48,7 @@
# and also where the project directory and version variables are set up.
#
-project(RMM VERSION 0.2.0 LANGUAGES ASM C)
+project(RMM VERSION 0.2.0 LANGUAGES ASM C CXX)
#
# Set global flags.
@@ -67,6 +67,11 @@
include("cmake/Platforms.cmake")
#
+# Include the Unit Test Framework
+#
+include(UnitTestFramework)
+
+#
# Include the common configuration options
#
include("cmake/CommonConfigs.cmake")
diff --git a/cmake/BuildCppUTest.cmake b/cmake/BuildCppUTest.cmake
new file mode 100644
index 0000000..df87e3c
--- /dev/null
+++ b/cmake/BuildCppUTest.cmake
@@ -0,0 +1,17 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#
+# Remove Werror from CXXFLAGS else CppUTest compiler checks will fail.
+# This will affect CMAKE_CXX_FLAG in the current scope and parent scope
+# is unaffected.
+#
+string(REPLACE "-Werror" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
+
+# Additional CXXFLAGS to get CppUTest to compile.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++98-compat-pedantic ")
+
+add_subdirectory("ext/cpputest")
+
diff --git a/cmake/CommonConfigs.cmake b/cmake/CommonConfigs.cmake
index 96dc650..6f743ca 100644
--- a/cmake/CommonConfigs.cmake
+++ b/cmake/CommonConfigs.cmake
@@ -51,7 +51,7 @@
endif()
target_compile_definitions(rmm-common
- INTERFACE "MAX_CPUS=U(${MAX_CPUS})")
+ INTERFACE "MAX_CPUS=${MAX_CPUS}U")
if(NOT(GRANULE_SIZE EQUAL 4096))
message(FATAL_ERROR "GRANULE_SIZE is not initialized correctly")
diff --git a/cmake/Modules/UnitTestFramework.cmake b/cmake/Modules/UnitTestFramework.cmake
new file mode 100644
index 0000000..758bce9
--- /dev/null
+++ b/cmake/Modules/UnitTestFramework.cmake
@@ -0,0 +1,138 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+#[=======================================================================[.rst:
+rmm_build_unittest
+------------------
+
+.. default-domain:: unit tests
+
+.. command:: rmm_build_unittest
+
+Build a unit test group for a given target
+
+.. code:: cmake
+
+ rmm_build_unittest(NAME <name> TARGET <target> SOURCES <sources>
+ [RUN_ISOLATED_TESTS <LIST of tests to run>]
+ [LIBRARIES <libraries_to_link>]
+ [ITERATIONS <iterations>])
+
+This helper function simplifies the mechanics to setup and enable an unit test.
+
+Basics
+^^^^^^
+
+Every unit test configuration has the following parameters (defined as
+strings):
+
+- ``NAME`` Name of the test. It must match the name of the CppUtest test group.
+- ``TARGET`` Target where the tests will be linked against.
+- ``SOURCES`` Source files for the tests. This is usually a single C++ file.
+- ``RUN_ISOLATED_TESTS`` Optional parameter that specifies a list of tests
+ implemented within ``SOURCES`` to be run. When this
+ list is specified, the binary is re-spawned for each
+ test and only executed once (``ITERATIONS`` is
+ ignored). Any test not included on the list will be
+ ignored.
+ If this parameter is not used, all the tests included
+ in the group will be run automatically by CppUTest,
+ the number of times specified by ``ITERATIONS``
+- ``LIBRARIES`` Optional parameter to define libraries to link against
+ the tests.
+- ``ITERATIONS`` Optional parameter that defines how many times the test will
+ run. By default it is 1 times.
+ This option is ignored when using ``RUN_ISOLATED_TESTS``
+
+#]=======================================================================]
+
+if(RMM_UNITTESTS)
+ include("${CMAKE_SOURCE_DIR}/cmake/BuildCppUTest.cmake")
+
+ # Clean ${IMPORT_TEST_GROUPS}, used to generate test_groups.h later.
+ SET(IMPORT_TEST_GROUPS "" CACHE INTERNAL "IMPORT_TEST_GROUP List")
+
+ # Generate an empty test_groups.h, needed if we don't have unittests
+ configure_file(${CMAKE_SOURCE_DIR}/plat/host/host_test/src/test_groups.h.in
+ ${CMAKE_BINARY_DIR}/plat/host/host_test/src/test_groups.h
+ @ONLY)
+
+ # Include CTest for unittests
+ include(CTest)
+
+ # Custom target to run the unit tests
+ add_custom_target(run-unittests
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMMAND ctest -C "$<CONFIG>"
+ DEPENDS rmm.elf rmm.map
+ )
+endif()
+
+function(rmm_build_unittest)
+ if(RMM_UNITTESTS)
+ set(_options "")
+ set(_multi_args "SOURCES;LIBRARIES;RUN_ISOLATED_TESTS")
+ set(_single_args "NAME;TARGET;ITERATIONS")
+
+ cmake_parse_arguments(
+ arg "${_options}" "${_single_args}" "${_multi_args}" ${ARGN})
+
+ if("NAME" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_NAME)
+ message(FATAL_ERROR "Missing unit test name")
+ endif()
+
+ if("TARGET" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_TARGET)
+ message(FATAL_ERROR "Missing test target")
+ endif()
+
+ if("SOURCES" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_SOURCES)
+ message(FATAL_ERROR "Missing test sources")
+ endif()
+
+ if("ITERATIONS" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_ITERATIONS)
+ set(arg_ITERATIONS "1")
+ endif()
+
+ target_sources("${arg_TARGET}"
+ PRIVATE ${arg_SOURCES})
+
+ target_link_libraries("${arg_TARGET}"
+ PRIVATE CppUTest ${arg_LIBRARIES})
+
+ # Add the test to the CMake test builder, so we can automate
+ # the test run process.
+ if("RUN_ISOLATED_TESTS" IN_LIST arg_KEYWORDS_MISSING_VALUES OR
+ NOT DEFINED arg_RUN_ISOLATED_TESTS)
+ # Run all tests at once
+ add_test(NAME "${arg_NAME}"
+ COMMAND ${CMAKE_BINARY_DIR}/rmm.elf
+ -g${arg_NAME}
+ -r${arg_ITERATIONS})
+ else()
+ # Register a test for each test case, so each one on them can
+ # run on isolation.
+ foreach(TEST IN LISTS arg_RUN_ISOLATED_TESTS)
+ add_test(NAME "${arg_NAME}::${TEST}"
+ COMMAND ${CMAKE_BINARY_DIR}/rmm.elf
+ -sg${arg_NAME}
+ -sn${TEST})
+ endforeach()
+ endif()
+
+ # Use CppUtest IMPORT_TEST_GROUP macro to explicitly include the new test
+ # group. This is needed as otherwise the linker will ignore the test code.
+ SET(IMPORT_TEST_GROUPS "${IMPORT_TEST_GROUPS} IMPORT_TEST_GROUP(${arg_NAME});"
+ CACHE INTERNAL "IMPORT_TEST_GROUP List")
+
+ # Generate the test_groups.h
+ configure_file(${CMAKE_SOURCE_DIR}/plat/host/host_test/src/test_groups.h.in
+ ${CMAKE_BINARY_DIR}/plat/host/host_test/src/test_groups.h
+ @ONLY)
+ endif()
+endfunction()
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index ab787a7..d41a145 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -162,6 +162,16 @@
cmake -DRMM_CONFIG=fvp_defcfg -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
cmake --build ${RMM_BUILD_DIR} -- checkincludes-codebase
+14. Perform unit tests on development host:
+
+Build and run unit tests on host platform. It is recommended to do the Debug
+build of RMM.
+
+.. code-block:: bash
+
+ cmake -DRMM_CONFIG=host_defcfg -DHOST_VARIANT=host_test -DCMAKE_BUILD_TYPE=Debug -S ${RMM_SOURCE_DIR} -B ${RMM_BUILD_DIR}
+ cmake --build ${RMM_BUILD_DIR} -- run-unittests
+
.. _build_options_table:
###################
@@ -198,6 +208,7 @@
MBEDTLS_ECP_MAX_OPS ,248 - ,1000 ,"Number of max operations per ECC signing iteration"
RMM_FPU_USE_AT_REL2 ,ON | OFF ,OFF(fake_host) ON(aarch64),"Enable FPU/SIMD usage in RMM."
RMM_MAX_GRANULES , ,0 ,"Maximum number of memory granules available to the system"
+ HOST_VARIANT ,host_build | host_test ,host_build , "Variant to build for the host platform. Only available when RMM_PLATFORM=host"
diff --git a/docs/readme.rst b/docs/readme.rst
index 25a7e1d..c198f72 100644
--- a/docs/readme.rst
+++ b/docs/readme.rst
@@ -64,7 +64,7 @@
The TF-RMM project requires to be linked with certain other 3rd party projects
and they are to be cloned from their repositories into ``ext`` folder before
-building. The projects are `MbedTLS`_, `t_cose`_, and `QCBOR`_.
+building. The projects are `MbedTLS`_, `t_cose`_, `QCBOR`_ and `CppUTest`_.
The project also contains files which are imported from other projects
into the source tree and may have a different license. Such files with
@@ -119,3 +119,4 @@
.. _BSD-3-Clause License: https://tf-rmm.readthedocs.io/en/latest/about/license.html
.. _License and Copyright for Contributions: https://tf-rmm.readthedocs.io/en/latest/process/contributing.html#license-and-copyright-for-contributions
.. _Contributor's Guide: https://tf-rmm.readthedocs.io/en/latest/process/contributing.html
+.. _CppUTest: https://github.com/cpputest/cpputest.git
diff --git a/ext/cpputest b/ext/cpputest
new file mode 160000
index 0000000..67d2dfd
--- /dev/null
+++ b/ext/cpputest
@@ -0,0 +1 @@
+Subproject commit 67d2dfd41e13f09ff218aa08e2d35f1c32f032a1
diff --git a/lib/arch/include/fake_host/instr_helpers.h b/lib/arch/include/fake_host/instr_helpers.h
index 5113cc6..50cf362 100644
--- a/lib/arch/include/fake_host/instr_helpers.h
+++ b/lib/arch/include/fake_host/instr_helpers.h
@@ -9,6 +9,20 @@
#include <host_harness.h>
#include <stddef.h>
+#ifdef __cplusplus
+/*
+ * Disable write-strings warnings when building C++ code (used for unit
+ * testing) as ISO C++ forbits converting a string constant to char*,
+ * which is actually done by DEFINE_SYSREG_{READ, WRITE}_FUNC macros.
+ */
+
+#ifdef __clang__
+ #pragma clang diagnostic ignored "-Wwrite-strings"
+#else
+ #pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif /* __clang__ */
+#endif /* __cplusplus__ */
+
/**********************************************************************
* Macros which create inline functions to read or write CPU system
* registers
diff --git a/lib/arch/include/fake_host/memory.h b/lib/arch/include/fake_host/memory.h
index 3f10f44..3f182a4 100644
--- a/lib/arch/include/fake_host/memory.h
+++ b/lib/arch/include/fake_host/memory.h
@@ -14,27 +14,27 @@
{
*ptr = val;
}
-#define SCA_WRITE64(_p, _v) __sca_write64((void *)(_p), ((uint64_t)(_v)))
+#define SCA_WRITE64(_p, _v) __sca_write64((uint64_t *)(_p), ((uint64_t)(_v)))
/* Single-Copy Atomic 64-bit write with RELEASE memory ordering semantics*/
static inline void __sca_write64_release(uint64_t *ptr, uint64_t val)
{
*ptr = val;
}
-#define SCA_WRITE64_RELEASE(_p, _v) __sca_write64_release((void *)(_p), ((uint64_t)(_v)))
+#define SCA_WRITE64_RELEASE(_p, _v) __sca_write64_release((uint64_t *)(_p), ((uint64_t)(_v)))
/* Single-Copy Atomic 64-bit read */
static inline uint64_t __sca_read64(uint64_t *ptr)
{
return *ptr;
}
-#define SCA_READ64(_p) ((typeof(*(_p)))__sca_read64((void *)(_p)))
+#define SCA_READ64(_p) ((typeof(*(_p)))__sca_read64((uint64_t *)(_p)))
/* Single-Copy Atomic 64-bit read with ACQUIRE memory ordering semantics */
static inline uint64_t __sca_read64_acquire(uint64_t *ptr)
{
return *ptr;
}
-#define SCA_READ64_ACQUIRE(_p) ((typeof(*(_p)))__sca_read64_acquire((void *)(_p)))
+#define SCA_READ64_ACQUIRE(_p) ((typeof(*(_p)))__sca_read64_acquire((uint64_t *)(_p)))
#endif /* MEMORY_H */
diff --git a/lib/realm/include/granule.h b/lib/realm/include/granule.h
index 34595c5..69d3b9e 100644
--- a/lib/realm/include/granule.h
+++ b/lib/realm/include/granule.h
@@ -221,7 +221,7 @@
g = find_lock_granule(addr, expected_state);
if (g == NULL) {
- return status_ptr(RMI_ERROR_INPUT);
+ return (struct granule *)status_ptr(RMI_ERROR_INPUT);
}
/*
@@ -230,7 +230,7 @@
*/
if (granule_refcount_read_acquire(g)) {
granule_unlock(g);
- return status_ptr(RMI_ERROR_IN_USE);
+ return (struct granule *)status_ptr(RMI_ERROR_IN_USE);
}
return g;
diff --git a/lib/realm/include/status.h b/lib/realm/include/status.h
index 51b48f1..f12335f 100644
--- a/lib/realm/include/status.h
+++ b/lib/realm/include/status.h
@@ -81,7 +81,7 @@
static inline return_code_t make_return_code(unsigned int status,
unsigned int index)
{
- return (return_code_t) {status, index};
+ return (return_code_t) {(status_t)status, index};
}
/*
diff --git a/plat/host/CMakeLists.txt b/plat/host/CMakeLists.txt
index 62fe84b..fc078ff 100644
--- a/plat/host/CMakeLists.txt
+++ b/plat/host/CMakeLists.txt
@@ -3,6 +3,21 @@
# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
#
+arm_config_option(
+ NAME HOST_VARIANT
+ HELP "Select the variant to use for the host platform"
+ TYPE STRING
+ STRINGS "host_build" "host_test"
+ DEFAULT "host_build")
+
+if(HOST_VARIANT STREQUAL host_test)
+ # Disable CppUtest self tests
+ set(TESTS "OFF" CACHE STRING "Compile and run CppUTest tests")
+
+ # Disable CppUtest Extension Library
+ set(EXTENSIONS "OFF" CACHE STRING "Use the CppUTest extension library")
+endif()
+
add_subdirectory("../common" ${RMM_BINARY_DIR}/plat/common)
add_subdirectory("common")
-add_subdirectory("host_build")
+add_subdirectory("${HOST_VARIANT}")
diff --git a/plat/host/common/CMakeLists.txt b/plat/host/common/CMakeLists.txt
index 5bbd41a..f358fa7 100644
--- a/plat/host/common/CMakeLists.txt
+++ b/plat/host/common/CMakeLists.txt
@@ -6,8 +6,8 @@
add_library(rmm-host-common)
target_link_libraries(rmm-host-common
- PRIVATE rmm-plat-common
- rmm-lib)
+ PRIVATE rmm-lib
+ rmm-plat-common)
target_sources(rmm-host-common
PRIVATE "src/host_harness_cmn.c"
diff --git a/plat/host/common/src/host_platform_api_cmn.c b/plat/host/common/src/host_platform_api_cmn.c
index a5cf7e0..2c4fe45 100644
--- a/plat/host/common/src/host_platform_api_cmn.c
+++ b/plat/host/common/src/host_platform_api_cmn.c
@@ -60,8 +60,8 @@
unsigned long plat_granule_addr_to_idx(unsigned long addr)
{
if (!(GRANULE_ALIGNED(addr) &&
- (addr < (host_util_get_granule_base() + HOST_MEM_SIZE))) &&
- (addr >= host_util_get_granule_base())) {
+ (addr < (host_util_get_granule_base() + HOST_MEM_SIZE)) &&
+ (addr >= host_util_get_granule_base()))) {
return UINT64_MAX;
}
diff --git a/plat/host/host_build/src/host_setup.c b/plat/host/host_build/src/host_setup.c
index d1b3c98..dc8379e 100644
--- a/plat/host/host_build/src/host_setup.c
+++ b/plat/host/host_build/src/host_setup.c
@@ -96,6 +96,7 @@
*/
enable_fake_host_mmu();
+ /* Start RMM */
rmm_main();
VERBOSE("RMM: Fake Host execution completed\n");
diff --git a/plat/host/host_test/CMakeLists.txt b/plat/host/host_test/CMakeLists.txt
new file mode 100644
index 0000000..c28fb13
--- /dev/null
+++ b/plat/host/host_test/CMakeLists.txt
@@ -0,0 +1,33 @@
+#
+# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+#
+
+add_library(rmm-plat-host_test)
+
+# host_test builds unittests for RMM
+arm_config_option(
+ NAME RMM_UNITTESTS
+ HELP "Enable Unitests for the build"
+ DEFAULT "ON"
+ TYPE INTERNAL)
+
+target_link_libraries(rmm-plat-host_test
+ PRIVATE rmm-lib
+ CppUTest
+ # Needed to export host_utils.h
+ PUBLIC rmm-host-common)
+
+# Used to locate test_groups.h
+target_include_directories(rmm-plat-host_test
+ PRIVATE "${CMAKE_BINARY_DIR}/plat/host/${HOST_VARIANT}/src")
+
+target_include_directories(rmm-plat-host_test
+ PUBLIC "include")
+
+target_sources(rmm-plat-host_test
+ PRIVATE "src/test_main.cpp"
+ "src/host_harness.c"
+ "src/test_helpers.c")
+
+add_library(rmm-platform ALIAS rmm-plat-host_test)
diff --git a/plat/host/host_test/include/test_harness.h b/plat/host/host_test/include/test_harness.h
new file mode 100644
index 0000000..db4f045
--- /dev/null
+++ b/plat/host/host_test/include/test_harness.h
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef TEST_HARNESS_H
+#define TEST_HARNESS_H
+
+#include <buffer.h>
+
+/*
+ * Below functions are to be defined by the tests and allow them to implement
+ * specific host harness APIs as defined in host_harness.h
+ */
+
+/*
+ * Map a given granule address to a specific slot buffer
+ * Args
+ * slot - Slot buffer type where to map to
+ * addr - Granule address to map
+ * ns - Flag to indicate if the granule is a non-secure one
+ * Return
+ * The VA (or platform equivalent) where the granule was mapped to
+ */
+void *test_buffer_map(enum buffer_slot slot,
+ unsigned long addr, bool ns);
+
+/*
+ * Unmap a given granule from its corresponding slot buffer given the
+ * mapped granule address.
+ */
+void test_buffer_unmap(void *buf);
+
+#endif /* TEST_HARNESS */
diff --git a/plat/host/host_test/include/test_helpers.h b/plat/host/host_test/include/test_helpers.h
new file mode 100644
index 0000000..1ff027d
--- /dev/null
+++ b/plat/host/host_test/include/test_helpers.h
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef TEST_HELPERS_H
+#define TEST_HELPERS_H
+
+/*
+ * Helper function to fully initialize RMM.
+ *
+ * Args
+ * secondaries - If true, support for secondary PEs is enabled.
+ */
+void test_helper_rmm_start(bool secondaries);
+
+/*
+ * Helper function to get the total number of memory granules available
+ * to the system.
+ */
+unsigned int test_helper_get_nr_granules(void);
+
+#endif
diff --git a/plat/host/host_test/src/host_harness.c b/plat/host/host_test/src/host_harness.c
new file mode 100644
index 0000000..4357c29
--- /dev/null
+++ b/plat/host/host_test/src/host_harness.c
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <host_harness.h>
+#include <test_harness.h>
+
+/*
+ * Maps addr to the requested slot buffer and returns a pointer to the
+ * fake VA for the slot (the current addr), so the host can perform R/W
+ * operations on the mapped granule.
+ */
+void *host_buffer_arch_map(enum buffer_slot slot,
+ unsigned long addr, bool ns)
+{
+ return test_buffer_map(slot, addr, ns);
+}
+
+void host_buffer_arch_unmap(void *buf)
+{
+ test_buffer_unmap(buf);
+}
diff --git a/plat/host/host_test/src/test_groups.h.in b/plat/host/host_test/src/test_groups.h.in
new file mode 100644
index 0000000..b3a548c
--- /dev/null
+++ b/plat/host/host_test/src/test_groups.h.in
@@ -0,0 +1,15 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#ifndef TEST_GROUPS_H
+#define TEST_GROUPS_H
+
+/*
+ * Add an IMPORT_TEST_GROUP for each test group, so the linker can
+ * find the tests.
+ */
+@IMPORT_TEST_GROUPS@
+
+#endif
diff --git a/plat/host/host_test/src/test_helpers.c b/plat/host/host_test/src/test_helpers.c
new file mode 100644
index 0000000..bef4759
--- /dev/null
+++ b/plat/host/host_test/src/test_helpers.c
@@ -0,0 +1,146 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <gic.h>
+#include <host_defs.h>
+#include <host_utils.h>
+#include <platform_api.h>
+#include <rmm_el3_ifc.h>
+#include <xlat_tables.h>
+
+/* Implemented in init.c and needed here */
+void rmm_warmboot_main(void);
+void rmm_main(void);
+
+/*
+ * Define and set the Boot Interface arguments.
+ */
+#define RMM_EL3_IFC_ABI_VERSION (RMM_EL3_IFC_SUPPORTED_VERSION)
+#define RMM_EL3_MAX_CPUS (MAX_CPUS)
+
+static unsigned char el3_rmm_shared_buffer[PAGE_SIZE] __aligned(PAGE_SIZE);
+
+/*
+ * Create a basic boot manifest.
+ */
+static struct rmm_core_manifest *boot_manifest =
+ (struct rmm_core_manifest *)el3_rmm_shared_buffer;
+
+/*
+ * Performs some initialization needed before RMM can be run, such as
+ * setting up callbacks for sysreg access.
+ */
+static void setup_sysreg_and_boot_manifest(void)
+{
+ /*
+ * Initialize ID_AA64MMFR0_EL1 with a physical address
+ * range of 18 bits
+ */
+ (void)host_util_set_default_sysreg_cb("id_aa64mmfr0_el1",
+ INPLACE(ID_AA64MMFR0_EL1_PARANGE, 5UL));
+
+ /*
+ * Initialize ICH_VTR_EL2 with 6 preemption bits.
+ * (PREbits is equal number of preemption bits minus one)
+ */
+ (void)host_util_set_default_sysreg_cb("ich_vtr_el2",
+ INPLACE(ICH_VTR_EL2_PRE_BITS, 5UL));
+
+ /* Used to hold CPU Id. Reset to CPUid 0. */
+ (void)host_util_set_default_sysreg_cb("tpidr_el2", 0UL);
+
+ (void)host_util_set_default_sysreg_cb("sctlr_el2", 0UL);
+
+ /* Initialize the boot manifest */
+ boot_manifest->version = RMM_EL3_IFC_SUPPORTED_VERSION;
+ boot_manifest->plat_data = (uintptr_t)NULL;
+}
+
+/*
+ * Function to emulate the turn on of the MMU for the fake_host architecture.
+ */
+static void enable_fake_mmu(void)
+{
+ write_sctlr_el2(SCTLR_EL2_WXN | SCTLR_EL2_M);
+}
+
+static void start_primary_pe(void)
+{
+ host_util_set_cpuid(0U);
+
+ /* Early setup the CpuId into tpidr_el2 */
+ write_tpidr_el2(0U);
+
+ plat_setup(0UL,
+ RMM_EL3_IFC_ABI_VERSION,
+ RMM_EL3_MAX_CPUS,
+ (uintptr_t)&el3_rmm_shared_buffer);
+
+ /*
+ * Enable the MMU. This is needed as some initialization code
+ * called by rmm_main() asserts that the mmu is enabled.
+ */
+ enable_fake_mmu();
+
+ /*
+ * rmm_main() finishhes the warmboot path.
+ *
+ * Note: It is expected that the attestation init will fail.
+ */
+ rmm_main();
+}
+
+static void start_secondary_pe(unsigned int cpuid)
+{
+ host_util_set_cpuid(cpuid);
+
+ /*
+ * Early setup the CpuId into tpidr_el2 for each secondary.
+ */
+ write_tpidr_el2(cpuid);
+
+ plat_warmboot_setup(0UL,
+ RMM_EL3_IFC_ABI_VERSION,
+ RMM_EL3_MAX_CPUS,
+ (uintptr_t)&el3_rmm_shared_buffer);
+
+ /*
+ * Enable the MMU. This is needed to avoid assertions during boot up
+ * that would otherwise occur if the MMU is disabled.
+ */
+ enable_fake_mmu();
+
+ /*
+ * Finalize the warmboot path.
+ * This enables the slot buffer mechanism.
+ */
+ rmm_warmboot_main();
+}
+
+void test_helper_rmm_start(bool secondaries)
+{
+ static bool initialized;
+
+ if (initialized == false) {
+ /* Enable RMM and setup basic structures for each test. */
+ setup_sysreg_and_boot_manifest();
+
+ /* bringup primary CPU */
+ start_primary_pe();
+
+ if (secondaries) {
+ for (unsigned int i = 1U; i < RMM_EL3_MAX_CPUS; i++) {
+ start_secondary_pe(i);
+ }
+ host_util_set_cpuid(0U);
+ }
+ initialized = true;
+ }
+}
+
+unsigned int test_helper_get_nr_granules(void)
+{
+ return HOST_NR_GRANULES;
+}
diff --git a/plat/host/host_test/src/test_main.cpp b/plat/host/host_test/src/test_main.cpp
new file mode 100644
index 0000000..a1c57e8
--- /dev/null
+++ b/plat/host/host_test/src/test_main.cpp
@@ -0,0 +1,13 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ */
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <test_groups.h>
+
+int main (int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
diff --git a/toolchains/common.cmake b/toolchains/common.cmake
index 4d710fb..86d6fb6 100644
--- a/toolchains/common.cmake
+++ b/toolchains/common.cmake
@@ -11,7 +11,7 @@
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
-foreach(language IN ITEMS ASM C)
+foreach(language IN ITEMS ASM C CXX)
string(APPEND CMAKE_${language}_FLAGS_INIT "-fno-common ")
string(APPEND CMAKE_${language}_FLAGS_INIT "-fomit-frame-pointer ")
string(APPEND CMAKE_${language}_FLAGS_INIT "-ffunction-sections ")
diff --git a/toolchains/fake_host/gnu.cmake b/toolchains/fake_host/gnu.cmake
index ca2f48f..70a1c34 100644
--- a/toolchains/fake_host/gnu.cmake
+++ b/toolchains/fake_host/gnu.cmake
@@ -12,6 +12,14 @@
DOC "Path to gcc."
REQUIRED)
+#
+# Needed to build CppUTest for unit tests
+#
+find_program(CMAKE_CXX_COMPILER
+ NAMES "g++"
+ DOC "Path to g++."
+ REQUIRED)
+
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--build-id=none ")
diff --git a/toolchains/fake_host/llvm.cmake b/toolchains/fake_host/llvm.cmake
index cb0f207..ad2ee5e 100644
--- a/toolchains/fake_host/llvm.cmake
+++ b/toolchains/fake_host/llvm.cmake
@@ -14,9 +14,10 @@
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
-foreach(language IN ITEMS ASM C)
+foreach(language IN ITEMS ASM C CXX)
string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unknown-warning-option ")
string(APPEND CMAKE_${language}_FLAGS_INIT "-Wno-unused-function ")
+ string(APPEND CMAKE_${language}_FLAGS_INIT "-fPIC ")
endforeach()
string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--build-id=none ")