summaryrefslogtreecommitdiff
path: root/src/core/hle/svc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/svc.cpp')
-rw-r--r--src/core/hle/svc.cpp109
1 files changed, 108 insertions, 1 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index e68b9f16a..2cdbe3fb9 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -25,6 +25,7 @@
25#include "core/hle/kernel/semaphore.h" 25#include "core/hle/kernel/semaphore.h"
26#include "core/hle/kernel/server_port.h" 26#include "core/hle/kernel/server_port.h"
27#include "core/hle/kernel/server_session.h" 27#include "core/hle/kernel/server_session.h"
28#include "core/hle/kernel/session.h"
28#include "core/hle/kernel/shared_memory.h" 29#include "core/hle/kernel/shared_memory.h"
29#include "core/hle/kernel/thread.h" 30#include "core/hle/kernel/thread.h"
30#include "core/hle/kernel/timer.h" 31#include "core/hle/kernel/timer.h"
@@ -397,6 +398,112 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
397 } 398 }
398} 399}
399 400
401/// In a single operation, sends a IPC reply and waits for a new request.
402static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count,
403 Kernel::Handle reply_target) {
404 // 'handles' has to be a valid pointer even if 'handle_count' is 0.
405 if (handles == nullptr)
406 return Kernel::ERR_INVALID_POINTER;
407
408 // Check if 'handle_count' is invalid
409 if (handle_count < 0)
410 return Kernel::ERR_OUT_OF_RANGE;
411
412 using ObjectPtr = SharedPtr<Kernel::WaitObject>;
413 std::vector<ObjectPtr> objects(handle_count);
414
415 for (int i = 0; i < handle_count; ++i) {
416 auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]);
417 if (object == nullptr)
418 return ERR_INVALID_HANDLE;
419 objects[i] = object;
420 }
421
422 // We are also sending a command reply.
423 // Do not send a reply if the command id in the command buffer is 0xFFFF.
424 u32* cmd_buff = Kernel::GetCommandBuffer();
425 IPC::Header header{cmd_buff[0]};
426 if (reply_target != 0 && header.command_id != 0xFFFF) {
427 auto session = Kernel::g_handle_table.Get<Kernel::ServerSession>(reply_target);
428 if (session == nullptr)
429 return ERR_INVALID_HANDLE;
430
431 auto request_thread = std::move(session->currently_handling);
432
433 // Mark the request as "handled".
434 session->currently_handling = nullptr;
435
436 // Error out if there's no request thread or the session was closed.
437 // TODO(Subv): Is the same error code (ClosedByRemote) returned for both of these cases?
438 if (request_thread == nullptr || session->parent->client == nullptr) {
439 *index = -1;
440 return Kernel::ERR_SESSION_CLOSED_BY_REMOTE;
441 }
442
443 // TODO(Subv): Perform IPC translation from the current thread to request_thread.
444
445 // Note: The scheduler is not invoked here.
446 request_thread->ResumeFromWait();
447 }
448
449 if (handle_count == 0) {
450 *index = 0;
451 // The kernel uses this value as a placeholder for the real error, and returns it when we
452 // pass no handles and do not perform any reply.
453 if (reply_target == 0 || header.command_id == 0xFFFF)
454 return ResultCode(0xE7E3FFFF);
455
456 return RESULT_SUCCESS;
457 }
458
459 auto thread = Kernel::GetCurrentThread();
460
461 // Find the first object that is acquirable in the provided list of objects
462 auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) {
463 return !object->ShouldWait(thread);
464 });
465
466 if (itr != objects.end()) {
467 // We found a ready object, acquire it and set the result value
468 Kernel::WaitObject* object = itr->get();
469 object->Acquire(thread);
470 *index = std::distance(objects.begin(), itr);
471
472 if (object->GetHandleType() == Kernel::HandleType::ServerSession) {
473 auto server_session = static_cast<Kernel::ServerSession*>(object);
474 if (server_session->parent->client == nullptr)
475 return Kernel::ERR_SESSION_CLOSED_BY_REMOTE;
476
477 // TODO(Subv): Perform IPC translation from the ServerSession to the current thread.
478 }
479 return RESULT_SUCCESS;
480 }
481
482 // No objects were ready to be acquired, prepare to suspend the thread.
483
484 // TODO(Subv): Perform IPC translation upon wakeup.
485
486 // Put the thread to sleep
487 thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
488
489 // Add the thread to each of the objects' waiting threads.
490 for (size_t i = 0; i < objects.size(); ++i) {
491 Kernel::WaitObject* object = objects[i].get();
492 object->AddWaitingThread(thread);
493 }
494
495 thread->wait_objects = std::move(objects);
496
497 Core::System::GetInstance().PrepareReschedule();
498
499 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
500 // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error.
501 // By default the index is set to -1.
502 thread->wait_set_output = true;
503 *index = -1;
504 return RESULT_SUCCESS;
505}
506
400/// Create an address arbiter (to allocate access to shared resources) 507/// Create an address arbiter (to allocate access to shared resources)
401static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) { 508static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) {
402 using Kernel::AddressArbiter; 509 using Kernel::AddressArbiter;
@@ -1128,7 +1235,7 @@ static const FunctionDef SVC_Table[] = {
1128 {0x4C, nullptr, "ReplyAndReceive2"}, 1235 {0x4C, nullptr, "ReplyAndReceive2"},
1129 {0x4D, nullptr, "ReplyAndReceive3"}, 1236 {0x4D, nullptr, "ReplyAndReceive3"},
1130 {0x4E, nullptr, "ReplyAndReceive4"}, 1237 {0x4E, nullptr, "ReplyAndReceive4"},
1131 {0x4F, nullptr, "ReplyAndReceive"}, 1238 {0x4F, HLE::Wrap<ReplyAndReceive>, "ReplyAndReceive"},
1132 {0x50, nullptr, "BindInterrupt"}, 1239 {0x50, nullptr, "BindInterrupt"},
1133 {0x51, nullptr, "UnbindInterrupt"}, 1240 {0x51, nullptr, "UnbindInterrupt"},
1134 {0x52, nullptr, "InvalidateProcessDataCache"}, 1241 {0x52, nullptr, "InvalidateProcessDataCache"},