Merge pull request #2793 from Subv/replyandreceive
Kernel/SVC: Partially implemented svcReplyAndReceive
This commit is contained in:
commit
56d718b2a1
6 changed files with 161 additions and 23 deletions
|
@ -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"},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue