mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-10 14:08:54 +02:00
Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3671 Reviewed-by: DraVee <chimera@dravee.dev> Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>
142 lines
5.5 KiB
C++
142 lines
5.5 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <fmt/ranges.h>
|
|
#include "common/assert.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/settings.h"
|
|
#include "core/core.h"
|
|
#include "core/hle/ipc.h"
|
|
#include "core/hle/kernel/kernel.h"
|
|
#include "core/hle/service/ipc_helpers.h"
|
|
#include "core/hle/service/service.h"
|
|
#include "core/hle/service/sm/sm.h"
|
|
#include "core/reporter.h"
|
|
|
|
namespace Service {
|
|
|
|
/// @brief Creates a function string for logging, complete with the name (or header code, depending
|
|
/// on what's passed in) the port name, and all the cmd_buf arguments.
|
|
[[maybe_unused]] static std::string MakeFunctionString(std::string_view name, std::string_view port_name, const u32* cmd_buf) {
|
|
// Number of params == bits 0-5 + bits 6-11
|
|
int num_params = (cmd_buf[0] & 0x3F) + ((cmd_buf[0] >> 6) & 0x3F);
|
|
std::string function_string = fmt::format("fn '{}': port={}", name, port_name);
|
|
for (int i = 1; i <= num_params; ++i)
|
|
function_string += fmt::format(", cmd_buf[{}]={:#X}", i, cmd_buf[i]);
|
|
return function_string;
|
|
}
|
|
|
|
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, u32 max_sessions_, InvokerFn* handler_invoker_)
|
|
: SessionRequestHandler(system_.Kernel(), service_name_)
|
|
, system{system_}
|
|
, service_name{service_name_}
|
|
, handler_invoker{handler_invoker_}
|
|
, max_sessions{max_sessions_}
|
|
{}
|
|
|
|
ServiceFrameworkBase::~ServiceFrameworkBase() {
|
|
// Wait for other threads to release access before destroying
|
|
const auto guard = ServiceFrameworkBase::LockService();
|
|
}
|
|
|
|
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
|
|
// Usually this array is sorted by id already, so hint to insert at the end
|
|
handlers.reserve(handlers.size() + n);
|
|
for (std::size_t i = 0; i < n; ++i)
|
|
handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
|
|
}
|
|
|
|
void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n) {
|
|
// Usually this array is sorted by id already, so hint to insert at the end
|
|
handlers_tipc.reserve(handlers_tipc.size() + n);
|
|
for (std::size_t i = 0; i < n; ++i)
|
|
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, functions[i]);
|
|
}
|
|
|
|
void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
|
const FunctionInfoBase* info) {
|
|
auto cmd_buf = ctx.CommandBuffer();
|
|
std::string function_name = info == nullptr ? "<unknown>" : info->name;
|
|
|
|
fmt::memory_buffer buf;
|
|
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]={:#X}", ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
|
|
for (int i = 1; i <= 8; ++i)
|
|
fmt::format_to(std::back_inserter(buf), ", [{}]={:#X}", i, cmd_buf[i]);
|
|
buf.push_back('}');
|
|
|
|
system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name, service_name);
|
|
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
|
|
if (Settings::values.use_auto_stub) {
|
|
LOG_WARNING(Service, "Using auto stub fallback!");
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultSuccess);
|
|
}
|
|
}
|
|
|
|
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
|
auto it = handlers.find(ctx.GetCommand());
|
|
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
|
if (info == nullptr || info->handler_callback == nullptr)
|
|
return ReportUnimplementedFunction(ctx, info);
|
|
|
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
|
handler_invoker(this, info->handler_callback, ctx);
|
|
}
|
|
|
|
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
|
auto it = handlers_tipc.find(ctx.GetCommand());
|
|
FunctionInfoBase const* info = it == handlers_tipc.end() ? nullptr : &it->second;
|
|
if (info == nullptr || info->handler_callback == nullptr)
|
|
return ReportUnimplementedFunction(ctx, info);
|
|
|
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
|
handler_invoker(this, info->handler_callback, ctx);
|
|
}
|
|
|
|
Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
|
HLERequestContext& ctx) {
|
|
const auto guard = LockService();
|
|
|
|
Result result = ResultSuccess;
|
|
|
|
switch (ctx.GetCommandType()) {
|
|
case IPC::CommandType::Close:
|
|
case IPC::CommandType::TIPC_Close: {
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
|
rb.Push(ResultSuccess);
|
|
result = IPC::ResultSessionClosed;
|
|
break;
|
|
}
|
|
case IPC::CommandType::ControlWithContext:
|
|
case IPC::CommandType::Control: {
|
|
system.ServiceManager().InvokeControlRequest(ctx);
|
|
break;
|
|
}
|
|
case IPC::CommandType::RequestWithContext:
|
|
case IPC::CommandType::Request: {
|
|
InvokeRequest(ctx);
|
|
break;
|
|
}
|
|
default:
|
|
if (ctx.IsTipc()) {
|
|
InvokeRequestTipc(ctx);
|
|
break;
|
|
}
|
|
|
|
UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
|
|
break;
|
|
}
|
|
|
|
// If emulation was shutdown, we are closing service threads, do not write the response back to
|
|
// memory that may be shutting down as well.
|
|
if (system.IsPoweredOn()) {
|
|
ctx.WriteToOutgoingCommandBuffer();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace Service
|