diff --git a/include/stratosphere.hpp b/include/stratosphere.hpp index 67b73b83..000a8b74 100644 --- a/include/stratosphere.hpp +++ b/include/stratosphere.hpp @@ -3,6 +3,7 @@ #include "stratosphere/iwaitable.hpp" #include "stratosphere/iserviceobject.hpp" #include "stratosphere/iserver.hpp" +#include "stratosphere/ipcsession.hpp" #include "stratosphere/servicesession.hpp" #include "stratosphere/serviceserver.hpp" #include "stratosphere/managedportserver.hpp" diff --git a/include/stratosphere/ipc_templating.hpp b/include/stratosphere/ipc_templating.hpp index 6825865f..e3cca287 100644 --- a/include/stratosphere/ipc_templating.hpp +++ b/include/stratosphere/ipc_templating.hpp @@ -9,28 +9,38 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-but-set-variable" +/* Base for In/Out Buffers. */ +struct IpcBufferBase {}; + /* Represents an A descriptor. */ -template -struct InBuffer { +struct InBufferBase : IpcBufferBase {}; + +template +struct InBuffer : InBufferBase { T *buffer; size_t num_elements; BufferType type; + static const BufferType expected_type = e_t; - InBuffer(void *b, size_t n) : buffer((T *)b), num_elements(n/sizeof(T)) { } + InBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { } }; /* Represents a B descriptor. */ -template -struct OutBuffer { +struct OutBufferBase : IpcBufferBase {}; + +template +struct OutBuffer : OutBufferBase { T *buffer; size_t num_elements; + BufferType type; + static const BufferType expected_type = e_t; - OutBuffer(void *b, size_t n) : buffer((T *)b), num_elements(n/sizeof(T)) { } + OutBuffer(void *b, size_t n, BufferType t) : buffer((T *)b), num_elements(n/sizeof(T)), type(t) { } }; /* Represents an X descriptor. */ template -struct InPointer { +struct InPointer : IpcBufferBase { T *pointer; size_t num_elements; @@ -38,7 +48,7 @@ struct InPointer { }; /* Represents a C descriptor. */ -struct OutPointerWithServerSizeBase {}; +struct OutPointerWithServerSizeBase : IpcBufferBase {}; template struct OutPointerWithServerSize : OutPointerWithServerSizeBase { @@ -50,7 +60,7 @@ struct OutPointerWithServerSize : OutPointerWithServerSizeBase { /* Represents a C descriptor with size in raw data. */ template -struct OutPointerWithClientSize { +struct OutPointerWithClientSize : IpcBufferBase { T *pointer; size_t num_elements; @@ -99,11 +109,7 @@ struct pop_front> { template struct is_ipc_buffer { - static const bool value = is_specialization_of::value - || is_specialization_of::value - || is_specialization_of::value - || std::is_base_of::value - || is_specialization_of::value; + static const bool value = std::is_base_of::value; }; template @@ -133,7 +139,7 @@ struct size_in_raw_data_with_out_pointers_for_arguments { template struct is_ipc_inbuffer { - static const size_t value = (is_specialization_of::value) ? 1 : 0; + static const size_t value = (std::is_base_of::value) ? 1 : 0; }; template @@ -163,7 +169,7 @@ struct num_outpointers_in_arguments { template struct is_ipc_inoutbuffer { - static const size_t value = (is_specialization_of::value || is_specialization_of::value) ? 1 : 0; + static const size_t value = (std::is_base_of::value || std::is_base_of::value) ? 1 : 0; }; template @@ -186,12 +192,12 @@ T GetValueFromIpcParsedCommand(IpcParsedCommand& r, IpcCommand& out_c, u8 *point const size_t old_rawdata_index = cur_rawdata_index; const size_t old_c_size_offset = cur_c_size_offset; const size_t old_pointer_buffer_offset = pointer_buffer_offset; - if constexpr (is_specialization_of::value) { - const T& value{r.Buffers[a_index], r.BufferSizes[a_index]}; + if constexpr (std::is_base_of::value) { + const T& value = T(r.Buffers[a_index], r.BufferSizes[a_index], r.BufferTypes[a_index]); ++a_index; return value; - } else if constexpr (is_specialization_of::value) { - const T& value{r.Buffers[b_index], r.BufferSizes[b_index]}; + } else if constexpr (std::is_base_of::value) { + const T& value = T(r.Buffers[b_index], r.BufferSizes[b_index], r.BufferTypes[b_index]); ++b_index; return value; } else if constexpr (is_specialization_of::value) { @@ -226,10 +232,10 @@ T GetValueFromIpcParsedCommand(IpcParsedCommand& r, IpcCommand& out_c, u8 *point template bool ValidateIpcParsedCommandArgument(IpcParsedCommand& r, size_t& cur_rawdata_index, size_t& cur_c_size_offset, size_t& a_index, size_t& b_index, size_t& x_index, size_t& c_index, size_t& h_index, size_t& total_c_size) { const size_t old_c_size_offset = cur_c_size_offset; - if constexpr (is_specialization_of::value) { - return r.Buffers[a_index] != NULL && r.BufferDirections[a_index++] == BufferDirection_Send; - } else if constexpr (is_specialization_of::value) { - return r.Buffers[b_index] != NULL && r.BufferDirections[b_index++] == BufferDirection_Recv; + if constexpr (std::is_base_of::value) { + return r.Buffers[a_index] != NULL && r.BufferDirections[a_index] == BufferDirection_Send && r.BufferTypes[a_index++] == T::expected_type; + } else if constexpr (std::is_base_of::value) { + return r.Buffers[b_index] != NULL && r.BufferDirections[b_index] == BufferDirection_Recv && r.BufferTypes[b_index++] == T::expected_type; } else if constexpr (is_specialization_of::value) { return r.Statics[x_index] != NULL; } else if constexpr (std::is_base_of::value) { diff --git a/include/stratosphere/ipcsession.hpp b/include/stratosphere/ipcsession.hpp new file mode 100644 index 00000000..ef95238c --- /dev/null +++ b/include/stratosphere/ipcsession.hpp @@ -0,0 +1,204 @@ +#pragma once +#include +#include + +#include "ipc_templating.hpp" +#include "iserviceobject.hpp" +#include "iwaitable.hpp" +#include "servicesession.hpp" + +template +class IPCSession final : public IWaitable { + static_assert(std::is_base_of::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: + IPCSession(size_t pbs = 0x400) : pointer_buffer_size(pbs) { + Result rc; + if (R_FAILED((rc = svcCreateSession(&server_handle, &client_handle, 0, 0)))) { + fatalSimple(rc); + } + this->service_object = new T(); + this->pointer_buffer = new char[pointer_buffer_size]; + } + + IPCSession(T *so, size_t pbs = 0x400) : service_object(so), pointer_buffer_size(pbs) { + Result rc; + if (R_FAILED((rc = svcCreateSession(&server_handle, &client_handle, 0, 0)))) { + fatalSimple(rc); + } + this->pointer_buffer = new char[pointer_buffer_size]; + } + + ~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 ConvertCurrentObjectToDomain() { + /* TODO */ + return {0xF601}; + } + std::tuple CopyFromCurrentDomain() { + /* TODO */ + return {0xF601}; + } + std::tuple CloneCurrentObject() { + /* TODO */ + return {0xF601}; + } + std::tuple QueryPointerBufferSize() { + return {0x0, (u32)this->pointer_buffer_size}; + } + std::tuple 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; + } +};