mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2025-01-03 11:11:14 +00:00
sf: implement service framework enough for ro to work.
This completely re-does the whole interface for ipc servers.
This commit is contained in:
parent
bd341d5c00
commit
f4dcd1db9b
47 changed files with 3545 additions and 166 deletions
|
@ -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)
|
||||
|
|
|
@ -38,3 +38,4 @@
|
|||
#include "stratosphere/pm.hpp"
|
||||
#include "stratosphere/rnd.hpp"
|
||||
#include "stratosphere/sm.hpp"
|
||||
#include "stratosphere/sf.hpp"
|
||||
|
|
|
@ -17,4 +17,4 @@
|
|||
#pragma once
|
||||
|
||||
#include "ams/ams_types.hpp"
|
||||
#include "ams/ams_firmware_version_api.hpp"
|
||||
#include "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();
|
||||
|
||||
}
|
|
@ -21,24 +21,28 @@
|
|||
/* 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,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sts::ams {
|
||||
|
||||
enum TargetFirmware : u32 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<u64>(-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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,9 +19,19 @@
|
|||
|
||||
static constexpr u32 Module_Hipc = 11;
|
||||
|
||||
static constexpr Result ResultHipcSessionAllocationFailure = MAKERESULT(Module_Hipc, 102);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -19,6 +19,14 @@
|
|||
|
||||
static constexpr u32 Module_ServiceFramework = 10;
|
||||
|
||||
static constexpr Result ResultServiceFrameworkNotSupported = MAKERESULT(Module_ServiceFramework, 1);
|
||||
static constexpr Result ResultServiceFrameworkPreconditionViolation = MAKERESULT(Module_ServiceFramework, 3);
|
||||
|
||||
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);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <switch.h>
|
||||
#include <cstdlib>
|
||||
#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<u64>(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;
|
||||
}
|
||||
|
||||
|
|
28
stratosphere/libstratosphere/include/stratosphere/sf.hpp
Normal file
28
stratosphere/libstratosphere/include/stratosphere/sf.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#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"
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<DomainObjectId>::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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
|
||||
constexpr void *GetPointer() const {
|
||||
return reinterpret_cast<void *>(this->pointer);
|
||||
}
|
||||
|
||||
constexpr uintptr_t GetAddress() const {
|
||||
return this->pointer;
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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;
|
||||
};
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<ServiceCommandMeta>::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<typename T>
|
||||
Result ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
|
||||
static_assert(std::is_base_of<ServiceDispatchTableBase, T>::value, "ServiceDispatchTableBase::Process<T>");
|
||||
return static_cast<const T *>(this)->ProcessMessage(ctx, in_raw_data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result ProcessMessageForMitm(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
|
||||
static_assert(std::is_base_of<ServiceDispatchTableBase, T>::value, "ServiceDispatchTableBase::ProcessForMitm<T>");
|
||||
return static_cast<const T *>(this)->ProcessMessageForMitm(ctx, in_raw_data);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t N, class = std::make_index_sequence<N>>
|
||||
class ServiceDispatchTableImpl;
|
||||
|
||||
template<size_t N, size_t... Is>
|
||||
class ServiceDispatchTableImpl<N, std::index_sequence<Is...>> : public ServiceDispatchTableBase {
|
||||
private:
|
||||
template<size_t>
|
||||
using EntryType = ServiceCommandMeta;
|
||||
private:
|
||||
const std::array<ServiceCommandMeta, N> entries;
|
||||
public:
|
||||
explicit constexpr ServiceDispatchTableImpl(EntryType<Is>... 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<typename ...Entries>
|
||||
class ServiceDispatchTable : public impl::ServiceDispatchTableImpl<sizeof...(Entries)> {
|
||||
public:
|
||||
explicit constexpr ServiceDispatchTable(Entries... entries) : impl::ServiceDispatchTableImpl<sizeof...(Entries)>(entries...) { /* ... */ }
|
||||
};
|
||||
|
||||
#define DEFINE_SERVICE_DISPATCH_TABLE \
|
||||
template<typename ServiceImpl> \
|
||||
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<uintptr_t>(this->DispatchTable);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ServiceDispatchTraits {
|
||||
static_assert(std::is_base_of<sf::IServiceObject, T>::value, "ServiceObjects must derive from sf::IServiceObject");
|
||||
|
||||
using ProcessHandlerType = decltype(ServiceDispatchMeta::ProcessHandler);
|
||||
|
||||
static constexpr inline auto DispatchTable = T::template s_CmifServiceDispatchTable<T>;
|
||||
using DispatchTableType = decltype(DispatchTable);
|
||||
|
||||
static constexpr ProcessHandlerType ProcessHandlerImpl = ServiceObjectTraits<T>::IsMitmServiceObject ? (&impl::ServiceDispatchTableBase::ProcessMessageForMitm<DispatchTableType>)
|
||||
: (&impl::ServiceDispatchTableBase::ProcessMessage<DispatchTableType>);
|
||||
|
||||
static constexpr inline ServiceDispatchMeta Meta{&DispatchTable, ProcessHandlerImpl};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
NX_CONSTEXPR const ServiceDispatchMeta *GetServiceDispatchMeta() {
|
||||
return &ServiceDispatchTraits<T>::Meta;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../sf_service_object.hpp"
|
||||
#include "sf_cmif_service_dispatch.hpp"
|
||||
|
||||
namespace sts::sf::cmif {
|
||||
|
||||
class ServiceObjectHolder {
|
||||
private:
|
||||
std::shared_ptr<sf::IServiceObject> 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<typename ServiceImpl>
|
||||
constexpr explicit ServiceObjectHolder(std::shared_ptr<ServiceImpl> &&s) {
|
||||
this->srv = std::move(s);
|
||||
this->dispatch_meta = GetServiceDispatchMeta<ServiceImpl>();
|
||||
}
|
||||
|
||||
/* 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<typename ServiceImpl>
|
||||
ServiceImpl *GetServiceObject() const {
|
||||
if (this->GetServiceId() == GetServiceDispatchMeta<ServiceImpl>()->GetServiceId()) {
|
||||
return static_cast<ServiceImpl *>(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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<size_t, typename, size_t>
|
||||
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<size_t, typename, size_t>
|
||||
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<typename ServiceImpl, auto MakeShared = std::make_shared<ServiceImpl>>
|
||||
class Server : public ServerBase {
|
||||
NON_COPYABLE(Server);
|
||||
NON_MOVEABLE(Server);
|
||||
private:
|
||||
static constexpr bool IsMitmServer = ServiceObjectTraits<ServiceImpl>::IsMitmServiceObject;
|
||||
public:
|
||||
Server(Handle ph, sm::ServiceName sn, bool m, cmif::ServiceObjectHolder &&sh) : ServerBase(ph, sn, m, std::forward<cmif::ServiceObjectHolder>(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<typename ServiceImpl, auto MakeShared = std::make_shared<ServiceImpl>>
|
||||
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<ServiceImpl, MakeShared>(port_handle, service_name, managed, std::forward<cmif::ServiceObjectHolder>(static_holder));
|
||||
|
||||
if constexpr (!ServiceObjectTraits<ServiceImpl>::IsMitmServiceObject) {
|
||||
/* Non-mitm server. */
|
||||
server->SetUserData(static_cast<uintptr_t>(UserDataTag::Server));
|
||||
} else {
|
||||
/* Mitm server. */
|
||||
server->SetUserData(static_cast<uintptr_t>(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<typename ServiceImpl, auto MakeShared = std::make_shared<ServiceImpl>>
|
||||
void RegisterServer(Handle port_handle, std::shared_ptr<ServiceImpl> static_object = nullptr) {
|
||||
static_assert(!ServiceObjectTraits<ServiceImpl>::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<ServiceImpl, MakeShared>(port_handle, sm::InvalidServiceName, false, std::move(static_holder));
|
||||
}
|
||||
|
||||
template<typename ServiceImpl, auto MakeShared = std::make_shared<ServiceImpl>>
|
||||
Result RegisterServer(sm::ServiceName service_name, size_t max_sessions, std::shared_ptr<ServiceImpl> static_object = nullptr) {
|
||||
static_assert(!ServiceObjectTraits<ServiceImpl>::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<ServiceImpl, MakeShared>(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<size_t MaxServers, typename ManagerOptions = DefaultServerManagerOptions, size_t MaxSessions = ServerSessionCountMax - MaxServers>
|
||||
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<uintptr_t>(this->pointer_buffer_storage), 0x10);
|
||||
this->saved_messages_start = util::AlignUp(reinterpret_cast<uintptr_t>(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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<typename Constructor>
|
||||
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<cmif::ServiceObjectHolder>(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<cmif::ServiceObjectHolder>(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<cmif::ServiceObjectHolder>(obj), std::forward<std::shared_ptr<::Service>>(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<cmif::ServiceObjectHolder>(obj), std::forward<std::shared_ptr<::Service>>(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<typename ServiceImpl>
|
||||
Result AcceptSession(Handle port_handle, std::shared_ptr<ServiceImpl> obj) {
|
||||
return this->AcceptSession(port_handle, cmif::ServiceObjectHolder(std::move(obj)));
|
||||
}
|
||||
|
||||
template<typename ServiceImpl>
|
||||
Result AcceptMitmSession(Handle mitm_port_handle, std::shared_ptr<ServiceImpl> obj, std::shared_ptr<::Service> &&fsrv) {
|
||||
return this->AcceptMitmSession(mitm_port_handle, cmif::ServiceObjectHolder(std::move(obj)), std::forward<std::shared_ptr<::Service>>(fsrv));
|
||||
}
|
||||
|
||||
Result ProcessRequest(ServerSession *session, const cmif::PointerAndSize &message);
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "sf_common.hpp"
|
||||
#include "sf_out.hpp"
|
||||
#include "cmif/sf_cmif_pointer_and_size.hpp"
|
||||
|
||||
namespace sts::sf {
|
||||
|
||||
enum class BufferTransferMode {
|
||||
MapAlias,
|
||||
Pointer,
|
||||
AutoSelect,
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
/* Buffer utilities. */
|
||||
struct BufferBaseTag{};
|
||||
|
||||
template<BufferTransferMode TransferMode>
|
||||
constexpr inline u32 BufferTransferModeAttributes = [] {
|
||||
if constexpr (TransferMode == BufferTransferMode::MapAlias) {
|
||||
return SfBufferAttr_HipcMapAlias;
|
||||
} else if constexpr (TransferMode == BufferTransferMode::Pointer) {
|
||||
return SfBufferAttr_HipcPointer;
|
||||
} else if constexpr(TransferMode == BufferTransferMode::AutoSelect) {
|
||||
return SfBufferAttr_HipcAutoSelect;
|
||||
} else {
|
||||
static_assert(TransferMode != TransferMode, "Invalid BufferTransferMode");
|
||||
}
|
||||
}();
|
||||
|
||||
}
|
||||
|
||||
/* Helper structs for serialization of buffers. */
|
||||
struct LargeData{};
|
||||
|
||||
struct PrefersMapAliasTransferMode{};
|
||||
|
||||
struct PrefersPointerTransferMode{};
|
||||
|
||||
struct PrefersAutoSelectTransferMode{};
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsLargeData = std::is_base_of<sf::LargeData, T>::value;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline bool IsLargeData<Out<T>> = IsLargeData<T>;
|
||||
|
||||
template<typename T>
|
||||
constexpr inline size_t LargeDataSize = sizeof(T);
|
||||
|
||||
template<typename T>
|
||||
constexpr inline size_t LargeDataSize<Out<T>> = sizeof(T);
|
||||
|
||||
template<typename T>
|
||||
constexpr inline BufferTransferMode PreferredTransferMode = [] {
|
||||
constexpr bool prefers_map_alias = std::is_base_of<PrefersMapAliasTransferMode, T>::value;
|
||||
constexpr bool prefers_pointer = std::is_base_of<PrefersPointerTransferMode, T>::value;
|
||||
constexpr bool prefers_auto_select = std::is_base_of<PrefersAutoSelectTransferMode, T>::value;
|
||||
if constexpr (prefers_map_alias) {
|
||||
static_assert(!prefers_pointer && !prefers_auto_select, "Type T must only prefer one transfer mode.");
|
||||
return BufferTransferMode::MapAlias;
|
||||
} else if constexpr (prefers_pointer) {
|
||||
static_assert(!prefers_map_alias && !prefers_auto_select, "Type T must only prefer one transfer mode.");
|
||||
return BufferTransferMode::Pointer;
|
||||
} else if constexpr (prefers_auto_select) {
|
||||
static_assert(!prefers_map_alias && !prefers_pointer, "Type T must only prefer one transfer mode.");
|
||||
return BufferTransferMode::AutoSelect;
|
||||
} else if constexpr (IsLargeData<T>) {
|
||||
return BufferTransferMode::Pointer;
|
||||
} else {
|
||||
return BufferTransferMode::MapAlias;
|
||||
}
|
||||
}();
|
||||
|
||||
template<typename T>
|
||||
constexpr inline BufferTransferMode PreferredTransferMode<Out<T>> = PreferredTransferMode<T>;
|
||||
|
||||
namespace impl {
|
||||
|
||||
class BufferBase : public BufferBaseTag {
|
||||
public:
|
||||
static constexpr u32 AdditionalAttributes = 0;
|
||||
private:
|
||||
const cmif::PointerAndSize pas;
|
||||
protected:
|
||||
constexpr uintptr_t GetAddressImpl() const {
|
||||
return this->pas.GetAddress();
|
||||
}
|
||||
|
||||
constexpr size_t GetSizeImpl() const {
|
||||
return this->pas.GetSize();
|
||||
}
|
||||
public:
|
||||
constexpr BufferBase() : pas() { /* ... */ }
|
||||
constexpr BufferBase(const cmif::PointerAndSize &_pas) : pas(_pas) { /* ... */ }
|
||||
constexpr BufferBase(uintptr_t ptr, size_t sz) : pas(ptr, sz) { /* ... */ }
|
||||
};
|
||||
|
||||
class InBufferBase : public BufferBase {
|
||||
public:
|
||||
using BaseType = BufferBase;
|
||||
static constexpr u32 AdditionalAttributes = BaseType::AdditionalAttributes |
|
||||
SfBufferAttr_In;
|
||||
public:
|
||||
constexpr InBufferBase() : BaseType() { /* ... */ }
|
||||
constexpr InBufferBase(const cmif::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||
constexpr InBufferBase(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||
|
||||
constexpr InBufferBase(const void *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
constexpr InBufferBase(const u8 *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
};
|
||||
|
||||
class OutBufferBase : public BufferBase {
|
||||
public:
|
||||
using BaseType = BufferBase;
|
||||
static constexpr u32 AdditionalAttributes = BaseType::AdditionalAttributes |
|
||||
SfBufferAttr_Out;
|
||||
public:
|
||||
constexpr OutBufferBase() : BaseType() { /* ... */ }
|
||||
constexpr OutBufferBase(const cmif::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||
constexpr OutBufferBase(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||
|
||||
constexpr OutBufferBase(void *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
constexpr OutBufferBase(u8 *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
};
|
||||
|
||||
template<BufferTransferMode TMode, u32 ExtraAttributes = 0>
|
||||
class InBufferImpl : public InBufferBase {
|
||||
public:
|
||||
using BaseType = InBufferBase;
|
||||
static constexpr BufferTransferMode TransferMode = TMode;
|
||||
static constexpr u32 AdditionalAttributes = BaseType::AdditionalAttributes |
|
||||
ExtraAttributes;
|
||||
public:
|
||||
constexpr InBufferImpl() : BaseType() { /* ... */ }
|
||||
constexpr InBufferImpl(const cmif::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||
constexpr InBufferImpl(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||
|
||||
constexpr InBufferImpl(const void *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
constexpr InBufferImpl(const u8 *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
|
||||
constexpr const u8 *GetPointer() const {
|
||||
return reinterpret_cast<const u8 *>(this->GetAddressImpl());
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetSizeImpl();
|
||||
}
|
||||
};
|
||||
|
||||
template<BufferTransferMode TMode, u32 ExtraAttributes = 0>
|
||||
class OutBufferImpl : public OutBufferBase {
|
||||
public:
|
||||
using BaseType = OutBufferBase;
|
||||
static constexpr BufferTransferMode TransferMode = TMode;
|
||||
static constexpr u32 AdditionalAttributes = BaseType::AdditionalAttributes |
|
||||
ExtraAttributes;
|
||||
public:
|
||||
constexpr OutBufferImpl() : BaseType() { /* ... */ }
|
||||
constexpr OutBufferImpl(const cmif::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||
constexpr OutBufferImpl(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||
|
||||
constexpr OutBufferImpl(void *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
constexpr OutBufferImpl(u8 *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
|
||||
constexpr u8 *GetPointer() const {
|
||||
return reinterpret_cast<u8 *>(this->GetAddressImpl());
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetSizeImpl();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, BufferTransferMode TMode = PreferredTransferMode<T>>
|
||||
struct InArrayImpl : public InBufferBase {
|
||||
public:
|
||||
using BaseType = InBufferBase;
|
||||
static constexpr BufferTransferMode TransferMode = TMode;
|
||||
static constexpr u32 AdditionalAttributes = BaseType::AdditionalAttributes;
|
||||
public:
|
||||
constexpr InArrayImpl() : BaseType() { /* ... */ }
|
||||
constexpr InArrayImpl(const cmif::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||
constexpr InArrayImpl(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||
|
||||
constexpr InArrayImpl(const void *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
constexpr InArrayImpl(const T *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
|
||||
constexpr const T *GetPointer() const {
|
||||
return reinterpret_cast<const T *>(this->GetAddressImpl());
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetSizeImpl();
|
||||
}
|
||||
|
||||
constexpr const T &operator[](size_t i) const {
|
||||
return this->GetPointer()[i];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, BufferTransferMode TMode = PreferredTransferMode<T>>
|
||||
struct OutArrayImpl : public OutBufferBase {
|
||||
public:
|
||||
using BaseType = OutBufferBase;
|
||||
static constexpr BufferTransferMode TransferMode = TMode;
|
||||
static constexpr u32 AdditionalAttributes = BaseType::AdditionalAttributes;
|
||||
public:
|
||||
constexpr OutArrayImpl() : BaseType() { /* ... */ }
|
||||
constexpr OutArrayImpl(const cmif::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||
constexpr OutArrayImpl(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||
|
||||
constexpr OutArrayImpl(void *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
constexpr OutArrayImpl(T *ptr, size_t sz) : BaseType(reinterpret_cast<uintptr_t>(ptr), sz) { /* ... */ }
|
||||
|
||||
constexpr T *GetPointer() const {
|
||||
return reinterpret_cast<T *>(this->GetAddressImpl());
|
||||
}
|
||||
|
||||
constexpr size_t GetSize() const {
|
||||
return this->GetSizeImpl();
|
||||
}
|
||||
|
||||
constexpr T &operator[](size_t i) const {
|
||||
return this->GetPointer()[i];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/* Buffer Types. */
|
||||
using InBuffer = typename impl::InBufferImpl<BufferTransferMode::MapAlias>;
|
||||
using InMapAliasBuffer = typename impl::InBufferImpl<BufferTransferMode::MapAlias>;
|
||||
using InPointerBuffer = typename impl::InBufferImpl<BufferTransferMode::Pointer>;
|
||||
using InAutoSelectBuffer = typename impl::InBufferImpl<BufferTransferMode::AutoSelect>;
|
||||
using InNonSecureBuffer = typename impl::InBufferImpl<BufferTransferMode::MapAlias, SfBufferAttr_HipcMapTransferAllowsNonSecure>;
|
||||
using InNonDeviceBuffer = typename impl::InBufferImpl<BufferTransferMode::MapAlias, SfBufferAttr_HipcMapTransferAllowsNonDevice>;
|
||||
|
||||
using OutBuffer = typename impl::OutBufferImpl<BufferTransferMode::MapAlias>;
|
||||
using OutMapAliasBuffer = typename impl::OutBufferImpl<BufferTransferMode::MapAlias>;
|
||||
using OutPointerBuffer = typename impl::OutBufferImpl<BufferTransferMode::Pointer>;
|
||||
using OutAutoSelectBuffer = typename impl::OutBufferImpl<BufferTransferMode::AutoSelect>;
|
||||
using OutNonSecureBuffer = typename impl::OutBufferImpl<BufferTransferMode::MapAlias, SfBufferAttr_HipcMapTransferAllowsNonSecure>;
|
||||
using OutNonDeviceBuffer = typename impl::OutBufferImpl<BufferTransferMode::MapAlias, SfBufferAttr_HipcMapTransferAllowsNonDevice>;
|
||||
|
||||
template<typename T>
|
||||
using InArray = typename impl::InArrayImpl<T>;
|
||||
template<typename T>
|
||||
using InMapAliasArray = typename impl::InArrayImpl<T, BufferTransferMode::MapAlias>;
|
||||
template<typename T>
|
||||
using InPointerArray = typename impl::InArrayImpl<T, BufferTransferMode::Pointer>;
|
||||
template<typename T>
|
||||
using InAutoSelectArray = typename impl::InArrayImpl<T, BufferTransferMode::AutoSelect>;
|
||||
|
||||
template<typename T>
|
||||
using OutArray = typename impl::OutArrayImpl<T>;
|
||||
template<typename T>
|
||||
using OutMapAliasArray = typename impl::OutArrayImpl<T, BufferTransferMode::MapAlias>;
|
||||
template<typename T>
|
||||
using OutPointerArray = typename impl::OutArrayImpl<T, BufferTransferMode::Pointer>;
|
||||
template<typename T>
|
||||
using OutAutoSelectArray = typename impl::OutArrayImpl<T, BufferTransferMode::AutoSelect>;
|
||||
|
||||
/* Attribute serialization structs. */
|
||||
template<typename T>
|
||||
constexpr inline bool IsBuffer = [] {
|
||||
const bool is_buffer = std::is_base_of<impl::BufferBaseTag, T>::value;
|
||||
const bool is_large_data = IsLargeData<T>;
|
||||
static_assert(!(is_buffer && is_large_data), "Invalid sf::IsBuffer state");
|
||||
return is_buffer || is_large_data;
|
||||
}();
|
||||
|
||||
template<typename T>
|
||||
constexpr inline u32 BufferAttributes = [] {
|
||||
static_assert(IsBuffer<T>, "BufferAttributes requires IsBuffer");
|
||||
if constexpr (std::is_base_of<impl::BufferBaseTag, T>::value) {
|
||||
return impl::BufferTransferModeAttributes<T::TransferMode> | T::AdditionalAttributes;
|
||||
} else if constexpr (IsLargeData<T>) {
|
||||
u32 attr = SfBufferAttr_FixedSize | impl::BufferTransferModeAttributes<PreferredTransferMode<T>>;
|
||||
if constexpr (std::is_base_of<impl::OutBaseTag, T>::value) {
|
||||
attr |= SfBufferAttr_Out;
|
||||
} else {
|
||||
attr |= SfBufferAttr_In;
|
||||
}
|
||||
return attr;
|
||||
} else {
|
||||
static_assert(!std::is_same<T, T>::value, "Invalid BufferAttributes<T>");
|
||||
}
|
||||
}();
|
||||
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
#include "../defines.hpp"
|
||||
#include "../results.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "../svc.hpp"
|
||||
#include "../ams.hpp"
|
||||
#include "../os.hpp"
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "sf_common.hpp"
|
||||
#include "sf_out.hpp"
|
||||
#include "cmif/sf_cmif_pointer_and_size.hpp"
|
||||
|
||||
namespace sts::sf {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct InHandleTag{};
|
||||
struct OutHandleTag{};
|
||||
|
||||
template<u32 Attribute>
|
||||
struct InHandle : public InHandleTag {
|
||||
::Handle handle;
|
||||
|
||||
constexpr InHandle() : handle(INVALID_HANDLE) { /* ... */ }
|
||||
constexpr InHandle(::Handle h) : handle(h) { /* ... */ }
|
||||
constexpr InHandle(const InHandle &o) : handle(o.handle) { /* ... */ }
|
||||
|
||||
constexpr void operator=(const ::Handle &h) { this->handle = h; }
|
||||
constexpr void operator=(const InHandle &o) { this->handle = o.handle; }
|
||||
|
||||
constexpr /* TODO: explicit? */ operator ::Handle() const { return this->handle; }
|
||||
constexpr ::Handle GetValue() const { return this->handle; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class OutHandleImpl : public OutHandleTag {
|
||||
static_assert(std::is_base_of<InHandleTag, T>::value, "OutHandleImpl requires InHandle base");
|
||||
private:
|
||||
T *ptr;
|
||||
public:
|
||||
constexpr OutHandleImpl(T *p) : ptr(p) { /* ... */ }
|
||||
|
||||
constexpr void SetValue(const Handle &value) {
|
||||
*this->ptr = value;
|
||||
}
|
||||
|
||||
constexpr void SetValue(const T &value) {
|
||||
*this->ptr = value;
|
||||
}
|
||||
|
||||
constexpr const T &GetValue() const {
|
||||
return *this->ptr;
|
||||
}
|
||||
|
||||
constexpr T *GetPointer() const {
|
||||
return this->ptr;
|
||||
}
|
||||
|
||||
constexpr Handle *GetHandlePointer() const {
|
||||
return &this->ptr->handle;
|
||||
}
|
||||
|
||||
constexpr T &operator *() const {
|
||||
return *this->ptr;
|
||||
}
|
||||
|
||||
constexpr T *operator ->() const {
|
||||
return this->ptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using MoveHandle = typename impl::InHandle<SfOutHandleAttr_HipcMove>;
|
||||
using CopyHandle = typename impl::InHandle<SfOutHandleAttr_HipcCopy>;
|
||||
|
||||
static_assert(sizeof(MoveHandle) == sizeof(::Handle), "sizeof(MoveHandle)");
|
||||
static_assert(sizeof(CopyHandle) == sizeof(::Handle), "sizeof(CopyHandle)");
|
||||
|
||||
template<>
|
||||
class IsOutForceEnabled<MoveHandle> : public std::true_type{};
|
||||
template<>
|
||||
class IsOutForceEnabled<CopyHandle> : public std::true_type{};
|
||||
|
||||
template<>
|
||||
class Out<MoveHandle> : public impl::OutHandleImpl<MoveHandle> {
|
||||
private:
|
||||
using T = MoveHandle;
|
||||
using Base = impl::OutHandleImpl<T>;
|
||||
public:
|
||||
constexpr Out<T>(T *p) : Base(p) { /* ... */ }
|
||||
|
||||
constexpr void SetValue(const Handle &value) {
|
||||
Base::SetValue(value);
|
||||
}
|
||||
|
||||
constexpr void SetValue(const T &value) {
|
||||
Base::SetValue(value);
|
||||
}
|
||||
|
||||
constexpr const T &GetValue() const {
|
||||
return Base::GetValue();
|
||||
}
|
||||
|
||||
constexpr T *GetPointer() const {
|
||||
return Base::GetPointer();
|
||||
}
|
||||
|
||||
constexpr Handle *GetHandlePointer() const {
|
||||
return Base::GetHandlePointer();
|
||||
}
|
||||
|
||||
constexpr T &operator *() const {
|
||||
return Base::operator*();
|
||||
}
|
||||
|
||||
constexpr T *operator ->() const {
|
||||
return Base::operator->();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class Out<CopyHandle> : public impl::OutHandleImpl<CopyHandle> {
|
||||
private:
|
||||
using T = CopyHandle;
|
||||
using Base = impl::OutHandleImpl<T>;
|
||||
public:
|
||||
constexpr Out<T>(T *p) : Base(p) { /* ... */ }
|
||||
|
||||
constexpr void SetValue(const Handle &value) {
|
||||
Base::SetValue(value);
|
||||
}
|
||||
|
||||
constexpr void SetValue(const T &value) {
|
||||
Base::SetValue(value);
|
||||
}
|
||||
|
||||
constexpr const T &GetValue() const {
|
||||
return Base::GetValue();
|
||||
}
|
||||
|
||||
constexpr T *GetPointer() const {
|
||||
return Base::GetPointer();
|
||||
}
|
||||
|
||||
constexpr Handle *GetHandlePointer() const {
|
||||
return Base::GetHandlePointer();
|
||||
}
|
||||
|
||||
constexpr T &operator *() const {
|
||||
return Base::operator*();
|
||||
}
|
||||
|
||||
constexpr T *operator ->() const {
|
||||
return Base::operator->();
|
||||
}
|
||||
};
|
||||
|
||||
using OutMoveHandle = sf::Out<sf::MoveHandle>;
|
||||
using OutCopyHandle = sf::Out<sf::CopyHandle>;
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "sf_common.hpp"
|
||||
#include "cmif/sf_cmif_pointer_and_size.hpp"
|
||||
|
||||
namespace sts::sf {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct OutBaseTag{};
|
||||
|
||||
}
|
||||
|
||||
template<typename>
|
||||
struct IsOutForceEnabled : public std::false_type{};
|
||||
|
||||
template<typename T>
|
||||
using IsOutEnabled = typename std::enable_if<std::is_trivial<T>::value || IsOutForceEnabled<T>::value>::type;
|
||||
|
||||
template<typename T, typename = IsOutEnabled<T>>
|
||||
class Out : public impl::OutBaseTag {
|
||||
public:
|
||||
static constexpr size_t TypeSize = sizeof(T);
|
||||
private:
|
||||
T *ptr;
|
||||
public:
|
||||
constexpr Out(uintptr_t p) : ptr(reinterpret_cast<T *>(p)) { /* ... */ }
|
||||
constexpr Out(T *p) : ptr(p) { /* ... */ }
|
||||
constexpr Out(const cmif::PointerAndSize &pas) : ptr(reinterpret_cast<T *>(pas.GetAddress())) { /* TODO: Is STS_ASSERT(pas.GetSize() >= sizeof(T)); necessary? */ }
|
||||
|
||||
void SetValue(const T& value) const {
|
||||
*this->ptr = value;
|
||||
}
|
||||
|
||||
const T &GetValue() const {
|
||||
return *this->ptr;
|
||||
}
|
||||
|
||||
T *GetPointer() const {
|
||||
return this->ptr;
|
||||
}
|
||||
|
||||
/* Convenience operators. */
|
||||
T &operator*() const {
|
||||
return *this->ptr;
|
||||
}
|
||||
|
||||
T *operator->() const {
|
||||
return this->ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Out<T *> {
|
||||
static_assert(!std::is_same<T, T>::value, "Invalid sf::Out<T> (Raw Pointer)");
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "sf_common.hpp"
|
||||
#include "sf_out.hpp"
|
||||
|
||||
namespace sts::sf {
|
||||
|
||||
class IServiceObject{};
|
||||
|
||||
class IMitmServiceObject : public IServiceObject {
|
||||
protected:
|
||||
std::shared_ptr<::Service> forward_service;
|
||||
os::ProcessId process_id;
|
||||
ncm::TitleId title_id;
|
||||
public:
|
||||
IMitmServiceObject(std::shared_ptr<::Service> &&s, os::ProcessId p, ncm::TitleId t) : forward_service(std::move(s)), process_id(p), title_id(t) { /* ... */ }
|
||||
|
||||
static bool ShouldMitm(os::ProcessId process_id, ncm::TitleId title_id);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ServiceObjectTraits {
|
||||
static_assert(std::is_base_of<sts::sf::IServiceObject, T>::value, "ServiceObjectTraits requires ServiceObject");
|
||||
|
||||
static constexpr bool IsMitmServiceObject = std::is_base_of<IMitmServiceObject, T>::value;
|
||||
};
|
||||
|
||||
}
|
|
@ -25,7 +25,7 @@ namespace sts::sm::mitm {
|
|||
Result InstallMitm(Handle *out_port, Handle *out_query, ServiceName name);
|
||||
Result UninstallMitm(ServiceName name);
|
||||
Result DeclareFutureMitm(ServiceName name);
|
||||
Result AcknowledgeSession(Service *out_service, u64 *out_pid, ncm::TitleId *out_tid, ServiceName name);
|
||||
Result AcknowledgeSession(Service *out_service, os::ProcessId *out_pid, ncm::TitleId *out_tid, ServiceName name);
|
||||
Result HasMitm(bool *out, ServiceName name);
|
||||
Result WaitMitm(ServiceName name);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <switch.h>
|
||||
#include "../defines.hpp"
|
||||
#include "../results.hpp"
|
||||
#include "../os.hpp"
|
||||
|
||||
namespace sts::sm {
|
||||
|
||||
|
@ -60,18 +61,15 @@ namespace sts::sm {
|
|||
/* For Debug Monitor extensions. */
|
||||
struct ServiceRecord {
|
||||
ServiceName service;
|
||||
u64 owner_pid;
|
||||
os::ProcessId owner_pid;
|
||||
u64 max_sessions;
|
||||
u64 mitm_pid;
|
||||
u64 mitm_waiting_ack_pid;
|
||||
os::ProcessId mitm_pid;
|
||||
os::ProcessId mitm_waiting_ack_pid;
|
||||
bool is_light;
|
||||
bool mitm_waiting_ack;
|
||||
};
|
||||
static_assert(sizeof(ServiceRecord) == 0x30, "ServiceRecord definition!");
|
||||
|
||||
/* For process validation. */
|
||||
static constexpr u64 InvalidProcessId = static_cast<u64>(-1ull);
|
||||
|
||||
/* Utility, for scoped access to libnx services. */
|
||||
template<Result Initializer(), void Finalizer()>
|
||||
class ScopedServiceHolder {
|
||||
|
|
|
@ -303,7 +303,7 @@ namespace sts::util {
|
|||
iterator first(_first.GetNonConstIterator());
|
||||
iterator last(_last.GetNonConstIterator());
|
||||
first->Unlink(&*last);
|
||||
pos->SplicePrev(&*first, &*last);
|
||||
pos->SplicePrev(&*first, &*first);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::ams {
|
||||
namespace sts::hos {
|
||||
|
||||
namespace {
|
||||
|
||||
FirmwareVersion g_firmware_version;
|
||||
hos::Version g_hos_version;
|
||||
bool g_has_cached;
|
||||
os::Mutex g_mutex;
|
||||
|
||||
|
@ -49,35 +49,35 @@ namespace sts::ams {
|
|||
|
||||
switch (static_cast<ams::TargetFirmware>(target_fw)) {
|
||||
case ams::TargetFirmware_100:
|
||||
g_firmware_version = FirmwareVersion_100;
|
||||
g_hos_version = hos::Version_100;
|
||||
break;
|
||||
case ams::TargetFirmware_200:
|
||||
g_firmware_version = FirmwareVersion_200;
|
||||
g_hos_version = hos::Version_200;
|
||||
break;
|
||||
case ams::TargetFirmware_300:
|
||||
g_firmware_version = FirmwareVersion_300;
|
||||
g_hos_version = hos::Version_300;
|
||||
break;
|
||||
case ams::TargetFirmware_400:
|
||||
g_firmware_version = FirmwareVersion_400;
|
||||
g_hos_version = hos::Version_400;
|
||||
break;
|
||||
case ams::TargetFirmware_500:
|
||||
g_firmware_version = FirmwareVersion_500;
|
||||
g_hos_version = hos::Version_500;
|
||||
break;
|
||||
case ams::TargetFirmware_600:
|
||||
case ams::TargetFirmware_620:
|
||||
g_firmware_version = FirmwareVersion_600;
|
||||
g_hos_version = hos::Version_600;
|
||||
break;
|
||||
case ams::TargetFirmware_700:
|
||||
g_firmware_version = FirmwareVersion_700;
|
||||
g_hos_version = hos::Version_700;
|
||||
break;
|
||||
case ams::TargetFirmware_800:
|
||||
g_firmware_version = FirmwareVersion_800;
|
||||
g_hos_version = hos::Version_800;
|
||||
break;
|
||||
case ams::TargetFirmware_810:
|
||||
g_firmware_version = FirmwareVersion_810;
|
||||
g_hos_version = hos::Version_810;
|
||||
break;
|
||||
case ams::TargetFirmware_900:
|
||||
g_firmware_version = FirmwareVersion_900;
|
||||
g_hos_version = hos::Version_900;
|
||||
break;
|
||||
STS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
@ -87,60 +87,60 @@ namespace sts::ams {
|
|||
|
||||
}
|
||||
|
||||
FirmwareVersion GetRuntimeFirmwareVersion() {
|
||||
::sts::hos::Version GetVersion() {
|
||||
CacheValues();
|
||||
return g_firmware_version;
|
||||
return g_hos_version;
|
||||
}
|
||||
|
||||
void SetFirmwareVersionForLibnx() {
|
||||
void SetVersionForLibnx() {
|
||||
u32 major = 0, minor = 0, micro = 0;
|
||||
switch (GetRuntimeFirmwareVersion()) {
|
||||
case FirmwareVersion_100:
|
||||
switch (hos::GetVersion()) {
|
||||
case hos::Version_100:
|
||||
major = 1;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_200:
|
||||
case hos::Version_200:
|
||||
major = 2;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_300:
|
||||
case hos::Version_300:
|
||||
major = 3;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_400:
|
||||
case hos::Version_400:
|
||||
major = 4;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_500:
|
||||
case hos::Version_500:
|
||||
major = 5;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_600:
|
||||
case hos::Version_600:
|
||||
major = 6;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_700:
|
||||
case hos::Version_700:
|
||||
major = 7;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_800:
|
||||
case hos::Version_800:
|
||||
major = 8;
|
||||
minor = 0;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_810:
|
||||
case hos::Version_810:
|
||||
major = 8;
|
||||
minor = 1;
|
||||
micro = 0;
|
||||
break;
|
||||
case FirmwareVersion_900:
|
||||
case hos::Version_900:
|
||||
major = 9;
|
||||
minor = 0;
|
||||
micro = 0;
|
|
@ -23,26 +23,26 @@ namespace sts::cfg {
|
|||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr u64 InitialProcessIdMinDeprecated = 0x00;
|
||||
constexpr u64 InitialProcessIdMaxDeprecated = 0x50;
|
||||
constexpr os::ProcessId InitialProcessIdMinDeprecated = {0x00};
|
||||
constexpr os::ProcessId InitialProcessIdMaxDeprecated = {0x50};
|
||||
|
||||
/* Privileged process globals. */
|
||||
os::Mutex g_lock;
|
||||
bool g_got_privileged_process_status = false;
|
||||
u64 g_min_initial_process_id = 0, g_max_initial_process_id = 0;
|
||||
u64 g_cur_process_id = 0;
|
||||
os::ProcessId g_min_initial_process_id = os::InvalidProcessId, g_max_initial_process_id = os::InvalidProcessId;
|
||||
os::ProcessId g_cur_process_id = os::InvalidProcessId;
|
||||
|
||||
/* SD card helpers. */
|
||||
void GetPrivilegedProcessIdRange(u64 *out_min, u64 *out_max) {
|
||||
u64 min = 0, max = 0;
|
||||
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
|
||||
void GetPrivilegedProcessIdRange(os::ProcessId *out_min, os::ProcessId *out_max) {
|
||||
os::ProcessId min = os::InvalidProcessId, max = os::InvalidProcessId;
|
||||
if (hos::GetVersion() >= hos::Version_500) {
|
||||
/* On 5.0.0+, we can get precise limits from svcGetSystemInfo. */
|
||||
R_ASSERT(svcGetSystemInfo(&min, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum));
|
||||
R_ASSERT(svcGetSystemInfo(&max, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum));
|
||||
} else if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_400) {
|
||||
R_ASSERT(svcGetSystemInfo(reinterpret_cast<u64 *>(&min), SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum));
|
||||
R_ASSERT(svcGetSystemInfo(reinterpret_cast<u64 *>(&max), SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum));
|
||||
} else if (hos::GetVersion() >= hos::Version_400) {
|
||||
/* On 4.0.0-4.1.0, we can get the precise limits from normal svcGetInfo. */
|
||||
R_ASSERT(svcGetInfo(&min, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum));
|
||||
R_ASSERT(svcGetInfo(&max, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum));
|
||||
R_ASSERT(svcGetInfo(reinterpret_cast<u64 *>(&min), InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum));
|
||||
R_ASSERT(svcGetInfo(reinterpret_cast<u64 *>(&max), InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum));
|
||||
} else {
|
||||
/* On < 4.0.0, we just use hardcoded extents. */
|
||||
min = InitialProcessIdMinDeprecated;
|
||||
|
@ -53,15 +53,9 @@ namespace sts::cfg {
|
|||
*out_max = max;
|
||||
}
|
||||
|
||||
u64 GetCurrentProcessId() {
|
||||
u64 process_id = 0;
|
||||
R_ASSERT(svcGetProcessId(&process_id, CUR_PROCESS_HANDLE));
|
||||
return process_id;
|
||||
}
|
||||
|
||||
void GetPrivilegedProcessStatus() {
|
||||
GetPrivilegedProcessIdRange(&g_min_initial_process_id, &g_max_initial_process_id);
|
||||
g_cur_process_id = GetCurrentProcessId();
|
||||
g_cur_process_id = os::GetCurrentProcessId();
|
||||
g_got_privileged_process_status = true;
|
||||
}
|
||||
|
||||
|
@ -80,7 +74,7 @@ namespace sts::cfg {
|
|||
return g_min_initial_process_id <= g_cur_process_id && g_cur_process_id <= g_max_initial_process_id;
|
||||
}
|
||||
|
||||
void GetInitialProcessRange(u64 *out_min, u64 *out_max) {
|
||||
void GetInitialProcessRange(os::ProcessId *out_min, os::ProcessId *out_max) {
|
||||
std::scoped_lock lk(g_lock);
|
||||
|
||||
/* If we've not detected, do detection. */
|
||||
|
|
|
@ -184,7 +184,7 @@ namespace sts::map {
|
|||
R_TRY(svcGetInfo(&out->heap_size, InfoType_HeapRegionSize, process_h, 0));
|
||||
R_TRY(svcGetInfo(&out->alias_base, InfoType_AliasRegionAddress, process_h, 0));
|
||||
R_TRY(svcGetInfo(&out->alias_size, InfoType_AliasRegionSize, process_h, 0));
|
||||
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
|
||||
if (hos::GetVersion() >= hos::Version_200) {
|
||||
R_TRY(svcGetInfo(&out->aslr_base, InfoType_AslrRegionAddress, process_h, 0));
|
||||
R_TRY(svcGetInfo(&out->aslr_size, InfoType_AslrRegionSize, process_h, 0));
|
||||
} else {
|
||||
|
@ -205,7 +205,7 @@ namespace sts::map {
|
|||
}
|
||||
|
||||
Result LocateMappableSpace(uintptr_t *out_address, size_t size) {
|
||||
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
|
||||
if (hos::GetVersion() >= hos::Version_200) {
|
||||
return LocateMappableSpaceModern(out_address, size);
|
||||
} else {
|
||||
return LocateMappableSpaceDeprecated(out_address, size);
|
||||
|
@ -213,7 +213,7 @@ namespace sts::map {
|
|||
}
|
||||
|
||||
Result MapCodeMemoryInProcess(MappedCodeMemory &out_mcm, Handle process_handle, uintptr_t base_address, size_t size) {
|
||||
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
|
||||
if (hos::GetVersion() >= hos::Version_200) {
|
||||
return MapCodeMemoryInProcessModern(out_mcm, process_handle, base_address, size);
|
||||
} else {
|
||||
return MapCodeMemoryInProcessDeprecated(out_mcm, process_handle, base_address, size);
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::sf::cmif {
|
||||
|
||||
Result impl::ServiceDispatchTableBase::ProcessMessageImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const {
|
||||
/* Get versioning info. */
|
||||
const auto hos_version = hos::GetVersion();
|
||||
const u32 max_cmif_version = hos_version >= hos::Version_500 ? 1 : 0;
|
||||
|
||||
/* Parse the CMIF in header. */
|
||||
const CmifInHeader *in_header = reinterpret_cast<const CmifInHeader *>(in_raw_data.GetPointer());
|
||||
R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), ResultServiceFrameworkInvalidCmifHeaderSize);
|
||||
R_UNLESS(in_header->magic == CMIF_IN_HEADER_MAGIC && in_header->version <= max_cmif_version, ResultServiceFrameworkInvalidCmifInHeader);
|
||||
const u32 cmd_id = in_header->command_id;
|
||||
|
||||
/* Find a handler. */
|
||||
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
if (entries[i].Matches(cmd_id, hos_version)) {
|
||||
cmd_handler = entries[i].GetHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
R_UNLESS(cmd_handler != nullptr, ResultServiceFrameworkUnknownCmifCommandId);
|
||||
|
||||
/* Invoke handler. */
|
||||
CmifOutHeader *out_header = nullptr;
|
||||
Result command_result = cmd_handler(&out_header, ctx, cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*out_header), in_raw_data.GetSize() - sizeof(*out_header)));
|
||||
|
||||
/* Forward forwardable results, otherwise ensure we can send result to user. */
|
||||
R_TRY_CATCH(command_result) {
|
||||
R_CATCH(ResultServiceFrameworkRequestDeferredByUser) { return ResultServiceFrameworkRequestDeferredByUser; }
|
||||
R_CATCH_ALL() { STS_ASSERT(out_header != nullptr); }
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Write output header to raw data. */
|
||||
if (out_header != nullptr) {
|
||||
*out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result, 0};
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result impl::ServiceDispatchTableBase::ProcessMessageForMitmImpl(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data, const ServiceCommandMeta *entries, const size_t entry_count) const {
|
||||
/* Get versioning info. */
|
||||
const auto hos_version = hos::GetVersion();
|
||||
const u32 max_cmif_version = hos_version >= hos::Version_500 ? 1 : 0;
|
||||
|
||||
/* Parse the CMIF in header. */
|
||||
const CmifInHeader *in_header = reinterpret_cast<const CmifInHeader *>(in_raw_data.GetPointer());
|
||||
R_UNLESS(in_raw_data.GetSize() >= sizeof(*in_header), ResultServiceFrameworkInvalidCmifHeaderSize);
|
||||
R_UNLESS(in_header->magic == CMIF_IN_HEADER_MAGIC && in_header->version <= max_cmif_version, ResultServiceFrameworkInvalidCmifInHeader);
|
||||
const u32 cmd_id = in_header->command_id;
|
||||
|
||||
/* Find a handler. */
|
||||
decltype(ServiceCommandMeta::handler) cmd_handler = nullptr;
|
||||
for (size_t i = 0; i < entry_count; i++) {
|
||||
if (entries[i].Matches(cmd_id, hos_version)) {
|
||||
cmd_handler = entries[i].GetHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't find a handler, forward the request. */
|
||||
if (cmd_handler == nullptr) {
|
||||
/* TODO: FORWARD REQUEST */
|
||||
STS_ASSERT(false);
|
||||
}
|
||||
|
||||
/* Invoke handler. */
|
||||
CmifOutHeader *out_header = nullptr;
|
||||
Result command_result = cmd_handler(&out_header, ctx, cmif::PointerAndSize(in_raw_data.GetAddress() + sizeof(*out_header), in_raw_data.GetSize() - sizeof(*out_header)));
|
||||
|
||||
/* Forward forwardable results, otherwise ensure we can send result to user. */
|
||||
R_TRY_CATCH(command_result) {
|
||||
R_CATCH(ResultServiceFrameworkRequestDeferredByUser) { return ResultServiceFrameworkRequestDeferredByUser; }
|
||||
R_CATCH(ResultAtmosphereMitmShouldForwardToSession) {
|
||||
/* TODO: Restore TLS. */
|
||||
/* TODO: FORWARD REQUEST */
|
||||
STS_ASSERT(false);
|
||||
}
|
||||
R_CATCH_ALL() { STS_ASSERT(out_header != nullptr); }
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Write output header to raw data. */
|
||||
if (out_header != nullptr) {
|
||||
*out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result, 0};
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <functional>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::sf::cmif {
|
||||
|
||||
Result ServiceObjectHolder::ProcessMessage(ServiceDispatchContext &ctx, const cmif::PointerAndSize &in_raw_data) const {
|
||||
const auto ProcessHandler = this->dispatch_meta->ProcessHandler;
|
||||
const auto *DispatchTable = this->dispatch_meta->DispatchTable;
|
||||
return (DispatchTable->*ProcessHandler)(ctx, in_raw_data);
|
||||
}
|
||||
|
||||
}
|
86
stratosphere/libstratosphere/source/sf/hipc/sf_hipc_api.cpp
Normal file
86
stratosphere/libstratosphere/source/sf/hipc/sf_hipc_api.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::sf::hipc {
|
||||
|
||||
namespace {
|
||||
|
||||
NX_INLINE Result ReceiveImpl(Handle session_handle, void *message_buf, size_t message_buf_size) {
|
||||
s32 unused_index;
|
||||
if (message_buf == armGetTls()) {
|
||||
/* Consider: STS_ASSERT(message_buf_size == TlsMessageBufferSize); */
|
||||
return svcReplyAndReceive(&unused_index, &session_handle, 1, INVALID_HANDLE, U64_MAX);
|
||||
} else {
|
||||
return svcReplyAndReceiveWithUserBuffer(&unused_index, message_buf, message_buf_size, &session_handle, 1, INVALID_HANDLE, U64_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
NX_INLINE Result ReplyImpl(Handle session_handle, void *message_buf, size_t message_buf_size) {
|
||||
s32 unused_index;
|
||||
if (message_buf == armGetTls()) {
|
||||
/* Consider: STS_ASSERT(message_buf_size == TlsMessageBufferSize); */
|
||||
return svcReplyAndReceive(&unused_index, &session_handle, 0, session_handle, 0);
|
||||
} else {
|
||||
return svcReplyAndReceiveWithUserBuffer(&unused_index, message_buf, message_buf_size, &session_handle, 0, session_handle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result Receive(ReceiveResult *out_recv_result, Handle session_handle, const cmif::PointerAndSize &message_buffer) {
|
||||
R_TRY_CATCH(ReceiveImpl(session_handle, message_buffer.GetPointer(), message_buffer.GetSize())) {
|
||||
R_CATCH(ResultKernelConnectionClosed) {
|
||||
*out_recv_result = ReceiveResult::Closed;
|
||||
return ResultSuccess;
|
||||
}
|
||||
R_CATCH(ResultKernelReceiveListBroken) {
|
||||
*out_recv_result = ReceiveResult::NeedsRetry;
|
||||
return ResultSuccess;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
*out_recv_result = ReceiveResult::Success;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Receive(bool *out_closed, Handle session_handle, const cmif::PointerAndSize &message_buffer) {
|
||||
R_TRY_CATCH(ReceiveImpl(session_handle, message_buffer.GetPointer(), message_buffer.GetSize())) {
|
||||
R_CATCH(ResultKernelConnectionClosed) {
|
||||
*out_closed = true;
|
||||
return ResultSuccess;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
*out_closed = false;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Reply(Handle session_handle, const cmif::PointerAndSize &message_buffer) {
|
||||
R_TRY_CATCH(ReplyImpl(session_handle, message_buffer.GetPointer(), message_buffer.GetSize())) {
|
||||
R_CATCH(ResultKernelTimedOut) { return ResultSuccess; }
|
||||
R_CATCH(ResultKernelConnectionClosed) { return ResultSuccess; }
|
||||
} R_END_TRY_CATCH;
|
||||
/* ReplyImpl should *always* return an error. */
|
||||
STS_ASSERT(false);
|
||||
}
|
||||
|
||||
Result CreateSession(Handle *out_server_handle, Handle *out_client_handle) {
|
||||
R_TRY_CATCH(svcCreateSession(out_server_handle, out_client_handle, 0, 0)) {
|
||||
R_CATCH(ResultKernelResourceExhausted) { return ResultHipcOutOfSessions; }
|
||||
} R_END_TRY_CATCH;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::sf::hipc {
|
||||
|
||||
ServerManagerBase::ServerBase::~ServerBase() { /* Pure virtual destructor, to prevent linker errors. */ }
|
||||
|
||||
void ServerManagerBase::RegisterSessionToWaitList(ServerSession *session) {
|
||||
session->has_received = false;
|
||||
|
||||
/* Set user data tag. */
|
||||
session->SetUserData(static_cast<uintptr_t>(UserDataTag::Session));
|
||||
|
||||
this->RegisterToWaitList(session);
|
||||
}
|
||||
|
||||
void ServerManagerBase::RegisterToWaitList(os::WaitableHolder *holder) {
|
||||
std::scoped_lock lk(this->waitlist_mutex);
|
||||
this->waitlist.LinkWaitableHolder(holder);
|
||||
this->notify_event.Signal();
|
||||
}
|
||||
|
||||
void ServerManagerBase::ProcessWaitList() {
|
||||
std::scoped_lock lk(this->waitlist_mutex);
|
||||
this->waitable_manager.MoveAllFrom(&this->waitlist);
|
||||
}
|
||||
|
||||
os::WaitableHolder *ServerManagerBase::WaitSignaled() {
|
||||
std::scoped_lock lk(this->waitable_selection_mutex);
|
||||
while (true) {
|
||||
this->ProcessWaitList();
|
||||
auto selected = this->waitable_manager.WaitAny();
|
||||
if (selected == &this->request_stop_event_holder) {
|
||||
return nullptr;
|
||||
} else if (selected == &this->notify_event_holder) {
|
||||
this->notify_event.Reset();
|
||||
} else {
|
||||
selected->UnlinkFromWaitableManager();
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManagerBase::ResumeProcessing() {
|
||||
this->request_stop_event.Reset();
|
||||
}
|
||||
|
||||
void ServerManagerBase::RequestStopProcessing() {
|
||||
this->request_stop_event.Signal();
|
||||
}
|
||||
|
||||
void ServerManagerBase::AddUserWaitableHolder(os::WaitableHolder *waitable) {
|
||||
const auto user_data_tag = static_cast<UserDataTag>(waitable->GetUserData());
|
||||
STS_ASSERT(user_data_tag != UserDataTag::Server);
|
||||
STS_ASSERT(user_data_tag != UserDataTag::MitmServer);
|
||||
STS_ASSERT(user_data_tag != UserDataTag::Session);
|
||||
this->RegisterToWaitList(waitable);
|
||||
}
|
||||
|
||||
Result ServerManagerBase::ProcessForServer(os::WaitableHolder *holder) {
|
||||
STS_ASSERT(static_cast<UserDataTag>(holder->GetUserData()) == UserDataTag::Server);
|
||||
|
||||
ServerBase *server = static_cast<ServerBase *>(holder);
|
||||
ON_SCOPE_EXIT { this->RegisterToWaitList(server); };
|
||||
|
||||
/* Create resources for new session. */
|
||||
cmif::ServiceObjectHolder obj;
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
server->CreateSessionObjectHolder(&obj, &fsrv);
|
||||
|
||||
/* Not a mitm server, so we must have no forward service. */
|
||||
STS_ASSERT(fsrv == nullptr);
|
||||
|
||||
/* Try to accept. */
|
||||
return this->AcceptSession(server->port_handle, std::move(obj));
|
||||
}
|
||||
|
||||
Result ServerManagerBase::ProcessForMitmServer(os::WaitableHolder *holder) {
|
||||
STS_ASSERT(static_cast<UserDataTag>(holder->GetUserData()) == UserDataTag::MitmServer);
|
||||
|
||||
ServerBase *server = static_cast<ServerBase *>(holder);
|
||||
ON_SCOPE_EXIT { this->RegisterToWaitList(server); };
|
||||
|
||||
/* Create resources for new session. */
|
||||
cmif::ServiceObjectHolder obj;
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
server->CreateSessionObjectHolder(&obj, &fsrv);
|
||||
|
||||
/* Mitm server, so we must have forward service. */
|
||||
STS_ASSERT(fsrv != nullptr);
|
||||
|
||||
/* Try to accept. */
|
||||
return this->AcceptMitmSession(server->port_handle, std::move(obj), std::move(fsrv));
|
||||
}
|
||||
|
||||
Result ServerManagerBase::ProcessForSession(os::WaitableHolder *holder) {
|
||||
STS_ASSERT(static_cast<UserDataTag>(holder->GetUserData()) == UserDataTag::Session);
|
||||
|
||||
ServerSession *session = static_cast<ServerSession *>(holder);
|
||||
|
||||
cmif::PointerAndSize tls_message(armGetTls(), hipc::TlsMessageBufferSize);
|
||||
const cmif::PointerAndSize &saved_message = session->saved_message;
|
||||
STS_ASSERT(tls_message.GetSize() == saved_message.GetSize());
|
||||
if (!session->has_received) {
|
||||
R_TRY(this->ReceiveRequest(session, tls_message));
|
||||
session->has_received = true;
|
||||
std::memcpy(saved_message.GetPointer(), tls_message.GetPointer(), tls_message.GetSize());
|
||||
} else {
|
||||
/* We were deferred and are re-receiving, so just memcpy. */
|
||||
std::memcpy(tls_message.GetPointer(), saved_message.GetPointer(), tls_message.GetSize());
|
||||
}
|
||||
|
||||
return this->ProcessRequest(session, tls_message);
|
||||
}
|
||||
|
||||
Result ServerManagerBase::Process(os::WaitableHolder *holder) {
|
||||
switch (static_cast<UserDataTag>(holder->GetUserData())) {
|
||||
case UserDataTag::Server:
|
||||
return this->ProcessForServer(holder);
|
||||
break;
|
||||
case UserDataTag::MitmServer:
|
||||
return this->ProcessForMitmServer(holder);
|
||||
break;
|
||||
case UserDataTag::Session:
|
||||
/* TODO: Process deferred. */
|
||||
return this->ProcessForSession(holder);
|
||||
break;
|
||||
STS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerManagerBase::WaitAndProcessImpl() {
|
||||
auto waitable = this->WaitSignaled();
|
||||
if (!waitable) {
|
||||
return false;
|
||||
}
|
||||
R_ASSERT(this->Process(waitable));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServerManagerBase::WaitAndProcess() {
|
||||
this->WaitAndProcessImpl();
|
||||
}
|
||||
|
||||
void ServerManagerBase::LoopProcess() {
|
||||
while (this->WaitAndProcessImpl()) {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace sts::sf::hipc {
|
||||
|
||||
void ServerSessionManager::DestroySession(ServerSession *session) {
|
||||
/* Destroy object. */
|
||||
session->~ServerSession();
|
||||
/* Free object memory. */
|
||||
this->FreeSession(session);
|
||||
}
|
||||
|
||||
void ServerSessionManager::CloseSessionImpl(ServerSession *session) {
|
||||
const Handle session_handle = session->session_handle;
|
||||
this->DestroySession(session);
|
||||
R_ASSERT(svcCloseHandle(session_handle));
|
||||
}
|
||||
|
||||
Result ServerSessionManager::RegisterSessionImpl(ServerSession *session_memory, Handle session_handle, cmif::ServiceObjectHolder &&obj) {
|
||||
/* Create session object. */
|
||||
new (session_memory) ServerSession(session_handle, std::forward<cmif::ServiceObjectHolder>(obj));
|
||||
/* Assign session resources. */
|
||||
session_memory->pointer_buffer = this->GetSessionPointerBuffer(session_memory);
|
||||
session_memory->saved_message = this->GetSessionSavedMessageBuffer(session_memory);
|
||||
/* Register to wait list. */
|
||||
this->RegisterSessionToWaitList(session_memory);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ServerSessionManager::AcceptSessionImpl(ServerSession *session_memory, Handle port_handle, cmif::ServiceObjectHolder &&obj) {
|
||||
/* Create session handle. */
|
||||
Handle session_handle;
|
||||
R_TRY(svcAcceptSession(&session_handle, port_handle));
|
||||
bool succeeded = false;
|
||||
ON_SCOPE_EXIT {
|
||||
if (!succeeded) {
|
||||
R_ASSERT(svcCloseHandle(session_handle));
|
||||
}
|
||||
};
|
||||
/* Register session. */
|
||||
R_TRY(this->RegisterSessionImpl(session_memory, session_handle, std::forward<cmif::ServiceObjectHolder>(obj)));
|
||||
succeeded = true;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ServerSessionManager::RegisterMitmSessionImpl(ServerSession *session_memory, Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) {
|
||||
/* Create session object. */
|
||||
new (session_memory) ServerSession(mitm_session_handle, std::forward<cmif::ServiceObjectHolder>(obj), std::forward<std::shared_ptr<::Service>>(fsrv));
|
||||
/* Assign session resources. */
|
||||
session_memory->pointer_buffer = this->GetSessionPointerBuffer(session_memory);
|
||||
session_memory->saved_message = this->GetSessionSavedMessageBuffer(session_memory);
|
||||
/* Register to wait list. */
|
||||
this->RegisterSessionToWaitList(session_memory);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ServerSessionManager::AcceptMitmSessionImpl(ServerSession *session_memory, Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) {
|
||||
/* Create session handle. */
|
||||
Handle mitm_session_handle;
|
||||
R_TRY(svcAcceptSession(&mitm_session_handle, mitm_port_handle));
|
||||
bool succeeded = false;
|
||||
ON_SCOPE_EXIT {
|
||||
if (!succeeded) {
|
||||
R_ASSERT(svcCloseHandle(mitm_session_handle));
|
||||
}
|
||||
};
|
||||
/* Register session. */
|
||||
R_TRY(this->RegisterMitmSessionImpl(session_memory, mitm_session_handle, std::forward<cmif::ServiceObjectHolder>(obj), std::forward<std::shared_ptr<::Service>>(fsrv)));
|
||||
succeeded = true;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ServerSessionManager::RegisterSession(Handle session_handle, cmif::ServiceObjectHolder &&obj) {
|
||||
/* We don't actually care about what happens to the session. It'll get linked. */
|
||||
ServerSession *session_ptr = nullptr;
|
||||
return this->RegisterSession(&session_ptr, session_handle,std::forward<cmif::ServiceObjectHolder>(obj));
|
||||
}
|
||||
|
||||
Result ServerSessionManager::AcceptSession(Handle port_handle, cmif::ServiceObjectHolder &&obj) {
|
||||
/* We don't actually care about what happens to the session. It'll get linked. */
|
||||
ServerSession *session_ptr = nullptr;
|
||||
return this->AcceptSession(&session_ptr, port_handle, std::forward<cmif::ServiceObjectHolder>(obj));
|
||||
}
|
||||
|
||||
Result ServerSessionManager::RegisterMitmSession(Handle mitm_session_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) {
|
||||
/* We don't actually care about what happens to the session. It'll get linked. */
|
||||
ServerSession *session_ptr = nullptr;
|
||||
return this->RegisterMitmSession(&session_ptr, mitm_session_handle, std::forward<cmif::ServiceObjectHolder>(obj), std::forward<std::shared_ptr<::Service>>(fsrv));
|
||||
}
|
||||
|
||||
Result ServerSessionManager::AcceptMitmSession(Handle mitm_port_handle, cmif::ServiceObjectHolder &&obj, std::shared_ptr<::Service> &&fsrv) {
|
||||
/* We don't actually care about what happens to the session. It'll get linked. */
|
||||
ServerSession *session_ptr = nullptr;
|
||||
return this->AcceptMitmSession(&session_ptr, mitm_port_handle, std::forward<cmif::ServiceObjectHolder>(obj), std::forward<std::shared_ptr<::Service>>(fsrv));
|
||||
}
|
||||
|
||||
Result ServerSessionManager::ReceiveRequestImpl(ServerSession *session, const cmif::PointerAndSize &message) {
|
||||
const cmif::PointerAndSize &pointer_buffer = session->pointer_buffer;
|
||||
|
||||
/* If the receive list is odd, we may need to receive repeatedly. */
|
||||
while (true) {
|
||||
if (pointer_buffer.GetPointer()) {
|
||||
hipcMakeRequestInline(message.GetPointer(),
|
||||
.type = CmifCommandType_Invalid,
|
||||
.num_recv_statics = HIPC_AUTO_RECV_STATIC,
|
||||
).recv_list[0] = hipcMakeRecvStatic(pointer_buffer.GetPointer(), pointer_buffer.GetSize());
|
||||
} else {
|
||||
hipcMakeRequestInline(message.GetPointer(),
|
||||
.type = CmifCommandType_Invalid,
|
||||
);
|
||||
}
|
||||
hipc::ReceiveResult recv_result;
|
||||
R_TRY(hipc::Receive(&recv_result, session->session_handle, message));
|
||||
switch (recv_result) {
|
||||
case hipc::ReceiveResult::Success:
|
||||
session->is_closed = false;
|
||||
return ResultSuccess;
|
||||
case hipc::ReceiveResult::Closed:
|
||||
session->is_closed = true;
|
||||
return ResultSuccess;
|
||||
case hipc::ReceiveResult::NeedsRetry:
|
||||
continue;
|
||||
STS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
NX_CONSTEXPR u32 GetCmifCommandType(const cmif::PointerAndSize &message) {
|
||||
HipcHeader hdr = {};
|
||||
__builtin_memcpy(&hdr, message.GetPointer(), sizeof(hdr));
|
||||
return hdr.type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result ServerSessionManager::ProcessRequest(ServerSession *session, const cmif::PointerAndSize &message) {
|
||||
if (session->is_closed) {
|
||||
this->CloseSessionImpl(session);
|
||||
return ResultSuccess;
|
||||
}
|
||||
switch (GetCmifCommandType(message)) {
|
||||
case CmifCommandType_Close:
|
||||
{
|
||||
this->CloseSessionImpl(session);
|
||||
return ResultSuccess;
|
||||
}
|
||||
default:
|
||||
{
|
||||
R_TRY_CATCH(this->ProcessRequestImpl(session, message, message)) {
|
||||
R_CATCH(ResultServiceFrameworkRequestDeferredByUser) { /* TODO: Properly include entire range Nintendo does */
|
||||
return ResultServiceFrameworkRequestDeferredByUser;
|
||||
}
|
||||
R_CATCH_ALL() {
|
||||
/* All other results indicate something went very wrong. */
|
||||
this->CloseSessionImpl(session);
|
||||
return ResultSuccess;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
/* We succeeded, so we can process future messages on this session. */
|
||||
this->RegisterSessionToWaitList(session);
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result ServerSessionManager::ProcessRequestImpl(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message) {
|
||||
/* TODO: Inline context support, retrieve from raw data + 0xC. */
|
||||
const auto cmif_command_type = GetCmifCommandType(in_message);
|
||||
switch (cmif_command_type) {
|
||||
case CmifCommandType_Request:
|
||||
case CmifCommandType_RequestWithContext:
|
||||
return this->DispatchRequest(session->srv_obj_holder.Clone(), session, in_message, out_message);
|
||||
case CmifCommandType_Control:
|
||||
case CmifCommandType_ControlWithContext:
|
||||
return this->DispatchManagerRequest(session, in_message, out_message);
|
||||
default:
|
||||
return ResultHipcUnknownCommandType;
|
||||
}
|
||||
}
|
||||
|
||||
Result ServerSessionManager::DispatchManagerRequest(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message) {
|
||||
/* This will get overridden by ... WithDomain class. */
|
||||
return ResultServiceFrameworkNotSupported;
|
||||
}
|
||||
|
||||
Result ServerSessionManager::DispatchRequest(cmif::ServiceObjectHolder &&obj_holder, ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message) {
|
||||
/* Create request context. */
|
||||
cmif::HandlesToClose handles_to_close = {};
|
||||
cmif::ServiceDispatchContext dispatch_ctx = {
|
||||
.srv_obj = obj_holder.GetServiceObjectUnsafe(),
|
||||
.manager = this,
|
||||
.processor = nullptr, /* Filled in by template implementations. */
|
||||
.handles_to_close = &handles_to_close,
|
||||
.pointer_buffer = session->pointer_buffer,
|
||||
.in_message_buffer = in_message,
|
||||
.out_message_buffer = out_message,
|
||||
.request = hipcParseRequest(in_message.GetPointer()),
|
||||
};
|
||||
|
||||
/* Validate message sizes. */
|
||||
const uintptr_t in_message_buffer_end = in_message.GetAddress() + in_message.GetSize();
|
||||
const uintptr_t in_raw_addr = reinterpret_cast<uintptr_t>(dispatch_ctx.request.data.data_words);
|
||||
const size_t in_raw_size = dispatch_ctx.request.meta.num_data_words * sizeof(u32);
|
||||
/* Note: Nintendo does not validate this size before subtracting 0x10 from it. This is not exploitable. */
|
||||
R_UNLESS(in_raw_size >= 0x10, ResultHipcInvalidRequestSize);
|
||||
R_UNLESS(in_raw_addr + in_raw_size <= in_message_buffer_end, ResultHipcInvalidRequestSize);
|
||||
const uintptr_t recv_list_end = reinterpret_cast<uintptr_t>(dispatch_ctx.request.data.recv_list + dispatch_ctx.request.meta.num_recv_statics);
|
||||
R_UNLESS(recv_list_end <= in_message_buffer_end, ResultHipcInvalidRequestSize);
|
||||
|
||||
/* CMIF has 0x10 of padding in raw data, and requires 0x10 alignment. */
|
||||
const cmif::PointerAndSize in_raw_data(util::AlignUp(in_raw_addr, 0x10), in_raw_size - 0x10);
|
||||
|
||||
/* Invoke command handler. */
|
||||
R_TRY(obj_holder.ProcessMessage(dispatch_ctx, in_raw_data));
|
||||
|
||||
/* Reply. */
|
||||
{
|
||||
ON_SCOPE_EXIT {
|
||||
for (size_t i = 0; i < handles_to_close.num_handles; i++) {
|
||||
R_ASSERT(svcCloseHandle(handles_to_close.handles[i]));
|
||||
}
|
||||
};
|
||||
R_TRY(hipc::Reply(session->session_handle, out_message));
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -42,9 +42,9 @@ namespace sts::sm::mitm {
|
|||
});
|
||||
}
|
||||
|
||||
Result AcknowledgeSession(Service *out_service, u64 *out_pid, ncm::TitleId *out_tid, ServiceName name) {
|
||||
Result AcknowledgeSession(Service *out_service, os::ProcessId *out_pid, ncm::TitleId *out_tid, ServiceName name) {
|
||||
return impl::DoWithMitmSession([&]() {
|
||||
return smAtmosphereMitmAcknowledgeSession(out_service, out_pid, &out_tid->value, name.name);
|
||||
return smAtmosphereMitmAcknowledgeSession(out_service, &out_pid->value, &out_tid->value, name.name);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace sts::ro::impl {
|
|||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ValidateNrr(const NrrHeader *header, u64 size, u64 title_id, ModuleType expected_type, bool enforce_type) {
|
||||
Result ValidateNrr(const NrrHeader *header, u64 size, ncm::TitleId title_id, ModuleType expected_type, bool enforce_type) {
|
||||
/* Check magic. */
|
||||
if (!header->IsMagicValid()) {
|
||||
return ResultRoInvalidNrr;
|
||||
|
@ -68,7 +68,7 @@ namespace sts::ro::impl {
|
|||
}
|
||||
|
||||
/* Check type. */
|
||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700 && enforce_type) {
|
||||
if (hos::GetVersion() >= hos::Version_700 && enforce_type) {
|
||||
if (expected_type != header->GetType()) {
|
||||
return ResultRoInvalidNrrType;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ namespace sts::ro::impl {
|
|||
}
|
||||
|
||||
/* Utilities for working with NRRs. */
|
||||
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type) {
|
||||
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, ncm::TitleId title_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type) {
|
||||
map::MappedCodeMemory nrr_mcm(ResultRoInternalError);
|
||||
|
||||
/* First, map the NRR. */
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
namespace sts::ro::impl {
|
||||
|
||||
/* Utilities for working with NRRs. */
|
||||
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type);
|
||||
Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, ncm::TitleId title_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type);
|
||||
Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address);
|
||||
|
||||
}
|
|
@ -77,25 +77,25 @@ namespace sts::ro::impl {
|
|||
NroInfo nro_infos[MaxNroInfos];
|
||||
NrrInfo nrr_infos[MaxNrrInfos];
|
||||
Handle process_handle;
|
||||
u64 process_id;
|
||||
os::ProcessId process_id;
|
||||
bool in_use;
|
||||
|
||||
u64 GetTitleId(Handle other_process_h) const {
|
||||
ncm::TitleId GetTitleId(Handle other_process_h) const {
|
||||
/* Automatically select a handle, allowing for override. */
|
||||
Handle process_h = this->process_handle;
|
||||
if (other_process_h != INVALID_HANDLE) {
|
||||
process_h = other_process_h;
|
||||
}
|
||||
|
||||
u64 title_id = 0;
|
||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
|
||||
ncm::TitleId title_id = ncm::TitleId::Invalid;
|
||||
if (hos::GetVersion() >= hos::Version_300) {
|
||||
/* 3.0.0+: Use svcGetInfo. */
|
||||
R_ASSERT(svcGetInfo(&title_id, InfoType_TitleId, process_h, 0));
|
||||
R_ASSERT(svcGetInfo(&title_id.value, InfoType_TitleId, process_h, 0));
|
||||
} else {
|
||||
/* 1.0.0-2.3.0: We're not inside loader, so ask pm. */
|
||||
u64 process_id = 0;
|
||||
R_ASSERT(svcGetProcessId(&process_id, process_h));
|
||||
R_ASSERT(pminfoGetTitleId(&title_id, process_id));
|
||||
os::ProcessId process_id = os::InvalidProcessId;
|
||||
R_ASSERT(svcGetProcessId(&process_id.value, process_h));
|
||||
R_ASSERT(pminfoGetTitleId(&title_id.value, process_id.value));
|
||||
}
|
||||
return title_id;
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ namespace sts::ro::impl {
|
|||
return &g_process_contexts[context_id];
|
||||
}
|
||||
|
||||
ProcessContext *GetContextByProcessId(u64 process_id) {
|
||||
ProcessContext *GetContextByProcessId(os::ProcessId process_id) {
|
||||
for (size_t i = 0; i < MaxSessions; i++) {
|
||||
if (g_process_contexts[i].process_id == process_id) {
|
||||
return &g_process_contexts[i];
|
||||
|
@ -263,7 +263,7 @@ namespace sts::ro::impl {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
size_t AllocateContext(Handle process_handle, u64 process_id) {
|
||||
size_t AllocateContext(Handle process_handle, os::ProcessId process_id) {
|
||||
/* Find a free process context. */
|
||||
for (size_t i = 0; i < MaxSessions; i++) {
|
||||
ProcessContext *context = &g_process_contexts[i];
|
||||
|
@ -328,13 +328,13 @@ namespace sts::ro::impl {
|
|||
}
|
||||
|
||||
/* Context utilities. */
|
||||
Result RegisterProcess(size_t *out_context_id, Handle process_handle, u64 process_id) {
|
||||
Result RegisterProcess(size_t *out_context_id, Handle process_handle, os::ProcessId process_id) {
|
||||
/* Validate process handle. */
|
||||
{
|
||||
u64 handle_pid = 0;
|
||||
os::ProcessId handle_pid = os::InvalidProcessId;
|
||||
|
||||
/* Validate handle is a valid process handle. */
|
||||
if (R_FAILED(svcGetProcessId(&handle_pid, process_handle))) {
|
||||
if (R_FAILED(svcGetProcessId(&handle_pid.value, process_handle))) {
|
||||
return ResultRoInvalidProcess;
|
||||
}
|
||||
|
||||
|
@ -353,7 +353,7 @@ namespace sts::ro::impl {
|
|||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ValidateProcess(size_t context_id, u64 process_id) {
|
||||
Result ValidateProcess(size_t context_id, os::ProcessId process_id) {
|
||||
const ProcessContext *ctx = GetContextById(context_id);
|
||||
if (ctx == nullptr || ctx->process_id != process_id) {
|
||||
return ResultRoInvalidProcess;
|
||||
|
@ -372,7 +372,7 @@ namespace sts::ro::impl {
|
|||
STS_ASSERT(context != nullptr);
|
||||
|
||||
/* Get title id. */
|
||||
const u64 title_id = context->GetTitleId(process_h);
|
||||
const ncm::TitleId title_id = context->GetTitleId(process_h);
|
||||
|
||||
/* Validate address/size. */
|
||||
if (nrr_address & 0xFFF) {
|
||||
|
@ -461,7 +461,7 @@ namespace sts::ro::impl {
|
|||
R_TRY(MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size));
|
||||
|
||||
/* Validate the NRO (parsing region extents). */
|
||||
u64 rx_size, ro_size, rw_size;
|
||||
u64 rx_size = 0, ro_size = 0, rw_size = 0;
|
||||
R_TRY_CLEANUP(context->ValidateNro(&nro_info->module_id, &rx_size, &ro_size, &rw_size, nro_info->base_address, nro_size, bss_size), {
|
||||
UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0);
|
||||
});
|
||||
|
@ -503,7 +503,7 @@ namespace sts::ro::impl {
|
|||
}
|
||||
|
||||
/* Debug service implementations. */
|
||||
Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id) {
|
||||
Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, os::ProcessId process_id) {
|
||||
size_t count = 0;
|
||||
const ProcessContext *context = GetContextByProcessId(process_id);
|
||||
if (context != nullptr) {
|
||||
|
|
|
@ -32,8 +32,8 @@ namespace sts::ro::impl {
|
|||
bool ShouldEaseNroRestriction();
|
||||
|
||||
/* Context utilities. */
|
||||
Result RegisterProcess(size_t *out_context_id, Handle process_handle, u64 process_id);
|
||||
Result ValidateProcess(size_t context_id, u64 process_id);
|
||||
Result RegisterProcess(size_t *out_context_id, Handle process_handle, os::ProcessId process_id);
|
||||
Result ValidateProcess(size_t context_id, os::ProcessId process_id);
|
||||
void UnregisterProcess(size_t context_id);
|
||||
|
||||
/* Service implementations. */
|
||||
|
@ -43,6 +43,6 @@ namespace sts::ro::impl {
|
|||
Result UnloadNro(size_t context_id, u64 nro_address);
|
||||
|
||||
/* Debug service implementations. */
|
||||
Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id);
|
||||
Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, os::ProcessId process_id);
|
||||
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
|
||||
namespace sts::ro {
|
||||
|
||||
Result DebugMonitorService::GetProcessModuleInfo(Out<u32> count, OutBuffer<LoaderModuleInfo> out_infos, u64 pid) {
|
||||
if (out_infos.num_elements > INT_MAX) {
|
||||
Result DebugMonitorService::GetProcessModuleInfo(sf::Out<u32> out_count, const sf::OutArray<LoaderModuleInfo> &out_infos, os::ProcessId process_id) {
|
||||
if (out_infos.GetSize() > INT_MAX) {
|
||||
return ResultRoInvalidSize;
|
||||
}
|
||||
return impl::GetProcessModuleInfo(count.GetPointer(), out_infos.buffer, out_infos.num_elements, pid);
|
||||
return impl::GetProcessModuleInfo(out_count.GetPointer(), out_infos.GetPointer(), out_infos.GetSize(), process_id);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,17 +20,17 @@
|
|||
|
||||
namespace sts::ro {
|
||||
|
||||
class DebugMonitorService final : public IServiceObject {
|
||||
class DebugMonitorService final : public sf::IServiceObject {
|
||||
protected:
|
||||
enum class CommandId {
|
||||
GetProcessModuleInfo = 0,
|
||||
};
|
||||
private:
|
||||
/* Actual commands. */
|
||||
Result GetProcessModuleInfo(Out<u32> count, OutBuffer<LoaderModuleInfo> out_infos, u64 pid);
|
||||
Result GetProcessModuleInfo(sf::Out<u32> out_count, const sf::OutArray<LoaderModuleInfo> &out_infos, os::ProcessId process_id);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(DebugMonitorService, GetProcessModuleInfo),
|
||||
MAKE_SERVICE_COMMAND_META(GetProcessModuleInfo),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ extern "C" {
|
|||
|
||||
u32 __nx_applet_type = AppletType_None;
|
||||
|
||||
#define INNER_HEAP_SIZE 0x30000
|
||||
#define INNER_HEAP_SIZE 0x4000
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
|
@ -57,13 +57,16 @@ void __libnx_initheap(void) {
|
|||
fake_heap_end = (char*)addr + size;
|
||||
}
|
||||
|
||||
using namespace sts;
|
||||
|
||||
void __appInit(void) {
|
||||
SetFirmwareVersionForLibnx();
|
||||
hos::SetVersionForLibnx();
|
||||
|
||||
DoWithSmSession([&]() {
|
||||
R_ASSERT(setsysInitialize());
|
||||
R_ASSERT(fsInitialize());
|
||||
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
|
||||
R_ASSERT(splInitialize());
|
||||
if (hos::GetVersion() < hos::Version_300) {
|
||||
R_ASSERT(pminfoInitialize());
|
||||
}
|
||||
});
|
||||
|
@ -76,44 +79,55 @@ void __appInit(void) {
|
|||
void __appExit(void) {
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
|
||||
if (hos::GetVersion() < hos::Version_300) {
|
||||
pminfoExit();
|
||||
}
|
||||
setsysExit();
|
||||
}
|
||||
|
||||
using namespace sts;
|
||||
|
||||
/* Helpers to create RO objects. */
|
||||
static const auto MakeRoServiceForSelf = []() { return std::make_shared<ro::Service>(ro::ModuleType::ForSelf); };
|
||||
static const auto MakeRoServiceForOthers = []() { return std::make_shared<ro::Service>(ro::ModuleType::ForOthers); };
|
||||
static constexpr auto MakeRoServiceForSelf = []() { return std::make_shared<ro::Service>(ro::ModuleType::ForSelf); };
|
||||
static constexpr auto MakeRoServiceForOthers = []() { return std::make_shared<ro::Service>(ro::ModuleType::ForOthers); };
|
||||
|
||||
namespace {
|
||||
|
||||
/* ldr:ro, ro:dmnt, ro:1. */
|
||||
/* TODO: Consider max sessions enforcement? */
|
||||
constexpr size_t NumServers = 3;
|
||||
sf::hipc::ServerManager<NumServers> g_server_manager;
|
||||
|
||||
constexpr sm::ServiceName DebugMonitorServiceName = sm::ServiceName::Encode("ro:dmnt");
|
||||
constexpr size_t DebugMonitorMaxSessions = 2;
|
||||
|
||||
/* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */
|
||||
constexpr sm::ServiceName ForSelfServiceName = sm::ServiceName::Encode("ldr:ro");
|
||||
constexpr size_t ForSelfMaxSessions = 2;
|
||||
|
||||
constexpr sm::ServiceName ForOthersServiceName = sm::ServiceName::Encode("ro:1");
|
||||
constexpr size_t ForOthersMaxSessions = 2;
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Initialize Debug config. */
|
||||
{
|
||||
DoWithSmSession([]() {
|
||||
R_ASSERT(splInitialize());
|
||||
});
|
||||
ON_SCOPE_EXIT { splExit(); };
|
||||
|
||||
ro::SetDevelopmentHardware(spl::IsDevelopmentHardware());
|
||||
ro::SetDevelopmentFunctionEnabled(spl::IsDevelopmentFunctionEnabled());
|
||||
}
|
||||
|
||||
/* Static server manager. */
|
||||
static auto s_server_manager = WaitableManager(1);
|
||||
|
||||
/* Create services. */
|
||||
s_server_manager.AddWaitable(new ServiceServer<ro::DebugMonitorService>("ro:dmnt", 2));
|
||||
/* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */
|
||||
s_server_manager.AddWaitable(new ServiceServer<ro::Service, +MakeRoServiceForSelf>("ldr:ro", 2));
|
||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_700) {
|
||||
s_server_manager.AddWaitable(new ServiceServer<ro::Service, +MakeRoServiceForOthers>("ro:1", 2));
|
||||
R_ASSERT((g_server_manager.RegisterServer<ro::DebugMonitorService>(DebugMonitorServiceName, DebugMonitorMaxSessions)));
|
||||
|
||||
R_ASSERT((g_server_manager.RegisterServer<ro::Service, +MakeRoServiceForSelf>(ForSelfServiceName, ForSelfMaxSessions)));
|
||||
if (hos::GetVersion() >= hos::Version_700) {
|
||||
R_ASSERT((g_server_manager.RegisterServer<ro::Service, +MakeRoServiceForOthers>(ForOthersServiceName, ForOthersMaxSessions)));
|
||||
}
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
s_server_manager.Process();
|
||||
g_server_manager.LoopProcess();
|
||||
|
||||
/* Cleanup */
|
||||
return 0;
|
||||
|
|
|
@ -40,33 +40,33 @@ namespace sts::ro {
|
|||
impl::UnregisterProcess(this->context_id);
|
||||
}
|
||||
|
||||
Result Service::LoadNro(Out<u64> load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
|
||||
Result Service::LoadNro(sf::Out<u64> load_address, const sf::ClientProcessId &client_pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue()));
|
||||
return impl::LoadNro(load_address.GetPointer(), this->context_id, nro_address, nro_size, bss_address, bss_size);
|
||||
}
|
||||
|
||||
Result Service::UnloadNro(PidDescriptor pid_desc, u64 nro_address) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
|
||||
Result Service::UnloadNro(const sf::ClientProcessId &client_pid, u64 nro_address) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue()));
|
||||
return impl::UnloadNro(this->context_id, nro_address);
|
||||
}
|
||||
|
||||
Result Service::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
|
||||
Result Service::LoadNrr(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue()));
|
||||
return impl::LoadNrr(this->context_id, INVALID_HANDLE, nrr_address, nrr_size, ModuleType::ForSelf, true);
|
||||
}
|
||||
|
||||
Result Service::UnloadNrr(PidDescriptor pid_desc, u64 nrr_address) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
|
||||
Result Service::UnloadNrr(const sf::ClientProcessId &client_pid, u64 nrr_address) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue()));
|
||||
return impl::UnloadNrr(this->context_id, nrr_address);
|
||||
}
|
||||
|
||||
Result Service::Initialize(PidDescriptor pid_desc, CopiedHandle process_h) {
|
||||
return impl::RegisterProcess(&this->context_id, process_h.handle, pid_desc.pid);
|
||||
Result Service::Initialize(const sf::ClientProcessId &client_pid, sf::CopyHandle process_h) {
|
||||
return impl::RegisterProcess(&this->context_id, process_h.GetValue(), client_pid.GetValue());
|
||||
}
|
||||
|
||||
Result Service::LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, pid_desc.pid));
|
||||
return impl::LoadNrr(this->context_id, process_h.handle, nrr_address, nrr_size, this->type, this->type == ModuleType::ForOthers);
|
||||
Result Service::LoadNrrEx(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h) {
|
||||
R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue()));
|
||||
return impl::LoadNrr(this->context_id, process_h.GetValue(), nrr_address, nrr_size, this->type, this->type == ModuleType::ForOthers);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace sts::ro {
|
|||
void SetDevelopmentHardware(bool is_development_hardware);
|
||||
void SetDevelopmentFunctionEnabled(bool is_development_function_enabled);
|
||||
|
||||
class Service final : public IServiceObject {
|
||||
class Service final : public sf::IServiceObject {
|
||||
protected:
|
||||
enum class CommandId {
|
||||
LoadNro = 0,
|
||||
|
@ -41,23 +41,23 @@ namespace sts::ro {
|
|||
ModuleType type;
|
||||
public:
|
||||
explicit Service(ModuleType t);
|
||||
virtual ~Service() override;
|
||||
virtual ~Service();
|
||||
private:
|
||||
/* Actual commands. */
|
||||
Result LoadNro(Out<u64> load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
|
||||
Result UnloadNro(PidDescriptor pid_desc, u64 nro_address);
|
||||
Result LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size);
|
||||
Result UnloadNrr(PidDescriptor pid_desc, u64 nrr_address);
|
||||
Result Initialize(PidDescriptor pid_desc, CopiedHandle process_h);
|
||||
Result LoadNrrEx(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size, CopiedHandle process_h);
|
||||
Result LoadNro(sf::Out<u64> out_load_address, const sf::ClientProcessId &client_pid, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
|
||||
Result UnloadNro(const sf::ClientProcessId &client_pid, u64 nro_address);
|
||||
Result LoadNrr(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size);
|
||||
Result UnloadNrr(const sf::ClientProcessId &client_pid, u64 nrr_address);
|
||||
Result Initialize(const sf::ClientProcessId &client_pid, sf::CopyHandle process_h);
|
||||
Result LoadNrrEx(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MAKE_SERVICE_COMMAND_META(Service, LoadNro),
|
||||
MAKE_SERVICE_COMMAND_META(Service, UnloadNro),
|
||||
MAKE_SERVICE_COMMAND_META(Service, LoadNrr),
|
||||
MAKE_SERVICE_COMMAND_META(Service, UnloadNrr),
|
||||
MAKE_SERVICE_COMMAND_META(Service, Initialize),
|
||||
MAKE_SERVICE_COMMAND_META(Service, LoadNrrEx, FirmwareVersion_700),
|
||||
MAKE_SERVICE_COMMAND_META(LoadNro),
|
||||
MAKE_SERVICE_COMMAND_META(UnloadNro),
|
||||
MAKE_SERVICE_COMMAND_META(LoadNrr),
|
||||
MAKE_SERVICE_COMMAND_META(UnloadNrr),
|
||||
MAKE_SERVICE_COMMAND_META(Initialize),
|
||||
MAKE_SERVICE_COMMAND_META(LoadNrrEx, hos::Version_700),
|
||||
};
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue