From 84c497292a27d75b83305d053e734ab5659ffe41 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Tue, 6 Jun 2017 21:20:52 -0700 Subject: Service: Add new ServiceFramework framework for writing HLE services The old "Interface" class had a few problems such as using free functions (Which didn't allow you to write the service handler as if it were a regular class.) which weren't very extensible. (Only received one parameter with a pointer to the Interface object.) The new ServiceFramework aims to solve these problems by working with member functions and passing a generic context struct as parameter. This struct can be extended in the future without having to update all existing service implementations. --- src/core/hle/service/service.cpp | 83 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) (limited to 'src/core/hle/service/service.cpp') diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0d443aa44..10f2b3ee3 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/logging/log.h" #include "common/string_util.h" #include "core/hle/kernel/client_port.h" @@ -45,6 +46,11 @@ #include "core/hle/service/ssl_c.h" #include "core/hle/service/y2r_u.h" +using Kernel::ClientPort; +using Kernel::ServerPort; +using Kernel::ServerSession; +using Kernel::SharedPtr; + namespace Service { std::unordered_map> g_kernel_named_ports; @@ -102,9 +108,84 @@ void Interface::Register(const FunctionInfo* functions, size_t n) { } } +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, + InvokerFn* handler_invoker) + : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} + +ServiceFrameworkBase::~ServiceFrameworkBase() = default; + +void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { + ASSERT(port == nullptr); + port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); + port->SetHleHandler(shared_from_this()); +} + +void ServiceFrameworkBase::InstallAsNamedPort() { + ASSERT(port == nullptr); + SharedPtr server_port; + SharedPtr client_port; + std::tie(server_port, client_port) = ServerPort::CreatePortPair(max_sessions, service_name); + server_port->SetHleHandler(shared_from_this()); + AddNamedPort(service_name, std::move(client_port)); +} + +void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) { + handlers.reserve(handlers.size() + n); + for (size_t i = 0; i < n; ++i) { + // Usually this array is sorted by id already, so hint to insert at the end + handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); + } +} + +void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info) { + IPC::Header header{cmd_buf[0]}; + int num_params = header.normal_params_size + header.translate_params_size; + std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name; + + fmt::MemoryWriter w; + w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name, + cmd_buf[0]); + for (int i = 1; i <= num_params; ++i) { + w.write(", [{}]={:#x}", i, cmd_buf[i]); + } + w << '}'; + + LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str()); + // TODO(bunnei): Hack - ignore error + cmd_buf[1] = 0; +} + +void ServiceFrameworkBase::HandleSyncRequest(SharedPtr server_session) { + u32* cmd_buf = Kernel::GetCommandBuffer(); + + // TODO(yuriks): The kernel should be the one handling this as part of translation after + // everything else is migrated + Kernel::HLERequestContext context; + context.cmd_buf = cmd_buf; + context.session = std::move(server_session); + + u32 header_code = cmd_buf[0]; + auto itr = handlers.find(header_code); + const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; + if (info == nullptr || info->handler_callback == nullptr) { + return ReportUnimplementedFunction(cmd_buf, info); + } + + LOG_TRACE(Service, "%s", + MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); + handler_invoker(this, info->handler_callback, context); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Module interface +// TODO(yuriks): Move to kernel +void AddNamedPort(std::string name, SharedPtr port) { + g_kernel_named_ports.emplace(std::move(name), std::move(port)); +} + static void AddNamedPort(Interface* interface_) { Kernel::SharedPtr server_port; Kernel::SharedPtr client_port; @@ -112,7 +193,7 @@ static void AddNamedPort(Interface* interface_) { Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName()); server_port->SetHleHandler(std::shared_ptr(interface_)); - g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port)); + AddNamedPort(interface_->GetPortName(), std::move(client_port)); } void AddService(Interface* interface_) { -- cgit v1.2.3