pw_cpu_exception_cortex_m: Move armv7m->cortex_m

Moves the armv7m implementation of pw_cpu_exception to
pw_cpu_exception_cortex_m since much of the logic can be shared between
ARMv8-M and ARMv7-M. Symbol names are also updated to reflect current
naming style for extern "C" symbols. Old names will be removed in
pwrev/31060.

Warning, symbol renames are breaking!
pw_CpuExceptionEntry -> pw_cpu_exception_Entry
pw_CpuExceptionDefaultHandler -> pw_cpu_exception_DefaultHandler

Change-Id: I7ab99ac7637c436ba959d7546043090c66fe4215
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/30922
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Pigweed-Auto-Submit: Armando Montanez <amontanez@google.com>
diff --git a/pw_cpu_exception_cortex_m/entry.cc b/pw_cpu_exception_cortex_m/entry.cc
new file mode 100644
index 0000000..97925f5
--- /dev/null
+++ b/pw_cpu_exception_cortex_m/entry.cc
@@ -0,0 +1,287 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_cpu_exception/entry.h"
+
+#include <cstdint>
+#include <cstring>
+
+#include "pw_cpu_exception/handler.h"
+#include "pw_cpu_exception_cortex_m/cpu_state.h"
+#include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
+#include "pw_preprocessor/compiler.h"
+
+// TODO(pwbug/311): Deprecated naming.
+PW_EXTERN_C PW_NO_PROLOGUE __attribute__((alias("pw_cpu_exception_Entry"))) void
+pw_CpuExceptionEntry(void);
+
+namespace pw::cpu_exception {
+namespace {
+
+// If the CPU fails to capture some registers, the captured struct members will
+// be populated with this value. The only registers that this value should be
+// loaded into are pc, lr, and psr when the CPU fails to push an exception
+// context frame.
+//
+// 0xFFFFFFFF is an illegal lr value, which is why it was selected for this
+// purpose. pc and psr values of 0xFFFFFFFF are dubious too, so this constant
+// is clear enough at expressing that the registers weren't properly captured.
+constexpr uint32_t kInvalidRegisterValue = 0xFFFFFFFF;
+
+// Checks exc_return in the captured CPU state to determine which stack pointer
+// was in use prior to entering the exception handler.
+bool PspWasActive(const pw_cpu_exception_State& cpu_state) {
+  return cpu_state.extended.exc_return & kExcReturnStackMask;
+}
+
+// Checks exc_return to determine if FPU state was pushed to the stack in
+// addition to the base CPU context frame.
+bool FpuStateWasPushed(const pw_cpu_exception_State& cpu_state) {
+  return !(cpu_state.extended.exc_return & kExcReturnBasicFrameMask);
+}
+
+// If the CPU successfully pushed context on exception, copy it into cpu_state.
+//
+// For more information see (See ARMv7-M Section B1.5.11, derived exceptions
+// on exception entry).
+void CloneBaseRegistersFromPsp(pw_cpu_exception_State* cpu_state) {
+  // If CPU succeeded in pushing context to PSP, copy it to the MSP.
+  if (!(cpu_state->extended.cfsr & kCfsrStkerrMask) &&
+      !(cpu_state->extended.cfsr & kCfsrMstkerrMask)) {
+    // TODO(amontanez): {r0-r3,r12} are captured in pw_cpu_exception_Entry(),
+    //                  so this only really needs to copy pc, lr, and psr. Could
+    //                  (possibly) improve speed, but would add marginally more
+    //                  complexity.
+    std::memcpy(&cpu_state->base,
+                reinterpret_cast<void*>(cpu_state->extended.psp),
+                sizeof(CortexMExceptionRegisters));
+  } else {
+    // If CPU context wasn't pushed to stack on exception entry, we can't
+    // recover psr, lr, and pc from exception-time. Make these values clearly
+    // invalid.
+    cpu_state->base.lr = kInvalidRegisterValue;
+    cpu_state->base.pc = kInvalidRegisterValue;
+    cpu_state->base.psr = kInvalidRegisterValue;
+  }
+}
+
+// If the CPU successfully pushed context on exception, restore it from
+// cpu_state. Otherwise, don't attempt to restore state.
+//
+// For more information see (See ARMv7-M Section B1.5.11, derived exceptions
+// on exception entry).
+void RestoreBaseRegistersToPsp(pw_cpu_exception_State* cpu_state) {
+  // If CPU succeeded in pushing context to PSP on exception entry, restore the
+  // contents of cpu_state to the CPU-pushed register frame so the CPU can
+  // continue. Otherwise, don't attempt as we'll likely end up in an escalated
+  // hard fault.
+  if (!(cpu_state->extended.cfsr & kCfsrStkerrMask) &&
+      !(cpu_state->extended.cfsr & kCfsrMstkerrMask)) {
+    std::memcpy(reinterpret_cast<void*>(cpu_state->extended.psp),
+                &cpu_state->base,
+                sizeof(CortexMExceptionRegisters));
+  }
+}
+
+// Determines the size of the CPU-pushed context frame.
+uint32_t CpuContextSize(const pw_cpu_exception_State& cpu_state) {
+  uint32_t cpu_context_size = sizeof(CortexMExceptionRegisters);
+  if (FpuStateWasPushed(cpu_state)) {
+    cpu_context_size += sizeof(CortexMExceptionRegistersFpu);
+  }
+  if (cpu_state.base.psr & kPsrExtraStackAlignBit) {
+    // Account for the extra 4-bytes the processor
+    // added to keep the stack pointer 8-byte aligned
+    cpu_context_size += 4;
+  }
+
+  return cpu_context_size;
+}
+
+// On exception entry, the Program Stack Pointer is patched to reflect the state
+// at exception-time. On exception return, it is restored to the appropriate
+// location. This calculates the delta that is used for these patch operations.
+uint32_t CalculatePspDelta(const pw_cpu_exception_State& cpu_state) {
+  // If CPU context was not pushed to program stack (because program stack
+  // wasn't in use, or an error occurred when pushing context), the PSP doesn't
+  // need to be shifted.
+  if (!PspWasActive(cpu_state) || (cpu_state.extended.cfsr & kCfsrStkerrMask) ||
+      (cpu_state.extended.cfsr & kCfsrMstkerrMask)) {
+    return 0;
+  }
+
+  return CpuContextSize(cpu_state);
+}
+
+// On exception entry, the Main Stack Pointer is patched to reflect the state
+// at exception-time. On exception return, it is restored to the appropriate
+// location. This calculates the delta that is used for these patch operations.
+uint32_t CalculateMspDelta(const pw_cpu_exception_State& cpu_state) {
+  if (PspWasActive(cpu_state)) {
+    // TODO(amontanez): Since FPU state isn't captured at this time, we ignore
+    //                  it when patching MSP. To add FPU capture support,
+    //                  delete this if block as CpuContextSize() will include
+    //                  FPU context size in the calculation.
+    return sizeof(CortexMExceptionRegisters) + sizeof(CortexMExtraRegisters);
+  }
+
+  return CpuContextSize(cpu_state) + sizeof(CortexMExtraRegisters);
+}
+
+}  // namespace
+
+extern "C" {
+
+// Collect remaining CPU state (memory mapped registers), populate memory mapped
+// registers, and call application exception handler.
+PW_USED void pw_PackageAndHandleCpuException(
+    pw_cpu_exception_State* cpu_state) {
+  // Capture memory mapped registers.
+  cpu_state->extended.cfsr = cortex_m_cfsr;
+  cpu_state->extended.mmfar = cortex_m_mmfar;
+  cpu_state->extended.bfar = cortex_m_bfar;
+  cpu_state->extended.icsr = cortex_m_icsr;
+  cpu_state->extended.hfsr = cortex_m_hfsr;
+  cpu_state->extended.shcsr = cortex_m_shcsr;
+
+  // CPU may have automatically pushed state to the program stack. If it did,
+  // the values can be copied into in the pw_cpu_exception_State struct that is
+  // passed to HandleCpuException(). The cpu_state passed to the handler is
+  // ALWAYS stored on the main stack (MSP).
+  if (PspWasActive(*cpu_state)) {
+    CloneBaseRegistersFromPsp(cpu_state);
+    // If PSP wasn't active, this delta is 0.
+    cpu_state->extended.psp += CalculatePspDelta(*cpu_state);
+  }
+
+  // Patch captured stack pointers so they reflect the state at exception time.
+  cpu_state->extended.msp += CalculateMspDelta(*cpu_state);
+
+  // Call application-level exception handler.
+  pw_cpu_exception_HandleException(cpu_state);
+
+  // Restore program stack pointer so exception return can restore state if
+  // needed.
+  // Note: The default behavior of NOT subtracting a delta from MSP is
+  // intentional. This simplifies the assembly to pop the exception state
+  // off the main stack on exception return (since MSP currently reflects
+  // exception-time state).
+  cpu_state->extended.psp -= CalculatePspDelta(*cpu_state);
+
+  // If PSP was active and the CPU pushed a context frame, we must copy the
+  // potentially modified state from cpu_state back to the PSP so the CPU can
+  // resume execution with the modified values.
+  if (PspWasActive(*cpu_state)) {
+    // In this case, there's no need to touch the MSP as it's at the location
+    // before we entering the exception (effectively popping the state initially
+    // pushed to the main stack).
+    RestoreBaseRegistersToPsp(cpu_state);
+  } else {
+    // Since we're restoring context from MSP, we DO need to adjust MSP to point
+    // to CPU-pushed context frame so it can be properly restored.
+    // No need to adjust PSP since nothing was pushed to program stack.
+    cpu_state->extended.msp -= CpuContextSize(*cpu_state);
+  }
+}
+
+// Captures faulting CPU state on the main stack (MSP), then calls the exception
+// handlers.
+// This function should be called immediately after an exception.
+void pw_cpu_exception_Entry(void) {
+  asm volatile(
+      // clang-format off
+      // If PSP was in use at the time of exception, it's possible the CPU
+      // wasn't able to push CPU state. To be safe, this first captures scratch
+      // registers before moving forward.
+      //
+      // Stack flag is bit index 2 (0x4) of exc_return value stored in lr. When
+      // this bit is set, the Process Stack Pointer (PSP) was in use. Otherwise,
+      // the Main Stack Pointer (MSP) was in use. (See ARMv7-M Section B1.5.8
+      // for more details)
+      // The following block of assembly is equivalent to:
+      //   if (lr & (1 << 2)) {
+      //     msp -= sizeof(CortexMExceptionRegisters);
+      //     CortexMExceptionRegisters* state =
+      //         (CortexMExceptionRegisters*) msp;
+      //     state->r0 = r0;
+      //     state->r1 = r1;
+      //     state->r2 = r2;
+      //     state->r3 = r3;
+      //     state->r12 = r12;
+      //   }
+      //
+      " tst lr, #(1 << 2)                                     \n"
+      " itt ne                                                \n"
+      " subne sp, sp, %[base_state_size]                      \n"
+      " stmne sp, {r0-r3, r12}                                \n"
+
+      // Reserve stack space for additional registers. Since we're in exception
+      // handler mode, the main stack pointer is currently in use.
+      // r0 will temporarily store the end of captured_cpu_state to simplify
+      // assembly for copying additional registers.
+      " mrs r0, msp                                           \n"
+      " sub sp, sp, %[extra_state_size]                       \n"
+
+      // Store GPRs to stack.
+      " stmdb r0!, {r4-r11}                                   \n"
+
+      // Load special registers.
+      " mov r1, lr                                            \n"
+      " mrs r2, msp                                           \n"
+      " mrs r3, psp                                           \n"
+      " mrs r4, control                                       \n"
+
+      // Store special registers to stack.
+      " stmdb r0!, {r1-r4}                                    \n"
+
+      // Store a pointer to the beginning of special registers in r4 so they can
+      // be restored later.
+      " mov r4, r0                                            \n"
+
+      // Restore captured_cpu_state pointer to r0. This makes adding more
+      // memory mapped registers easier in the future since they're skipped in
+      // this assembly.
+      " mrs r0, msp                                           \n"
+
+      // Call intermediate handler that packages data.
+      " ldr r3, =pw_PackageAndHandleCpuException              \n"
+      " blx r3                                                \n"
+
+      // Restore state and exit exception handler.
+      // Pointer to saved CPU state was stored in r4.
+      " mov r0, r4                                            \n"
+
+      // Restore special registers.
+      " ldm r0!, {r1-r4}                                      \n"
+      " mov lr, r1                                            \n"
+      " msr control, r4                                       \n"
+
+      // Restore GPRs.
+      " ldm r0, {r4-r11}                                      \n"
+
+      // Restore stack pointers.
+      " msr msp, r2                                           \n"
+      " msr psp, r3                                           \n"
+
+      // Exit exception.
+      " bx lr                                                 \n"
+      : /*output=*/
+      : /*input=*/[base_state_size]"i"(sizeof(CortexMExceptionRegisters)),
+                  [extra_state_size]"i"(sizeof(CortexMExtraRegisters))
+      // clang-format on
+  );
+}
+
+}  // extern "C"
+}  // namespace pw::cpu_exception