From f4dcd1db9bda0af9e5676e1ecb48b6b57713b753 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 10 Oct 2019 23:49:28 -0700 Subject: [PATCH] sf: implement service framework enough for ro to work. This completely re-does the whole interface for ipc servers. --- stratosphere/libstratosphere/Makefile | 4 +- .../libstratosphere/include/stratosphere.hpp | 3 +- .../include/stratosphere/ams.hpp | 2 +- ...ersion_api.hpp => ams_hos_version_api.hpp} | 6 +- .../include/stratosphere/ams/ams_types.hpp | 34 +- .../include/stratosphere/cfg/cfg_api.hpp | 2 +- .../stratosphere/os/os_common_types.hpp | 45 + .../stratosphere/results/hipc_results.hpp | 14 +- .../stratosphere/results/kernel_results.hpp | 1 + .../stratosphere/results/sf_results.hpp | 12 +- .../include/stratosphere/ro/ro_types.hpp | 7 +- .../include/stratosphere/sf.hpp | 28 + .../sf/cmif/sf_cmif_domain_api.hpp | 68 + .../sf/cmif/sf_cmif_pointer_and_size.hpp | 44 + .../cmif/sf_cmif_server_message_processor.hpp | 39 + .../sf/cmif/sf_cmif_service_dispatch.hpp | 148 +++ .../sf/cmif/sf_cmif_service_object_holder.hpp | 98 ++ .../stratosphere/sf/hipc/sf_hipc_api.hpp | 37 + .../sf/hipc/sf_hipc_server_manager.hpp | 328 +++++ .../hipc/sf_hipc_server_session_manager.hpp | 156 +++ .../sf/impl/sf_impl_command_serialization.hpp | 1105 +++++++++++++++++ .../include/stratosphere/sf/sf_buffers.hpp | 308 +++++ .../include/stratosphere/sf/sf_common.hpp | 25 + .../include/stratosphere/sf/sf_handles.hpp | 171 +++ .../include/stratosphere/sf/sf_out.hpp | 73 ++ .../stratosphere/sf/sf_service_object.hpp | 43 + .../include/stratosphere/sm/sm_mitm_api.hpp | 2 +- .../include/stratosphere/sm/sm_types.hpp | 10 +- .../stratosphere/util/util_intrusive_list.hpp | 2 +- ...ersion_api.cpp => ams_hos_version_api.cpp} | 52 +- .../source/cfg/cfg_privileged_process.cpp | 34 +- .../libstratosphere/source/map/map_api.cpp | 6 +- .../sf/cmif/sf_cmif_service_dispatch.cpp | 108 ++ .../sf/cmif/sf_cmif_service_object_holder.cpp | 27 + .../source/sf/hipc/sf_hipc_api.cpp | 86 ++ .../source/sf/hipc/sf_hipc_server_manager.cpp | 165 +++ .../hipc/sf_hipc_server_session_manager.cpp | 246 ++++ .../libstratosphere/source/sm/sm_mitm_api.cpp | 4 +- stratosphere/ro/source/impl/ro_nrr_utils.cpp | 6 +- stratosphere/ro/source/impl/ro_nrr_utils.hpp | 2 +- .../ro/source/impl/ro_service_impl.cpp | 34 +- .../ro/source/impl/ro_service_impl.hpp | 6 +- stratosphere/ro/source/ro_debug_monitor.cpp | 6 +- stratosphere/ro/source/ro_debug_monitor.hpp | 6 +- stratosphere/ro/source/ro_main.cpp | 54 +- stratosphere/ro/source/ro_service.cpp | 26 +- stratosphere/ro/source/ro_service.hpp | 28 +- 47 files changed, 3545 insertions(+), 166 deletions(-) rename stratosphere/libstratosphere/include/stratosphere/ams/{ams_firmware_version_api.hpp => ams_hos_version_api.hpp} (86%) create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/sf_buffers.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/sf_common.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/sf_handles.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/sf_out.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/sf/sf_service_object.hpp rename stratosphere/libstratosphere/source/ams/{ams_firmware_version_api.cpp => ams_hos_version_api.cpp} (74%) create mode 100644 stratosphere/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp create mode 100644 stratosphere/libstratosphere/source/sf/cmif/sf_cmif_service_object_holder.cpp create mode 100644 stratosphere/libstratosphere/source/sf/hipc/sf_hipc_api.cpp create mode 100644 stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp create mode 100644 stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_session_manager.cpp diff --git a/stratosphere/libstratosphere/Makefile b/stratosphere/libstratosphere/Makefile index 677d95526..79d160acc 100644 --- a/stratosphere/libstratosphere/Makefile +++ b/stratosphere/libstratosphere/Makefile @@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules # INCLUDES is a list of directories containing header files #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) -SOURCES := source source/ams source/os source/os/impl source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb +SOURCES := source source/ams source/os source/os/impl source/sf source/sf/cmif source/sf/hipc source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb DATA := data INCLUDES := include @@ -32,7 +32,7 @@ CFLAGS := -g -Wall -O2 -ffunction-sections \ CFLAGS += $(INCLUDE) -D__SWITCH__ -CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -flto -std=gnu++17 +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp index a5b239d13..b1a36f453 100644 --- a/stratosphere/libstratosphere/include/stratosphere.hpp +++ b/stratosphere/libstratosphere/include/stratosphere.hpp @@ -37,4 +37,5 @@ #include "stratosphere/ncm.hpp" #include "stratosphere/pm.hpp" #include "stratosphere/rnd.hpp" -#include "stratosphere/sm.hpp" \ No newline at end of file +#include "stratosphere/sm.hpp" +#include "stratosphere/sf.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/ams.hpp b/stratosphere/libstratosphere/include/stratosphere/ams.hpp index 4a679b34e..9af4ad13d 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ams.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ams.hpp @@ -17,4 +17,4 @@ #pragma once #include "ams/ams_types.hpp" -#include "ams/ams_firmware_version_api.hpp" \ No newline at end of file +#include "ams/ams_hos_version_api.hpp" \ No newline at end of file diff --git a/stratosphere/libstratosphere/include/stratosphere/ams/ams_firmware_version_api.hpp b/stratosphere/libstratosphere/include/stratosphere/ams/ams_hos_version_api.hpp similarity index 86% rename from stratosphere/libstratosphere/include/stratosphere/ams/ams_firmware_version_api.hpp rename to stratosphere/libstratosphere/include/stratosphere/ams/ams_hos_version_api.hpp index d509d77cd..6197e489a 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ams/ams_firmware_version_api.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ams/ams_hos_version_api.hpp @@ -17,9 +17,9 @@ #pragma once #include "ams_types.hpp" -namespace sts::ams { +namespace sts::hos { - FirmwareVersion GetRuntimeFirmwareVersion(); - void SetFirmwareVersionForLibnx(); + sts::hos::Version GetVersion(); + void SetVersionForLibnx(); } diff --git a/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp b/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp index 9989e6237..9c0e2436b 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ams/ams_types.hpp @@ -21,21 +21,25 @@ /* Define firmware version in global namespace, for convenience. */ namespace sts { - enum FirmwareVersion : u32 { - FirmwareVersion_Min = 0, - FirmwareVersion_100 = FirmwareVersion_Min, - FirmwareVersion_200 = 1, - FirmwareVersion_300 = 2, - FirmwareVersion_400 = 3, - FirmwareVersion_500 = 4, - FirmwareVersion_600 = 5, - FirmwareVersion_700 = 6, - FirmwareVersion_800 = 7, - FirmwareVersion_810 = 8, - FirmwareVersion_900 = 9, - FirmwareVersion_Current = FirmwareVersion_900, - FirmwareVersion_Max = 32, - }; + namespace hos { + + enum Version : u16 { + Version_Min = 0, + Version_100 = Version_Min, + Version_200 = 1, + Version_300 = 2, + Version_400 = 3, + Version_500 = 4, + Version_600 = 5, + Version_700 = 6, + Version_800 = 7, + Version_810 = 8, + Version_900 = 9, + Version_Current = Version_900, + Version_Max = 32, + }; + + } } diff --git a/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp b/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp index 9c5c8efe8..8c6e8cbd2 100644 --- a/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/cfg/cfg_api.hpp @@ -22,7 +22,7 @@ namespace sts::cfg { /* Privileged Process configuration. */ bool IsInitialProcess(); - void GetInitialProcessRange(u64 *out_min, u64 *out_max); + void GetInitialProcessRange(os::ProcessId *out_min, os::ProcessId *out_max); /* SD card configuration. */ bool IsSdCardInitialized(); diff --git a/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp b/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp index b6d36df93..9c44ccdf2 100644 --- a/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/os/os_common_types.hpp @@ -29,4 +29,49 @@ namespace sts::os { ForNotFull, }; + struct ProcessId { + u64 value; + + inline constexpr explicit operator u64() const { + return this->value; + } + + /* Invalid Process ID. */ + static const ProcessId Invalid; + }; + + inline constexpr const ProcessId ProcessId::Invalid = {static_cast(-1ull)}; + + inline constexpr const ProcessId InvalidProcessId = ProcessId::Invalid; + + NX_INLINE ProcessId GetCurrentProcessId() { + u64 current_process_id = 0; + R_ASSERT(svcGetProcessId(¤t_process_id, CUR_PROCESS_HANDLE)); + return os::ProcessId{current_process_id}; + } + + inline constexpr bool operator==(const ProcessId &lhs, const ProcessId &rhs) { + return lhs.value == rhs.value; + } + + inline constexpr bool operator!=(const ProcessId &lhs, const ProcessId &rhs) { + return lhs.value != rhs.value; + } + + inline constexpr bool operator<(const ProcessId &lhs, const ProcessId &rhs) { + return lhs.value < rhs.value; + } + + inline constexpr bool operator<=(const ProcessId &lhs, const ProcessId &rhs) { + return lhs.value <= rhs.value; + } + + inline constexpr bool operator>(const ProcessId &lhs, const ProcessId &rhs) { + return lhs.value > rhs.value; + } + + inline constexpr bool operator>=(const ProcessId &lhs, const ProcessId &rhs) { + return lhs.value >= rhs.value; + } + } diff --git a/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp index 86ef678ba..5406123a7 100644 --- a/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/results/hipc_results.hpp @@ -19,9 +19,19 @@ static constexpr u32 Module_Hipc = 11; -static constexpr Result ResultHipcOutOfDomains = MAKERESULT(Module_Hipc, 200); +static constexpr Result ResultHipcSessionAllocationFailure = MAKERESULT(Module_Hipc, 102); -static constexpr Result ResultHipcSessionClosed = MAKERESULT(Module_Hipc, 301); +static constexpr Result ResultHipcOutOfSessions = MAKERESULT(Module_Hipc, 131); +static constexpr Result ResultHipcPointerBufferTooSmall = MAKERESULT(Module_Hipc, 141); + +static constexpr Result ResultHipcOutOfDomains = MAKERESULT(Module_Hipc, 200); + +static constexpr Result ResultHipcSessionClosed = MAKERESULT(Module_Hipc, 301); + +static constexpr Result ResultHipcInvalidRequestSize = MAKERESULT(Module_Hipc, 402); +static constexpr Result ResultHipcUnknownCommandType = MAKERESULT(Module_Hipc, 403); + +static constexpr Result ResultHipcInvalidRequest = MAKERESULT(Module_Hipc, 420); static constexpr Result ResultHipcTargetNotDomain = MAKERESULT(Module_Hipc, 491); static constexpr Result ResultHipcDomainObjectNotFound = MAKERESULT(Module_Hipc, 492); diff --git a/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp index c53663037..1482f5db9 100644 --- a/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/results/kernel_results.hpp @@ -57,6 +57,7 @@ static constexpr Result ResultKernelOwnedByAnotherProcess = MAKERESULT(Mo static constexpr Result ResultKernelConnectionRefused = MAKERESULT(Module_Kernel, KernelError_ConnectionRefused); static constexpr Result ResultKernelLimitReached = MAKERESULT(Module_Kernel, 132 /* KernelError_OutOfResource */); +static constexpr Result ResultKernelReceiveListBroken = MAKERESULT(Module_Kernel, 258); static constexpr Result ResultKernelIpcMapFailed = MAKERESULT(Module_Kernel, KernelError_IpcMapFailed); static constexpr Result ResultKernelIpcCmdBufTooSmall = MAKERESULT(Module_Kernel, KernelError_IpcCmdbufTooSmall); diff --git a/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp b/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp index aed8d5fbb..a68a29572 100644 --- a/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/results/sf_results.hpp @@ -19,9 +19,17 @@ static constexpr u32 Module_ServiceFramework = 10; -static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261); +static constexpr Result ResultServiceFrameworkNotSupported = MAKERESULT(Module_ServiceFramework, 1); +static constexpr Result ResultServiceFrameworkPreconditionViolation = MAKERESULT(Module_ServiceFramework, 3); -static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301); +static constexpr Result ResultServiceFrameworkInvalidCmifHeaderSize = MAKERESULT(Module_ServiceFramework, 202); +static constexpr Result ResultServiceFrameworkInvalidCmifInHeader = MAKERESULT(Module_ServiceFramework, 211); +static constexpr Result ResultServiceFrameworkUnknownCmifCommandId = MAKERESULT(Module_ServiceFramework, 221); +static constexpr Result ResultServiceFrameworkInvalidCmifOutRawSize = MAKERESULT(Module_ServiceFramework, 232); + +static constexpr Result ResultServiceFrameworkTargetNotFound = MAKERESULT(Module_ServiceFramework, 261); + +static constexpr Result ResultServiceFrameworkOutOfDomainEntries = MAKERESULT(Module_ServiceFramework, 301); static constexpr Result ResultServiceFrameworkRequestDeferred = MAKERESULT(Module_ServiceFramework, 811); diff --git a/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp b/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp index 7c092159a..19c3d2b54 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ro/ro_types.hpp @@ -18,6 +18,7 @@ #include #include #include "../defines.hpp" +#include "../ncm.hpp" namespace sts::ro { @@ -44,7 +45,7 @@ namespace sts::ro { u8 modulus[0x100]; u8 fixed_key_signature[0x100]; u8 nrr_signature[0x100]; - u64 title_id; + ncm::TitleId title_id; u32 size; u8 type; /* 7.0.0+ */ u8 reserved_33D[3]; @@ -57,7 +58,7 @@ namespace sts::ro { } bool IsTitleIdValid() const { - return (this->title_id & this->title_id_mask) == this->title_id_pattern; + return (static_cast(this->title_id) & this->title_id_mask) == this->title_id_pattern; } ModuleType GetType() const { @@ -66,7 +67,7 @@ namespace sts::ro { return type; } - u64 GetTitleId() const { + ncm::TitleId GetTitleId() const { return this->title_id; } diff --git a/stratosphere/libstratosphere/include/stratosphere/sf.hpp b/stratosphere/libstratosphere/include/stratosphere/sf.hpp new file mode 100644 index 000000000..8b39292ae --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf.hpp @@ -0,0 +1,28 @@ +/* + * 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 + +#include "sf/sf_common.hpp" +#include "sf/sf_service_object.hpp" +#include "sf/hipc/sf_hipc_server_session_manager.hpp" + +#include "sf/sf_out.hpp" +#include "sf/sf_buffers.hpp" +#include "sf/impl/sf_impl_command_serialization.hpp" + +#include "sf/hipc/sf_hipc_server_manager.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp new file mode 100644 index 000000000..f09993d05 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_api.hpp @@ -0,0 +1,68 @@ +/* + * 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 "../sf_common.hpp" +#include "sf_cmif_service_object_holder.hpp" + +namespace sts::sf::cmif { + + struct DomainObjectId { + u32 value; + + constexpr void SetValue(u32 new_value) { this->value = new_value; } + }; + + static_assert(std::is_trivial::value, "DomainObjectId"); + + inline constexpr bool operator==(const DomainObjectId &lhs, const DomainObjectId &rhs) { + return lhs.value == rhs.value; + } + + inline constexpr bool operator!=(const DomainObjectId &lhs, const DomainObjectId &rhs) { + return lhs.value != rhs.value; + } + + inline constexpr bool operator<(const DomainObjectId &lhs, const DomainObjectId &rhs) { + return lhs.value < rhs.value; + } + + inline constexpr bool operator<=(const DomainObjectId &lhs, const DomainObjectId &rhs) { + return lhs.value <= rhs.value; + } + + inline constexpr bool operator>(const DomainObjectId &lhs, const DomainObjectId &rhs) { + return lhs.value > rhs.value; + } + + inline constexpr bool operator>=(const DomainObjectId &lhs, const DomainObjectId &rhs) { + return lhs.value >= rhs.value; + } + + constexpr inline const DomainObjectId InvalidDomainObjectId = { .value = 0 }; + + class ServerDomainBase { + public: + virtual Result ReserveIds(DomainObjectId *out_ids, size_t count) = 0; + virtual Result AlterReservedIds(const DomainObjectId *old_reserved_ids, const DomainObjectId *new_reserved_ids, size_t count) = 0; + virtual void UnreserveIds(const DomainObjectId *ids, size_t count) = 0; + virtual void RegisterObject(DomainObjectId id, ServiceObjectHolder &&obj) = 0; + + virtual ServiceObjectHolder UnregisterObject(DomainObjectId id) = 0; + virtual ServiceObjectHolder GetObject(DomainObjectId id) = 0; + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp new file mode 100644 index 000000000..13f1858c0 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_pointer_and_size.hpp @@ -0,0 +1,44 @@ +/* + * 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 "../sf_common.hpp" + +namespace sts::sf::cmif { + + class PointerAndSize { + private: + uintptr_t pointer; + size_t size; + public: + constexpr PointerAndSize() : pointer(0), size(0) { /* ... */ } + constexpr PointerAndSize(uintptr_t ptr, size_t sz) : pointer(ptr), size(sz) { /* ... */ } + constexpr PointerAndSize(void *ptr, size_t sz) : PointerAndSize(reinterpret_cast(ptr), sz) { /* ... */ } + + constexpr void *GetPointer() const { + return reinterpret_cast(this->pointer); + } + + constexpr uintptr_t GetAddress() const { + return this->pointer; + } + + constexpr size_t GetSize() const { + return this->size; + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp new file mode 100644 index 000000000..4bc1fdb05 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_server_message_processor.hpp @@ -0,0 +1,39 @@ +/* + * 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 "../sf_service_object.hpp" +#include "sf_cmif_pointer_and_size.hpp" + +namespace sts::sf::cmif { + + /* Forward declare ServiceDispatchContext, ServiceObjectHolder. */ + struct ServiceDispatchContext; + class ServiceObjectHolder; + struct DomainObjectId; + + class ServerMessageProcessor { + public: + /* Used to enabled templated message processors. */ + virtual void SetImplementationProcessor(ServerMessageProcessor *impl) { /* ... */ } + + virtual Result PrepareForProcess(const ServiceDispatchContext &ctx, const size_t headers_size) const = 0; + virtual Result GetInObjects(ServiceObjectHolder *in_objects) const = 0; + virtual HipcRequest PrepareForReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size, size_t &num_out_object_handles) = 0; + virtual void PrepareForErrorReply(const cmif::ServiceDispatchContext &ctx, PointerAndSize &out_raw_data, const size_t headers_size) = 0; + virtual void SetOutObjects(const cmif::ServiceDispatchContext &ctx, const HipcRequest &response, ServiceObjectHolder *out_objects, DomainObjectId *ids) = 0; + }; +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp new file mode 100644 index 000000000..fdc493e38 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_dispatch.hpp @@ -0,0 +1,148 @@ +/* + * 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 "../sf_service_object.hpp" +#include "sf_cmif_pointer_and_size.hpp" +#include "sf_cmif_server_message_processor.hpp" + +namespace sts::sf::hipc { + + class ServerSessionManager; + +} + +namespace sts::sf::cmif { + + class ServerMessageProcessor; + + struct HandlesToClose { + Handle handles[8]; + size_t num_handles; + }; + + struct ServiceDispatchContext { + sf::IServiceObject *srv_obj; + hipc::ServerSessionManager *manager; + ServerMessageProcessor *processor; + HandlesToClose *handles_to_close; + const PointerAndSize pointer_buffer; + const PointerAndSize in_message_buffer; + const PointerAndSize out_message_buffer; + const HipcParsedRequest request; + }; + + struct ServiceCommandMeta { + hos::Version hosver_low; + hos::Version hosver_high; + u32 cmd_id; + Result (*handler)(CmifOutHeader **out_header_ptr, ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data); + + constexpr inline bool Matches(u32 cmd_id, hos::Version hosver) const { + return this->cmd_id == cmd_id && this->hosver_low <= hosver && hosver <= this->hosver_high; + } + + constexpr inline decltype(handler) GetHandler() const { + return this->handler; + } + }; + static_assert(std::is_pod::value && sizeof(ServiceCommandMeta) == 0x10, "sizeof(ServiceCommandMeta)"); + + namespace impl { + + class ServiceDispatchTableBase { + protected: + Result ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const; + Result ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const; + public: + /* CRTP. */ + template + Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { + static_assert(std::is_base_of::value, "ServiceDispatchTableBase::Process"); + return static_cast(this)->ProcessMessage(ctx, in_raw_data); + } + + template + Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { + static_assert(std::is_base_of::value, "ServiceDispatchTableBase::ProcessForMitm"); + return static_cast(this)->ProcessMessageForMitm(ctx, in_raw_data); + } + }; + + template> + class ServiceDispatchTableImpl; + + template + class ServiceDispatchTableImpl> : public ServiceDispatchTableBase { + private: + template + using EntryType = ServiceCommandMeta; + private: + const std::array entries; + public: + explicit constexpr ServiceDispatchTableImpl(EntryType... args) : entries { args... } { /* ... */ } + + Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { + return this->ProcessMessageImpl(ctx, in_raw_data, this->entries.data(), this->entries.size()); + } + + Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const { + return this->ProcessMessageForMitmImpl(ctx, in_raw_data, this->entries.data(), this->entries.size()); + } + }; + + } + + template + class ServiceDispatchTable : public impl::ServiceDispatchTableImpl { + public: + explicit constexpr ServiceDispatchTable(Entries... entries) : impl::ServiceDispatchTableImpl(entries...) { /* ... */ } + }; + + #define DEFINE_SERVICE_DISPATCH_TABLE \ + template \ + static constexpr inline sf::cmif::ServiceDispatchTable s_CmifServiceDispatchTable + + struct ServiceDispatchMeta { + const impl::ServiceDispatchTableBase *DispatchTable; + Result (impl::ServiceDispatchTableBase::*ProcessHandler)(ServiceDispatchContext &, const cmif::PointerAndSize &) const; + + constexpr uintptr_t GetServiceId() const { + return reinterpret_cast(this->DispatchTable); + } + }; + + template + struct ServiceDispatchTraits { + static_assert(std::is_base_of::value, "ServiceObjects must derive from sf::IServiceObject"); + + using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler); + + static constexpr inline auto DispatchTable = T::template s_CmifServiceDispatchTable; + using DispatchTableType = decltype(DispatchTable); + + static constexpr ProcessHandlerType ProcessHandlerImpl = ServiceObjectTraits::IsMitmServiceObject ? (&impl::ServiceDispatchTableBase::ProcessMessageForMitm) + : (&impl::ServiceDispatchTableBase::ProcessMessage); + + static constexpr inline ServiceDispatchMeta Meta{&DispatchTable, ProcessHandlerImpl}; + }; + + template + NX_CONSTEXPR const ServiceDispatchMeta *GetServiceDispatchMeta() { + return &ServiceDispatchTraits::Meta; + } + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp new file mode 100644 index 000000000..e585f6ce0 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_service_object_holder.hpp @@ -0,0 +1,98 @@ +/* + * 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 "../sf_service_object.hpp" +#include "sf_cmif_service_dispatch.hpp" + +namespace sts::sf::cmif { + + class ServiceObjectHolder { + private: + std::shared_ptr srv; + const ServiceDispatchMeta *dispatch_meta; + private: + /* Copy constructor. */ + ServiceObjectHolder(const ServiceObjectHolder &o) : srv(o.srv), dispatch_meta(o.dispatch_meta) { /* ... */ } + ServiceObjectHolder &operator=(const ServiceObjectHolder &o) = delete; + public: + /* Default constructor, null all members. */ + ServiceObjectHolder() : srv(nullptr), dispatch_meta(nullptr) { /* ... */ } + + /* Ensure correct type id at runtime through template constructor. */ + template + constexpr explicit ServiceObjectHolder(std::shared_ptr &&s) { + this->srv = std::move(s); + this->dispatch_meta = GetServiceDispatchMeta(); + } + + /* Move constructor, assignment operator. */ + ServiceObjectHolder(ServiceObjectHolder &&o) : srv(std::move(o.srv)), dispatch_meta(std::move(o.dispatch_meta)) { /* ... */ } + ServiceObjectHolder &operator=(ServiceObjectHolder &&o) { + ServiceObjectHolder tmp(std::move(o)); + tmp.Swap(*this); + return *this; + } + + /* State management. */ + void Swap(ServiceObjectHolder &o) { + std::swap(this->srv, o.srv); + std::swap(this->dispatch_meta, o.dispatch_meta); + } + + void Reset() { + this->srv = nullptr; + this->dispatch_meta = nullptr; + } + + ServiceObjectHolder Clone() const { + return ServiceObjectHolder(*this); + } + + /* Boolean operators. */ + explicit constexpr operator bool() const { + return this->dispatch_meta != nullptr; + } + + constexpr bool operator!() const { + return this->dispatch_meta == nullptr; + } + + /* Getters. */ + constexpr uintptr_t GetServiceId() const { + if (this->dispatch_meta) { + return this->dispatch_meta->GetServiceId(); + } + return 0; + } + + template + ServiceImpl *GetServiceObject() const { + if (this->GetServiceId() == GetServiceDispatchMeta()->GetServiceId()) { + return static_cast(this->srv.get()); + } + return nullptr; + } + + sf::IServiceObject *GetServiceObjectUnsafe() const { + return this->srv.get(); + } + + /* Processing. */ + Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const; + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp new file mode 100644 index 000000000..0478ca495 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_api.hpp @@ -0,0 +1,37 @@ +/* + * 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 "../sf_common.hpp" +#include "../cmif/sf_cmif_pointer_and_size.hpp" + +namespace sts::sf::hipc { + + constexpr size_t TlsMessageBufferSize = 0x100; + + enum class ReceiveResult { + Success, + Closed, + NeedsRetry, + }; + + Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer); + Result Receive(bool *out_closed, Handle session_handle, const cmif::PointerAndSize &message_buffer); + Result Reply(Handle session_handle, const cmif::PointerAndSize &message_buffer); + + Result CreateSession(Handle *out_server_handle, Handle *out_client_handle); + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp new file mode 100644 index 000000000..62f170559 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_manager.hpp @@ -0,0 +1,328 @@ +/* + * 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 "sf_hipc_server_session_manager.hpp" + +namespace sts::sf::hipc { + + struct DefaultServerManagerOptions { + static constexpr size_t PointerBufferSize = 0; + static constexpr size_t MaxDomains = 0; + static constexpr size_t MaxDomainObjects = 0; + }; + + static constexpr size_t ServerSessionCountMax = 0x40; + static_assert(ServerSessionCountMax == 0x40, "ServerSessionCountMax isn't 0x40 somehow, this assert is a reminder that this will break lots of things"); + + template + class ServerManager; + + class ServerManagerBase : public ServerSessionManager { + NON_COPYABLE(ServerManagerBase); + NON_MOVEABLE(ServerManagerBase); + private: + enum class UserDataTag : uintptr_t { + Server = 1, + Session = 2, + MitmServer = 3, + }; + private: + class ServerBase : public os::WaitableHolder { + friend class ServerManagerBase; + template + friend class ServerManager; + NON_COPYABLE(ServerBase); + NON_MOVEABLE(ServerBase); + protected: + cmif::ServiceObjectHolder static_object; + ::Handle port_handle; + sm::ServiceName service_name; + bool service_managed; + public: + ServerBase(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) : + os::WaitableHolder(ph), static_object(std::move(sh)), port_handle(ph), service_name(sn), service_managed(m) + { + /* ... */ + } + + virtual ~ServerBase() = 0; + virtual void CreateSessionObjectHolder(cmif::ServiceObjectHolder *out_obj, std::shared_ptr<::Service> *out_fsrv) const = 0; + }; + + template> + class Server : public ServerBase { + NON_COPYABLE(Server); + NON_MOVEABLE(Server); + private: + static constexpr bool IsMitmServer = ServiceObjectTraits::IsMitmServiceObject; + public: + Server(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) : ServerBase(ph, sn, m, std::forward(sh)) { + /* ... */ + } + + virtual ~Server() override { + if (this->service_managed) { + if constexpr (IsMitmServer) { + R_ASSERT(sm::mitm::UninstallMitm(this->service_name)); + } else { + R_ASSERT(sm::UnregisterService(this->service_name)); + } + R_ASSERT(svcCloseHandle(this->port_handle)); + } + } + + virtual void CreateSessionObjectHolder(cmif::ServiceObjectHolder *out_obj, std::shared_ptr<::Service> *out_fsrv) const override final { + /* If we're serving a static object, use it. */ + if (this->static_object) { + *out_obj = std::move(this->static_object.Clone()); + *out_fsrv = nullptr; + return; + } + + /* Otherwise, we're either a mitm session or a non-mitm session. */ + if constexpr (IsMitmServer) { + /* Custom deleter ensures that nothing goes awry. */ + /* TODO: Should this just be a custom wrapper object? */ + std::shared_ptr<::Service> forward_service(new ::Service(), [](::Service *srv) { + serviceClose(srv); + delete srv; + }); + + /* Get mitm forward session. */ + os::ProcessId client_pid; + ncm::TitleId client_tid; + R_ASSERT(sm::mitm::AcknowledgeSession(forward_service.get(), &client_pid, &client_tid, this->service_name)); + + *out_obj = std::move(cmif::ServiceObjectHolder(std::move(MakeShared(forward_service)))); + *out_fsrv = std::move(forward_service); + } else { + *out_obj = std::move(cmif::ServiceObjectHolder(std::move(MakeShared()))); + *out_fsrv = nullptr; + } + } + }; + private: + /* Management of waitables. */ + os::WaitableManager waitable_manager; + os::Event request_stop_event; + os::WaitableHolder request_stop_event_holder; + os::Event notify_event; + os::WaitableHolder notify_event_holder; + + os::Mutex waitable_selection_mutex; + + os::Mutex waitlist_mutex; + os::WaitableManager waitlist; + private: + virtual void RegisterSessionToWaitList(ServerSession *session) override final; + void RegisterToWaitList(os::WaitableHolder *holder); + void ProcessWaitList(); + + bool WaitAndProcessImpl(); + + Result ProcessForServer(os::WaitableHolder *holder); + Result ProcessForMitmServer(os::WaitableHolder *holder); + Result ProcessForSession(os::WaitableHolder *holder); + + template> + void RegisterServerImpl(Handle port_handle, sm::ServiceName service_name, bool managed, cmif::ServiceObjectHolder &&static_holder) { + /* Allocate server memory. */ + auto *server = this->AllocateServer(); + STS_ASSERT(server != nullptr); + new (server) Server(port_handle, service_name, managed, std::forward(static_holder)); + + if constexpr (!ServiceObjectTraits::IsMitmServiceObject) { + /* Non-mitm server. */ + server->SetUserData(static_cast(UserDataTag::Server)); + } else { + /* Mitm server. */ + server->SetUserData(static_cast(UserDataTag::MitmServer)); + } + + this->waitable_manager.LinkWaitableHolder(server); + } + protected: + virtual ServerBase *AllocateServer() = 0; + virtual void DestroyServer(ServerBase *server) = 0; + public: + ServerManagerBase() : ServerSessionManager(), + request_stop_event(false), request_stop_event_holder(&request_stop_event), + notify_event(false), notify_event_holder(¬ify_event) + { + /* Link waitables. */ + this->waitable_manager.LinkWaitableHolder(&this->request_stop_event_holder); + this->waitable_manager.LinkWaitableHolder(&this->notify_event_holder); + } + + template> + void RegisterServer(Handle port_handle, std::shared_ptr static_object = nullptr) { + static_assert(!ServiceObjectTraits::IsMitmServiceObject, "RegisterServer requires non-mitm object. Use RegisterMitmServer instead."); + /* Register server. */ + cmif::ServiceObjectHolder static_holder; + if (static_object != nullptr) { + static_holder = cmif::ServiceObjectHolder(std::move(static_object)); + } + this->RegisterServerImpl(port_handle, sm::InvalidServiceName, false, std::move(static_holder)); + } + + template> + Result RegisterServer(sm::ServiceName service_name, size_t max_sessions, std::shared_ptr static_object = nullptr) { + static_assert(!ServiceObjectTraits::IsMitmServiceObject, "RegisterServer requires non-mitm object. Use RegisterMitmServer instead."); + + /* Register service. */ + Handle port_handle; + R_TRY(sm::RegisterService(&port_handle, service_name, max_sessions, false)); + + /* Register server. */ + cmif::ServiceObjectHolder static_holder; + if (static_object != nullptr) { + static_holder = cmif::ServiceObjectHolder(std::move(static_object)); + } + this->RegisterServerImpl(port_handle, service_name, true, std::move(static_holder)); + return ResultSuccess; + } + + /* Processing. */ + os::WaitableHolder *WaitSignaled(); + + void ResumeProcessing(); + void RequestStopProcessing(); + void AddUserWaitableHolder(os::WaitableHolder *waitable); + + Result Process(os::WaitableHolder *waitable); + void WaitAndProcess(); + void LoopProcess(); + }; + + template + class ServerManager : public ServerManagerBase { + NON_COPYABLE(ServerManager); + NON_MOVEABLE(ServerManager); + static_assert(MaxServers <= ServerSessionCountMax, "MaxServers can never be larger than ServerSessionCountMax (0x40)."); + static_assert(MaxSessions <= ServerSessionCountMax, "MaxSessions can never be larger than ServerSessionCountMax (0x40)."); + static_assert(MaxServers + MaxSessions <= ServerSessionCountMax, "MaxServers + MaxSessions can never be larger than ServerSessionCountMax (0x40)."); + private: + /* Resource storage. */ + os::Mutex resource_mutex; + TYPED_STORAGE(ServerBase) server_storages[MaxServers]; + bool server_allocated[MaxServers]; + TYPED_STORAGE(ServerSession) session_storages[MaxSessions]; + bool session_allocated[MaxSessions]; + u8 pointer_buffer_storage[0x10 + (MaxSessions * ManagerOptions::PointerBufferSize)]; + u8 saved_message_storage[0x10 + (MaxSessions * hipc::TlsMessageBufferSize)]; + uintptr_t pointer_buffers_start; + uintptr_t saved_messages_start; + + /* TODO: Domain resources. */ + private: + constexpr inline size_t GetServerIndex(const ServerBase *server) const { + const size_t i = server - GetPointer(this->server_storages[0]); + STS_ASSERT(i < MaxServers); + return i; + } + + constexpr inline size_t GetSessionIndex(const ServerSession *session) const { + const size_t i = session - GetPointer(this->session_storages[0]); + STS_ASSERT(i < MaxSessions); + return i; + } + + constexpr inline cmif::PointerAndSize GetObjectBySessionIndex(const ServerSession *session, uintptr_t start, size_t size) const { + return cmif::PointerAndSize(start + this->GetSessionIndex(session) * size, size); + } + protected: + virtual ServerSession *AllocateSession() override final { + std::scoped_lock lk(this->resource_mutex); + for (size_t i = 0; i < MaxSessions; i++) { + if (!this->session_allocated[i]) { + this->session_allocated[i] = true; + return GetPointer(this->session_storages[i]); + } + } + return nullptr; + } + + virtual void FreeSession(ServerSession *session) override final { + std::scoped_lock lk(this->resource_mutex); + const size_t index = this->GetSessionIndex(session); + STS_ASSERT(this->session_allocated[index]); + this->session_allocated[index] = false; + } + + virtual ServerBase *AllocateServer() override final { + std::scoped_lock lk(this->resource_mutex); + for (size_t i = 0; i < MaxServers; i++) { + if (!this->server_allocated[i]) { + this->server_allocated[i] = true; + return GetPointer(this->server_storages[i]); + } + } + return nullptr; + } + + virtual void DestroyServer(ServerBase *server) override final { + std::scoped_lock lk(this->resource_mutex); + const size_t index = this->GetServerIndex(server); + STS_ASSERT(this->server_allocated[index]); + server->~ServerBase(); + this->server_allocated[index] = false; + } + + virtual cmif::PointerAndSize GetSessionPointerBuffer(const ServerSession *session) const override final { + if constexpr (ManagerOptions::PointerBufferSize > 0) { + return this->GetObjectBySessionIndex(session, this->pointer_buffers_start, ManagerOptions::PointerBufferSize); + } else { + return cmif::PointerAndSize(); + } + } + + virtual cmif::PointerAndSize GetSessionSavedMessageBuffer(const ServerSession *session) const override final { + return this->GetObjectBySessionIndex(session, this->saved_messages_start, hipc::TlsMessageBufferSize); + } + public: + ServerManager() : ServerManagerBase() { + /* Clear storages. */ + std::memset(this->server_storages, 0, sizeof(this->server_storages)); + std::memset(this->server_allocated, 0, sizeof(this->server_allocated)); + std::memset(this->session_storages, 0, sizeof(this->session_storages)); + std::memset(this->session_allocated, 0, sizeof(this->session_allocated)); + std::memset(this->pointer_buffer_storage, 0, sizeof(this->pointer_buffer_storage)); + std::memset(this->saved_message_storage, 0, sizeof(this->saved_message_storage)); + + /* Set resource starts. */ + this->pointer_buffers_start = util::AlignUp(reinterpret_cast(this->pointer_buffer_storage), 0x10); + this->saved_messages_start = util::AlignUp(reinterpret_cast(this->saved_message_storage), 0x10); + } + + ~ServerManager() { + /* Close all sessions. */ + for (size_t i = 0; i < MaxSessions; i++) { + if (this->session_allocated[i]) { + this->CloseSessionImpl(GetPointer(this->session_storages[i])); + } + } + + /* Close all servers. */ + for (size_t i = 0; i < MaxServers; i++) { + if (this->server_allocated[i]) { + this->DestroyServer(GetPointer(this->server_storages[i])); + } + } + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp new file mode 100644 index 000000000..9e267f54c --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_session_manager.hpp @@ -0,0 +1,156 @@ +/* + * 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 "../sf_common.hpp" +#include "../sf_service_object.hpp" +#include "../cmif/sf_cmif_pointer_and_size.hpp" +#include "../cmif/sf_cmif_service_object_holder.hpp" +#include "sf_hipc_api.hpp" + +namespace sts::sf::hipc { + + class ServerSessionManager; + class ServerManagerBase; + + class ServerSession : public os::WaitableHolder { + friend class ServerSessionManager; + friend class ServerManagerBase; + NON_COPYABLE(ServerSession); + NON_MOVEABLE(ServerSession); + private: + cmif::ServiceObjectHolder srv_obj_holder; + cmif::PointerAndSize pointer_buffer; + cmif::PointerAndSize saved_message; + std::shared_ptr<::Service> forward_service; + Handle session_handle; + bool is_closed; + bool has_received; + public: + ServerSession(Handle h, cmif::ServiceObjectHolder &&obj) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) { + this->is_closed = false; + this->has_received = false; + this->forward_service = nullptr; + STS_ASSERT(!this->IsMitmSession()); + } + + ServerSession(Handle h, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) : WaitableHolder(h), srv_obj_holder(std::move(obj)), session_handle(h) { + this->is_closed = false; + this->has_received = false; + this->forward_service = std::move(fsrv); + STS_ASSERT(this->IsMitmSession()); + } + + bool IsMitmSession() const { + return this->forward_service != nullptr; + } + }; + + class ServerSessionManager { + private: + template + Result CreateSessionImpl(ServerSession **out, const Constructor &ctor) { + /* Allocate session. */ + ServerSession *session_memory = this->AllocateSession(); + R_UNLESS(session_memory != nullptr, ResultHipcSessionAllocationFailure); + /* Register session. */ + bool succeeded = false; + ON_SCOPE_EXIT { + if (!succeeded) { + this->DestroySession(session_memory); + } + }; + R_TRY(ctor(session_memory)); + /* Save new session to output. */ + succeeded = true; + *out = session_memory; + return ResultSuccess; + } + void DestroySession(ServerSession *session); + + Result ProcessRequestImpl(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message); + Result DispatchRequest(cmif::ServiceObjectHolder &&obj, ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message); + virtual Result DispatchManagerRequest(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message); + virtual void RegisterSessionToWaitList(ServerSession *session) = 0; + protected: + virtual ServerSession *AllocateSession() = 0; + virtual void FreeSession(ServerSession *session) = 0; + virtual cmif::PointerAndSize GetSessionPointerBuffer(const ServerSession *session) const = 0; + virtual cmif::PointerAndSize GetSessionSavedMessageBuffer(const ServerSession *session) const = 0; + + Result ReceiveRequestImpl(ServerSession *session, const cmif::PointerAndSize &message); + void CloseSessionImpl(ServerSession *session); + Result RegisterSessionImpl(ServerSession *session_memory, Handle session_handle, cmif::ServiceObjectHolder &&obj); + Result AcceptSessionImpl(ServerSession *session_memory, Handle port_handle, cmif::ServiceObjectHolder &&obj); + Result RegisterMitmSessionImpl(ServerSession *session_memory, Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv); + Result AcceptMitmSessionImpl(ServerSession *session_memory, Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv); + + Result ReceiveRequest(ServerSession *session, const cmif::PointerAndSize &message) { + return this->ReceiveRequestImpl(session, message); + } + + Result RegisterSession(ServerSession **out, Handle session_handle, cmif::ServiceObjectHolder &&obj) { + auto ctor = [&](ServerSession *session_memory) -> Result { + return this->RegisterSessionImpl(session_memory, session_handle, std::forward(obj)); + }; + return this->CreateSessionImpl(out, ctor); + } + + Result AcceptSession(ServerSession **out, Handle port_handle, cmif::ServiceObjectHolder &&obj) { + auto ctor = [&](ServerSession *session_memory) -> Result { + return this->AcceptSessionImpl(session_memory, port_handle, std::forward(obj)); + }; + return this->CreateSessionImpl(out, ctor); + } + + Result RegisterMitmSession(ServerSession **out, Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) { + auto ctor = [&](ServerSession *session_memory) -> Result { + return this->RegisterMitmSessionImpl(session_memory, mitm_session_handle, std::forward(obj), std::forward>(fsrv)); + }; + return this->CreateSessionImpl(out, ctor); + } + + Result AcceptMitmSession(ServerSession **out, Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) { + auto ctor = [&](ServerSession *session_memory) -> Result { + return this->AcceptMitmSessionImpl(session_memory, mitm_port_handle, std::forward(obj), std::forward>(fsrv)); + }; + return this->CreateSessionImpl(out, ctor); + } + + virtual ServerSessionManager *GetSessionManagerByTag(u32 tag) { + /* This is unused. */ + return this; + } + public: + Result RegisterSession(Handle session_handle, cmif::ServiceObjectHolder &&obj); + Result AcceptSession(Handle port_handle, cmif::ServiceObjectHolder &&obj); + Result RegisterMitmSession(Handle session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv); + Result AcceptMitmSession(Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv); + + template + Result AcceptSession(Handle port_handle, std::shared_ptr obj) { + return this->AcceptSession(port_handle, cmif::ServiceObjectHolder(std::move(obj))); + } + + template + Result AcceptMitmSession(Handle mitm_port_handle, std::shared_ptr obj, std::shared_ptr<::Service> &&fsrv) { + return this->AcceptMitmSession(mitm_port_handle, cmif::ServiceObjectHolder(std::move(obj)), std::forward>(fsrv)); + } + + Result ProcessRequest(ServerSession *session, const cmif::PointerAndSize &message); + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp new file mode 100644 index 000000000..ff97dffc9 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/sf/impl/sf_impl_command_serialization.hpp @@ -0,0 +1,1105 @@ +/* + * 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 "../sf_common.hpp" +#include "../sf_service_object.hpp" +#include "../sf_out.hpp" +#include "../sf_buffers.hpp" +#include "../sf_handles.hpp" +#include "../cmif/sf_cmif_pointer_and_size.hpp" +#include "../cmif/sf_cmif_service_dispatch.hpp" +#include "../cmif/sf_cmif_service_object_holder.hpp" +#include "../cmif/sf_cmif_domain_api.hpp" +#include "../hipc/sf_hipc_api.hpp" +#include "../hipc/sf_hipc_server_session_manager.hpp" + +/* Serialization classes. */ +namespace sts::sf { + + namespace impl { + + struct ProcessIdHolder { + os::ProcessId process_id; + + constexpr explicit operator os::ProcessId() const { return this->process_id; } + constexpr os::ProcessId GetValue() const { return this->process_id; } + constexpr void SetValue(const os::ProcessId &p) { this->process_id = p; } + }; + + } + + struct ClientProcessId : public impl::ProcessIdHolder {}; + static_assert(std::is_trivial::value && sizeof(ClientProcessId) == sizeof(os::ProcessId), "ClientProcessId"); + + struct ClientAppletResourceUserId : public impl::ProcessIdHolder {}; + static_assert(std::is_trivial::value && sizeof(ClientAppletResourceUserId ) == sizeof(os::ProcessId), "ClientAppletResourceUserId"); + + namespace impl { + + constexpr inline Result MarshalProcessId(ClientProcessId &client, const os::ProcessId &client_process_id) { + client.SetValue(client_process_id); + return ResultSuccess; + } + + constexpr inline Result MarshalProcessId(ClientAppletResourceUserId &client, const os::ProcessId &client_process_id) { + if (client.GetValue() != client_process_id && client.GetValue() != os::ProcessId{}) { + return ResultServiceFrameworkPreconditionViolation; + } + return ResultSuccess; + } + + } + + namespace impl { + + struct OutObjectTag{}; + + } + + template + class Out> : public impl::OutObjectTag { + static_assert(std::is_base_of::value, "Out> requires ServiceObject base."); + + template + friend class Out; + + public: + using ServiceImplType = ServiceImpl; + private: + cmif::ServiceObjectHolder *srv; + cmif::DomainObjectId *object_id; + public: + Out(cmif::ServiceObjectHolder *s, cmif::DomainObjectId *o) : srv(s), object_id(o) { /* ... */ } + + void SetValue(std::shared_ptr &&s, cmif::DomainObjectId new_object_id = cmif::InvalidDomainObjectId) { + *this->srv = ServiceObjectHolder(std::move(s)); + if (new_object_id != cmif::InvalidDomainObjectId) { + *this->object_id = new_object_id; + } + } + }; + +} + + +namespace sts::sf::impl { + + /* Machinery for filtering type lists. */ + template + struct TupleCat; + + template + struct TupleCat, std::tuple> { + using type = std::tuple; + }; + + template