1 // Copyright 2021 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 #define PW_LOG_LEVEL PW_CPU_EXCEPTION_CORTEX_M_LOG_LEVEL
16
17 #include "pw_cpu_exception_cortex_m/snapshot.h"
18
19 #include "pw_cpu_exception_cortex_m/proto_dump.h"
20 #include "pw_cpu_exception_cortex_m/util.h"
21 #include "pw_cpu_exception_cortex_m_private/config.h"
22 #include "pw_cpu_exception_cortex_m_private/cortex_m_constants.h"
23 #include "pw_cpu_exception_cortex_m_protos/cpu_state.pwpb.h"
24 #include "pw_log/log.h"
25 #include "pw_protobuf/encoder.h"
26 #include "pw_status/status.h"
27 #include "pw_thread/snapshot.h"
28 #include "pw_thread_protos/thread.pwpb.h"
29
30 namespace pw::cpu_exception::cortex_m {
31 namespace {
32
33 constexpr char kMainStackHandlerModeName[] = "Main Stack (Handler Mode)";
34 constexpr char kMainStackThreadModeName[] = "Main Stack (Thread Mode)";
35
CaptureMainStack(ProcessorMode mode,uintptr_t stack_low_addr,uintptr_t stack_high_addr,uintptr_t stack_pointer,thread::proto::SnapshotThreadInfo::StreamEncoder & snapshot_encoder,thread::ProcessThreadStackCallback & thread_stack_callback)36 Status CaptureMainStack(
37 ProcessorMode mode,
38 uintptr_t stack_low_addr,
39 uintptr_t stack_high_addr,
40 uintptr_t stack_pointer,
41 thread::proto::SnapshotThreadInfo::StreamEncoder& snapshot_encoder,
42 thread::ProcessThreadStackCallback& thread_stack_callback) {
43 thread::proto::Thread::StreamEncoder encoder =
44 snapshot_encoder.GetThreadsEncoder();
45
46 const char* thread_name;
47 thread::proto::ThreadState::Enum thread_state;
48 if (mode == ProcessorMode::kHandlerMode) {
49 thread_name = kMainStackHandlerModeName;
50 PW_LOG_DEBUG("Capturing thread info for Main Stack (Handler Mode)");
51 thread_state = thread::proto::ThreadState::Enum::INTERRUPT_HANDLER;
52 PW_LOG_DEBUG("Thread state: INTERRUPT_HANDLER");
53 } else { // mode == ProcessorMode::kThreadMode
54 thread_name = kMainStackThreadModeName;
55 PW_LOG_DEBUG("Capturing thread info for Main Stack (Thread Mode)");
56 thread_state = thread::proto::ThreadState::Enum::RUNNING;
57 PW_LOG_DEBUG("Thread state: RUNNING");
58 }
59 encoder.WriteState(thread_state);
60 encoder.WriteName(as_bytes(span(std::string_view(thread_name))));
61
62 const thread::StackContext thread_ctx = {
63 .thread_name = thread_name,
64 .stack_low_addr = stack_low_addr,
65 .stack_high_addr = stack_high_addr,
66 .stack_pointer = stack_pointer,
67 .stack_pointer_est_peak = std::nullopt,
68 };
69 return thread::SnapshotStack(thread_ctx, encoder, thread_stack_callback);
70 }
71
72 } // namespace
73
SnapshotCpuState(const pw_cpu_exception_State & cpu_state,SnapshotCpuStateOverlay::StreamEncoder & snapshot_encoder)74 Status SnapshotCpuState(
75 const pw_cpu_exception_State& cpu_state,
76 SnapshotCpuStateOverlay::StreamEncoder& snapshot_encoder) {
77 {
78 ArmV7mCpuState::StreamEncoder cpu_state_encoder =
79 snapshot_encoder.GetArmv7mCpuStateEncoder();
80 DumpCpuStateProto(cpu_state_encoder, cpu_state);
81 }
82 return snapshot_encoder.status();
83 }
84
SnapshotMainStackThread(uintptr_t stack_low_addr,uintptr_t stack_high_addr,thread::proto::SnapshotThreadInfo::StreamEncoder & encoder,thread::ProcessThreadStackCallback & thread_stack_callback)85 Status SnapshotMainStackThread(
86 uintptr_t stack_low_addr,
87 uintptr_t stack_high_addr,
88 thread::proto::SnapshotThreadInfo::StreamEncoder& encoder,
89 thread::ProcessThreadStackCallback& thread_stack_callback) {
90 uintptr_t stack_pointer;
91 asm volatile("mrs %0, msp\n" : "=r"(stack_pointer));
92
93 // First check if we're in Handler mode, AKA handling exceptions/interrupts.
94 //
95 // Handler mode vs thread mode can be determined via IPSR, bits 8:0 of xPSR.
96 // In thread mode the value is 0, in handler mode the value is non-zero.
97 uint32_t xpsr;
98 asm volatile("mrs %0, xpsr\n" : "=r"(xpsr));
99 if ((xpsr & kXpsrIpsrMask) != 0) {
100 return CaptureMainStack(ProcessorMode::kHandlerMode,
101 stack_low_addr,
102 stack_high_addr,
103 stack_pointer,
104 encoder,
105 thread_stack_callback);
106 }
107
108 // It looks like we're in Thread mode which means we need to check whether
109 // or not we are executing off the main stack currently.
110 //
111 // See ARMv7-M Architecture Reference Manual Section B1.4.4 for the control
112 // register values, in particular the SPSEL bit while in Thread mode which
113 // is 0 while running off the main stack and 1 while running off the proces
114 // stack.
115 uint32_t control;
116 asm volatile("mrs %0, control\n" : "=r"(control));
117 if ((control & kControlThreadModeStackMask) != 0) {
118 return OkStatus(); // Main stack is not currently active.
119 }
120
121 // We're running off the main stack in Thread mode.
122 return CaptureMainStack(ProcessorMode::kThreadMode,
123 stack_low_addr,
124 stack_high_addr,
125 stack_pointer,
126 encoder,
127 thread_stack_callback);
128 }
129
SnapshotMainStackThread(const pw_cpu_exception_State & cpu_state,uintptr_t stack_low_addr,uintptr_t stack_high_addr,thread::proto::SnapshotThreadInfo::StreamEncoder & encoder,thread::ProcessThreadStackCallback & thread_stack_callback)130 Status SnapshotMainStackThread(
131 const pw_cpu_exception_State& cpu_state,
132 uintptr_t stack_low_addr,
133 uintptr_t stack_high_addr,
134 thread::proto::SnapshotThreadInfo::StreamEncoder& encoder,
135 thread::ProcessThreadStackCallback& thread_stack_callback) {
136 if (!MainStackActive(cpu_state)) {
137 return OkStatus(); // Main stack wasn't active, nothing to capture.
138 }
139
140 return CaptureMainStack(ActiveProcessorMode(cpu_state),
141 stack_low_addr,
142 stack_high_addr,
143 cpu_state.extended.msp,
144 encoder,
145 thread_stack_callback);
146 }
147
148 } // namespace pw::cpu_exception::cortex_m
149