diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/function_wrappers.h | 23 | ||||
| -rw-r--r-- | src/core/hle/kernel/client_session.cpp | 10 | ||||
| -rw-r--r-- | src/core/hle/kernel/client_session.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.cpp | 22 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.h | 14 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 111 |
6 files changed, 161 insertions, 23 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 410bb87ea..5e6002f4e 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h | |||
| @@ -16,9 +16,6 @@ namespace HLE { | |||
| 16 | 16 | ||
| 17 | #define PARAM(n) Core::CPU().GetReg(n) | 17 | #define PARAM(n) Core::CPU().GetReg(n) |
| 18 | 18 | ||
| 19 | /// An invalid result code that is meant to be overwritten when a thread resumes from waiting | ||
| 20 | static const ResultCode RESULT_INVALID(0xDEADC0DE); | ||
| 21 | |||
| 22 | /** | 19 | /** |
| 23 | * HLE a function return from the current ARM11 userland process | 20 | * HLE a function return from the current ARM11 userland process |
| 24 | * @param res Result to return | 21 | * @param res Result to return |
| @@ -68,10 +65,18 @@ void Wrap() { | |||
| 68 | (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) | 65 | (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) |
| 69 | .raw; | 66 | .raw; |
| 70 | 67 | ||
| 71 | if (retval != RESULT_INVALID.raw) { | 68 | Core::CPU().SetReg(1, (u32)param_1); |
| 72 | Core::CPU().SetReg(1, (u32)param_1); | 69 | FuncReturn(retval); |
| 73 | FuncReturn(retval); | 70 | } |
| 74 | } | 71 | |
| 72 | template <ResultCode func(s32*, u32*, s32, u32)> | ||
| 73 | void Wrap() { | ||
| 74 | s32 param_1 = 0; | ||
| 75 | u32 retval = | ||
| 76 | func(¶m_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), PARAM(3)).raw; | ||
| 77 | |||
| 78 | Core::CPU().SetReg(1, (u32)param_1); | ||
| 79 | FuncReturn(retval); | ||
| 75 | } | 80 | } |
| 76 | 81 | ||
| 77 | template <ResultCode func(u32, u32, u32, u32, s64)> | 82 | template <ResultCode func(u32, u32, u32, u32, s64)> |
| @@ -92,9 +97,7 @@ template <ResultCode func(u32, s64)> | |||
| 92 | void Wrap() { | 97 | void Wrap() { |
| 93 | s32 retval = func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw; | 98 | s32 retval = func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw; |
| 94 | 99 | ||
| 95 | if (retval != RESULT_INVALID.raw) { | 100 | FuncReturn(retval); |
| 96 | FuncReturn(retval); | ||
| 97 | } | ||
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | template <ResultCode func(MemoryInfo*, PageInfo*, u32)> | 103 | template <ResultCode func(MemoryInfo*, PageInfo*, u32)> |
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index fef97af1f..646a5cc64 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/hle_ipc.h" | 9 | #include "core/hle/kernel/hle_ipc.h" |
| 10 | #include "core/hle/kernel/server_session.h" | 10 | #include "core/hle/kernel/server_session.h" |
| 11 | #include "core/hle/kernel/session.h" | 11 | #include "core/hle/kernel/session.h" |
| 12 | #include "core/hle/kernel/thread.h" | ||
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| @@ -27,19 +28,24 @@ ClientSession::~ClientSession() { | |||
| 27 | 28 | ||
| 28 | // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set | 29 | // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set |
| 29 | // their WaitSynchronization result to 0xC920181A. | 30 | // their WaitSynchronization result to 0xC920181A. |
| 31 | |||
| 32 | // Clean up the list of client threads with pending requests, they are unneeded now that the | ||
| 33 | // client endpoint is closed. | ||
| 34 | server->pending_requesting_threads.clear(); | ||
| 35 | server->currently_handling = nullptr; | ||
| 30 | } | 36 | } |
| 31 | 37 | ||
| 32 | parent->client = nullptr; | 38 | parent->client = nullptr; |
| 33 | } | 39 | } |
| 34 | 40 | ||
| 35 | ResultCode ClientSession::SendSyncRequest() { | 41 | ResultCode ClientSession::SendSyncRequest(SharedPtr<Thread> thread) { |
| 36 | // Keep ServerSession alive until we're done working with it. | 42 | // Keep ServerSession alive until we're done working with it. |
| 37 | SharedPtr<ServerSession> server = parent->server; | 43 | SharedPtr<ServerSession> server = parent->server; |
| 38 | if (server == nullptr) | 44 | if (server == nullptr) |
| 39 | return ERR_SESSION_CLOSED_BY_REMOTE; | 45 | return ERR_SESSION_CLOSED_BY_REMOTE; |
| 40 | 46 | ||
| 41 | // Signal the server session that new data is available | 47 | // Signal the server session that new data is available |
| 42 | return server->HandleSyncRequest(); | 48 | return server->HandleSyncRequest(std::move(thread)); |
| 43 | } | 49 | } |
| 44 | 50 | ||
| 45 | } // namespace | 51 | } // namespace |
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 2de379c09..daf521529 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -14,6 +14,7 @@ namespace Kernel { | |||
| 14 | 14 | ||
| 15 | class ServerSession; | 15 | class ServerSession; |
| 16 | class Session; | 16 | class Session; |
| 17 | class Thread; | ||
| 17 | 18 | ||
| 18 | class ClientSession final : public Object { | 19 | class ClientSession final : public Object { |
| 19 | public: | 20 | public: |
| @@ -34,9 +35,10 @@ public: | |||
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| 36 | * Sends an SyncRequest from the current emulated thread. | 37 | * Sends an SyncRequest from the current emulated thread. |
| 38 | * @param thread Thread that initiated the request. | ||
| 37 | * @return ResultCode of the operation. | 39 | * @return ResultCode of the operation. |
| 38 | */ | 40 | */ |
| 39 | ResultCode SendSyncRequest(); | 41 | ResultCode SendSyncRequest(SharedPtr<Thread> thread); |
| 40 | 42 | ||
| 41 | std::string name; ///< Name of client port (optional) | 43 | std::string name; ///< Name of client port (optional) |
| 42 | 44 | ||
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index d197137c3..337896abf 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -32,22 +32,29 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) { | |||
| 32 | SharedPtr<ServerSession> server_session(new ServerSession); | 32 | SharedPtr<ServerSession> server_session(new ServerSession); |
| 33 | 33 | ||
| 34 | server_session->name = std::move(name); | 34 | server_session->name = std::move(name); |
| 35 | server_session->signaled = false; | ||
| 36 | server_session->parent = nullptr; | 35 | server_session->parent = nullptr; |
| 37 | 36 | ||
| 38 | return MakeResult(std::move(server_session)); | 37 | return MakeResult(std::move(server_session)); |
| 39 | } | 38 | } |
| 40 | 39 | ||
| 41 | bool ServerSession::ShouldWait(Thread* thread) const { | 40 | bool ServerSession::ShouldWait(Thread* thread) const { |
| 42 | return !signaled; | 41 | // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. |
| 42 | if (parent->client == nullptr) | ||
| 43 | return false; | ||
| 44 | // Wait if we have no pending requests, or if we're currently handling a request. | ||
| 45 | return pending_requesting_threads.empty() || currently_handling != nullptr; | ||
| 43 | } | 46 | } |
| 44 | 47 | ||
| 45 | void ServerSession::Acquire(Thread* thread) { | 48 | void ServerSession::Acquire(Thread* thread) { |
| 46 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 49 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 47 | signaled = false; | 50 | // We are now handling a request, pop it from the stack. |
| 51 | // TODO(Subv): What happens if the client endpoint is closed before any requests are made? | ||
| 52 | ASSERT(!pending_requesting_threads.empty()); | ||
| 53 | currently_handling = pending_requesting_threads.back(); | ||
| 54 | pending_requesting_threads.pop_back(); | ||
| 48 | } | 55 | } |
| 49 | 56 | ||
| 50 | ResultCode ServerSession::HandleSyncRequest() { | 57 | ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { |
| 51 | // The ServerSession received a sync request, this means that there's new data available | 58 | // The ServerSession received a sync request, this means that there's new data available |
| 52 | // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or | 59 | // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or |
| 53 | // similar. | 60 | // similar. |
| @@ -60,11 +67,14 @@ ResultCode ServerSession::HandleSyncRequest() { | |||
| 60 | return result; | 67 | return result; |
| 61 | hle_handler->HandleSyncRequest(SharedPtr<ServerSession>(this)); | 68 | hle_handler->HandleSyncRequest(SharedPtr<ServerSession>(this)); |
| 62 | // TODO(Subv): Translate the response command buffer. | 69 | // TODO(Subv): Translate the response command buffer. |
| 70 | } else { | ||
| 71 | // Add the thread to the list of threads that have issued a sync request with this | ||
| 72 | // server. | ||
| 73 | pending_requesting_threads.push_back(std::move(thread)); | ||
| 63 | } | 74 | } |
| 64 | 75 | ||
| 65 | // If this ServerSession does not have an HLE implementation, just wake up the threads waiting | 76 | // If this ServerSession does not have an HLE implementation, just wake up the threads waiting |
| 66 | // on it. | 77 | // on it. |
| 67 | signaled = true; | ||
| 68 | WakeupAllWaitingThreads(); | 78 | WakeupAllWaitingThreads(); |
| 69 | return RESULT_SUCCESS; | 79 | return RESULT_SUCCESS; |
| 70 | } | 80 | } |
| @@ -90,4 +100,4 @@ ResultCode TranslateHLERequest(ServerSession* server_session) { | |||
| 90 | // TODO(Subv): Implement this function once multiple concurrent processes are supported. | 100 | // TODO(Subv): Implement this function once multiple concurrent processes are supported. |
| 91 | return RESULT_SUCCESS; | 101 | return RESULT_SUCCESS; |
| 92 | } | 102 | } |
| 93 | } | 103 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 5365605da..f4360ddf3 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -67,20 +67,30 @@ public: | |||
| 67 | 67 | ||
| 68 | /** | 68 | /** |
| 69 | * Handle a sync request from the emulated application. | 69 | * Handle a sync request from the emulated application. |
| 70 | * @param thread Thread that initiated the request. | ||
| 70 | * @returns ResultCode from the operation. | 71 | * @returns ResultCode from the operation. |
| 71 | */ | 72 | */ |
| 72 | ResultCode HandleSyncRequest(); | 73 | ResultCode HandleSyncRequest(SharedPtr<Thread> thread); |
| 73 | 74 | ||
| 74 | bool ShouldWait(Thread* thread) const override; | 75 | bool ShouldWait(Thread* thread) const override; |
| 75 | 76 | ||
| 76 | void Acquire(Thread* thread) override; | 77 | void Acquire(Thread* thread) override; |
| 77 | 78 | ||
| 78 | std::string name; ///< The name of this session (optional) | 79 | std::string name; ///< The name of this session (optional) |
| 79 | bool signaled; ///< Whether there's new data available to this ServerSession | ||
| 80 | std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. | 80 | std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. |
| 81 | std::shared_ptr<SessionRequestHandler> | 81 | std::shared_ptr<SessionRequestHandler> |
| 82 | hle_handler; ///< This session's HLE request handler (optional) | 82 | hle_handler; ///< This session's HLE request handler (optional) |
| 83 | 83 | ||
| 84 | /// List of threads that are pending a response after a sync request. This list is processed in | ||
| 85 | /// a LIFO manner, thus, the last request will be dispatched first. | ||
| 86 | /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test. | ||
| 87 | std::vector<SharedPtr<Thread>> pending_requesting_threads; | ||
| 88 | |||
| 89 | /// Thread whose request is currently being handled. A request is considered "handled" when a | ||
| 90 | /// response is sent via svcReplyAndReceive. | ||
| 91 | /// TODO(Subv): Find a better name for this. | ||
| 92 | SharedPtr<Thread> currently_handling; | ||
| 93 | |||
| 84 | private: | 94 | private: |
| 85 | ServerSession(); | 95 | ServerSession(); |
| 86 | ~ServerSession() override; | 96 | ~ServerSession() override; |
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c05401143..e4b803046 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" |
| @@ -237,7 +238,7 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { | |||
| 237 | 238 | ||
| 238 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server | 239 | // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server |
| 239 | // responds and cause a reschedule. | 240 | // responds and cause a reschedule. |
| 240 | return session->SendSyncRequest(); | 241 | return session->SendSyncRequest(Kernel::GetCurrentThread()); |
| 241 | } | 242 | } |
| 242 | 243 | ||
| 243 | /// Close a handle | 244 | /// Close a handle |
| @@ -398,6 +399,112 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha | |||
| 398 | } | 399 | } |
| 399 | } | 400 | } |
| 400 | 401 | ||
| 402 | /// In a single operation, sends a IPC reply and waits for a new request. | ||
| 403 | static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count, | ||
| 404 | Kernel::Handle reply_target) { | ||
| 405 | // 'handles' has to be a valid pointer even if 'handle_count' is 0. | ||
| 406 | if (handles == nullptr) | ||
| 407 | return Kernel::ERR_INVALID_POINTER; | ||
| 408 | |||
| 409 | // Check if 'handle_count' is invalid | ||
| 410 | if (handle_count < 0) | ||
| 411 | return Kernel::ERR_OUT_OF_RANGE; | ||
| 412 | |||
| 413 | using ObjectPtr = SharedPtr<Kernel::WaitObject>; | ||
| 414 | std::vector<ObjectPtr> objects(handle_count); | ||
| 415 | |||
| 416 | for (int i = 0; i < handle_count; ++i) { | ||
| 417 | auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); | ||
| 418 | if (object == nullptr) | ||
| 419 | return ERR_INVALID_HANDLE; | ||
| 420 | objects[i] = object; | ||
| 421 | } | ||
| 422 | |||
| 423 | // We are also sending a command reply. | ||
| 424 | // Do not send a reply if the command id in the command buffer is 0xFFFF. | ||
| 425 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 426 | IPC::Header header{cmd_buff[0]}; | ||
| 427 | if (reply_target != 0 && header.command_id != 0xFFFF) { | ||
| 428 | auto session = Kernel::g_handle_table.Get<Kernel::ServerSession>(reply_target); | ||
| 429 | if (session == nullptr) | ||
| 430 | return ERR_INVALID_HANDLE; | ||
| 431 | |||
| 432 | auto request_thread = std::move(session->currently_handling); | ||
| 433 | |||
| 434 | // Mark the request as "handled". | ||
| 435 | session->currently_handling = nullptr; | ||
| 436 | |||
| 437 | // Error out if there's no request thread or the session was closed. | ||
| 438 | // TODO(Subv): Is the same error code (ClosedByRemote) returned for both of these cases? | ||
| 439 | if (request_thread == nullptr || session->parent->client == nullptr) { | ||
| 440 | *index = -1; | ||
| 441 | return Kernel::ERR_SESSION_CLOSED_BY_REMOTE; | ||
| 442 | } | ||
| 443 | |||
| 444 | // TODO(Subv): Perform IPC translation from the current thread to request_thread. | ||
| 445 | |||
| 446 | // Note: The scheduler is not invoked here. | ||
| 447 | request_thread->ResumeFromWait(); | ||
| 448 | } | ||
| 449 | |||
| 450 | if (handle_count == 0) { | ||
| 451 | *index = 0; | ||
| 452 | // The kernel uses this value as a placeholder for the real error, and returns it when we | ||
| 453 | // pass no handles and do not perform any reply. | ||
| 454 | if (reply_target == 0 || header.command_id == 0xFFFF) | ||
| 455 | return ResultCode(0xE7E3FFFF); | ||
| 456 | |||
| 457 | return RESULT_SUCCESS; | ||
| 458 | } | ||
| 459 | |||
| 460 | auto thread = Kernel::GetCurrentThread(); | ||
| 461 | |||
| 462 | // Find the first object that is acquirable in the provided list of objects | ||
| 463 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { | ||
| 464 | return !object->ShouldWait(thread); | ||
| 465 | }); | ||
| 466 | |||
| 467 | if (itr != objects.end()) { | ||
| 468 | // We found a ready object, acquire it and set the result value | ||
| 469 | Kernel::WaitObject* object = itr->get(); | ||
| 470 | object->Acquire(thread); | ||
| 471 | *index = std::distance(objects.begin(), itr); | ||
| 472 | |||
| 473 | if (object->GetHandleType() == Kernel::HandleType::ServerSession) { | ||
| 474 | auto server_session = static_cast<Kernel::ServerSession*>(object); | ||
| 475 | if (server_session->parent->client == nullptr) | ||
| 476 | return Kernel::ERR_SESSION_CLOSED_BY_REMOTE; | ||
| 477 | |||
| 478 | // TODO(Subv): Perform IPC translation from the ServerSession to the current thread. | ||
| 479 | } | ||
| 480 | return RESULT_SUCCESS; | ||
| 481 | } | ||
| 482 | |||
| 483 | // No objects were ready to be acquired, prepare to suspend the thread. | ||
| 484 | |||
| 485 | // TODO(Subv): Perform IPC translation upon wakeup. | ||
| 486 | |||
| 487 | // Put the thread to sleep | ||
| 488 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 489 | |||
| 490 | // Add the thread to each of the objects' waiting threads. | ||
| 491 | for (size_t i = 0; i < objects.size(); ++i) { | ||
| 492 | Kernel::WaitObject* object = objects[i].get(); | ||
| 493 | object->AddWaitingThread(thread); | ||
| 494 | } | ||
| 495 | |||
| 496 | thread->wait_objects = std::move(objects); | ||
| 497 | |||
| 498 | Core::System::GetInstance().PrepareReschedule(); | ||
| 499 | |||
| 500 | // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a | ||
| 501 | // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. | ||
| 502 | // By default the index is set to -1. | ||
| 503 | thread->wait_set_output = true; | ||
| 504 | *index = -1; | ||
| 505 | return RESULT_SUCCESS; | ||
| 506 | } | ||
| 507 | |||
| 401 | /// Create an address arbiter (to allocate access to shared resources) | 508 | /// Create an address arbiter (to allocate access to shared resources) |
| 402 | static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) { | 509 | static ResultCode CreateAddressArbiter(Kernel::Handle* out_handle) { |
| 403 | using Kernel::AddressArbiter; | 510 | using Kernel::AddressArbiter; |
| @@ -1163,7 +1270,7 @@ static const FunctionDef SVC_Table[] = { | |||
| 1163 | {0x4C, nullptr, "ReplyAndReceive2"}, | 1270 | {0x4C, nullptr, "ReplyAndReceive2"}, |
| 1164 | {0x4D, nullptr, "ReplyAndReceive3"}, | 1271 | {0x4D, nullptr, "ReplyAndReceive3"}, |
| 1165 | {0x4E, nullptr, "ReplyAndReceive4"}, | 1272 | {0x4E, nullptr, "ReplyAndReceive4"}, |
| 1166 | {0x4F, nullptr, "ReplyAndReceive"}, | 1273 | {0x4F, HLE::Wrap<ReplyAndReceive>, "ReplyAndReceive"}, |
| 1167 | {0x50, nullptr, "BindInterrupt"}, | 1274 | {0x50, nullptr, "BindInterrupt"}, |
| 1168 | {0x51, nullptr, "UnbindInterrupt"}, | 1275 | {0x51, nullptr, "UnbindInterrupt"}, |
| 1169 | {0x52, nullptr, "InvalidateProcessDataCache"}, | 1276 | {0x52, nullptr, "InvalidateProcessDataCache"}, |