From d159f43cf3a04c37f452e358dcc400fae42989c8 Mon Sep 17 00:00:00 2001 From: Dragios Date: Sat, 16 Apr 2016 01:43:46 +0800 Subject: [PATCH] WIP (#17) --- src/core/hle/kernel/kernel.h | 10 ++-- src/core/hle/kernel/session.cpp | 48 ++++++++++++++++-- src/core/hle/kernel/session.h | 79 ++++++++++++++++++++++++++--- src/core/hle/service/fs/archive.h | 4 +- src/core/hle/service/fs/fs_user.cpp | 2 +- src/core/hle/service/service.cpp | 9 ++-- src/core/hle/service/service.h | 20 +++----- src/core/hle/service/srv.cpp | 26 +++++++++- src/core/hle/svc.cpp | 6 ++- 9 files changed, 166 insertions(+), 38 deletions(-) diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 4d4276f7a..63dee51f4 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -35,8 +35,8 @@ enum KernelHandle : Handle { enum class HandleType : u32 { Unknown = 0, - Port = 1, - Session = 2, + ServerPort = 1, + ClientSession = 2, Event = 3, Mutex = 4, SharedMemory = 5, @@ -48,6 +48,7 @@ enum class HandleType : u32 { Timer = 11, ResourceLimit = 12, CodeSet = 13, + ServerSession = 14, }; enum { @@ -71,7 +72,7 @@ public: */ bool IsWaitable() const { switch (GetHandleType()) { - case HandleType::Session: + case HandleType::ClientSession: case HandleType::Event: case HandleType::Mutex: case HandleType::Thread: @@ -80,13 +81,14 @@ public: return true; case HandleType::Unknown: - case HandleType::Port: + case HandleType::ServerPort: case HandleType::SharedMemory: case HandleType::Redirection: case HandleType::Process: case HandleType::AddressArbiter: case HandleType::ResourceLimit: case HandleType::CodeSet: + case HandleType::ServerSession: return false; } } diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 0594967f8..083e35338 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -1,13 +1,55 @@ -// Copyright 2015 Citra Emulator Project +// Copyright 2016 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "core/hle/kernel/session.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" namespace Kernel { -Session::Session() {} -Session::~Session() {} +SharedPtr ServerPort::Create(const std::string& name, u32 max_sessions) { + SharedPtr server_port(new ServerPort); + server_port->name = name; + server_port->owner_process = g_current_process; + server_port->max_sessions = max_sessions; + + return server_port; +} + +SharedPtr ServerPort::CreateSession() { + SharedPtr client_session(new ClientSession); + if (IsHLE()) { + client_session->hle_port = this; + } else { + SharedPtr server_session(new ServerSession); + client_session->server_session = server_session; + pending_sessions.push_back(server_session); + // Set the register to the value of the triggered index from svcReplyAndReceive + WakeupAllWaitingThreads(); + } + // TODO(Subv): Schedule to the thread waiting on svcReplyAndReceive + return client_session; +} + +ClientSession::ClientSession() {} +ClientSession::~ClientSession() {} + +ResultVal ClientSession::SyncRequest() { + if (hle_port) { + hle_port->SyncRequest(); + } else { + server_session->data_ready = true; + server_session->waiting_thread = Kernel::GetCurrentThread(); + // TODO(Subv): Sleep the current thread until the response is ready + // TODO(Subv): Immediately schedule the handler thread + } + return MakeResult(true); +} + +ResultVal ServerPort::SyncRequest() { + + return MakeResult(true); +} } diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 6ddaf970e..b52e0aa45 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/result.h" #include "core/memory.h" @@ -64,6 +65,28 @@ inline static u32* GetCommandBuffer(const int offset = 0) { return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + offset); } +class ServerSession : public WaitObject { +public: + std::string GetTypeName() const override { return "ServerSession"; } + + static const HandleType HANDLE_TYPE = HandleType::ServerSession; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + bool ShouldWait() override { + return !data_ready; + } + + void Acquire() override { + data_ready = false; + } + + bool data_ready; + SharedPtr handler_thread; + SharedPtr waiting_thread; +}; + +class ServerPort; + /** * 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 @@ -86,21 +109,21 @@ inline static u32* GetCommandBuffer(const int offset = 0) { * 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 { +class ClientSession : public WaitObject { public: - Session(); - ~Session() override; + ClientSession(); + ~ClientSession() override; - std::string GetTypeName() const override { return "Session"; } + std::string GetTypeName() const override { return "ClientSession"; } - static const HandleType HANDLE_TYPE = HandleType::Session; + static const HandleType HANDLE_TYPE = HandleType::ClientSession; 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 SyncRequest() = 0; + virtual ResultVal SyncRequest(); // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object // passed into WaitSynchronization. Figure out the meaning of them. @@ -112,6 +135,50 @@ public: void Acquire() override { ASSERT_MSG(!ShouldWait(), "object unavailable!"); } + + SharedPtr server_session; + SharedPtr hle_port; +}; + +class ServerPort : public WaitObject { +public: + std::string GetTypeName() const override { return "ServerPort"; } + + static const HandleType HANDLE_TYPE = HandleType::ServerPort; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + /** + * Gets the string name used by CTROS for a service + * @return Port name of service + */ + virtual std::string GetPortName() const { + return name; + } + + virtual ResultVal SyncRequest(); + + std::string GetName() const override { return GetPortName(); } + + static SharedPtr Create(const std::string& name, u32 max_sessions); + + SharedPtr CreateSession(); + + virtual bool IsHLE() const { return false; } + + bool ShouldWait() override { + return pending_sessions.empty(); + } + + void Acquire() override { + + } + +private: + std::string name; + SharedPtr owner_process; + u32 max_sessions; + + std::vector> pending_sessions; }; } diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 006606740..4cee9eaa0 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -46,7 +46,7 @@ enum class MediaType : u32 { typedef u64 ArchiveHandle; -class File : public Kernel::Session { +class File : public Kernel::ClientSession { public: File(std::unique_ptr&& backend, const FileSys::Path& path); ~File(); @@ -59,7 +59,7 @@ public: std::unique_ptr backend; ///< File backend interface }; -class Directory : public Kernel::Session { +class Directory : public Kernel::ClientSession { public: Directory(std::unique_ptr&& backend, const FileSys::Path& path); ~Directory(); diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 3ec7ceb30..e0cfe19a1 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -18,7 +18,7 @@ // Namespace FS_User using Kernel::SharedPtr; -using Kernel::Session; +using Kernel::ClientSession; namespace Service { namespace FS { diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0fe3a4d7a..4045aabf9 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -5,6 +5,7 @@ #include "common/logging/log.h" #include "common/string_util.h" +#include "core/hle/kernel/session.h" #include "core/hle/service/service.h" #include "core/hle/service/ac_u.h" #include "core/hle/service/act_u.h" @@ -42,8 +43,8 @@ namespace Service { -std::unordered_map> g_kernel_named_ports; -std::unordered_map> g_srv_services; +std::unordered_map> g_kernel_named_ports; +std::unordered_map> g_srv_services; /** * Creates a function string for logging, complete with the name (or header code, depending @@ -90,11 +91,11 @@ void Interface::Register(const FunctionInfo* functions, size_t n) { //////////////////////////////////////////////////////////////////////////////////////////////////// // Module interface -static void AddNamedPort(Interface* interface_) { +static void AddNamedPort(Kernel::ServerPort* interface_) { g_kernel_named_ports.emplace(interface_->GetPortName(), interface_); } -void AddService(Interface* interface_) { +void AddService(Kernel::ServerPort* interface_) { g_srv_services.emplace(interface_->GetPortName(), interface_); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index f31135212..b150914c8 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -23,13 +23,11 @@ namespace Service { static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) /// Interface to a CTROS service -class Interface : public Kernel::Session { +class Interface : public Kernel::ServerPort { // 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. public: - std::string GetName() const override { return GetPortName(); } - typedef void (*Function)(Interface*); struct FunctionInfo { @@ -38,16 +36,10 @@ public: const char* name; }; - /** - * Gets the string name used by CTROS for a service - * @return Port name of service - */ - virtual std::string GetPortName() const { - return "[UNKNOWN SERVICE PORT]"; - } - ResultVal SyncRequest() override; + bool IsHLE() const override { return true; } + protected: /** @@ -72,11 +64,11 @@ void Init(); void Shutdown(); /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. -extern std::unordered_map> g_kernel_named_ports; +extern std::unordered_map> g_kernel_named_ports; /// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. -extern std::unordered_map> g_srv_services; +extern std::unordered_map> g_srv_services; /// Adds a service to the services table -void AddService(Interface* interface_); +void AddService(Kernel::ServerPort* interface_); } // namespace diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index aae955bf8..de2cdf663 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -40,7 +40,8 @@ static void GetServiceHandle(Service::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_session = it->second->CreateSession(); + 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()); @@ -49,10 +50,31 @@ static void GetServiceHandle(Service::Interface* self) { cmd_buff[1] = res.raw; } +static void RegisterService(Service::Interface* self) { + ResultCode res = RESULT_SUCCESS; + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 max_sessions = cmd_buff[4]; + std::string port_name = std::string((const char*)&cmd_buff[1], 0, std::min(cmd_buff[3], Service::kMaxPortSize)); + auto it = Service::g_srv_services.find(port_name); + + if (it != Service::g_srv_services.end()) { + LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s already exists", port_name.c_str()); + res = UnimplementedFunction(ErrorModule::SRV); + } else { + LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); + auto server_port = Kernel::ServerPort::Create(port_name, max_sessions); + Service::g_srv_services.emplace(port_name, server_port); + cmd_buff[3] = Kernel::g_handle_table.Create(server_port).MoveFrom(); + } + + cmd_buff[1] = res.raw; +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010002, Initialize, "Initialize"}, {0x00020000, GetProcSemaphore, "GetProcSemaphore"}, - {0x00030100, nullptr, "RegisterService"}, + {0x00030100, RegisterService, "RegisterService"}, {0x000400C0, nullptr, "UnregisterService"}, {0x00050100, GetServiceHandle, "GetServiceHandle"}, {0x000600C2, nullptr, "RegisterHandle"}, diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index ae54afb1c..0bb496e3f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -215,13 +215,15 @@ 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)); + // TODO(Subv): This should return a ClientPort, not a ClientSession + auto session = it->second->CreateSession(); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(session)); return RESULT_SUCCESS; } /// Synchronize to an OS service static ResultCode SendSyncRequest(Handle handle) { - SharedPtr session = Kernel::g_handle_table.Get(handle); + SharedPtr session = Kernel::g_handle_table.Get(handle); if (session == nullptr) { return ERR_INVALID_HANDLE; }