Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_cpu_exception_cortex_m / entry.cc
1 // Copyright 2019 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_cpu_exception/entry.h"
16
17 #include <cstdint>
18 #include <cstring>
19
20 #include "pw_cpu_exception/handler.h"
21 #include "pw_cpu_exception_cortex_m/cpu_state.h"
22 #include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
23 #include "pw_preprocessor/compiler.h"
24
25 // TODO(pwbug/311): Deprecated naming.
26 PW_EXTERN_C PW_NO_PROLOGUE __attribute__((alias("pw_cpu_exception_Entry"))) void
27 pw_CpuExceptionEntry(void);
28
29 namespace pw::cpu_exception {
30 namespace {
31
32 // If the CPU fails to capture some registers, the captured struct members will
33 // be populated with this value. The only registers that this value should be
34 // loaded into are pc, lr, and psr when the CPU fails to push an exception
35 // context frame.
36 //
37 // 0xFFFFFFFF is an illegal lr value, which is why it was selected for this
38 // purpose. pc and psr values of 0xFFFFFFFF are dubious too, so this constant
39 // is clear enough at expressing that the registers weren't properly captured.
40 constexpr uint32_t kInvalidRegisterValue = 0xFFFFFFFF;
41
42 // Checks exc_return in the captured CPU state to determine which stack pointer
43 // was in use prior to entering the exception handler.
44 bool PspWasActive(const pw_cpu_exception_State& cpu_state) {
45   return cpu_state.extended.exc_return & kExcReturnStackMask;
46 }
47
48 // Checks exc_return to determine if FPU state was pushed to the stack in
49 // addition to the base CPU context frame.
50 bool FpuStateWasPushed(const pw_cpu_exception_State& cpu_state) {
51   return !(cpu_state.extended.exc_return & kExcReturnBasicFrameMask);
52 }
53
54 // If the CPU successfully pushed context on exception, copy it into cpu_state.
55 //
56 // For more information see (See ARMv7-M Section B1.5.11, derived exceptions
57 // on exception entry).
58 void CloneBaseRegistersFromPsp(pw_cpu_exception_State* cpu_state) {
59   // If CPU succeeded in pushing context to PSP, copy it to the MSP.
60   if (!(cpu_state->extended.cfsr & kCfsrStkerrMask) &&
61       !(cpu_state->extended.cfsr & kCfsrMstkerrMask)) {
62     // TODO(amontanez): {r0-r3,r12} are captured in pw_cpu_exception_Entry(),
63     //                  so this only really needs to copy pc, lr, and psr. Could
64     //                  (possibly) improve speed, but would add marginally more
65     //                  complexity.
66     std::memcpy(&cpu_state->base,
67                 reinterpret_cast<void*>(cpu_state->extended.psp),
68                 sizeof(CortexMExceptionRegisters));
69   } else {
70     // If CPU context wasn't pushed to stack on exception entry, we can't
71     // recover psr, lr, and pc from exception-time. Make these values clearly
72     // invalid.
73     cpu_state->base.lr = kInvalidRegisterValue;
74     cpu_state->base.pc = kInvalidRegisterValue;
75     cpu_state->base.psr = kInvalidRegisterValue;
76   }
77 }
78
79 // If the CPU successfully pushed context on exception, restore it from
80 // cpu_state. Otherwise, don't attempt to restore state.
81 //
82 // For more information see (See ARMv7-M Section B1.5.11, derived exceptions
83 // on exception entry).
84 void RestoreBaseRegistersToPsp(pw_cpu_exception_State* cpu_state) {
85   // If CPU succeeded in pushing context to PSP on exception entry, restore the
86   // contents of cpu_state to the CPU-pushed register frame so the CPU can
87   // continue. Otherwise, don't attempt as we'll likely end up in an escalated
88   // hard fault.
89   if (!(cpu_state->extended.cfsr & kCfsrStkerrMask) &&
90       !(cpu_state->extended.cfsr & kCfsrMstkerrMask)) {
91     std::memcpy(reinterpret_cast<void*>(cpu_state->extended.psp),
92                 &cpu_state->base,
93                 sizeof(CortexMExceptionRegisters));
94   }
95 }
96
97 // Determines the size of the CPU-pushed context frame.
98 uint32_t CpuContextSize(const pw_cpu_exception_State& cpu_state) {
99   uint32_t cpu_context_size = sizeof(CortexMExceptionRegisters);
100   if (FpuStateWasPushed(cpu_state)) {
101     cpu_context_size += sizeof(CortexMExceptionRegistersFpu);
102   }
103   if (cpu_state.base.psr & kPsrExtraStackAlignBit) {
104     // Account for the extra 4-bytes the processor
105     // added to keep the stack pointer 8-byte aligned
106     cpu_context_size += 4;
107   }
108
109   return cpu_context_size;
110 }
111
112 // On exception entry, the Program Stack Pointer is patched to reflect the state
113 // at exception-time. On exception return, it is restored to the appropriate
114 // location. This calculates the delta that is used for these patch operations.
115 uint32_t CalculatePspDelta(const pw_cpu_exception_State& cpu_state) {
116   // If CPU context was not pushed to program stack (because program stack
117   // wasn't in use, or an error occurred when pushing context), the PSP doesn't
118   // need to be shifted.
119   if (!PspWasActive(cpu_state) || (cpu_state.extended.cfsr & kCfsrStkerrMask) ||
120       (cpu_state.extended.cfsr & kCfsrMstkerrMask)) {
121     return 0;
122   }
123
124   return CpuContextSize(cpu_state);
125 }
126
127 // On exception entry, the Main Stack Pointer is patched to reflect the state
128 // at exception-time. On exception return, it is restored to the appropriate
129 // location. This calculates the delta that is used for these patch operations.
130 uint32_t CalculateMspDelta(const pw_cpu_exception_State& cpu_state) {
131   if (PspWasActive(cpu_state)) {
132     // TODO(amontanez): Since FPU state isn't captured at this time, we ignore
133     //                  it when patching MSP. To add FPU capture support,
134     //                  delete this if block as CpuContextSize() will include
135     //                  FPU context size in the calculation.
136     return sizeof(CortexMExceptionRegisters) + sizeof(CortexMExtraRegisters);
137   }
138
139   return CpuContextSize(cpu_state) + sizeof(CortexMExtraRegisters);
140 }
141
142 }  // namespace
143
144 extern "C" {
145
146 // Collect remaining CPU state (memory mapped registers), populate memory mapped
147 // registers, and call application exception handler.
148 PW_USED void pw_PackageAndHandleCpuException(
149     pw_cpu_exception_State* cpu_state) {
150   // Capture memory mapped registers.
151   cpu_state->extended.cfsr = cortex_m_cfsr;
152   cpu_state->extended.mmfar = cortex_m_mmfar;
153   cpu_state->extended.bfar = cortex_m_bfar;
154   cpu_state->extended.icsr = cortex_m_icsr;
155   cpu_state->extended.hfsr = cortex_m_hfsr;
156   cpu_state->extended.shcsr = cortex_m_shcsr;
157
158   // CPU may have automatically pushed state to the program stack. If it did,
159   // the values can be copied into in the pw_cpu_exception_State struct that is
160   // passed to HandleCpuException(). The cpu_state passed to the handler is
161   // ALWAYS stored on the main stack (MSP).
162   if (PspWasActive(*cpu_state)) {
163     CloneBaseRegistersFromPsp(cpu_state);
164     // If PSP wasn't active, this delta is 0.
165     cpu_state->extended.psp += CalculatePspDelta(*cpu_state);
166   }
167
168   // Patch captured stack pointers so they reflect the state at exception time.
169   cpu_state->extended.msp += CalculateMspDelta(*cpu_state);
170
171   // Call application-level exception handler.
172   pw_cpu_exception_HandleException(cpu_state);
173
174   // Restore program stack pointer so exception return can restore state if
175   // needed.
176   // Note: The default behavior of NOT subtracting a delta from MSP is
177   // intentional. This simplifies the assembly to pop the exception state
178   // off the main stack on exception return (since MSP currently reflects
179   // exception-time state).
180   cpu_state->extended.psp -= CalculatePspDelta(*cpu_state);
181
182   // If PSP was active and the CPU pushed a context frame, we must copy the
183   // potentially modified state from cpu_state back to the PSP so the CPU can
184   // resume execution with the modified values.
185   if (PspWasActive(*cpu_state)) {
186     // In this case, there's no need to touch the MSP as it's at the location
187     // before we entering the exception (effectively popping the state initially
188     // pushed to the main stack).
189     RestoreBaseRegistersToPsp(cpu_state);
190   } else {
191     // Since we're restoring context from MSP, we DO need to adjust MSP to point
192     // to CPU-pushed context frame so it can be properly restored.
193     // No need to adjust PSP since nothing was pushed to program stack.
194     cpu_state->extended.msp -= CpuContextSize(*cpu_state);
195   }
196 }
197
198 // Captures faulting CPU state on the main stack (MSP), then calls the exception
199 // handlers.
200 // This function should be called immediately after an exception.
201 void pw_cpu_exception_Entry(void) {
202   asm volatile(
203       // clang-format off
204       // If PSP was in use at the time of exception, it's possible the CPU
205       // wasn't able to push CPU state. To be safe, this first captures scratch
206       // registers before moving forward.
207       //
208       // Stack flag is bit index 2 (0x4) of exc_return value stored in lr. When
209       // this bit is set, the Process Stack Pointer (PSP) was in use. Otherwise,
210       // the Main Stack Pointer (MSP) was in use. (See ARMv7-M Section B1.5.8
211       // for more details)
212       // The following block of assembly is equivalent to:
213       //   if (lr & (1 << 2)) {
214       //     msp -= sizeof(CortexMExceptionRegisters);
215       //     CortexMExceptionRegisters* state =
216       //         (CortexMExceptionRegisters*) msp;
217       //     state->r0 = r0;
218       //     state->r1 = r1;
219       //     state->r2 = r2;
220       //     state->r3 = r3;
221       //     state->r12 = r12;
222       //   }
223       //
224       " tst lr, #(1 << 2)                                     \n"
225       " itt ne                                                \n"
226       " subne sp, sp, %[base_state_size]                      \n"
227       " stmne sp, {r0-r3, r12}                                \n"
228
229       // Reserve stack space for additional registers. Since we're in exception
230       // handler mode, the main stack pointer is currently in use.
231       // r0 will temporarily store the end of captured_cpu_state to simplify
232       // assembly for copying additional registers.
233       " mrs r0, msp                                           \n"
234       " sub sp, sp, %[extra_state_size]                       \n"
235
236       // Store GPRs to stack.
237       " stmdb r0!, {r4-r11}                                   \n"
238
239       // Load special registers.
240       " mov r1, lr                                            \n"
241       " mrs r2, msp                                           \n"
242       " mrs r3, psp                                           \n"
243       " mrs r4, control                                       \n"
244
245       // Store special registers to stack.
246       " stmdb r0!, {r1-r4}                                    \n"
247
248       // Store a pointer to the beginning of special registers in r4 so they can
249       // be restored later.
250       " mov r4, r0                                            \n"
251
252       // Restore captured_cpu_state pointer to r0. This makes adding more
253       // memory mapped registers easier in the future since they're skipped in
254       // this assembly.
255       " mrs r0, msp                                           \n"
256
257       // Call intermediate handler that packages data.
258       " ldr r3, =pw_PackageAndHandleCpuException              \n"
259       " blx r3                                                \n"
260
261       // Restore state and exit exception handler.
262       // Pointer to saved CPU state was stored in r4.
263       " mov r0, r4                                            \n"
264
265       // Restore special registers.
266       " ldm r0!, {r1-r4}                                      \n"
267       " mov lr, r1                                            \n"
268       " msr control, r4                                       \n"
269
270       // Restore GPRs.
271       " ldm r0, {r4-r11}                                      \n"
272
273       // Restore stack pointers.
274       " msr msp, r2                                           \n"
275       " msr psp, r3                                           \n"
276
277       // Exit exception.
278       " bx lr                                                 \n"
279       : /*output=*/
280       : /*input=*/[base_state_size]"i"(sizeof(CortexMExceptionRegisters)),
281                   [extra_state_size]"i"(sizeof(CortexMExtraRegisters))
282       // clang-format on
283   );
284 }
285
286 }  // extern "C"
287 }  // namespace pw::cpu_exception