diff options
Diffstat (limited to 'pw_cpu_exception_armv7m')
-rw-r--r-- | pw_cpu_exception_armv7m/BUILD | 2 | ||||
-rw-r--r-- | pw_cpu_exception_armv7m/BUILD.gn | 26 | ||||
-rw-r--r-- | pw_cpu_exception_armv7m/cpu_state.cc | 11 | ||||
-rw-r--r-- | pw_cpu_exception_armv7m/docs.rst | 10 | ||||
-rw-r--r-- | pw_cpu_exception_armv7m/entry.cc (renamed from pw_cpu_exception_armv7m/cpu_exception_entry.cc) | 29 | ||||
-rw-r--r-- | pw_cpu_exception_armv7m/exception_entry_test.cc | 50 | ||||
-rw-r--r-- | pw_cpu_exception_armv7m/public/pw_cpu_exception_armv7m/cpu_state.h | 10 |
7 files changed, 80 insertions, 58 deletions
diff --git a/pw_cpu_exception_armv7m/BUILD b/pw_cpu_exception_armv7m/BUILD index dee7414d4..884433824 100644 --- a/pw_cpu_exception_armv7m/BUILD +++ b/pw_cpu_exception_armv7m/BUILD @@ -19,7 +19,7 @@ licenses(["notice"]) # Apache License 2.0 filegroup( name = "pw_cpu_exception_armv7m", srcs = [ - "cpu_exception_entry.cc", + "entry.cc", "cpu_state.cc", "public/pw_cpu_exception_armv7m/cpu_state.h", ], diff --git a/pw_cpu_exception_armv7m/BUILD.gn b/pw_cpu_exception_armv7m/BUILD.gn index 014824807..c1206f522 100644 --- a/pw_cpu_exception_armv7m/BUILD.gn +++ b/pw_cpu_exception_armv7m/BUILD.gn @@ -22,17 +22,26 @@ config("default_config") { include_dirs = [ "public" ] } -pw_source_set("pw_cpu_exception_armv7m") { +pw_source_set("support") { public_configs = [ ":default_config" ] - deps = [ - "$dir_pw_cpu_exception:facade", + public_deps = [ + "$dir_pw_cpu_exception:support_facade", "$dir_pw_preprocessor", + "$dir_pw_string", ] public = [ "public/pw_cpu_exception_armv7m/cpu_state.h" ] - sources = [ - "cpu_exception_entry.cc", - "cpu_state.cc", + sources = [ "cpu_state.cc" ] +} + +pw_source_set("pw_cpu_exception_armv7m") { + public_configs = [ ":default_config" ] + public_deps = [ + ":support", + "$dir_pw_cpu_exception:entry_facade", + "$dir_pw_cpu_exception:handler", + "$dir_pw_preprocessor", ] + sources = [ "entry.cc" ] } pw_test_group("tests") { @@ -40,10 +49,7 @@ pw_test_group("tests") { } pw_test("cpu_exception_entry_test") { - deps = [ - ":pw_cpu_exception_armv7m", - "$dir_pw_cpu_exception", - ] + deps = [ ":pw_cpu_exception_armv7m" ] sources = [ "exception_entry_test.cc" ] } diff --git a/pw_cpu_exception_armv7m/cpu_state.cc b/pw_cpu_exception_armv7m/cpu_state.cc index d3245a4bc..466a216b5 100644 --- a/pw_cpu_exception_armv7m/cpu_state.cc +++ b/pw_cpu_exception_armv7m/cpu_state.cc @@ -18,23 +18,26 @@ #include <cstdint> #include <span> -#include "pw_cpu_exception/cpu_exception.h" +#include "pw_cpu_exception/support.h" #include "pw_string/string_builder.h" namespace pw::cpu_exception { -std::span<const uint8_t> RawFaultingCpuState(const CpuState& cpu_state) { +std::span<const uint8_t> RawFaultingCpuState( + const pw_CpuExceptionState& cpu_state) { return std::span(reinterpret_cast<const uint8_t*>(&cpu_state), sizeof(cpu_state)); } // Using this function adds approximately 100 bytes to binary size. -void ToString(const CpuState& cpu_state, StringBuilder* builder) { +void ToString(const pw_CpuExceptionState& cpu_state, + const std::span<char>& dest) { + StringBuilder builder(dest); const ArmV7mFaultRegisters& base = cpu_state.base; const ArmV7mExtraRegisters& extended = cpu_state.extended; #define _PW_FORMAT_REGISTER(state_section, name) \ - builder->Format("%s=0x%08" PRIx32 "\n", #name, state_section.name) + builder.Format("%s=0x%08" PRIx32 "\n", #name, state_section.name) // Other registers. _PW_FORMAT_REGISTER(base, pc); diff --git a/pw_cpu_exception_armv7m/docs.rst b/pw_cpu_exception_armv7m/docs.rst index cf969961d..d55339f7d 100644 --- a/pw_cpu_exception_armv7m/docs.rst +++ b/pw_cpu_exception_armv7m/docs.rst @@ -78,11 +78,11 @@ architecture-specific registers, using the generic exception handler functions is preferred. However, some projects may need to explicitly access architecture-specific -registers to attempt to recover from a CPU exception. ``CpuState`` provides -access to the captured CPU state at the time of the fault. When the -application-provided ``HandleCpuException()`` function returns, the CPU state is -restored. This allows the exception handler to modify the captured state so that -execution can safely continue. +registers to attempt to recover from a CPU exception. ``pw_CpuExceptionState`` +provides access to the captured CPU state at the time of the fault. When the +application-provided ``pw_CpuExceptionDefaultHandler()`` function returns, the +CPU state is restored. This allows the exception handler to modify the captured +state so that execution can safely continue. Expected Behavior ----------------- diff --git a/pw_cpu_exception_armv7m/cpu_exception_entry.cc b/pw_cpu_exception_armv7m/entry.cc index 9f81789b4..a247dee59 100644 --- a/pw_cpu_exception_armv7m/cpu_exception_entry.cc +++ b/pw_cpu_exception_armv7m/entry.cc @@ -12,9 +12,12 @@ // 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/cpu_exception.h" +#include "pw_cpu_exception/handler.h" #include "pw_cpu_exception_armv7m/cpu_state.h" #include "pw_preprocessor/compiler.h" @@ -57,13 +60,13 @@ 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 CpuState& cpu_state) { +bool PspWasActive(const pw_CpuExceptionState& 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 CpuState& cpu_state) { +bool FpuStateWasPushed(const pw_CpuExceptionState& cpu_state) { return !(cpu_state.extended.exc_return & kExcReturnBasicFrameMask); } @@ -71,7 +74,7 @@ bool FpuStateWasPushed(const CpuState& cpu_state) { // // For more information see (See ARMv7-M Section B1.5.11, derived exceptions // on exception entry). -void CloneBaseRegistersFromPsp(CpuState* cpu_state) { +void CloneBaseRegistersFromPsp(pw_CpuExceptionState* cpu_state) { // If CPU succeeded in pushing context to PSP, copy it to the MSP. if (!(cpu_state->extended.cfsr & kStkErrMask) && !(cpu_state->extended.cfsr & kMStkErrMask)) { @@ -97,7 +100,7 @@ void CloneBaseRegistersFromPsp(CpuState* cpu_state) { // // For more information see (See ARMv7-M Section B1.5.11, derived exceptions // on exception entry). -void RestoreBaseRegistersToPsp(CpuState* cpu_state) { +void RestoreBaseRegistersToPsp(pw_CpuExceptionState* 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 @@ -111,7 +114,7 @@ void RestoreBaseRegistersToPsp(CpuState* cpu_state) { } // Determines the size of the CPU-pushed context frame. -uint32_t CpuContextSize(const CpuState& cpu_state) { +uint32_t CpuContextSize(const pw_CpuExceptionState& cpu_state) { uint32_t cpu_context_size = sizeof(ArmV7mFaultRegisters); if (FpuStateWasPushed(cpu_state)) { cpu_context_size += sizeof(ArmV7mFaultRegistersFpu); @@ -128,7 +131,7 @@ uint32_t CpuContextSize(const CpuState& cpu_state) { // 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 CpuState& cpu_state) { +uint32_t CalculatePspDelta(const pw_CpuExceptionState& 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. @@ -143,7 +146,7 @@ uint32_t CalculatePspDelta(const CpuState& 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 CpuState& cpu_state) { +uint32_t CalculateMspDelta(const pw_CpuExceptionState& 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, @@ -161,7 +164,7 @@ extern "C" { // Collect remaining CPU state (memory mapped registers), populate memory mapped // registers, and call application exception handler. -PW_USED void pw_PackageAndHandleCpuException(CpuState* cpu_state) { +PW_USED void pw_PackageAndHandleCpuException(pw_CpuExceptionState* cpu_state) { // Capture memory mapped registers. cpu_state->extended.cfsr = arm_v7m_cfsr; cpu_state->extended.icsr = arm_v7m_icsr; @@ -169,9 +172,9 @@ PW_USED void pw_PackageAndHandleCpuException(CpuState* cpu_state) { cpu_state->extended.mmfar = arm_v7m_mmfar; // CPU may have automatically pushed state to the program stack. If it did, - // the values can be copied into in the CpuState struct that is passed - // to HandleCpuException(). The cpu_state passed to the handler is ALWAYS - // stored on the main stack (MSP). + // the values can be copied into in the pw_CpuExceptionState 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. @@ -182,7 +185,7 @@ PW_USED void pw_PackageAndHandleCpuException(CpuState* cpu_state) { cpu_state->extended.msp += CalculateMspDelta(*cpu_state); // Call application-level exception handler. - HandleCpuException(cpu_state); + pw_HandleCpuException(cpu_state); // Restore program stack pointer so exception return can restore state if // needed. diff --git a/pw_cpu_exception_armv7m/exception_entry_test.cc b/pw_cpu_exception_armv7m/exception_entry_test.cc index fbd5fb36d..3000833cd 100644 --- a/pw_cpu_exception_armv7m/exception_entry_test.cc +++ b/pw_cpu_exception_armv7m/exception_entry_test.cc @@ -17,7 +17,9 @@ #include <type_traits> #include "gtest/gtest.h" -#include "pw_cpu_exception/cpu_exception.h" +#include "pw_cpu_exception/entry.h" +#include "pw_cpu_exception/handler.h" +#include "pw_cpu_exception/support.h" #include "pw_cpu_exception_armv7m/cpu_state.h" namespace pw::cpu_exception { @@ -132,10 +134,10 @@ constexpr size_t kMaxFaultDepth = 2; // Variable to prevent more than kMaxFaultDepth nested crashes. size_t current_fault_depth = 0; -// Faulting CpuState is copied here so values can be validated after exiting -// exception handler. -CpuState captured_states[kMaxFaultDepth] = {}; -CpuState& captured_state = captured_states[0]; +// Faulting pw_CpuExceptionState is copied here so values can be validated after +// exiting exception handler. +pw_CpuExceptionState captured_states[kMaxFaultDepth] = {}; +pw_CpuExceptionState& captured_state = captured_states[0]; // Flag used to check if the contents of std::span matches the captured state. bool span_matches = false; @@ -147,10 +149,10 @@ bool span_matches = false; // point support for double. volatile float float_test_value; -// Magic pattern to help identify if the exception handler's CpuState pointer -// was pointing to captured CPU state that was pushed onto the stack when -// the faulting context uses the VFP. Has to be computed at runtime -// because it uses values only available at link time. +// Magic pattern to help identify if the exception handler's +// pw_CpuExceptionState pointer was pointing to captured CPU state that was +// pushed onto the stack when the faulting context uses the VFP. Has to be +// computed at runtime because it uses values only available at link time. const float kFloatTestPattern = 12.345f * 67.89f; volatile float fpu_lhs_val = 12.345f; @@ -159,13 +161,14 @@ volatile float fpu_rhs_val = 67.89f; // This macro provides a calculation that equals kFloatTestPattern. #define _PW_TEST_FPU_OPERATION (fpu_lhs_val * fpu_rhs_val) -// Magic pattern to help identify if the exception handler's CpuState pointer -// was pointing to captured CPU state that was pushed onto the stack. +// Magic pattern to help identify if the exception handler's +// pw_CpuExceptionState pointer was pointing to captured CPU state that was +// pushed onto the stack. constexpr uint32_t kMagicPattern = 0xDEADBEEF; // This pattern serves a purpose similar to kMagicPattern, but is used for -// testing a nested fault to ensure both CpuState objects are correctly -// captured. +// testing a nested fault to ensure both pw_CpuExceptionState objects are +// correctly captured. constexpr uint32_t kNestedMagicPattern = 0x900DF00D; // The manually captured PC won't be the exact same as the faulting PC. This is @@ -176,6 +179,9 @@ constexpr int32_t kMaxPcDistance = 4; using InterruptVectorTable = std::aligned_storage_t<512, 512>; InterruptVectorTable ram_vector_table; +// Forward declaration of the exception handler. +void TestingExceptionHandler(pw_CpuExceptionState*); + // Populate the device's registers with testable values, then trigger exception. void BeginBaseFaultTest() { // Make sure divide by zero causes a fault. @@ -337,7 +343,7 @@ void InstallVectorTableEntries() { // Override exception handling vector table entries. uint32_t* exception_entry_addr = - reinterpret_cast<uint32_t*>(pw::cpu_exception::pw_CpuExceptionEntry); + reinterpret_cast<uint32_t*>(pw_CpuExceptionEntry); uint32_t** interrupts = reinterpret_cast<uint32_t**>(&ram_vector_table); interrupts[kHardFaultIsrNum] = exception_entry_addr; interrupts[kMemFaultIsrNum] = exception_entry_addr; @@ -364,6 +370,7 @@ void Setup(bool use_fpu) { } else { DisableFpu(); } + pw_CpuExceptionSetHandler(TestingExceptionHandler); EnableAllFaultHandlers(); InstallVectorTableEntries(); exceptions_handled = 0; @@ -546,9 +553,7 @@ TEST(FaultEntry, FloatUnalignedStackFault) { #endif // defined(PW_ARMV7M_ENABLE_FPU) && PW_ARMV7M_ENABLE_FPU == 1 -} // namespace - -void HandleCpuException(CpuState* state) { +void TestingExceptionHandler(pw_CpuExceptionState* state) { if (++current_fault_depth > kMaxFaultDepth) { volatile bool loop = true; while (loop) { @@ -571,7 +576,9 @@ void HandleCpuException(CpuState* state) { if (arm_v7m_cfsr & kUnalignedFaultMask) { // Copy captured state to check later. - std::memcpy(&captured_states[exceptions_handled], state, sizeof(CpuState)); + std::memcpy(&captured_states[exceptions_handled], + state, + sizeof(pw_CpuExceptionState)); // Disable unaligned read/write trapping to "handle" exception. arm_v7m_ccr &= ~kUnalignedTrapEnableMask; @@ -580,11 +587,13 @@ void HandleCpuException(CpuState* state) { return; } else if (arm_v7m_cfsr & kDivByZeroFaultMask) { // Copy captured state to check later. - std::memcpy(&captured_states[exceptions_handled], state, sizeof(CpuState)); + std::memcpy(&captured_states[exceptions_handled], + state, + sizeof(pw_CpuExceptionState)); // Ensure std::span compares to be the same. std::span<const uint8_t> state_span = RawFaultingCpuState(*state); - EXPECT_EQ(state_span.size(), sizeof(CpuState)); + EXPECT_EQ(state_span.size(), sizeof(pw_CpuExceptionState)); if (std::memcmp(state, state_span.data(), state_span.size()) == 0) { span_matches = true; } else { @@ -603,4 +612,5 @@ void HandleCpuException(CpuState* state) { } } +} // namespace } // namespace pw::cpu_exception diff --git a/pw_cpu_exception_armv7m/public/pw_cpu_exception_armv7m/cpu_state.h b/pw_cpu_exception_armv7m/public/pw_cpu_exception_armv7m/cpu_state.h index 8646d9465..f38b949ba 100644 --- a/pw_cpu_exception_armv7m/public/pw_cpu_exception_armv7m/cpu_state.h +++ b/pw_cpu_exception_armv7m/public/pw_cpu_exception_armv7m/cpu_state.h @@ -84,12 +84,12 @@ PW_PACKED(struct) ArmV7mExtraRegisters { uint32_t r11; }; -PW_PACKED(struct) CpuState { - ArmV7mExtraRegisters extended; - ArmV7mFaultRegisters base; +} // namespace pw::cpu_exception + +PW_PACKED(struct) pw_CpuExceptionState { + pw::cpu_exception::ArmV7mExtraRegisters extended; + pw::cpu_exception::ArmV7mFaultRegisters base; // TODO(amontanez): FPU registers may or may not be here as well. Make the // availability of the FPU registers a compile-time configuration when FPU // register support is added. }; - -} // namespace pw::cpu_exception |