From 5c80016c81fe238bef02af48c39e9d8ca6730b0e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 10 Jun 2018 11:11:05 -0600 Subject: [PATCH] fs.mitm: Add HANS-style redirection for System Data Archives. --- stratosphere/fs_mitm/source/fs_istorage.hpp | 120 +++++++++++ stratosphere/fs_mitm/source/fs_shim.c | 155 +++++++++++++ stratosphere/fs_mitm/source/fs_shim.h | 31 +++ .../fs_mitm/source/fsmitm_romstorage.hpp | 71 ++++++ .../fs_mitm/source/fsmitm_service.cpp | 35 +++ .../fs_mitm/source/fsmitm_service.hpp | 7 +- stratosphere/fs_mitm/source/fsmitm_utils.cpp | 29 +++ stratosphere/fs_mitm/source/fsmitm_utils.hpp | 8 + .../fs_mitm/source/imitmserviceobject.hpp | 6 + stratosphere/fs_mitm/source/mitm_session.hpp | 3 +- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/ipc_templating.hpp | 54 ++--- .../include/stratosphere/ipcsession.hpp | 204 ++++++++++++++++++ 13 files changed, 698 insertions(+), 26 deletions(-) create mode 100644 stratosphere/fs_mitm/source/fs_istorage.hpp create mode 100644 stratosphere/fs_mitm/source/fs_shim.c create mode 100644 stratosphere/fs_mitm/source/fs_shim.h create mode 100644 stratosphere/fs_mitm/source/fsmitm_romstorage.hpp create mode 100644 stratosphere/fs_mitm/source/fsmitm_utils.cpp create mode 100644 stratosphere/fs_mitm/source/fsmitm_utils.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/ipcsession.hpp diff --git a/stratosphere/fs_mitm/source/fs_istorage.hpp b/stratosphere/fs_mitm/source/fs_istorage.hpp new file mode 100644 index 000000000..cb352ef97 --- /dev/null +++ b/stratosphere/fs_mitm/source/fs_istorage.hpp @@ -0,0 +1,120 @@ +#pragma once +#include +#include +#include "fs_shim.h" + +enum FsIStorageCmd { + FsIStorage_Cmd_Read = 0, + FsIStorage_Cmd_Write = 1, + FsIStorage_Cmd_Flush = 2, + FsIStorage_Cmd_SetSize = 3, + FsIStorage_Cmd_GetSize = 4, + FsIStorage_Cmd_OperateRange = 5, +}; + +class IStorage { + public: + virtual ~IStorage() { + + } + virtual Result Read(void *buffer, size_t size, u64 offset, u64 *out_read_size) = 0; + virtual Result Write(void *buffer, size_t size, u64 offset) = 0; + virtual Result Flush() = 0; + virtual Result SetSize(u64 size) = 0; + virtual Result GetSize(u64 *out_size) = 0; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; +}; + +class IStorageInterface : public IServiceObject { + private: + IStorage *base_storage; + public: + IStorageInterface(IStorage *s) : base_storage(s) { + /* ... */ + }; + + ~IStorageInterface() { + delete base_storage; + }; + + Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) final { + Result rc = 0xF601; + switch ((FsIStorageCmd)cmd_id) { + case FsIStorage_Cmd_Read: + rc = WrapIpcCommandImpl<&IStorageInterface::read>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case FsIStorage_Cmd_Write: + rc = WrapIpcCommandImpl<&IStorageInterface::write>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case FsIStorage_Cmd_Flush: + rc = WrapIpcCommandImpl<&IStorageInterface::flush>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case FsIStorage_Cmd_SetSize: + rc = WrapIpcCommandImpl<&IStorageInterface::set_size>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case FsIStorage_Cmd_GetSize: + rc = WrapIpcCommandImpl<&IStorageInterface::get_size>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; + case FsIStorage_Cmd_OperateRange: + if (kernelAbove400()) { + rc = WrapIpcCommandImpl<&IStorageInterface::operate_range>(this, r, out_c, pointer_buffer, pointer_buffer_size); + } + break; + default: + break; + } + return rc; + }; + + Result handle_deferred() final { + /* TODO: Panic, we can never defer. */ + return 0; + }; + private: + /* Actual command API. */ + virtual std::tuple read(OutBuffer buffer, u64 offset, u64 size) final { + u64 out_size = 0; + Result rc = this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset , &out_size); + return {rc, out_size}; + }; + virtual std::tuple write(InBuffer buffer, u64 offset, u64 size) final { + return {this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset)}; + + }; + virtual std::tuple flush() final { + return {this->base_storage->Flush()}; + }; + virtual std::tuple set_size(u64 size) final { + return {this->base_storage->SetSize(size)}; + }; + virtual std::tuple get_size() final { + u64 out_size = 0; + Result rc = this->base_storage->GetSize(&out_size); + return {rc, out_size}; + }; + virtual std::tuple operate_range(u32 operation_type, u64 offset, u64 size) final { + FsRangeInfo out_range_info = {0}; + Result rc = this->base_storage->OperateRange(operation_type, offset, size, &out_range_info); + return {rc, out_range_info}; + }; +}; + +class IROStorage : public IStorage { + protected: + virtual Result Read(void *buffer, size_t size, u64 offset, u64 *out_read_size) = 0; + Result Write(void *buffer, size_t size, u64 offset) final { + (void)(buffer); + (void)(offset); + (void)(size); + return 0x313802; + }; + Result Flush() final { + return 0x0; + }; + Result SetSize(u64 size) final { + (void)(size); + return 0x313802; + }; + virtual Result GetSize(u64 *out_size) = 0; + virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; +}; diff --git a/stratosphere/fs_mitm/source/fs_shim.c b/stratosphere/fs_mitm/source/fs_shim.c new file mode 100644 index 000000000..179d3d73a --- /dev/null +++ b/stratosphere/fs_mitm/source/fs_shim.c @@ -0,0 +1,155 @@ +#include +#include "fs_shim.h" + +/* Missing fsp-srv commands. */ +Result fsOpenDataStorageByDataId(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + FsStorageId storage_id; + u64 data_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 202; + raw->storage_id = storage_id; + raw->data_id = data_id; + + Result rc = serviceIpcDispatch(s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreate(&out->s, r.Handles[0]); + } + } + + return rc; +} + +/* Missing FS File commands. */ +Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 op_id; + u64 off; + u64 len; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->op_id = op_id; + raw->off = off; + raw->len = len; + + Result rc = serviceIpcDispatch(&f->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + FsRangeInfo range_info; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out) *out = resp->range_info; + } + + return rc; +} + +/* Missing FS Storage commands. */ +Result fsStorageGetSize(FsStorage* s, u64* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 4; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + u64 size; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out) *out = resp->size; + } + + return rc; +} + +Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 op_id; + u64 off; + u64 len; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->op_id = op_id; + raw->off = off; + raw->len = len; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct { + u64 magic; + u64 result; + FsRangeInfo range_info; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out) *out = resp->range_info; + } + + return rc; +} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fs_shim.h b/stratosphere/fs_mitm/source/fs_shim.h new file mode 100644 index 000000000..e50752b27 --- /dev/null +++ b/stratosphere/fs_mitm/source/fs_shim.h @@ -0,0 +1,31 @@ +/** + * @file fs_shim.h + * @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO: Reverse this more. */ +typedef struct { + u32 flags[0x40/sizeof(u32)]; +} FsRangeInfo; + +/* Missing fsp-srv commands. */ +Result fsOpenDataStorageByDataId(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out); + +/* Missing FS File commands. */ +Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out); + +/* Missing FS Storage commands. */ +Result fsStorageGetSize(FsStorage* s, u64* out); +Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp new file mode 100644 index 000000000..fab72816f --- /dev/null +++ b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp @@ -0,0 +1,71 @@ +#pragma once +#include +#include + +#include "fs_istorage.hpp" + +/* Represents a RomFS stored in some file. */ +class RomFileStorage : public IROStorage { + private: + FsFile *base_file; + public: + RomFileStorage(FsFile *f) : base_file(f) { + /* ... */ + }; + RomFileStorage(FsFile f) { + this->base_file = new FsFile(f); + }; + ~RomFileStorage() { + fsFileClose(base_file); + delete base_file; + }; + protected: + Result Read(void *buffer, size_t size, u64 offset, u64 *out_read_size) override { + size_t out_sz = 0; + Result rc = fsFileRead(this->base_file, offset, buffer, size, &out_sz); + if (R_SUCCEEDED(rc)) { + *out_read_size = out_sz; + } + return rc; + }; + Result GetSize(u64 *out_size) override { + return fsFileGetSize(this->base_file, out_size); + }; + Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + /* TODO: Merge into libnx. */ + return fsFileOperateRange(this->base_file, operation_type, offset, size, out_range_info); + }; +}; + +/* Represents a RomFS accessed via some IStorage. */ +class RomInterfaceStorage : public IROStorage { + private: + FsStorage *base_storage; + public: + RomInterfaceStorage(FsStorage *s) : base_storage(s) { + /* ... */ + }; + RomInterfaceStorage(FsStorage s) { + this->base_storage = new FsStorage(s); + }; + ~RomInterfaceStorage() { + fsStorageClose(base_storage); + delete base_storage; + }; + protected: + Result Read(void *buffer, size_t size, u64 offset, u64 *out_read_size) override { + Result rc = fsStorageRead(this->base_storage, offset, buffer, size); + if (R_SUCCEEDED(rc)) { + *out_read_size = size; + } + return rc; + }; + Result GetSize(u64 *out_size) override { + /* TODO: Merge into libnx. */ + return fsStorageGetSize(this->base_storage, out_size); + }; + Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override { + /* TODO: Merge into libnx. */ + return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info); + }; +}; diff --git a/stratosphere/fs_mitm/source/fsmitm_service.cpp b/stratosphere/fs_mitm/source/fsmitm_service.cpp index 2f23765ef..26c1bcaa2 100644 --- a/stratosphere/fs_mitm/source/fsmitm_service.cpp +++ b/stratosphere/fs_mitm/source/fsmitm_service.cpp @@ -1,14 +1,25 @@ #include #include "fsmitm_service.hpp" +#include "fs_shim.h" + +#include "fsmitm_worker.hpp" +#include "fsmitm_utils.hpp" +#include "fsmitm_romstorage.hpp" + +#include "debug.hpp" Result FsMitMService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) { Result rc = 0xF601; + switch (cmd_id) { case FspSrv_Cmd_SetCurrentProcess: if (!this->has_initialized && r.HasPid) { this->process_id = r.Pid; } break; + case FspSrv_Cmd_OpenDataStorageByDataId: + rc = WrapIpcCommandImpl<&FsMitMService::open_data_storage_by_data_id>(this, r, out_c, pointer_buffer, pointer_buffer_size); + break; } return rc; } @@ -37,4 +48,28 @@ Result FsMitMService::postprocess(IpcParsedCommand &r, IpcCommand &out_c, u64 cm Result FsMitMService::handle_deferred() { /* This service is never deferrable. */ return 0; +} + +/* Add redirection for System Data Archives to the SD card. */ +std::tuple FsMitMService::open_data_storage_by_data_id(FsStorageId storage_id, u64 data_id) { + Handle out_h = 0; + FsStorage data_storage; + FsFile data_file; + Result rc = fsOpenDataStorageByDataId(this->forward_service, storage_id, data_id, &data_storage); + if (R_SUCCEEDED(rc)) { + IPCSession *out_session = NULL; + char path[FS_MAX_PATH] = {0}; + /* TODO: Is there a sensible path that ends in ".romfs" we can use?" */ + snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs.bin", data_id); + if (R_SUCCEEDED(Utils::OpenSdFile(path, FS_OPEN_READ, &data_file))) { + fsStorageClose(&data_storage); + out_session = new IPCSession(new IStorageInterface(new RomFileStorage(data_file))); + } else { + + out_session = new IPCSession(new IStorageInterface(new RomInterfaceStorage(data_storage))); + } + FsMitmWorker::AddWaitable(out_session); + out_h = out_session->get_client_handle(); + } + return {rc, out_h}; } \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_service.hpp b/stratosphere/fs_mitm/source/fsmitm_service.hpp index 6f22af97d..16a02c5fc 100644 --- a/stratosphere/fs_mitm/source/fsmitm_service.hpp +++ b/stratosphere/fs_mitm/source/fsmitm_service.hpp @@ -5,6 +5,7 @@ enum FspSrvCmd { FspSrv_Cmd_SetCurrentProcess = 1, + FspSrv_Cmd_OpenDataStorageByDataId = 202, }; class FsMitMService : public IMitMServiceObject { @@ -13,10 +14,14 @@ class FsMitMService : public IMitMServiceObject { u64 process_id; u64 title_id; public: - FsMitMService() : has_initialized(false), process_id(0), title_id(0) { + FsMitMService(Service *s) : IMitMServiceObject(s), has_initialized(false), process_id(0), title_id(0) { /* ... */ } virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); virtual Result postprocess(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); virtual Result handle_deferred(); + + protected: + /* Overridden commands. */ + std::tuple open_data_storage_by_data_id(FsStorageId storage_id, u64 data_id); }; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.cpp b/stratosphere/fs_mitm/source/fsmitm_utils.cpp new file mode 100644 index 000000000..d87ee2599 --- /dev/null +++ b/stratosphere/fs_mitm/source/fsmitm_utils.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "fsmitm_utils.hpp" + +static FsFileSystem g_sd_filesystem; +static bool g_has_initialized = false; + +static Result EnsureInitialized() { + if (g_has_initialized) { + return 0x0; + } + Result rc = fsMountSdcard(&g_sd_filesystem); + if (R_SUCCEEDED(rc)) { + g_has_initialized = true; + } + return rc; +} + +Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) { + Result rc; + if (R_FAILED((rc = EnsureInitialized()))) { + return rc; + } + char path[FS_MAX_PATH]; + strcpy(path, fn); + return fsFsOpenFile(&g_sd_filesystem, path, flags, out); +} \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_utils.hpp b/stratosphere/fs_mitm/source/fsmitm_utils.hpp new file mode 100644 index 000000000..5a497c5f6 --- /dev/null +++ b/stratosphere/fs_mitm/source/fsmitm_utils.hpp @@ -0,0 +1,8 @@ +#pragma once +#include +#include + +class Utils { + public: + static Result OpenSdFile(const char *fn, int flags, FsFile *out); +}; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/imitmserviceobject.hpp b/stratosphere/fs_mitm/source/imitmserviceobject.hpp index bba10a4c8..56c2b51c4 100644 --- a/stratosphere/fs_mitm/source/imitmserviceobject.hpp +++ b/stratosphere/fs_mitm/source/imitmserviceobject.hpp @@ -4,6 +4,12 @@ #include class IMitMServiceObject : public IServiceObject { + protected: + Service *forward_service; + public: + IMitMServiceObject(Service *s) : forward_service(s) { + + } protected: virtual ~IMitMServiceObject() { } virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0; diff --git a/stratosphere/fs_mitm/source/mitm_session.hpp b/stratosphere/fs_mitm/source/mitm_session.hpp index ceb1f4926..0f2b36f09 100644 --- a/stratosphere/fs_mitm/source/mitm_session.hpp +++ b/stratosphere/fs_mitm/source/mitm_session.hpp @@ -28,18 +28,19 @@ class MitMSession final : public IWaitable { static_assert(sizeof(pointer_buffer) <= POINTER_BUFFER_SIZE_MAX, "Incorrect Size for PointerBuffer!"); public: MitMSession(MitMServer *s, Handle s_h, Handle c_h, const char *srv) : server(s), server_handle(s_h), client_handle(c_h) { - this->service_object = new T(); if (R_FAILED(smMitMGetService(&forward_service, srv))) { /* TODO: Panic. */ } if (R_FAILED(ipcQueryPointerBufferSize(forward_service.handle, &pointer_buffer_size))) { /* TODO: Panic. */ } + this->service_object = new T(&forward_service); this->pointer_buffer = new char[pointer_buffer_size]; } ~MitMSession() override { delete this->service_object; + delete this->pointer_buffer; serviceClose(&forward_service); if (server_handle) { svcCloseHandle(server_handle); diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp index 67b73b833..000a8b741 100644 --- a/stratosphere/libstratosphere/include/stratosphere.hpp +++ b/stratosphere/libstratosphere/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/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp index 6825865f1..e3cca2873 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp +++ b/stratosphere/libstratosphere/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/stratosphere/libstratosphere/include/stratosphere/ipcsession.hpp b/stratosphere/libstratosphere/include/stratosphere/ipcsession.hpp new file mode 100644 index 000000000..ef95238ce --- /dev/null +++ b/stratosphere/libstratosphere/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; + } +};