cmake: tfm: support for custom CMake args when building TF-M

This commit allows a subsystem to specify additional CMake flags to be
given to the TF-M build.

The additional CMake flags can be provided through the TFM_CMAKE_OPTIONS
property on the zephyr_property_target.
Using the zephyr_property_target allows Zephyr modules to append extra
TFM_CMAKE_OPTIONS regardless of the CMake processing order.

It splits the ExternalProject_Add into a two step process with the CMake
invocation executed using add_custom_target() and the build process
using ExternalProject_Add(). The reason for this split is because CMake
generator expressions passed through ExternalProject_Add to CMake will
quoted so that `$<TARGET_PROPERTY:<tgt>,<prop>>` becomes
`"-DFOO=bar -DBAR=foo"` instead of `-DFOO=bar -DBAR=foo` which again
results in CMake failures.

Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
diff --git a/modules/trusted-firmware-m/CMakeLists.txt b/modules/trusted-firmware-m/CMakeLists.txt
index 7df9648..e3a81cb 100644
--- a/modules/trusted-firmware-m/CMakeLists.txt
+++ b/modules/trusted-firmware-m/CMakeLists.txt
@@ -36,6 +36,11 @@
 #                TF-M regression tests
 # BL2: Boolean if the TF-M build uses MCUboot. Default: True
 # ENABLED_PARTITIONS: List of TFM partitions to enable.
+# CMAKE_ARGS: Additional CMake flags to be used when building TF-M
+#             This is a list of flags, such as
+#             `CMAKE_ARGS -DARG0=val0 -DARG1=val1`
+#              or a generator expression, such as:
+#             `CMAKE_ARGS $<TARGET_PROPERTY:target,property>
 #
 # Example usage:
 #
@@ -160,30 +165,44 @@
     message(FATAL_ERROR "Unsupported ZEPHYR_TOOLCHAIN_VARIANT: ${ZEPHYR_TOOLCHAIN_VARIANT}")
   endif()
 
+  file(MAKE_DIRECTORY ${TFM_BINARY_DIR})
+  add_custom_target(tfm_cmake
+    COMMAND ${CMAKE_COMMAND}
+      -G${CMAKE_GENERATOR}
+      -DTFM_TOOLCHAIN_FILE=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/${TFM_TOOLCHAIN_FILE}
+      -DTFM_PLATFORM=${TFM_BOARD}
+      -DCROSS_COMPILE=${TFM_TOOLCHAIN_PATH}/${TFM_TOOLCHAIN_PREFIX}
+      ${TFM_CMAKE_BUILD_TYPE_ARG}
+      -DBL2=${TFM_BL2}
+      ${TFM_IPC_ARG}
+      ${TFM_ISOLATION_LEVEL_ARG}
+      ${TFM_REGRESSION_S_ARG}
+      ${TFM_REGRESSION_NS_ARG}
+      ${TFM_PROFILE_ARG}
+      ${MCUBOOT_IMAGE_NUM_ARG}
+      ${PSA_TEST_ARG}
+      ${TFM_CMAKE_ARGS}
+      -DTFM_TEST_REPO_PATH=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/tf-m-tests
+      -DMCUBOOT_PATH=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/../tfm-mcuboot
+      -DPSA_ARCH_TESTS_PATH=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/psa-arch-tests
+      ${TFM_PARTITIONS_ARGS}
+      ${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/trusted-firmware-m
+    WORKING_DIRECTORY ${TFM_BINARY_DIR}
+    COMMAND_EXPAND_LISTS
+  )
+
   include(ExternalProject)
 
   ExternalProject_Add(
     tfm
     SOURCE_DIR ${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/trusted-firmware-m
     BINARY_DIR ${TFM_BINARY_DIR}
-    CMAKE_ARGS -DTFM_TOOLCHAIN_FILE=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/${TFM_TOOLCHAIN_FILE}
-               -DTFM_PLATFORM=${TFM_BOARD}
-               -DCROSS_COMPILE=${TFM_TOOLCHAIN_PATH}/${TFM_TOOLCHAIN_PREFIX}
-               ${TFM_CMAKE_BUILD_TYPE_ARG}
-               -DBL2=${TFM_BL2}
-               ${TFM_IPC_ARG}
-               ${TFM_ISOLATION_LEVEL_ARG}
-               ${TFM_REGRESSION_S_ARG}
-               ${TFM_REGRESSION_NS_ARG}
-               ${TFM_PROFILE_ARG}
-               ${MCUBOOT_IMAGE_NUM_ARG}
-               ${PSA_TEST_ARG}
-               -DTFM_TEST_REPO_PATH=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/tf-m-tests
-               -DMCUBOOT_PATH=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/../tfm-mcuboot
-               -DPSA_ARCH_TESTS_PATH=${ZEPHYR_TRUSTED_FIRMWARE_M_MODULE_DIR}/psa-arch-tests
-               ${TFM_PARTITIONS_ARGS}
+    CONFIGURE_COMMAND ""
+    BUILD_COMMAND ${CMAKE_COMMAND} --build .
+    INSTALL_COMMAND ${CMAKE_COMMAND} --install .
     BUILD_ALWAYS True
     USES_TERMINAL_BUILD True
+    DEPENDS tfm_cmake
     BUILD_BYPRODUCTS ${BUILD_BYPRODUCTS}
   )
 
@@ -299,6 +318,7 @@
     ${TFM_IPC_ARG}
     ${TFM_REGRESSION_S_ARG}
     ${TFM_REGRESSION_NS_ARG}
+    CMAKE_ARGS $<GENEX_EVAL:$<TARGET_PROPERTY:zephyr_property_target,TFM_CMAKE_OPTIONS>>
     ENABLED_PARTITIONS ${TFM_ENABLED_PARTITIONS_ARG}
     ${TFM_PSA_TEST_ARG}
     CMAKE_BUILD_TYPE ${TFM_CMAKE_BUILD_TYPE}
diff --git a/samples/tfm_integration/tfm_integration.rst b/samples/tfm_integration/tfm_integration.rst
index 60932a3..56b807a 100644
--- a/samples/tfm_integration/tfm_integration.rst
+++ b/samples/tfm_integration/tfm_integration.rst
@@ -172,3 +172,30 @@
 
 .. _PSA Certified Level 1:
   https://www.psacertified.org/security-certification/psa-certified-level-1/
+
+Custom CMake arguments
+======================
+
+When building a Zephyr application with TF-M it might be necessary to control
+the CMake arguments passed to the TF-M build.
+
+Zephyr TF-M build offers several Kconfig options for controlling the build, but
+doesn't cover every CMake argument supported by the TF-M build system.
+
+The ``TFM_CMAKE_OPTIONS`` property on the ``zephyr_property_target`` can be used
+to pass custom CMake arguments to the TF-M build system.
+
+To pass the CMake argument ``-DFOO=bar`` to the TF-M build system, place the
+following CMake snippet in your CMakeLists.txt file.
+
+   .. code-block:: cmake
+
+     set_property(TARGET zephyr_property_target
+                  APPEND PROPERTY TFM_CMAKE_OPTIONS
+                  -DFOO=bar
+     )
+
+.. note::
+   The ``TFM_CMAKE_OPTIONS`` is a list so it is possible to append multiple
+   options. Also CMake generator expressions are supported, such as
+   ``$<1:-DFOO=bar>``