1 /*
2  * Copyright (c) 2021, Nordic Semiconductor ASA. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <string.h>
8 #include "tfm_arch.h"
9 #include "tfm_spm_log.h"
10 /* "exception_info.h" must be the last include because of the IAR pragma */
11 #include "exception_info.h"
12 
13 static struct exception_info_t exception_info;
14 
15 /**
16  * \brief Check whether the exception was triggered in thread or handler mode.
17  *
18  * \param[in] lr            LR register containing the EXC_RETURN value.
19  *
20  * \retval true             The exception will return to thread mode.
21  */
is_return_thread_mode(uint32_t lr)22 __STATIC_INLINE bool is_return_thread_mode(uint32_t lr)
23 {
24 #if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
25     return !((lr == EXC_RETURN_HANDLER) || (lr == EXC_RETURN_HANDLER_FPU));
26 #elif defined(__ARM_ARCH_8M_BASE__) || defined(__ARM_ARCH_8M_MAIN__) \
27         || defined(__ARM_ARCH_8_1M_MAIN__)
28     return (lr & EXC_RETURN_MODE);
29 #else
30     return !(lr == EXC_RETURN_HANDLER);
31 #endif
32 }
33 
34 /**
35  * \brief Check whether the PSP or MSP is used to restore stack frame on
36  *        exception return.
37  *
38  * \param[in] lr            LR register containing the EXC_RETURN value.
39  *
40  * \retval true             The exception frame is on the PSP
41  */
is_return_psp(uint32_t lr)42 __STATIC_INLINE bool is_return_psp(uint32_t lr)
43 {
44 #if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
45     return ((lr == EXC_RETURN_THREAD_PSP) || (lr == EXC_RETURN_THREAD_PSP_FPU));
46 #elif defined(__ARM_ARCH_8M_BASE__) || defined(__ARM_ARCH_8M_MAIN__) \
47         || defined(__ARM_ARCH_8_1M_MAIN__)
48     if (is_return_secure_stack(lr)) {
49         /* PSP is used only if SPSEL is set, and we came from thread mode. */
50         return ((lr & EXC_RETURN_SPSEL) && is_return_thread_mode(lr));
51     } else {
52         /* PSP is used only if CONTROL_NS.SPSEL is set, and we came from thread mode. */
53         bool sp_sel = _FLD2VAL(CONTROL_SPSEL, __TZ_get_CONTROL_NS()) != 0;
54         return (sp_sel && is_return_thread_mode(lr));
55     }
56 #else
57     return (lr == EXC_RETURN_THREAD_PSP);
58 #endif
59 }
60 
61 /**
62  * \brief Get a pointer to the current exception frame
63  *
64  * \param[in] lr            LR register containing the EXC_RETURN value.
65  * \param[in] msp           The MSP at the start of the exception handler.
66  * \param[in] psp           The PSP at the start of the exception handler.
67  *
68  * \return  A pointer to the current exception frame.
69  */
70 __STATIC_INLINE
get_exception_frame(uint32_t lr,uint32_t msp,uint32_t psp)71 uint32_t *get_exception_frame(uint32_t lr, uint32_t msp, uint32_t psp)
72 {
73 #if defined(__ARM_ARCH_8M_BASE__) || defined(__ARM_ARCH_8M_MAIN__) \
74         || defined(__ARM_ARCH_8_1M_MAIN__)
75     bool is_psp = is_return_psp(lr);
76 
77     return (uint32_t *)(is_return_secure_stack(lr)
78                         ? (is_psp ? psp : msp)
79                         : (is_psp ? __TZ_get_PSP_NS() : __TZ_get_MSP_NS()));
80 #else
81     return (uint32_t *)(is_return_psp(lr) ? psp : msp);
82 #endif
83 }
84 
dump_exception_info(bool stack_error,const struct exception_info_t * ctx)85 static void dump_exception_info(bool stack_error,
86                                 const struct exception_info_t *ctx)
87 {
88     SPMLOG_DBGMSG("Here is some context for the exception:\r\n");
89     SPMLOG_DBGMSGVAL("    EXC_RETURN (LR): ", ctx->EXC_RETURN);
90     SPMLOG_DBGMSG("    Exception came from");
91 #ifdef TRUSTZONE_PRESENT
92     if (is_return_secure_stack(ctx->EXC_RETURN)) {
93         SPMLOG_DBGMSG(" secure FW in");
94     } else {
95         SPMLOG_DBGMSG(" non-secure FW in");
96     }
97 #endif
98 
99     if (is_return_thread_mode(ctx->EXC_RETURN)) {
100         SPMLOG_DBGMSG(" thread mode.\r\n");
101     } else {
102         SPMLOG_DBGMSG(" handler mode.\r\n");
103     }
104     SPMLOG_DBGMSGVAL("    xPSR:    ", ctx->xPSR);
105     SPMLOG_DBGMSGVAL("    MSP:     ", ctx->MSP);
106     SPMLOG_DBGMSGVAL("    PSP:     ", ctx->PSP);
107 #ifdef TRUSTZONE_PRESENT
108     SPMLOG_DBGMSGVAL("    MSP_NS:  ", __TZ_get_MSP_NS());
109     SPMLOG_DBGMSGVAL("    PSP_NS:  ", __TZ_get_PSP_NS());
110 #endif
111 
112     SPMLOG_DBGMSGVAL("    Exception frame at: ", (uint32_t)ctx->EXC_FRAME);
113     if (stack_error) {
114         SPMLOG_DBGMSG(
115             "       (Note that the exception frame may be corrupted for this type of error.)\r\n");
116     }
117     SPMLOG_DBGMSGVAL("        R0:   ", ctx->EXC_FRAME_COPY[0]);
118     SPMLOG_DBGMSGVAL("        R1:   ", ctx->EXC_FRAME_COPY[1]);
119     SPMLOG_DBGMSGVAL("        R2:   ", ctx->EXC_FRAME_COPY[2]);
120     SPMLOG_DBGMSGVAL("        R3:   ", ctx->EXC_FRAME_COPY[3]);
121     SPMLOG_DBGMSGVAL("        R12:  ", ctx->EXC_FRAME_COPY[4]);
122     SPMLOG_DBGMSGVAL("        LR:   ", ctx->EXC_FRAME_COPY[5]);
123     SPMLOG_DBGMSGVAL("        PC:   ", ctx->EXC_FRAME_COPY[6]);
124     SPMLOG_DBGMSGVAL("        xPSR: ", ctx->EXC_FRAME_COPY[7]);
125 
126     SPMLOG_DBGMSG("    Callee saved register state:");
127     SPMLOG_DBGMSGVAL("        R4:   ", ctx->CALLEE_SAVED_COPY[0]);
128     SPMLOG_DBGMSGVAL("        R5:   ", ctx->CALLEE_SAVED_COPY[1]);
129     SPMLOG_DBGMSGVAL("        R6:   ", ctx->CALLEE_SAVED_COPY[2]);
130     SPMLOG_DBGMSGVAL("        R7:   ", ctx->CALLEE_SAVED_COPY[3]);
131     SPMLOG_DBGMSGVAL("        R8:   ", ctx->CALLEE_SAVED_COPY[4]);
132     SPMLOG_DBGMSGVAL("        R9:   ", ctx->CALLEE_SAVED_COPY[5]);
133     SPMLOG_DBGMSGVAL("        R10:  ", ctx->CALLEE_SAVED_COPY[6]);
134     SPMLOG_DBGMSGVAL("        R11:  ", ctx->CALLEE_SAVED_COPY[7]);
135 
136 #ifdef FAULT_STATUS_PRESENT
137     SPMLOG_DBGMSGVAL("    CFSR:  ", ctx->CFSR);
138     SPMLOG_DBGMSGVAL("    BFSR:  ",
139                     (ctx->CFSR & SCB_CFSR_BUSFAULTSR_Msk) >> SCB_CFSR_BUSFAULTSR_Pos);
140     if (ctx->BFARVALID) {
141         SPMLOG_DBGMSGVAL("    BFAR: ", ctx->BFAR);
142     } else {
143         SPMLOG_DBGMSG("    BFAR:  Not Valid\r\n");
144     }
145     SPMLOG_DBGMSGVAL("    MMFSR: ",
146                     (ctx->CFSR & SCB_CFSR_MEMFAULTSR_Msk) >> SCB_CFSR_MEMFAULTSR_Pos);
147     if (ctx->MMARVALID) {
148         SPMLOG_DBGMSGVAL("    MMFAR: ", ctx->MMFAR);
149     } else {
150         SPMLOG_DBGMSG("    MMFAR: Not Valid\r\n");
151     }
152     SPMLOG_DBGMSGVAL("    UFSR:  ",
153                     (ctx->CFSR & SCB_CFSR_USGFAULTSR_Msk) >> SCB_CFSR_USGFAULTSR_Pos);
154     SPMLOG_DBGMSGVAL("    HFSR:  ", ctx->HFSR);
155 #ifdef TRUSTZONE_PRESENT
156     SPMLOG_DBGMSGVAL("    SFSR:  ", ctx->SFSR);
157     if (ctx->SFARVALID) {
158         SPMLOG_DBGMSGVAL("    SFAR: ", ctx->SFAR);
159     } else {
160         SPMLOG_DBGMSG("    SFAR: Not Valid\r\n");
161     }
162 #endif
163 
164 #endif
165 }
166 
dump_error(const struct exception_info_t * ctx)167 static void dump_error(const struct exception_info_t *ctx)
168 {
169     bool stack_error = false;
170 
171     SPMLOG_ERRMSG("FATAL ERROR: ");
172     switch (ctx->VECTACTIVE) {
173     case EXCEPTION_TYPE_HARDFAULT:
174         SPMLOG_ERRMSG("HardFault\r\n");
175         break;
176 #ifdef FAULT_STATUS_PRESENT
177     case EXCEPTION_TYPE_MEMMANAGEFAULT:
178         SPMLOG_ERRMSG("MemManage fault\r\n");
179         stack_error = true;
180         break;
181     case EXCEPTION_TYPE_BUSFAULT:
182         SPMLOG_ERRMSG("BusFault\r\n");
183         stack_error = true;
184         break;
185     case EXCEPTION_TYPE_USAGEFAULT:
186         SPMLOG_ERRMSG("UsageFault\r\n");
187         stack_error = true;
188         break;
189 #ifdef TRUSTZONE_PRESENT
190     case EXCEPTION_TYPE_SECUREFAULT:
191         SPMLOG_ERRMSG("SecureFault\r\n");
192         break;
193 #endif
194 #endif
195     /* Platform specific external interrupt secure handler. */
196     default:
197         if (ctx->VECTACTIVE < 16) {
198             SPMLOG_ERRMSGVAL("Reserved Exception ", ctx->VECTACTIVE);
199         } else {
200             SPMLOG_ERRMSGVAL("Platform external interrupt (IRQn): ", ctx->VECTACTIVE - 16);
201         }
202         /* Depends on the platform, assume it may cause stack error */
203         stack_error = true;
204         break;
205     }
206 
207     dump_exception_info(stack_error, ctx);
208 }
209 
tfm_exception_info_get_context(struct exception_info_t * ctx)210 void tfm_exception_info_get_context(struct exception_info_t *ctx)
211 {
212     memcpy(ctx, &exception_info, sizeof(exception_info));
213 }
214 
store_and_dump_context(uint32_t MSP_in,uint32_t PSP_in,uint32_t LR_in,uint32_t * callee_saved)215 void store_and_dump_context(uint32_t MSP_in, uint32_t PSP_in, uint32_t LR_in,
216                             uint32_t *callee_saved)
217 {
218     struct exception_info_t *ctx = &exception_info;
219 
220     ctx->VECTACTIVE = SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk;
221     ctx->xPSR = __get_xPSR();
222     ctx->EXC_RETURN = LR_in;
223     ctx->MSP = MSP_in;
224     ctx->PSP = PSP_in;
225     ctx->EXC_FRAME = get_exception_frame(ctx->EXC_RETURN, ctx->MSP, ctx->PSP);
226     memcpy(ctx->EXC_FRAME_COPY, ctx->EXC_FRAME, sizeof(ctx->EXC_FRAME_COPY));
227 
228     if (callee_saved) {
229         memcpy(ctx->CALLEE_SAVED_COPY, callee_saved, sizeof(ctx->CALLEE_SAVED_COPY));
230     }
231 
232 #ifdef FAULT_STATUS_PRESENT
233     ctx->CFSR = SCB->CFSR;
234     ctx->HFSR = SCB->HFSR;
235     ctx->BFAR = SCB->BFAR;
236     ctx->BFARVALID = ctx->CFSR & SCB_CFSR_BFARVALID_Msk;
237     ctx->MMFAR = SCB->MMFAR;
238     ctx->MMARVALID = ctx->CFSR & SCB_CFSR_MMARVALID_Msk;
239     SCB->CFSR = ctx->CFSR; /* Clear bits. CFSR is write-one-to-clear. */
240     SCB->HFSR = ctx->HFSR; /* Clear bits. HFSR is write-one-to-clear. */
241 #ifdef TRUSTZONE_PRESENT
242     ctx->SFSR = SAU->SFSR;
243     ctx->SFAR = SAU->SFAR;
244     ctx->SFARVALID = ctx->SFSR & SAU_SFSR_SFARVALID_Msk;
245     SAU->SFSR = ctx->SFSR; /* Clear bits. SFSR is write-one-to-clear. */
246 #endif
247 #endif
248 
249     dump_error(ctx);
250 }
251