// Copyright 2024 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. #define PW_LOG_MODULE_NAME "pw_system" #include "pw_system/system.h" #include #include "pw_allocator/best_fit.h" #include "pw_allocator/synchronized_allocator.h" #include "pw_assert/check.h" #include "pw_async2/allocate_task.h" #include "pw_async2/pend_func_task.h" #include "pw_log/log.h" #include "pw_rpc/echo_service_pwpb.h" #include "pw_sync/interrupt_spin_lock.h" #include "pw_system/config.h" #include "pw_system/device_service.h" #include "pw_system/file_service.h" #include "pw_system/internal/async_packet_io.h" #include "pw_system/log.h" #include "pw_system/thread_snapshot_service.h" #include "pw_system/transfer_service.h" #include "pw_system/work_queue.h" #include "pw_system_private/threads.h" #include "pw_thread/detached_thread.h" #if PW_SYSTEM_ENABLE_CRASH_HANDLER #include "pw_system/crash_handler.h" #include "pw_system/crash_snapshot.h" #endif // PW_SYSTEM_ENABLE_CRASH_HANDLER namespace pw { void SystemStart(channel::ByteReaderWriter& io_channel) { System().Init(io_channel); system::StartScheduler(); } namespace system { namespace { using pw::allocator::BestFitAllocator; using pw::allocator::SynchronizedAllocator; // TODO: b/349654108 - Standardize component declaration and initialization. alignas(internal::PacketIO) std::byte packet_io_[sizeof(internal::PacketIO)]; internal::PacketIO& InitializePacketIoGlobal( channel::ByteReaderWriter& io_channel) { static std::byte buffer[256]; internal::PacketIO* packet_io = new (packet_io_) internal::PacketIO( io_channel, buffer, System().allocator(), System().rpc_server()); return *packet_io; } // Functions for quickly creating a task from a function. This functions could // be moved to `pw::system::AsyncCore`. template [[nodiscard]] bool PostTaskFunction(Func&& func) { async2::Task* task = async2::AllocateTask>( System().allocator(), std::forward(func)); if (task == nullptr) { return false; } System().dispatcher().Post(*task); return true; } template void PostTaskFunctionOrCrash(Func&& func) { PW_CHECK(PostTaskFunction(std::forward(func))); } } // namespace async2::Dispatcher& AsyncCore::dispatcher() { static async2::Dispatcher dispatcher; return dispatcher; } Allocator& AsyncCore::allocator() { alignas(uintptr_t) static std::byte buffer[8192]; static BestFitAllocator<> block_allocator(buffer); static SynchronizedAllocator<::pw::sync::InterruptSpinLock> sync_allocator( block_allocator); return sync_allocator; } bool AsyncCore::RunOnce(Function&& function) { return GetWorkQueue().PushWork(std::move(function)).ok(); } void AsyncCore::Init(channel::ByteReaderWriter& io_channel) { #if PW_SYSTEM_ENABLE_CRASH_HANDLER RegisterCrashHandler(); #endif // PW_SYSTEM_ENABLE_CRASH_HANDLER PW_LOG_INFO("Initializing pw_system"); #if PW_SYSTEM_ENABLE_CRASH_HANDLER if (HasCrashSnapshot()) { PW_LOG_ERROR("=========================="); PW_LOG_ERROR("======CRASH DETECTED======"); PW_LOG_ERROR("=========================="); PW_LOG_ERROR("Crash snapshots available."); PW_LOG_ERROR( "Run `device.get_crash_snapshots()` to download and clear the " "snapshots."); } else { PW_LOG_DEBUG("No crash snapshot"); } #endif // PW_SYSTEM_ENABLE_CRASH_HANDLER PostTaskFunctionOrCrash(InitTask); // Initialize the packet_io subsystem internal::PacketIO& packet_io = InitializePacketIoGlobal(io_channel); packet_io.Start(System().dispatcher(), RpcThreadOptions()); thread::DetachedThread(DispatcherThreadOptions(), [] { System().dispatcher().RunToCompletion(); }); thread::DetachedThread(WorkQueueThreadOptions(), GetWorkQueue()); } async2::Poll<> AsyncCore::InitTask(async2::Context&) { PW_LOG_INFO("Initializing pw_system"); const Status status = GetLogThread().OpenUnrequestedLogStream( kLoggingRpcChannelId, System().rpc_server(), GetLogService()); if (!status.ok()) { PW_LOG_ERROR("Error opening unrequested log streams %d", static_cast(status.code())); } System().rpc_server().RegisterService(GetLogService()); thread::DetachedThread(system::LogThreadOptions(), GetLogThread()); static rpc::EchoService echo_service; System().rpc_server().RegisterService(echo_service); RegisterDeviceService(System().rpc_server()); if (PW_SYSTEM_ENABLE_THREAD_SNAPSHOT_SERVICE != 0) { RegisterThreadSnapshotService(System().rpc_server()); } if (PW_SYSTEM_ENABLE_TRANSFER_SERVICE != 0) { RegisterTransferService(System().rpc_server()); RegisterFileService(System().rpc_server()); thread::DetachedThread(system::TransferThreadOptions(), GetTransferThread()); InitTransferService(); } PW_LOG_INFO("pw_system initialization complete"); return async2::Ready(); } } // namespace system } // namespace pw