blob: ef0f25a7e1ed82ec9ea68a78ee2392ae39e27c8d [file] [log] [blame]
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +01001/*
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 "exception_info.h"
10#include "tfm_spm_log.h"
11#include "tfm_core_utils.h"
12
13struct 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
37static 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 */
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 */
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
89uint32_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
103static void dump_exception_info_t(bool stack_error,
104 struct exception_info_t *ctx)
105{
Joakim Andersson53831432021-11-03 15:44:28 +0100106 SPMLOG_DBGMSG("Here is some context for the exception:\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100107 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)) {
Joakim Andersson53831432021-11-03 15:44:28 +0100118 SPMLOG_DBGMSG(" thread mode.\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100119 } else {
Joakim Andersson53831432021-11-03 15:44:28 +0100120 SPMLOG_DBGMSG(" handler mode.\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100121 }
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(
Joakim Andersson53831432021-11-03 15:44:28 +0100133 " (Note that the exception frame may be corrupted for this type of error.)\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100134 }
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 {
Joakim Andersson53831432021-11-03 15:44:28 +0100151 SPMLOG_DBGMSG(" BFAR: Not Valid\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100152 }
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 {
Joakim Andersson53831432021-11-03 15:44:28 +0100158 SPMLOG_DBGMSG(" MMFAR: Not Valid\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100159 }
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 {
Joakim Andersson53831432021-11-03 15:44:28 +0100168 SPMLOG_DBGMSG(" SFAR: Not Valid\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100169 }
170#endif
171
172#endif
173}
174
175static void dump_error(uint32_t error_type)
176{
177 bool stack_error = false;
178
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100179 SPMLOG_ERRMSG("FATAL ERROR: ");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100180 switch (error_type) {
181 case EXCEPTION_TYPE_SECUREFAULT:
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100182 SPMLOG_ERRMSG("SecureFault\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100183 break;
184 case EXCEPTION_TYPE_HARDFAULT:
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100185 SPMLOG_ERRMSG("HardFault\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100186 break;
187 case EXCEPTION_TYPE_MEMFAULT:
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100188 SPMLOG_ERRMSG("MemManage fault\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100189 stack_error = true;
190 break;
191 case EXCEPTION_TYPE_BUSFAULT:
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100192 SPMLOG_ERRMSG("BusFault\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100193 stack_error = true;
194 break;
195 case EXCEPTION_TYPE_USAGEFAULT:
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100196 SPMLOG_ERRMSG("UsageFault\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100197 stack_error = true;
198 break;
199 default:
Joakim Andersson3d3c4452021-11-11 14:29:25 +0100200 SPMLOG_ERRMSG("Unknown\r\n");
Øyvind Rønningstadf2c8dad2021-01-15 15:33:33 +0100201 break;
202 }
203 dump_exception_info_t(stack_error, &exception_info);
204}
205
206void store_and_dump_context(uint32_t LR_in, uint32_t MSP_in, uint32_t PSP_in,
207 uint32_t exception_type)
208{
209 struct exception_info_t *ctx = &exception_info;
210
211 ctx->xPSR = __get_xPSR();
212 ctx->EXC_RETURN = LR_in;
213 ctx->MSP = MSP_in;
214 ctx->PSP = PSP_in;
215 ctx->EXC_FRAME = get_exception_frame(ctx->EXC_RETURN, ctx->MSP, ctx->PSP);
216 spm_memcpy(ctx->EXC_FRAME_COPY, ctx->EXC_FRAME,
217 sizeof(ctx->EXC_FRAME_COPY));
218
219#ifdef FAULT_STATUS_PRESENT
220 ctx->CFSR = SCB->CFSR;
221 ctx->HFSR = SCB->HFSR;
222 ctx->BFAR = SCB->BFAR;
223 ctx->BFARVALID = ctx->CFSR & SCB_CFSR_BFARVALID_Msk;
224 ctx->MMFAR = SCB->MMFAR;
225 ctx->MMARVALID = ctx->CFSR & SCB_CFSR_MMARVALID_Msk;
226 SCB->CFSR = ctx->CFSR; /* Clear bits. CFSR is write-one-to-clear. */
227 SCB->HFSR = ctx->HFSR; /* Clear bits. HFSR is write-one-to-clear. */
228#ifdef TRUSTZONE_PRESENT
229 ctx->SFSR = SAU->SFSR;
230 ctx->SFAR = SAU->SFAR;
231 ctx->SFARVALID = ctx->SFSR & SAU_SFSR_SFARVALID_Msk;
232 SAU->SFSR = ctx->SFSR; /* Clear bits. SFSR is write-one-to-clear. */
233#endif
234#endif
235
236 dump_error(exception_type);
237}