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>
diff --git a/BUILD.gn b/BUILD.gn
index 31be12f..745b9c4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -185,7 +185,7 @@
import("$dir_pw_cpu_exception/backend.gni")
# TODO(pwbug/17): Re-think when Pigweed config system is added.
- if (pw_cpu_exception_BACKEND == dir_pw_cpu_exception_armv7m) {
+ if (pw_cpu_exception_ENTRY_BACKEND == dir_pw_cpu_exception_armv7m) {
group_deps += [ "$dir_pw_cpu_exception_armv7m:tests" ]
}
diff --git a/pw_cpu_exception/BUILD b/pw_cpu_exception/BUILD
index 24e3a2a..d58a55c 100644
--- a/pw_cpu_exception/BUILD
+++ b/pw_cpu_exception/BUILD
@@ -19,6 +19,10 @@
filegroup(
name = "pw_cpu_exception",
srcs = [
- "public/pw_cpu_exception/cpu_exception.h",
+ "basic_handler.cc",
+ "public/pw_cpu_exception/entry.h",
+ "public/pw_cpu_exception/handler.h",
+ "public/pw_cpu_exception/support.h",
+ "start_exception_handler.cc",
],
)
diff --git a/pw_cpu_exception/BUILD.gn b/pw_cpu_exception/BUILD.gn
index 137fd84..435781d 100644
--- a/pw_cpu_exception/BUILD.gn
+++ b/pw_cpu_exception/BUILD.gn
@@ -22,15 +22,73 @@
include_dirs = [ "public" ]
}
-pw_facade("pw_cpu_exception") {
- backend = pw_cpu_exception_BACKEND
+group("pw_cpu_exception") {
+ public_deps = [
+ ":entry",
+ ":handler",
+ ]
+}
+
+# This module has three facades, each of whose backends are set with a
+# different GN variable.
+#
+# - entry: This is the library that handles early exception entry and prepares
+# any CPU state that must be available to the exception handler via the
+# pw_CpuState object. The backend for this facade will be architecture-
+# specific.
+# Set this facade's backend via `pw_cpu_exception_ENTRY_BACKEND`
+#
+# - handler: This facade is backed by an application-specific handler that
+# determines what to do when an exception is encountered. This may be
+# capturing a crash report before resetting the device, or in some cases
+# handling the exception to allow execution to continue.
+# Set this facade's backend via `pw_cpu_exception_HANDLER_BACKEND`
+#
+# - support: This facade provides architecture-independent functions that may be
+# helpful for dumping CPU state in various forms. This allows an application
+# to create an application-specific handler that is portable across multiple
+# architectures.
+# Set this facade's backend via `pw_cpu_exception_SUPPORT_BACKEND`
+
+pw_facade("entry") {
+ backend = pw_cpu_exception_ENTRY_BACKEND
+ facade_name = "entry_facade"
+ public_configs = [ ":default_config" ]
+ public_deps = [ "$dir_pw_preprocessor" ]
+ deps = [ ":handler_facade" ]
+ public = [ "public/pw_cpu_exception/entry.h" ]
+}
+
+pw_facade("handler") {
+ backend = pw_cpu_exception_HANDLER_BACKEND
+ facade_name = "handler_facade"
public_configs = [ ":default_config" ]
public_deps = [
"$dir_pw_preprocessor",
"$dir_pw_span",
- "$dir_pw_string",
]
- public = [ "public/pw_cpu_exception/cpu_exception.h" ]
+ sources = [ "start_exception_handler.cc" ]
+ public = [ "public/pw_cpu_exception/handler.h" ]
+}
+
+# This library is technically optional. It is recommended to use `support` when
+# doing basic dumps of CPU state. As an alternative, projects may choose to
+# directly depend on the entry backend if they require direct access to
+# pw_CpuExceptionState members.
+pw_facade("support") {
+ backend = pw_cpu_exception_SUPPORT_BACKEND
+ facade_name = "support_facade"
+ public_configs = [ ":default_config" ]
+ public_deps = [ "$dir_pw_span" ]
+ public = [ "public/pw_cpu_exception/support.h" ]
+}
+
+pw_source_set("basic_handler") {
+ deps = [
+ ":handler_facade",
+ dir_pw_log,
+ ]
+ sources = [ "basic_handler.cc" ]
}
pw_doc_group("docs") {
diff --git a/pw_cpu_exception/backend.gni b/pw_cpu_exception/backend.gni
index adc62ae..07214d2 100644
--- a/pw_cpu_exception/backend.gni
+++ b/pw_cpu_exception/backend.gni
@@ -14,5 +14,7 @@
declare_args() {
# Backend for the pw_cpu_exception module.
- pw_cpu_exception_BACKEND = ""
+ pw_cpu_exception_ENTRY_BACKEND = ""
+ pw_cpu_exception_HANDLER_BACKEND = ""
+ pw_cpu_exception_SUPPORT_BACKEND = ""
}
diff --git a/pw_cpu_exception/basic_handler.cc b/pw_cpu_exception/basic_handler.cc
new file mode 100644
index 0000000..689fad3
--- /dev/null
+++ b/pw_cpu_exception/basic_handler.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 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 <cstdlib>
+
+#include "pw_cpu_exception/handler.h"
+#include "pw_log/log.h"
+
+namespace pw::cpu_exception {
+
+extern "C" void pw_CpuExceptionDefaultHandler(pw_CpuExceptionState*) {
+ PW_LOG_CRITICAL("Unhandled CPU exception encountered!");
+ // TODO(pwbug/95): Replace with pw_abort when that module exists.
+ std::abort();
+}
+
+} // namespace pw::cpu_exception
diff --git a/pw_cpu_exception/docs.rst b/pw_cpu_exception/docs.rst
index ec5a275..99ff6ac 100644
--- a/pw_cpu_exception/docs.rst
+++ b/pw_cpu_exception/docs.rst
@@ -22,8 +22,8 @@
see the backend documentation for your architecture.
Applications must also provide an implementation for
-``pw::cpu_exception::HandleCpuException()``. The behavior of this functions
-is entirely up to the application/project, but some examples are provided below:
+``pw_CpuExceptionDefaultHandler()``. The behavior of this functions is entirely
+up to the application/project, but some examples are provided below:
* Enter an infinite loop so the device can be debugged by JTAG.
* Reset the device.
@@ -35,42 +35,53 @@
Module Usage
============
Basic usage of this module entails applications supplying a definition for
-``pw::cpu_exception::HandleCpuException()``. ``HandleCpuException()`` should
-contain any logic to determine if a exception can be recovered from, as well
-as necessary actions to properly recover. If the device cannot recover from the
+``pw_CpuExceptionDefaultHandler()``. ``pw_CpuExceptionDefaultHandler()`` should
+contain any logic to determine if a exception can be recovered from, as well as
+necessary actions to properly recover. If the device cannot recover from the
exception, the function should **not** return.
+``pw_CpuExceptionDefaultHandler()`` is called indirectly, and may be overridden
+at runtime via ``pw_CpuExceptionSetHandler()``. The handler can also be reset to
+point to ``pw_CpuExceptionDefaultHandler()`` by calling
+``pw_CpuExceptionRestoreDefaultHandler()``.
+
When writing an exception handler, prefer to use the functions provided by this
-interface rather than relying on the backend implementation of ``CpuState``.
-This allows better code portability as it helps prevent an application fault
-handler from being tied to a single backend.
+interface rather than relying on the backend implementation of
+``pw_CpuExceptionState``. This allows better code portability as it helps
+prevent an application fault handler from being tied to a single backend.
For example; when logging or dumping CPU state, prefer ``ToString()`` or
-``RawFaultingCpuState()`` over directly accessing members of a ``CpuState``
-object.
+``RawFaultingCpuState()`` over directly accessing members of a
+``pw_CpuExceptionState`` object.
Some exception handling behavior may require architecture-specific CPU state to
attempt to correct a fault. In this situation, the application's exception
handler will be tied to the backend implementation of the CPU exception module.
-Dependencies
-============
- * ``pw_span``
- * ``pw_preprocessor``
-
Backend Expectations
====================
CPU exception backends do not provide an exception handler, but instead provide
mechanisms to capture CPU state for use by an application's exception handler,
and allow recovery from CPU exceptions when possible.
- * A backend should provide a definition for the ``CpuState`` struct that
- provides suitable means to access and modify any captured CPU state.
+ * A backend should provide a definition for the ``pw_CpuExceptionState``
+ struct that provides suitable means to access and modify any captured CPU
+ state.
* If an application's exception handler modifies the captured CPU state, the
state should be treated as though it were the original state of the CPU when
the exception occurred. The backend may need to manually restore some of the
modified state to ensure this on exception handler return.
* A backend should implement the ``pw_CpuExceptionEntry()`` function that will
- call ``HandleCpuException()`` after performing any necessary actions prior
- to handing control to the application's exception handler (e.g. capturing
- necessary CPU state).
+ call ``pw_HandleCpuException()`` after performing any necessary
+ actions prior to handing control to the application's exception handler
+ (e.g. capturing necessary CPU state).
+
+Compatibility
+=============
+Most of the pw_cpu_exception module is C-compatible. The exception to this is
+the "support" facade and library, which requires C++.
+
+Dependencies
+============
+ * ``pw_span``
+ * ``pw_preprocessor``
diff --git a/pw_cpu_exception/public/pw_cpu_exception/cpu_exception.h b/pw_cpu_exception/public/pw_cpu_exception/cpu_exception.h
deleted file mode 100644
index b9b76f2..0000000
--- a/pw_cpu_exception/public/pw_cpu_exception/cpu_exception.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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.
-#pragma once
-
-// Platform-independent mechanism to catch hardware CPU faults in user code.
-// This module encapsulates low level CPU exception handling assembly for the
-// platform. After early exception handling completes, this module invokes
-// the following user-defined function:
-//
-// pw::cpu_exception::HandleCpuException(CpuState* state)
-//
-// If platform-dependent access to the CPU registers is needed, then
-// applications can include the respective backend module directly; for example
-// cpu_exception_armv7m.
-//
-// IMPORTANT: To use this module, you MUST implement HandleCpuException() in
-// some part of your application.
-
-#include <cstdint>
-#include <span>
-
-#include "pw_preprocessor/compiler.h"
-#include "pw_string/string_builder.h"
-
-namespace pw::cpu_exception {
-
-// Forward declaration of CpuState. Definition provided by backend.
-struct CpuState;
-
-// Gets raw CPU state as a single contiguous block of data. The particular
-// contents will depend on the specific backend and platform.
-std::span<const uint8_t> RawFaultingCpuState(const CpuState& cpu_state);
-
-// Writes CPU state as a formatted string to a string builder.
-// NEVER depend on the format of this output. This is exclusively FYI human
-// readable output.
-void ToString(const CpuState& cpu_state, StringBuilder* builder);
-
-// Application-defined recoverable CPU exception handler.
-//
-// Applications must define this function; it is not defined by the exception
-// handler backend. After CPU state is captured by the cpu exception backend,
-// this function is called. Applications can then choose to either gracefully
-// handle the issue and return, or decide the exception cannot be handled and
-// abort normal execution (e.g. reset).
-//
-// Examples of what applications could do in the handler: gracefully recover
-// (e.g. enabling a floating point unit after triggering an exception executing
-// a floating point instruction), reset the device, or wait for a debugger to
-// attach.
-//
-// See the cpu_exception module documentation for more details.
-PW_USED void HandleCpuException(CpuState* state);
-
-// Low-level raw exception entry handler.
-//
-// Captures faulting CPU state into a platform-specific CpuState object, then
-// calls the user-supplied HandleCpuException() fault handler.
-//
-// This function should be called immediately after a fault; typically by being
-// in the interrupt vector table entries for the hard fault exceptions.
-//
-// Note: applications should almost never invoke this directly; if you do, make
-// sure you know what you are doing.
-extern "C" PW_NO_PROLOGUE void pw_CpuExceptionEntry(void);
-
-} // namespace pw::cpu_exception
diff --git a/pw_cpu_exception/public/pw_cpu_exception/entry.h b/pw_cpu_exception/public/pw_cpu_exception/entry.h
new file mode 100644
index 0000000..53b5a93
--- /dev/null
+++ b/pw_cpu_exception/public/pw_cpu_exception/entry.h
@@ -0,0 +1,43 @@
+// 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.
+#pragma once
+
+// Platform-independent mechanism to catch hardware CPU faults in user code.
+// This module encapsulates low level CPU exception handling assembly for the
+// platform. By default, this module invokes the following user-defined function
+// after early exception handling completes:
+//
+// pw_CpuExceptionDefaultHandler(pw_CpuExceptionState* state)
+//
+// If platform-dependent access to the CPU registers is needed, then
+// applications can include the respective backend module directly; for example
+// cpu_exception_armv7m.
+//
+// IMPORTANT: To use this module, you MUST implement
+// pw_CpuExceptionDefaultHandler() in some part of your application.
+
+#include "pw_preprocessor/compiler.h"
+#include "pw_preprocessor/util.h"
+
+// Low-level raw exception entry handler.
+//
+// Captures faulting CPU state into a platform-specific pw_CpuExceptionState
+// object, then calls the user-provided fault handler.
+//
+// This function should be called immediately after a fault; typically by being
+// in the interrupt vector table entries for the hard fault exceptions.
+//
+// Note: applications should almost never invoke this directly; if you do, make
+// sure you know what you are doing.
+PW_EXTERN_C PW_NO_PROLOGUE void pw_CpuExceptionEntry(void);
diff --git a/pw_cpu_exception/public/pw_cpu_exception/handler.h b/pw_cpu_exception/public/pw_cpu_exception/handler.h
new file mode 100644
index 0000000..83c9bf4
--- /dev/null
+++ b/pw_cpu_exception/public/pw_cpu_exception/handler.h
@@ -0,0 +1,55 @@
+// Copyright 2020 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.
+#pragma once
+
+#include "pw_preprocessor/compiler.h"
+#include "pw_preprocessor/util.h"
+
+PW_EXTERN_C_START
+
+// Forward declaration of pw_CpuExceptionState. Definition provided by cpu
+// exception entry backend.
+struct pw_CpuExceptionState;
+
+// By default, the exception entry function will terminate by handing execution
+// over to pw_CpuExceptionDefaultHandler(). This can be used to override the
+// current handler. This allows runtime insertion of an exception handler which
+// may also be helpful for loading a bootloader exception handler by default
+// that an application overrides.
+void pw_CpuExceptionSetHandler(void (*handler)(pw_CpuExceptionState*));
+
+// Set the exception handler to point to pw_CpuExceptionDefaultHandler().
+void pw_CpuExceptionRestoreDefaultHandler(void);
+
+// Application-defined recoverable CPU exception handler.
+//
+// Applications must define this function; it is not defined by the exception
+// entry backend. After CPU state is captured by the cpu exception entry
+// backend, this function is called. Applications can then choose to either
+// gracefully handle the exception and return, or decide the exception cannot be
+// handled and abort normal execution (e.g. reset).
+//
+// Examples of what applications could do in the handler: gracefully recover
+// (e.g. enabling a floating point unit after triggering an exception executing
+// a floating point instruction), reset the device, or wait for a debugger to
+// attach.
+//
+// See the cpu_exception module documentation for more details.
+PW_USED void pw_CpuExceptionDefaultHandler(pw_CpuExceptionState* state);
+
+// This is the underlying function the CPU exception entry backend should call.
+// This calls the currently set handler.
+void pw_HandleCpuException(void* cpu_state);
+
+PW_EXTERN_C_END
diff --git a/pw_cpu_exception/public/pw_cpu_exception/support.h b/pw_cpu_exception/public/pw_cpu_exception/support.h
new file mode 100644
index 0000000..5ce1cc5
--- /dev/null
+++ b/pw_cpu_exception/public/pw_cpu_exception/support.h
@@ -0,0 +1,41 @@
+// Copyright 2020 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.
+
+// This facade provides an API for capturing the contents of a
+// pw_CpuExceptionState struct in a platform-agnostic way. While this facade
+// does not provide a means to directly access individual members of a
+// pw_CpuExceptionState object, it does allow dumping CPU state without needing
+// to know any specifics about the underlying architecture.
+#pragma once
+
+#include <cstdint>
+#include <span>
+
+// Forward declaration of pw_CpuExceptionState. Definition provided by backend.
+struct pw_CpuExceptionState;
+
+namespace pw::cpu_exception {
+
+// Gets raw CPU state as a single contiguous block of data. The particular
+// contents will depend on the specific backend and platform.
+std::span<const uint8_t> RawFaultingCpuState(
+ const pw_CpuExceptionState& cpu_state);
+
+// Writes CPU state as a formatted string to a string builder.
+// NEVER depend on the format of this output. This is exclusively FYI human
+// readable output.
+void ToString(const pw_CpuExceptionState& cpu_state,
+ const std::span<char>& dest);
+
+} // namespace pw::cpu_exception
diff --git a/pw_cpu_exception/start_exception_handler.cc b/pw_cpu_exception/start_exception_handler.cc
new file mode 100644
index 0000000..dc566d9
--- /dev/null
+++ b/pw_cpu_exception/start_exception_handler.cc
@@ -0,0 +1,36 @@
+// Copyright 2020 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/handler.h"
+
+namespace pw::cpu_exception {
+
+static void (*exception_handler)(pw_CpuExceptionState*) =
+ &pw_CpuExceptionDefaultHandler;
+
+extern "C" void pw_CpuExceptionSetHandler(
+ void (*handler)(pw_CpuExceptionState*)) {
+ exception_handler = handler;
+}
+
+// Revert the exception handler to point to pw_CpuExceptionDefaultHandler().
+extern "C" void pw_CpuExceptionRestoreDefaultHandler() {
+ exception_handler = &pw_CpuExceptionDefaultHandler;
+}
+
+extern "C" void pw_HandleCpuException(void* cpu_state) {
+ exception_handler(reinterpret_cast<pw_CpuExceptionState*>(cpu_state));
+}
+
+} // namespace pw::cpu_exception
\ No newline at end of file
diff --git a/pw_cpu_exception_armv7m/BUILD b/pw_cpu_exception_armv7m/BUILD
index dee7414..8844338 100644
--- a/pw_cpu_exception_armv7m/BUILD
+++ b/pw_cpu_exception_armv7m/BUILD
@@ -19,7 +19,7 @@
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 0148248..c1206f5 100644
--- a/pw_cpu_exception_armv7m/BUILD.gn
+++ b/pw_cpu_exception_armv7m/BUILD.gn
@@ -22,17 +22,26 @@
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("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 d3245a4..466a216 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 cf96996..d55339f 100644
--- a/pw_cpu_exception_armv7m/docs.rst
+++ b/pw_cpu_exception_armv7m/docs.rst
@@ -78,11 +78,11 @@
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
similarity index 93%
rename from pw_cpu_exception_armv7m/cpu_exception_entry.cc
rename to pw_cpu_exception_armv7m/entry.cc
index 9f81789..a247dee 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 <cstdint>
+#include "pw_cpu_exception/entry.h"
-#include "pw_cpu_exception/cpu_exception.h"
+#include <cstdint>
+#include <cstring>
+
+#include "pw_cpu_exception/handler.h"
#include "pw_cpu_exception_armv7m/cpu_state.h"
#include "pw_preprocessor/compiler.h"
@@ -57,13 +60,13 @@
// 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 @@
//
// 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 @@
//
// 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 @@
}
// 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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 fbd5fb3..3000833 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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
// 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 @@
} else {
DisableFpu();
}
+ pw_CpuExceptionSetHandler(TestingExceptionHandler);
EnableAllFaultHandlers();
InstallVectorTableEntries();
exceptions_handled = 0;
@@ -546,9 +553,7 @@
#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 @@
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 @@
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 @@
}
}
+} // 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 8646d94..f38b949 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 @@
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
diff --git a/targets/stm32f429i-disc1/target_toolchains.gni b/targets/stm32f429i-disc1/target_toolchains.gni
index 350ac9e..70b1f6c 100644
--- a/targets/stm32f429i-disc1/target_toolchains.gni
+++ b/targets/stm32f429i-disc1/target_toolchains.gni
@@ -43,7 +43,9 @@
# Facade backends
pw_assert_BACKEND = dir_pw_assert_basic
pw_boot_BACKEND = dir_pw_boot_armv7m
- pw_cpu_exception_BACKEND = dir_pw_cpu_exception_armv7m
+ pw_cpu_exception_ENTRY_BACKEND = dir_pw_cpu_exception_armv7m
+ pw_cpu_exception_HANDLER_BACKEND = "$dir_pw_cpu_exception:basic_handler"
+ pw_cpu_exception_SUPPORT_BACKEND = "$dir_pw_cpu_exception_armv7m:support"
pw_log_BACKEND = dir_pw_log_basic
pw_sys_io_BACKEND = dir_pw_sys_io_baremetal_stm32f429