diff --git a/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp b/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp index f1d44ae69..f9e00fdfc 100644 --- a/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/ipc_templating.hpp @@ -387,6 +387,26 @@ struct Encoder> { }; +template +Result WrapDeferredIpcCommandImpl(Class *this_ptr, Args... args) { + using InArgs = typename boost::callable_traits::args_t; + using InArgsWithoutThis = typename pop_front::type; + using OutArgs = typename boost::callable_traits::return_type_t; + + static_assert(is_specialization_of::value, "IpcCommandImpls must return std::tuple"); + static_assert(std::is_same_v, Result>, "IpcCommandImpls must return std::tuple"); + static_assert(std::is_same_v>, "Invalid Deferred Wrapped IpcCommandImpl arguments!"); + + IpcCommand out_command; + + ipcInitialize(&out_command); + + auto tuple_args = std::make_tuple(args...); + auto result = std::apply( [=](auto&&... a) { return (this_ptr->*IpcCommandImpl)(a...); }, tuple_args); + + return std::apply(Encoder{out_command}, result); +} + template Result WrapIpcCommandImpl(Class *this_ptr, IpcParsedCommand& r, IpcCommand &out_command, u8 *pointer_buffer, size_t pointer_buffer_size) { using InArgs = typename boost::callable_traits::args_t; diff --git a/stratosphere/libstratosphere/include/stratosphere/iserver.hpp b/stratosphere/libstratosphere/include/stratosphere/iserver.hpp index 0188000c1..ed6cb57a1 100644 --- a/stratosphere/libstratosphere/include/stratosphere/iserver.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/iserver.hpp @@ -90,6 +90,11 @@ class IServer : public IWaitable { return this->port_handle; } + + virtual void handle_deferred() { + /* TODO: Panic, because we can never defer a server. */ + } + virtual Result handle_signaled(u64 timeout) { /* If this server's port was signaled, accept a new session. */ Handle session_h; diff --git a/stratosphere/libstratosphere/include/stratosphere/iserviceobject.hpp b/stratosphere/libstratosphere/include/stratosphere/iserviceobject.hpp index a34e60754..cf4e7d670 100644 --- a/stratosphere/libstratosphere/include/stratosphere/iserviceobject.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/iserviceobject.hpp @@ -7,4 +7,5 @@ class IServiceObject { public: virtual ~IServiceObject() { } virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size) = 0; + virtual Result handle_deferred() = 0; }; \ No newline at end of file diff --git a/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp b/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp index f124ae696..ded313965 100644 --- a/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/iwaitable.hpp @@ -4,6 +4,7 @@ class IWaitable { u64 wait_priority = 0; + bool is_deferred = false; IWaitable *parent_waitable; public: virtual ~IWaitable() { } @@ -11,6 +12,7 @@ class 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 Handle get_handle() = 0; virtual Result handle_signaled(u64 timeout) = 0; @@ -34,7 +36,15 @@ class IWaitable { this->wait_priority = g_cur_priority++; } + bool get_deferred() { + return this->is_deferred; + } + + void set_deferred(bool d) { + this->is_deferred = d; + } + static bool compare(IWaitable *a, IWaitable *b) { - return (a->wait_priority < b->wait_priority); + return (a->wait_priority < b->wait_priority) && !a->is_deferred; } }; \ No newline at end of file diff --git a/stratosphere/libstratosphere/include/stratosphere/servicesession.hpp b/stratosphere/libstratosphere/include/stratosphere/servicesession.hpp index 996a228fe..e730849d6 100644 --- a/stratosphere/libstratosphere/include/stratosphere/servicesession.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/servicesession.hpp @@ -16,6 +16,8 @@ enum IpcControlCommand { }; #define POINTER_BUFFER_SIZE_MAX 0xFFFF +#define RESULT_DEFER_SESSION (0x6580A) + template class IServer; @@ -68,6 +70,20 @@ class ServiceSession : public IWaitable { return this->server_handle; } + virtual void handle_deferred() { + 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_signaled(u64 timeout) { Result rc; int handle_index; @@ -124,10 +140,15 @@ class ServiceSession : public IWaitable { } - if (retval != 0xF601) { - rc = svcReplyAndReceive(&handle_index, &this->server_handle, 1, this->server_handle, 0); - } else { + 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); } } diff --git a/stratosphere/libstratosphere/source/waitablemanager.cpp b/stratosphere/libstratosphere/source/waitablemanager.cpp index 5eee835c5..ea2edee85 100644 --- a/stratosphere/libstratosphere/source/waitablemanager.cpp +++ b/stratosphere/libstratosphere/source/waitablemanager.cpp @@ -41,7 +41,14 @@ void WaitableManager::process() { handles.resize(signalables.size()); std::transform(signalables.begin(), signalables.end(), handles.begin(), [](IWaitable *w) { return w->get_handle(); }); - rc = svcWaitSynchronization(&handle_index, handles.data(), handles.size(), this->timeout); + unsigned int num_not_deferred = 0; + for (auto & signalable : signalables) { + if (!signalable->get_deferred()) { + num_not_deferred++; + } + } + + rc = svcWaitSynchronization(&handle_index, handles.data(), num_not_deferred, this->timeout); if (R_SUCCEEDED(rc)) { /* Handle a signaled waitable. */ /* TODO: What timeout should be passed here? */ @@ -79,6 +86,9 @@ void WaitableManager::process() { for (int i = 0; i < handle_index; i++) { signalables[i]->update_priority(); } - } + } + + /* Do deferred callback for each waitable. */ + std::for_each(signalables.begin() + num_not_deferred, signalables.end(), [](IWaitable *w) { w->handle_deferred(); }); } } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_debug_monitor.hpp b/stratosphere/loader/source/ldr_debug_monitor.hpp index 20f8fef64..09c64b0c9 100644 --- a/stratosphere/loader/source/ldr_debug_monitor.hpp +++ b/stratosphere/loader/source/ldr_debug_monitor.hpp @@ -12,7 +12,11 @@ enum DebugMonitorServiceCmd { class DebugMonitorService : IServiceObject { public: - Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result handle_deferred() { + /* This service will never defer. */ + return 0; + } private: /* Actual commands. */ diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index c68de9cce..c8d6e29a8 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -1,4 +1,3 @@ -#pragma once #include #include "ldr_process_creation.hpp" diff --git a/stratosphere/loader/source/ldr_process_manager.hpp b/stratosphere/loader/source/ldr_process_manager.hpp index 3da91dcf3..fadfecd0d 100644 --- a/stratosphere/loader/source/ldr_process_manager.hpp +++ b/stratosphere/loader/source/ldr_process_manager.hpp @@ -29,7 +29,11 @@ class ProcessManagerService : IServiceObject { static_assert(sizeof(ProcessManagerService::ProgramInfo) == 0x400, "Incorrect ProgramInfo definition."); public: - Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result handle_deferred() { + /* This service will never defer. */ + return 0; + } private: /* Actual commands. */ diff --git a/stratosphere/loader/source/ldr_shell.hpp b/stratosphere/loader/source/ldr_shell.hpp index ab6e8749e..a4e8699e6 100644 --- a/stratosphere/loader/source/ldr_shell.hpp +++ b/stratosphere/loader/source/ldr_shell.hpp @@ -9,7 +9,11 @@ enum ShellServiceCmd { class ShellService : IServiceObject { public: - Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result handle_deferred() { + /* This service will never defer. */ + return 0; + } private: /* Actual commands. */ diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index f63e7ca28..d21236040 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -18,6 +18,11 @@ Result ManagerService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_ return rc; } +Result ManagerService::handle_deferred() { + /* This service is never deferrable. */ + return 0; +} + std::tuple ManagerService::register_process(u64 pid, InBuffer acid_sac, InBuffer aci0_sac) { return std::make_tuple(Registration::RegisterProcess(pid, acid_sac.buffer, acid_sac.num_elements, aci0_sac.buffer, aci0_sac.num_elements)); diff --git a/stratosphere/sm/source/sm_manager_service.hpp b/stratosphere/sm/source/sm_manager_service.hpp index 7da8a51fe..e84eca60e 100644 --- a/stratosphere/sm/source/sm_manager_service.hpp +++ b/stratosphere/sm/source/sm_manager_service.hpp @@ -9,7 +9,8 @@ enum ManagerServiceCmd { class ManagerService : IServiceObject { public: - Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result handle_deferred(); private: /* Actual commands. */ diff --git a/stratosphere/sm/source/sm_registration.cpp b/stratosphere/sm/source/sm_registration.cpp index 013bd4db0..5a38b1765 100644 --- a/stratosphere/sm/source/sm_registration.cpp +++ b/stratosphere/sm/source/sm_registration.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "sm_registration.hpp" static Registration::Process g_process_list[REGISTRATION_LIST_MAX_PROCESS] = {0}; @@ -132,6 +133,24 @@ bool Registration::HasService(u64 service) { return false; } +Result Registration::GetServiceHandle(u64 service, Handle *out) { + Registration::Service *target_service = GetService(service); + if (target_service == NULL) { + /* Note: This defers the result until later. */ + return RESULT_DEFER_SESSION; + } + + *out = 0; + Result rc = svcConnectToPort(out, target_service->port_h); + if (R_FAILED(rc)) { + if ((rc & 0x3FFFFF) == 0xE01) { + return 0x615; + } + } + + return rc; +} + Result Registration::GetServiceForPid(u64 pid, u64 service, Handle *out) { if (!service) { return 0xC15; @@ -158,20 +177,7 @@ Result Registration::GetServiceForPid(u64 pid, u64 service, Handle *out) { } } - Registration::Service *target_service = GetService(service); - if (target_service == NULL) { - /* TODO: This defers the request, somehow? Needs to be RE'd in more detail. */ - return 0x6580A; - } - - *out = 0; - Result rc = svcConnectToPort(out, target_service->port_h); - if (R_FAILED(rc)) { - if ((rc & 0x3FFFFF) == 0xE01) { - return 0x615; - } - } - return rc; + return GetServiceHandle(service, out); } Result Registration::RegisterServiceForPid(u64 pid, u64 service, u64 max_sessions, bool is_light, Handle *out) { diff --git a/stratosphere/sm/source/sm_registration.hpp b/stratosphere/sm/source/sm_registration.hpp index 17d984ebd..2a19be857 100644 --- a/stratosphere/sm/source/sm_registration.hpp +++ b/stratosphere/sm/source/sm_registration.hpp @@ -34,6 +34,7 @@ class Registration { /* Service management. */ static bool HasService(u64 service); + static Result GetServiceHandle(u64 service, Handle *out); static Result GetServiceForPid(u64 pid, u64 service, Handle *out); static Result RegisterServiceForPid(u64 pid, u64 service, u64 max_sessions, bool is_light, Handle *out); static Result RegisterServiceForSelf(u64 service, u64 max_sessions, bool is_light, Handle *out); diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index 78652366d..f57f45858 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -1,4 +1,5 @@ #include +#include #include "sm_user_service.hpp" #include "sm_registration.hpp" @@ -24,6 +25,11 @@ Result UserService::dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, return rc; } +Result UserService::handle_deferred() { + /* If we're deferred, GetService failed. */ + return WrapDeferredIpcCommandImpl<&UserService::deferred_get_service>(this, this->deferred_service); +} + std::tuple UserService::initialize(PidDescriptor pid) { this->pid = pid.pid; @@ -37,6 +43,16 @@ std::tuple UserService::get_service(u64 service) { if (this->has_initialized) { rc = Registration::GetServiceForPid(this->pid, service, &session_h); } + /* It's possible that this will end up deferring us...take that into account. */ + if (rc == RESULT_DEFER_SESSION) { + this->deferred_service = service; + } + return std::make_tuple(rc, MovedHandle{session_h}); +} + +std::tuple UserService::deferred_get_service(u64 service) { + Handle session_h = 0; + Result rc = Registration::GetServiceHandle(service, &session_h); return std::make_tuple(rc, MovedHandle{session_h}); } diff --git a/stratosphere/sm/source/sm_user_service.hpp b/stratosphere/sm/source/sm_user_service.hpp index 1bcfddc6c..eddf75723 100644 --- a/stratosphere/sm/source/sm_user_service.hpp +++ b/stratosphere/sm/source/sm_user_service.hpp @@ -12,15 +12,18 @@ enum UserServiceCmd { class UserService : IServiceObject { u64 pid; bool has_initialized; + u64 deferred_service; public: - Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); - UserService() : pid(U64_MAX), has_initialized(false) { } + UserService() : pid(U64_MAX), has_initialized(false), deferred_service(0) { } + virtual Result dispatch(IpcParsedCommand &r, IpcCommand &out_c, u64 cmd_id, u8 *pointer_buffer, size_t pointer_buffer_size); + virtual Result handle_deferred(); private: /* Actual commands. */ std::tuple initialize(PidDescriptor pid); std::tuple get_service(u64 service); + std::tuple deferred_get_service(u64 service); std::tuple register_service(u64 service, u8 is_light, u32 max_sessions); std::tuple unregister_service(u64 service); }; \ No newline at end of file