SPM: Introduce option for halting instead of rebooting on panic

Introduce the option to halt instead of reboot to retain the backtrace
that triggered the fault and thereby make it easier to debug.

In addition to retaining the backtrace it is also easier to debug a
program that has halted than one that infinitely reboots.

Also, introduce tfm_hal_system_halt() to allow platform specific
behaviour when halting the system.

Change-Id: I965bf90c6b47bfff1a52ffbf66ca24d567456801
Signed-off-by: Sebastian Bøe <sebastian.boe@nordicsemi.no>
diff --git a/config/build_type/debug.cmake b/config/build_type/debug.cmake
index 29a97fd..0875490 100644
--- a/config/build_type/debug.cmake
+++ b/config/build_type/debug.cmake
@@ -8,3 +8,5 @@
 set(MBEDCRYPTO_BUILD_TYPE               relwithdebinfo CACHE STRING "Build type of Mbed Crypto library")
 set(TFM_SPM_LOG_LEVEL                   TFM_SPM_LOG_LEVEL_DEBUG CACHE STRING "Set debug SPM log level as Debug level")
 set(TFM_PARTITION_LOG_LEVEL             TFM_PARTITION_LOG_LEVEL_DEBUG CACHE STRING "Set debug SP log level as Debug level")
+
+set(CONFIG_TFM_HALT_ON_CORE_PANIC       ON         CACHE BOOL      "On fatal errors in the secure firmware, halt instead of rebooting.")
diff --git a/config/config_default.cmake b/config/config_default.cmake
index 424e73a..03b6faf 100755
--- a/config/config_default.cmake
+++ b/config/config_default.cmake
@@ -51,6 +51,8 @@
 
 set(TFM_EXCEPTION_INFO_DUMP             OFF         CACHE BOOL      "On fatal errors in the secure firmware, capture info about the exception. Print the info if the SPM log level is sufficient.")
 
+set(CONFIG_TFM_HALT_ON_CORE_PANIC       OFF         CACHE BOOL       "On fatal errors in the secure firmware, halt instead of rebooting.")
+
 set(CONFIG_TFM_FP                       "soft"      CACHE STRING    "FP ABI type in SPE and NSPE: soft-Software ABI, hard-Hardware ABI")
 set(CONFIG_TFM_LAZY_STACKING            OFF         CACHE BOOL      "Enable/disable lazy stacking")
 
diff --git a/docs/technical_references/design_docs/hardware_abstraction_layer.rst b/docs/technical_references/design_docs/hardware_abstraction_layer.rst
index f843e70..6a0f6ed 100644
--- a/docs/technical_references/design_docs/hardware_abstraction_layer.rst
+++ b/docs/technical_references/design_docs/hardware_abstraction_layer.rst
@@ -234,6 +234,38 @@
 
 The platform can uninitialize some resources before reset.
 
+When ``CONFIG_TFM_HALT_ON_PANIC`` is disabled this function is called to reset
+the system when a fatal error occurs.
+
+**Parameter**
+
+- ``void`` - None
+
+**Return Values**
+
+- ``void`` - None
+
+**Note**
+
+This API should not return.
+
+tfm_hal_system_halt()
+^^^^^^^^^^^^^^^^^^^^^
+**Prototype**
+
+.. code-block:: c
+
+  void tfm_hal_system_halt(void)
+
+**Description**
+
+This API enters the CPU into an infinite loop.
+
+The platform can uninitialize some resources before looping forever.
+
+When ``CONFIG_TFM_HALT_ON_PANIC`` is enabled this function is called to halt the
+system when a fatal error occurs.
+
 **Parameter**
 
 - ``void`` - None
@@ -832,4 +864,4 @@
 
 --------------
 
-*Copyright (c) 2020-2021, Arm Limited. All rights reserved.*
+*Copyright (c) 2020-2022, Arm Limited. All rights reserved.*
diff --git a/platform/ext/common/tfm_platform.c b/platform/ext/common/tfm_platform.c
index 3b5860e..0c7aa98 100644
--- a/platform/ext/common/tfm_platform.c
+++ b/platform/ext/common/tfm_platform.c
@@ -12,3 +12,20 @@
 {
     NVIC_SystemReset();
 }
+
+__WEAK void tfm_hal_system_halt(void)
+{
+    /*
+     * Disable IRQs to stop all threads, not just the thread that
+     * halted the system.
+     */
+    __disable_irq();
+
+    /*
+     * Enter sleep to reduce power consumption and do it in a loop in
+     * case a signal wakes up the CPU.
+     */
+    while (1) {
+        __WFE();
+    }
+}
diff --git a/platform/include/tfm_hal_platform.h b/platform/include/tfm_hal_platform.h
index 9af08f7..e584b8b 100644
--- a/platform/include/tfm_hal_platform.h
+++ b/platform/include/tfm_hal_platform.h
@@ -44,6 +44,11 @@
 void tfm_hal_system_reset(void);
 
 /**
+ * \brief System halt
+ */
+void tfm_hal_system_halt(void);
+
+/**
  * \brief Set up the RNG for use with random delays.
  *
  * \retval TFM_HAL_SUCCESS        Platform specific random number generation
diff --git a/secure_fw/spm/CMakeLists.txt b/secure_fw/spm/CMakeLists.txt
index 9803599..746d2ce 100755
--- a/secure_fw/spm/CMakeLists.txt
+++ b/secure_fw/spm/CMakeLists.txt
@@ -95,6 +95,7 @@
     PRIVATE
         $<$<CONFIG:Debug>:TFM_CORE_DEBUG>
         $<$<AND:$<BOOL:${BL2}>,$<BOOL:${MCUBOOT_MEASURED_BOOT}>>:BOOT_DATA_AVAILABLE>
+        $<$<BOOL:${CONFIG_TFM_HALT_ON_CORE_PANIC}>:CONFIG_TFM_HALT_ON_CORE_PANIC>
         $<$<BOOL:${TFM_NS_MANAGE_NSID}>:TFM_NS_MANAGE_NSID>
         $<$<BOOL:${TFM_PSA_API}>:CONFIG_TFM_CONN_HANDLE_MAX_NUM=${CONFIG_TFM_CONN_HANDLE_MAX_NUM}>
         # CONFIG_TFM_FP
diff --git a/secure_fw/spm/ffm/utilities.c b/secure_fw/spm/ffm/utilities.c
index e3f3697..cdc5d7c 100644
--- a/secure_fw/spm/ffm/utilities.c
+++ b/secure_fw/spm/ffm/utilities.c
@@ -13,6 +13,21 @@
 {
     fih_delay();
 
+#ifdef CONFIG_TFM_HALT_ON_CORE_PANIC
+
+    /*
+     * Halt instead of reboot to retain the backtrace that triggered
+     * the fault and thereby make it easier to debug.
+     */
+    tfm_hal_system_halt();
+
+#ifdef TFM_FIH_PROFILE_ON
+    fih_delay();
+
+    tfm_hal_system_halt();
+#endif
+
+#else /* CONFIG_TFM_HALT_ON_CORE_PANIC */
     /*
      * FixMe: In the first stage, the SPM will restart the entire system when a
      * programmer error is detected in either the SPE or NSPE.
@@ -28,4 +43,6 @@
 
     tfm_hal_system_reset();
 #endif
+
+#endif /* CONFIG_TFM_HALT_ON_CORE_PANIC */
 }