mirror of
https://github.com/Atmosphere-NX/Atmosphere-libs.git
synced 2025-06-21 11:02:45 +02:00
libstratosphere/fs.mitm: Push WIP support for Domains. Not yet fully working.
This commit is contained in:
parent
5f10855b57
commit
cd5da88405
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "stratosphere/ipc_templating.hpp"
|
||||||
|
|
||||||
#include "stratosphere/iwaitable.hpp"
|
#include "stratosphere/iwaitable.hpp"
|
||||||
#include "stratosphere/iserviceobject.hpp"
|
#include "stratosphere/iserviceobject.hpp"
|
||||||
#include "stratosphere/iserver.hpp"
|
#include "stratosphere/iserver.hpp"
|
||||||
@ -8,12 +10,9 @@
|
|||||||
#include "stratosphere/serviceserver.hpp"
|
#include "stratosphere/serviceserver.hpp"
|
||||||
#include "stratosphere/managedportserver.hpp"
|
#include "stratosphere/managedportserver.hpp"
|
||||||
#include "stratosphere/existingportserver.hpp"
|
#include "stratosphere/existingportserver.hpp"
|
||||||
#include "stratosphere/childholder.hpp"
|
|
||||||
|
|
||||||
#include "stratosphere/ievent.hpp"
|
#include "stratosphere/ievent.hpp"
|
||||||
#include "stratosphere/systemevent.hpp"
|
#include "stratosphere/systemevent.hpp"
|
||||||
#include "stratosphere/hossynch.hpp"
|
#include "stratosphere/hossynch.hpp"
|
||||||
|
|
||||||
#include "stratosphere/waitablemanager.hpp"
|
#include "stratosphere/waitablemanager.hpp"
|
||||||
|
|
||||||
#include "stratosphere/ipc_templating.hpp"
|
|
@ -1,76 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "iwaitable.hpp"
|
|
||||||
|
|
||||||
class ChildWaitableHolder : public IWaitable {
|
|
||||||
protected:
|
|
||||||
std::vector<IWaitable *> children;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/* Implicit constructor. */
|
|
||||||
|
|
||||||
void add_child(IWaitable *child) {
|
|
||||||
this->children.push_back(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~ChildWaitableHolder() {
|
|
||||||
for (unsigned int i = 0; i < this->children.size(); i++) {
|
|
||||||
delete this->children[i];
|
|
||||||
}
|
|
||||||
this->children.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
virtual unsigned int get_num_waitables() {
|
|
||||||
unsigned int n = 0;
|
|
||||||
for (unsigned int i = 0; i < this->children.size(); i++) {
|
|
||||||
if (this->children[i]) {
|
|
||||||
n += this->children[i]->get_num_waitables();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void get_waitables(IWaitable **dst) {
|
|
||||||
unsigned int n = 0;
|
|
||||||
for (unsigned int i = 0; i < this->children.size(); i++) {
|
|
||||||
if (this->children[i]) {
|
|
||||||
this->children[i]->get_waitables(&dst[n]);
|
|
||||||
n += this->children[i]->get_num_waitables();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void delete_child(IWaitable *child) {
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i < this->children.size(); i++) {
|
|
||||||
if (this->children[i] == child) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == this->children.size()) {
|
|
||||||
/* TODO: Panic, because this isn't our child. */
|
|
||||||
} else {
|
|
||||||
delete this->children[i];
|
|
||||||
this->children.erase(this->children.begin() + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Handle get_handle() {
|
|
||||||
/* We don't have a handle. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
virtual void handle_deferred() {
|
|
||||||
/* TODO: Panic, because we can never defer a server. */
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Result handle_signaled(u64 timeout) {
|
|
||||||
/* TODO: Panic, because we can never be signalled. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
80
include/stratosphere/domainowner.hpp
Normal file
80
include/stratosphere/domainowner.hpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "iserviceobject.hpp"
|
||||||
|
|
||||||
|
#define DOMAIN_ID_MAX 0x200
|
||||||
|
|
||||||
|
class IServiceObject;
|
||||||
|
|
||||||
|
class DomainOwner {
|
||||||
|
private:
|
||||||
|
IServiceObject *domain_objects[DOMAIN_ID_MAX];
|
||||||
|
public:
|
||||||
|
DomainOwner() {
|
||||||
|
for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) {
|
||||||
|
domain_objects[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~DomainOwner() {
|
||||||
|
for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) {
|
||||||
|
this->delete_object(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IServiceObject *get_domain_object(unsigned int i) {
|
||||||
|
if (i < DOMAIN_ID_MAX) {
|
||||||
|
return domain_objects[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result reserve_object(IServiceObject *object, unsigned int *out_i) {
|
||||||
|
for (unsigned int i = 4; i < DOMAIN_ID_MAX; i++) {
|
||||||
|
if (domain_objects[i] == NULL) {
|
||||||
|
domain_objects[i] = object;
|
||||||
|
object->set_owner(this);
|
||||||
|
*out_i = i;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0x1900B;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result set_object(IServiceObject *object, unsigned int i) {
|
||||||
|
if (domain_objects[i] == NULL) {
|
||||||
|
domain_objects[i] = object;
|
||||||
|
object->set_owner(this);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0x1900B;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int get_object_id(IServiceObject *object) {
|
||||||
|
for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) {
|
||||||
|
if (domain_objects[i] == object) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DOMAIN_ID_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_object(unsigned int i) {
|
||||||
|
if (domain_objects[i]) {
|
||||||
|
delete domain_objects[i];
|
||||||
|
domain_objects[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void delete_object(IServiceObject *object) {
|
||||||
|
for (unsigned int i = 0; i < DOMAIN_ID_MAX; i++) {
|
||||||
|
if (domain_objects[i] == object) {
|
||||||
|
delete domain_objects[i];
|
||||||
|
domain_objects[i] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -5,7 +5,11 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class ExistingPortServer : public IServer<T> {
|
class ExistingPortServer : public IServer<T> {
|
||||||
public:
|
public:
|
||||||
ExistingPortServer(Handle port_h, unsigned int max_s) : IServer<T>(NULL, max_s) {
|
ExistingPortServer(Handle port_h, unsigned int max_s, bool s_d = false) : IServer<T>(NULL, max_s, s_d) {
|
||||||
this->port_handle = port_h;
|
this->port_handle = port_h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISession<T> *get_new_session(Handle session_h) override {
|
||||||
|
return new ServiceSession<T>(this, session_h, 0);
|
||||||
|
}
|
||||||
};
|
};
|
@ -27,24 +27,7 @@ class IEvent : public IWaitable {
|
|||||||
|
|
||||||
virtual Result signal_event() = 0;
|
virtual Result signal_event() = 0;
|
||||||
|
|
||||||
/* IWaitable */
|
/* IWaitable */
|
||||||
virtual unsigned int get_num_waitables() {
|
|
||||||
if (handles.size() > 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void get_waitables(IWaitable **dst) {
|
|
||||||
if (handles.size() > 0) {
|
|
||||||
dst[0] = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void delete_child(IWaitable *child) {
|
|
||||||
/* TODO: Panic, an event can never be a parent. */
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Handle get_handle() {
|
virtual Handle get_handle() {
|
||||||
if (handles.size() > 0) {
|
if (handles.size() > 0) {
|
||||||
return this->handles[0];
|
return this->handles[0];
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include "../boost/callable_traits.hpp"
|
#include "../boost/callable_traits.hpp"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "domainowner.hpp"
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||||
|
|
||||||
@ -88,6 +90,21 @@ struct CopiedHandle {
|
|||||||
CopiedHandle(Handle h) : handle(h) { }
|
CopiedHandle(Handle h) : handle(h) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Forward declarations. */
|
||||||
|
template <typename T>
|
||||||
|
class ISession;
|
||||||
|
|
||||||
|
/* Represents an output ServiceObject. */
|
||||||
|
struct OutSessionBase {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct OutSession : OutSessionBase {
|
||||||
|
ISession<T> *session;
|
||||||
|
u32 domain_id;
|
||||||
|
|
||||||
|
OutSession(ISession<T> *s) : session(s), domain_id(DOMAIN_ID_MAX) { }
|
||||||
|
};
|
||||||
|
|
||||||
/* Utilities. */
|
/* Utilities. */
|
||||||
template <typename T, template <typename...> class Template>
|
template <typename T, template <typename...> class Template>
|
||||||
struct is_specialization_of {
|
struct is_specialization_of {
|
||||||
@ -117,9 +134,14 @@ struct is_ipc_handle {
|
|||||||
static const size_t value = (std::is_same<T, MovedHandle>::value || std::is_same<T, CopiedHandle>::value) ? 1 : 0;
|
static const size_t value = (std::is_same<T, MovedHandle>::value || std::is_same<T, CopiedHandle>::value) ? 1 : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_out_session {
|
||||||
|
static const bool value = std::is_base_of<OutSessionBase, T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct size_in_raw_data {
|
struct size_in_raw_data {
|
||||||
static const size_t value = (is_ipc_buffer<T>::value || is_ipc_handle<T>::value) ? 0 : ((sizeof(T) < sizeof(u32)) ? sizeof(u32) : (sizeof(T) + 3) & (~3));
|
static const size_t value = (is_ipc_buffer<T>::value || is_ipc_handle<T>::value || is_out_session<T>::value) ? 0 : ((sizeof(T) < sizeof(u32)) ? sizeof(u32) : (sizeof(T) + 3) & (~3));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ...Args>
|
template <typename ...Args>
|
||||||
@ -127,6 +149,11 @@ struct size_in_raw_data_for_arguments {
|
|||||||
static const size_t value = (size_in_raw_data<Args>::value + ... + 0);
|
static const size_t value = (size_in_raw_data<Args>::value + ... + 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
struct num_out_sessions_in_arguments {
|
||||||
|
static const size_t value = ((is_out_session<Args>::value ? 1 : 0) + ... + 0);
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct size_in_raw_data_with_out_pointers {
|
struct size_in_raw_data_with_out_pointers {
|
||||||
static const size_t value = is_specialization_of<T, OutPointerWithClientSize>::value ? 2 : size_in_raw_data<T>::value;
|
static const size_t value = is_specialization_of<T, OutPointerWithClientSize>::value ? 2 : size_in_raw_data<T>::value;
|
||||||
@ -293,6 +320,10 @@ struct Validator<std::tuple<Args...>> {
|
|||||||
return 0xF601;
|
return 0xF601;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (((u32 *)r.Raw)[0] != SFCI_MAGIC) {
|
||||||
|
//return 0xF601;
|
||||||
|
}
|
||||||
|
|
||||||
size_t a_index = 0, b_index = num_inbuffers_in_arguments<Args ...>::value, x_index = 0, c_index = 0, h_index = 0;
|
size_t a_index = 0, b_index = num_inbuffers_in_arguments<Args ...>::value, x_index = 0, c_index = 0, h_index = 0;
|
||||||
size_t cur_rawdata_index = 4;
|
size_t cur_rawdata_index = 4;
|
||||||
size_t cur_c_size_offset = 0x10 + size_in_raw_data_for_arguments<Args... >::value + (0x10 - ((uintptr_t)r.Raw - (uintptr_t)r.RawWithoutPadding));
|
size_t cur_c_size_offset = 0x10 + size_in_raw_data_for_arguments<Args... >::value + (0x10 - ((uintptr_t)r.Raw - (uintptr_t)r.RawWithoutPadding));
|
||||||
@ -334,33 +365,55 @@ template<typename ArgsTuple>
|
|||||||
struct Encoder;
|
struct Encoder;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
constexpr size_t GetAndUpdateOffsetIntoRawData(size_t& offset) {
|
constexpr size_t GetAndUpdateOffsetIntoRawData(DomainOwner *domain_owner, size_t& offset) {
|
||||||
auto old = offset;
|
auto old = offset;
|
||||||
|
|
||||||
if (old == 0) {
|
if (old == 0) {
|
||||||
offset += sizeof(u64);
|
offset += sizeof(u64);
|
||||||
} else {
|
} else {
|
||||||
offset += size_in_raw_data<T>::value;
|
if constexpr (is_out_session<T>::value) {
|
||||||
|
if (domain_owner) {
|
||||||
|
offset += sizeof(u32);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offset += size_in_raw_data<T>::value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void EncodeValueIntoIpcMessageBeforePrepare(IpcCommand *c, T value) {
|
void EncodeValueIntoIpcMessageBeforePrepare(DomainOwner *domain_owner, IpcCommand *c, T &value) {
|
||||||
if constexpr (std::is_same<T, MovedHandle>::value) {
|
if constexpr (std::is_same<T, MovedHandle>::value) {
|
||||||
ipcSendHandleMove(c, value.handle);
|
ipcSendHandleMove(c, value.handle);
|
||||||
} else if constexpr (std::is_same<T, CopiedHandle>::value) {
|
} else if constexpr (std::is_same<T, CopiedHandle>::value) {
|
||||||
ipcSendHandleCopy(c, value.handle);
|
ipcSendHandleCopy(c, value.handle);
|
||||||
} else if constexpr (std::is_same<T, PidDescriptor>::value) {
|
} else if constexpr (std::is_same<T, PidDescriptor>::value) {
|
||||||
ipcSendPid(c);
|
ipcSendPid(c);
|
||||||
|
} else if constexpr (is_out_session<T>::value) {
|
||||||
|
if (domain_owner && value.session) {
|
||||||
|
/* TODO: Check error... */
|
||||||
|
if (value.domain_id != DOMAIN_ID_MAX) {
|
||||||
|
domain_owner->set_object(value.session->get_service_object(), value.domain_id);
|
||||||
|
} else {
|
||||||
|
domain_owner->reserve_object(value.session->get_service_object(), &value.domain_id);
|
||||||
|
}
|
||||||
|
value.session->close_handles();
|
||||||
|
} else {
|
||||||
|
ipcSendHandleMove(c, value.session ? value.session->get_client_handle() : 0x0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void EncodeValueIntoIpcMessageAfterPrepare(u8 *cur_out, T value) {
|
void EncodeValueIntoIpcMessageAfterPrepare(DomainOwner *domain_owner, u8 *cur_out, T value) {
|
||||||
if constexpr (is_ipc_handle<T>::value || std::is_same<T, PidDescriptor>::value) {
|
if constexpr (is_ipc_handle<T>::value || std::is_same<T, PidDescriptor>::value) {
|
||||||
/* Do nothing. */
|
/* Do nothing. */
|
||||||
|
} else if constexpr (is_out_session<T>::value) {
|
||||||
|
if (domain_owner) {
|
||||||
|
*((u32 *)cur_out) = value.domain_id;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*((T *)(cur_out)) = value;
|
*((T *)(cur_out)) = value;
|
||||||
}
|
}
|
||||||
@ -370,7 +423,7 @@ template<typename... Args>
|
|||||||
struct Encoder<std::tuple<Args...>> {
|
struct Encoder<std::tuple<Args...>> {
|
||||||
IpcCommand &out_command;
|
IpcCommand &out_command;
|
||||||
|
|
||||||
auto operator()(Args... args) {
|
auto operator()(DomainOwner *domain_owner, Args... args) {
|
||||||
static_assert(sizeof...(Args) > 0, "IpcCommandImpls must return std::tuple<Result, ...>");
|
static_assert(sizeof...(Args) > 0, "IpcCommandImpls must return std::tuple<Result, ...>");
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
|
||||||
@ -378,19 +431,26 @@ struct Encoder<std::tuple<Args...>> {
|
|||||||
|
|
||||||
std::fill(tls, tls + 0x100, 0x00);
|
std::fill(tls, tls + 0x100, 0x00);
|
||||||
|
|
||||||
((EncodeValueIntoIpcMessageBeforePrepare<Args>(&out_command, args)), ...);
|
((EncodeValueIntoIpcMessageBeforePrepare<Args>(domain_owner, &out_command, args)), ...);
|
||||||
|
|
||||||
/* Remove the extra space resulting from first Result type. */
|
/* Remove the extra space resulting from first Result type. */
|
||||||
struct {
|
struct {
|
||||||
u64 magic;
|
u64 magic;
|
||||||
u64 result;
|
u64 result;
|
||||||
} *raw = (decltype(raw))ipcPrepareHeader(&out_command, sizeof(*raw) + size_in_raw_data_for_arguments<Args... >::value - sizeof(Result));
|
} *raw;
|
||||||
|
if (domain_owner == NULL) {
|
||||||
|
raw = (decltype(raw))ipcPrepareHeader(&out_command, sizeof(*raw) + size_in_raw_data_for_arguments<Args... >::value - sizeof(Result));
|
||||||
|
} else {
|
||||||
|
raw = (decltype(raw))ipcPrepareHeaderForDomain(&out_command, sizeof(*raw) + size_in_raw_data_for_arguments<Args... >::value + (num_out_sessions_in_arguments<Args... >::value * sizeof(u32)) - sizeof(Result), 0);
|
||||||
|
*((DomainMessageHeader *)((uintptr_t)raw - sizeof(DomainMessageHeader))) = {0};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
raw->magic = SFCO_MAGIC;
|
raw->magic = SFCO_MAGIC;
|
||||||
|
|
||||||
u8 *raw_data = (u8 *)&raw->result;
|
u8 *raw_data = (u8 *)&raw->result;
|
||||||
|
|
||||||
((EncodeValueIntoIpcMessageAfterPrepare<Args>(raw_data + GetAndUpdateOffsetIntoRawData<Args>(offset), args)), ...);
|
((EncodeValueIntoIpcMessageAfterPrepare<Args>(domain_owner, raw_data + GetAndUpdateOffsetIntoRawData<Args>(domain_owner, offset), args)), ...);
|
||||||
|
|
||||||
Result rc = raw->result;
|
Result rc = raw->result;
|
||||||
|
|
||||||
@ -424,7 +484,9 @@ Result WrapDeferredIpcCommandImpl(Class *this_ptr, Args... args) {
|
|||||||
auto tuple_args = std::make_tuple(args...);
|
auto tuple_args = std::make_tuple(args...);
|
||||||
auto result = std::apply( [=](auto&&... a) { return (this_ptr->*IpcCommandImpl)(a...); }, tuple_args);
|
auto result = std::apply( [=](auto&&... a) { return (this_ptr->*IpcCommandImpl)(a...); }, tuple_args);
|
||||||
|
|
||||||
return std::apply(Encoder<OutArgs>{out_command}, result);
|
DomainOwner *down = NULL;
|
||||||
|
|
||||||
|
return std::apply(Encoder<OutArgs>{out_command}, std::tuple_cat(std::make_tuple(down), result));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto IpcCommandImpl, typename Class>
|
template<auto IpcCommandImpl, typename Class>
|
||||||
@ -446,8 +508,12 @@ Result WrapIpcCommandImpl(Class *this_ptr, IpcParsedCommand& r, IpcCommand &out_
|
|||||||
|
|
||||||
auto args = Decoder<OutArgs, InArgsWithoutThis>::Decode(r, out_command, pointer_buffer);
|
auto args = Decoder<OutArgs, InArgsWithoutThis>::Decode(r, out_command, pointer_buffer);
|
||||||
auto result = std::apply( [=](auto&&... args) { return (this_ptr->*IpcCommandImpl)(args...); }, args);
|
auto result = std::apply( [=](auto&&... args) { return (this_ptr->*IpcCommandImpl)(args...); }, args);
|
||||||
|
DomainOwner *down = NULL;
|
||||||
|
if (r.IsDomainMessage) {
|
||||||
|
down = this_ptr->get_owner();
|
||||||
|
}
|
||||||
|
|
||||||
return std::apply(Encoder<OutArgs>{out_command}, result);
|
return std::apply(Encoder<OutArgs>{out_command}, std::tuple_cat(std::make_tuple(down), result));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<auto IpcCommandImpl>
|
template<auto IpcCommandImpl>
|
||||||
@ -468,8 +534,11 @@ Result WrapStaticIpcCommandImpl(IpcParsedCommand& r, IpcCommand &out_command, u8
|
|||||||
|
|
||||||
auto args = Decoder<OutArgs, InArgs>::Decode(r, out_command, pointer_buffer);
|
auto args = Decoder<OutArgs, InArgs>::Decode(r, out_command, pointer_buffer);
|
||||||
auto result = std::apply(IpcCommandImpl, args);
|
auto result = std::apply(IpcCommandImpl, args);
|
||||||
|
DomainOwner *down = NULL;
|
||||||
|
|
||||||
return std::apply(Encoder<OutArgs>{out_command}, result);
|
return std::apply(Encoder<OutArgs>{out_command}, std::tuple_cat(std::make_tuple(down), result));
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#include "isession.hpp"
|
||||||
|
@ -5,200 +5,31 @@
|
|||||||
#include "ipc_templating.hpp"
|
#include "ipc_templating.hpp"
|
||||||
#include "iserviceobject.hpp"
|
#include "iserviceobject.hpp"
|
||||||
#include "iwaitable.hpp"
|
#include "iwaitable.hpp"
|
||||||
#include "servicesession.hpp"
|
#include "isession.hpp"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class IPCSession final : public IWaitable {
|
class IPCSession final : public ISession<T> {
|
||||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||||
|
|
||||||
T *service_object;
|
|
||||||
Handle server_handle;
|
|
||||||
Handle client_handle;
|
|
||||||
char *pointer_buffer;
|
|
||||||
size_t pointer_buffer_size;
|
|
||||||
|
|
||||||
static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!");
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IPCSession<T>(size_t pbs = 0x400) : pointer_buffer_size(pbs) {
|
IPCSession<T>(size_t pbs = 0x400) : ISession<T>(NULL, 0, 0, 0) {
|
||||||
Result rc;
|
Result rc;
|
||||||
if (R_FAILED((rc = svcCreateSession(&server_handle, &client_handle, 0, 0)))) {
|
if (R_FAILED((rc = svcCreateSession(&this->server_handle, &this->client_handle, 0, 0)))) {
|
||||||
fatalSimple(rc);
|
fatalSimple(rc);
|
||||||
}
|
}
|
||||||
this->service_object = new T();
|
this->service_object = new T();
|
||||||
this->pointer_buffer = new char[pointer_buffer_size];
|
this->pointer_buffer_size = pbs;
|
||||||
|
this->pointer_buffer = new char[this->pointer_buffer_size];
|
||||||
|
this->is_domain = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCSession<T>(T *so, size_t pbs = 0x400) : service_object(so), pointer_buffer_size(pbs) {
|
IPCSession<T>(T *so, size_t pbs = 0x400) : ISession<T>(NULL, 0, 0, so, 0) {
|
||||||
Result rc;
|
Result rc;
|
||||||
if (R_FAILED((rc = svcCreateSession(&server_handle, &client_handle, 0, 0)))) {
|
if (R_FAILED((rc = svcCreateSession(&this->server_handle, &this->client_handle, 0, 0)))) {
|
||||||
fatalSimple(rc);
|
fatalSimple(rc);
|
||||||
}
|
}
|
||||||
this->pointer_buffer = new char[pointer_buffer_size];
|
this->pointer_buffer_size = pbs;
|
||||||
}
|
this->pointer_buffer = new char[this->pointer_buffer_size];
|
||||||
|
this->is_domain = false;
|
||||||
~IPCSession() override {
|
|
||||||
delete this->service_object;
|
|
||||||
delete this->pointer_buffer;
|
|
||||||
if (server_handle) {
|
|
||||||
svcCloseHandle(server_handle);
|
|
||||||
}
|
|
||||||
if (client_handle) {
|
|
||||||
svcCloseHandle(client_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T *get_service_object() { return this->service_object; }
|
|
||||||
Handle get_server_handle() { return this->server_handle; }
|
|
||||||
Handle get_client_handle() { return this->client_handle; }
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
unsigned int get_num_waitables() override {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_waitables(IWaitable **dst) override {
|
|
||||||
dst[0] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_child(IWaitable *child) override {
|
|
||||||
/* TODO: Panic, because we can never have any children. */
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle get_handle() override {
|
|
||||||
return this->server_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_deferred() override {
|
|
||||||
Result rc = this->service_object->handle_deferred();
|
|
||||||
int handle_index;
|
|
||||||
|
|
||||||
if (rc != RESULT_DEFER_SESSION) {
|
|
||||||
this->set_deferred(false);
|
|
||||||
if (rc == 0xF601) {
|
|
||||||
svcCloseHandle(this->get_handle());
|
|
||||||
} else {
|
|
||||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result handle_signaled(u64 timeout) override {
|
|
||||||
Result rc;
|
|
||||||
int handle_index;
|
|
||||||
|
|
||||||
/* Prepare pointer buffer... */
|
|
||||||
IpcCommand c_for_reply;
|
|
||||||
ipcInitialize(&c_for_reply);
|
|
||||||
ipcAddRecvStatic(&c_for_reply, this->pointer_buffer, this->pointer_buffer_size, 0);
|
|
||||||
ipcPrepareHeader(&c_for_reply, 0);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, timeout))) {
|
|
||||||
if (handle_index != 0) {
|
|
||||||
/* TODO: Panic? */
|
|
||||||
}
|
|
||||||
u32 *cmdbuf = (u32 *)armGetTls();
|
|
||||||
Result retval = 0;
|
|
||||||
u32 *rawdata_start = cmdbuf;
|
|
||||||
|
|
||||||
IpcParsedCommand r;
|
|
||||||
IpcCommand c;
|
|
||||||
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
retval = ipcParse(&r);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(retval)) {
|
|
||||||
rawdata_start = (u32 *)r.Raw;
|
|
||||||
switch (r.CommandType) {
|
|
||||||
case IpcCommandType_Close:
|
|
||||||
/* TODO: This should close the session and clean up its resources. */
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_LegacyControl:
|
|
||||||
/* TODO: What does this allow one to do? */
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_LegacyRequest:
|
|
||||||
/* TODO: What does this allow one to do? */
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Request:
|
|
||||||
case IpcCommandType_RequestWithContext:
|
|
||||||
retval = this->service_object->dispatch(r, c, rawdata_start[2], (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Control:
|
|
||||||
case IpcCommandType_ControlWithContext:
|
|
||||||
retval = this->dispatch_control_command(r, c, rawdata_start[2]);
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Invalid:
|
|
||||||
default:
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retval == RESULT_DEFER_SESSION) {
|
|
||||||
/* Session defer. */
|
|
||||||
this->set_deferred(true);
|
|
||||||
rc = retval;
|
|
||||||
} else if (retval == 0xF601) {
|
|
||||||
/* Session close. */
|
|
||||||
rc = retval;
|
|
||||||
} else {
|
|
||||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Control commands. */
|
|
||||||
std::tuple<Result> ConvertCurrentObjectToDomain() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
std::tuple<Result> CopyFromCurrentDomain() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
std::tuple<Result> CloneCurrentObject() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
std::tuple<Result, u32> QueryPointerBufferSize() {
|
|
||||||
return {0x0, (u32)this->pointer_buffer_size};
|
|
||||||
}
|
|
||||||
std::tuple<Result> CloneCurrentObjectEx() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result dispatch_control_command(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id) {
|
|
||||||
Result rc = 0xF601;
|
|
||||||
|
|
||||||
/* TODO: Implement. */
|
|
||||||
switch ((IpcControlCommand)cmd_id) {
|
|
||||||
case IpcCtrl_Cmd_ConvertCurrentObjectToDomain:
|
|
||||||
rc = WrapIpcCommandImpl<&IPCSession::ConvertCurrentObjectToDomain>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_CopyFromCurrentDomain:
|
|
||||||
rc = WrapIpcCommandImpl<&IPCSession::CopyFromCurrentDomain>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_CloneCurrentObject:
|
|
||||||
rc = WrapIpcCommandImpl<&IPCSession::CloneCurrentObject>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_QueryPointerBufferSize:
|
|
||||||
rc = WrapIpcCommandImpl<&IPCSession::QueryPointerBufferSize>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_CloneCurrentObjectEx:
|
|
||||||
rc = WrapIpcCommandImpl<&IPCSession::CloneCurrentObjectEx>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
#include "iserviceobject.hpp"
|
#include "iserviceobject.hpp"
|
||||||
#include "iwaitable.hpp"
|
#include "iwaitable.hpp"
|
||||||
#include "servicesession.hpp"
|
#include "isession.hpp"
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ServiceSession;
|
class ISession;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class IServer : public IWaitable {
|
class IServer : public IWaitable {
|
||||||
@ -15,71 +15,22 @@ class IServer : public IWaitable {
|
|||||||
protected:
|
protected:
|
||||||
Handle port_handle;
|
Handle port_handle;
|
||||||
unsigned int max_sessions;
|
unsigned int max_sessions;
|
||||||
unsigned int num_sessions;
|
bool supports_domains;
|
||||||
ServiceSession<T> **sessions;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IServer(const char *service_name, unsigned int max_s) : max_sessions(max_s) {
|
IServer(const char *service_name, unsigned int max_s, bool s_d = false) : max_sessions(max_s), supports_domains(s_d) {
|
||||||
this->sessions = new ServiceSession<T> *[this->max_sessions];
|
|
||||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
|
||||||
this->sessions[i] = NULL;
|
|
||||||
}
|
|
||||||
this->num_sessions = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~IServer() {
|
virtual ~IServer() {
|
||||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
|
||||||
if (this->sessions[i]) {
|
|
||||||
delete this->sessions[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this->sessions;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port_handle) {
|
if (port_handle) {
|
||||||
svcCloseHandle(port_handle);
|
svcCloseHandle(port_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ISession<T> *get_new_session(Handle session_h) = 0;
|
||||||
|
|
||||||
/* IWaitable */
|
/* IWaitable */
|
||||||
virtual unsigned int get_num_waitables() {
|
|
||||||
unsigned int n = 1;
|
|
||||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
|
||||||
if (this->sessions[i]) {
|
|
||||||
n += this->sessions[i]->get_num_waitables();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void get_waitables(IWaitable **dst) {
|
|
||||||
dst[0] = this;
|
|
||||||
unsigned int n = 0;
|
|
||||||
for (unsigned int i = 0; i < this->max_sessions; i++) {
|
|
||||||
if (this->sessions[i]) {
|
|
||||||
this->sessions[i]->get_waitables(&dst[1 + n]);
|
|
||||||
n += this->sessions[i]->get_num_waitables();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void delete_child(IWaitable *child) {
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i < this->max_sessions; i++) {
|
|
||||||
if (this->sessions[i] == child) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == this->max_sessions) {
|
|
||||||
/* TODO: Panic, because this isn't our child. */
|
|
||||||
} else {
|
|
||||||
delete this->sessions[i];
|
|
||||||
this->sessions[i] = NULL;
|
|
||||||
this->num_sessions--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Handle get_handle() {
|
virtual Handle get_handle() {
|
||||||
return this->port_handle;
|
return this->port_handle;
|
||||||
}
|
}
|
||||||
@ -92,23 +43,12 @@ class IServer : public IWaitable {
|
|||||||
virtual Result handle_signaled(u64 timeout) {
|
virtual Result handle_signaled(u64 timeout) {
|
||||||
/* If this server's port was signaled, accept a new session. */
|
/* If this server's port was signaled, accept a new session. */
|
||||||
Handle session_h;
|
Handle session_h;
|
||||||
svcAcceptSession(&session_h, this->port_handle);
|
Result rc = svcAcceptSession(&session_h, this->port_handle);
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->num_sessions >= this->max_sessions) {
|
this->get_manager()->add_waitable(this->get_new_session(session_h));
|
||||||
svcCloseHandle(session_h);
|
|
||||||
return 0x10601;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i < this->max_sessions; i++) {
|
|
||||||
if (this->sessions[i] == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->sessions[i] = new ServiceSession<T>(this, session_h, 0);
|
|
||||||
this->sessions[i]->set_parent(this);
|
|
||||||
this->num_sessions++;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -1,11 +1,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
#include "ipc_templating.hpp"
|
template <typename T>
|
||||||
|
class ISession;
|
||||||
|
|
||||||
|
class DomainOwner;
|
||||||
|
|
||||||
class IServiceObject {
|
class IServiceObject {
|
||||||
protected:
|
private:
|
||||||
|
DomainOwner *owner = NULL;
|
||||||
|
public:
|
||||||
virtual ~IServiceObject() { }
|
virtual ~IServiceObject() { }
|
||||||
|
|
||||||
|
virtual IServiceObject *clone() = 0;
|
||||||
|
|
||||||
|
bool is_domain() { return this->owner != NULL; }
|
||||||
|
DomainOwner *get_owner() { return this->owner; }
|
||||||
|
void set_owner(DomainOwner *owner) { this->owner = owner; }
|
||||||
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0;
|
virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0;
|
||||||
|
protected:
|
||||||
virtual Result handle_deferred() = 0;
|
virtual Result handle_deferred() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "domainowner.hpp"
|
||||||
|
288
include/stratosphere/isession.hpp
Normal file
288
include/stratosphere/isession.hpp
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "ipc_templating.hpp"
|
||||||
|
#include "iserviceobject.hpp"
|
||||||
|
#include "iwaitable.hpp"
|
||||||
|
#include "iserver.hpp"
|
||||||
|
|
||||||
|
#include "domainowner.hpp"
|
||||||
|
|
||||||
|
enum IpcControlCommand {
|
||||||
|
IpcCtrl_Cmd_ConvertCurrentObjectToDomain = 0,
|
||||||
|
IpcCtrl_Cmd_CopyFromCurrentDomain = 1,
|
||||||
|
IpcCtrl_Cmd_CloneCurrentObject = 2,
|
||||||
|
IpcCtrl_Cmd_QueryPointerBufferSize = 3,
|
||||||
|
IpcCtrl_Cmd_CloneCurrentObjectEx = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
#define POINTER_BUFFER_SIZE_MAX 0xFFFF
|
||||||
|
#define RESULT_DEFER_SESSION (0x6580A)
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class IServer;
|
||||||
|
|
||||||
|
class IServiceObject;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class ISession : public IWaitable, public DomainOwner {
|
||||||
|
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||||
|
protected:
|
||||||
|
T *service_object;
|
||||||
|
IServer<T> *server;
|
||||||
|
Handle server_handle;
|
||||||
|
Handle client_handle;
|
||||||
|
char *pointer_buffer;
|
||||||
|
size_t pointer_buffer_size;
|
||||||
|
|
||||||
|
bool is_domain;
|
||||||
|
|
||||||
|
|
||||||
|
IServiceObject *active_object;
|
||||||
|
|
||||||
|
static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!");
|
||||||
|
|
||||||
|
public:
|
||||||
|
ISession<T>(IServer<T> *s, Handle s_h, Handle c_h, size_t pbs = 0x400) : server(s), server_handle(s_h), client_handle(c_h), pointer_buffer_size(pbs) {
|
||||||
|
this->service_object = new T();
|
||||||
|
if (this->pointer_buffer_size) {
|
||||||
|
this->pointer_buffer = new char[this->pointer_buffer_size];
|
||||||
|
}
|
||||||
|
this->is_domain = false;
|
||||||
|
this->active_object = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISession<T>(IServer<T> *s, Handle s_h, Handle c_h, T *so, size_t pbs = 0x400) : service_object(so), server(s), server_handle(s_h), client_handle(c_h), pointer_buffer_size(pbs) {
|
||||||
|
if (this->pointer_buffer_size) {
|
||||||
|
this->pointer_buffer = new char[this->pointer_buffer_size];
|
||||||
|
}
|
||||||
|
this->is_domain = false;
|
||||||
|
this->active_object = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ISession() override {
|
||||||
|
delete this->pointer_buffer;
|
||||||
|
if (this->service_object && !this->is_domain) {
|
||||||
|
//delete this->service_object;
|
||||||
|
}
|
||||||
|
if (server_handle) {
|
||||||
|
svcCloseHandle(server_handle);
|
||||||
|
}
|
||||||
|
if (client_handle) {
|
||||||
|
svcCloseHandle(client_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_handles() {
|
||||||
|
if (server_handle) {
|
||||||
|
svcCloseHandle(server_handle);
|
||||||
|
server_handle = 0;
|
||||||
|
}
|
||||||
|
if (client_handle) {
|
||||||
|
svcCloseHandle(client_handle);
|
||||||
|
client_handle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T *get_service_object() { return this->service_object; }
|
||||||
|
Handle get_server_handle() { return this->server_handle; }
|
||||||
|
Handle get_client_handle() { return this->client_handle; }
|
||||||
|
|
||||||
|
|
||||||
|
DomainOwner *get_owner() { return is_domain ? this : NULL; }
|
||||||
|
|
||||||
|
/* IWaitable */
|
||||||
|
Handle get_handle() override {
|
||||||
|
return this->server_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_deferred() override {
|
||||||
|
Result rc = this->service_object->handle_deferred();
|
||||||
|
int handle_index;
|
||||||
|
|
||||||
|
if (rc != RESULT_DEFER_SESSION) {
|
||||||
|
this->set_deferred(false);
|
||||||
|
if (rc == 0xF601) {
|
||||||
|
svcCloseHandle(this->get_handle());
|
||||||
|
} else {
|
||||||
|
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual Result handle_message(IpcParsedCommand &r) {
|
||||||
|
Result retval = 0xF601;
|
||||||
|
|
||||||
|
IpcCommand c;
|
||||||
|
ipcInitialize(&c);
|
||||||
|
|
||||||
|
if (r.IsDomainMessage && this->active_object == NULL) {
|
||||||
|
return 0xF601;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (r.IsDomainMessage && r.MessageType == DomainMessageType_Close) {
|
||||||
|
this->delete_object(this->active_object);
|
||||||
|
this->active_object = NULL;
|
||||||
|
struct {
|
||||||
|
u64 magic;
|
||||||
|
u64 result;
|
||||||
|
} *raw = (decltype(raw))ipcPrepareHeader(&c, sizeof(*raw));
|
||||||
|
|
||||||
|
raw->magic = SFCO_MAGIC;
|
||||||
|
raw->result = 0x0;
|
||||||
|
return 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 cmd_id = ((u32 *)r.Raw)[2];
|
||||||
|
switch (r.CommandType) {
|
||||||
|
case IpcCommandType_Close:
|
||||||
|
/* TODO: This should close the session and clean up its resources. */
|
||||||
|
retval = 0xF601;
|
||||||
|
break;
|
||||||
|
case IpcCommandType_LegacyControl:
|
||||||
|
/* TODO: What does this allow one to do? */
|
||||||
|
retval = 0xF601;
|
||||||
|
break;
|
||||||
|
case IpcCommandType_LegacyRequest:
|
||||||
|
/* TODO: What does this allow one to do? */
|
||||||
|
retval = 0xF601;
|
||||||
|
break;
|
||||||
|
case IpcCommandType_Request:
|
||||||
|
case IpcCommandType_RequestWithContext:
|
||||||
|
retval = this->active_object->dispatch(r, c, cmd_id, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
||||||
|
break;
|
||||||
|
case IpcCommandType_Control:
|
||||||
|
case IpcCommandType_ControlWithContext:
|
||||||
|
retval = this->dispatch_control_command(r, c, cmd_id);
|
||||||
|
break;
|
||||||
|
case IpcCommandType_Invalid:
|
||||||
|
default:
|
||||||
|
retval = 0xF601;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void postprocess(IpcParsedCommand &r, u64 cmd_id) {
|
||||||
|
/* ... */
|
||||||
|
(void)(r);
|
||||||
|
(void)(cmd_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void cleanup() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
Result handle_signaled(u64 timeout) override {
|
||||||
|
Result rc;
|
||||||
|
int handle_index;
|
||||||
|
|
||||||
|
/* Prepare pointer buffer... */
|
||||||
|
IpcCommand c_for_reply;
|
||||||
|
ipcInitialize(&c_for_reply);
|
||||||
|
ipcAddRecvStatic(&c_for_reply, this->pointer_buffer, this->pointer_buffer_size, 0);
|
||||||
|
ipcPrepareHeader(&c_for_reply, 0);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, timeout))) {
|
||||||
|
if (handle_index != 0) {
|
||||||
|
/* TODO: Panic? */
|
||||||
|
}
|
||||||
|
IpcParsedCommand r;
|
||||||
|
u64 cmd_id;
|
||||||
|
|
||||||
|
|
||||||
|
Result retval = ipcParse(&r);
|
||||||
|
if (R_SUCCEEDED(retval)) {
|
||||||
|
if (this->is_domain && (r.CommandType == IpcCommandType_Request || r.CommandType == IpcCommandType_RequestWithContext)) {
|
||||||
|
retval = ipcParseForDomain(&r);
|
||||||
|
if (!r.IsDomainMessage || r.ThisObjectId >= DOMAIN_ID_MAX) {
|
||||||
|
retval = 0xF601;
|
||||||
|
} else {
|
||||||
|
this->active_object = this->get_domain_object(r.ThisObjectId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->active_object = this->service_object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (R_SUCCEEDED(retval)) {
|
||||||
|
cmd_id = ((u32 *)r.Raw)[2];
|
||||||
|
}
|
||||||
|
if (R_SUCCEEDED(retval)) {
|
||||||
|
retval = this->handle_message(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retval == RESULT_DEFER_SESSION) {
|
||||||
|
/* Session defer. */
|
||||||
|
this->active_object = NULL;
|
||||||
|
this->set_deferred(true);
|
||||||
|
rc = retval;
|
||||||
|
} else if (retval == 0xF601) {
|
||||||
|
/* Session close. */
|
||||||
|
this->active_object = NULL;
|
||||||
|
rc = retval;
|
||||||
|
} else {
|
||||||
|
if (R_SUCCEEDED(retval)) {
|
||||||
|
this->postprocess(r, cmd_id);
|
||||||
|
}
|
||||||
|
this->active_object = NULL;
|
||||||
|
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
||||||
|
this->cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result dispatch_control_command(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id) {
|
||||||
|
Result rc = 0xF601;
|
||||||
|
|
||||||
|
/* TODO: Implement. */
|
||||||
|
switch ((IpcControlCommand)cmd_id) {
|
||||||
|
case IpcCtrl_Cmd_ConvertCurrentObjectToDomain:
|
||||||
|
rc = WrapIpcCommandImpl<&ISession::ConvertCurrentObjectToDomain>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
||||||
|
break;
|
||||||
|
case IpcCtrl_Cmd_CopyFromCurrentDomain:
|
||||||
|
rc = WrapIpcCommandImpl<&ISession::CopyFromCurrentDomain>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
||||||
|
break;
|
||||||
|
case IpcCtrl_Cmd_CloneCurrentObject:
|
||||||
|
rc = WrapIpcCommandImpl<&ISession::CloneCurrentObject>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
||||||
|
break;
|
||||||
|
case IpcCtrl_Cmd_QueryPointerBufferSize:
|
||||||
|
rc = WrapIpcCommandImpl<&ISession::QueryPointerBufferSize>(this, r, out_c, (u8 *)this->pointer_buffer, this->pointer_buffer_size);
|
||||||
|
break;
|
||||||
|
case IpcCtrl_Cmd_CloneCurrentObjectEx:
|
||||||
|
rc = WrapIpcCommandImpl<&ISession::CloneCurrentObjectEx>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Control commands. */
|
||||||
|
std::tuple<Result> ConvertCurrentObjectToDomain() {
|
||||||
|
/* TODO */
|
||||||
|
return {0xF601};
|
||||||
|
}
|
||||||
|
std::tuple<Result> CopyFromCurrentDomain() {
|
||||||
|
/* TODO */
|
||||||
|
return {0xF601};
|
||||||
|
}
|
||||||
|
std::tuple<Result> CloneCurrentObject() {
|
||||||
|
/* TODO */
|
||||||
|
return {0xF601};
|
||||||
|
}
|
||||||
|
std::tuple<Result, u32> QueryPointerBufferSize() {
|
||||||
|
return {0x0, (u32)this->pointer_buffer_size};
|
||||||
|
}
|
||||||
|
std::tuple<Result> CloneCurrentObjectEx() {
|
||||||
|
/* TODO */
|
||||||
|
return {0xF601};
|
||||||
|
}
|
||||||
|
};
|
@ -2,38 +2,34 @@
|
|||||||
|
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include "waitablemanagerbase.hpp"
|
||||||
|
|
||||||
|
class WaitableManager;
|
||||||
|
|
||||||
class IWaitable {
|
class IWaitable {
|
||||||
u64 wait_priority = 0;
|
private:
|
||||||
bool is_deferred = false;
|
u64 wait_priority = 0;
|
||||||
IWaitable *parent_waitable;
|
bool is_deferred = false;
|
||||||
|
WaitableManagerBase *manager;
|
||||||
public:
|
public:
|
||||||
virtual ~IWaitable() { }
|
virtual ~IWaitable() { }
|
||||||
|
|
||||||
virtual unsigned int get_num_waitables() = 0;
|
|
||||||
virtual void get_waitables(IWaitable **dst) = 0;
|
|
||||||
virtual void delete_child(IWaitable *child) = 0;
|
|
||||||
virtual void handle_deferred() = 0;
|
virtual void handle_deferred() = 0;
|
||||||
virtual Handle get_handle() = 0;
|
virtual Handle get_handle() = 0;
|
||||||
virtual Result handle_signaled(u64 timeout) = 0;
|
virtual Result handle_signaled(u64 timeout) = 0;
|
||||||
|
|
||||||
bool has_parent() {
|
WaitableManager *get_manager() {
|
||||||
return this->parent_waitable != NULL;
|
return (WaitableManager *)this->manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_parent(IWaitable *p) {
|
void set_manager(WaitableManagerBase *m) {
|
||||||
if (has_parent()) {
|
this->manager = m;
|
||||||
/* TODO: Panic? */
|
|
||||||
}
|
|
||||||
this->parent_waitable = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
IWaitable *get_parent() {
|
|
||||||
return this->parent_waitable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_priority() {
|
void update_priority() {
|
||||||
static u64 g_cur_priority = 0;
|
if (manager) {
|
||||||
this->wait_priority = g_cur_priority++;
|
this->wait_priority = this->manager->get_priority();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_deferred() {
|
bool get_deferred() {
|
||||||
@ -47,4 +43,6 @@ class IWaitable {
|
|||||||
static bool compare(IWaitable *a, IWaitable *b) {
|
static bool compare(IWaitable *a, IWaitable *b) {
|
||||||
return (a->wait_priority < b->wait_priority) && !a->is_deferred;
|
return (a->wait_priority < b->wait_priority) && !a->is_deferred;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "waitablemanager.hpp"
|
@ -5,9 +5,13 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class ManagedPortServer : public IServer<T> {
|
class ManagedPortServer : public IServer<T> {
|
||||||
public:
|
public:
|
||||||
ManagedPortServer(const char *service_name, unsigned int max_s) : IServer<T>(service_name, max_s) {
|
ManagedPortServer(const char *service_name, unsigned int max_s, bool s_d = false) : IServer<T>(service_name, max_s, s_d) {
|
||||||
if (R_FAILED(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions))) {
|
if (R_FAILED(svcManageNamedPort(&this->port_handle, service_name, this->max_sessions))) {
|
||||||
/* TODO: panic */
|
/* TODO: panic */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISession<T> *get_new_session(Handle session_h) override {
|
||||||
|
return new ServiceSession<T>(this, session_h, 0);
|
||||||
|
}
|
||||||
};
|
};
|
@ -5,9 +5,13 @@
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class ServiceServer : public IServer<T> {
|
class ServiceServer : public IServer<T> {
|
||||||
public:
|
public:
|
||||||
ServiceServer(const char *service_name, unsigned int max_s) : IServer<T>(service_name, max_s) {
|
ServiceServer(const char *service_name, unsigned int max_s, bool s_d = false) : IServer<T>(service_name, max_s, s_d) {
|
||||||
if (R_FAILED(smRegisterService(&this->port_handle, service_name, false, this->max_sessions))) {
|
if (R_FAILED(smRegisterService(&this->port_handle, service_name, false, this->max_sessions))) {
|
||||||
/* TODO: Panic. */
|
/* TODO: Panic. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISession<T> *get_new_session(Handle session_h) override {
|
||||||
|
return new ServiceSession<T>(this, session_h, 0);
|
||||||
|
}
|
||||||
};
|
};
|
@ -6,200 +6,15 @@
|
|||||||
#include "iserviceobject.hpp"
|
#include "iserviceobject.hpp"
|
||||||
#include "iwaitable.hpp"
|
#include "iwaitable.hpp"
|
||||||
#include "iserver.hpp"
|
#include "iserver.hpp"
|
||||||
|
#include "isession.hpp"
|
||||||
enum IpcControlCommand {
|
|
||||||
IpcCtrl_Cmd_ConvertCurrentObjectToDomain = 0,
|
|
||||||
IpcCtrl_Cmd_CopyFromCurrentDomain = 1,
|
|
||||||
IpcCtrl_Cmd_CloneCurrentObject = 2,
|
|
||||||
IpcCtrl_Cmd_QueryPointerBufferSize = 3,
|
|
||||||
IpcCtrl_Cmd_CloneCurrentObjectEx = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
#define POINTER_BUFFER_SIZE_MAX 0xFFFF
|
|
||||||
#define RESULT_DEFER_SESSION (0x6580A)
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class IServer;
|
class ServiceSession final : public ISession<T> {
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class ServiceSession final : public IWaitable {
|
|
||||||
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
static_assert(std::is_base_of<IServiceObject, T>::value, "Service Objects must derive from IServiceObject");
|
||||||
|
|
||||||
T *service_object;
|
|
||||||
IServer<T> *server;
|
|
||||||
Handle server_handle;
|
|
||||||
Handle client_handle;
|
|
||||||
char pointer_buffer[0x400];
|
|
||||||
|
|
||||||
static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!");
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ServiceSession<T>(IServer<T> *s, Handle s_h, Handle c_h) : server(s), server_handle(s_h), client_handle(c_h) {
|
ServiceSession<T>(IServer<T> *s, Handle s_h, Handle c_h, size_t pbs = 0x400) : ISession<T>(s, s_h, c_h, pbs) {
|
||||||
this->service_object = new T();
|
/* ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
~ServiceSession() override {
|
|
||||||
delete this->service_object;
|
|
||||||
if (server_handle) {
|
|
||||||
svcCloseHandle(server_handle);
|
|
||||||
}
|
|
||||||
if (client_handle) {
|
|
||||||
svcCloseHandle(client_handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T *get_service_object() { return this->service_object; }
|
|
||||||
Handle get_server_handle() { return this->server_handle; }
|
|
||||||
Handle get_client_handle() { return this->client_handle; }
|
|
||||||
|
|
||||||
/* IWaitable */
|
|
||||||
unsigned int get_num_waitables() override {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_waitables(IWaitable **dst) override {
|
|
||||||
dst[0] = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_child(IWaitable *child) override {
|
|
||||||
/* TODO: Panic, because we can never have any children. */
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle get_handle() override {
|
|
||||||
return this->server_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_deferred() override {
|
|
||||||
Result rc = this->service_object->handle_deferred();
|
|
||||||
int handle_index;
|
|
||||||
|
|
||||||
if (rc != RESULT_DEFER_SESSION) {
|
|
||||||
this->set_deferred(false);
|
|
||||||
if (rc == 0xF601) {
|
|
||||||
svcCloseHandle(this->get_handle());
|
|
||||||
} else {
|
|
||||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Result handle_signaled(u64 timeout) override {
|
|
||||||
Result rc;
|
|
||||||
int handle_index;
|
|
||||||
|
|
||||||
/* Prepare pointer buffer... */
|
|
||||||
IpcCommand c_for_reply;
|
|
||||||
ipcInitialize(&c_for_reply);
|
|
||||||
ipcAddRecvStatic(&c_for_reply, this->pointer_buffer, sizeof(this->pointer_buffer), 0);
|
|
||||||
ipcPrepareHeader(&c_for_reply, 0);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, 0, timeout))) {
|
|
||||||
if (handle_index != 0) {
|
|
||||||
/* TODO: Panic? */
|
|
||||||
}
|
|
||||||
u32 *cmdbuf = (u32 *)armGetTls();
|
|
||||||
Result retval = 0;
|
|
||||||
u32 *rawdata_start = cmdbuf;
|
|
||||||
|
|
||||||
IpcParsedCommand r;
|
|
||||||
IpcCommand c;
|
|
||||||
|
|
||||||
ipcInitialize(&c);
|
|
||||||
|
|
||||||
retval = ipcParse(&r);
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(retval)) {
|
|
||||||
rawdata_start = (u32 *)r.Raw;
|
|
||||||
switch (r.CommandType) {
|
|
||||||
case IpcCommandType_Close:
|
|
||||||
/* TODO: This should close the session and clean up its resources. */
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_LegacyControl:
|
|
||||||
/* TODO: What does this allow one to do? */
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_LegacyRequest:
|
|
||||||
/* TODO: What does this allow one to do? */
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Request:
|
|
||||||
case IpcCommandType_RequestWithContext:
|
|
||||||
retval = this->service_object->dispatch(r, c, rawdata_start[2], (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Control:
|
|
||||||
case IpcCommandType_ControlWithContext:
|
|
||||||
retval = this->dispatch_control_command(r, c, rawdata_start[2]);
|
|
||||||
break;
|
|
||||||
case IpcCommandType_Invalid:
|
|
||||||
default:
|
|
||||||
retval = 0xF601;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retval == RESULT_DEFER_SESSION) {
|
|
||||||
/* Session defer. */
|
|
||||||
this->set_deferred(true);
|
|
||||||
rc = retval;
|
|
||||||
} else if (retval == 0xF601) {
|
|
||||||
/* Session close. */
|
|
||||||
rc = retval;
|
|
||||||
} else {
|
|
||||||
rc = svcReplyAndReceive(&handle_index, &this->server_handle, 0, this->server_handle, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Control commands. */
|
|
||||||
std::tuple<Result> ConvertCurrentObjectToDomain() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
std::tuple<Result> CopyFromCurrentDomain() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
std::tuple<Result> CloneCurrentObject() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
std::tuple<Result, u32> QueryPointerBufferSize() {
|
|
||||||
return {0x0, (u32)sizeof(this->pointer_buffer)};
|
|
||||||
}
|
|
||||||
std::tuple<Result> CloneCurrentObjectEx() {
|
|
||||||
/* TODO */
|
|
||||||
return {0xF601};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result dispatch_control_command(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id) {
|
|
||||||
Result rc = 0xF601;
|
|
||||||
|
|
||||||
/* TODO: Implement. */
|
|
||||||
switch ((IpcControlCommand)cmd_id) {
|
|
||||||
case IpcCtrl_Cmd_ConvertCurrentObjectToDomain:
|
|
||||||
rc = WrapIpcCommandImpl<&ServiceSession::ConvertCurrentObjectToDomain>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_CopyFromCurrentDomain:
|
|
||||||
rc = WrapIpcCommandImpl<&ServiceSession::CopyFromCurrentDomain>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_CloneCurrentObject:
|
|
||||||
rc = WrapIpcCommandImpl<&ServiceSession::CloneCurrentObject>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_QueryPointerBufferSize:
|
|
||||||
rc = WrapIpcCommandImpl<&ServiceSession::QueryPointerBufferSize>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
|
||||||
break;
|
|
||||||
case IpcCtrl_Cmd_CloneCurrentObjectEx:
|
|
||||||
rc = WrapIpcCommandImpl<&ServiceSession::CloneCurrentObjectEx>(this, r, out_c, (u8 *)this->pointer_buffer, sizeof(this->pointer_buffer));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -2,15 +2,22 @@
|
|||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "waitablemanagerbase.hpp"
|
||||||
#include "iwaitable.hpp"
|
#include "iwaitable.hpp"
|
||||||
|
#include "hossynch.hpp"
|
||||||
|
|
||||||
class WaitableManager {
|
class IWaitable;
|
||||||
|
|
||||||
|
class WaitableManager : public WaitableManagerBase {
|
||||||
|
std::vector<IWaitable *> to_add_waitables;
|
||||||
std::vector<IWaitable *> waitables;
|
std::vector<IWaitable *> waitables;
|
||||||
u64 timeout;
|
u64 timeout;
|
||||||
|
HosMutex lock;
|
||||||
|
std::atomic_bool has_new_items;
|
||||||
private:
|
private:
|
||||||
void process_internal(bool break_on_timeout);
|
void process_internal(bool break_on_timeout);
|
||||||
public:
|
public:
|
||||||
WaitableManager(u64 t) : waitables(0), timeout(t) { }
|
WaitableManager(u64 t) : waitables(0), timeout(t), has_new_items(false) { }
|
||||||
~WaitableManager() {
|
~WaitableManager() {
|
||||||
/* This should call the destructor for every waitable. */
|
/* This should call the destructor for every waitable. */
|
||||||
for (auto & waitable : waitables) {
|
for (auto & waitable : waitables) {
|
||||||
@ -19,7 +26,6 @@ class WaitableManager {
|
|||||||
waitables.clear();
|
waitables.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int get_num_signalable();
|
|
||||||
void add_waitable(IWaitable *waitable);
|
void add_waitable(IWaitable *waitable);
|
||||||
void process();
|
void process();
|
||||||
void process_until_timeout();
|
void process_until_timeout();
|
||||||
|
14
include/stratosphere/waitablemanagerbase.hpp
Normal file
14
include/stratosphere/waitablemanagerbase.hpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class WaitableManagerBase {
|
||||||
|
std::atomic<u64> cur_priority;
|
||||||
|
public:
|
||||||
|
WaitableManagerBase() : cur_priority(0) { }
|
||||||
|
|
||||||
|
u64 get_priority() {
|
||||||
|
return std::atomic_fetch_add(&cur_priority, (u64)1);
|
||||||
|
}
|
||||||
|
};
|
@ -4,56 +4,51 @@
|
|||||||
|
|
||||||
#include <stratosphere/waitablemanager.hpp>
|
#include <stratosphere/waitablemanager.hpp>
|
||||||
|
|
||||||
|
|
||||||
unsigned int WaitableManager::get_num_signalable() {
|
|
||||||
unsigned int n = 0;
|
|
||||||
for (auto & waitable : this->waitables) {
|
|
||||||
n += waitable->get_num_waitables();
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitableManager::add_waitable(IWaitable *waitable) {
|
void WaitableManager::add_waitable(IWaitable *waitable) {
|
||||||
this->waitables.push_back(waitable);
|
this->lock.Lock();
|
||||||
|
this->to_add_waitables.push_back(waitable);
|
||||||
|
waitable->set_manager(this);
|
||||||
|
this->has_new_items = true;
|
||||||
|
this->lock.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitableManager::process_internal(bool break_on_timeout) {
|
void WaitableManager::process_internal(bool break_on_timeout) {
|
||||||
std::vector<IWaitable *> signalables;
|
|
||||||
std::vector<Handle> handles;
|
std::vector<Handle> handles;
|
||||||
|
|
||||||
int handle_index = 0;
|
int handle_index = 0;
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
/* Create vector of signalable items. */
|
/* Add new items, if relevant. */
|
||||||
signalables.resize(this->get_num_signalable(), NULL);
|
if (this->has_new_items) {
|
||||||
unsigned int n = 0;
|
this->lock.Lock();
|
||||||
for (auto & waitable : this->waitables) {
|
this->waitables.insert(this->waitables.end(), this->to_add_waitables.begin(), this->to_add_waitables.end());
|
||||||
waitable->get_waitables(signalables.data() + n);
|
this->to_add_waitables.clear();
|
||||||
n += waitable->get_num_waitables();
|
this->has_new_items = false;
|
||||||
|
this->lock.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sort signalables by priority. */
|
/* Sort waitables by priority. */
|
||||||
std::sort(signalables.begin(), signalables.end(), IWaitable::compare);
|
std::sort(this->waitables.begin(), this->waitables.end(), IWaitable::compare);
|
||||||
|
|
||||||
/* Copy out handles. */
|
/* Copy out handles. */
|
||||||
handles.resize(signalables.size());
|
handles.resize(this->waitables.size());
|
||||||
std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); });
|
std::transform(this->waitables.begin(), this->waitables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); });
|
||||||
|
|
||||||
|
|
||||||
rc = svcWaitSynchronization(&handle_index, handles.data(), signalables.size(), this->timeout);
|
rc = svcWaitSynchronization(&handle_index, handles.data(), this->waitables.size(), this->timeout);
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
/* Handle a signaled waitable. */
|
/* Handle a signaled waitable. */
|
||||||
/* TODO: What timeout should be passed here? */
|
/* TODO: What timeout should be passed here? */
|
||||||
|
|
||||||
rc = signalables[handle_index]->handle_signaled(0);
|
rc = this->waitables[handle_index]->handle_signaled(0);
|
||||||
|
|
||||||
for (int i = 0; i < handle_index; i++) {
|
for (int i = 0; i < handle_index; i++) {
|
||||||
signalables[i]->update_priority();
|
this->waitables[i]->update_priority();
|
||||||
}
|
}
|
||||||
} else if (rc == 0xEA01) {
|
} else if (rc == 0xEA01) {
|
||||||
/* Timeout. */
|
/* Timeout. */
|
||||||
for (auto & waitable : signalables) {
|
for (auto & waitable : this->waitables) {
|
||||||
waitable->update_priority();
|
waitable->update_priority();
|
||||||
}
|
}
|
||||||
if (break_on_timeout) {
|
if (break_on_timeout) {
|
||||||
@ -69,26 +64,21 @@ void WaitableManager::process_internal(bool break_on_timeout) {
|
|||||||
/* Close the handle. */
|
/* Close the handle. */
|
||||||
svcCloseHandle(handles[handle_index]);
|
svcCloseHandle(handles[handle_index]);
|
||||||
|
|
||||||
|
IWaitable *to_delete = this->waitables[handle_index];
|
||||||
|
|
||||||
/* If relevant, remove from waitables. */
|
/* If relevant, remove from waitables. */
|
||||||
this->waitables.erase(std::remove(this->waitables.begin(), this->waitables.end(), signalables[handle_index]), this->waitables.end());
|
this->waitables.erase(this->waitables.begin() + handle_index);
|
||||||
|
|
||||||
/* Delete it. */
|
/* Delete it. */
|
||||||
if (signalables[handle_index]->has_parent()) {
|
delete to_delete;
|
||||||
signalables[handle_index]->get_parent()->delete_child(signalables[handle_index]);
|
|
||||||
} else {
|
|
||||||
delete signalables[handle_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If relevant, remove from signalables. */
|
|
||||||
signalables.erase(std::remove(signalables.begin(), signalables.end(), signalables[handle_index]), signalables.end());
|
|
||||||
|
|
||||||
for (int i = 0; i < handle_index; i++) {
|
for (int i = 0; i < handle_index; i++) {
|
||||||
signalables[i]->update_priority();
|
this->waitables[i]->update_priority();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do deferred callback for each waitable. */
|
/* Do deferred callback for each waitable. */
|
||||||
for (auto & waitable : signalables) {
|
for (auto & waitable : this->waitables) {
|
||||||
if (waitable->get_deferred()) {
|
if (waitable->get_deferred()) {
|
||||||
waitable->handle_deferred();
|
waitable->handle_deferred();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user