Merge pull request #2793 from Subv/replyandreceive

Kernel/SVC: Partially implemented svcReplyAndReceive
This commit is contained in:
Sebastian Valle 2017-06-29 17:05:22 -05:00 committed by GitHub
commit 56d718b2a1
6 changed files with 161 additions and 23 deletions

View file

@ -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"},