Added uart.mitm.

This commit is contained in:
yellows8 2020-12-21 21:51:43 -05:00
parent 1545fa9d44
commit d7adef3810
No known key found for this signature in database
GPG Key ID: 0AF90DA3F1E60E43
10 changed files with 659 additions and 0 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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<ns::MitmModule>(),
GetModuleDefinition<socket::resolver::MitmModule>(),
GetModuleDefinition<sysupdater::MitmModule>(),
GetModuleDefinition<uart::MitmModule>(),
};
}

View File

@ -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. */

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<timeInitialize, timeExit> 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(&timestamp0);
timestamp1 = svcGetSystemTick();
std::snprintf(this->base_path, sizeof(this->base_path), "uart_logs/%011lu_%011lu_%016lx", timestamp0, timestamp1, static_cast<u64>(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<u8 *>(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<u8 *>(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<u32>(size));
ams::util::StoreBigEndian(&pkt_hdr.included_length, static_cast<u32>(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<decltype(hci_cmd)>(&cache_buffer[0x1]);
static_assert(sizeof(*hci_cmd) == 0x3);
struct {
u8 handle_flags[0x2];
u16 data_len;
} *hci_acl_data = reinterpret_cast<decltype(hci_acl_data)>(&cache_buffer[0x1]);
static_assert(sizeof(*hci_acl_data) == 0x4);
struct {
u8 handle_flags[0x2];
u8 data_len;
} *hci_sco_data = reinterpret_cast<decltype(hci_sco_data)>(&cache_buffer[0x1]);
static_assert(sizeof(*hci_sco_data) == 0x3);
struct {
u8 event_code;
u8 param_len;
} *hci_event = reinterpret_cast<decltype(hci_event)>(&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<decltype(hci_iso_data)>(&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<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, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) {
Result rc = uartPortSessionOpenPortFwd(this->srv.get(), reinterpret_cast<bool *>(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<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, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length) {
Result rc = uartPortSessionOpenPortForDevFwd(this->srv.get(), reinterpret_cast<bool *>(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<u64> out) {
Result rc = uartPortSessionGetWritableLength(this->srv.get(), reinterpret_cast<u64 *>(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<u64> out_size, const sf::InAutoSelectBuffer &data) {
Result rc = uartPortSessionSend(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast<u64 *>(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<u64> out) {
Result rc = uartPortSessionGetReadableLength(this->srv.get(), reinterpret_cast<u64 *>(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<u64> out_size, const sf::OutAutoSelectBuffer &data) {
Result rc = uartPortSessionReceive(this->srv.get(), data.GetPointer(), data.GetSize(), reinterpret_cast<u64 *>(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<bool> out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold) {
Result rc = uartPortSessionBindPortEventFwd(this->srv.get(), port_event_type, threshold, reinterpret_cast<bool *>(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<bool> out, UartPortEventType port_event_type) {
Result rc = uartPortSessionUnbindPortEvent(this->srv.get(), port_event_type, reinterpret_cast<bool *>(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<std::shared_ptr<impl::IPortSession>> 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<impl::IPortSession, UartPortService>(this->client_info, std::make_unique<UartPortSession>(port)), target_object_id);
return ResultSuccess();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#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<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, 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<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, 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<u64> out)) \
AMS_SF_METHOD_INFO(C, H, 3, Result, Send, (sf::Out<u64> out_size, const sf::InAutoSelectBuffer &data)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, GetReadableLength, (sf::Out<u64> out)) \
AMS_SF_METHOD_INFO(C, H, 5, Result, Receive, (sf::Out<u64> out_size, const sf::OutAutoSelectBuffer &data)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, BindPortEvent, (sf::Out<bool> out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold)) \
AMS_SF_METHOD_INFO(C, H, 7, Result, UnbindPortEvent, (sf::Out<bool> 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<std::shared_ptr<IPortSession>> 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<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, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length);
Result OpenPortForDev(sf::Out<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, sf::CopyHandle send_handle, sf::CopyHandle receive_handle, u64 send_buffer_length, u64 receive_buffer_length);
Result GetWritableLength(sf::Out<u64> out);
Result Send(sf::Out<u64> out_size, const sf::InAutoSelectBuffer &data);
Result GetReadableLength(sf::Out<u64> out);
Result Receive(sf::Out<u64> out_size, const sf::OutAutoSelectBuffer &data);
Result BindPortEvent(sf::Out<bool> out, sf::OutCopyHandle out_event_handle, UartPortEventType port_event_type, s64 threshold);
Result UnbindPortEvent(sf::Out<bool> out, UartPortEventType port_event_type);
};
static_assert(impl::IsIPortSession<UartPortService>);
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<std::shared_ptr<impl::IPortSession>> out);
};
static_assert(impl::IsIUartMitmInterface<UartMitmService>);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <switch.h>
#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;
}

View File

@ -0,0 +1,23 @@
/**
* @file uart_shim.h
* @brief UART IPC wrapper.
* @author yellows8
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<MaxServers, ServerOptions, MaxSessions> 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<impl::IUartMitmInterface, UartMitmService>(UartMitmServiceName)));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../amsmitm_module.hpp"
namespace ams::mitm::uart {
DEFINE_MITM_MODULE_CLASS(0x4000, AMS_GET_SYSTEM_THREAD_PRIORITY(uart, IpcServer) - 1);
}