2026-03-08 20:50:29 +01:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
2025-08-27 22:39:11 +02:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2022-04-23 04:59:50 -04:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2014-04-12 21:55:36 -04:00
|
|
|
|
2025-04-09 19:11:22 -04:00
|
|
|
#include <fmt/ranges.h>
|
2017-06-08 23:52:30 -07:00
|
|
|
#include "common/assert.h"
|
2026-03-12 18:29:15 +01:00
|
|
|
#include "common/logging.h"
|
2021-04-14 16:07:40 -07:00
|
|
|
#include "common/settings.h"
|
2018-03-13 17:49:59 -04:00
|
|
|
#include "core/core.h"
|
2017-06-08 23:52:30 -07:00
|
|
|
#include "core/hle/ipc.h"
|
2018-09-02 11:58:58 -04:00
|
|
|
#include "core/hle/kernel/kernel.h"
|
2023-02-19 14:42:12 -05:00
|
|
|
#include "core/hle/service/ipc_helpers.h"
|
2016-09-20 23:52:38 -07:00
|
|
|
#include "core/hle/service/service.h"
|
2017-06-05 23:31:59 -07:00
|
|
|
#include "core/hle/service/sm/sm.h"
|
2019-05-17 21:48:08 -04:00
|
|
|
#include "core/reporter.h"
|
2015-02-26 21:13:08 -05:00
|
|
|
|
2014-04-12 21:55:36 -04:00
|
|
|
namespace Service {
|
|
|
|
|
|
2025-12-18 21:46:00 +01:00
|
|
|
/// @brief Creates a function string for logging, complete with the name (or header code, depending
|
2026-01-06 22:44:57 +01:00
|
|
|
/// 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) {
|
2020-09-25 00:05:12 -04:00
|
|
|
// Number of params == bits 0-5 + bits 6-11
|
2026-01-06 22:44:57 +01:00
|
|
|
int num_params = (cmd_buf[0] & 0x3F) + ((cmd_buf[0] >> 6) & 0x3F);
|
|
|
|
|
std::string function_string = fmt::format("fn '{}': port={}", name, port_name);
|
2025-12-18 21:46:00 +01:00
|
|
|
for (int i = 1; i <= num_params; ++i)
|
2026-01-06 22:44:57 +01:00
|
|
|
function_string += fmt::format(", cmd_buf[{}]={:#X}", i, cmd_buf[i]);
|
2020-09-25 00:05:12 -04:00
|
|
|
return function_string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 20:50:29 +01:00
|
|
|
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_}
|
|
|
|
|
{}
|
2017-06-06 21:20:52 -07:00
|
|
|
|
2020-12-28 18:23:42 -08:00
|
|
|
ServiceFrameworkBase::~ServiceFrameworkBase() {
|
|
|
|
|
// Wait for other threads to release access before destroying
|
2025-12-18 21:46:00 +01:00
|
|
|
const auto guard = ServiceFrameworkBase::LockService();
|
2017-06-06 21:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
2018-09-15 15:21:06 +02:00
|
|
|
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
|
2025-12-18 21:46:00 +01:00
|
|
|
// Usually this array is sorted by id already, so hint to insert at the end
|
2017-06-06 21:20:52 -07:00
|
|
|
handlers.reserve(handlers.size() + n);
|
2025-12-18 21:46:00 +01:00
|
|
|
for (std::size_t i = 0; i < n; ++i)
|
2017-06-06 21:20:52 -07:00
|
|
|
handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 21:46:00 +01:00
|
|
|
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
|
2021-05-10 16:08:06 -07:00
|
|
|
handlers_tipc.reserve(handlers_tipc.size() + n);
|
2025-12-18 21:46:00 +01:00
|
|
|
for (std::size_t i = 0; i < n; ++i)
|
2026-03-08 20:50:29 +01:00
|
|
|
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, functions[i]);
|
2021-05-10 16:08:06 -07:00
|
|
|
}
|
|
|
|
|
|
2023-02-19 14:42:12 -05:00
|
|
|
void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
2017-10-15 01:24:22 -04:00
|
|
|
const FunctionInfoBase* info) {
|
2017-10-14 22:18:42 -04:00
|
|
|
auto cmd_buf = ctx.CommandBuffer();
|
2024-04-05 01:58:30 +02:00
|
|
|
std::string function_name = info == nullptr ? "<unknown>" : info->name;
|
2017-06-06 21:20:52 -07:00
|
|
|
|
2018-04-03 02:49:58 +08:00
|
|
|
fmt::memory_buffer buf;
|
2025-12-18 21:46:00 +01:00
|
|
|
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)
|
2025-08-27 22:39:11 +02:00
|
|
|
fmt::format_to(std::back_inserter(buf), ", [{}]={:#X}", i, cmd_buf[i]);
|
2018-04-03 02:49:58 +08:00
|
|
|
buf.push_back('}');
|
2017-06-06 21:20:52 -07:00
|
|
|
|
2025-12-18 21:46:00 +01:00
|
|
|
system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name, service_name);
|
2018-12-06 01:40:21 -05:00
|
|
|
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
|
2021-03-12 17:56:02 -05:00
|
|
|
if (Settings::values.use_auto_stub) {
|
|
|
|
|
LOG_WARNING(Service, "Using auto stub fallback!");
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
2021-05-21 01:05:04 -04:00
|
|
|
rb.Push(ResultSuccess);
|
2021-03-12 17:56:02 -05:00
|
|
|
}
|
2017-06-06 21:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
2023-02-19 14:42:12 -05:00
|
|
|
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
2025-12-18 21:46:00 +01:00
|
|
|
auto it = handlers.find(ctx.GetCommand());
|
|
|
|
|
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
|
|
|
|
if (info == nullptr || info->handler_callback == nullptr)
|
2017-10-14 22:18:42 -04:00
|
|
|
return ReportUnimplementedFunction(ctx, info);
|
2017-06-06 21:20:52 -07:00
|
|
|
|
2019-03-10 18:00:54 -04:00
|
|
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
2017-10-14 22:18:42 -04:00
|
|
|
handler_invoker(this, info->handler_callback, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-19 14:42:12 -05:00
|
|
|
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
2025-12-18 21:46:00 +01:00
|
|
|
auto it = handlers_tipc.find(ctx.GetCommand());
|
|
|
|
|
FunctionInfoBase const* info = it == handlers_tipc.end() ? nullptr : &it->second;
|
|
|
|
|
if (info == nullptr || info->handler_callback == nullptr)
|
2021-05-10 16:08:06 -07:00
|
|
|
return ReportUnimplementedFunction(ctx, info);
|
|
|
|
|
|
|
|
|
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
|
|
|
|
handler_invoker(this, info->handler_callback, ctx);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-25 22:44:19 -05:00
|
|
|
Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
|
2023-02-19 14:42:12 -05:00
|
|
|
HLERequestContext& ctx) {
|
2020-12-28 18:23:42 -08:00
|
|
|
const auto guard = LockService();
|
|
|
|
|
|
2022-07-31 23:17:45 +03:00
|
|
|
Result result = ResultSuccess;
|
|
|
|
|
|
2021-05-10 16:05:37 -07:00
|
|
|
switch (ctx.GetCommandType()) {
|
|
|
|
|
case IPC::CommandType::Close:
|
|
|
|
|
case IPC::CommandType::TIPC_Close: {
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2};
|
2021-05-21 01:05:04 -04:00
|
|
|
rb.Push(ResultSuccess);
|
2023-03-06 19:04:12 -05:00
|
|
|
result = IPC::ResultSessionClosed;
|
2022-07-31 23:17:45 +03:00
|
|
|
break;
|
2017-10-14 22:18:42 -04:00
|
|
|
}
|
2018-05-17 14:03:52 -07:00
|
|
|
case IPC::CommandType::ControlWithContext:
|
2017-10-15 01:24:22 -04:00
|
|
|
case IPC::CommandType::Control: {
|
2021-05-10 16:05:37 -07:00
|
|
|
system.ServiceManager().InvokeControlRequest(ctx);
|
2017-10-14 22:18:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2018-05-17 14:03:52 -07:00
|
|
|
case IPC::CommandType::RequestWithContext:
|
2017-10-15 01:24:22 -04:00
|
|
|
case IPC::CommandType::Request: {
|
2021-05-10 16:05:37 -07:00
|
|
|
InvokeRequest(ctx);
|
2017-10-14 22:18:42 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
2021-05-10 16:08:06 -07:00
|
|
|
if (ctx.IsTipc()) {
|
|
|
|
|
InvokeRequestTipc(ctx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-10 16:05:37 -07:00
|
|
|
UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
|
2022-11-11 04:12:37 -08:00
|
|
|
break;
|
2017-10-14 22:18:42 -04:00
|
|
|
}
|
|
|
|
|
|
2020-12-14 17:58:12 -08:00
|
|
|
// 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()) {
|
2023-12-24 19:20:43 -05:00
|
|
|
ctx.WriteToOutgoingCommandBuffer();
|
2020-12-14 17:58:12 -08:00
|
|
|
}
|
2017-10-31 19:30:05 -04:00
|
|
|
|
2022-07-31 23:17:45 +03:00
|
|
|
return result;
|
2017-06-06 21:20:52 -07:00
|
|
|
}
|
|
|
|
|
|
2017-10-15 01:24:22 -04:00
|
|
|
} // namespace Service
|