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::pwpb::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::pwpb::SnapshotThreadInfo::StreamEncoder& snapshot_encoder,
42 thread::ProcessThreadStackCallback& thread_stack_callback) {
43 thread::proto::pwpb::Thread::StreamEncoder encoder =
44 snapshot_encoder.GetThreadsEncoder();
45
46 const char* thread_name;
47 thread::proto::pwpb::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::pwpb::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::pwpb::ThreadState::Enum::RUNNING;
57 PW_LOG_DEBUG("Thread state: RUNNING");
58 }
59 // TODO: https://pwbug.dev/357138093 - Review IgnoreError calls in this file.
60 encoder.WriteState(thread_state).IgnoreError();
61 encoder.WriteName(as_bytes(span(std::string_view(thread_name))))
62 .IgnoreError();
63
64 const thread::StackContext thread_ctx = {
65 .thread_name = thread_name,
66 .stack_low_addr = stack_low_addr,
67 .stack_high_addr = stack_high_addr,
68 .stack_pointer = stack_pointer,
69 .stack_pointer_est_peak = std::nullopt,
70 };
71 return thread::SnapshotStack(thread_ctx, encoder, thread_stack_callback);
72 }
73
74 } // namespace
75
SnapshotCpuState(const pw_cpu_exception_State & cpu_state,pwpb::SnapshotCpuStateOverlay::StreamEncoder & snapshot_encoder)76 Status SnapshotCpuState(
77 const pw_cpu_exception_State& cpu_state,
78 pwpb::SnapshotCpuStateOverlay::StreamEncoder& snapshot_encoder) {
79 {
80 pwpb::ArmV7mCpuState::StreamEncoder cpu_state_encoder =
81 snapshot_encoder.GetArmv7mCpuStateEncoder();
82 DumpCpuStateProto(cpu_state_encoder, cpu_state).IgnoreError();
83 }
84 return snapshot_encoder.status();
85 }
86
SnapshotMainStackThread(uintptr_t stack_low_addr,uintptr_t stack_high_addr,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder,thread::ProcessThreadStackCallback & thread_stack_callback)87 Status SnapshotMainStackThread(
88 uintptr_t stack_low_addr,
89 uintptr_t stack_high_addr,
90 thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
91 thread::ProcessThreadStackCallback& thread_stack_callback) {
92 uintptr_t stack_pointer;
93 asm volatile("mrs %0, msp\n" : "=r"(stack_pointer));
94
95 // First check if we're in Handler mode, AKA handling exceptions/interrupts.
96 //
97 // Handler mode vs thread mode can be determined via IPSR, bits 8:0 of xPSR.
98 // In thread mode the value is 0, in handler mode the value is non-zero.
99 uint32_t xpsr;
100 asm volatile("mrs %0, xpsr\n" : "=r"(xpsr));
101 if ((xpsr & kXpsrIpsrMask) != 0) {
102 return CaptureMainStack(ProcessorMode::kHandlerMode,
103 stack_low_addr,
104 stack_high_addr,
105 stack_pointer,
106 encoder,
107 thread_stack_callback);
108 }
109
110 // It looks like we're in Thread mode which means we need to check whether
111 // or not we are executing off the main stack currently.
112 //
113 // See ARMv7-M Architecture Reference Manual Section B1.4.4 for the control
114 // register values, in particular the SPSEL bit while in Thread mode which
115 // is 0 while running off the main stack and 1 while running off the proces
116 // stack.
117 uint32_t control;
118 asm volatile("mrs %0, control\n" : "=r"(control));
119 if ((control & kControlThreadModeStackMask) != 0) {
120 return OkStatus(); // Main stack is not currently active.
121 }
122
123 // We're running off the main stack in Thread mode.
124 return CaptureMainStack(ProcessorMode::kThreadMode,
125 stack_low_addr,
126 stack_high_addr,
127 stack_pointer,
128 encoder,
129 thread_stack_callback);
130 }
131
SnapshotMainStackThread(const pw_cpu_exception_State & cpu_state,uintptr_t stack_low_addr,uintptr_t stack_high_addr,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder,thread::ProcessThreadStackCallback & thread_stack_callback)132 Status SnapshotMainStackThread(
133 const pw_cpu_exception_State& cpu_state,
134 uintptr_t stack_low_addr,
135 uintptr_t stack_high_addr,
136 thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder,
137 thread::ProcessThreadStackCallback& thread_stack_callback) {
138 if (!MainStackActive(cpu_state)) {
139 return OkStatus(); // Main stack wasn't active, nothing to capture.
140 }
141
142 return CaptureMainStack(ActiveProcessorMode(cpu_state),
143 stack_low_addr,
144 stack_high_addr,
145 cpu_state.extended.msp,
146 encoder,
147 thread_stack_callback);
148 }
149
150 } // namespace pw::cpu_exception::cortex_m
151