diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-31 03:02:24 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 12:39:36 -0500 |
| commit | a07b8a31908ef8cc56e430449f374cdf997cd4fb (patch) | |
| tree | 6bc53c121cb2c1c719aa9e47a3a9f29a5f59e111 /src/core/hle/service/service.cpp | |
| parent | c26cfaeac4cd86bfcdea2aea82ac6979bcd8144b (diff) | |
service: placeholder handlers for known but unimplemented commands
Add StubReplyZeroed, StubReplyEvent and StubReplyInterface to
ServiceFrameworkBase: for Switch 2 callers they reply with zeroed data, with
zeroed data and a copy handle to an event that never fires, or with an nx2:
stub interface; other callers get the unimplemented-function path as before.
Each command is logged once.
Use them for the commands of the interfaces games open that had no handler
or a nullptr one (hid, am, audio, aoc, caps, mii, set, lm, acc, ssl, bsd and
the nx2 vi2/gsv/gpu/syncpt sessions): a command that returns an interface now
returns one, a command that returns a handle returns an event, so callers no
longer get handle 0 or object 0 back.
Diffstat (limited to 'src/core/hle/service/service.cpp')
| -rw-r--r-- | src/core/hle/service/service.cpp | 84 |
1 files changed, 76 insertions, 8 deletions
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 38ea936d3c..b3bfe1f733 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -9,6 +9,9 @@ #include <tuple> #include <mutex> #include <map> +#include <memory> +#include <set> +#include <string_view> #include <chrono> #include <algorithm> #include <cstdlib> @@ -18,8 +21,10 @@ #include "common/settings.h" #include "core/core.h" #include "core/hle/ipc.h" +#include "core/hle/kernel/k_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" #include "core/hle/service/nx2_stub_interface.h" #include "core/hle/service/sm/sm.h" @@ -49,6 +54,75 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se ServiceFrameworkBase::~ServiceFrameworkBase() { // Wait for other threads to release access before destroying const auto guard = ServiceFrameworkBase::LockService(); + if (stub_event) { + stub_context->CloseEvent(stub_event); + } +} + +namespace { + +// Callers read the reply words they expect without checking how many were written, so an empty +// reply hands them the request's leftover words as data (an HDR output format, a refresh rate, a +// device id). Stub replies carry this many zeroed words. +constexpr u32 StubZeroedWords = 32; + +void PushStubZeroes(IPC::ResponseBuilder& rb) { + for (u32 i = 0; i < StubZeroedWords; ++i) { + rb.Push<u32>(0); + } +} + +} // namespace + +bool ServiceFrameworkBase::StubAllowed(HLERequestContext& ctx) { + static const bool env_auto_stub = std::getenv("EDEN_NX2_AUTO_STUB") != nullptr; + const auto* const caller = ctx.GetThread().GetOwnerProcess(); + if (Settings::values.use_auto_stub || env_auto_stub || + std::string_view{service_name}.starts_with(Nx2StubPrefix) || + (caller != nullptr && caller->IsSwitch2())) { + // Logged once per command: these are still unimplemented. + static std::mutex mutex; + static std::set<std::pair<std::string_view, u32>> logged; + std::scoped_lock lk{mutex}; + if (logged.emplace(service_name, ctx.GetCommand()).second) { + LOG_WARNING(Service, "(STUBBED) {}::{} (placeholder reply)", service_name, ctx.GetCommand()); + } + return true; + } + const auto& table = ctx.IsTipc() ? handlers_tipc : handlers; + const auto it = table.find(ctx.GetCommand()); + ReportUnimplementedFunction(ctx, it == table.end() ? nullptr : &it->second); + return false; +} + +void ServiceFrameworkBase::StubReplyZeroed(HLERequestContext& ctx) { + if (!StubAllowed(ctx)) { + return; + } + IPC::ResponseBuilder rb{ctx, 2 + StubZeroedWords}; + rb.Push(ResultSuccess); + PushStubZeroes(rb); +} + +void ServiceFrameworkBase::StubReplyEvent(HLERequestContext& ctx) { + if (!StubAllowed(ctx)) { + return; + } + if (!stub_event) { + stub_context = std::make_unique<KernelHelpers::ServiceContext>(system, service_name); + stub_event = stub_context->CreateEvent(fmt::format("{}:StubEvent", service_name)); + } + IPC::ResponseBuilder rb{ctx, 2 + StubZeroedWords, 1, 0}; + rb.Push(ResultSuccess); + PushStubZeroes(rb); + rb.PushCopyObjects(ctx, stub_event->GetReadableEvent()); +} + +void ServiceFrameworkBase::StubReplyInterface(HLERequestContext& ctx) { + if (!StubAllowed(ctx)) { + return; + } + ReturnNx2StubInterface(system, ctx, service_name); } void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { @@ -88,15 +162,9 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx, std::string_view{service_name}.starts_with(Nx2StubPrefix) || (caller != nullptr && caller->IsSwitch2())) { LOG_WARNING(Service, "Using auto stub fallback!"); - // Callers read the reply words they expect without checking how many were written, so an - // empty reply hands them the request's leftover words as data (an HDR output format, a - // refresh rate, a device id). Reply with zeroed words instead. - constexpr u32 ZeroedWords = 32; - IPC::ResponseBuilder rb{ctx, 2 + ZeroedWords}; + IPC::ResponseBuilder rb{ctx, 2 + StubZeroedWords}; rb.Push(ResultSuccess); - for (u32 i = 0; i < ZeroedWords; ++i) { - rb.Push<u32>(0); - } + PushStubZeroes(rb); } } |
