mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #2249 from Subv/sessions_v3
Kernel/IPC: Use Ports and Sessions as the fundamental building block of Inter Process Communication.
This commit is contained in:
		
						commit
						905fc92ce1
					
				
					 25 changed files with 591 additions and 171 deletions
				
			
		|  | @ -8,7 +8,6 @@ | |||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| #include "core/hle/kernel/semaphore.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,6 +38,7 @@ set(SRCS | |||
|             hle/applets/swkbd.cpp | ||||
|             hle/kernel/address_arbiter.cpp | ||||
|             hle/kernel/client_port.cpp | ||||
|             hle/kernel/client_session.cpp | ||||
|             hle/kernel/event.cpp | ||||
|             hle/kernel/kernel.cpp | ||||
|             hle/kernel/memory.cpp | ||||
|  | @ -46,7 +47,7 @@ set(SRCS | |||
|             hle/kernel/resource_limit.cpp | ||||
|             hle/kernel/semaphore.cpp | ||||
|             hle/kernel/server_port.cpp | ||||
|             hle/kernel/session.cpp | ||||
|             hle/kernel/server_session.cpp | ||||
|             hle/kernel/shared_memory.cpp | ||||
|             hle/kernel/thread.cpp | ||||
|             hle/kernel/timer.cpp | ||||
|  | @ -194,12 +195,14 @@ set(HEADERS | |||
|             hle/config_mem.h | ||||
|             hle/function_wrappers.h | ||||
|             hle/hle.h | ||||
|             hle/ipc.h | ||||
|             hle/applets/applet.h | ||||
|             hle/applets/erreula.h | ||||
|             hle/applets/mii_selector.h | ||||
|             hle/applets/swkbd.h | ||||
|             hle/kernel/address_arbiter.h | ||||
|             hle/kernel/client_port.h | ||||
|             hle/kernel/client_session.h | ||||
|             hle/kernel/event.h | ||||
|             hle/kernel/kernel.h | ||||
|             hle/kernel/memory.h | ||||
|  | @ -208,7 +211,7 @@ set(HEADERS | |||
|             hle/kernel/resource_limit.h | ||||
|             hle/kernel/semaphore.h | ||||
|             hle/kernel/server_port.h | ||||
|             hle/kernel/session.h | ||||
|             hle/kernel/server_session.h | ||||
|             hle/kernel/shared_memory.h | ||||
|             hle/kernel/thread.h | ||||
|             hle/kernel/timer.h | ||||
|  |  | |||
|  | @ -1,17 +1,31 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns a pointer to the command buffer in the current thread's TLS | ||||
|  * TODO(Subv): This is not entirely correct, the command buffer should be copied from | ||||
|  * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to | ||||
|  * the service handler process' memory. | ||||
|  * @param offset Optional offset into command buffer | ||||
|  * @return Pointer to command buffer | ||||
|  */ | ||||
| inline u32* GetCommandBuffer(const int offset = 0) { | ||||
|     return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + | ||||
|                                     offset); | ||||
| } | ||||
| } | ||||
| 
 | ||||
| namespace IPC { | ||||
| 
 | ||||
| enum DescriptorType : u32 { | ||||
|  | @ -144,75 +158,3 @@ inline DescriptorType GetDescriptorType(u32 descriptor) { | |||
| } | ||||
| 
 | ||||
| } // namespace IPC
 | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns a pointer to the command buffer in the current thread's TLS | ||||
|  * TODO(Subv): This is not entirely correct, the command buffer should be copied from | ||||
|  * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to | ||||
|  * the service handler process' memory. | ||||
|  * @param offset Optional offset into command buffer | ||||
|  * @return Pointer to command buffer | ||||
|  */ | ||||
| inline u32* GetCommandBuffer(const int offset = 0) { | ||||
|     return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + | ||||
|                                     offset); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS | ||||
|  * primitive for communication between different processes, and are used to implement service calls | ||||
|  * to the various system services. | ||||
|  * | ||||
|  * To make a service call, the client must write the command header and parameters to the buffer | ||||
|  * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest | ||||
|  * SVC call with its Session handle. The kernel will read the command header, using it to marshall | ||||
|  * the parameters to the process at the server endpoint of the session. After the server replies to | ||||
|  * the request, the response is marshalled back to the caller's TLS buffer and control is | ||||
|  * transferred back to it. | ||||
|  * | ||||
|  * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC | ||||
|  * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called | ||||
|  * with the session handle, this class's SyncRequest method is called, which should read the TLS | ||||
|  * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, | ||||
|  * no parameter marshalling is done. | ||||
|  * | ||||
|  * In the long term, this should be turned into the full-fledged IPC mechanism implemented by | ||||
|  * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as | ||||
|  * opposed to HLE simulations. | ||||
|  */ | ||||
| class Session : public WaitObject { | ||||
| public: | ||||
|     Session(); | ||||
|     ~Session() override; | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "Session"; | ||||
|     } | ||||
| 
 | ||||
|     static const HandleType HANDLE_TYPE = HandleType::Session; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls | ||||
|      * aren't supported yet. | ||||
|      */ | ||||
|     virtual ResultVal<bool> SyncRequest() = 0; | ||||
| 
 | ||||
|     // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object
 | ||||
|     // passed into WaitSynchronization. Figure out the meaning of them.
 | ||||
| 
 | ||||
|     bool ShouldWait() override { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void Acquire() override { | ||||
|         ASSERT_MSG(!ShouldWait(), "object unavailable!"); | ||||
|     } | ||||
| }; | ||||
| } | ||||
|  | @ -4,12 +4,44 @@ | |||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/server_port.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ClientPort::ClientPort() {} | ||||
| ClientPort::~ClientPort() {} | ||||
| 
 | ||||
| ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { | ||||
|     // Note: Threads do not wait for the server endpoint to call
 | ||||
|     // AcceptSession before returning from this call.
 | ||||
| 
 | ||||
|     if (active_sessions >= max_sessions) { | ||||
|         // TODO(Subv): Return an error code in this situation after session disconnection is
 | ||||
|         // implemented.
 | ||||
|         /*return ResultCode(ErrorDescription::MaxConnectionsReached,
 | ||||
|                           ErrorModule::OS, ErrorSummary::WouldBlock, | ||||
|                           ErrorLevel::Temporary);*/ | ||||
|     } | ||||
|     active_sessions++; | ||||
| 
 | ||||
|     // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
 | ||||
|     auto sessions = | ||||
|         ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler); | ||||
|     auto client_session = std::get<SharedPtr<ClientSession>>(sessions); | ||||
|     auto server_session = std::get<SharedPtr<ServerSession>>(sessions); | ||||
| 
 | ||||
|     if (server_port->hle_handler) | ||||
|         server_port->hle_handler->ClientConnected(server_session); | ||||
| 
 | ||||
|     server_port->pending_sessions.push_back(std::move(server_session)); | ||||
| 
 | ||||
|     // Wake the threads waiting on the ServerPort
 | ||||
|     server_port->WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
|  | @ -11,8 +11,9 @@ | |||
| namespace Kernel { | ||||
| 
 | ||||
| class ServerPort; | ||||
| class ClientSession; | ||||
| 
 | ||||
| class ClientPort : public Object { | ||||
| class ClientPort final : public Object { | ||||
| public: | ||||
|     friend class ServerPort; | ||||
|     std::string GetTypeName() const override { | ||||
|  | @ -27,12 +28,20 @@ public: | |||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a new Session pair, adds the created ServerSession to the associated ServerPort's | ||||
|      * list of pending sessions, and signals the ServerPort, causing any threads | ||||
|      * waiting on it to awake. | ||||
|      * @returns ClientSession The client endpoint of the created Session pair, or error code. | ||||
|      */ | ||||
|     ResultVal<SharedPtr<ClientSession>> Connect(); | ||||
| 
 | ||||
|     SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port.
 | ||||
|     u32 max_sessions;    ///< Maximum number of simultaneous sessions the port can have
 | ||||
|     u32 active_sessions; ///< Number of currently open sessions to this port
 | ||||
|     std::string name;    ///< Name of client port (optional)
 | ||||
| 
 | ||||
| protected: | ||||
| private: | ||||
|     ClientPort(); | ||||
|     ~ClientPort() override; | ||||
| }; | ||||
|  |  | |||
							
								
								
									
										40
									
								
								src/core/hle/kernel/client_session.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/core/hle/kernel/client_session.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ClientSession::ClientSession() = default; | ||||
| ClientSession::~ClientSession() { | ||||
|     // This destructor will be called automatically when the last ClientSession handle is closed by
 | ||||
|     // the emulated application.
 | ||||
| 
 | ||||
|     if (server_session->hle_handler) | ||||
|         server_session->hle_handler->ClientDisconnected(server_session); | ||||
| 
 | ||||
|     // TODO(Subv): If the session is still open, set the connection status to 2 (Closed by client),
 | ||||
|     // wake up all the ServerSession's waiting threads and set the WaitSynchronization result to
 | ||||
|     // 0xC920181A.
 | ||||
| } | ||||
| 
 | ||||
| ResultVal<SharedPtr<ClientSession>> ClientSession::Create(ServerSession* server_session, | ||||
|                                                           std::string name) { | ||||
|     SharedPtr<ClientSession> client_session(new ClientSession); | ||||
| 
 | ||||
|     client_session->name = std::move(name); | ||||
|     client_session->server_session = server_session; | ||||
|     client_session->session_status = SessionStatus::Open; | ||||
|     return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ClientSession::SendSyncRequest() { | ||||
|     // Signal the server session that new data is available
 | ||||
|     return server_session->HandleSyncRequest(); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										65
									
								
								src/core/hle/kernel/client_session.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/core/hle/kernel/client_session.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class ServerSession; | ||||
| 
 | ||||
| enum class SessionStatus { | ||||
|     Open = 1, | ||||
|     ClosedByClient = 2, | ||||
|     ClosedBYServer = 3, | ||||
| }; | ||||
| 
 | ||||
| class ClientSession final : public Object { | ||||
| public: | ||||
|     friend class ServerSession; | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "ClientSession"; | ||||
|     } | ||||
| 
 | ||||
|     std::string GetName() const override { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     static const HandleType HANDLE_TYPE = HandleType::ClientSession; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sends an SyncRequest from the current emulated thread. | ||||
|      * @return ResultCode of the operation. | ||||
|      */ | ||||
|     ResultCode SendSyncRequest(); | ||||
| 
 | ||||
|     std::string name;              ///< Name of client port (optional)
 | ||||
|     ServerSession* server_session; ///< The server session associated with this client session.
 | ||||
|     SessionStatus session_status;  ///< The session's current status.
 | ||||
| 
 | ||||
| private: | ||||
|     ClientSession(); | ||||
|     ~ClientSession() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a client session. | ||||
|      * @param server_session The server session associated with this client session | ||||
|      * @param name Optional name of client session | ||||
|      * @return The created client session | ||||
|      */ | ||||
|     static ResultVal<SharedPtr<ClientSession>> Create(ServerSession* server_session, | ||||
|                                                       std::string name = "Unknown"); | ||||
| }; | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -31,22 +31,21 @@ enum KernelHandle : Handle { | |||
| }; | ||||
| 
 | ||||
| enum class HandleType : u32 { | ||||
|     Unknown = 0, | ||||
| 
 | ||||
|     Session = 2, | ||||
|     Event = 3, | ||||
|     Mutex = 4, | ||||
|     SharedMemory = 5, | ||||
|     Redirection = 6, | ||||
|     Thread = 7, | ||||
|     Process = 8, | ||||
|     AddressArbiter = 9, | ||||
|     Semaphore = 10, | ||||
|     Timer = 11, | ||||
|     ResourceLimit = 12, | ||||
|     CodeSet = 13, | ||||
|     ClientPort = 14, | ||||
|     ServerPort = 15, | ||||
|     Unknown, | ||||
|     Event, | ||||
|     Mutex, | ||||
|     SharedMemory, | ||||
|     Thread, | ||||
|     Process, | ||||
|     AddressArbiter, | ||||
|     Semaphore, | ||||
|     Timer, | ||||
|     ResourceLimit, | ||||
|     CodeSet, | ||||
|     ClientPort, | ||||
|     ServerPort, | ||||
|     ClientSession, | ||||
|     ServerSession, | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
|  | @ -82,23 +81,23 @@ public: | |||
|      */ | ||||
|     bool IsWaitable() const { | ||||
|         switch (GetHandleType()) { | ||||
|         case HandleType::Session: | ||||
|         case HandleType::ServerPort: | ||||
|         case HandleType::Event: | ||||
|         case HandleType::Mutex: | ||||
|         case HandleType::Thread: | ||||
|         case HandleType::Semaphore: | ||||
|         case HandleType::Timer: | ||||
|         case HandleType::ServerPort: | ||||
|         case HandleType::ServerSession: | ||||
|             return true; | ||||
| 
 | ||||
|         case HandleType::Unknown: | ||||
|         case HandleType::SharedMemory: | ||||
|         case HandleType::Redirection: | ||||
|         case HandleType::Process: | ||||
|         case HandleType::AddressArbiter: | ||||
|         case HandleType::ResourceLimit: | ||||
|         case HandleType::CodeSet: | ||||
|         case HandleType::ClientPort: | ||||
|         case HandleType::ClientSession: | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -24,12 +24,14 @@ void ServerPort::Acquire() { | |||
| } | ||||
| 
 | ||||
| std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( | ||||
|     u32 max_sessions, std::string name) { | ||||
|     u32 max_sessions, std::string name, | ||||
|     std::shared_ptr<Service::SessionRequestHandler> hle_handler) { | ||||
| 
 | ||||
|     SharedPtr<ServerPort> server_port(new ServerPort); | ||||
|     SharedPtr<ClientPort> client_port(new ClientPort); | ||||
| 
 | ||||
|     server_port->name = name + "_Server"; | ||||
|     server_port->hle_handler = std::move(hle_handler); | ||||
|     client_port->name = name + "_Client"; | ||||
|     client_port->server_port = server_port; | ||||
|     client_port->max_sessions = max_sessions; | ||||
|  |  | |||
|  | @ -4,11 +4,16 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <tuple> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| class SessionRequestHandler; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class ClientPort; | ||||
|  | @ -19,10 +24,13 @@ public: | |||
|      * Creates a pair of ServerPort and an associated ClientPort. | ||||
|      * @param max_sessions Maximum number of sessions to the port | ||||
|      * @param name Optional name of the ports | ||||
|      * @param hle_handler Optional HLE handler template for the port, | ||||
|      * ServerSessions crated from this port will inherit a reference to this handler. | ||||
|      * @return The created port tuple | ||||
|      */ | ||||
|     static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( | ||||
|         u32 max_sessions, std::string name = "UnknownPort"); | ||||
|         u32 max_sessions, std::string name = "UnknownPort", | ||||
|         std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "ServerPort"; | ||||
|  | @ -41,6 +49,10 @@ public: | |||
|     std::vector<SharedPtr<WaitObject>> | ||||
|         pending_sessions; ///< ServerSessions waiting to be accepted by the port
 | ||||
| 
 | ||||
|     /// This session's HLE request handler template (optional)
 | ||||
|     /// ServerSessions created from this port inherit a reference to this handler.
 | ||||
|     std::shared_ptr<Service::SessionRequestHandler> hle_handler; | ||||
| 
 | ||||
|     bool ShouldWait() override; | ||||
|     void Acquire() override; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										79
									
								
								src/core/hle/kernel/server_session.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/core/hle/kernel/server_session.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ServerSession::ServerSession() = default; | ||||
| ServerSession::~ServerSession() { | ||||
|     // This destructor will be called automatically when the last ServerSession handle is closed by
 | ||||
|     // the emulated application.
 | ||||
|     // TODO(Subv): Reduce the ClientPort's connection count,
 | ||||
|     // if the session is still open, set the connection status to 3 (Closed by server),
 | ||||
| } | ||||
| 
 | ||||
| ResultVal<SharedPtr<ServerSession>> ServerSession::Create( | ||||
|     std::string name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { | ||||
|     SharedPtr<ServerSession> server_session(new ServerSession); | ||||
| 
 | ||||
|     server_session->name = std::move(name); | ||||
|     server_session->signaled = false; | ||||
|     server_session->hle_handler = std::move(hle_handler); | ||||
| 
 | ||||
|     return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); | ||||
| } | ||||
| 
 | ||||
| bool ServerSession::ShouldWait() { | ||||
|     return !signaled; | ||||
| } | ||||
| 
 | ||||
| void ServerSession::Acquire() { | ||||
|     ASSERT_MSG(!ShouldWait(), "object unavailable!"); | ||||
|     signaled = false; | ||||
| } | ||||
| 
 | ||||
| ResultCode ServerSession::HandleSyncRequest() { | ||||
|     // The ServerSession received a sync request, this means that there's new data available
 | ||||
|     // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
 | ||||
|     // similar.
 | ||||
| 
 | ||||
|     // If this ServerSession has an associated HLE handler, forward the request to it.
 | ||||
|     if (hle_handler != nullptr) { | ||||
|         // Attempt to translate the incoming request's command buffer.
 | ||||
|         ResultCode result = TranslateHLERequest(this); | ||||
|         if (result.IsError()) | ||||
|             return result; | ||||
|         hle_handler->HandleSyncRequest(SharedPtr<ServerSession>(this)); | ||||
|         // TODO(Subv): Translate the response command buffer.
 | ||||
|     } | ||||
| 
 | ||||
|     // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
 | ||||
|     // on it.
 | ||||
|     signaled = true; | ||||
|     WakeupAllWaitingThreads(); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ServerSession::SessionPair ServerSession::CreateSessionPair( | ||||
|     const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { | ||||
|     auto server_session = | ||||
|         ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom(); | ||||
|     // We keep a non-owning pointer to the ServerSession in the ClientSession because we don't want
 | ||||
|     // to prevent the ServerSession's destructor from being called when the emulated
 | ||||
|     // application closes the last ServerSession handle.
 | ||||
|     auto client_session = ClientSession::Create(server_session.get(), name + "_Client").MoveFrom(); | ||||
| 
 | ||||
|     return std::make_tuple(std::move(server_session), std::move(client_session)); | ||||
| } | ||||
| 
 | ||||
| ResultCode TranslateHLERequest(ServerSession* server_session) { | ||||
|     // TODO(Subv): Implement this function once multiple concurrent processes are supported.
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| } | ||||
							
								
								
									
										94
									
								
								src/core/hle/kernel/server_session.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/core/hle/kernel/server_session.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class ClientSession; | ||||
| 
 | ||||
| /**
 | ||||
|  * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS | ||||
|  * primitive for communication between different processes, and are used to implement service calls | ||||
|  * to the various system services. | ||||
|  * | ||||
|  * To make a service call, the client must write the command header and parameters to the buffer | ||||
|  * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest | ||||
|  * SVC call with its ClientSession handle. The kernel will read the command header, using it to | ||||
|  * marshall the parameters to the process at the server endpoint of the session. | ||||
|  * After the server replies to the request, the response is marshalled back to the caller's | ||||
|  * TLS buffer and control is transferred back to it. | ||||
|  */ | ||||
| class ServerSession final : public WaitObject { | ||||
| public: | ||||
|     std::string GetTypeName() const override { | ||||
|         return "ServerSession"; | ||||
|     } | ||||
| 
 | ||||
|     static const HandleType HANDLE_TYPE = HandleType::ServerSession; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a pair of ServerSession and an associated ClientSession. | ||||
|      * @param name Optional name of the ports | ||||
|      * @return The created session tuple | ||||
|      */ | ||||
|     static SessionPair CreateSessionPair( | ||||
|         const std::string& name = "Unknown", | ||||
|         std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Handle a sync request from the emulated application. | ||||
|      * @returns ResultCode from the operation. | ||||
|      */ | ||||
|     ResultCode HandleSyncRequest(); | ||||
| 
 | ||||
|     bool ShouldWait() override; | ||||
| 
 | ||||
|     void Acquire() override; | ||||
| 
 | ||||
|     std::string name; ///< The name of this session (optional)
 | ||||
|     bool signaled;    ///< Whether there's new data available to this ServerSession
 | ||||
|     std::shared_ptr<Service::SessionRequestHandler> | ||||
|         hle_handler; ///< This session's HLE request handler (optional)
 | ||||
| 
 | ||||
| private: | ||||
|     ServerSession(); | ||||
|     ~ServerSession() override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a server session. The server session can have an optional HLE handler, | ||||
|      * which will be invoked to handle the IPC requests that this session receives. | ||||
|      * @param name Optional name of the server session. | ||||
|      * @param hle_handler Optional HLE handler for this server session. | ||||
|      * @return The created server session | ||||
|      */ | ||||
|     static ResultVal<SharedPtr<ServerSession>> Create( | ||||
|         std::string name = "Unknown", | ||||
|         std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Performs command buffer translation for an HLE IPC request. | ||||
|  * The command buffer from the ServerSession thread's TLS is copied into a | ||||
|  * buffer and all descriptors in the buffer are processed. | ||||
|  * TODO(Subv): Implement this function, currently we do not support multiple processes running at | ||||
|  * once, but once that is implemented we'll need to properly translate all descriptors | ||||
|  * in the command buffer. | ||||
|  */ | ||||
| ResultCode TranslateHLERequest(ServerSession* server_session); | ||||
| } | ||||
|  | @ -18,6 +18,7 @@ enum class ErrorDescription : u32 { | |||
|     Success = 0, | ||||
|     WrongPermission = 46, | ||||
|     OS_InvalidBufferDescriptor = 48, | ||||
|     MaxConnectionsReached = 52, | ||||
|     WrongAddress = 53, | ||||
|     FS_ArchiveNotMounted = 101, | ||||
|     FS_FileNotFound = 112, | ||||
|  |  | |||
|  | @ -14,6 +14,9 @@ class Interface; | |||
| 
 | ||||
| namespace APT { | ||||
| 
 | ||||
| /// Each APT service can only have up to 2 sessions connected at the same time.
 | ||||
| static const u32 MaxAPTSessions = 2; | ||||
| 
 | ||||
| /// Holds information about the parameters used in Send/Glance/ReceiveParameter
 | ||||
| struct MessageParameter { | ||||
|     u32 sender_id = 0; | ||||
|  |  | |||
|  | @ -102,7 +102,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
|     {0x01050100, nullptr, "IsTitleAllowed"}, | ||||
| }; | ||||
| 
 | ||||
| APT_A_Interface::APT_A_Interface() { | ||||
| APT_A_Interface::APT_A_Interface() : Interface(MaxAPTSessions) { | ||||
|     Register(FunctionTable); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -102,7 +102,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
|     {0x01050100, nullptr, "IsTitleAllowed"}, | ||||
| }; | ||||
| 
 | ||||
| APT_S_Interface::APT_S_Interface() { | ||||
| APT_S_Interface::APT_S_Interface() : Interface(MaxAPTSessions) { | ||||
|     Register(FunctionTable); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -99,7 +99,7 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
|     {0x01020000, CheckNew3DS, "CheckNew3DS"}, | ||||
| }; | ||||
| 
 | ||||
| APT_U_Interface::APT_U_Interface() { | ||||
| APT_U_Interface::APT_U_Interface() : Interface(MaxAPTSessions) { | ||||
|     Register(FunctionTable); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #include "core/file_sys/directory_backend.h" | ||||
| #include "core/file_sys/file_backend.h" | ||||
| #include "core/hle/hle.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/hle/service/fs/fs_user.h" | ||||
|  | @ -93,7 +94,7 @@ File::File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& | |||
| 
 | ||||
| File::~File() {} | ||||
| 
 | ||||
| ResultVal<bool> File::SyncRequest() { | ||||
| void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); | ||||
|     switch (cmd) { | ||||
|  | @ -116,7 +117,7 @@ ResultVal<bool> File::SyncRequest() { | |||
|         ResultVal<size_t> read = backend->Read(offset, data.size(), data.data()); | ||||
|         if (read.Failed()) { | ||||
|             cmd_buff[1] = read.Code().raw; | ||||
|             return read.Code(); | ||||
|             return; | ||||
|         } | ||||
|         Memory::WriteBlock(address, data.data(), *read); | ||||
|         cmd_buff[2] = static_cast<u32>(*read); | ||||
|  | @ -137,7 +138,7 @@ ResultVal<bool> File::SyncRequest() { | |||
|         ResultVal<size_t> written = backend->Write(offset, data.size(), flush != 0, data.data()); | ||||
|         if (written.Failed()) { | ||||
|             cmd_buff[1] = written.Code().raw; | ||||
|             return written.Code(); | ||||
|             return; | ||||
|         } | ||||
|         cmd_buff[2] = static_cast<u32>(*written); | ||||
|         break; | ||||
|  | @ -173,7 +174,11 @@ ResultVal<bool> File::SyncRequest() { | |||
| 
 | ||||
|     case FileCommand::OpenLinkFile: { | ||||
|         LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(this).ValueOr(INVALID_HANDLE); | ||||
|         auto sessions = Kernel::ServerSession::CreateSessionPair(GetName(), shared_from_this()); | ||||
|         ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); | ||||
|         cmd_buff[3] = Kernel::g_handle_table | ||||
|                           .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) | ||||
|                           .ValueOr(INVALID_HANDLE); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|  | @ -194,10 +199,9 @@ ResultVal<bool> File::SyncRequest() { | |||
|         LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||||
|         ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||||
|         cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
 | ||||
|         return error; | ||||
|         return; | ||||
|     } | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||
|     return MakeResult<bool>(false); | ||||
| } | ||||
| 
 | ||||
| Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, | ||||
|  | @ -206,11 +210,10 @@ Directory::Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, | |||
| 
 | ||||
| Directory::~Directory() {} | ||||
| 
 | ||||
| ResultVal<bool> Directory::SyncRequest() { | ||||
| void Directory::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); | ||||
|     switch (cmd) { | ||||
| 
 | ||||
|     // Read from directory...
 | ||||
|     case DirectoryCommand::Read: { | ||||
|         u32 count = cmd_buff[1]; | ||||
|  | @ -237,10 +240,9 @@ ResultVal<bool> Directory::SyncRequest() { | |||
|         LOG_ERROR(Service_FS, "Unknown command=0x%08X!", cmd); | ||||
|         ResultCode error = UnimplementedFunction(ErrorModule::FS); | ||||
|         cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
 | ||||
|         return MakeResult<bool>(false); | ||||
|         return; | ||||
|     } | ||||
|     cmd_buff[1] = RESULT_SUCCESS.raw; // No error
 | ||||
|     return MakeResult<bool>(false); | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -307,9 +309,9 @@ ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factor | |||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, | ||||
|                                                        const FileSys::Path& path, | ||||
|                                                        const FileSys::Mode mode) { | ||||
| ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, | ||||
|                                                      const FileSys::Path& path, | ||||
|                                                      const FileSys::Mode mode) { | ||||
|     ArchiveBackend* archive = GetArchive(archive_handle); | ||||
|     if (archive == nullptr) | ||||
|         return ERR_INVALID_ARCHIVE_HANDLE; | ||||
|  | @ -318,8 +320,8 @@ ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_han | |||
|     if (backend.Failed()) | ||||
|         return backend.Code(); | ||||
| 
 | ||||
|     auto file = Kernel::SharedPtr<File>(new File(backend.MoveFrom(), path)); | ||||
|     return MakeResult<Kernel::SharedPtr<File>>(std::move(file)); | ||||
|     auto file = std::shared_ptr<File>(new File(backend.MoveFrom(), path)); | ||||
|     return MakeResult<std::shared_ptr<File>>(std::move(file)); | ||||
| } | ||||
| 
 | ||||
| ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { | ||||
|  | @ -398,8 +400,8 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, | |||
|     } | ||||
| } | ||||
| 
 | ||||
| ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | ||||
|                                                                  const FileSys::Path& path) { | ||||
| ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | ||||
|                                                                const FileSys::Path& path) { | ||||
|     ArchiveBackend* archive = GetArchive(archive_handle); | ||||
|     if (archive == nullptr) | ||||
|         return ERR_INVALID_ARCHIVE_HANDLE; | ||||
|  | @ -408,8 +410,8 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a | |||
|     if (backend.Failed()) | ||||
|         return backend.Code(); | ||||
| 
 | ||||
|     auto directory = Kernel::SharedPtr<Directory>(new Directory(backend.MoveFrom(), path)); | ||||
|     return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory)); | ||||
|     auto directory = std::shared_ptr<Directory>(new Directory(backend.MoveFrom(), path)); | ||||
|     return MakeResult<std::shared_ptr<Directory>>(std::move(directory)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) { | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ | |||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
| #include "core/file_sys/archive_backend.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace FileSys { | ||||
|  | @ -43,33 +43,37 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 }; | |||
| 
 | ||||
| typedef u64 ArchiveHandle; | ||||
| 
 | ||||
| class File : public Kernel::Session { | ||||
| class File final : public SessionRequestHandler, public std::enable_shared_from_this<File> { | ||||
| public: | ||||
|     File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path); | ||||
|     ~File(); | ||||
| 
 | ||||
|     std::string GetName() const override { | ||||
|     std::string GetName() const { | ||||
|         return "Path: " + path.DebugStr(); | ||||
|     } | ||||
|     ResultVal<bool> SyncRequest() override; | ||||
| 
 | ||||
|     FileSys::Path path; ///< Path of the file
 | ||||
|     u32 priority;       ///< Priority of the file. TODO(Subv): Find out what this means
 | ||||
|     std::unique_ptr<FileSys::FileBackend> backend; ///< File backend interface
 | ||||
| 
 | ||||
| protected: | ||||
|     void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; | ||||
| }; | ||||
| 
 | ||||
| class Directory : public Kernel::Session { | ||||
| class Directory final : public SessionRequestHandler { | ||||
| public: | ||||
|     Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path); | ||||
|     ~Directory(); | ||||
| 
 | ||||
|     std::string GetName() const override { | ||||
|     std::string GetName() const { | ||||
|         return "Directory: " + path.DebugStr(); | ||||
|     } | ||||
|     ResultVal<bool> SyncRequest() override; | ||||
| 
 | ||||
|     FileSys::Path path;                                 ///< Path of the directory
 | ||||
|     std::unique_ptr<FileSys::DirectoryBackend> backend; ///< File backend interface
 | ||||
| 
 | ||||
| protected: | ||||
|     void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -99,11 +103,11 @@ ResultCode RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factor | |||
|  * @param archive_handle Handle to an open Archive object | ||||
|  * @param path Path to the File inside of the Archive | ||||
|  * @param mode Mode under which to open the File | ||||
|  * @return The opened File object as a Session | ||||
|  * @return The opened File object | ||||
|  */ | ||||
| ResultVal<Kernel::SharedPtr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, | ||||
|                                                        const FileSys::Path& path, | ||||
|                                                        const FileSys::Mode mode); | ||||
| ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, | ||||
|                                                      const FileSys::Path& path, | ||||
|                                                      const FileSys::Mode mode); | ||||
| 
 | ||||
| /**
 | ||||
|  * Delete a File from an Archive | ||||
|  | @ -178,10 +182,10 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, | |||
|  * Open a Directory from an Archive | ||||
|  * @param archive_handle Handle to an open Archive object | ||||
|  * @param path Path to the Directory inside of the Archive | ||||
|  * @return The opened Directory object as a Session | ||||
|  * @return The opened Directory object | ||||
|  */ | ||||
| ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | ||||
|                                                                  const FileSys::Path& path); | ||||
| ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, | ||||
|                                                                const FileSys::Path& path); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get the free space in an Archive | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include "common/logging/log.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/hle/service/fs/fs_user.h" | ||||
|  | @ -17,7 +18,7 @@ | |||
| // Namespace FS_User
 | ||||
| 
 | ||||
| using Kernel::SharedPtr; | ||||
| using Kernel::Session; | ||||
| using Kernel::ServerSession; | ||||
| 
 | ||||
| namespace Service { | ||||
| namespace FS { | ||||
|  | @ -67,10 +68,16 @@ static void OpenFile(Service::Interface* self) { | |||
|     LOG_DEBUG(Service_FS, "path=%s, mode=%u attrs=%u", file_path.DebugStr().c_str(), mode.hex, | ||||
|               attributes); | ||||
| 
 | ||||
|     ResultVal<SharedPtr<File>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); | ||||
|     ResultVal<std::shared_ptr<File>> file_res = | ||||
|         OpenFileFromArchive(archive_handle, file_path, mode); | ||||
|     cmd_buff[1] = file_res.Code().raw; | ||||
|     if (file_res.Succeeded()) { | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); | ||||
|         std::shared_ptr<File> file = *file_res; | ||||
|         auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); | ||||
|         file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); | ||||
|         cmd_buff[3] = Kernel::g_handle_table | ||||
|                           .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) | ||||
|                           .MoveFrom(); | ||||
|     } else { | ||||
|         cmd_buff[3] = 0; | ||||
|         LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); | ||||
|  | @ -127,10 +134,16 @@ static void OpenFileDirectly(Service::Interface* self) { | |||
|     } | ||||
|     SCOPE_EXIT({ CloseArchive(*archive_handle); }); | ||||
| 
 | ||||
|     ResultVal<SharedPtr<File>> file_res = OpenFileFromArchive(*archive_handle, file_path, mode); | ||||
|     ResultVal<std::shared_ptr<File>> file_res = | ||||
|         OpenFileFromArchive(*archive_handle, file_path, mode); | ||||
|     cmd_buff[1] = file_res.Code().raw; | ||||
|     if (file_res.Succeeded()) { | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); | ||||
|         std::shared_ptr<File> file = *file_res; | ||||
|         auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); | ||||
|         file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); | ||||
|         cmd_buff[3] = Kernel::g_handle_table | ||||
|                           .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) | ||||
|                           .MoveFrom(); | ||||
|     } else { | ||||
|         cmd_buff[3] = 0; | ||||
|         LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", | ||||
|  | @ -388,10 +401,16 @@ static void OpenDirectory(Service::Interface* self) { | |||
|     LOG_DEBUG(Service_FS, "type=%u size=%u data=%s", static_cast<u32>(dirname_type), dirname_size, | ||||
|               dir_path.DebugStr().c_str()); | ||||
| 
 | ||||
|     ResultVal<SharedPtr<Directory>> dir_res = OpenDirectoryFromArchive(archive_handle, dir_path); | ||||
|     ResultVal<std::shared_ptr<Directory>> dir_res = | ||||
|         OpenDirectoryFromArchive(archive_handle, dir_path); | ||||
|     cmd_buff[1] = dir_res.Code().raw; | ||||
|     if (dir_res.Succeeded()) { | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); | ||||
|         std::shared_ptr<Directory> directory = *dir_res; | ||||
|         auto sessions = ServerSession::CreateSessionPair(directory->GetName(), directory); | ||||
|         directory->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); | ||||
|         cmd_buff[3] = Kernel::g_handle_table | ||||
|                           .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) | ||||
|                           .MoveFrom(); | ||||
|     } else { | ||||
|         LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", | ||||
|                   dirname_type, dirname_size, dir_path.DebugStr().c_str()); | ||||
|  |  | |||
|  | @ -2,8 +2,12 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <boost/range/algorithm_ext/erase.hpp> | ||||
| 
 | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/server_port.h" | ||||
| #include "core/hle/service/ac_u.h" | ||||
| #include "core/hle/service/act_a.h" | ||||
| #include "core/hle/service/act_u.h" | ||||
|  | @ -44,8 +48,8 @@ | |||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; | ||||
| std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; | ||||
| std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; | ||||
| std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a function string for logging, complete with the name (or header code, depending | ||||
|  | @ -64,7 +68,23 @@ static std::string MakeFunctionString(const char* name, const char* port_name, | |||
|     return function_string; | ||||
| } | ||||
| 
 | ||||
| ResultVal<bool> Interface::SyncRequest() { | ||||
| void SessionRequestHandler::ClientConnected( | ||||
|     Kernel::SharedPtr<Kernel::ServerSession> server_session) { | ||||
|     connected_sessions.push_back(server_session); | ||||
| } | ||||
| 
 | ||||
| void SessionRequestHandler::ClientDisconnected( | ||||
|     Kernel::SharedPtr<Kernel::ServerSession> server_session) { | ||||
|     boost::range::remove_erase(connected_sessions, server_session); | ||||
| } | ||||
| 
 | ||||
| Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} | ||||
| Interface::~Interface() = default; | ||||
| 
 | ||||
| void Interface::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { | ||||
|     // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which
 | ||||
|     // session triggered each command.
 | ||||
| 
 | ||||
|     u32* cmd_buff = Kernel::GetCommandBuffer(); | ||||
|     auto itr = m_functions.find(cmd_buff[0]); | ||||
| 
 | ||||
|  | @ -78,14 +98,12 @@ ResultVal<bool> Interface::SyncRequest() { | |||
| 
 | ||||
|         // TODO(bunnei): Hack - ignore error
 | ||||
|         cmd_buff[1] = 0; | ||||
|         return MakeResult<bool>(false); | ||||
|         return; | ||||
|     } | ||||
|     LOG_TRACE(Service, "%s", | ||||
|               MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); | ||||
| 
 | ||||
|     itr->second.func(this); | ||||
| 
 | ||||
|     return MakeResult<bool>(false); // TODO: Implement return from actual function
 | ||||
| } | ||||
| 
 | ||||
| void Interface::Register(const FunctionInfo* functions, size_t n) { | ||||
|  | @ -100,11 +118,19 @@ void Interface::Register(const FunctionInfo* functions, size_t n) { | |||
| // Module interface
 | ||||
| 
 | ||||
| static void AddNamedPort(Interface* interface_) { | ||||
|     g_kernel_named_ports.emplace(interface_->GetPortName(), interface_); | ||||
|     auto ports = | ||||
|         Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), | ||||
|                                            std::shared_ptr<Interface>(interface_)); | ||||
|     auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); | ||||
|     g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port)); | ||||
| } | ||||
| 
 | ||||
| void AddService(Interface* interface_) { | ||||
|     g_srv_services.emplace(interface_->GetPortName(), interface_); | ||||
|     auto ports = | ||||
|         Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), | ||||
|                                            std::shared_ptr<Interface>(interface_)); | ||||
|     auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); | ||||
|     g_srv_services.emplace(interface_->GetPortName(), std::move(client_port)); | ||||
| } | ||||
| 
 | ||||
| /// Initialize ServiceManager
 | ||||
|  |  | |||
|  | @ -9,8 +9,15 @@ | |||
| #include <unordered_map> | ||||
| #include <boost/container/flat_map.hpp> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/ipc.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| class ServerSession; | ||||
| } | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Namespace Service
 | ||||
|  | @ -18,14 +25,63 @@ | |||
| namespace Service { | ||||
| 
 | ||||
| static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters)
 | ||||
| /// Arbitrary default number of maximum connections to an HLE service.
 | ||||
| static const u32 DefaultMaxSessions = 10; | ||||
| 
 | ||||
| /// Interface to a CTROS service
 | ||||
| class Interface : public Kernel::Session { | ||||
|     // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be
 | ||||
|     // just something that encapsulates a session and acts as a helper to implement service
 | ||||
|     // processes.
 | ||||
| /**
 | ||||
|  * Interface implemented by HLE Session handlers. | ||||
|  * This can be provided to a ServerSession in order to hook into several relevant events | ||||
|  * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. | ||||
|  */ | ||||
| class SessionRequestHandler { | ||||
| public: | ||||
|     std::string GetName() const override { | ||||
|     /**
 | ||||
|      * Handles a sync request from the emulated application. | ||||
|      * @param server_session The ServerSession that was triggered for this sync request, | ||||
|      * it should be used to differentiate which client (As in ClientSession) we're answering to. | ||||
|      * TODO(Subv): Use a wrapper structure to hold all the information relevant to | ||||
|      * this request (ServerSession, Originator thread, Translated command buffer, etc). | ||||
|      * @returns ResultCode the result code of the translate operation. | ||||
|      */ | ||||
|     virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Signals that a client has just connected to this HLE handler and keeps the | ||||
|      * associated ServerSession alive for the duration of the connection. | ||||
|      * @param server_session Owning pointer to the ServerSession associated with the connection. | ||||
|      */ | ||||
|     void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Signals that a client has just disconnected from this HLE handler and releases the | ||||
|      * associated ServerSession. | ||||
|      * @param server_session ServerSession associated with the connection. | ||||
|      */ | ||||
|     void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); | ||||
| 
 | ||||
| protected: | ||||
|     /// List of sessions that are connected to this handler.
 | ||||
|     /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list
 | ||||
|     // for the duration of the connection.
 | ||||
|     std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a | ||||
|  * table mapping header ids to handler functions. | ||||
|  */ | ||||
| class Interface : public SessionRequestHandler { | ||||
| public: | ||||
|     /**
 | ||||
|      * Creates an HLE interface with the specified max sessions. | ||||
|      * @param max_sessions Maximum number of sessions that can be | ||||
|      * connected to this service at the same time. | ||||
|      */ | ||||
|     Interface(u32 max_sessions = DefaultMaxSessions); | ||||
| 
 | ||||
|     virtual ~Interface(); | ||||
| 
 | ||||
|     std::string GetName() const { | ||||
|         return GetPortName(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -33,6 +89,15 @@ public: | |||
|         version.raw = raw_version; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Gets the maximum allowed number of sessions that can be connected to this service | ||||
|      * at the same time. | ||||
|      * @returns The maximum number of connections allowed. | ||||
|      */ | ||||
|     u32 GetMaxSessions() const { | ||||
|         return max_sessions; | ||||
|     } | ||||
| 
 | ||||
|     typedef void (*Function)(Interface*); | ||||
| 
 | ||||
|     struct FunctionInfo { | ||||
|  | @ -49,9 +114,9 @@ public: | |||
|         return "[UNKNOWN SERVICE PORT]"; | ||||
|     } | ||||
| 
 | ||||
|     ResultVal<bool> SyncRequest() override; | ||||
| 
 | ||||
| protected: | ||||
|     void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Registers the functions in the service | ||||
|      */ | ||||
|  | @ -71,6 +136,7 @@ protected: | |||
|     } version = {}; | ||||
| 
 | ||||
| private: | ||||
|     u32 max_sessions; ///< Maximum number of concurrent sessions that this service can handle.
 | ||||
|     boost::container::flat_map<u32, FunctionInfo> m_functions; | ||||
| }; | ||||
| 
 | ||||
|  | @ -81,9 +147,9 @@ void Init(); | |||
| void Shutdown(); | ||||
| 
 | ||||
| /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
 | ||||
| extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; | ||||
| extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; | ||||
| /// Map of services registered with the "srv:" service, retrieved using GetServiceHandle.
 | ||||
| extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; | ||||
| extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; | ||||
| 
 | ||||
| /// Adds a service to the services table
 | ||||
| void AddService(Interface* interface_); | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ | |||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/result.h" | ||||
| #include "core/hle/service/soc_u.h" | ||||
| #include "core/memory.h" | ||||
|  |  | |||
|  | @ -2,9 +2,13 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/service/srv.h" | ||||
| 
 | ||||
| namespace Service { | ||||
|  | @ -79,7 +83,15 @@ static void GetServiceHandle(Interface* self) { | |||
|     auto it = Service::g_srv_services.find(port_name); | ||||
| 
 | ||||
|     if (it != Service::g_srv_services.end()) { | ||||
|         cmd_buff[3] = Kernel::g_handle_table.Create(it->second).MoveFrom(); | ||||
|         auto client_port = it->second; | ||||
| 
 | ||||
|         auto client_session = client_port->Connect(); | ||||
|         res = client_session.Code(); | ||||
| 
 | ||||
|         if (client_session.Succeeded()) { | ||||
|             // Return the client session
 | ||||
|             cmd_buff[3] = Kernel::g_handle_table.Create(*client_session).MoveFrom(); | ||||
|         } | ||||
|         LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); | ||||
|     } else { | ||||
|         LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "core/hle/function_wrappers.h" | ||||
| #include "core/hle/kernel/address_arbiter.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
|  | @ -20,6 +21,7 @@ | |||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/semaphore.h" | ||||
| #include "core/hle/kernel/server_port.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/kernel/timer.h" | ||||
|  | @ -222,20 +224,29 @@ static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) { | |||
|         return ERR_NOT_FOUND; | ||||
|     } | ||||
| 
 | ||||
|     CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(it->second)); | ||||
|     auto client_port = it->second; | ||||
| 
 | ||||
|     SharedPtr<Kernel::ClientSession> client_session; | ||||
|     CASCADE_RESULT(client_session, client_port->Connect()); | ||||
| 
 | ||||
|     // Return the client session
 | ||||
|     CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(client_session)); | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| /// Synchronize to an OS service
 | ||||
| /// Makes a blocking IPC call to an OS service.
 | ||||
| static ResultCode SendSyncRequest(Handle handle) { | ||||
|     SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle); | ||||
|     SharedPtr<Kernel::ClientSession> session = | ||||
|         Kernel::g_handle_table.Get<Kernel::ClientSession>(handle); | ||||
|     if (session == nullptr) { | ||||
|         return ERR_INVALID_HANDLE; | ||||
|     } | ||||
| 
 | ||||
|     LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); | ||||
| 
 | ||||
|     return session->SyncRequest().Code(); | ||||
|     // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
 | ||||
|     // responds and cause a reschedule.
 | ||||
|     return session->SendSyncRequest(); | ||||
| } | ||||
| 
 | ||||
| /// Close a handle
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue