libstrat: update for latest libnx, delete ipc in prep for rewrite

This commit is contained in:
Michael Scire 2019-09-30 02:52:32 -07:00 committed by SciresM
parent add18d868f
commit bd341d5c00
32 changed files with 244 additions and 3113 deletions

View file

@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
SOURCES := source 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/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

View file

@ -23,18 +23,13 @@
#include "stratosphere/version_check.hpp"
#include "stratosphere/iwaitable.hpp"
#include "stratosphere/waitable_manager.hpp"
#include "stratosphere/ipc.hpp"
#include "stratosphere/mitm.hpp"
#include "stratosphere/results.hpp"
#include "stratosphere/on_crash.hpp"
#include "stratosphere/util.hpp"
#include "stratosphere/svc.hpp"
#include "stratosphere/ams.hpp"
#include "stratosphere/os.hpp"
#include "stratosphere/cfg.hpp"
#include "stratosphere/fatal.hpp"
@ -42,5 +37,4 @@
#include "stratosphere/ncm.hpp"
#include "stratosphere/pm.hpp"
#include "stratosphere/rnd.hpp"
#include "stratosphere/sm.hpp"
#include "stratosphere/util.hpp"
#include "stratosphere/sm.hpp"

View file

@ -15,24 +15,6 @@
*/
#pragma once
#include <atomic>
class IWaitable;
class WaitableManagerBase {
std::atomic<u64> cur_priority = 0;
public:
WaitableManagerBase() = default;
virtual ~WaitableManagerBase() = default;
u64 GetNextPriority() {
return std::atomic_fetch_add(&cur_priority, (u64)1);
}
virtual void AddWaitable(IWaitable *w) = 0;
virtual void NotifySignaled(IWaitable *w) = 0;
virtual void RequestStop() = 0;
virtual void Process() = 0;
};
#include "ams/ams_types.hpp"
#include "ams/ams_firmware_version_api.hpp"

View file

@ -15,12 +15,11 @@
*/
#pragma once
#include "ams_types.hpp"
#include "sm.hpp"
namespace sts::ams {
#include "ipc.hpp"
FirmwareVersion GetRuntimeFirmwareVersion();
void SetFirmwareVersionForLibnx();
#include "mitm/imitmserviceobject.hpp"
#include "mitm/mitm_query_service.hpp"
#include "mitm/mitm_session.hpp"
#include "mitm/mitm_server.hpp"
}

View file

@ -0,0 +1,58 @@
/*
* 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 "../defines.hpp"
/* 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 sts::ams {
enum TargetFirmware : u32 {
TargetFirmware_100 = 1,
TargetFirmware_200 = 2,
TargetFirmware_300 = 3,
TargetFirmware_400 = 4,
TargetFirmware_500 = 5,
TargetFirmware_600 = 6,
TargetFirmware_620 = 7,
TargetFirmware_700 = 8,
TargetFirmware_800 = 9,
TargetFirmware_810 = 10,
TargetFirmware_900 = 11,
};
}

View file

@ -1,51 +0,0 @@
/*
* 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
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,
};
enum AtmosphereTargetFirmware : u32 {
AtmosphereTargetFirmware_100 = 1,
AtmosphereTargetFirmware_200 = 2,
AtmosphereTargetFirmware_300 = 3,
AtmosphereTargetFirmware_400 = 4,
AtmosphereTargetFirmware_500 = 5,
AtmosphereTargetFirmware_600 = 6,
AtmosphereTargetFirmware_620 = 7,
AtmosphereTargetFirmware_700 = 8,
AtmosphereTargetFirmware_800 = 9,
AtmosphereTargetFirmware_810 = 10,
AtmosphereTargetFirmware_900 = 11,
};
FirmwareVersion GetRuntimeFirmwareVersion();
void SetFirmwareVersionForLibnx();

View file

@ -1,117 +0,0 @@
/*
* 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 <type_traits>
enum class IpcBufferType {
InBuffer,
OutBuffer,
InPointer,
OutPointer,
};
/* Base for In/Out Buffers. */
struct IpcBufferBase {};
struct InOutBufferBase : public IpcBufferBase {};
/* Represents an A descriptor. */
struct InBufferBase : public InOutBufferBase {};
template <typename T, BufferType e_t = BufferType_Normal>
struct InBuffer : public InBufferBase {
T *buffer;
size_t num_elements;
BufferType type;
static const BufferType expected_type = e_t;
/* Convenience. */
T& operator[](size_t i) const {
return buffer[i];
}
InBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { }
};
/* Represents a B descriptor. */
struct OutBufferBase : public InOutBufferBase {};
template <typename T, BufferType e_t = BufferType_Normal>
struct OutBuffer : OutBufferBase {
T *buffer;
size_t num_elements;
BufferType type;
static const BufferType expected_type = e_t;
/* Convenience. */
T& operator[](size_t i) const {
return buffer[i];
}
OutBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { }
};
/* Represents an X descriptor. */
struct InPointerBase : public IpcBufferBase {};
template <typename T>
struct InPointer : public InPointerBase {
T *pointer;
size_t num_elements;
/* Convenience. */
T& operator[](size_t i) const {
return pointer[i];
}
InPointer(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
};
/* Represents a C descriptor. */
struct OutPointerWithServerSizeBase : public IpcBufferBase {};
template <typename T, size_t N>
struct OutPointerWithServerSize : public OutPointerWithServerSizeBase {
T *pointer;
static const size_t num_elements = N;
static const size_t element_size = sizeof(T);
/* Convenience. */
T& operator[](size_t i) const {
return pointer[i];
}
OutPointerWithServerSize(void *p) : pointer((T *)p) { }
OutPointerWithServerSize(void *p, size_t n) : pointer((T *)p) { }
};
struct OutPointerWithClientSizeBase : public IpcBufferBase {};
/* Represents a C descriptor with size in raw data. */
template <typename T>
struct OutPointerWithClientSize : public OutPointerWithClientSizeBase {
T *pointer;
size_t num_elements;
/* Convenience. */
T& operator[](size_t i) const {
return pointer[i];
}
OutPointerWithClientSize(void *p, size_t n) : pointer((T *)p), num_elements(n/sizeof(T)) { }
};

View file

@ -1,130 +0,0 @@
/*
* 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 <algorithm>
#include <memory>
#include <type_traits>
#include "ipc_service_object.hpp"
class IDomainObject;
class DomainManager {
public:
static constexpr u32 MinimumDomainId = 1;
public:
virtual std::shared_ptr<IDomainObject> AllocateDomain() = 0;
virtual void FreeDomain(IDomainObject *domain) = 0;
virtual Result ReserveObject(IDomainObject *domain, u32 *out_object_id) = 0;
virtual Result ReserveSpecificObject(IDomainObject *domain, u32 object_id) = 0;
virtual void SetObject(IDomainObject *domain, u32 object_id, ServiceObjectHolder&& holder) = 0;
virtual ServiceObjectHolder *GetObject(IDomainObject *domain, u32 object_id) = 0;
virtual Result FreeObject(IDomainObject *domain, u32 object_id) = 0;
virtual Result ForceFreeObject(u32 object_id) = 0;
};
class IDomainObject : public IServiceObject {
private:
DomainManager *manager;
public:
IDomainObject(DomainManager *m) : manager(m) {}
virtual ~IDomainObject() override {
this->manager->FreeDomain(this);
}
DomainManager *GetManager() {
return this->manager;
}
ServiceObjectHolder *GetObject(u32 object_id) {
return this->manager->GetObject(this, object_id);
}
Result ReserveObject(u32 *out_object_id) {
return this->manager->ReserveObject(this, out_object_id);
}
Result ReserveSpecificObject(u32 object_id) {
return this->manager->ReserveSpecificObject(this, object_id);
}
void SetObject(u32 object_id, ServiceObjectHolder&& holder) {
this->manager->SetObject(this, object_id, std::move(holder));
}
Result FreeObject(u32 object_id) {
return this->manager->FreeObject(this, object_id);
}
Result ForceFreeObject(u32 object_id) {
return this->manager->ForceFreeObject(object_id);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* IDomainObject has no callable functions. */
};
};
static constexpr bool IsDomainObject(ServiceObjectHolder &holder) {
return holder.GetServiceId() == ServiceObjectId<IDomainObject>();
}
static constexpr bool IsDomainObject(ServiceObjectHolder *holder) {
return holder->GetServiceId() == ServiceObjectId<IDomainObject>();
}
/* Out for service impl. */
template <typename ServiceImpl>
class Out<std::shared_ptr<ServiceImpl>> : public OutSessionTag {
static_assert(std::is_base_of_v<IServiceObject, ServiceImpl>, "OutSessions must be shared_ptr<IServiceObject>!");
template<typename, typename>
friend class Out;
private:
std::shared_ptr<ServiceImpl> *srv;
IDomainObject *domain = nullptr;
u32 *object_id = nullptr;
public:
Out<std::shared_ptr<ServiceImpl>>(std::shared_ptr<IServiceObject> *s, IDomainObject *dm, u32 *o) : srv(reinterpret_cast<std::shared_ptr<ServiceImpl> *>(s)), domain(dm), object_id(o) { }
ServiceObjectHolder GetHolder() {
std::shared_ptr<ServiceImpl> clone = *srv;
return ServiceObjectHolder(std::move(clone));
}
bool IsDomain() {
return domain != nullptr;
}
u32 GetObjectId() {
return *object_id;
}
void ChangeObjectId(u32 o) {
domain->ForceFreeObject(*object_id);
domain->ReserveSpecificObject(o);
*object_id = o;
}
void SetValue(std::shared_ptr<ServiceImpl> &&s) {
*this->srv = std::move(s);
}
};

View file

@ -1,75 +0,0 @@
/*
* 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 <type_traits>
/* Declare false allowed struct. */
template <typename>
struct AllowedOut : std::false_type {};
struct OutDataTag{};
struct OutHandleTag{};
struct OutSessionTag{};
/* Define out struct, so that we can get errors on enable_if */
template <typename T, typename Allowed = void>
class Out {
static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "Invalid IPC Out Type!");
};
template <typename T>
class Out<T, typename std::enable_if<std::is_trivial<T>::value || AllowedOut<T>::value>::type> : public OutDataTag {
private:
T *obj;
public:
Out(T *o) : obj(o) { }
void SetValue(const T& t) {
*obj = t;
}
const T& GetValue() {
return *obj;
}
T *GetPointer() {
return obj;
}
/* Convenience operators. */
T& operator*() {
return *obj;
}
T* operator->() {
return obj;
}
};
template <typename T>
class Out<T*> {
static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "Invalid IPC Out Type (Raw Pointer)!");
};
template <typename T>
struct OutHelper;
template <typename T>
struct OutHelper<Out<T>> {
using type = T;
};

View file

@ -1,44 +0,0 @@
/*
* 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 "ipc_service_object.hpp"
#include "ipc_domain_object.hpp"
#include "ipc_special.hpp"
#include "ipc_session_manager_base.hpp"
struct IpcResponseContext {
/* Request/Reply data. */
IpcParsedCommand request;
IpcCommand reply;
u8 out_data[0x100];
std::shared_ptr<IServiceObject> *out_objs[8];
Handle out_object_server_handles[8];
IpcHandle out_handles[8];
u32 out_object_ids[8];
IpcCommandType cmd_type;
u64 cmd_id;
Result rc;
/* Context. */
SessionManagerBase *manager;
ServiceObjectHolder *obj_holder;
unsigned char *pb;
size_t pb_size;
};

View file

@ -1,672 +0,0 @@
/*
* 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 <cstdlib>
#include <cstring>
#include <tuple>
#include <type_traits>
#include <memory>
#include "../results.hpp"
#include "ipc_out.hpp"
#include "ipc_buffers.hpp"
#include "ipc_special.hpp"
#include "ipc_domain_object.hpp"
#include "ipc_response_context.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
template <typename...>
struct TypeList{};
template <typename... T1s, typename... T2s>
constexpr auto Concatenate(TypeList<T1s...>, TypeList<T2s...>) {
return TypeList<T1s..., T2s...>{};
}
template <template <typename> typename Condition, typename R>
constexpr auto FilterTypes(R result, TypeList<>) {
return result;
}
template <template <typename> typename Condition, typename R, typename T, typename... Ts>
constexpr auto FilterTypes(R result, TypeList<T, Ts...>) {
if constexpr (Condition<T>{})
return FilterTypes<Condition>(Concatenate(result, TypeList<T>{}), TypeList<Ts...>{});
else
return FilterTypes<Condition>(result, TypeList<Ts...>{});
}
template<typename Types> struct TypeListToTuple;
template<typename... Types>
struct TypeListToTuple<TypeList<Types...>> {
using type = std::tuple<Types...>;
};
template <template <typename> typename Condition, typename... Types>
using FilteredTypes = typename TypeListToTuple<std::decay_t<decltype(FilterTypes<Condition>(TypeList<>{}, TypeList<Types...>{}))>>::type;
enum class ArgType {
InData,
OutData,
InHandle,
OutHandle,
InSession,
OutSession,
PidDesc,
InBuffer,
OutBuffer,
InPointer,
OutPointerClientSize,
OutPointerServerSize,
};
template<typename X>
constexpr ArgType GetArgType() {
if constexpr (std::is_base_of_v<OutDataTag, X>) {
return ArgType::OutData;
} else if constexpr (std::is_base_of_v<OutSessionTag, X>) {
return ArgType::OutSession;
} else if constexpr (std::is_base_of_v<OutHandleTag, X>) {
return ArgType::OutHandle;
} else if constexpr (std::is_base_of_v<InBufferBase, X>) {
return ArgType::InBuffer;
} else if constexpr (std::is_base_of_v<OutBufferBase, X>) {
return ArgType::OutBuffer;
} else if constexpr (std::is_base_of_v<InPointerBase, X>) {
return ArgType::InPointer;
} else if constexpr (std::is_base_of_v<OutPointerWithClientSizeBase, X>) {
return ArgType::OutPointerClientSize;
} else if constexpr (std::is_base_of_v<OutPointerWithServerSizeBase, X>) {
return ArgType::OutPointerServerSize;
} else if constexpr (std::is_base_of_v<PidDescriptorTag, X>) {
return ArgType::PidDesc;
} else if constexpr (std::is_base_of_v<IpcHandleTag, X>) {
return ArgType::InHandle;
} else if constexpr (std::is_trivial_v<X> && !std::is_pointer_v<X>) {
return ArgType::InData;
} else {
static_assert(std::is_pod_v<X> && !std::is_pod_v<X>, "Unhandled InSession!");
return ArgType::InSession;
}
}
template<ArgType ArgT>
struct ArgTypeFilter {
template<typename X>
using type = std::conditional_t<GetArgType<X>() == ArgT, std::true_type, std::false_type>;
};
template<ArgType ArgT>
struct IsArgTypeBuffer {
static constexpr bool value = ArgT == ArgType::InBuffer || ArgT == ArgType::OutBuffer || ArgT == ArgType::InPointer || ArgT == ArgType::OutPointerClientSize || ArgT == ArgType::OutPointerServerSize;
};
struct ArgTypeBufferFilter {
template<typename X>
using type = std::conditional_t<IsArgTypeBuffer<GetArgType<X>()>::value, std::true_type, std::false_type>;
};
template<ArgType ArgT>
struct IsArgTypeInData {
static constexpr bool value = ArgT == ArgType::InData || ArgT == ArgType::PidDesc;
};
struct ArgTypeInDataFilter {
template<typename X>
using type = std::conditional_t<IsArgTypeInData<GetArgType<X>()>::value, std::true_type, std::false_type>;
};
template<typename T>
struct RawDataHelper {
static_assert(GetArgType<T>() == ArgType::InData || GetArgType<T>() == ArgType::PidDesc);
static constexpr size_t align = (GetArgType<T>() == ArgType::InData) ? __alignof__(T) : __alignof__(u64);
static constexpr size_t size = (GetArgType<T>() == ArgType::InData) ? sizeof(T) : sizeof(u64);
};
template<typename T>
struct RawDataHelper<Out<T>> {
static_assert(GetArgType<T>() == ArgType::InData);
static constexpr size_t align = __alignof(T);
static constexpr size_t size = sizeof(T);
};
template<typename Ts>
struct RawDataComputer;
template<typename... Ts>
struct RawDataComputer<std::tuple<Ts...>> {
/* https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,2604 */
static constexpr void QuickSort(std::array<size_t, sizeof...(Ts)> &map, std::array<size_t, sizeof...(Ts)> &values, int left, int right) {
do {
int i = left;
int j = right;
int x = map[i + ((j - i) >> 1)];
do {
while (i < static_cast<int>(sizeof...(Ts)) && values[x] > values[map[i]]) i++;
while (j >= 0 && values[x] < values[map[j]]) j--;
if (i > j) break;
if (i < j) {
const size_t temp = map[i];
map[i] = map[j];
map[j] = temp;
}
i++;
j--;
} while (i <= j);
if (j - left <= right - i) {
if (left < j) QuickSort(map, values, left, j);
left = i;
} else {
if (i < right) QuickSort(map, values, i, right);
right = j;
}
} while (left < right);
}
static constexpr void StableSort(std::array<size_t, sizeof...(Ts)> &map, std::array<size_t, sizeof...(Ts)> &values) {
/* First, quicksort a copy of the map. */
std::array<size_t, sizeof...(Ts)> map_unstable(map);
QuickSort(map_unstable, values, 0, sizeof...(Ts)-1);
/* Now, create stable sorted map from unstably quicksorted indices (via repeated insertion sort on element runs). */
for (size_t i = 0; i < sizeof...(Ts); i++) {
map[i] = map_unstable[i];
for (ssize_t j = i-1; j >= 0 && values[map[j]] == values[map[j+1]] && map[j] > map[j+1]; j--) {
const size_t temp = map[j];
map[j] = map[j+1];
map[j+1] = temp;
}
}
}
static constexpr std::array<size_t, sizeof...(Ts)+1> GetOffsets() {
std::array<size_t, sizeof...(Ts)+1> offsets = {};
offsets[0] = 0;
if constexpr (sizeof...(Ts) > 0) {
/* Get size, alignment for each type. */
std::array<size_t, sizeof...(Ts)> sizes = { RawDataHelper<Ts>::size... };
std::array<size_t, sizeof...(Ts)> aligns = { RawDataHelper<Ts>::align... };
/* We want to sort...by alignment. */
std::array<size_t, sizeof...(Ts)> map = {};
for (size_t i = 0; i < sizeof...(Ts); i++) { map[i] = i; }
StableSort(map, aligns);
/* Iterate over sorted types. */
size_t cur_offset = 0;
for (size_t i = 0; i < sizeof...(Ts); i++) {
const size_t align = aligns[map[i]];
if (cur_offset % align != 0) {
cur_offset += align - (cur_offset % align);
}
offsets[map[i]] = cur_offset;
cur_offset += sizes[map[i]];
}
offsets[sizeof...(Ts)] = cur_offset;
}
return offsets;
}
static constexpr std::array<size_t, sizeof...(Ts)+1> offsets = GetOffsets();
};
template <typename _Args, typename _ReturnType>
struct CommandMetaInfo;
template<typename... _Args, typename _ReturnType>
struct CommandMetaInfo<std::tuple<_Args...>, _ReturnType> {
using Args = std::tuple<_Args...>;
using ReturnType = _ReturnType;
static constexpr bool ReturnsResult = std::is_same_v<ReturnType, Result>;
static constexpr bool ReturnsVoid = std::is_same_v<ReturnType, void>;
using InDatas = FilteredTypes<ArgTypeInDataFilter::type, _Args...>;
using OutDatas = FilteredTypes<ArgTypeFilter<ArgType::OutData>::type, _Args...>;
using InHandles = FilteredTypes<ArgTypeFilter<ArgType::InHandle>::type, _Args...>;
using OutHandles = FilteredTypes<ArgTypeFilter<ArgType::OutHandle>::type, _Args...>;
using InSessions = FilteredTypes<ArgTypeFilter<ArgType::InSession>::type, _Args...>;
using OutSessions = FilteredTypes<ArgTypeFilter<ArgType::OutSession>::type, _Args...>;
using PidDescs = FilteredTypes<ArgTypeFilter<ArgType::PidDesc>::type, _Args...>;
using InBuffers = FilteredTypes<ArgTypeFilter<ArgType::InBuffer>::type, _Args...>;
using OutBuffers = FilteredTypes<ArgTypeFilter<ArgType::OutBuffer>::type, _Args...>;
using InPointers = FilteredTypes<ArgTypeFilter<ArgType::InPointer>::type, _Args...>;
using ClientSizeOutPointers = FilteredTypes<ArgTypeFilter<ArgType::OutPointerClientSize>::type, _Args...>;
using ServerSizeOutPointers = FilteredTypes<ArgTypeFilter<ArgType::OutPointerServerSize>::type, _Args...>;
using Buffers = FilteredTypes<ArgTypeBufferFilter::type, _Args...>;
static constexpr size_t NumInDatas = std::tuple_size_v<InDatas>;
static constexpr size_t NumOutDatas = std::tuple_size_v<OutDatas>;
static constexpr size_t NumInHandles = std::tuple_size_v<InHandles>;
static constexpr size_t NumOutHandles = std::tuple_size_v<OutHandles>;
static constexpr size_t NumInSessions = std::tuple_size_v<InSessions>;
static constexpr size_t NumOutSessions = std::tuple_size_v<OutSessions>;
static constexpr size_t NumPidDescs = std::tuple_size_v<PidDescs>;
static constexpr size_t NumInBuffers = std::tuple_size_v<InBuffers>;
static constexpr size_t NumOutBuffers = std::tuple_size_v<OutBuffers>;
static constexpr size_t NumInPointers = std::tuple_size_v<InPointers>;
static constexpr size_t NumClientSizeOutPointers = std::tuple_size_v<ClientSizeOutPointers>;
static constexpr size_t NumServerSizeOutPointers = std::tuple_size_v<ServerSizeOutPointers>;
static constexpr size_t NumBuffers = std::tuple_size_v<Buffers>;
static_assert(NumInSessions == 0, "InSessions not yet supported!");
static_assert(NumPidDescs == 0 || NumPidDescs == 1, "Methods can only take in 0 or 1 PIDs!");
static_assert(NumBuffers <= 8, "Methods can only take in <= 8 Buffers!");
static_assert(NumInHandles <= 8, "Methods can take in <= 8 Handles!");
static_assert(NumOutHandles + NumOutSessions <= 8, "Methods can only return <= 8 Handles+Sessions!");
static constexpr std::array<size_t, NumInDatas+1> InDataOffsets = RawDataComputer<InDatas>::offsets;
static constexpr size_t InRawArgSize = InDataOffsets[NumInDatas];
static constexpr size_t InRawArgSizeWithOutPointers = ((InRawArgSize + NumClientSizeOutPointers * sizeof(u16)) + 3) & ~3;
static constexpr std::array<size_t, NumOutDatas+1> OutDataOffsets = RawDataComputer<OutDatas>::offsets;
static constexpr size_t OutRawArgSize = OutDataOffsets[NumOutDatas];
};
/* ================================================================================= */
/* Actual wrapping implementation goes here. */
/* Validator. */
struct Validator {
template <typename T>
static constexpr bool ValidateCommandArgument(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& h_index, size_t& cur_c_size_offset, size_t& total_c_size) {
constexpr ArgType argT = GetArgType<T>();
if constexpr (argT == ArgType::InBuffer) {
return (ctx->request.Buffers[a_index] != nullptr || ctx->request.BufferSizes[a_index] == 0) && ctx->request.BufferDirections[a_index] == BufferDirection_Send && ctx->request.BufferTypes[a_index++] == T::expected_type;
} else if constexpr (argT == ArgType::OutBuffer) {
return (ctx->request.Buffers[b_index] != nullptr || ctx->request.BufferSizes[b_index] == 0) && ctx->request.BufferDirections[b_index] == BufferDirection_Recv && ctx->request.BufferTypes[b_index++] == T::expected_type;
} else if constexpr (argT == ArgType::InPointer) {
return ctx->request.Statics[x_index++] != nullptr;
} else if constexpr (argT == ArgType::InHandle) {
if constexpr (std::is_same_v<T, MovedHandle>) {
return !ctx->request.WasHandleCopied[h_index++];
} else if constexpr (std::is_same_v<T, CopiedHandle>) {
return ctx->request.WasHandleCopied[h_index++];
}
} else {
if constexpr (argT == ArgType::OutPointerServerSize) {
total_c_size += T::num_elements * sizeof(T);
} else if constexpr (argT == ArgType::OutPointerClientSize) {
total_c_size += *((u16 *)((uintptr_t)(ctx->request.Raw) + 0x10 + cur_c_size_offset));
cur_c_size_offset += sizeof(u16);
}
return true;
}
}
template <typename Ts>
struct ValidateCommandTuple;
template <typename ...Ts>
struct ValidateCommandTuple<std::tuple<Ts...>> {
static constexpr bool IsValid(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& h_index, size_t& cur_c_size_offset, size_t& total_c_size) {
return (ValidateCommandArgument<Ts>(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size) && ...);
}
};
template<typename MetaInfo>
static constexpr Result Validate(IpcResponseContext *ctx) {
if (ctx->request.RawSize < MetaInfo::InRawArgSizeWithOutPointers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumBuffers != MetaInfo::NumInBuffers + MetaInfo::NumOutBuffers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumStatics != MetaInfo::NumInPointers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumStaticsOut != MetaInfo::NumClientSizeOutPointers + MetaInfo::NumServerSizeOutPointers) {
return ResultKernelConnectionClosed;
}
if (ctx->request.NumHandles != MetaInfo::NumInHandles) {
return ResultKernelConnectionClosed;
}
if ((ctx->request.HasPid && MetaInfo::NumPidDescs == 0) || (!ctx->request.HasPid && MetaInfo::NumPidDescs != 0)) {
return ResultKernelConnectionClosed;
}
if (((u32 *)ctx->request.Raw)[0] != SFCI_MAGIC) {
return ResultKernelConnectionClosed;
}
size_t a_index = 0, b_index = MetaInfo::NumInBuffers, x_index = 0, h_index = 0;
size_t cur_c_size_offset = MetaInfo::InRawArgSize + (0x10 - ((uintptr_t)ctx->request.Raw - (ctx->request.IsDomainRequest ? sizeof(DomainMessageHeader) : 0) - (uintptr_t)ctx->request.RawWithoutPadding));
size_t total_c_size = 0;
if (!ValidateCommandTuple<typename MetaInfo::Args>::IsValid(ctx, a_index, b_index, x_index, h_index, cur_c_size_offset, total_c_size)) {
return ResultKernelConnectionClosed;
}
if (total_c_size > ctx->pb_size) {
return ResultKernelConnectionClosed;
}
return ResultSuccess;
}
};
/* ================================================================================= */
/* Decoder. */
template<typename MetaInfo>
struct Decoder {
template<typename T>
static constexpr T DecodeCommandArgument(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& in_h_index, size_t& out_h_index, size_t& out_obj_index, size_t& in_data_index, size_t& out_data_index, size_t& pb_offset, size_t& c_sz_offset) {
constexpr ArgType argT = GetArgType<T>();
if constexpr (argT == ArgType::InBuffer) {
const T& value = T(ctx->request.Buffers[a_index], ctx->request.BufferSizes[a_index], ctx->request.BufferTypes[a_index]);
++a_index;
return value;
} else if constexpr (argT == ArgType::OutBuffer) {
const T& value = T(ctx->request.Buffers[b_index], ctx->request.BufferSizes[b_index], ctx->request.BufferTypes[b_index]);
++b_index;
return value;
} else if constexpr (argT == ArgType::InPointer) {
const T& value = T(ctx->request.Statics[x_index], ctx->request.StaticSizes[x_index]);
++x_index;
return value;
} else if constexpr (argT == ArgType::InHandle) {
return T(ctx->request.Handles[in_h_index++]);
} else if constexpr (argT == ArgType::OutHandle) {
return T(&ctx->out_handles[out_h_index++]);
} else if constexpr (argT == ArgType::PidDesc) {
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + MetaInfo::InDataOffsets[in_data_index++]);
*(u64 *)ptr = ctx->request.Pid;
return T(ctx->request.Pid);
} else if constexpr (argT == ArgType::InData) {
uintptr_t ptr = ((uintptr_t)ctx->request.Raw + 0x10 + MetaInfo::InDataOffsets[in_data_index++]);
if constexpr (std::is_same_v<bool, T>) {
return *((u8 *)ptr) & 1;
} else {
return *((T *)ptr);
}
} else if constexpr (argT == ArgType::OutData) {
uintptr_t ptr = ((uintptr_t)ctx->out_data + MetaInfo::OutDataOffsets[out_data_index++]);
return T(reinterpret_cast<typename OutHelper<T>::type *>(ptr));
} else if constexpr (argT == ArgType::OutPointerClientSize || argT == ArgType::OutPointerServerSize) {
u16 sz;
if constexpr(argT == ArgType::OutPointerServerSize) {
sz = T::element_size;
} else {
sz = *(const u16 *)((uintptr_t)ctx->request.Raw + 0x10 + c_sz_offset);
}
u8* buf = ctx->pb + pb_offset;
c_sz_offset += sizeof(u16);
pb_offset += sz;
ipcAddSendStatic(&ctx->reply, buf, sz, c_index++);
return T(buf, sz);
} else if constexpr (argT == ArgType::OutSession) {
if (IsDomainObject(ctx->obj_holder)) {
const T& value = T(ctx->out_objs[out_obj_index], ctx->obj_holder->GetServiceObject<IDomainObject>(), &ctx->out_object_ids[out_obj_index]);
out_obj_index++;
return value;
} else {
const T& value = T(ctx->out_objs[out_obj_index], nullptr, 0);
out_obj_index++;
return value;
}
}
}
template <typename Ts>
struct DecodeTuple;
template <typename ...Ts>
struct DecodeTuple<std::tuple<Ts...>> {
static constexpr std::tuple<Ts...> GetArgs(IpcResponseContext *ctx, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& in_h_index, size_t& out_h_index, size_t& out_obj_index, size_t& in_data_index, size_t& out_data_index, size_t& pb_offset, size_t& c_sz_offset) {
return std::tuple<Ts... > {
DecodeCommandArgument<Ts>(ctx, a_index, b_index, x_index, c_index, in_h_index, out_h_index, out_obj_index, in_data_index, out_data_index, pb_offset, c_sz_offset)
...
};
}
};
static constexpr typename MetaInfo::Args Decode(IpcResponseContext *ctx) {
size_t a_index = 0, b_index = MetaInfo::NumInBuffers, x_index = 0, c_index = 0, in_h_index = 0, out_h_index = 0, out_obj_index = 0;
size_t in_data_index = 0x0, out_data_index = 0, pb_offset = 0;
size_t c_sz_offset = MetaInfo::InRawArgSize + (0x10 - ((uintptr_t)ctx->request.Raw - (ctx->request.IsDomainRequest ? sizeof(DomainMessageHeader) : 0) - (uintptr_t)ctx->request.RawWithoutPadding));
return DecodeTuple<typename MetaInfo::Args>::GetArgs(ctx, a_index, b_index, x_index, c_index, in_h_index, out_h_index, out_obj_index, in_data_index, out_data_index, pb_offset, c_sz_offset);
}
};
/* ================================================================================= */
template<typename MetaInfo, typename T>
static constexpr void EncodeArgument(IpcResponseContext *ctx, size_t&out_obj_index, T& arg) {
constexpr ArgType argT = GetArgType<T>();
if constexpr (argT == ArgType::OutHandle) {
if constexpr (std::is_same_v<MovedHandle, typename OutHelper<T>::type>) {
ipcSendHandleMove(&ctx->reply, arg.GetValue().handle);
} else {
ipcSendHandleCopy(&ctx->reply, arg.GetValue().handle);
}
} else if constexpr (argT == ArgType::OutSession) {
if (IsDomainObject(ctx->obj_holder)) {
auto domain = ctx->obj_holder->GetServiceObject<IDomainObject>();
domain->SetObject(arg.GetObjectId(), std::move(arg.GetHolder()));
} else {
ctx->manager->AddSession(ctx->out_object_server_handles[out_obj_index++], std::move(arg.GetHolder()));
}
}
}
template<typename MetaInfo, typename ArgsTuple>
struct Encoder;
template <typename MetaInfo, typename ...Args>
struct Encoder<MetaInfo, std::tuple<Args...>> {
static constexpr void EncodeFailure(IpcResponseContext *ctx, Result rc) {
memset(armGetTls(), 0, 0x100);
ipcInitialize(&ctx->reply);
struct {
u64 magic;
u64 result;
} *raw;
if (IsDomainObject(ctx->obj_holder)) {
raw = (decltype(raw))ipcPrepareHeaderForDomain(&ctx->reply, sizeof(*raw), 0);
auto resp_header = (DomainResponseHeader *)((uintptr_t)raw - sizeof(DomainResponseHeader));
*resp_header = {};
} else {
raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
}
raw->magic = SFCO_MAGIC;
raw->result = rc;
}
static constexpr void EncodeSuccess(IpcResponseContext *ctx, Args... args) {
size_t out_obj_index = 0;
((EncodeArgument<MetaInfo, Args>(ctx, out_obj_index, args)), ...);
const bool is_domain = IsDomainObject(ctx->obj_holder);
if (!is_domain) {
for (unsigned int i = 0; i < MetaInfo::NumOutSessions; i++) {
ipcSendHandleMove(&ctx->reply, ctx->out_handles[MetaInfo::NumOutHandles + i].handle);
}
}
struct {
u64 magic;
u64 result;
} *raw;
if (is_domain) {
raw = (decltype(raw))ipcPrepareHeaderForDomain(&ctx->reply, sizeof(*raw) + MetaInfo::OutRawArgSize + sizeof(*ctx->out_object_ids) * MetaInfo::NumOutSessions, 0);
auto resp_header = (DomainResponseHeader *)((uintptr_t)raw - sizeof(DomainResponseHeader));
*resp_header = {};
resp_header->NumObjectIds = MetaInfo::NumOutSessions;
} else {
raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw)+ MetaInfo::OutRawArgSize);
}
raw->magic = SFCO_MAGIC;
raw->result = 0;
memcpy((void *)((uintptr_t)raw + sizeof(*raw)), ctx->out_data, MetaInfo::OutRawArgSize);
if (is_domain) {
memcpy((void *)((uintptr_t)raw + sizeof(*raw) + MetaInfo::OutRawArgSize), ctx->out_object_ids, sizeof(*ctx->out_object_ids) * MetaInfo::NumOutSessions);
}
}
};
/* ================================================================================= */
template<auto MemberFunction>
struct MemberFunctionTraits {
private:
template<typename R, typename C, typename... A>
static R GetReturnTypeImpl(R(C::*)(A...));
template<typename R, typename C, typename... A>
static C GetClassTypeImpl(R(C::*)(A...));
template<typename R, typename C, typename... A>
static std::tuple<A...> GetArgsImpl(R(C::*)(A...));
public:
using ReturnType = decltype(GetReturnTypeImpl(MemberFunction));
using ClassType = decltype(GetClassTypeImpl(MemberFunction));
using ArgsType = decltype(GetArgsImpl(MemberFunction));
};
template<auto IpcCommandImpl, typename ClassType = typename MemberFunctionTraits<IpcCommandImpl>::ClassType>
constexpr Result WrapIpcCommandImpl(IpcResponseContext *ctx) {
using Traits = MemberFunctionTraits<IpcCommandImpl>;
using ArgsType = typename Traits::ArgsType;
using ReturnType = typename Traits::ReturnType;
using BaseClassType = typename Traits::ClassType;
static_assert(std::is_base_of_v<BaseClassType, ClassType>, "Override class type incorrect");
using CommandMetaData = CommandMetaInfo<ArgsType, ReturnType>;
static_assert(CommandMetaData::ReturnsResult || CommandMetaData::ReturnsVoid, "IpcCommandImpls must return Result or void");
ipcInitialize(&ctx->reply);
memset(ctx->out_data, 0, CommandMetaData::OutRawArgSize);
R_TRY(Validator::Validate<CommandMetaData>(ctx));
ClassType *this_ptr = nullptr;
if (IsDomainObject(ctx->obj_holder)) {
this_ptr = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId)->GetServiceObject<ClassType>();
} else {
this_ptr = ctx->obj_holder->GetServiceObject<ClassType>();
}
if (this_ptr == nullptr) {
return ResultServiceFrameworkTargetNotFound;
}
size_t num_out_objects;
std::shared_ptr<IServiceObject> out_objects[CommandMetaData::NumOutSessions];
auto cleanup_guard = SCOPE_GUARD {
/* Clean up objects as necessary. */
if (IsDomainObject(ctx->obj_holder)) {
for (unsigned int i = 0; i < num_out_objects; i++) {
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->out_object_ids[i]);
}
} else {
for (unsigned int i = 0; i < num_out_objects; i++) {
svcCloseHandle(ctx->out_object_server_handles[i]);
svcCloseHandle(ctx->out_handles[CommandMetaData::NumOutHandles + i].handle);
}
}
for (unsigned int i = 0; i < num_out_objects; i++) {
ctx->out_objs[i] = nullptr;
}
};
/* Allocate out object IDs. */
if (IsDomainObject(ctx->obj_holder)) {
for (num_out_objects = 0; num_out_objects < CommandMetaData::NumOutSessions; num_out_objects++) {
R_TRY_CLEANUP(ctx->obj_holder->GetServiceObject<IDomainObject>()->ReserveObject(&ctx->out_object_ids[num_out_objects]), {
std::apply(Encoder<CommandMetaData, typename CommandMetaData::Args>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(R_CLEANUP_RESULT)));
});
ctx->out_objs[num_out_objects] = &out_objects[num_out_objects];
}
} else {
for (num_out_objects = 0; num_out_objects < CommandMetaData::NumOutSessions; num_out_objects++) {
Handle server_h, client_h;
R_TRY_CLEANUP(SessionManagerBase::CreateSessionHandles(&server_h, &client_h), {
std::apply(Encoder<CommandMetaData, typename CommandMetaData::Args>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(R_CLEANUP_RESULT)));
});
ctx->out_object_server_handles[num_out_objects] = server_h;
ctx->out_handles[CommandMetaData::NumOutHandles + num_out_objects].handle = client_h;
ctx->out_objs[num_out_objects] = &out_objects[num_out_objects];
}
}
/* Decode, apply, encode. */
{
auto args = Decoder<CommandMetaData>::Decode(ctx);
if constexpr (CommandMetaData::ReturnsResult) {
R_TRY_CLEANUP(std::apply( [=](auto&&... args) { return (this_ptr->*IpcCommandImpl)(args...); }, args), {
std::apply(Encoder<CommandMetaData, decltype(args)>::EncodeFailure, std::tuple_cat(std::make_tuple(ctx), std::make_tuple(R_CLEANUP_RESULT)));
});
} else {
std::apply( [=](auto&&... args) { (this_ptr->*IpcCommandImpl)(args...); }, args);
}
std::apply(Encoder<CommandMetaData, decltype(args)>::EncodeSuccess, std::tuple_cat(std::make_tuple(ctx), args));
}
/* Cancel object guard, clear remaining object references. */
cleanup_guard.Cancel();
for (unsigned int i = 0; i < num_out_objects; i++) {
ctx->out_objs[i] = nullptr;
}
return ResultSuccess;
}
template <auto CommandId, auto CommandImpl, typename OverrideClassType, FirmwareVersion Low = FirmwareVersion_Min, FirmwareVersion High = FirmwareVersion_Max>
inline static constexpr ServiceCommandMeta MakeServiceCommandMeta() {
return {
.fw_low = Low,
.fw_high = High,
.cmd_id = static_cast<u32>(CommandId),
.handler = WrapIpcCommandImpl<CommandImpl, OverrideClassType>,
};
};
#define MAKE_SERVICE_COMMAND_META(class, name, ...) MakeServiceCommandMeta<CommandId::name, &class::name, class, ##__VA_ARGS__>()
#pragma GCC diagnostic pop

View file

@ -1,135 +0,0 @@
/*
* 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 <memory>
#include <type_traits>
#include "ipc_out.hpp"
#include "../firmware_version.hpp"
class IpcResponseContext;
struct ServiceCommandMeta {
FirmwareVersion fw_low = FirmwareVersion_Max;
FirmwareVersion fw_high = FirmwareVersion_Max;
u32 cmd_id = 0;
Result (*handler)(IpcResponseContext *) = nullptr;
};
class IServiceObject {
public:
virtual ~IServiceObject() { }
virtual bool IsMitmObject() const { return false; }
};
#define SERVICE_DISPATCH_TABLE_NAME s_DispatchTable
#define DEFINE_SERVICE_DISPATCH_TABLE static constexpr ServiceCommandMeta SERVICE_DISPATCH_TABLE_NAME[]
template <typename T>
static constexpr size_t DispatchTableEntryCount() {
static_assert(std::is_base_of<IServiceObject, T>::value, "DispatchTable owners must derive from IServiceObject");
return sizeof(T::SERVICE_DISPATCH_TABLE_NAME)/sizeof(ServiceCommandMeta);
}
template <typename T>
static constexpr const ServiceCommandMeta* DispatchTable() {
static_assert(std::is_base_of<IServiceObject, T>::value, "DispatchTable owners must derive from IServiceObject");
return reinterpret_cast<const ServiceCommandMeta*>(&T::SERVICE_DISPATCH_TABLE_NAME);
}
template <typename T>
static constexpr uintptr_t ServiceObjectId() {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
return reinterpret_cast<uintptr_t>(&T::SERVICE_DISPATCH_TABLE_NAME);
}
class ServiceObjectHolder {
private:
std::shared_ptr<IServiceObject> srv;
const ServiceCommandMeta *dispatch_table;
size_t entry_count;
/* Private copy constructor. */
ServiceObjectHolder(const ServiceObjectHolder& other) : srv(other.srv), dispatch_table(other.dispatch_table), entry_count(other.entry_count) { }
ServiceObjectHolder& operator=(const ServiceObjectHolder& other);
public:
/* Templated constructor ensures correct type id at runtime. */
template <typename ServiceImpl>
explicit ServiceObjectHolder(std::shared_ptr<ServiceImpl>&& s) : srv(std::move(s)), dispatch_table(DispatchTable<ServiceImpl>()), entry_count(DispatchTableEntryCount<ServiceImpl>()) { }
template <typename ServiceImpl>
ServiceImpl *GetServiceObject() const {
if (GetServiceId() == ServiceObjectId<ServiceImpl>()) {
return static_cast<ServiceImpl *>(this->srv.get());
}
return nullptr;
}
template<typename ServiceImpl>
ServiceImpl *GetServiceObjectUnsafe() const {
return static_cast<ServiceImpl *>(this->srv.get());
}
const ServiceCommandMeta *GetDispatchTable() const {
return this->dispatch_table;
}
size_t GetDispatchTableEntryCount() const {
return this->entry_count;
}
constexpr uintptr_t GetServiceId() const {
return reinterpret_cast<uintptr_t>(this->dispatch_table);
}
bool IsMitmObject() const {
return this->srv->IsMitmObject();
}
/* Default constructor, move constructor, move assignment operator. */
ServiceObjectHolder() : srv(nullptr), dispatch_table(nullptr) { }
ServiceObjectHolder(ServiceObjectHolder&& other) : srv(std::move(other.srv)), dispatch_table(std::move(other.dispatch_table)), entry_count(std::move(other.entry_count)) { }
ServiceObjectHolder& operator=(ServiceObjectHolder&& other) {
this->srv = other.srv;
this->dispatch_table = other.dispatch_table;
this->entry_count = other.entry_count;
other.Reset();
return *this;
}
explicit operator bool() const {
return this->srv != nullptr;
}
bool operator!() const {
return this->srv == nullptr;
}
void Reset() {
this->srv.reset();
this->dispatch_table = nullptr;
this->entry_count = 0;
}
ServiceObjectHolder Clone() const {
ServiceObjectHolder clone(*this);
return clone;
}
};

View file

@ -1,344 +0,0 @@
/*
* 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 "../results.hpp"
#include "../iwaitable.hpp"
#include "ipc_service_object.hpp"
#include "ipc_serialization.hpp"
class ServiceSession : public IWaitable
{
protected:
Handle session_handle;
std::vector<unsigned char> pointer_buffer;
ServiceObjectHolder obj_holder;
ServiceObjectHolder control_holder = ServiceObjectHolder(std::make_shared<IHipcControlService>(this));
u8 backup_tls[0x100];
ServiceSession(Handle s_h) : session_handle(s_h) { }
public:
template<typename T>
ServiceSession(Handle s_h, size_t pbs) : session_handle(s_h), pointer_buffer(pbs), obj_holder(std::make_shared<T>()) { }
ServiceSession(Handle s_h, size_t pbs, ServiceObjectHolder &&h) : session_handle(s_h), pointer_buffer(pbs), obj_holder(std::move(h)) { }
virtual ~ServiceSession() override {
svcCloseHandle(this->session_handle);
}
SessionManagerBase *GetSessionManager() {
return static_cast<SessionManagerBase *>(this->GetManager());
}
DomainManager *GetDomainManager() {
return static_cast<DomainManager *>(this->GetSessionManager());
}
Result Receive() {
int handle_index;
/* Prepare pointer buffer... */
IpcCommand c;
ipcInitialize(&c);
if (this->pointer_buffer.size() > 0) {
ipcAddRecvStatic(&c, this->pointer_buffer.data(), this->pointer_buffer.size(), 0);
ipcPrepareHeader(&c, 0);
/* Fix libnx bug in serverside C descriptor handling. */
((u32 *)armGetTls())[1] &= 0xFFFFC3FF;
((u32 *)armGetTls())[1] |= (2) << 10;
} else {
ipcPrepareHeader(&c, 0);
}
/* Receive. */
R_TRY(svcReplyAndReceive(&handle_index, &this->session_handle, 1, 0, U64_MAX));
std::memcpy(this->backup_tls, armGetTls(), sizeof(this->backup_tls));
return ResultSuccess;
}
Result Reply() {
int handle_index;
return svcReplyAndReceive(&handle_index, &this->session_handle, 0, this->session_handle, 0);
}
/* For preparing basic replies. */
Result PrepareBasicResponse(IpcResponseContext *ctx, Result rc) {
ipcInitialize(&ctx->reply);
struct {
u64 magic;
u64 result;
} *raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
raw->magic = SFCO_MAGIC;
raw->result = rc;
return raw->result;
}
Result PrepareBasicDomainResponse(IpcResponseContext *ctx, Result rc) {
ipcInitialize(&ctx->reply);
struct {
DomainResponseHeader hdr;
u64 magic;
u64 result;
} *raw = (decltype(raw))ipcPrepareHeader(&ctx->reply, sizeof(*raw));
raw->hdr = {};
raw->magic = SFCO_MAGIC;
raw->result = rc;
return raw->result;
}
/* For making a new response context. */
void InitializeResponseContext(IpcResponseContext *ctx) {
std::memset(ctx, 0, sizeof(*ctx));
ctx->manager = this->GetSessionManager();
ctx->obj_holder = &this->obj_holder;
ctx->pb = this->pointer_buffer.data();
ctx->pb_size = this->pointer_buffer.size();
}
/* IWaitable */
virtual Handle GetHandle() {
return this->session_handle;
}
virtual Result GetResponse(IpcResponseContext *ctx) {
FirmwareVersion fw = GetRuntimeFirmwareVersion();
const ServiceCommandMeta *dispatch_table = ctx->obj_holder->GetDispatchTable();
size_t entry_count = ctx->obj_holder->GetDispatchTableEntryCount();
if (IsDomainObject(ctx->obj_holder)) {
switch (ctx->request.InMessageType) {
case DomainMessageType_Invalid:
return ResultKernelConnectionClosed;
case DomainMessageType_Close:
return PrepareBasicDomainResponse(ctx, ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId));
case DomainMessageType_SendMessage:
{
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj == nullptr) {
return PrepareBasicDomainResponse(ctx, ResultHipcDomainObjectNotFound);
}
dispatch_table = sub_obj->GetDispatchTable();
entry_count = sub_obj->GetDispatchTableEntryCount();
}
}
}
for (size_t i = 0; i < entry_count; i++) {
if (ctx->cmd_id == dispatch_table[i].cmd_id && dispatch_table[i].fw_low <= fw && fw <= dispatch_table[i].fw_high) {
R_TRY(dispatch_table[i].handler(ctx));
break;
}
}
return ResultSuccess;
}
virtual Result HandleReceived() {
IpcResponseContext ctx;
this->InitializeResponseContext(&ctx);
ctx.cmd_type = (IpcCommandType)(*(u16 *)(armGetTls()));
ctx.rc = ResultSuccess;
/* Parse based on command type. */
switch (ctx.cmd_type) {
case IpcCommandType_Invalid:
case IpcCommandType_LegacyRequest:
case IpcCommandType_LegacyControl:
return ResultKernelConnectionClosed;
case IpcCommandType_Close:
{
/* Clean up gracefully. */
PrepareBasicResponse(&ctx, 0);
this->Reply();
}
return ResultKernelConnectionClosed;
case IpcCommandType_Control:
case IpcCommandType_ControlWithContext:
ctx.rc = ipcParse(&ctx.request);
ctx.obj_holder = &this->control_holder;
break;
case IpcCommandType_Request:
case IpcCommandType_RequestWithContext:
if (IsDomainObject(&this->obj_holder)) {
ctx.rc = ipcParseDomainRequest(&ctx.request);
} else {
ctx.rc = ipcParse(&ctx.request);
}
break;
default:
return ResultKernelConnectionClosed;
}
if (R_SUCCEEDED(ctx.rc)) {
ctx.cmd_id = ((u32 *)ctx.request.Raw)[2];
this->PreProcessRequest(&ctx);
ctx.rc = this->GetResponse(&ctx);
}
if (ctx.rc == ResultServiceFrameworkRequestDeferredByUser) {
/* Session defer. */
this->SetDeferred(true);
} else if (ctx.rc == ResultKernelConnectionClosed) {
/* Session close, nothing to do. */
} else {
if (R_SUCCEEDED(ctx.rc)) {
this->PostProcessResponse(&ctx);
}
ctx.rc = this->Reply();
if (ctx.rc == ResultKernelTimedOut) {
ctx.rc = ResultSuccess;
}
this->CleanupResponse(&ctx);
}
return ctx.rc;
}
virtual Result HandleDeferred() override {
memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
auto defer_guard = SCOPE_GUARD {
this->SetDeferred(false);
};
R_TRY_CATCH(this->HandleReceived()) {
R_CATCH(ResultServiceFrameworkRequestDeferredByUser) {
defer_guard.Cancel();
return ResultServiceFrameworkRequestDeferredByUser;
}
} R_END_TRY_CATCH;
return ResultSuccess;
}
virtual Result HandleSignaled(u64 timeout) {
R_TRY(this->Receive());
R_TRY(this->HandleReceived());
return ResultSuccess;
}
virtual void PreProcessRequest(IpcResponseContext *ctx) {
/* ... */
(void)(ctx);
}
virtual void PostProcessResponse(IpcResponseContext *ctx) {
/* ... */
(void)(ctx);
}
virtual void CleanupResponse(IpcResponseContext *ctx) {
std::memset(this->backup_tls, 0, sizeof(this->backup_tls));
}
public:
class IHipcControlService : public IServiceObject {
private:
enum class CommandId {
ConvertCurrentObjectToDomain = 0,
CopyFromCurrentDomain = 1,
CloneCurrentObject = 2,
QueryPointerBufferSize = 3,
CloneCurrentObjectEx = 4,
};
private:
ServiceSession *session;
public:
explicit IHipcControlService(ServiceSession *s) : session(s) {
}
virtual ~IHipcControlService() override { }
Result ConvertCurrentObjectToDomain(Out<u32> object_id) {
/* Allocate new domain. */
auto new_domain = this->session->GetDomainManager()->AllocateDomain();
if (new_domain == nullptr) {
return ResultHipcOutOfDomains;
}
/* Reserve an object in the domain for our session. */
u32 reserved_id;
R_TRY(new_domain->ReserveObject(&reserved_id));
new_domain->SetObject(reserved_id, std::move(this->session->obj_holder));
this->session->obj_holder = std::move(ServiceObjectHolder(std::move(new_domain)));
/* Return the object id. */
object_id.SetValue(reserved_id);
return ResultSuccess;
}
Result CopyFromCurrentDomain(Out<MovedHandle> out_h, u32 id) {
auto domain = this->session->obj_holder.GetServiceObject<IDomainObject>();
if (domain == nullptr) {
return ResultHipcTargetNotDomain;
}
auto object = domain->GetObject(id);
if (object == nullptr) {
return ResultHipcDomainObjectNotFound;
}
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
this->session->GetSessionManager()->AddSession(server_h, std::move(object->Clone()));
out_h.SetValue(client_h);
return ResultSuccess;
}
void CloneCurrentObject(Out<MovedHandle> out_h) {
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
this->session->GetSessionManager()->AddSession(server_h, std::move(this->session->obj_holder.Clone()));
out_h.SetValue(client_h);
}
void QueryPointerBufferSize(Out<u16> size) {
size.SetValue(this->session->pointer_buffer.size());
}
void CloneCurrentObjectEx(Out<MovedHandle> out_h, u32 which) {
/* TODO: Figure out what this u32 controls. */
return CloneCurrentObject(out_h);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, ConvertCurrentObjectToDomain),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, CopyFromCurrentDomain),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, CloneCurrentObject),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, QueryPointerBufferSize),
MAKE_SERVICE_COMMAND_META(ServiceSession::IHipcControlService, CloneCurrentObjectEx),
};
};
};

View file

@ -1,34 +0,0 @@
/*
* 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 <atomic>
#include "../waitable_manager_base.hpp"
#include "ipc_service_object.hpp"
class SessionManagerBase : public WaitableManagerBase, public DomainManager {
public:
SessionManagerBase() = default;
virtual ~SessionManagerBase() = default;
virtual void AddSession(Handle server_h, ServiceObjectHolder &&service) = 0;
static Result CreateSessionHandles(Handle *server_h, Handle *client_h) {
return svcCreateSession(server_h, client_h, 0, 0);
}
};

View file

@ -1,152 +0,0 @@
/*
* 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 <type_traits>
#include "ipc_out.hpp"
/* Represents an input PID. */
struct PidDescriptorTag{};
struct PidDescriptor : public PidDescriptorTag {
u64 pid;
void operator=(u64 &p) {
pid = p;
}
PidDescriptor(u64 p) : pid(p) { }
};
struct IpcHandleTag{};
struct IpcHandle : public IpcHandleTag {
Handle handle;
};
/* Represents a moved handle. */
struct MovedHandle : public IpcHandle {
void operator=(const Handle &h) {
this->handle = h;
}
void operator=(const IpcHandle &o) {
this->handle = o.handle;
}
MovedHandle(Handle h) {
this->handle = h;
}
Handle GetValue() const {
return this->handle;
}
};
/* Represents a copied handle. */
struct CopiedHandle : public IpcHandle {
void operator=(const Handle &h) {
handle = h;
}
void operator=(const IpcHandle &o) {
this->handle = o.handle;
}
CopiedHandle(Handle h) {
this->handle = h;
}
Handle GetValue() const {
return this->handle;
}
};
template <>
class Out<MovedHandle> : public OutHandleTag {
private:
MovedHandle *obj;
public:
Out(IpcHandle *o) : obj(static_cast<MovedHandle *>(o)) { }
void SetValue(const Handle& h) {
*obj = h;
}
void SetValue(const MovedHandle& o) {
*obj = o;
}
const MovedHandle& GetValue() {
return *obj;
}
MovedHandle* GetPointer() {
return obj;
}
Handle* GetHandlePointer() {
return &obj->handle;
}
/* Convenience operators. */
MovedHandle& operator*() {
return *obj;
}
MovedHandle* operator->() {
return obj;
}
};
template <>
class Out<CopiedHandle> : public OutHandleTag {
private:
CopiedHandle *obj;
public:
Out(IpcHandle *o) : obj(static_cast<CopiedHandle *>(o)) { }
void SetValue(const Handle& h) {
*obj = h;
}
void SetValue(const CopiedHandle& o) {
*obj = o;
}
const CopiedHandle& GetValue() {
return *obj;
}
CopiedHandle* GetPointer() {
return obj;
}
Handle* GetHandlePointer() {
return &obj->handle;
}
/* Convenience operators. */
CopiedHandle& operator*() {
return *obj;
}
CopiedHandle* operator->() {
return obj;
}
};

View file

@ -1,79 +0,0 @@
/*
* 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 "os.hpp"
#include "waitable_manager_base.hpp"
class IWaitable {
private:
u64 wait_priority = 0;
bool is_deferred = false;
WaitableManagerBase *manager = nullptr;
protected:
sts::os::Mutex sig_lock;
bool is_signaled = false;
public:
virtual ~IWaitable() = default;
virtual Result HandleDeferred() {
/* By default, HandleDeferred panics, because object shouldn't be deferrable. */
std::abort();
}
bool IsSignaled() {
std::scoped_lock lock(this->sig_lock);
return this->is_signaled;
}
virtual Handle GetHandle() = 0;
virtual Result HandleSignaled(u64 timeout) = 0;
WaitableManagerBase *GetManager() {
return this->manager;
}
void SetManager(WaitableManagerBase *m) {
this->manager = m;
}
void UpdatePriority() {
if (manager) {
this->wait_priority = this->manager->GetNextPriority();
}
}
bool IsDeferred() {
return this->is_deferred;
}
void SetDeferred(bool d) {
this->is_deferred = d;
}
static bool Compare(IWaitable *a, IWaitable *b) {
return (a->wait_priority < b->wait_priority) && !a->IsDeferred() && (a->GetHandle() != INVALID_HANDLE);
}
void NotifyManagerSignaled() {
if (this->manager) {
this->manager->NotifySignaled(this);
}
}
};

View file

@ -1,47 +0,0 @@
/*
* 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 <atomic>
#include <stratosphere.hpp>
#include "../ncm.hpp"
#include "mitm_query_service.hpp"
class IMitmServiceObject : public IServiceObject {
protected:
std::shared_ptr<Service> forward_service;
u64 process_id;
sts::ncm::TitleId title_id;
public:
IMitmServiceObject(std::shared_ptr<Service> s, u64 pid, sts::ncm::TitleId tid) : forward_service(s), process_id(pid), title_id(tid) { /* ... */ }
virtual sts::ncm::TitleId GetTitleId() const {
return this->title_id;
}
virtual u64 GetProcessId() const {
return this->process_id;
}
virtual bool IsMitmObject() const override { return true; }
static bool ShouldMitm(u64 pid, sts::ncm::TitleId tid);
protected:
virtual ~IMitmServiceObject() = default;
};

View file

@ -1,36 +0,0 @@
/*
* 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 <stratosphere.hpp>
#include "../ncm.hpp"
template <typename T>
class MitmQueryService : public IServiceObject {
private:
enum class CommandId {
ShouldMitm = 65000,
};
protected:
void ShouldMitm(Out<bool> should_mitm, u64 process_id, sts::ncm::TitleId title_id) {
should_mitm.SetValue(T::ShouldMitm(process_id, title_id));
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(MitmQueryService<T>, ShouldMitm),
};
};

View file

@ -1,91 +0,0 @@
/*
* 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 "mitm_query_service.hpp"
#include "mitm_session.hpp"
#include "../utilities.hpp"
void RegisterMitmServerQueryHandle(Handle query_h, ServiceObjectHolder &&service);
template <typename T, auto MakeShared>
class MitmServer : public IWaitable {
static_assert(std::is_base_of<IMitmServiceObject, T>::value, "MitM Service Objects must derive from IMitmServiceObject");
private:
Handle port_handle;
unsigned int max_sessions;
sts::sm::ServiceName mitm_name;
public:
MitmServer(const char *service_name, unsigned int max_s) : port_handle(0), max_sessions(max_s), mitm_name(sts::sm::ServiceName::Encode(service_name)) {
Handle query_h = INVALID_HANDLE;
R_ASSERT(sts::sm::mitm::InstallMitm(&this->port_handle, &query_h, this->mitm_name));
RegisterMitmServerQueryHandle(query_h, std::move(ServiceObjectHolder(std::move(std::make_shared<MitmQueryService<T>>()))));
}
virtual ~MitmServer() override {
if (this->port_handle) {
R_ASSERT(sts::sm::mitm::UninstallMitm(this->mitm_name));
svcCloseHandle(port_handle);
}
}
SessionManagerBase *GetSessionManager() {
return static_cast<SessionManagerBase *>(this->GetManager());
}
/* IWaitable */
virtual Handle GetHandle() override {
return this->port_handle;
}
virtual Result HandleSignaled(u64 timeout) override {
/* If this server's port was signaled, accept a new session. */
Handle session_h;
R_TRY(svcAcceptSession(&session_h, this->port_handle));
/* Create a forward service for this instance. */
std::shared_ptr<Service> forward_service(new Service(), [](Service *s) {
/* Custom deleter to ensure service is open as long as necessary. */
serviceClose(s);
delete s;
});
u64 client_pid;
sts::ncm::TitleId client_tid;
R_ASSERT(sts::sm::mitm::AcknowledgeSession(forward_service.get(), &client_pid, &client_tid, this->mitm_name));
this->GetSessionManager()->AddWaitable(new MitmSession(session_h, client_pid, forward_service, MakeShared(forward_service, client_pid, client_tid)));
return ResultSuccess;
}
};
template<typename T>
struct MakeSharedMitmHelper {
static constexpr std::shared_ptr<T> Make(std::shared_ptr<Service> forward_srv, u64 client_pid, sts::ncm::TitleId client_tid) {
return std::make_shared<T>(forward_srv, client_pid, client_tid);
}
};
template<typename T, auto MakeShared = MakeSharedMitmHelper<T>::Make>
static void AddMitmServerToManager(SessionManagerBase *manager, const char *srv_name, unsigned int max_sessions) {
auto *srv = new MitmServer<T, MakeShared>(srv_name, max_sessions);
manager->AddWaitable(srv);
}

View file

@ -1,359 +0,0 @@
/*
* 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 <stratosphere.hpp>
#include "imitmserviceobject.hpp"
#include "../results.hpp"
#include "mitm_query_service.hpp"
class MitmSession final : public ServiceSession {
private:
/* This will be for the actual session. */
std::shared_ptr<Service> forward_service;
struct PostProcessHandlerContext {
bool closed;
void (*handler)(IMitmServiceObject *, IpcResponseContext *);
};
/* Store a handler for the service. */
std::shared_ptr<PostProcessHandlerContext> service_post_process_ctx;
/* For cleanup usage. */
u64 client_pid;
u32 num_fwd_copy_hnds = 0;
Handle fwd_copy_hnds[8];
public:
template<typename T>
MitmSession(Handle s_h, u64 pid, std::shared_ptr<Service> fs, std::shared_ptr<T> srv) : ServiceSession(s_h), client_pid(pid) {
this->forward_service = std::move(fs);
this->obj_holder = std::move(ServiceObjectHolder(std::move(srv)));
this->service_post_process_ctx = std::make_shared<PostProcessHandlerContext>();
this->service_post_process_ctx->closed = false;
this->service_post_process_ctx->handler = T::PostProcess;
size_t pbs;
R_ASSERT(ipcQueryPointerBufferSize(forward_service->handle, &pbs));
this->pointer_buffer.resize(pbs);
this->control_holder.Reset();
this->control_holder = std::move(ServiceObjectHolder(std::move(std::make_shared<IMitmHipcControlService>(this))));
}
MitmSession(Handle s_h, u64 pid, std::shared_ptr<Service> fs, ServiceObjectHolder &&h, std::shared_ptr<PostProcessHandlerContext> ppc) : ServiceSession(s_h), client_pid(pid) {
this->session_handle = s_h;
this->forward_service = std::move(fs);
this->obj_holder = std::move(h);
this->service_post_process_ctx = ppc;
size_t pbs;
R_ASSERT(ipcQueryPointerBufferSize(forward_service->handle, &pbs));
this->pointer_buffer.resize(pbs);
this->control_holder.Reset();
this->control_holder = std::move(ServiceObjectHolder(std::move(std::make_shared<IMitmHipcControlService>(this))));
}
virtual void PreProcessRequest(IpcResponseContext *ctx) override {
u32 *cmdbuf = (u32 *)armGetTls();
u32 *backup_cmdbuf = (u32 *)this->backup_tls;
if (ctx->request.HasPid) {
/* [ctrl 0] [ctrl 1] [handle desc 0] [pid low] [pid high] */
cmdbuf[4] = 0xFFFE0000UL | (cmdbuf[4] & 0xFFFFUL);
backup_cmdbuf[4] = cmdbuf[4];
}
}
Result ForwardRequest(IpcResponseContext *ctx) {
/* Mitm forward specific preprocessing. */
if (ctx->request.NumStaticsOut) {
u32 *cmdbuf = (u32 *)armGetTls();
/* Overwrite the number of C descriptors to only use a single buffer. */
cmdbuf[1] = (cmdbuf[1] & (~u32(0xF << 10))) | (0x2 << 10);
IpcStaticRecvDescriptor *c_desc = (IpcStaticRecvDescriptor *)(reinterpret_cast<uintptr_t>(ctx->request.RawWithoutPadding) + ctx->request.RawSize);
/* Don't write out of bounds, though this should never happen. */
if (reinterpret_cast<uintptr_t>(c_desc) + sizeof(*c_desc) <= reinterpret_cast<uintptr_t>(cmdbuf) + 0x100) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(this->pointer_buffer.data());
c_desc->Addr = ptr;
c_desc->Packed = (ptr >> 32) | (this->pointer_buffer.size() << 16);
} else {
std::abort();
}
}
/* Dispatch forwards. */
R_TRY(serviceIpcDispatch(this->forward_service.get()));
/* Parse. */
{
IpcParsedCommand r;
if (ctx->request.IsDomainRequest) {
/* We never work with out object ids, so this should be fine. */
ipcParseDomainResponse(&r, 0);
} else {
ipcParse(&r);
}
struct {
u64 magic;
u64 result;
} *resp = (decltype(resp))r.Raw;
for (unsigned int i = 0; i < r.NumHandles; i++) {
if (r.WasHandleCopied[i]) {
this->fwd_copy_hnds[num_fwd_copy_hnds++] = r.Handles[i];
}
}
R_TRY(resp->result);
}
return ResultSuccess;
}
virtual Result GetResponse(IpcResponseContext *ctx) {
FirmwareVersion fw = GetRuntimeFirmwareVersion();
const ServiceCommandMeta *dispatch_table = ctx->obj_holder->GetDispatchTable();
size_t entry_count = ctx->obj_holder->GetDispatchTableEntryCount();
if (IsDomainObject(ctx->obj_holder)) {
switch (ctx->request.InMessageType) {
case DomainMessageType_Invalid:
return ResultKernelConnectionClosed;
case DomainMessageType_Close:
{
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj == nullptr) {
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
if (sub_obj->IsMitmObject()) {
R_TRY(ForwardRequest(ctx));
ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId);
} else {
R_TRY(ctx->obj_holder->GetServiceObject<IDomainObject>()->FreeObject(ctx->request.InThisObjectId));
}
if (ctx->request.InThisObjectId == serviceGetObjectId(this->forward_service.get()) && !this->service_post_process_ctx->closed) {
/* If we're not longer MitMing anything, we'll no longer do any postprocessing. */
this->service_post_process_ctx->closed = true;
}
return ResultSuccess;
}
case DomainMessageType_SendMessage:
{
auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj == nullptr) {
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
dispatch_table = sub_obj->GetDispatchTable();
entry_count = sub_obj->GetDispatchTableEntryCount();
}
}
}
Result (*handler)(IpcResponseContext *ctx) = nullptr;
for (size_t i = 0; i < entry_count; i++) {
if (ctx->cmd_id == dispatch_table[i].cmd_id && dispatch_table[i].fw_low <= fw && fw <= dispatch_table[i].fw_high) {
handler = dispatch_table[i].handler;
break;
}
}
if (handler == nullptr) {
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
R_TRY_CATCH(handler(ctx)) {
R_CATCH(ResultAtmosphereMitmShouldForwardToSession) {
std::memcpy(armGetTls(), this->backup_tls, sizeof(this->backup_tls));
R_TRY(ForwardRequest(ctx));
return ResultSuccess;
}
} R_END_TRY_CATCH;
return ResultSuccess;
}
virtual void PostProcessResponse(IpcResponseContext *ctx) override {
if ((ctx->cmd_type == IpcCommandType_Request || ctx->cmd_type == IpcCommandType_RequestWithContext) && R_SUCCEEDED(ctx->rc)) {
if (this->service_post_process_ctx->closed) {
return;
}
if (!IsDomainObject(ctx->obj_holder) || ctx->request.InThisObjectId == serviceGetObjectId(this->forward_service.get())) {
IMitmServiceObject *obj = nullptr;
if (!IsDomainObject(ctx->obj_holder)) {
obj = ctx->obj_holder->GetServiceObjectUnsafe<IMitmServiceObject>();
} else {
const auto sub_obj = ctx->obj_holder->GetServiceObject<IDomainObject>()->GetObject(ctx->request.InThisObjectId);
if (sub_obj != nullptr) {
obj = sub_obj->GetServiceObjectUnsafe<IMitmServiceObject>();
}
}
if (obj != nullptr) {
this->service_post_process_ctx->handler(obj, ctx);
}
}
}
}
virtual void CleanupResponse(IpcResponseContext *ctx) override {
/* Cleanup tls backup. */
std::memset(this->backup_tls, 0, sizeof(this->backup_tls));
/* Clean up copy handles. */
for (unsigned int i = 0; i < ctx->request.NumHandles; i++) {
if (ctx->request.WasHandleCopied[i]) {
svcCloseHandle(ctx->request.Handles[i]);
}
}
for (unsigned int i = 0; i < this->num_fwd_copy_hnds; i++) {
svcCloseHandle(this->fwd_copy_hnds[i]);
}
this->num_fwd_copy_hnds = 0;
}
public:
class IMitmHipcControlService : public IServiceObject {
private:
enum class CommandId {
ConvertCurrentObjectToDomain = 0,
CopyFromCurrentDomain = 1,
CloneCurrentObject = 2,
QueryPointerBufferSize = 3,
CloneCurrentObjectEx = 4,
};
private:
MitmSession *session;
public:
explicit IMitmHipcControlService(MitmSession *s) : session(s) {
}
virtual ~IMitmHipcControlService() override { }
public:
Result ConvertCurrentObjectToDomain(Out<u32> object_id) {
if (IsDomainObject(this->session->obj_holder)) {
return ResultKernelConnectionClosed;
}
R_TRY(serviceConvertToDomain(this->session->forward_service.get()));
u32 expected_id = serviceGetObjectId(this->session->forward_service.get());
/* Allocate new domain. */
auto new_domain = this->session->GetDomainManager()->AllocateDomain();
if (new_domain == nullptr) {
/* If our domains mismatch, we're in trouble. */
return ResultKernelConnectionClosed;
}
/* Reserve the expected object in the domain for our session. */
if (R_FAILED(new_domain->ReserveSpecificObject(expected_id))) {
return ResultKernelConnectionClosed;
}
new_domain->SetObject(expected_id, std::move(this->session->obj_holder));
this->session->obj_holder = std::move(ServiceObjectHolder(std::move(new_domain)));
/* Return the object id. */
object_id.SetValue(expected_id);
return ResultSuccess;
}
Result CopyFromCurrentDomain(Out<MovedHandle> out_h, u32 id) {
auto domain = this->session->obj_holder.GetServiceObject<IDomainObject>();
if (domain == nullptr) {
return ResultHipcTargetNotDomain;
}
auto object = domain->GetObject(id);
if (object == nullptr) {
/* Forward onwards. */
u32 *buf = (u32 *)armGetTls();
buf[0] = IpcCommandType_Control;
buf[1] = 0xA;
buf[4] = SFCI_MAGIC;
buf[5] = 0;
buf[6] = 1;
buf[7] = 0;
buf[8] = id;
buf[9] = 0;
R_TRY(ipcDispatch(this->session->forward_service->handle));
{
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *raw = (decltype(raw))r.Raw;
R_TRY(raw->result);
out_h.SetValue(r.Handles[0]);
this->session->fwd_copy_hnds[this->session->num_fwd_copy_hnds++] = r.Handles[0];
}
return ResultSuccess;
}
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
out_h.SetValue(client_h);
if (id == serviceGetObjectId(this->session->forward_service.get())) {
this->session->GetSessionManager()->AddWaitable(new MitmSession(server_h, this->session->client_pid, this->session->forward_service, std::move(object->Clone()), this->session->service_post_process_ctx));
} else {
this->session->GetSessionManager()->AddSession(server_h, std::move(object->Clone()));
}
return ResultSuccess;
}
void CloneCurrentObject(Out<MovedHandle> out_h) {
Handle server_h, client_h;
R_ASSERT(SessionManagerBase::CreateSessionHandles(&server_h, &client_h));
this->session->GetSessionManager()->AddWaitable(new MitmSession(server_h, this->session->client_pid, this->session->forward_service, std::move(this->session->obj_holder.Clone()), this->session->service_post_process_ctx));
out_h.SetValue(client_h);
}
void QueryPointerBufferSize(Out<u16> size) {
size.SetValue(this->session->pointer_buffer.size());
}
void CloneCurrentObjectEx(Out<MovedHandle> out_h, u32 which) {
/* TODO: Figure out what this u32 controls. */
return CloneCurrentObject(out_h);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, ConvertCurrentObjectToDomain),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, CopyFromCurrentDomain),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, CloneCurrentObject),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, QueryPointerBufferSize),
MAKE_SERVICE_COMMAND_META(MitmSession::IMitmHipcControlService, CloneCurrentObjectEx),
};
};
};

View file

@ -1,83 +0,0 @@
/*
* 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 "iwaitable.hpp"
#include "ipc.hpp"
#include "utilities.hpp"
template<typename T, auto MakeShared>
class IServer : public IWaitable {
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
protected:
Handle port_handle;
unsigned int max_sessions;
public:
IServer(unsigned int max_s) : port_handle(0), max_sessions(max_s) { }
virtual ~IServer() {
if (port_handle) {
svcCloseHandle(port_handle);
}
}
SessionManagerBase *GetSessionManager() {
return static_cast<SessionManagerBase *>(this->GetManager());
}
/* IWaitable */
virtual Handle GetHandle() override {
return this->port_handle;
}
virtual Result HandleSignaled(u64 timeout) override {
/* If this server's port was signaled, accept a new session. */
Handle session_h;
R_TRY(svcAcceptSession(&session_h, this->port_handle));
this->GetSessionManager()->AddSession(session_h, std::move(ServiceObjectHolder(std::move(MakeShared()))));
return ResultSuccess;
}
};
template <typename T, auto MakeShared = std::make_shared<T>>
class ServiceServer : public IServer<T, MakeShared> {
public:
ServiceServer(const char *service_name, unsigned int max_s) : IServer<T, MakeShared>(max_s) {
DoWithSmSession([&]() {
R_ASSERT(smRegisterService(&this->port_handle, service_name, false, this->max_sessions));
});
}
};
template <typename T, auto MakeShared = std::make_shared<T>>
class ExistingPortServer : public IServer<T, MakeShared> {
public:
ExistingPortServer(Handle port_h, unsigned int max_s) : IServer<T, MakeShared>(max_s) {
this->port_handle = port_h;
}
};
template <typename T, auto MakeShared = std::make_shared<T>>
class ManagedPortServer : public IServer<T, MakeShared> {
public:
ManagedPortServer(const char *service_name, unsigned int max_s) : IServer<T, MakeShared>(max_s) {
R_ASSERT(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions));
}
};

View file

@ -1,413 +0,0 @@
/*
* 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 <memory>
#include <functional>
#include "results.hpp"
#include "os.hpp"
#include "waitable_manager_base.hpp"
#include "ipc.hpp"
#include "servers.hpp"
#include "util.hpp"
static inline Handle GetCurrentThreadHandle() {
return threadGetCurHandle();
}
struct DefaultManagerOptions {
static constexpr size_t PointerBufferSize = 0;
static constexpr size_t MaxDomains = 0;
static constexpr size_t MaxDomainObjects = 0;
};
struct DomainEntry {
ServiceObjectHolder obj_holder;
IDomainObject *owner = nullptr;
};
template<typename ManagerOptions = DefaultManagerOptions>
class WaitableManager : public SessionManagerBase {
private:
/* Domain Manager */
sts::os::Mutex domain_lock;
std::array<uintptr_t, ManagerOptions::MaxDomains> domain_keys;
std::array<bool, ManagerOptions::MaxDomains> is_domain_allocated;
std::array<DomainEntry, ManagerOptions::MaxDomainObjects> domain_objects;
/* Waitable Manager */
std::vector<IWaitable *> to_add_waitables;
std::vector<IWaitable *> waitables;
std::vector<IWaitable *> deferred_waitables;
u32 num_extra_threads = 0;
sts::os::Thread *threads = nullptr;
sts::os::Mutex process_lock;
sts::os::Mutex signal_lock;
sts::os::Mutex add_lock;
sts::os::Mutex cur_thread_lock;
sts::os::Mutex deferred_lock;
bool has_new_waitables = false;
std::atomic<bool> should_stop = false;
IWaitable *next_signaled = nullptr;
Handle main_thread_handle = INVALID_HANDLE;
Handle cur_thread_handle = INVALID_HANDLE;
public:
WaitableManager(u32 n, u32 ss = 0x8000) : num_extra_threads(n-1) {
u32 prio;
if (num_extra_threads) {
threads = new sts::os::Thread[num_extra_threads];
R_ASSERT(svcGetThreadPriority(&prio, CUR_THREAD_HANDLE));
for (unsigned int i = 0; i < num_extra_threads; i++) {
R_ASSERT(threads[i].Initialize(&WaitableManager::ProcessLoop, this, ss, prio));
}
}
}
~WaitableManager() override {
/* This should call the destructor for every waitable. */
std::for_each(to_add_waitables.begin(), to_add_waitables.end(), std::default_delete<IWaitable>{});
std::for_each(waitables.begin(), waitables.end(), std::default_delete<IWaitable>{});
std::for_each(deferred_waitables.begin(), deferred_waitables.end(), std::default_delete<IWaitable>{});
/* If we've reached here, we should already have exited the threads. */
}
virtual void AddWaitable(IWaitable *w) override {
std::scoped_lock lk{this->add_lock};
this->to_add_waitables.push_back(w);
w->SetManager(this);
this->has_new_waitables = true;
this->CancelSynchronization();
}
virtual void RequestStop() {
this->should_stop = true;
this->CancelSynchronization();
}
virtual void CancelSynchronization() {
svcCancelSynchronization(GetProcessingThreadHandle());
}
virtual void NotifySignaled(IWaitable *w) override {
std::scoped_lock lk{this->signal_lock};
if (this->next_signaled == nullptr) {
this->next_signaled = w;
}
this->CancelSynchronization();
}
virtual void Process() override {
/* Add initial set of waitables. */
AddWaitablesInternal();
/* Set main thread handle. */
this->main_thread_handle = GetCurrentThreadHandle();
for (unsigned int i = 0; i < num_extra_threads; i++) {
R_ASSERT(threads[i].Start());
}
ProcessLoop(this);
}
private:
void SetProcessingThreadHandle(Handle h) {
std::scoped_lock lk{this->cur_thread_lock};
this->cur_thread_handle = h;
}
Handle GetProcessingThreadHandle() {
std::scoped_lock lk{this->cur_thread_lock};
return this->cur_thread_handle;
}
static void ProcessLoop(void *t) {
WaitableManager *this_ptr = (WaitableManager *)t;
while (true) {
IWaitable *w = this_ptr->GetWaitable();
if (this_ptr->should_stop) {
if (GetCurrentThreadHandle() == this_ptr->main_thread_handle) {
/* Join all threads but the main one. */
for (unsigned int i = 0; i < this_ptr->num_extra_threads; i++) {
this_ptr->threads[i].Join();
}
break;
} else {
/* Return, this will cause thread to exit. */
return;
}
}
if (w) {
if (w->HandleSignaled(0) == ResultKernelConnectionClosed) {
/* Close! */
delete w;
} else {
if (w->IsDeferred()) {
std::scoped_lock lk{this_ptr->deferred_lock};
this_ptr->deferred_waitables.push_back(w);
} else {
this_ptr->AddWaitable(w);
}
}
}
/* We finished processing, and maybe that means we can stop deferring an object. */
{
std::scoped_lock lk{this_ptr->deferred_lock};
bool undeferred_any = true;
while (undeferred_any) {
undeferred_any = false;
for (auto it = this_ptr->deferred_waitables.begin(); it != this_ptr->deferred_waitables.end();) {
auto w = *it;
const bool closed = (w->HandleDeferred() == ResultKernelConnectionClosed);
if (closed || !w->IsDeferred()) {
/* Remove from the deferred list, set iterator. */
it = this_ptr->deferred_waitables.erase(it);
if (closed) {
/* Delete the closed waitable. */
delete w;
} else {
/* Add to the waitables list. */
this_ptr->AddWaitable(w);
undeferred_any = true;
}
} else {
/* Move on to the next deferred waitable. */
it++;
}
}
}
}
}
}
IWaitable *GetWaitable() {
std::scoped_lock lk{this->process_lock};
/* Set processing thread handle while in scope. */
SetProcessingThreadHandle(GetCurrentThreadHandle());
ON_SCOPE_EXIT {
SetProcessingThreadHandle(INVALID_HANDLE);
};
/* Prepare variables for result. */
this->next_signaled = nullptr;
IWaitable *result = nullptr;
if (this->should_stop) {
return nullptr;
}
/* Add new waitables, if any. */
AddWaitablesInternal();
/* First, see if anything's already signaled. */
for (auto &w : this->waitables) {
if (w->IsSignaled()) {
result = w;
}
}
/* It's possible somebody signaled us while we were iterating. */
{
std::scoped_lock lk{this->signal_lock};
if (this->next_signaled != nullptr) result = this->next_signaled;
}
if (result == nullptr) {
std::vector<Handle> handles;
std::vector<IWaitable *> wait_list;
int handle_index = 0;
while (result == nullptr) {
/* Sort waitables by priority. */
std::sort(this->waitables.begin(), this->waitables.end(), IWaitable::Compare);
/* Copy out handles. */
handles.resize(this->waitables.size());
wait_list.resize(this->waitables.size());
unsigned int num_handles = 0;
/* Try to add waitables to wait list. */
for (unsigned int i = 0; i < this->waitables.size(); i++) {
Handle h = this->waitables[i]->GetHandle();
if (h != INVALID_HANDLE) {
wait_list[num_handles] = this->waitables[i];
handles[num_handles++] = h;
}
}
/* Wait forever. */
const Result wait_res = svcWaitSynchronization(&handle_index, handles.data(), num_handles, U64_MAX);
if (this->should_stop) {
return nullptr;
}
if (R_SUCCEEDED(wait_res)) {
IWaitable *w = wait_list[handle_index];
size_t w_ind = std::distance(this->waitables.begin(), std::find(this->waitables.begin(), this->waitables.end(), w));
std::for_each(waitables.begin(), waitables.begin() + w_ind + 1, std::mem_fn(&IWaitable::UpdatePriority));
result = w;
} else if (wait_res == ResultKernelTimedOut) {
/* Timeout: Just update priorities. */
std::for_each(waitables.begin(), waitables.end(), std::mem_fn(&IWaitable::UpdatePriority));
} else if (wait_res == ResultKernelCancelled) {
/* svcCancelSynchronization was called. */
AddWaitablesInternal();
{
std::scoped_lock lk{this->signal_lock};
if (this->next_signaled != nullptr) {
result = this->next_signaled;
}
}
} else {
/* TODO: Consider the following cases that this covers: */
/* 7601: Thread termination requested. */
/* E401: Handle is dead. */
/* E601: Handle list address invalid. */
/* EE01: Too many handles. */
std::abort();
}
}
}
this->waitables.erase(std::remove_if(this->waitables.begin(), this->waitables.end(), [&](IWaitable *w) { return w == result; }), this->waitables.end());
return result;
}
void AddWaitablesInternal() {
std::scoped_lock lk{this->add_lock};
if (this->has_new_waitables) {
this->waitables.insert(this->waitables.end(), this->to_add_waitables.begin(), this->to_add_waitables.end());
this->to_add_waitables.clear();
this->has_new_waitables = false;
}
}
/* Session Manager */
public:
virtual void AddSession(Handle server_h, ServiceObjectHolder &&service) override {
this->AddWaitable(new ServiceSession(server_h, ManagerOptions::PointerBufferSize, std::move(service)));
}
/* Domain Manager */
public:
virtual std::shared_ptr<IDomainObject> AllocateDomain() override {
std::scoped_lock lk{this->domain_lock};
for (size_t i = 0; i < ManagerOptions::MaxDomains; i++) {
if (!this->is_domain_allocated[i]) {
auto new_domain = std::make_shared<IDomainObject>(this);
this->domain_keys[i] = reinterpret_cast<uintptr_t>(new_domain.get());
this->is_domain_allocated[i] = true;
return new_domain;
}
}
return nullptr;
}
void FreeDomain(IDomainObject *domain) override {
std::scoped_lock lk{this->domain_lock};
for (size_t i = 0; i < ManagerOptions::MaxDomainObjects; i++) {
FreeObject(domain, i+1);
}
for (size_t i = 0; i < ManagerOptions::MaxDomains; i++) {
if (this->domain_keys[i] == reinterpret_cast<uintptr_t>(domain)) {
this->is_domain_allocated[i] = false;
break;
}
}
}
virtual Result ReserveObject(IDomainObject *domain, u32 *out_object_id) override {
std::scoped_lock lk{this->domain_lock};
for (size_t i = 0; i < ManagerOptions::MaxDomainObjects; i++) {
if (this->domain_objects[i].owner == nullptr) {
this->domain_objects[i].owner = domain;
*out_object_id = i+1;
return ResultSuccess;
}
}
return ResultServiceFrameworkOutOfDomainEntries;
}
virtual Result ReserveSpecificObject(IDomainObject *domain, u32 object_id) override {
std::scoped_lock lk{this->domain_lock};
if (object_id > ManagerOptions::MaxDomainObjects || object_id < MinimumDomainId) {
return ResultServiceFrameworkOutOfDomainEntries;
}
if (this->domain_objects[object_id-1].owner == nullptr) {
this->domain_objects[object_id-1].owner = domain;
return ResultSuccess;
}
return ResultServiceFrameworkOutOfDomainEntries;
}
virtual void SetObject(IDomainObject *domain, u32 object_id, ServiceObjectHolder&& holder) override {
std::scoped_lock lk{this->domain_lock};
if (object_id > ManagerOptions::MaxDomainObjects || object_id < MinimumDomainId) {
return;
}
if (this->domain_objects[object_id-1].owner == domain) {
this->domain_objects[object_id-1].obj_holder = std::move(holder);
}
}
virtual ServiceObjectHolder *GetObject(IDomainObject *domain, u32 object_id) override {
std::scoped_lock lk{this->domain_lock};
if (object_id > ManagerOptions::MaxDomainObjects || object_id < MinimumDomainId) {
return nullptr;
}
if (this->domain_objects[object_id-1].owner == domain) {
return &this->domain_objects[object_id-1].obj_holder;
}
return nullptr;
}
virtual Result FreeObject(IDomainObject *domain, u32 object_id) override {
std::scoped_lock lk{this->domain_lock};
if (object_id > ManagerOptions::MaxDomainObjects || object_id < MinimumDomainId) {
return ResultHipcDomainObjectNotFound;
}
if (this->domain_objects[object_id-1].owner == domain) {
this->domain_objects[object_id-1].obj_holder.Reset();
this->domain_objects[object_id-1].owner = nullptr;
return ResultSuccess;
}
return ResultHipcDomainObjectNotFound;
}
virtual Result ForceFreeObject(u32 object_id) override {
std::scoped_lock lk{this->domain_lock};
if (object_id > ManagerOptions::MaxDomainObjects || object_id < MinimumDomainId) {
return ResultHipcDomainObjectNotFound;
}
if (this->domain_objects[object_id-1].owner != nullptr) {
this->domain_objects[object_id-1].obj_holder.Reset();
this->domain_objects[object_id-1].owner = nullptr;
return ResultSuccess;
}
return ResultHipcDomainObjectNotFound;
}
};

View file

@ -0,0 +1,153 @@
/*
* 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::ams {
namespace {
FirmwareVersion g_firmware_version;
bool g_has_cached;
os::Mutex g_mutex;
void CacheValues() {
if (__atomic_load_n(&g_has_cached, __ATOMIC_SEQ_CST)) {
return;
}
std::scoped_lock lk(g_mutex);
if (g_has_cached) {
return;
}
/* TODO: spl::smc:: */
u32 target_fw = 0;
{
SecmonArgs args = {0};
args.X[0] = 0xC3000002; /* smcGetConfig */
args.X[1] = 65000; /* ConfigItem_ExosphereVersion */
R_ASSERT(svcCallSecureMonitor(&args));
STS_ASSERT(args.X[0] == 0);
target_fw = (args.X[1] >> 0x08) & 0xFF;
}
switch (static_cast<ams::TargetFirmware>(target_fw)) {
case ams::TargetFirmware_100:
g_firmware_version = FirmwareVersion_100;
break;
case ams::TargetFirmware_200:
g_firmware_version = FirmwareVersion_200;
break;
case ams::TargetFirmware_300:
g_firmware_version = FirmwareVersion_300;
break;
case ams::TargetFirmware_400:
g_firmware_version = FirmwareVersion_400;
break;
case ams::TargetFirmware_500:
g_firmware_version = FirmwareVersion_500;
break;
case ams::TargetFirmware_600:
case ams::TargetFirmware_620:
g_firmware_version = FirmwareVersion_600;
break;
case ams::TargetFirmware_700:
g_firmware_version = FirmwareVersion_700;
break;
case ams::TargetFirmware_800:
g_firmware_version = FirmwareVersion_800;
break;
case ams::TargetFirmware_810:
g_firmware_version = FirmwareVersion_810;
break;
case ams::TargetFirmware_900:
g_firmware_version = FirmwareVersion_900;
break;
STS_UNREACHABLE_DEFAULT_CASE();
}
__atomic_store_n(&g_has_cached, true, __ATOMIC_SEQ_CST);
}
}
FirmwareVersion GetRuntimeFirmwareVersion() {
CacheValues();
return g_firmware_version;
}
void SetFirmwareVersionForLibnx() {
u32 major = 0, minor = 0, micro = 0;
switch (GetRuntimeFirmwareVersion()) {
case FirmwareVersion_100:
major = 1;
minor = 0;
micro = 0;
break;
case FirmwareVersion_200:
major = 2;
minor = 0;
micro = 0;
break;
case FirmwareVersion_300:
major = 3;
minor = 0;
micro = 0;
break;
case FirmwareVersion_400:
major = 4;
minor = 0;
micro = 0;
break;
case FirmwareVersion_500:
major = 5;
minor = 0;
micro = 0;
break;
case FirmwareVersion_600:
major = 6;
minor = 0;
micro = 0;
break;
case FirmwareVersion_700:
major = 7;
minor = 0;
micro = 0;
break;
case FirmwareVersion_800:
major = 8;
minor = 0;
micro = 0;
break;
case FirmwareVersion_810:
major = 8;
minor = 1;
micro = 0;
break;
case FirmwareVersion_900:
major = 9;
minor = 0;
micro = 0;
break;
STS_UNREACHABLE_DEFAULT_CASE();
}
hosversionSet(MAKEHOSVERSION(major, minor, micro));
}
}

View file

@ -38,7 +38,7 @@ namespace sts::cfg {
/* Open the file. */
FsFile flag_file;
if (R_FAILED(fsFsOpenFile(&sd_fs, flag_path, FS_OPEN_READ, &flag_file))) {
if (R_FAILED(fsFsOpenFile(&sd_fs, flag_path, FsOpenMode_Read, &flag_file))) {
return false;
}
fsFileClose(&flag_file);

View file

@ -185,7 +185,7 @@ namespace sts::cfg {
/* Open the file. */
FsFile config_file;
if (R_FAILED(fsFsOpenFile(&sd_fs, path, FS_OPEN_READ, &config_file))) {
if (R_FAILED(fsFsOpenFile(&sd_fs, path, FsOpenMode_Read, &config_file))) {
return;
}
ON_SCOPE_EXIT { fsFileClose(&config_file); };

View file

@ -35,11 +35,11 @@ namespace sts::cfg {
/* SD card helpers. */
void GetPrivilegedProcessIdRange(u64 *out_min, u64 *out_max) {
u64 min = 0, max = 0;
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_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 (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) {
} else if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_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));

View file

@ -1,149 +0,0 @@
/*
* 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 <mutex>
#include <switch.h>
#include <stratosphere.hpp>
static FirmwareVersion g_firmware_version = FirmwareVersion_Min;
static bool g_HasCached = 0;
static Mutex g_Mutex;
static void _CacheValues(void)
{
if (__atomic_load_n(&g_HasCached, __ATOMIC_SEQ_CST))
return;
mutexLock(&g_Mutex);
if (g_HasCached) {
mutexUnlock(&g_Mutex);
return;
}
u32 target_fw = 0;
{
SecmonArgs args = {0};
args.X[0] = 0xC3000002; /* smcGetConfig */
args.X[1] = 65000; /* ConfigItem_ExosphereVersion */
R_ASSERT(svcCallSecureMonitor(&args));
STS_ASSERT(args.X[0] == 0);
target_fw = (args.X[1] >> 0x08) & 0xFF;
}
switch (static_cast<AtmosphereTargetFirmware>(target_fw)) {
case AtmosphereTargetFirmware_100:
g_firmware_version = FirmwareVersion_100;
break;
case AtmosphereTargetFirmware_200:
g_firmware_version = FirmwareVersion_200;
break;
case AtmosphereTargetFirmware_300:
g_firmware_version = FirmwareVersion_300;
break;
case AtmosphereTargetFirmware_400:
g_firmware_version = FirmwareVersion_400;
break;
case AtmosphereTargetFirmware_500:
g_firmware_version = FirmwareVersion_500;
break;
case AtmosphereTargetFirmware_600:
case AtmosphereTargetFirmware_620:
g_firmware_version = FirmwareVersion_600;
break;
case AtmosphereTargetFirmware_700:
g_firmware_version = FirmwareVersion_700;
break;
case AtmosphereTargetFirmware_800:
g_firmware_version = FirmwareVersion_800;
break;
case AtmosphereTargetFirmware_810:
g_firmware_version = FirmwareVersion_810;
break;
case AtmosphereTargetFirmware_900:
g_firmware_version = FirmwareVersion_900;
break;
STS_UNREACHABLE_DEFAULT_CASE();
}
__atomic_store_n(&g_HasCached, true, __ATOMIC_SEQ_CST);
mutexUnlock(&g_Mutex);
}
FirmwareVersion GetRuntimeFirmwareVersion() {
_CacheValues();
return g_firmware_version;
}
void SetFirmwareVersionForLibnx() {
u32 major = 0, minor = 0, micro = 0;
switch (GetRuntimeFirmwareVersion()) {
case FirmwareVersion_100:
major = 1;
minor = 0;
micro = 0;
break;
case FirmwareVersion_200:
major = 2;
minor = 0;
micro = 0;
break;
case FirmwareVersion_300:
major = 3;
minor = 0;
micro = 0;
break;
case FirmwareVersion_400:
major = 4;
minor = 0;
micro = 0;
break;
case FirmwareVersion_500:
major = 5;
minor = 0;
micro = 0;
break;
case FirmwareVersion_600:
major = 6;
minor = 0;
micro = 0;
break;
case FirmwareVersion_700:
major = 7;
minor = 0;
micro = 0;
break;
case FirmwareVersion_800:
major = 8;
minor = 0;
micro = 0;
break;
case FirmwareVersion_810:
major = 8;
minor = 1;
micro = 0;
break;
case FirmwareVersion_900:
major = 9;
minor = 0;
micro = 0;
break;
STS_UNREACHABLE_DEFAULT_CASE();
}
hosversionSet(MAKEHOSVERSION(major, minor, micro));
}

View file

@ -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 (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_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 (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_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 (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
if (ams::GetRuntimeFirmwareVersion() >= FirmwareVersion_200) {
return MapCodeMemoryInProcessModern(out_mcm, process_handle, base_address, size);
} else {
return MapCodeMemoryInProcessDeprecated(out_mcm, process_handle, base_address, size);

View file

@ -1,47 +0,0 @@
/*
* 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 <mutex>
#include <switch.h>
#include <stratosphere.hpp>
static sts::os::Mutex g_server_query_mutex;
static sts::os::Thread g_server_query_manager_thread;
static SessionManagerBase *g_server_query_manager = nullptr;
static void ServerQueryManagerThreadFunc(void *arg) {
g_server_query_manager->Process();
}
void RegisterMitmServerQueryHandle(Handle query_h, ServiceObjectHolder &&service) {
std::scoped_lock lock(g_server_query_mutex);
const bool exists = g_server_query_manager != nullptr;
if (!exists) {
/* Create a new waitable manager if it doesn't exist already. */
static auto s_server_query_manager = WaitableManager(1);
g_server_query_manager = &s_server_query_manager;
}
/* Add session to the manager. */
g_server_query_manager->AddSession(query_h, std::move(service));
/* If this is our first time, launch thread. */
if (!exists) {
R_ASSERT(g_server_query_manager_thread.Initialize(&ServerQueryManagerThreadFunc, nullptr, 0x4000, 27));
R_ASSERT(g_server_query_manager_thread.Start());
}
}

View file

@ -36,7 +36,10 @@ namespace sts::pm::dmnt {
}
Result HookToCreateApplicationProcess(Handle *out_handle) {
return pmdmntEnableDebugForApplication(out_handle);
Event evt;
R_TRY(pmdmntEnableDebugForApplication(&evt));
*out_handle = evt.revent;
return ResultSuccess;
}
Result AtmosphereGetProcessInfo(Handle *out_handle, ncm::TitleLocation *out_loc, u64 process_id) {

View file

@ -32,7 +32,7 @@ namespace sts::updater {
/* Configuration Prototypes. */
bool HasEks(BootImageUpdateType boot_image_update_type);
bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type);
u32 GetNcmTitleType(BootModeType mode);
NcmContentMetaType GetNcmContentMetaType(BootModeType mode);
Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size);
/* Verification Prototypes. */
@ -86,7 +86,7 @@ namespace sts::updater {
}
}
u32 GetNcmTitleType(BootModeType mode) {
NcmContentMetaType GetNcmContentMetaType(BootModeType mode) {
switch (mode) {
case BootModeType::Normal:
return NcmContentMetaType_BootImagePackage;
@ -139,19 +139,19 @@ namespace sts::updater {
Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
/* Ensure we can read content metas. */
constexpr size_t MaxContentMetas = 0x40;
STS_ASSERT(work_buffer_size >= sizeof(NcmMetaRecord) * MaxContentMetas);
STS_ASSERT(work_buffer_size >= sizeof(NcmContentMetaKey) * MaxContentMetas);
/* Open NAND System meta database, list contents. */
NcmContentMetaDatabase meta_db;
R_TRY(ncmOpenContentMetaDatabase(FsStorageId_NandSystem, &meta_db));
R_TRY(ncmOpenContentMetaDatabase(&meta_db, FsStorageId_NandSystem));
ON_SCOPE_EXIT { serviceClose(&meta_db.s); };
NcmMetaRecord *records = reinterpret_cast<NcmMetaRecord *>(work_buffer);
NcmContentMetaKey *records = reinterpret_cast<NcmContentMetaKey *>(work_buffer);
const u32 title_type = GetNcmTitleType(mode);
const auto title_type = GetNcmContentMetaType(mode);
u32 written_entries;
u32 total_entries;
R_TRY(ncmContentMetaDatabaseList(&meta_db, title_type, 0, 0, UINT64_MAX, records, MaxContentMetas * sizeof(*records), &written_entries, &total_entries));
R_TRY(ncmContentMetaDatabaseList(&meta_db, &total_entries, &written_entries, records, MaxContentMetas * sizeof(*records), title_type, 0, 0, UINT64_MAX, NcmContentInstallType_Full));
if (total_entries == 0) {
return ResultUpdaterBootImagePackageNotFound;
}
@ -164,15 +164,15 @@ namespace sts::updater {
u8 attr;
R_TRY(ncmContentMetaDatabaseGetAttributes(&meta_db, &records[i], &attr));
if (attr & NcmContentMetaAttribute_Exfat) {
*out_data_id = records[i].titleId;
if (attr & NcmContentMetaAttribute_IncludesExFatDriver) {
*out_data_id = records[i].title_id;
return ResultSuccess;
}
}
}
/* If there's only one entry or no exfat entries, return that entry. */
*out_data_id = records[0].titleId;
*out_data_id = records[0].title_id;
return ResultSuccess;
}

View file

@ -49,7 +49,7 @@ namespace sts::util::ini {
/* Read as many bytes as we can. */
size_t try_read = std::min(size_t(num - 1), ctx->num_left);
size_t actually_read;
R_ASSERT(fsFileRead(ctx->f, ctx->offset, str, try_read, FS_READOPTION_NONE, &actually_read));
R_ASSERT(fsFileRead(ctx->f, ctx->offset, str, try_read, FsReadOption_None, &actually_read));
STS_ASSERT(actually_read == try_read);
/* Only "read" up to the first \n. */