SPM: Check both MODE and SPSEL to choose the stack during SVC

The exception frame is never placed on PSP when in handler mode.
If SPSEL is 1 (PSP), it is "read as 0", which means that the SVC handler
must also check the MODE bit of the EXC_RETURN to properly know which
stack the exception frame is on.

Previously, the checking of the SPSEL bit was done in assembly in a number
of places. This patch refactors that logic out of assembly, into C code,
since the new logic is more complicated.

Note that the logic works just as well for ARMv7 and below.

This patch is initially created by:
Øyvind Rønningstad <oyvind.ronningstad@nordicsemi.no>

Change-Id: I62dda123d7a410251091c416954d2ac4904568ba
Signed-off-by: Kevin Peng <kevin.peng@arm.com>
diff --git a/secure_fw/spm/cmsis_func/arch.c b/secure_fw/spm/cmsis_func/arch.c
index c615d13..6fe0550 100644
--- a/secure_fw/spm/cmsis_func/arch.c
+++ b/secure_fw/spm/cmsis_func/arch.c
@@ -10,7 +10,7 @@
 #include "tfm/tfm_spm_services.h"
 
 #if defined(__ICCARM__)
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t lr, uint32_t *msp);
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return);
 #pragma required=tfm_core_svc_handler
 #endif
 
@@ -308,26 +308,23 @@
 #if !defined(__ICCARM__)
     ".syntax unified                        \n"
 #endif
-    "MRS     r0, PSP                        \n"
-    "MRS     r2, MSP                        \n"
-    "MOVS    r1, #4                         \n"
-    "MOV     r3, lr                         \n"
-    "TST     r1, r3                         \n"
+    "MRS     r0, MSP                        \n"
+    "MOV     r2, lr                         \n"
+    "MOVS    r3, #8                         \n"
+    "TST     r2, r3                         \n"
     "BNE     from_thread                    \n"
     /*
      * This branch is taken when the code is being invoked from handler mode.
      * This happens when a de-privileged interrupt handler is to be run. Seal
      * the stack before de-privileging.
      */
-    "LDR     r0, =0xFEF5EDA5                \n"
-    "MOVS    r3, r0                         \n"
-    "PUSH    {r0, r3}                       \n"
-    /* Overwrite r0 with MSP */
-    "MOV     r0, r2                         \n"
+    "LDR     r1, =0xFEF5EDA5                \n"
+    "MOVS    r3, r1                         \n"
+    "PUSH    {r1, r3}                       \n"
     "from_thread:                           \n"
-    "MOV     r1, lr                         \n"
+    "MRS     r1, PSP                        \n"
     "BL      tfm_core_svc_handler           \n"
-    "MOVS    r1, #4                         \n"
+    "MOVS    r1, #8                         \n"
     "TST     r1, r0                         \n"
     "BNE     to_thread                      \n"
     /*
@@ -345,18 +342,11 @@
 __attribute__((naked)) void SVC_Handler(void)
 {
     __ASM volatile(
-    "MOVS    r0, #4                \n" /* Check store SP in thread mode to r0 */
-    "MOV     r1, lr                \n"
-    "TST     r0, r1                \n"
-    "BEQ     handler               \n"
-    "MRS     r0, PSP               \n" /* Coming from thread mode */
-    "B       sp_stored             \n"
-    "handler:                      \n"
-    "BX      lr                    \n" /* Coming from handler mode */
-    "sp_stored:                    \n"
-    "MOV     r1, lr                \n"
-    "BL      tfm_core_svc_handler  \n"
-    "BX      r0                    \n"
+    "MRS     r0, MSP                        \n"
+    "MRS     r1, PSP                        \n"
+    "MOV     r2, lr                         \n"
+    "BL      tfm_core_svc_handler           \n"
+    "BX      r0                             \n"
     );
 }
 #endif
diff --git a/secure_fw/spm/cmsis_func/tfm_core_svcalls_func.c b/secure_fw/spm/cmsis_func/tfm_core_svcalls_func.c
index 95a95ae..a3c536b 100644
--- a/secure_fw/spm/cmsis_func/tfm_core_svcalls_func.c
+++ b/secure_fw/spm/cmsis_func/tfm_core_svcalls_func.c
@@ -29,15 +29,26 @@
  */
 #include "tfm_secure_irq_handlers.inc"
 
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t lr, uint32_t *msp)
+
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return)
 {
     uint8_t svc_number = 0;
+    uint32_t *svc_args = msp;
+    uint32_t retval = exc_return;
+
+    if ((exc_return & EXC_RETURN_MODE) && (exc_return & EXC_RETURN_SPSEL)) {
+        /* Use PSP when both EXC_RETURN.MODE and EXC_RETURN.SPSEL are set */
+        svc_args = psp;
+    } else {
+        svc_args = msp;
+    }
+
     /*
      * Stack contains:
      * r0, r1, r2, r3, r12, r14 (lr), the return address and xPSR
      * First argument (r0) is svc_args[0]
      */
-    if (is_return_secure_stack(lr)) {
+    if (is_return_secure_stack(exc_return)) {
         /* SV called directly from secure context. Check instruction for
          * svc_number
          */
@@ -51,10 +62,10 @@
     }
     switch (svc_number) {
     case TFM_SVC_SFN_REQUEST:
-        lr = tfm_spm_partition_request_svc_handler(svc_args, lr);
+        retval = tfm_spm_partition_request_svc_handler(svc_args, exc_return);
         break;
     case TFM_SVC_SFN_RETURN:
-        lr = tfm_spm_partition_return_handler(lr);
+        retval = tfm_spm_partition_return_handler(exc_return);
         break;
     case TFM_SVC_GET_CALLER_CLIENT_ID:
         tfm_spm_get_caller_client_id_handler(svc_args);
@@ -63,10 +74,10 @@
         tfm_spm_request_handler((struct tfm_state_context_t *)svc_args);
         break;
     case TFM_SVC_DEPRIV_REQ:
-        lr = tfm_spm_depriv_req_handler(svc_args, lr);
+        retval = tfm_spm_depriv_req_handler(svc_args, exc_return);
         break;
     case TFM_SVC_DEPRIV_RET:
-        lr = tfm_spm_depriv_return_handler(msp, lr);
+        retval = tfm_spm_depriv_return_handler(msp, exc_return);
         break;
     case TFM_SVC_PSA_WAIT:
         tfm_spm_psa_wait(svc_args);
@@ -85,12 +96,12 @@
         break;
     default:
 #ifdef PLATFORM_SVC_HANDLERS
-        svc_args[0] = platform_svc_handlers(svc_num, svc_args, lr);
+        svc_args[0] = platform_svc_handlers(svc_num, svc_args, exc_return);
 #endif
         break;
     }
 
-    return lr;
+    return retval;
 }
 
 void tfm_access_violation_handler(void)
diff --git a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.c b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.c
index fd3e8b6..dad9667 100644
--- a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.c
+++ b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.c
@@ -85,25 +85,18 @@
 }
 
 #if defined(__ICCARM__)
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t exc_return);
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return);
 #pragma required = tfm_core_svc_handler
 #endif
 
 __attribute__((naked)) void SVC_Handler(void)
 {
     __ASM volatile(
-    "MOVS    r0, #4                \n" /* Check store SP in thread mode to r0 */
-    "MOV     r1, lr                \n"
-    "TST     r0, r1                \n"
-    "BEQ     handler               \n"
-    "MRS     r0, PSP               \n" /* Coming from thread mode */
-    "B       sp_stored             \n"
-    "handler:                      \n"
-    "BX      lr                    \n" /* Coming from handler mode */
-    "sp_stored:                    \n"
-    "MOV     r1, lr                \n"
-    "BL      tfm_core_svc_handler  \n"
-    "BX      r0                    \n"
+    "MRS     r0, MSP                        \n"
+    "MRS     r1, PSP                        \n"
+    "MOV     r2, lr                         \n"
+    "BL      tfm_core_svc_handler           \n"
+    "BX      r0                             \n"
     );
 }
 
diff --git a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.h b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.h
index 0d8b79d..4f2fb3a 100644
--- a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.h
+++ b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v6m_v7m.h
@@ -23,6 +23,13 @@
 /* Initial EXC_RETURN value in LR when a thread is loaded at the first time */
 #define EXC_RETURN_THREAD_S_PSP                 0xFFFFFFFD
 
+/* Exception return behavior */
+
+/* stack pointer used to restore context: 0=MSP 1=PSP. */
+#define EXC_RETURN_SPSEL    (1UL << 2)
+/* processor mode for return: 0=Handler mode 1=Thread mod. */
+#define EXC_RETURN_MODE     (1UL << 3)
+
 struct tfm_arch_ctx_t {
     uint32_t    r8;
     uint32_t    r9;
diff --git a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_base.c b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_base.c
index 4379707..a210ee0 100644
--- a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_base.c
+++ b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_base.c
@@ -108,23 +108,16 @@
 }
 
 #if defined(__ICCARM__)
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t exc_return);
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return);
 #pragma required = tfm_core_svc_handler
 #endif
 
 __attribute__((naked)) void SVC_Handler(void)
 {
     __ASM volatile(
-    "MRS     r2, MSP                        \n"
-    "MOVS    r1, #4                         \n"
-    "MOV     r3, lr                         \n"
-    "MOV     r0, r2                         \n"
-    "TST     r1, r3                         \n"
-    "BEQ     handler                        \n"
-    /* If SVC was made from thread mode, overwrite r0 with PSP */
-    "MRS     r0, PSP                        \n"
-    "handler:                               \n"
-    "MOV     r1, lr                         \n"
+    "MRS     r0, MSP                        \n"
+    "MRS     r1, PSP                        \n"
+    "MOV     r2, lr                         \n"
     "BL      tfm_core_svc_handler           \n"
     "BX      r0                             \n"
     );
diff --git a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_main.c b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_main.c
index 7ee1141..7b9f169 100644
--- a/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_main.c
+++ b/secure_fw/spm/cmsis_psa/arch/tfm_arch_v8m_main.c
@@ -94,20 +94,16 @@
 }
 
 #if defined(__ICCARM__)
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t exc_return);
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return);
 #pragma required = tfm_core_svc_handler
 #endif
 
 __attribute__((naked)) void SVC_Handler(void)
 {
     __ASM volatile(
-    "MRS     r2, MSP                        \n"
-    /* Check store SP in thread mode to r0 */
-    "TST     lr, #4                         \n"
-    "ITE     EQ                             \n"
-    "MOVEQ   r0, r2                         \n"
-    "MRSNE   r0, PSP                        \n"
-    "MOV     r1, lr                         \n"
+    "MRS     r0, MSP                        \n"
+    "MRS     r1, PSP                        \n"
+    "MOV     r2, lr                         \n"
     "BL      tfm_core_svc_handler           \n"
     "BX      r0                             \n"
     );
diff --git a/secure_fw/spm/cmsis_psa/tfm_core_svcalls_ipc.c b/secure_fw/spm/cmsis_psa/tfm_core_svcalls_ipc.c
index 042b04a..cff1ee3 100644
--- a/secure_fw/spm/cmsis_psa/tfm_core_svcalls_ipc.c
+++ b/secure_fw/spm/cmsis_psa/tfm_core_svcalls_ipc.c
@@ -123,9 +123,19 @@
     return PSA_SUCCESS;
 }
 
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t exc_return)
+
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return)
 {
     tfm_svc_number_t svc_number = TFM_SVC_PSA_FRAMEWORK_VERSION;
+    uint32_t *svc_args = msp;
+
+    if ((exc_return & EXC_RETURN_MODE) && (exc_return & EXC_RETURN_SPSEL)) {
+        /* Use PSP when both EXC_RETURN.MODE and EXC_RETURN.SPSEL are set */
+        svc_args = psp;
+    } else {
+        svc_args = msp;
+    }
+
     /*
      * Stack contains:
      * r0, r1, r2, r3, r12, r14 (lr), the return address and xPSR
diff --git a/secure_fw/spm/cmsis_psa/tfm_svcalls.h b/secure_fw/spm/cmsis_psa/tfm_svcalls.h
index d553fc8..d3b374f 100644
--- a/secure_fw/spm/cmsis_psa/tfm_svcalls.h
+++ b/secure_fw/spm/cmsis_psa/tfm_svcalls.h
@@ -12,11 +12,12 @@
 /**
  * \brief The C source of SVCall handlers
  *
- * \param[in] svc_args          The arguments list.
+ * \param[in] msp               MSP at SVCall entry.
+ * \param[in] psp               PSP at SVCall entry.
  * \param[in] exc_return        EXC_RETURN value of the SVC.
  *
  * \returns                     EXC_RETURN value indicates where to return.
  */
-uint32_t tfm_core_svc_handler(uint32_t *svc_args, uint32_t exc_return);
+uint32_t tfm_core_svc_handler(uint32_t *msp, uint32_t *psp, uint32_t exc_return);
 
 #endif