aboutsummaryrefslogtreecommitdiff
path: root/pw_cpu_exception_armv7m
diff options
context:
space:
mode:
authorArmando Montanez <amontanez@google.com>2020-06-04 10:35:55 -0700
committerCQ Bot Account <commit-bot@chromium.org>2020-07-16 21:03:29 +0000
commit356bf9786defcc303c27952c68b90ae007c0d746 (patch)
tree2af401be538d432c8c582b968db2f931b3d0f242 /pw_cpu_exception_armv7m
parentce87bc01d82d813506ece5122250a1b909d78e68 (diff)
downloadpigweed-356bf9786defcc303c27952c68b90ae007c0d746.tar.gz
pw_cpu_exception: Split facade
The pw_cpu_exception facade is comprised of three parts: the part implemented by the architecture, and the part implemented by the application, and some supporting libraries that make it easier to dump CPU state. Since these are provided independently, the facade has been split into three facades: 1. entry 2. handler 3. support Change-Id: I48c92f42b208f79596740f1bec59ed3d29e277a8 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/11646 Commit-Queue: Armando Montanez <amontanez@google.com> Reviewed-by: Wyatt Hepler <hepler@google.com>
Diffstat (limited to 'pw_cpu_exception_armv7m')
-rw-r--r--pw_cpu_exception_armv7m/BUILD2
-rw-r--r--pw_cpu_exception_armv7m/BUILD.gn26
-rw-r--r--pw_cpu_exception_armv7m/cpu_state.cc11
-rw-r--r--pw_cpu_exception_armv7m/docs.rst10
-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.cc50
-rw-r--r--pw_cpu_exception_armv7m/public/pw_cpu_exception_armv7m/cpu_state.h10
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