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