From d7adef3810df4e1da76f73a523ceb1fdd00d222e Mon Sep 17 00:00:00 2001 From: yellows8 Date: Mon, 21 Dec 2020 21:51:43 -0500 Subject: [PATCH 1/8] Added uart.mitm. --- config_templates/system_settings.ini | 5 + .../impl/ams_system_thread_definitions.hpp | 3 + .../source/amsmitm_module_management.cpp | 3 + .../source/set_mitm/settings_sd_kvs.cpp | 6 + .../source/uart_mitm/uart_mitm_service.cpp | 341 ++++++++++++++++++ .../source/uart_mitm/uart_mitm_service.hpp | 109 ++++++ .../ams_mitm/source/uart_mitm/uart_shim.c | 77 ++++ .../ams_mitm/source/uart_mitm/uart_shim.h | 23 ++ .../source/uart_mitm/uartmitm_module.cpp | 68 ++++ .../source/uart_mitm/uartmitm_module.hpp | 24 ++ 10 files changed, 659 insertions(+) create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uart_shim.c create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uart_shim.h create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.hpp diff --git a/config_templates/system_settings.ini b/config_templates/system_settings.ini index 3059cb839..2e7abf8ff 100644 --- a/config_templates/system_settings.ini +++ b/config_templates/system_settings.ini @@ -52,6 +52,11 @@ ; Controls whether dns.mitm logs to the sd card for debugging ; 0 = Disabled, 1 = Enabled ; enable_dns_mitm_debug_log = u8!0x0 +; Controls whether to enable uart mitm +; for logging bluetooth HCI to btsnoop captures. +; This is only implemented for [7.0.0+]. +; 0 = Do not enable, 1 = Enable. +; enable_uart_mitm = u8!0x0 [hbloader] ; Controls the size of the homebrew heap when running as applet. ; If set to zero, all available applet memory is used as heap. diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index ff789a78d..b9a025663 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp @@ -109,6 +109,9 @@ namespace ams::impl { AMS_DEFINE_SYSTEM_THREAD(21, settings, Main); AMS_DEFINE_SYSTEM_THREAD(21, settings, IpcServer); + /* Bus. */ + AMS_DEFINE_SYSTEM_THREAD(-12, uart, IpcServer); + /* erpt. */ AMS_DEFINE_SYSTEM_THREAD(21, erpt, Main); AMS_DEFINE_SYSTEM_THREAD(21, erpt, IpcServer); diff --git a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp index 436c95e43..eb7a7471d 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module_management.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_module_management.cpp @@ -24,6 +24,7 @@ #include "ns_mitm/nsmitm_module.hpp" #include "dns_mitm/dnsmitm_module.hpp" #include "sysupdater/sysupdater_module.hpp" +#include "uart_mitm/uartmitm_module.hpp" namespace ams::mitm { @@ -37,6 +38,7 @@ namespace ams::mitm { ModuleId_NsMitm, ModuleId_DnsMitm, ModuleId_Sysupdater, + ModuleId_UartMitm, ModuleId_Count, }; @@ -70,6 +72,7 @@ namespace ams::mitm { GetModuleDefinition(), GetModuleDefinition(), GetModuleDefinition(), + GetModuleDefinition(), }; } diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 1ed8312e5..8d91302cc 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -362,6 +362,12 @@ namespace ams::settings::fwdbg { /* 0 = Disabled, 1 = Enabled */ R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm_debug_log", "u8!0x0")); + /* Controls whether to enable uart mitm */ + /* for logging bluetooth HCI to btsnoop captures. */ + /* This is only implemented for [7.0.0+]. */ + /* 0 = Do not enable, 1 = Enable. */ + R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_uart_mitm", "u8!0x0")); + /* Hbloader custom settings. */ /* Controls the size of the homebrew heap when running as applet. */ diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp new file mode 100644 index 000000000..0531cf776 --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "uart_mitm_service.hpp" +#include "../amsmitm_debug.hpp" +#include "../amsmitm_fs_utils.hpp" + +/* TODO: This should really use async fs-writing, there's a slowdown with bluetooth communications with current fs-writing. */ + +namespace ams::mitm::uart { + + /* Helper functions. */ + bool UartPortService::TryGetCurrentTimestamp(u64 *out) { + /* Clear output. */ + *out = 0; + + /* Check if we have time service. */ + { + bool has_time_service = false; + if (R_FAILED(sm::HasService(&has_time_service, sm::ServiceName::Encode("time:s"))) || !has_time_service) { + return false; + } + } + + /* Try to get the current time. */ + { + sm::ScopedServiceHolder time_holder; + return time_holder && R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out)); + } + } + + UartPortService::UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s) : client_info(cl), srv(std::move(s)) { + Result rc=0; + /* Get a timestamp. */ + u64 timestamp0, timestamp1; + this->TryGetCurrentTimestamp(×tamp0); + timestamp1 = svcGetSystemTick(); + + std::snprintf(this->base_path, sizeof(this->base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast(this->client_info.program_id)); + ams::mitm::fs::CreateAtmosphereSdDirectory("uart_logs"); + ams::mitm::fs::CreateAtmosphereSdDirectory(this->base_path); + + char tmp_path[256]; + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "cmd_log"); + ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); + this->cmdlog_pos = 0; + + this->send_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); + if (this->send_cache_buffer != nullptr) { + std::memset(this->send_cache_buffer, 0, this->CacheBufferSize); + } + this->send_cache_pos = 0; + + this->receive_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); + if (this->receive_cache_buffer != nullptr) { + std::memset(this->receive_cache_buffer, 0, this->CacheBufferSize); + } + this->receive_cache_pos = 0; + + if (this->send_cache_buffer != nullptr && this->receive_cache_buffer != nullptr) { + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "btsnoop_hci.log"); + ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); + rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + this->datalog_ready = R_SUCCEEDED(rc); + } + this->datalog_pos = 0; + + struct { + char id[8]; + u32 version; + u32 datalink_type; + } btsnoop_header = { .id = "btsnoop" }; + + u32 version = 1; + u32 datalink_type = 1002; /* HCI UART (H4) */ + ams::util::StoreBigEndian(&btsnoop_header.version, version); + ams::util::StoreBigEndian(&btsnoop_header.datalink_type, datalink_type); + + this->data_logging_enabled = true; + this->WriteLog(&btsnoop_header, sizeof(btsnoop_header)); + /* This will be re-enabled by WriteUartData once a certain command is detected. */ + /* If you want to log all HCI traffic during system-boot initialization, you can comment out the below line, however there will be a slowdown. */ + this->data_logging_enabled = false; + } + + void UartPortService::SaveFile(const char *path, const void* buffer, size_t size) { + Result rc=0; + FsFile file={}; + char tmp_path[256]; + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, path); + ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); + rc = ams::mitm::fs::OpenAtmosphereSdFile(&file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + if (R_SUCCEEDED(rc)) { + rc = fsFileWrite(&file, 0, buffer, size, FsWriteOption_None); + } + fsFileClose(&file); + } + + void UartPortService::WriteCmdLog(const char *str) { + Result rc=0; + FsFile file={}; + char tmp_path[256]; + size_t len = strlen(str); + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "cmd_log"); + rc = ams::mitm::fs::OpenAtmosphereSdFile(&file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + if (R_SUCCEEDED(rc)) { + rc = fsFileWrite(&file, this->cmdlog_pos, str, len, FsWriteOption_None); + } + if (R_SUCCEEDED(rc)) { + this->cmdlog_pos += len; + } + fsFileClose(&file); + } + + void UartPortService::WriteLog(const void* buffer, size_t size) { + if (this->data_logging_enabled && this->datalog_ready) { + if (R_SUCCEEDED(fsFileWrite(&this->datalog_file, this->datalog_pos, buffer, size, FsWriteOption_None))) { + this->datalog_pos += size; + } + } + } + + void UartPortService::WriteLogPacket(bool dir, const void* buffer, size_t size) { + struct { + u32 original_length; + u32 included_length; + u32 packet_flags; + u32 cumulative_drops; + s64 timestamp_microseconds; + } pkt_hdr = {}; + + u32 flags = 0; + if (dir) { + flags |= BIT(0); + } + ams::util::StoreBigEndian(&pkt_hdr.original_length, static_cast(size)); + ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast(size)); + ams::util::StoreBigEndian(&pkt_hdr.packet_flags, flags); + + this->WriteLog(&pkt_hdr, sizeof(pkt_hdr)); + this->WriteLog(buffer, size); + } + + void UartPortService::WriteUartData(bool dir, const void* buffer, size_t size) { + u8 *cache_buffer = !dir ? this->send_cache_buffer : this->receive_cache_buffer; + size_t *cache_pos = !dir ? &this->send_cache_pos : &this->receive_cache_pos; + if (size && *cache_pos + size <= this->CacheBufferSize) { + struct { + u8 opcode[0x2]; + u8 param_len; + } *hci_cmd = reinterpret_cast(&cache_buffer[0x1]); + static_assert(sizeof(*hci_cmd) == 0x3); + + struct { + u8 handle_flags[0x2]; + u16 data_len; + } *hci_acl_data = reinterpret_cast(&cache_buffer[0x1]); + static_assert(sizeof(*hci_acl_data) == 0x4); + + struct { + u8 handle_flags[0x2]; + u8 data_len; + } *hci_sco_data = reinterpret_cast(&cache_buffer[0x1]); + static_assert(sizeof(*hci_sco_data) == 0x3); + + struct { + u8 event_code; + u8 param_len; + } *hci_event = reinterpret_cast(&cache_buffer[0x1]); + static_assert(sizeof(*hci_event) == 0x2); + + struct { + u8 handle_flags[0x2]; + u16 data_load_len : 14; + u8 rfu1 : 2; + } *hci_iso_data = reinterpret_cast(&cache_buffer[0x1]); + static_assert(sizeof(*hci_iso_data) == 0x4); + + std::memcpy(&cache_buffer[*cache_pos], buffer, size); + (*cache_pos)+= size; + + do { + size_t orig_pkt_len = 0x0; + size_t pkt_len = 0x1; + if (cache_buffer[0] == 0x1) { + if (*cache_pos >= 0x1+sizeof(*hci_cmd)) { + orig_pkt_len = sizeof(*hci_cmd) + hci_cmd->param_len; + /* Check for the first cmd used in the port which is opened last. */ + if (!this->data_logging_enabled && hci_cmd->opcode[1] == 0xFC && hci_cmd->opcode[0] == 0x16) { + this->data_logging_enabled = true; + } + } + } + else if (cache_buffer[0] == 0x2) { + if (*cache_pos >= 0x1+sizeof(*hci_acl_data)) { + orig_pkt_len = sizeof(*hci_acl_data) + hci_acl_data->data_len; + } + } + else if (cache_buffer[0] == 0x3) { + if (*cache_pos >= 0x1+sizeof(*hci_sco_data)) { + orig_pkt_len = sizeof(*hci_sco_data) + hci_sco_data->data_len; + } + } + else if (cache_buffer[0] == 0x4) { + if (*cache_pos >= 0x1+sizeof(*hci_event)) { + orig_pkt_len = sizeof(*hci_event) + hci_event->param_len; + } + } + else if (cache_buffer[0] == 0x5) { + if (*cache_pos >= 0x1+sizeof(*hci_iso_data)) { + orig_pkt_len = sizeof(*hci_iso_data) + hci_iso_data->data_load_len; + } + } + else { + char str[256]; + std::snprintf(str, sizeof(str), "WriteUartData(dir = %s): Unknown HCI packet indicator 0x%x, ignoring the packet and emptying the cache.\n", !dir ? "send" : "receive", cache_buffer[0]); + this->WriteCmdLog(str); + *cache_pos = 0; + } + + if (orig_pkt_len) { + if (*cache_pos >= 0x1+orig_pkt_len) { + pkt_len+= orig_pkt_len; + } + } + + if (pkt_len>0x1) { + this->WriteLogPacket(dir, cache_buffer, pkt_len); + (*cache_pos)-= pkt_len; + if (*cache_pos) { + std::memmove(cache_buffer, &cache_buffer[pkt_len], *cache_pos); + } + } + else break; + } while(*cache_pos); + } + } + + Result UartPortService::OpenPort(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { + Result rc = uartPortSessionOpenPortFwd(this->srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); + svcCloseHandle(send_handle.GetValue()); + svcCloseHandle(receive_handle.GetValue()); + + char str[256]; + std::snprintf(str, sizeof(str), "OpenPort(port = 0x%x, baud_rate = %u, flow_control_mode = %u, device_variation = %u, is_invert_tx = %d, is_invert_rx = %d, is_invert_rts = %d, is_invert_cts = %d, send_buffer_length = 0x%lx, receive_buffer_length = 0x%lx): rc = 0x%x, out = %d\n", port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_buffer_length, receive_buffer_length, rc.GetValue(), out.GetValue()); + this->WriteCmdLog(str); + return rc; + } + + Result UartPortService::OpenPortForDev(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { + Result rc = uartPortSessionOpenPortForDevFwd(this->srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); + svcCloseHandle(send_handle.GetValue()); + svcCloseHandle(receive_handle.GetValue()); + + char str[256]; + std::snprintf(str, sizeof(str), "OpenPortForDev(port = 0x%x, baud_rate = %u, flow_control_mode = %u, device_variation = %u, is_invert_tx = %d, is_invert_rx = %d, is_invert_rts = %d, is_invert_cts = %d, send_buffer_length = 0x%lx, receive_buffer_length = 0x%lx): rc = 0x%x, out = %d\n", port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_buffer_length, receive_buffer_length, rc.GetValue(), out.GetValue()); + this->WriteCmdLog(str); + return rc; + } + + Result UartPortService::GetWritableLength(sf::Out out) { + Result rc = uartPortSessionGetWritableLength(this->srv.get(), reinterpret_cast(out.GetPointer())); + + char str[256]; + std::snprintf(str, sizeof(str), "GetWritableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue()); + this->WriteCmdLog(str); + + return rc; + } + + Result UartPortService::Send(sf::Out out_size, const sf::InAutoSelectBuffer &data) { + Result rc = uartPortSessionSend(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); + + if (R_SUCCEEDED(rc) && out_size.GetValue()) { + this->WriteUartData(false, data.GetPointer(), out_size.GetValue()); + } + return rc; + } + + Result UartPortService::GetReadableLength(sf::Out out) { + Result rc = uartPortSessionGetReadableLength(this->srv.get(), reinterpret_cast(out.GetPointer())); + + char str[256]; + std::snprintf(str, sizeof(str), "GetWritableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue()); + this->WriteCmdLog(str); + + return rc; + } + + Result UartPortService::Receive(sf::Out out_size, const sf::OutAutoSelectBuffer &data) { + Result rc = uartPortSessionReceive(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); + + if (R_SUCCEEDED(rc) && out_size.GetValue()) { + this->WriteUartData(true, data.GetPointer(), out_size.GetValue()); + } + return rc; + } + + Result UartPortService::BindPortEvent(sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold) { + Result rc = uartPortSessionBindPortEventFwd(this->srv.get(), port_event_type, threshold, reinterpret_cast(out.GetPointer()), out_event_handle.GetHandlePointer()); + + char str[256]; + std::snprintf(str, sizeof(str), "BindPortEvent(port_event_type = 0x%x, threshold = 0x%lx): rc = 0x%x, out = %d\n", port_event_type, threshold, rc.GetValue(), out.GetValue()); + this->WriteCmdLog(str); + return rc; + } + + Result UartPortService::UnbindPortEvent(sf::Out out, UartPortEventType port_event_type) { + Result rc = uartPortSessionUnbindPortEvent(this->srv.get(), port_event_type, reinterpret_cast(out.GetPointer())); + + char str[256]; + std::snprintf(str, sizeof(str), "UnbindPortEvent(port_event_type = 0x%x): rc = 0x%x, out = %d\n", port_event_type, rc.GetValue(), out.GetValue()); + this->WriteCmdLog(str); + + return rc; + } + + Result UartMitmService::CreatePortSession(sf::Out> out) { + /* Open a port interface. */ + UartPortSession port; + R_TRY(uartCreatePortSessionFwd(this->forward_service.get(), &port)); + const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&port.s)}; + + out.SetValue(sf::MakeShared(this->client_info, std::make_unique(port)), target_object_id); + return ResultSuccess(); + } + +} diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp new file mode 100644 index 000000000..b162e14f3 --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include "uart_shim.h" + +namespace ams::mitm::uart { + + namespace impl { + + #define AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenPort, (sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, OpenPortForDev, (sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetWritableLength, (sf::Out out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, Send, (sf::Out out_size, const sf::InAutoSelectBuffer &data)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetReadableLength, (sf::Out out)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, Receive, (sf::Out out_size, const sf::OutAutoSelectBuffer &data)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, BindPortEvent, (sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, UnbindPortEvent, (sf::Out out, UartPortEventType port_event_type)) \ + + AMS_SF_DEFINE_INTERFACE(IPortSession, AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO) + + #define AMS_UART_MITM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, CreatePortSession, (sf::Out> out)) + + AMS_SF_DEFINE_MITM_INTERFACE(IUartMitmInterface, AMS_UART_MITM_INTERFACE_INFO) + + } + + class UartPortService { + private: + sm::MitmProcessInfo client_info; + std::unique_ptr<::UartPortSession> srv; + + static constexpr inline size_t CacheBufferSize = 0x1000; + + char base_path[256]; + + size_t cmdlog_pos; + size_t datalog_pos; + + bool datalog_ready; + bool data_logging_enabled; + + FsFile datalog_file; + + u8 *send_cache_buffer; + u8 *receive_cache_buffer; + size_t send_cache_pos; + size_t receive_cache_pos; + + bool TryGetCurrentTimestamp(u64 *out); + void SaveFile(const char *path, const void* buffer, size_t size); + void WriteCmdLog(const char *str); + void WriteLog(const void* buffer, size_t size); + void WriteLogPacket(bool dir, const void* buffer, size_t size); + void WriteUartData(bool dir, const void* buffer, size_t size); + public: + UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s); + + virtual ~UartPortService() { + uartPortSessionClose(this->srv.get()); + fsFileClose(&this->datalog_file); + std::free(this->send_cache_buffer); + std::free(this->receive_cache_buffer); + } + public: + /* Actual command API. */ + Result OpenPort(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length); + Result OpenPortForDev(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length); + Result GetWritableLength(sf::Out out); + Result Send(sf::Out out_size, const sf::InAutoSelectBuffer &data); + Result GetReadableLength(sf::Out out); + Result Receive(sf::Out out_size, const sf::OutAutoSelectBuffer &data); + Result BindPortEvent(sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold); + Result UnbindPortEvent(sf::Out out, UartPortEventType port_event_type); + }; + static_assert(impl::IsIPortSession); + + class UartMitmService : public sf::MitmServiceImplBase { + public: + using MitmServiceImplBase::MitmServiceImplBase; + public: + static bool ShouldMitm(const sm::MitmProcessInfo &client_info) { + /* We will mitm: + * - bluetooth, for logging HCI. + */ + return client_info.program_id == ncm::SystemProgramId::Bluetooth; + } + public: + Result CreatePortSession(sf::Out> out); + }; + static_assert(impl::IsIUartMitmInterface); + +} diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_shim.c b/stratosphere/ams_mitm/source/uart_mitm/uart_shim.c new file mode 100644 index 000000000..e28f3bdec --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_shim.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "uart_shim.h" + +/* Command forwarders. */ +Result uartCreatePortSessionFwd(Service* s, UartPortSession* out) { + return serviceDispatch(s, 6, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} + +// [7.0.0+] +static Result _uartPortSessionOpenPortFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length, u32 cmd_id) { + const struct { + u8 is_invert_tx; + u8 is_invert_rx; + u8 is_invert_rts; + u8 is_invert_cts; + u32 port; + u32 baud_rate; + u32 flow_control_mode; + u32 device_variation; + u32 pad; + u64 send_buffer_length; + u64 receive_buffer_length; + } in = { is_invert_tx!=0, is_invert_rx!=0, is_invert_rts!=0, is_invert_cts!=0, port, baud_rate, flow_control_mode, device_variation, 0, send_buffer_length, receive_buffer_length }; + + u8 tmp=0; + Result rc = serviceDispatchInOut(&s->s, cmd_id, in, tmp, + .in_num_handles = 2, + .in_handles = { send_handle, receive_handle }, + ); + if (R_SUCCEEDED(rc) && out) *out = tmp & 1; + return rc; +} + +Result uartPortSessionOpenPortFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { + return _uartPortSessionOpenPortFwd(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length, 0); +} + +Result uartPortSessionOpenPortForDevFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { + return _uartPortSessionOpenPortFwd(s, out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length, 1); +} + +Result uartPortSessionBindPortEventFwd(UartPortSession* s, UartPortEventType port_event_type, s64 threshold, bool *out, Handle *handle_out) { + const struct { + u32 port_event_type; + u32 pad; + u64 threshold; + } in = { port_event_type, 0, threshold }; + + u8 tmp=0; + Result rc = serviceDispatchInOut(&s->s, 6, in, tmp, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = handle_out, + ); + if (R_SUCCEEDED(rc) && out) *out = tmp & 1; + return rc; +} + diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_shim.h b/stratosphere/ams_mitm/source/uart_mitm/uart_shim.h new file mode 100644 index 000000000..04e759189 --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_shim.h @@ -0,0 +1,23 @@ +/** + * @file uart_shim.h + * @brief UART IPC wrapper. + * @author yellows8 + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Command forwarders. */ +Result uartCreatePortSessionFwd(Service* s, UartPortSession* out); + +Result uartPortSessionOpenPortFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length); +Result uartPortSessionOpenPortForDevFwd(UartPortSession* s, bool *out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, Handle send_handle, Handle receive_handle, u64 send_buffer_length, u64 receive_buffer_length); +Result uartPortSessionBindPortEventFwd(UartPortSession* s, UartPortEventType port_event_type, s64 threshold, bool *out, Handle *handle_out); + +#ifdef __cplusplus +} +#endif diff --git a/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp new file mode 100644 index 000000000..297e91bbc --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "../amsmitm_initialization.hpp" +#include "uartmitm_module.hpp" +#include "uart_mitm_service.hpp" + +namespace ams::mitm::uart { + + namespace { + + constexpr sm::ServiceName UartMitmServiceName = sm::ServiceName::Encode("uart"); + + struct ServerOptions { + static constexpr size_t PointerBufferSize = 0x1000; + static constexpr size_t MaxDomains = 0; + static constexpr size_t MaxDomainObjects = 0; + }; + + constexpr size_t MaxServers = 1; + constexpr size_t MaxSessions = 10; + + sf::hipc::ServerManager g_server_manager; + + bool ShouldMitmUart() { + u8 en = 0; + if (settings::fwdbg::GetSettingsItemValue(&en, sizeof(en), "atmosphere", "enable_uart_mitm") == sizeof(en)) { + return (en != 0); + } + return false; + } + } + + void MitmModule::ThreadFunction(void *arg) { + /* The OpenPort cmds had the params changed with 6.x/7.x, so only support 7.x+. */ + if (hos::GetVersion() < hos::Version_7_0_0) { + return; + } + + /* Wait until initialization is complete. */ + mitm::WaitInitialized(); + + /* Only use uart-mitm if enabled by the sys-setting. */ + if (!ShouldMitmUart()) { + return; + } + + /* Create mitm servers. */ + R_ABORT_UNLESS((g_server_manager.RegisterMitmServer(UartMitmServiceName))); + + /* Loop forever, servicing our services. */ + g_server_manager.LoopProcess(); + } + +} diff --git a/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.hpp b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.hpp new file mode 100644 index 000000000..dd2a71940 --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.hpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../amsmitm_module.hpp" + +namespace ams::mitm::uart { + + DEFINE_MITM_MODULE_CLASS(0x4000, AMS_GET_SYSTEM_THREAD_PRIORITY(uart, IpcServer) - 1); + +} From 6e1b0abf1d72a8342d722f4c3e829dabc4e71cb2 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Tue, 22 Dec 2020 21:48:18 -0500 Subject: [PATCH 2/8] uart.mitm: comments, etc. --- .../source/uart_mitm/uart_mitm_service.cpp | 74 +++++++++++++------ .../source/uart_mitm/uart_mitm_service.hpp | 1 - 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp index 0531cf776..4f9f4ea56 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -45,39 +45,47 @@ namespace ams::mitm::uart { UartPortService::UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s) : client_info(cl), srv(std::move(s)) { Result rc=0; /* Get a timestamp. */ - u64 timestamp0, timestamp1; + u64 timestamp0=0, timestamp1; this->TryGetCurrentTimestamp(×tamp0); timestamp1 = svcGetSystemTick(); + /* Setup/create the logging directory. */ std::snprintf(this->base_path, sizeof(this->base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast(this->client_info.program_id)); ams::mitm::fs::CreateAtmosphereSdDirectory("uart_logs"); ams::mitm::fs::CreateAtmosphereSdDirectory(this->base_path); + /* Create/initialize the text cmd_log. */ char tmp_path[256]; std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "cmd_log"); ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); this->cmdlog_pos = 0; + /* Initialize the Send cache-buffer. */ this->send_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); if (this->send_cache_buffer != nullptr) { std::memset(this->send_cache_buffer, 0, this->CacheBufferSize); } this->send_cache_pos = 0; + /* Initialize the Receive cache-buffer. */ this->receive_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); if (this->receive_cache_buffer != nullptr) { std::memset(this->receive_cache_buffer, 0, this->CacheBufferSize); } this->receive_cache_pos = 0; + /* When the above is successful, initialize the datalog. */ if (this->send_cache_buffer != nullptr && this->receive_cache_buffer != nullptr) { std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "btsnoop_hci.log"); ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + /* Set datalog_ready to whether initialization was successful. */ this->datalog_ready = R_SUCCEEDED(rc); } this->datalog_pos = 0; + /* Setup the btsnoop header. */ + struct { char id[8]; u32 version; @@ -89,26 +97,18 @@ namespace ams::mitm::uart { ams::util::StoreBigEndian(&btsnoop_header.version, version); ams::util::StoreBigEndian(&btsnoop_header.datalink_type, datalink_type); + /* Enable data-logging, required for WriteLog() to write anything. */ this->data_logging_enabled = true; + + /* Write the btsnoop header to the datalog. */ this->WriteLog(&btsnoop_header, sizeof(btsnoop_header)); + /* This will be re-enabled by WriteUartData once a certain command is detected. */ /* If you want to log all HCI traffic during system-boot initialization, you can comment out the below line, however there will be a slowdown. */ this->data_logging_enabled = false; } - void UartPortService::SaveFile(const char *path, const void* buffer, size_t size) { - Result rc=0; - FsFile file={}; - char tmp_path[256]; - std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, path); - ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); - rc = ams::mitm::fs::OpenAtmosphereSdFile(&file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); - if (R_SUCCEEDED(rc)) { - rc = fsFileWrite(&file, 0, buffer, size, FsWriteOption_None); - } - fsFileClose(&file); - } - + /* Append the specified string to the text cmd_log file. */ void UartPortService::WriteCmdLog(const char *str) { Result rc=0; FsFile file={}; @@ -125,7 +125,9 @@ namespace ams::mitm::uart { fsFileClose(&file); } + /* Append the specified data to the datalog file. */ void UartPortService::WriteLog(const void* buffer, size_t size) { + /* Only write to the file if data-logging is enabled and initialized. */ if (this->data_logging_enabled && this->datalog_ready) { if (R_SUCCEEDED(fsFileWrite(&this->datalog_file, this->datalog_pos, buffer, size, FsWriteOption_None))) { this->datalog_pos += size; @@ -133,6 +135,8 @@ namespace ams::mitm::uart { } } + /* Append the specified packet to the datalog via WriteLog. */ + /* dir: false = Send (host->controller), true = Receive (controller->host). */ void UartPortService::WriteLogPacket(bool dir, const void* buffer, size_t size) { struct { u32 original_length; @@ -150,13 +154,20 @@ namespace ams::mitm::uart { ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast(size)); ams::util::StoreBigEndian(&pkt_hdr.packet_flags, flags); + /* Currently we leave the timestamp at value 0. */ + this->WriteLog(&pkt_hdr, sizeof(pkt_hdr)); this->WriteLog(buffer, size); } + /* Log data from Send/Receive. */ + /* dir: false = Send (host->controller), true = Receive (controller->host). */ void UartPortService::WriteUartData(bool dir, const void* buffer, size_t size) { + /* Select which cache buffer/pos to use via dir. */ u8 *cache_buffer = !dir ? this->send_cache_buffer : this->receive_cache_buffer; size_t *cache_pos = !dir ? &this->send_cache_pos : &this->receive_cache_pos; + + /* Verify that the input size is non-zero, and within cache buffer bounds. */ if (size && *cache_pos + size <= this->CacheBufferSize) { struct { u8 opcode[0x2]; @@ -189,54 +200,64 @@ namespace ams::mitm::uart { } *hci_iso_data = reinterpret_cast(&cache_buffer[0x1]); static_assert(sizeof(*hci_iso_data) == 0x4); + /* Copy the input data into the cache and update the pos. */ std::memcpy(&cache_buffer[*cache_pos], buffer, size); (*cache_pos)+= size; + /* Process the packets in the cache. */ do { size_t orig_pkt_len = 0x0; size_t pkt_len = 0x1; - if (cache_buffer[0] == 0x1) { + + /* Determine which HCI packet this is, via the packet indicator. */ + /* These are supported regardless of whether the official bluetooth-sysmodule supports it. */ + + if (cache_buffer[0] == 0x1) { /* HCI Command */ if (*cache_pos >= 0x1+sizeof(*hci_cmd)) { orig_pkt_len = sizeof(*hci_cmd) + hci_cmd->param_len; - /* Check for the first cmd used in the port which is opened last. */ + /* Check for the first command used in the port which is opened last by bluetooth-sysmodule. */ + /* This is a vendor command. */ + /* Once detected, data-logging will be enabled. */ if (!this->data_logging_enabled && hci_cmd->opcode[1] == 0xFC && hci_cmd->opcode[0] == 0x16) { this->data_logging_enabled = true; } } } - else if (cache_buffer[0] == 0x2) { + else if (cache_buffer[0] == 0x2) { /* HCI ACL Data */ if (*cache_pos >= 0x1+sizeof(*hci_acl_data)) { orig_pkt_len = sizeof(*hci_acl_data) + hci_acl_data->data_len; } } - else if (cache_buffer[0] == 0x3) { + else if (cache_buffer[0] == 0x3) { /* HCI Synchronous Data (SCO) */ if (*cache_pos >= 0x1+sizeof(*hci_sco_data)) { orig_pkt_len = sizeof(*hci_sco_data) + hci_sco_data->data_len; } } - else if (cache_buffer[0] == 0x4) { + else if (cache_buffer[0] == 0x4) { /* HCI Event */ if (*cache_pos >= 0x1+sizeof(*hci_event)) { orig_pkt_len = sizeof(*hci_event) + hci_event->param_len; } } - else if (cache_buffer[0] == 0x5) { + else if (cache_buffer[0] == 0x5) { /* HCI ISO Data */ if (*cache_pos >= 0x1+sizeof(*hci_iso_data)) { orig_pkt_len = sizeof(*hci_iso_data) + hci_iso_data->data_load_len; } } - else { + else { /* Unknown HCI packet */ char str[256]; std::snprintf(str, sizeof(str), "WriteUartData(dir = %s): Unknown HCI packet indicator 0x%x, ignoring the packet and emptying the cache.\n", !dir ? "send" : "receive", cache_buffer[0]); this->WriteCmdLog(str); *cache_pos = 0; } + /* If a full packet is available in the cache, update pkt_len. */ if (orig_pkt_len) { if (*cache_pos >= 0x1+orig_pkt_len) { pkt_len+= orig_pkt_len; } } + /* If a packet is available, log it and update the cache. */ if (pkt_len>0x1) { this->WriteLogPacket(dir, cache_buffer, pkt_len); (*cache_pos)-= pkt_len; @@ -244,11 +265,13 @@ namespace ams::mitm::uart { std::memmove(cache_buffer, &cache_buffer[pkt_len], *cache_pos); } } + /* Otherwise, exit the loop. */ else break; } while(*cache_pos); } } + /* Forward OpenPort and write to the cmd_log. */ Result UartPortService::OpenPort(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { Result rc = uartPortSessionOpenPortFwd(this->srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); svcCloseHandle(send_handle.GetValue()); @@ -260,6 +283,7 @@ namespace ams::mitm::uart { return rc; } + /* Forward OpenPortForDev and write to the cmd_log. */ Result UartPortService::OpenPortForDev(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { Result rc = uartPortSessionOpenPortForDevFwd(this->srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); svcCloseHandle(send_handle.GetValue()); @@ -271,6 +295,7 @@ namespace ams::mitm::uart { return rc; } + /* Forward GetWritableLength and write to the cmd_log. */ Result UartPortService::GetWritableLength(sf::Out out) { Result rc = uartPortSessionGetWritableLength(this->srv.get(), reinterpret_cast(out.GetPointer())); @@ -281,6 +306,7 @@ namespace ams::mitm::uart { return rc; } + /* Forward Send and log the data if the out_size is non-zero. */ Result UartPortService::Send(sf::Out out_size, const sf::InAutoSelectBuffer &data) { Result rc = uartPortSessionSend(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); @@ -290,16 +316,18 @@ namespace ams::mitm::uart { return rc; } + /* Forward GetReadableLength and write to the cmd_log. */ Result UartPortService::GetReadableLength(sf::Out out) { Result rc = uartPortSessionGetReadableLength(this->srv.get(), reinterpret_cast(out.GetPointer())); char str[256]; - std::snprintf(str, sizeof(str), "GetWritableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue()); + std::snprintf(str, sizeof(str), "GetReadableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue()); this->WriteCmdLog(str); return rc; } + /* Forward Receive and log the data if the out_size is non-zero. */ Result UartPortService::Receive(sf::Out out_size, const sf::OutAutoSelectBuffer &data) { Result rc = uartPortSessionReceive(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); @@ -309,6 +337,7 @@ namespace ams::mitm::uart { return rc; } + /* Forward BindPortEvent and write to the cmd_log. */ Result UartPortService::BindPortEvent(sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold) { Result rc = uartPortSessionBindPortEventFwd(this->srv.get(), port_event_type, threshold, reinterpret_cast(out.GetPointer()), out_event_handle.GetHandlePointer()); @@ -318,6 +347,7 @@ namespace ams::mitm::uart { return rc; } + /* Forward UnbindPortEvent and write to the cmd_log. */ Result UartPortService::UnbindPortEvent(sf::Out out, UartPortEventType port_event_type) { Result rc = uartPortSessionUnbindPortEvent(this->srv.get(), port_event_type, reinterpret_cast(out.GetPointer())); diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp index b162e14f3..53e7a0885 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp @@ -64,7 +64,6 @@ namespace ams::mitm::uart { size_t receive_cache_pos; bool TryGetCurrentTimestamp(u64 *out); - void SaveFile(const char *path, const void* buffer, size_t size); void WriteCmdLog(const char *str); void WriteLog(const void* buffer, size_t size); void WriteLogPacket(bool dir, const void* buffer, size_t size); From 2b3c7fd104581764797ed231a55a17e3f5580308 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 2 Feb 2021 15:00:49 -0800 Subject: [PATCH 3/8] uart.mitm: update for new sf semantics --- .../source/uart_mitm/uart_mitm_service.cpp | 5 +-- .../source/uart_mitm/uart_mitm_service.hpp | 40 +++++++++---------- .../source/uart_mitm/uartmitm_module.cpp | 27 ++++++++++++- 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp index 4f9f4ea56..14a165feb 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -15,7 +15,6 @@ */ #include #include "uart_mitm_service.hpp" -#include "../amsmitm_debug.hpp" #include "../amsmitm_fs_utils.hpp" /* TODO: This should really use async fs-writing, there's a slowdown with bluetooth communications with current fs-writing. */ @@ -358,13 +357,13 @@ namespace ams::mitm::uart { return rc; } - Result UartMitmService::CreatePortSession(sf::Out> out) { + Result UartMitmService::CreatePortSession(sf::Out> out) { /* Open a port interface. */ UartPortSession port; R_TRY(uartCreatePortSessionFwd(this->forward_service.get(), &port)); const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&port.s)}; - out.SetValue(sf::MakeShared(this->client_info, std::make_unique(port)), target_object_id); + out.SetValue(sf::CreateSharedObjectEmplaced(this->client_info, std::make_unique(port)), target_object_id); return ResultSuccess(); } diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp index 53e7a0885..0b0b4471b 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp @@ -18,29 +18,25 @@ #include "uart_shim.h" +#define AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenPort, (sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length), (out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, OpenPortForDev, (sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length), (out, port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle, receive_handle, send_buffer_length, receive_buffer_length)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetWritableLength, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, Send, (sf::Out out_size, const sf::InAutoSelectBuffer &data), (out_size, data)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, GetReadableLength, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, Receive, (sf::Out out_size, const sf::OutAutoSelectBuffer &data), (out_size, data)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, BindPortEvent, (sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold), (out, out_event_handle, port_event_type, threshold)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, UnbindPortEvent, (sf::Out out, UartPortEventType port_event_type), (out, port_event_type)) + +AMS_SF_DEFINE_INTERFACE(ams::mitm::uart::impl, IPortSession, AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO) + +#define AMS_UART_MITM_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, CreatePortSession, (sf::Out> out), (out)) + +AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::uart::impl, IUartMitmInterface, AMS_UART_MITM_INTERFACE_INFO) + namespace ams::mitm::uart { - namespace impl { - - #define AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, OpenPort, (sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, OpenPortForDev, (sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, GetWritableLength, (sf::Out out)) \ - AMS_SF_METHOD_INFO(C, H, 3, Result, Send, (sf::Out out_size, const sf::InAutoSelectBuffer &data)) \ - AMS_SF_METHOD_INFO(C, H, 4, Result, GetReadableLength, (sf::Out out)) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, Receive, (sf::Out out_size, const sf::OutAutoSelectBuffer &data)) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, BindPortEvent, (sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold)) \ - AMS_SF_METHOD_INFO(C, H, 7, Result, UnbindPortEvent, (sf::Out out, UartPortEventType port_event_type)) \ - - AMS_SF_DEFINE_INTERFACE(IPortSession, AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO) - - #define AMS_UART_MITM_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, CreatePortSession, (sf::Out> out)) - - AMS_SF_DEFINE_MITM_INTERFACE(IUartMitmInterface, AMS_UART_MITM_INTERFACE_INFO) - - } - class UartPortService { private: sm::MitmProcessInfo client_info; @@ -101,7 +97,7 @@ namespace ams::mitm::uart { return client_info.program_id == ncm::SystemProgramId::Bluetooth; } public: - Result CreatePortSession(sf::Out> out); + Result CreatePortSession(sf::Out> out); }; static_assert(impl::IsIUartMitmInterface); diff --git a/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp index 297e91bbc..dfe75342f 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp @@ -22,6 +22,11 @@ namespace ams::mitm::uart { namespace { + enum PortIndex { + PortIndex_Mitm, + PortIndex_Count, + }; + constexpr sm::ServiceName UartMitmServiceName = sm::ServiceName::Encode("uart"); struct ServerOptions { @@ -33,7 +38,25 @@ namespace ams::mitm::uart { constexpr size_t MaxServers = 1; constexpr size_t MaxSessions = 10; - sf::hipc::ServerManager g_server_manager; + class ServerManager final : public sf::hipc::ServerManager { + private: + virtual Result OnNeedsToAccept(int port_index, Server *server) override; + }; + + ServerManager g_server_manager; + + Result ServerManager::OnNeedsToAccept(int port_index, Server *server) { + /* Acknowledge the mitm session. */ + std::shared_ptr<::Service> fsrv; + sm::MitmProcessInfo client_info; + server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info)); + + switch (port_index) { + case PortIndex_Mitm: + return this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced(decltype(fsrv)(fsrv), client_info), fsrv); + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } bool ShouldMitmUart() { u8 en = 0; @@ -59,7 +82,7 @@ namespace ams::mitm::uart { } /* Create mitm servers. */ - R_ABORT_UNLESS((g_server_manager.RegisterMitmServer(UartMitmServiceName))); + R_ABORT_UNLESS((g_server_manager.RegisterMitmServer(PortIndex_Mitm, UartMitmServiceName))); /* Loop forever, servicing our services. */ g_server_manager.LoopProcess(); From 1dd9d46415d6b31ebcfe84ed77865df197dcbc5d Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 18 Feb 2021 11:05:47 -0500 Subject: [PATCH 4/8] uart.mitm: Implemented fs writing with a seperate thread. --- .../source/uart_mitm/uart_mitm_logger.cpp | 252 ++++++++++++++++++ .../source/uart_mitm/uart_mitm_logger.hpp | 74 +++++ .../source/uart_mitm/uart_mitm_service.cpp | 149 ++++------- .../source/uart_mitm/uart_mitm_service.hpp | 37 +-- .../source/uart_mitm/uartmitm_module.cpp | 5 + 5 files changed, 398 insertions(+), 119 deletions(-) create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp create mode 100644 stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp new file mode 100644 index 000000000..34ff1dc9e --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "uart_mitm_logger.hpp" +#include "../amsmitm_fs_utils.hpp" + +namespace ams::mitm::uart { + + alignas(os::ThreadStackAlignment) u8 g_logger_stack[0x4000]; + + std::shared_ptr g_logger; + + UartLogger::UartLogger() : m_request_event(os::EventClearMode_ManualClear), m_finish_event(os::EventClearMode_ManualClear), m_client_queue(m_client_queue_list, this->QueueSize), m_thread_queue(m_thread_queue_list, this->QueueSize) { + for (size_t i=0; iQueueSize; i++) { + UartLogMessage *msg = &this->m_queue_list_msgs[i]; + std::memset(msg, 0, sizeof(UartLogMessage)); + + msg->data = static_cast(std::malloc(this->QueueBufferSize)); + if (msg->data != nullptr) { + std::memset(msg->data, 0, this->QueueBufferSize); + } + + this->m_client_queue.Send(reinterpret_cast(msg)); + } + + /* Create and start the logger thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(this->m_thread), this->ThreadEntry, this, g_logger_stack, sizeof(g_logger_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(uart, IpcServer) - 2)); + os::StartThread(std::addressof(this->m_thread)); + } + + UartLogger::~UartLogger() { + /* Tell the logger thread to exit. */ + UartLogMessage *msg=nullptr; + this->m_client_queue.Receive(reinterpret_cast(&msg)); + + msg->type = 0; + + this->m_finish_event.Clear(); + this->m_thread_queue.Send(reinterpret_cast(msg)); + this->m_request_event.Signal(); + + /* Wait on the logger thread, then destroy it. */ + os::WaitThread(std::addressof(this->m_thread)); + os::DestroyThread(std::addressof(this->m_thread)); + + for (size_t i=0; iQueueSize; i++) { + UartLogMessage *msg = &this->m_queue_list_msgs[i]; + std::free(msg->data); + msg->data = nullptr; + } + } + + void UartLogger::ThreadFunction() { + bool exit_flag=false; + + this->m_cache_count = 0; + this->m_cache_pos = 0; + std::memset(this->m_cache_list, 0, sizeof(this->m_cache_list)); + std::memset(this->m_cache_buffer, 0, sizeof(this->m_cache_buffer)); + + while (!exit_flag) { + this->m_request_event.Wait(); + + /* Receive messages, process them, then Send them. */ + UartLogMessage *msg=nullptr; + while (this->m_thread_queue.TryReceive(reinterpret_cast(&msg))) { + if (msg->type==0) { + exit_flag = true; + } + else if (msg->type==1) { + this->WriteCache(msg); + } + else if (msg->type==2) { + this->WriteCmdLog(reinterpret_cast(msg->data), reinterpret_cast(&msg->data[std::strlen((const char*)msg->data)+1]), msg->file_pos); + } + else if (msg->type==3) { + this->FlushCache(); + } + this->m_client_queue.Send(reinterpret_cast(msg)); + } + + this->m_request_event.Clear(); + this->m_finish_event.Signal(); + } + } + + /* Wait for the thread to finish processing messages. */ + void UartLogger::WaitFinished() { + /* Tell the thread to flush the cache. */ + UartLogMessage *msg=nullptr; + this->m_client_queue.Receive(reinterpret_cast(&msg)); + + msg->type = 3; + + this->m_finish_event.Clear(); + this->m_thread_queue.Send(reinterpret_cast(msg)); + this->m_request_event.Signal(); + + /* Wait for processing to finish. */ + m_finish_event.Wait(); + } + + /* Initialize the specified btsnoop log file. */ + void UartLogger::InitializeDataLog(FsFile *f, size_t *datalog_pos) { + *datalog_pos = 0; + + /* Setup the btsnoop header. */ + + struct { + char id[8]; + u32 version; + u32 datalink_type; + } btsnoop_header = { .id = "btsnoop" }; + + u32 version = 1; + u32 datalink_type = 1002; /* HCI UART (H4) */ + ams::util::StoreBigEndian(&btsnoop_header.version, version); + ams::util::StoreBigEndian(&btsnoop_header.datalink_type, datalink_type); + + /* Write the btsnoop header to the datalog. */ + this->WriteLog(f, datalog_pos, &btsnoop_header, sizeof(btsnoop_header)); + } + + /* Flush the cache into the file. */ + void UartLogger::FlushCache() { + for (size_t i=0; im_cache_count; i++) { + UartLogMessage *cache_msg=&this->m_cache_list[i]; + this->WriteLogPacket(cache_msg->datalog_file, cache_msg->file_pos, cache_msg->dir, cache_msg->data, cache_msg->size); + } + + this->m_cache_count = 0; + this->m_cache_pos = 0; + } + + /* Write the specified message into the cache. */ + /* dir: false = Send (host->controller), true = Receive (controller->host). */ + void UartLogger::WriteCache(UartLogMessage *msg) { + if (this->m_cache_count >= this->CacheListSize || this->m_cache_pos + msg->size >= this->CacheBufferSize) { + this->FlushCache(); + } + + UartLogMessage *cache_msg=&this->m_cache_list[this->m_cache_count]; + *cache_msg = *msg; + cache_msg->data = &this->m_cache_buffer[this->m_cache_pos]; + std::memcpy(cache_msg->data, msg->data, msg->size); + this->m_cache_count++; + this->m_cache_pos+= msg->size; + } + + /* Append the specified string to the text file. */ + void UartLogger::WriteCmdLog(const char *path, const char *str, size_t *file_pos) { + Result rc=0; + FsFile file={}; + size_t len = std::strlen(str); + rc = ams::mitm::fs::OpenAtmosphereSdFile(&file, path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + if (R_SUCCEEDED(rc)) { + rc = fsFileWrite(&file, *file_pos, str, len, FsWriteOption_None); + } + if (R_SUCCEEDED(rc)) { + *file_pos += len; + } + fsFileClose(&file); + } + + /* Append the specified data to the datalog file. */ + void UartLogger::WriteLog(FsFile *f, size_t *datalog_pos, const void* buffer, size_t size) { + if (R_SUCCEEDED(fsFileWrite(f, *datalog_pos, buffer, size, FsWriteOption_None))) { + *datalog_pos += size; + } + } + + /* Append the specified packet to the datalog via WriteLog. */ + /* dir: false = Send (host->controller), true = Receive (controller->host). */ + void UartLogger::WriteLogPacket(FsFile *f, size_t *datalog_pos, bool dir, const void* buffer, size_t size) { + struct { + u32 original_length; + u32 included_length; + u32 packet_flags; + u32 cumulative_drops; + s64 timestamp_microseconds; + } pkt_hdr = {}; + + u32 flags = 0; + if (dir) { + flags |= BIT(0); + } + ams::util::StoreBigEndian(&pkt_hdr.original_length, static_cast(size)); + ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast(size)); + ams::util::StoreBigEndian(&pkt_hdr.packet_flags, flags); + + /* Currently we leave the timestamp at value 0. */ + + this->WriteLog(f, datalog_pos, &pkt_hdr, sizeof(pkt_hdr)); + this->WriteLog(f, datalog_pos, buffer, size); + } + + /* Send the specified data to the Logger thread. */ + /* dir: false = Send (host->controller), true = Receive (controller->host). */ + void UartLogger::SendLogData(FsFile *f, size_t *file_pos, bool dir, const void* buffer, size_t size) { + /* Ignore log data which is too large. */ + if (size > this->QueueBufferSize) return; + + UartLogMessage *msg=nullptr; + this->m_client_queue.Receive(reinterpret_cast(&msg)); + if (msg->data == nullptr) return; + + /* Setup the msg and send it. */ + msg->type = 1; + msg->dir = dir; + msg->datalog_file = f; + msg->file_pos = file_pos; + msg->size = size; + std::memcpy(msg->data, buffer, size); + + this->m_finish_event.Clear(); + this->m_thread_queue.Send(reinterpret_cast(msg)); + this->m_request_event.Signal(); + } + + /* Send the specified text log to the Logger thread. */ + void UartLogger::SendTextLogData(const char *path, size_t *file_pos, const char *str) { + /* Ignore log data which is too large. */ + if (std::strlen(path)+1 + std::strlen(str)+1 > this->QueueBufferSize) return; + + UartLogMessage *msg=nullptr; + this->m_client_queue.Receive(reinterpret_cast(&msg)); + if (msg->data == nullptr) return; + + /* Setup the msg and send it. */ + msg->type = 2; + msg->file_pos = file_pos; + std::memcpy(msg->data, path, std::strlen(path)+1); + std::memcpy(&msg->data[std::strlen(path)+1], str, std::strlen(str)+1); + + this->m_finish_event.Clear(); + this->m_thread_queue.Send(reinterpret_cast(msg)); + this->m_request_event.Signal(); + } +} diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp new file mode 100644 index 000000000..db52c265f --- /dev/null +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::mitm::uart { + + struct UartLogMessage { + u8 type; + bool dir; + FsFile *datalog_file; + size_t *file_pos; + size_t size; + u8 *data; + }; + + class UartLogger { + private: + ams::os::ThreadType m_thread; + os::Event m_request_event; + os::Event m_finish_event; + os::MessageQueue m_client_queue; + os::MessageQueue m_thread_queue; + + static constexpr inline size_t QueueSize = 0x20; + static constexpr inline size_t QueueBufferSize = 0x400; + static constexpr inline size_t CacheListSize = 0x80; + static constexpr inline size_t CacheBufferSize = 0x1000; + + uintptr_t m_client_queue_list[QueueSize]; + uintptr_t m_thread_queue_list[QueueSize]; + UartLogMessage m_queue_list_msgs[QueueSize]; + + size_t m_cache_count; + size_t m_cache_pos; + UartLogMessage m_cache_list[CacheListSize]; + u8 m_cache_buffer[CacheBufferSize]; + + static void ThreadEntry(void *arg) { static_cast(arg)->ThreadFunction(); } + void ThreadFunction(); + + void FlushCache(); + void WriteCache(UartLogMessage *msg); + + void WriteCmdLog(const char *path, const char *str, size_t *file_pos); + void WriteLog(FsFile *f, size_t *datalog_pos, const void* buffer, size_t size); + void WriteLogPacket(FsFile *f, size_t *datalog_pos, bool dir, const void* buffer, size_t size); + public: + UartLogger(); + ~UartLogger(); + + void WaitFinished(); + + void InitializeDataLog(FsFile *f, size_t *datalog_pos); + + void SendLogData(FsFile *f, size_t *file_pos, bool dir, const void* buffer, size_t size); + void SendTextLogData(const char *path, size_t *file_pos, const char *str); + }; + + extern std::shared_ptr g_logger; +} diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp index 14a165feb..1fb3a5773 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -15,10 +15,9 @@ */ #include #include "uart_mitm_service.hpp" +#include "uart_mitm_logger.hpp" #include "../amsmitm_fs_utils.hpp" -/* TODO: This should really use async fs-writing, there's a slowdown with bluetooth communications with current fs-writing. */ - namespace ams::mitm::uart { /* Helper functions. */ @@ -41,7 +40,7 @@ namespace ams::mitm::uart { } } - UartPortService::UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s) : client_info(cl), srv(std::move(s)) { + UartPortService::UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s) : m_client_info(cl), m_srv(std::move(s)) { Result rc=0; /* Get a timestamp. */ u64 timestamp0=0, timestamp1; @@ -49,122 +48,66 @@ namespace ams::mitm::uart { timestamp1 = svcGetSystemTick(); /* Setup/create the logging directory. */ - std::snprintf(this->base_path, sizeof(this->base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast(this->client_info.program_id)); + std::snprintf(this->m_base_path, sizeof(this->m_base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast(this->m_client_info.program_id)); ams::mitm::fs::CreateAtmosphereSdDirectory("uart_logs"); - ams::mitm::fs::CreateAtmosphereSdDirectory(this->base_path); + ams::mitm::fs::CreateAtmosphereSdDirectory(this->m_base_path); /* Create/initialize the text cmd_log. */ char tmp_path[256]; - std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "cmd_log"); + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "cmd_log"); ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); - this->cmdlog_pos = 0; + this->m_cmdlog_pos = 0; /* Initialize the Send cache-buffer. */ - this->send_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); - if (this->send_cache_buffer != nullptr) { - std::memset(this->send_cache_buffer, 0, this->CacheBufferSize); + this->m_send_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); + if (this->m_send_cache_buffer != nullptr) { + std::memset(this->m_send_cache_buffer, 0, this->CacheBufferSize); } - this->send_cache_pos = 0; + this->m_send_cache_pos = 0; /* Initialize the Receive cache-buffer. */ - this->receive_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); - if (this->receive_cache_buffer != nullptr) { - std::memset(this->receive_cache_buffer, 0, this->CacheBufferSize); + this->m_receive_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); + if (this->m_receive_cache_buffer != nullptr) { + std::memset(this->m_receive_cache_buffer, 0, this->CacheBufferSize); } - this->receive_cache_pos = 0; + this->m_receive_cache_pos = 0; + + this->m_datalog_ready = false; /* When the above is successful, initialize the datalog. */ - if (this->send_cache_buffer != nullptr && this->receive_cache_buffer != nullptr) { - std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "btsnoop_hci.log"); + if (this->m_send_cache_buffer != nullptr && this->m_receive_cache_buffer != nullptr) { + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "btsnoop_hci.log"); ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); - rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->m_datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); /* Set datalog_ready to whether initialization was successful. */ - this->datalog_ready = R_SUCCEEDED(rc); + this->m_datalog_ready = R_SUCCEEDED(rc); } - this->datalog_pos = 0; - /* Setup the btsnoop header. */ + if (this->m_datalog_ready) { + std::shared_ptr logger = mitm::uart::g_logger; + logger->InitializeDataLog(&this->m_datalog_file, &this->m_datalog_pos); + } - struct { - char id[8]; - u32 version; - u32 datalink_type; - } btsnoop_header = { .id = "btsnoop" }; - - u32 version = 1; - u32 datalink_type = 1002; /* HCI UART (H4) */ - ams::util::StoreBigEndian(&btsnoop_header.version, version); - ams::util::StoreBigEndian(&btsnoop_header.datalink_type, datalink_type); - - /* Enable data-logging, required for WriteLog() to write anything. */ - this->data_logging_enabled = true; - - /* Write the btsnoop header to the datalog. */ - this->WriteLog(&btsnoop_header, sizeof(btsnoop_header)); - - /* This will be re-enabled by WriteUartData once a certain command is detected. */ - /* If you want to log all HCI traffic during system-boot initialization, you can comment out the below line, however there will be a slowdown. */ - this->data_logging_enabled = false; + /* This will be enabled by WriteUartData once a certain command is detected. */ + /* If you want to log all HCI traffic during system-boot initialization, you can change this field to true. */ + /* When changed to true, qlaunch will hang at a black-screen during system-boot, due to the bluetooth slowdown. */ + this->m_data_logging_enabled = false; } /* Append the specified string to the text cmd_log file. */ void UartPortService::WriteCmdLog(const char *str) { - Result rc=0; - FsFile file={}; char tmp_path[256]; - size_t len = strlen(str); - std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->base_path, "cmd_log"); - rc = ams::mitm::fs::OpenAtmosphereSdFile(&file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); - if (R_SUCCEEDED(rc)) { - rc = fsFileWrite(&file, this->cmdlog_pos, str, len, FsWriteOption_None); - } - if (R_SUCCEEDED(rc)) { - this->cmdlog_pos += len; - } - fsFileClose(&file); - } - - /* Append the specified data to the datalog file. */ - void UartPortService::WriteLog(const void* buffer, size_t size) { - /* Only write to the file if data-logging is enabled and initialized. */ - if (this->data_logging_enabled && this->datalog_ready) { - if (R_SUCCEEDED(fsFileWrite(&this->datalog_file, this->datalog_pos, buffer, size, FsWriteOption_None))) { - this->datalog_pos += size; - } - } - } - - /* Append the specified packet to the datalog via WriteLog. */ - /* dir: false = Send (host->controller), true = Receive (controller->host). */ - void UartPortService::WriteLogPacket(bool dir, const void* buffer, size_t size) { - struct { - u32 original_length; - u32 included_length; - u32 packet_flags; - u32 cumulative_drops; - s64 timestamp_microseconds; - } pkt_hdr = {}; - - u32 flags = 0; - if (dir) { - flags |= BIT(0); - } - ams::util::StoreBigEndian(&pkt_hdr.original_length, static_cast(size)); - ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast(size)); - ams::util::StoreBigEndian(&pkt_hdr.packet_flags, flags); - - /* Currently we leave the timestamp at value 0. */ - - this->WriteLog(&pkt_hdr, sizeof(pkt_hdr)); - this->WriteLog(buffer, size); + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "cmd_log"); + std::shared_ptr logger = mitm::uart::g_logger; + logger->SendTextLogData(tmp_path, &this->m_cmdlog_pos, str); } /* Log data from Send/Receive. */ /* dir: false = Send (host->controller), true = Receive (controller->host). */ void UartPortService::WriteUartData(bool dir, const void* buffer, size_t size) { /* Select which cache buffer/pos to use via dir. */ - u8 *cache_buffer = !dir ? this->send_cache_buffer : this->receive_cache_buffer; - size_t *cache_pos = !dir ? &this->send_cache_pos : &this->receive_cache_pos; + u8 *cache_buffer = !dir ? this->m_send_cache_buffer : this->m_receive_cache_buffer; + size_t *cache_pos = !dir ? &this->m_send_cache_pos : &this->m_receive_cache_pos; /* Verify that the input size is non-zero, and within cache buffer bounds. */ if (size && *cache_pos + size <= this->CacheBufferSize) { @@ -217,8 +160,8 @@ namespace ams::mitm::uart { /* Check for the first command used in the port which is opened last by bluetooth-sysmodule. */ /* This is a vendor command. */ /* Once detected, data-logging will be enabled. */ - if (!this->data_logging_enabled && hci_cmd->opcode[1] == 0xFC && hci_cmd->opcode[0] == 0x16) { - this->data_logging_enabled = true; + if (!this->m_data_logging_enabled && hci_cmd->opcode[1] == 0xFC && hci_cmd->opcode[0] == 0x16) { + this->m_data_logging_enabled = true; } } } @@ -258,7 +201,11 @@ namespace ams::mitm::uart { /* If a packet is available, log it and update the cache. */ if (pkt_len>0x1) { - this->WriteLogPacket(dir, cache_buffer, pkt_len); + /* Only write to the file if data-logging is enabled and initialized. */ + if (this->m_data_logging_enabled && this->m_datalog_ready) { + std::shared_ptr logger = mitm::uart::g_logger; + logger->SendLogData(&this->m_datalog_file, &this->m_datalog_pos, dir, cache_buffer, pkt_len); + } (*cache_pos)-= pkt_len; if (*cache_pos) { std::memmove(cache_buffer, &cache_buffer[pkt_len], *cache_pos); @@ -272,7 +219,7 @@ namespace ams::mitm::uart { /* Forward OpenPort and write to the cmd_log. */ Result UartPortService::OpenPort(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { - Result rc = uartPortSessionOpenPortFwd(this->srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); + Result rc = uartPortSessionOpenPortFwd(this->m_srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); svcCloseHandle(send_handle.GetValue()); svcCloseHandle(receive_handle.GetValue()); @@ -284,7 +231,7 @@ namespace ams::mitm::uart { /* Forward OpenPortForDev and write to the cmd_log. */ Result UartPortService::OpenPortForDev(sf::Out out, u32 port, u32 baud_rate, UartFlowControlMode flow_control_mode, u32 device_variation, bool is_invert_tx, bool is_invert_rx, bool is_invert_rts, bool is_invert_cts, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) { - Result rc = uartPortSessionOpenPortForDevFwd(this->srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); + Result rc = uartPortSessionOpenPortForDevFwd(this->m_srv.get(), reinterpret_cast(out.GetPointer()), port, baud_rate, flow_control_mode, device_variation, is_invert_tx, is_invert_rx, is_invert_rts, is_invert_cts, send_handle.GetValue(), receive_handle.GetValue(), send_buffer_length, receive_buffer_length); svcCloseHandle(send_handle.GetValue()); svcCloseHandle(receive_handle.GetValue()); @@ -296,7 +243,7 @@ namespace ams::mitm::uart { /* Forward GetWritableLength and write to the cmd_log. */ Result UartPortService::GetWritableLength(sf::Out out) { - Result rc = uartPortSessionGetWritableLength(this->srv.get(), reinterpret_cast(out.GetPointer())); + Result rc = uartPortSessionGetWritableLength(this->m_srv.get(), reinterpret_cast(out.GetPointer())); char str[256]; std::snprintf(str, sizeof(str), "GetWritableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue()); @@ -307,7 +254,7 @@ namespace ams::mitm::uart { /* Forward Send and log the data if the out_size is non-zero. */ Result UartPortService::Send(sf::Out out_size, const sf::InAutoSelectBuffer &data) { - Result rc = uartPortSessionSend(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); + Result rc = uartPortSessionSend(this->m_srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); if (R_SUCCEEDED(rc) && out_size.GetValue()) { this->WriteUartData(false, data.GetPointer(), out_size.GetValue()); @@ -317,7 +264,7 @@ namespace ams::mitm::uart { /* Forward GetReadableLength and write to the cmd_log. */ Result UartPortService::GetReadableLength(sf::Out out) { - Result rc = uartPortSessionGetReadableLength(this->srv.get(), reinterpret_cast(out.GetPointer())); + Result rc = uartPortSessionGetReadableLength(this->m_srv.get(), reinterpret_cast(out.GetPointer())); char str[256]; std::snprintf(str, sizeof(str), "GetReadableLength(): rc = 0x%x, out = 0x%lx\n", rc.GetValue(), out.GetValue()); @@ -328,7 +275,7 @@ namespace ams::mitm::uart { /* Forward Receive and log the data if the out_size is non-zero. */ Result UartPortService::Receive(sf::Out out_size, const sf::OutAutoSelectBuffer &data) { - Result rc = uartPortSessionReceive(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); + Result rc = uartPortSessionReceive(this->m_srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast(out_size.GetPointer())); if (R_SUCCEEDED(rc) && out_size.GetValue()) { this->WriteUartData(true, data.GetPointer(), out_size.GetValue()); @@ -338,7 +285,7 @@ namespace ams::mitm::uart { /* Forward BindPortEvent and write to the cmd_log. */ Result UartPortService::BindPortEvent(sf::Out out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold) { - Result rc = uartPortSessionBindPortEventFwd(this->srv.get(), port_event_type, threshold, reinterpret_cast(out.GetPointer()), out_event_handle.GetHandlePointer()); + Result rc = uartPortSessionBindPortEventFwd(this->m_srv.get(), port_event_type, threshold, reinterpret_cast(out.GetPointer()), out_event_handle.GetHandlePointer()); char str[256]; std::snprintf(str, sizeof(str), "BindPortEvent(port_event_type = 0x%x, threshold = 0x%lx): rc = 0x%x, out = %d\n", port_event_type, threshold, rc.GetValue(), out.GetValue()); @@ -348,7 +295,7 @@ namespace ams::mitm::uart { /* Forward UnbindPortEvent and write to the cmd_log. */ Result UartPortService::UnbindPortEvent(sf::Out out, UartPortEventType port_event_type) { - Result rc = uartPortSessionUnbindPortEvent(this->srv.get(), port_event_type, reinterpret_cast(out.GetPointer())); + Result rc = uartPortSessionUnbindPortEvent(this->m_srv.get(), port_event_type, reinterpret_cast(out.GetPointer())); char str[256]; std::snprintf(str, sizeof(str), "UnbindPortEvent(port_event_type = 0x%x): rc = 0x%x, out = %d\n", port_event_type, rc.GetValue(), out.GetValue()); diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp index 0b0b4471b..2a9c0917d 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp @@ -16,6 +16,7 @@ #pragma once #include +#include "uart_mitm_logger.hpp" #include "uart_shim.h" #define AMS_UART_IPORTSESSION_MITM_INTERFACE_INFO(C, H) \ @@ -39,39 +40,39 @@ namespace ams::mitm::uart { class UartPortService { private: - sm::MitmProcessInfo client_info; - std::unique_ptr<::UartPortSession> srv; + sm::MitmProcessInfo m_client_info; + std::unique_ptr<::UartPortSession> m_srv; static constexpr inline size_t CacheBufferSize = 0x1000; - char base_path[256]; + char m_base_path[256]; - size_t cmdlog_pos; - size_t datalog_pos; + size_t m_cmdlog_pos; + size_t m_datalog_pos; - bool datalog_ready; - bool data_logging_enabled; + bool m_datalog_ready; + bool m_data_logging_enabled; - FsFile datalog_file; + FsFile m_datalog_file; - u8 *send_cache_buffer; - u8 *receive_cache_buffer; - size_t send_cache_pos; - size_t receive_cache_pos; + u8 *m_send_cache_buffer; + u8 *m_receive_cache_buffer; + size_t m_send_cache_pos; + size_t m_receive_cache_pos; bool TryGetCurrentTimestamp(u64 *out); void WriteCmdLog(const char *str); - void WriteLog(const void* buffer, size_t size); - void WriteLogPacket(bool dir, const void* buffer, size_t size); void WriteUartData(bool dir, const void* buffer, size_t size); public: UartPortService(const sm::MitmProcessInfo &cl, std::unique_ptr<::UartPortSession> s); virtual ~UartPortService() { - uartPortSessionClose(this->srv.get()); - fsFileClose(&this->datalog_file); - std::free(this->send_cache_buffer); - std::free(this->receive_cache_buffer); + std::shared_ptr logger = mitm::uart::g_logger; + logger->WaitFinished(); + uartPortSessionClose(this->m_srv.get()); + fsFileClose(&this->m_datalog_file); + std::free(this->m_send_cache_buffer); + std::free(this->m_receive_cache_buffer); } public: /* Actual command API. */ diff --git a/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp index dfe75342f..4229f6e3b 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uartmitm_module.cpp @@ -17,6 +17,7 @@ #include "../amsmitm_initialization.hpp" #include "uartmitm_module.hpp" #include "uart_mitm_service.hpp" +#include "uart_mitm_logger.hpp" namespace ams::mitm::uart { @@ -84,8 +85,12 @@ namespace ams::mitm::uart { /* Create mitm servers. */ R_ABORT_UNLESS((g_server_manager.RegisterMitmServer(PortIndex_Mitm, UartMitmServiceName))); + mitm::uart::g_logger = std::make_shared(); + /* Loop forever, servicing our services. */ g_server_manager.LoopProcess(); + + mitm::uart::g_logger.reset(); } } From 296fb31358353c149f058e149a89d759943ba249 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 18 Feb 2021 14:15:53 -0500 Subject: [PATCH 5/8] uart.mitm: Implemented btsnoop timestamp handling. --- .../source/uart_mitm/uart_mitm_logger.cpp | 15 ++++++++++----- .../source/uart_mitm/uart_mitm_logger.hpp | 5 +++-- .../source/uart_mitm/uart_mitm_service.cpp | 11 +++++++++-- .../source/uart_mitm/uart_mitm_service.hpp | 3 +++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp index 34ff1dc9e..9341c8cac 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp @@ -138,7 +138,7 @@ namespace ams::mitm::uart { void UartLogger::FlushCache() { for (size_t i=0; im_cache_count; i++) { UartLogMessage *cache_msg=&this->m_cache_list[i]; - this->WriteLogPacket(cache_msg->datalog_file, cache_msg->file_pos, cache_msg->dir, cache_msg->data, cache_msg->size); + this->WriteLogPacket(cache_msg->datalog_file, cache_msg->file_pos, cache_msg->timestamp, cache_msg->dir, cache_msg->data, cache_msg->size); } this->m_cache_count = 0; @@ -184,7 +184,7 @@ namespace ams::mitm::uart { /* Append the specified packet to the datalog via WriteLog. */ /* dir: false = Send (host->controller), true = Receive (controller->host). */ - void UartLogger::WriteLogPacket(FsFile *f, size_t *datalog_pos, bool dir, const void* buffer, size_t size) { + void UartLogger::WriteLogPacket(FsFile *f, size_t *datalog_pos, s64 timestamp, bool dir, const void* buffer, size_t size) { struct { u32 original_length; u32 included_length; @@ -200,8 +200,7 @@ namespace ams::mitm::uart { ams::util::StoreBigEndian(&pkt_hdr.original_length, static_cast(size)); ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast(size)); ams::util::StoreBigEndian(&pkt_hdr.packet_flags, flags); - - /* Currently we leave the timestamp at value 0. */ + ams::util::StoreBigEndian(&pkt_hdr.timestamp_microseconds, timestamp); this->WriteLog(f, datalog_pos, &pkt_hdr, sizeof(pkt_hdr)); this->WriteLog(f, datalog_pos, buffer, size); @@ -209,7 +208,7 @@ namespace ams::mitm::uart { /* Send the specified data to the Logger thread. */ /* dir: false = Send (host->controller), true = Receive (controller->host). */ - void UartLogger::SendLogData(FsFile *f, size_t *file_pos, bool dir, const void* buffer, size_t size) { + void UartLogger::SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size) { /* Ignore log data which is too large. */ if (size > this->QueueBufferSize) return; @@ -220,6 +219,12 @@ namespace ams::mitm::uart { /* Setup the msg and send it. */ msg->type = 1; msg->dir = dir; + if (timestamp_base) { + msg->timestamp = (armTicksToNs(armGetSystemTick() - tick_base) / 1000) + timestamp_base; + } + else { + msg->timestamp = 0; + } msg->datalog_file = f; msg->file_pos = file_pos; msg->size = size; diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp index db52c265f..4aea59fa5 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp @@ -21,6 +21,7 @@ namespace ams::mitm::uart { struct UartLogMessage { u8 type; bool dir; + s64 timestamp; FsFile *datalog_file; size_t *file_pos; size_t size; @@ -57,7 +58,7 @@ namespace ams::mitm::uart { void WriteCmdLog(const char *path, const char *str, size_t *file_pos); void WriteLog(FsFile *f, size_t *datalog_pos, const void* buffer, size_t size); - void WriteLogPacket(FsFile *f, size_t *datalog_pos, bool dir, const void* buffer, size_t size); + void WriteLogPacket(FsFile *f, size_t *datalog_pos, s64 timestamp, bool dir, const void* buffer, size_t size); public: UartLogger(); ~UartLogger(); @@ -66,7 +67,7 @@ namespace ams::mitm::uart { void InitializeDataLog(FsFile *f, size_t *datalog_pos); - void SendLogData(FsFile *f, size_t *file_pos, bool dir, const void* buffer, size_t size); + void SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size); void SendTextLogData(const char *path, size_t *file_pos, const char *str); }; diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp index 1fb3a5773..776c8e4a0 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -45,7 +45,14 @@ namespace ams::mitm::uart { /* Get a timestamp. */ u64 timestamp0=0, timestamp1; this->TryGetCurrentTimestamp(×tamp0); - timestamp1 = svcGetSystemTick(); + timestamp1 = armGetSystemTick(); + + /* Setup the btsnoop base timestamps. */ + this->m_timestamp_base = timestamp0; + if (this->m_timestamp_base) { + this->m_timestamp_base = 0x00E03AB44A676000 + (this->m_timestamp_base - 946684800) * 1000000; + } + this->m_tick_base = timestamp1; /* Setup/create the logging directory. */ std::snprintf(this->m_base_path, sizeof(this->m_base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast(this->m_client_info.program_id)); @@ -204,7 +211,7 @@ namespace ams::mitm::uart { /* Only write to the file if data-logging is enabled and initialized. */ if (this->m_data_logging_enabled && this->m_datalog_ready) { std::shared_ptr logger = mitm::uart::g_logger; - logger->SendLogData(&this->m_datalog_file, &this->m_datalog_pos, dir, cache_buffer, pkt_len); + logger->SendLogData(&this->m_datalog_file, &this->m_datalog_pos, this->m_timestamp_base, this->m_tick_base, dir, cache_buffer, pkt_len); } (*cache_pos)-= pkt_len; if (*cache_pos) { diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp index 2a9c0917d..5291dcd30 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.hpp @@ -45,6 +45,9 @@ namespace ams::mitm::uart { static constexpr inline size_t CacheBufferSize = 0x1000; + s64 m_timestamp_base; + s64 m_tick_base; + char m_base_path[256]; size_t m_cmdlog_pos; From 4cb8034ac8e116e7e45a892d25bee8352cdf0045 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 18 Feb 2021 14:36:21 -0500 Subject: [PATCH 6/8] uart.mitm: Log when the data for SendLogData is too large. --- .../ams_mitm/source/uart_mitm/uart_mitm_logger.cpp | 7 ++++--- .../ams_mitm/source/uart_mitm/uart_mitm_logger.hpp | 2 +- .../ams_mitm/source/uart_mitm/uart_mitm_service.cpp | 6 +++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp index 9341c8cac..7eaac88d9 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp @@ -208,13 +208,13 @@ namespace ams::mitm::uart { /* Send the specified data to the Logger thread. */ /* dir: false = Send (host->controller), true = Receive (controller->host). */ - void UartLogger::SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size) { + bool UartLogger::SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size) { /* Ignore log data which is too large. */ - if (size > this->QueueBufferSize) return; + if (size > this->QueueBufferSize) return false; UartLogMessage *msg=nullptr; this->m_client_queue.Receive(reinterpret_cast(&msg)); - if (msg->data == nullptr) return; + if (msg->data == nullptr) return true; /* Setup the msg and send it. */ msg->type = 1; @@ -233,6 +233,7 @@ namespace ams::mitm::uart { this->m_finish_event.Clear(); this->m_thread_queue.Send(reinterpret_cast(msg)); this->m_request_event.Signal(); + return true; } /* Send the specified text log to the Logger thread. */ diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp index 4aea59fa5..138010af5 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.hpp @@ -67,7 +67,7 @@ namespace ams::mitm::uart { void InitializeDataLog(FsFile *f, size_t *datalog_pos); - void SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size); + bool SendLogData(FsFile *f, size_t *file_pos, s64 timestamp_base, s64 tick_base, bool dir, const void* buffer, size_t size); void SendTextLogData(const char *path, size_t *file_pos, const char *str); }; diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp index 776c8e4a0..a357b4bf2 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -211,7 +211,11 @@ namespace ams::mitm::uart { /* Only write to the file if data-logging is enabled and initialized. */ if (this->m_data_logging_enabled && this->m_datalog_ready) { std::shared_ptr logger = mitm::uart::g_logger; - logger->SendLogData(&this->m_datalog_file, &this->m_datalog_pos, this->m_timestamp_base, this->m_tick_base, dir, cache_buffer, pkt_len); + if (!logger->SendLogData(&this->m_datalog_file, &this->m_datalog_pos, this->m_timestamp_base, this->m_tick_base, dir, cache_buffer, pkt_len)) { + char str[256]; + std::snprintf(str, sizeof(str), "WriteUartData(): SendLogData dropped packet with size = 0x%lx\n", pkt_len); + this->WriteCmdLog(str); + } } (*cache_pos)-= pkt_len; if (*cache_pos) { From 8a9ddc30e0c0e5938ed5fe73306522db3046e45b Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 18 Feb 2021 19:25:59 -0500 Subject: [PATCH 7/8] uart.mitm: Abort on memalloc failure and adjust stack size. --- .../source/uart_mitm/uart_mitm_logger.cpp | 11 ++++---- .../source/uart_mitm/uart_mitm_service.cpp | 26 +++++++------------ 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp index 7eaac88d9..1bf444809 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_logger.cpp @@ -19,7 +19,7 @@ namespace ams::mitm::uart { - alignas(os::ThreadStackAlignment) u8 g_logger_stack[0x4000]; + alignas(os::ThreadStackAlignment) u8 g_logger_stack[0x1000]; std::shared_ptr g_logger; @@ -29,9 +29,8 @@ namespace ams::mitm::uart { std::memset(msg, 0, sizeof(UartLogMessage)); msg->data = static_cast(std::malloc(this->QueueBufferSize)); - if (msg->data != nullptr) { - std::memset(msg->data, 0, this->QueueBufferSize); - } + AMS_ABORT_UNLESS(msg->data != nullptr); + std::memset(msg->data, 0, this->QueueBufferSize); this->m_client_queue.Send(reinterpret_cast(msg)); } @@ -214,7 +213,7 @@ namespace ams::mitm::uart { UartLogMessage *msg=nullptr; this->m_client_queue.Receive(reinterpret_cast(&msg)); - if (msg->data == nullptr) return true; + AMS_ABORT_UNLESS(msg->data != nullptr); /* Setup the msg and send it. */ msg->type = 1; @@ -243,7 +242,7 @@ namespace ams::mitm::uart { UartLogMessage *msg=nullptr; this->m_client_queue.Receive(reinterpret_cast(&msg)); - if (msg->data == nullptr) return; + AMS_ABORT_UNLESS(msg->data != nullptr); /* Setup the msg and send it. */ msg->type = 2; diff --git a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp index a357b4bf2..323a8076b 100644 --- a/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/uart_mitm/uart_mitm_service.cpp @@ -67,28 +67,22 @@ namespace ams::mitm::uart { /* Initialize the Send cache-buffer. */ this->m_send_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); - if (this->m_send_cache_buffer != nullptr) { - std::memset(this->m_send_cache_buffer, 0, this->CacheBufferSize); - } + AMS_ABORT_UNLESS(this->m_send_cache_buffer != nullptr); + std::memset(this->m_send_cache_buffer, 0, this->CacheBufferSize); this->m_send_cache_pos = 0; /* Initialize the Receive cache-buffer. */ this->m_receive_cache_buffer = static_cast(std::malloc(this->CacheBufferSize)); - if (this->m_receive_cache_buffer != nullptr) { - std::memset(this->m_receive_cache_buffer, 0, this->CacheBufferSize); - } + AMS_ABORT_UNLESS(this->m_receive_cache_buffer != nullptr); + std::memset(this->m_receive_cache_buffer, 0, this->CacheBufferSize); this->m_receive_cache_pos = 0; - this->m_datalog_ready = false; - - /* When the above is successful, initialize the datalog. */ - if (this->m_send_cache_buffer != nullptr && this->m_receive_cache_buffer != nullptr) { - std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "btsnoop_hci.log"); - ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); - rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->m_datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); - /* Set datalog_ready to whether initialization was successful. */ - this->m_datalog_ready = R_SUCCEEDED(rc); - } + /* Initialize the datalog. */ + std::snprintf(tmp_path, sizeof(tmp_path), "%s/%s", this->m_base_path, "btsnoop_hci.log"); + ams::mitm::fs::CreateAtmosphereSdFile(tmp_path, 0, 0); + rc = ams::mitm::fs::OpenAtmosphereSdFile(&this->m_datalog_file, tmp_path, FsOpenMode_Read | FsOpenMode_Write | FsOpenMode_Append); + /* Set datalog_ready to whether initialization was successful. */ + this->m_datalog_ready = R_SUCCEEDED(rc); if (this->m_datalog_ready) { std::shared_ptr logger = mitm::uart::g_logger; From c9dd93687bbc2838150c0d965d8ada3c5cfea0c1 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Fri, 19 Feb 2021 15:05:06 -0500 Subject: [PATCH 8/8] docs: Added uart_mitm. --- docs/components/modules/ams_mitm.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/components/modules/ams_mitm.md b/docs/components/modules/ams_mitm.md index 743f45426..f212ca968 100644 --- a/docs/components/modules/ams_mitm.md +++ b/docs/components/modules/ams_mitm.md @@ -38,3 +38,14 @@ It does so in order to enable user configuration of system settings, which are p dns_mitm enables intercepting requests to dns resolution services, to enable redirecting requests for specified hostnames. For documentation, see [here](../../features/dns_mitm.md). + +## uart_mitm +`uart_mitm` intercepts the uart service used by bluetooth, on 7.0.0+ when enabled by [system_settings.ini](../../features/configurations.md). This allows logging bluetooth traffic. + +Usage of bluetooth devices will be less responsive when this is enabled. + +Logs are written to directory `/atmosphere/uart_logs/{PosixTime}_{TickTimestamp}_{ProgramId}`, which then contains the following: ++ `cmd_log` Text log for uart IPortSession commands, and any warning messages. ++ `btsnoop_hci.log` Bluetooth HCI log in the btsnoop format. This file is not accessible while it's the current log being used with HOS running. + +4 directories are created for each system-boot. btsnoop logging is disabled for the first 3, with only the 4th enabled (enabled when a certain HCI vendor command is detected).