mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
libstrat: update for latest libnx, delete ipc in prep for rewrite
This commit is contained in:
parent
add18d868f
commit
bd341d5c00
32 changed files with 244 additions and 3113 deletions
|
@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules
|
|||
# INCLUDES is a list of directories containing header files
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
SOURCES := source source/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
|
||||
|
||||
|
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
|
@ -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)) { }
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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),
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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));
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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); };
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in a new issue