|
|
|
@ -25,6 +25,7 @@ |
|
|
|
#include "core/hle/kernel/semaphore.h"
|
|
|
|
#include "core/hle/kernel/server_port.h"
|
|
|
|
#include "core/hle/kernel/server_session.h"
|
|
|
|
#include "core/hle/kernel/session.h"
|
|
|
|
#include "core/hle/kernel/shared_memory.h"
|
|
|
|
#include "core/hle/kernel/thread.h"
|
|
|
|
#include "core/hle/kernel/timer.h"
|
|
|
|
@ -237,7 +238,7 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { |
|
|
|
|
|
|
|
// TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
|
|
|
|
// responds and cause a reschedule.
|
|
|
|
return session->SendSyncRequest(); |
|
|
|
return session->SendSyncRequest(Kernel::GetCurrentThread()); |
|
|
|
} |
|
|
|
|
|
|
|
/// Close a handle
|
|
|
|
@ -398,6 +399,112 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// In a single operation, sends a IPC reply and waits for a new request.
|
|
|
|
static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count, |
|
|
|
Kernel::Handle reply_target) { |
|
|
|
// 'handles' has to be a valid pointer even if 'handle_count' is 0.
|
|
|
|
if (handles == nullptr) |
|
|
|
return Kernel::ERR_INVALID_POINTER; |
|
|
|
|
|
|
|
// Check if 'handle_count' is invalid
|
|
|
|
if (handle_count < 0) |
|
|
|
return Kernel::ERR_OUT_OF_RANGE; |
|
|
|
|
|
|
|
using ObjectPtr = SharedPtr<Kernel::WaitObject>; |
|
|
|
std::vector<ObjectPtr> objects(handle_count); |
|
|
|
|
|
|
|
for (int i = 0; i < handle_count; ++i) { |
|
|
|
auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); |
|
|
|
if (object == nullptr) |
|
|
|
return ERR_INVALID_HANDLE; |
|
|
|
objects[i] = object; |
|
|
|
} |
|
|
|
|
|
|
|
// We are also sending a command reply.
|
|
|
|
// Do not send a reply if the command id in the command buffer is 0xFFFF.
|
|
|
|
u32* cmd_buff = Kernel::GetCommandBuffer(); |
|
|
|
IPC::Header header{cmd_buff[0]}; |
|
|
|
if (reply_target != 0 && header.command_id != 0xFFFF) { |
|
|
|
auto session = Kernel::g_handle_table.Get<Kernel::ServerSession>(reply_target); |
|
|
|
if (session == nullptr) |
|
|
|
return ERR_INVALID_HANDLE; |
|
|
|
|
|
|
|
auto request_thread = std::move(session->currently_handling); |
|
|
|
|
|
|
|
// Mark the request as "handled".
|
|
|
|
session->currently_handling = nullptr; |
|
|
|
|
|
|
|
// Error out if there's no request thread or the session was closed.
|
|
|
|
// TODO(Subv): Is the same error code (ClosedByRemote) returned for both of these cases?
|
|
|
|
if (request_thread == nullptr || session->parent->client == nullptr) { |
|
|
|
*index = -1; |
|
|
|
return Kernel::ERR_SESSION_CLOSED_BY_REMOTE; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO(Subv): Perform IPC translation from the current thread to request_thread.
|
|
|
|
|
|
|
|
// Note: The scheduler is not invoked here.
|
|
|
|
request_thread->ResumeFromWait(); |
|
|
|
} |
|
|
|
|
|
|
|
if (handle_count == 0) { |
|
|
|
*index = 0; |
|
|
|
// The kernel uses this value as a placeholder for the real error, and returns it when we
|
|
|
|
// pass no handles and do not perform any reply.
|
|
|
|
if (reply_target == 0 || header.command_id == 0xFFFF) |
|
|
|
return ResultCode(0xE7E3FFFF); |
|
|
|
|
|
|
|
return RESULT_SUCCESS; |
|
|
|
} |
|
|
|
|
|
|
|
auto thread = Kernel::GetCurrentThread(); |
|
|
|
|
|
|
|
// Find the first object that is acquirable in the provided list of objects
|
|
|
|
auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { |
|
|
|
return !object->ShouldWait(thread); |
|
|
|
}); |
|
|
|
|
|
|
|
if (itr != objects.end()) { |
|
|
|
// We found a ready object, acquire it and set the result value
|
|
|
|
Kernel::WaitObject* object = itr->get(); |
|
|
|
object->Acquire(thread); |
|
|
|
*index = std::distance(objects.begin(), itr); |
|
|
|
|
|
|
|
if (object->GetHandleType() == Kernel::HandleType::ServerSession) { |
|
|
|
auto server_session = static_cast<Kernel::ServerSession*>(object); |
|
|
|
if (server_session->parent->client == nullptr) |
|
|
|
return Kernel::ERR_SESSION_CLOSED_BY_REMOTE; |
|
|
|
|
|
|
|
// TODO(Subv): Perform IPC translation from the ServerSession to the current thread.
|
|
|
|
} |
|
|
|
return RESULT_SUCCESS; |
|
|
|
} |
|
|
|
|
|
|
|
// No objects were ready to be acquired, prepare to suspend the thread.
|
|
|
|
|
|
|
|
// TODO(Subv): Perform IPC translation upon wakeup.
|
|
|
|
|
|
|
|
// Put the thread to sleep
|
|
|
|
thread->status = THREADSTATUS_WAIT_SYNCH_ANY; |
|
|
|
|
|
|
|
// Add the thread to each of the objects' waiting threads.
|
|
|
|
for (size_t i = 0; i < objects.size(); ++i) { |
|
|
|
Kernel::WaitObject* object = objects[i].get(); |
|
|
|
object->AddWaitingThread(thread); |
|
|
|
} |
|
|
|
|
|
|
|
thread->wait_objects = std::move(objects); |
|
|
|
|
|
|
|
Core::System::GetInstance().PrepareReschedule(); |
|
|
|
|
|
|
|
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
|
|
|
|
// signal in one of its wait objects, or to 0xC8A01836 if there was a translation error.
|
|
|
|
// By default the index is set to -1.
|
|
|
|
thread->wait_set_output = true; |
|
|
|
*index = -1; |
|
|
|
return RESULT_SUCCESS; |
|
|
|
} |
|
|
|
|
|
|
|
/// Create an address arbiter (to allocate access to shared resources)
|
|
|
|
static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) { |
|
|
|
using Kernel::AddressArbiter; |
|
|
|
@ -1163,7 +1270,7 @@ static const FunctionDef SVC_Table[] = { |
|
|
|
{0x4C, nullptr, "ReplyAndReceive2"}, |
|
|
|
{0x4D, nullptr, "ReplyAndReceive3"}, |
|
|
|
{0x4E, nullptr, "ReplyAndReceive4"}, |
|
|
|
{0x4F, nullptr, "ReplyAndReceive"}, |
|
|
|
{0x4F, HLE::Wrap<ReplyAndReceive>, "ReplyAndReceive"}, |
|
|
|
{0x50, nullptr, "BindInterrupt"}, |
|
|
|
{0x51, nullptr, "UnbindInterrupt"}, |
|
|
|
{0x52, nullptr, "InvalidateProcessDataCache"}, |
|
|
|
|