From 37e065fa2d082f82f9b36e63c4fe3bb7198e91e5 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 21 Nov 2019 01:48:47 -0800 Subject: [PATCH] ams_mitm: implement bpc:mitm --- .../ams_mitm/source/amsmitm_fs_utils.cpp | 72 +++++++++++ .../ams_mitm/source/amsmitm_fs_utils.hpp | 29 +++++ .../source/amsmitm_initialization.cpp | 61 +++++++++ .../source/amsmitm_initialization.hpp | 25 ++++ stratosphere/ams_mitm/source/amsmitm_main.cpp | 4 + .../source/bpc_mitm/bpc_ams_power_utils.cpp | 121 ++++++++++++++++++ .../source/bpc_mitm/bpc_ams_power_utils.hpp | 30 +++++ .../source/bpc_mitm/bpc_ams_service.cpp | 25 ++++ .../source/bpc_mitm/bpc_ams_service.hpp | 35 +++++ .../source/bpc_mitm/bpc_mitm_service.cpp | 32 +++++ .../source/bpc_mitm/bpc_mitm_service.hpp | 21 ++- .../source/bpc_mitm/bpcmitm_module.cpp | 24 +++- 12 files changed, 472 insertions(+), 7 deletions(-) create mode 100644 stratosphere/ams_mitm/source/amsmitm_fs_utils.cpp create mode 100644 stratosphere/ams_mitm/source/amsmitm_fs_utils.hpp create mode 100644 stratosphere/ams_mitm/source/amsmitm_initialization.cpp create mode 100644 stratosphere/ams_mitm/source/amsmitm_initialization.hpp create mode 100644 stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp create mode 100644 stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp create mode 100644 stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp create mode 100644 stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp create mode 100644 stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp diff --git a/stratosphere/ams_mitm/source/amsmitm_fs_utils.cpp b/stratosphere/ams_mitm/source/amsmitm_fs_utils.cpp new file mode 100644 index 000000000..70e924897 --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_fs_utils.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2019 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 "amsmitm_fs_utils.hpp" + +namespace ams::mitm::fs { + + namespace { + + /* Globals. */ + FsFileSystem g_sd_filesystem; + + /* Helpers. */ + Result EnsureSdInitialized() { + R_UNLESS(mitm::IsInitialized(), ams::fs::ResultSdCardNotPresent()); + return ResultSuccess(); + } + + void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path) { + if (src_path[0] == '/') { + std::snprintf(dst_path, dst_path_size, "/atmosphere%s", src_path); + } else { + std::snprintf(dst_path, dst_path_size, "/atmosphere/%s", src_path); + } + } + + void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) { + if (src_path[0] == '/') { + std::snprintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx%s", static_cast(program_id), src_path); + } else { + std::snprintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s", static_cast(program_id), src_path); + } + } + + } + + void OpenGlobalSdCardFileSystem() { + R_ASSERT(fsOpenSdCardFileSystem(&g_sd_filesystem)); + } + + Result OpenSdFile(FsFile *out, const char *path, u32 mode) { + R_TRY(EnsureSdInitialized()); + return fsFsOpenFile(&g_sd_filesystem, path, mode, out); + } + + Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode) { + char fixed_path[FS_MAX_PATH]; + FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path); + return OpenSdFile(out, fixed_path, mode); + } + + Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) { + char fixed_path[FS_MAX_PATH]; + FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path); + return OpenSdFile(out, fixed_path, mode); + } + +} diff --git a/stratosphere/ams_mitm/source/amsmitm_fs_utils.hpp b/stratosphere/ams_mitm/source/amsmitm_fs_utils.hpp new file mode 100644 index 000000000..17e972daf --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_fs_utils.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2019 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::fs { + + /* Initialization. */ + void OpenGlobalSdCardFileSystem(); + + /* Utilities. */ + Result OpenSdFile(FsFile *out, const char *path, u32 mode); + Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode); + Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode); + +} diff --git a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp new file mode 100644 index 000000000..1537e81bb --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2019 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 "amsmitm_fs_utils.hpp" + +namespace ams::mitm { + + namespace { + + void InitializeThreadFunc(void *arg); + + constexpr size_t InitializeThreadStackSize = 0x4000; + constexpr int InitializeThreadPriority = 0x15; + os::StaticThread g_initialize_thread(&InitializeThreadFunc, nullptr, InitializeThreadPriority); + + /* Globals. */ + os::Event g_init_event(false); + + /* Initialization implementation */ + void InitializeThreadFunc(void *arg) { + /* Wait for the SD card to be ready. */ + cfg::WaitSdCardInitialized(); + + /* TODO: Other initialization tasks. */ + + /* Open global SD card file system, so that other threads can begin using the SD. */ + mitm::fs::OpenGlobalSdCardFileSystem(); + + /* Signal to waiters that we are ready. */ + g_init_event.Signal(); + } + + } + + void StartInitialize() { + R_ASSERT(g_initialize_thread.Start()); + } + + bool IsInitialized() { + return g_init_event.TryWait(); + } + + void WaitInitialized() { + g_init_event.Wait(); + } + +} diff --git a/stratosphere/ams_mitm/source/amsmitm_initialization.hpp b/stratosphere/ams_mitm/source/amsmitm_initialization.hpp new file mode 100644 index 000000000..9af84b3c9 --- /dev/null +++ b/stratosphere/ams_mitm/source/amsmitm_initialization.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2019 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 { + + void StartInitialize(); + bool IsInitialized(); + void WaitInitialized(); + +} diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index ff8635b3b..505e5346c 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "amsmitm_initialization.hpp" #include "amsmitm_module_management.hpp" extern "C" { @@ -95,6 +96,9 @@ void __appExit(void) { } int main(int argc, char **argv) { + /* Start initialization (sd card init, automatic backups, etc) */ + mitm::StartInitialize(); + /* Launch all mitm modules in sequence. */ mitm::LaunchAllModules(); diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp new file mode 100644 index 000000000..46ce4ff99 --- /dev/null +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018-2019 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 "bpc_ams_power_utils.hpp" +#include "../amsmitm_fs_utils.hpp" + +namespace ams::mitm::bpc { + + namespace { + + /* Convenience definitions. */ + constexpr uintptr_t IramBase = 0x40000000ull; + constexpr uintptr_t IramPayloadBase = 0x40010000ull; + constexpr size_t IramSize = 0x40000; + constexpr size_t IramPayloadMaxSize = 0x2E000; + + /* Helper enum. */ + enum class RebootType : u32 { + Standard, + ToRcm, + ToPayload, + }; + + /* Globals. */ + alignas(0x1000) u8 g_work_page[0x1000]; + alignas(0x1000) u8 g_reboot_payload[IramPayloadMaxSize]; + RebootType g_reboot_type = RebootType::ToRcm; + + /* Helpers. */ + void ClearIram() { + /* Make page CCs. */ + std::memset(g_work_page, 0xCC, sizeof(g_work_page)); + + /* Overwrite all of IRAM with CCs. */ + for (size_t ofs = 0; ofs < IramSize; ofs += sizeof(g_work_page)) { + exosphere::CopyToIram(IramBase + ofs, g_work_page, sizeof(g_work_page)); + } + } + + void DoRebootToPayload(const ams::FatalErrorContext *ctx) { + /* Ensure clean IRAM state. */ + ClearIram(); + + /* Copy in payload. */ + for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) { + std::memcpy(g_work_page, &g_reboot_payload[ofs], std::min(sizeof(g_reboot_payload) - ofs, size_t(0x1000))); + exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, 0x1000); + } + + /* Copy in fatal error context, if relevant. */ + if (ctx != nullptr) { + std::memset(g_work_page, 0xCC, sizeof(g_work_page)); + std::memcpy(g_work_page, ctx, sizeof(*ctx)); + exosphere::CopyToIram(IramPayloadBase + IramPayloadMaxSize, g_work_page, sizeof(g_work_page)); + } + + exosphere::ForceRebootToIramPayload(); + } + + } + + /* Power utilities. */ + bool IsRebootManaged() { + return g_reboot_type != RebootType::Standard; + } + + void RebootSystem() { + switch (g_reboot_type) { + case RebootType::ToRcm: + exosphere::ForceRebootToRcm(); + break; + case RebootType::ToPayload: + default: /* This should never be called with ::Standard */ + DoRebootToPayload(nullptr); + break; + } + } + + void ShutdownSystem() { + exosphere::ForceShutdown(); + } + + /* Atmosphere power utilities. */ + void RebootForFatalError(const ams::FatalErrorContext *ctx) { + DoRebootToPayload(ctx); + } + + Result LoadRebootPayload() { + /* Clear payload buffer */ + std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload)); + + /* Open payload file. */ + FsFile payload_file; + R_TRY(fs::OpenAtmosphereSdFile(&payload_file, "/reboot_payload.bin", FsOpenMode_Read)); + ON_SCOPE_EXIT { fsFileClose(&payload_file); }; + + /* Read payload file. Discard result. */ + { + size_t actual_size; + fsFileRead(&payload_file, 0, g_reboot_payload, sizeof(g_reboot_payload), FsReadOption_None, &actual_size); + } + + /* TODO: Parse reboot type from settings. */ + g_reboot_type = RebootType::ToPayload; + + return ResultSuccess(); + } + +} diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp new file mode 100644 index 000000000..841678ab6 --- /dev/null +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2019 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::bpc { + + /* Power utilities. */ + bool IsRebootManaged(); + void RebootSystem(); + void ShutdownSystem(); + + /* Atmosphere power utilities. */ + Result LoadRebootPayload(); + void RebootForFatalError(const ams::FatalErrorContext *ctx); + +} diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp new file mode 100644 index 000000000..071186d29 --- /dev/null +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2019 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 "bpc_ams_service.hpp" +#include "bpc_ams_power_utils.hpp" + +namespace ams::mitm::bpc { + + void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) { + bpc::RebootForFatalError(&ctx); + } + +} diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp new file mode 100644 index 000000000..256d9d0de --- /dev/null +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_service.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-2019 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::bpc { + + class AtmosphereService final : public sf::IServiceObject { + private: + enum class CommandId { + RebootToFatalError = 65000, + }; + private: + void RebootToFatalError(const ams::FatalErrorContext &ctx); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(RebootToFatalError), + }; + }; + +} diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp new file mode 100644 index 000000000..1eb48ba6e --- /dev/null +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018-2019 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 "bpc_mitm_service.hpp" +#include "bpc_ams_power_utils.hpp" + +namespace ams::mitm::bpc { + + Result BpcMitmService::RebootSystem() { + R_UNLESS(bpc::IsRebootManaged(), sm::mitm::ResultShouldForwardToSession()); + bpc::RebootSystem(); + return ResultSuccess(); + } + + Result BpcMitmService::ShutdownSystem() { + bpc::ShutdownSystem(); + return ResultSuccess(); + } + +} diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp index 758e90f2d..1c4a596bc 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_mitm_service.hpp @@ -21,20 +21,31 @@ namespace ams::mitm::bpc { class BpcMitmService : public sf::IMitmServiceObject { private: enum class CommandId { - /* TODO */ + ShutdownSystem = 0, + RebootSystem = 1, }; public: static bool ShouldMitm(os::ProcessId process_id, ncm::ProgramId program_id) { - /* TODO */ - return false; + /* We will mitm: + * - am, to intercept the Reboot/Power buttons in the overlay menu. + * - fatal, to simplify payload reboot logic significantly + * - applications and hbl, to allow homebrew to take advantage of the feature. + */ + return program_id == ncm::ProgramId::Am || + program_id == ncm::ProgramId::Fatal || + ncm::IsApplicationProgramId(program_id); + /* TODO: Hbl */ } public: SF_MITM_SERVICE_OBJECT_CTOR(BpcMitmService) { /* ... */ } protected: - /* TODO */ + /* Overridden commands. */ + Result ShutdownSystem(); + Result RebootSystem(); public: DEFINE_SERVICE_DISPATCH_TABLE { - /* TODO */ + MAKE_SERVICE_COMMAND_META(ShutdownSystem), + MAKE_SERVICE_COMMAND_META(RebootSystem), }; }; diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp index a0b6aa967..b55c0f6c9 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp @@ -13,8 +13,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "../amsmitm_initialization.hpp" #include "bpcmitm_module.hpp" #include "bpc_mitm_service.hpp" +#include "bpc_ams_service.hpp" +#include "bpc_ams_power_utils.hpp" namespace ams::mitm::bpc { @@ -24,14 +27,31 @@ namespace ams::mitm::bpc { constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c"); constexpr size_t MitmServiceMaxSessions = 13; - constexpr size_t MaxServers = 1; - constexpr size_t MaxSessions = MitmServiceMaxSessions; + constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams"); + constexpr size_t AtmosphereMaxSessions = 3; + + constexpr size_t MaxServers = 2; + constexpr size_t MaxSessions = MitmServiceMaxSessions + AtmosphereMaxSessions; using ServerOptions = sf::hipc::DefaultServerManagerOptions; sf::hipc::ServerManager g_server_manager; } void MitmModule::ThreadFunction(void *arg) { + /* Wait until initialization is complete. */ + mitm::WaitInitialized(); + + /* Initialize the reboot manager (load a payload off the SD). */ + /* Discard result, since it doesn't need to succeed. */ + LoadRebootPayload(); + + /* Create bpc:ams. */ + { + Handle bpcams_h; + R_ASSERT(svcManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions)); + g_server_manager.RegisterServer(bpcams_h); + } + /* Create bpc mitm. */ const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_200) ? MitmServiceName : DeprecatedMitmServiceName; R_ASSERT(g_server_manager.RegisterMitmServer(service_name));