1 // Copyright 2022 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 #include "pw_system/device_handler.h"
15
16 #include "hardware/watchdog.h"
17 #include "pw_cpu_exception/state.h"
18 #include "pw_cpu_exception_cortex_m/snapshot.h"
19 #include "pw_thread_freertos/snapshot.h"
20
21 namespace pw::system::device_handler {
22
23 namespace {
24
25 // These symbols are added to the default pico_sdk linker script as part
26 // of the build process. If the build fails due to missing these symbols,
27 // it may be because a different linker script is configured and these
28 // symbols need added.
29 extern "C" uint32_t __pw_code_begin;
30 extern "C" uint32_t __pw_code_end;
31
32 extern "C" uint32_t __StackBottom;
33 extern "C" uint32_t __StackTop;
34
GetLinkerSymbolValue(const uint32_t & symbol)35 uintptr_t GetLinkerSymbolValue(const uint32_t& symbol) {
36 return reinterpret_cast<uintptr_t>(&symbol);
37 }
38
IsAddressExecutable(uintptr_t address)39 bool IsAddressExecutable(uintptr_t address) {
40 const uintptr_t code_begin = GetLinkerSymbolValue(__pw_code_begin);
41 const uintptr_t code_end = GetLinkerSymbolValue(__pw_code_end);
42
43 if ((address >= code_begin) && (address <= code_end)) {
44 return true;
45 }
46
47 return false;
48 }
49
AddressFilteredDumper(thread::proto::pwpb::Thread::StreamEncoder & encoder,ConstByteSpan stack)50 Status AddressFilteredDumper(
51 thread::proto::pwpb::Thread::StreamEncoder& encoder, ConstByteSpan stack) {
52 span<const uint32_t> addresses =
53 span<const uint32_t>(reinterpret_cast<const uint32_t*>(stack.data()),
54 stack.size_bytes() / sizeof(uint32_t));
55 for (const uint32_t address : addresses) {
56 if (IsAddressExecutable(address)) {
57 auto status = encoder.WriteRawBacktrace(address);
58 if (status != OkStatus())
59 return status;
60 }
61 }
62 return OkStatus();
63 }
64
65 } // namespace
66
RebootSystem()67 void RebootSystem() { watchdog_reboot(0, 0, 0); }
68
CapturePlatformMetadata(snapshot::pwpb::Metadata::StreamEncoder & metadata_encoder)69 void CapturePlatformMetadata(
70 snapshot::pwpb::Metadata::StreamEncoder& metadata_encoder) {
71 // The device_handler is shared between rp2040 and 2350, so handle differences
72 // with the preprocessor.
73 #if _PW_ARCH_ARM_V6M
74 // TODO: https://pwbug.dev/357132837 - Review if IgnoreError is correct here.
75 metadata_encoder.WriteCpuArch(snapshot::pwpb::CpuArchitecture::Enum::ARMV6M)
76 .IgnoreError();
77 #elif _PW_ARCH_ARM_V8M_MAINLINE || _PW_ARCH_ARM_V8_1M_MAINLINE
78 // TODO: https://pwbug.dev/357132837 - Review if IgnoreError is correct here.
79 metadata_encoder.WriteCpuArch(snapshot::pwpb::CpuArchitecture::Enum::ARMV8M)
80 .IgnoreError();
81 #else
82 #error "Unknown CPU architecture."
83 #endif // _PW_ARCH_ARM_V6M
84 }
85
CaptureCpuState(const pw_cpu_exception_State & cpu_state,snapshot::pwpb::Snapshot::StreamEncoder & snapshot_encoder)86 Status CaptureCpuState(
87 const pw_cpu_exception_State& cpu_state,
88 snapshot::pwpb::Snapshot::StreamEncoder& snapshot_encoder) {
89 return cpu_exception::cortex_m::SnapshotCpuState(
90 cpu_state,
91 *static_cast<cpu_exception::cortex_m::pwpb::SnapshotCpuStateOverlay::
92 StreamEncoder*>(
93 static_cast<protobuf::StreamEncoder*>(&snapshot_encoder)));
94 }
95
CaptureMainStackThread(const pw_cpu_exception_State & cpu_state,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder)96 Status CaptureMainStackThread(
97 const pw_cpu_exception_State& cpu_state,
98 thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder) {
99 uintptr_t stack_low_addr = GetLinkerSymbolValue(__StackBottom);
100 uintptr_t stack_high_addr = GetLinkerSymbolValue(__StackTop);
101 thread::ProcessThreadStackCallback stack_dumper =
102 device_handler::AddressFilteredDumper;
103
104 return cpu_exception::cortex_m::SnapshotMainStackThread(
105 cpu_state, stack_low_addr, stack_high_addr, encoder, stack_dumper);
106 }
107
CaptureThreads(uint32_t running_thread_stack_pointer,thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder & encoder)108 Status CaptureThreads(
109 uint32_t running_thread_stack_pointer,
110 thread::proto::pwpb::SnapshotThreadInfo::StreamEncoder& encoder) {
111 thread::ProcessThreadStackCallback stack_dumper =
112 device_handler::AddressFilteredDumper;
113 return thread::freertos::SnapshotThreads(
114 reinterpret_cast<void*>(running_thread_stack_pointer),
115 encoder,
116 stack_dumper);
117 }
118
119 } // namespace pw::system::device_handler
120