Stratosphere: Implement support for deferred commands. Finish sm GetService()'s deferred path.

This commit is contained in:
Michael Scire 2018-04-22 03:02:08 -06:00
parent 809090e40d
commit 674528b246
16 changed files with 137 additions and 27 deletions

View file

@ -387,6 +387,26 @@ struct Encoder<std::tuple<Args...>> {
};
template<auto IpcCommandImpl, typename Class, typename... Args>
Result WrapDeferredIpcCommandImpl(Class *this_ptr, Args... args) {
using InArgs = typename boost::callable_traits::args_t<decltype(IpcCommandImpl)>;
using InArgsWithoutThis = typename pop_front<InArgs>::type;
using OutArgs = typename boost::callable_traits::return_type_t<decltype(IpcCommandImpl)>;
static_assert(is_specialization_of<OutArgs, std::tuple>::value, "IpcCommandImpls must return std::tuple<Result, ...>");
static_assert(std::is_same_v<std::tuple_element_t<0, OutArgs>, Result>, "IpcCommandImpls must return std::tuple<Result, ...>");
static_assert(std::is_same_v<InArgsWithoutThis, std::tuple<Args...>>, "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<OutArgs>{out_command}, result);
}
template<auto IpcCommandImpl, typename Class>
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<decltype(IpcCommandImpl)>;

View file

@ -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;

View file

@ -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;
};

View file

@ -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;
}
};

View file

@ -16,6 +16,8 @@ enum IpcControlCommand {
};
#define POINTER_BUFFER_SIZE_MAX 0xFFFF
#define RESULT_DEFER_SESSION (0x6580A)
template <typename T>
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);
}
}

View file

@ -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? */
@ -80,5 +87,8 @@ void WaitableManager::process() {
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(); });
}
}

View file

@ -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. */

View file

@ -1,4 +1,3 @@
#pragma once
#include <switch.h>
#include "ldr_process_creation.hpp"

View file

@ -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. */

View file

@ -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. */

View file

@ -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<Result> ManagerService::register_process(u64 pid, InBuffer<u8> acid_sac, InBuffer<u8> aci0_sac) {
return std::make_tuple(Registration::RegisterProcess(pid, acid_sac.buffer, acid_sac.num_elements, aci0_sac.buffer, aci0_sac.num_elements));

View file

@ -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. */

View file

@ -1,5 +1,6 @@
#include <switch.h>
#include <algorithm>
#include <stratosphere/servicesession.hpp>
#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) {

View file

@ -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);

View file

@ -1,4 +1,5 @@
#include <switch.h>
#include <stratosphere/servicesession.hpp>
#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<Result> UserService::initialize(PidDescriptor pid) {
this->pid = pid.pid;
@ -37,6 +43,16 @@ std::tuple<Result, MovedHandle> 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<Result, MovedHandle> 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});
}

View file

@ -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<Result> initialize(PidDescriptor pid);
std::tuple<Result, MovedHandle> get_service(u64 service);
std::tuple<Result, MovedHandle> deferred_get_service(u64 service);
std::tuple<Result, MovedHandle> register_service(u64 service, u8 is_light, u32 max_sessions);
std::tuple<Result> unregister_service(u64 service);
};