aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/service/service.cpp
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-31 03:02:24 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 12:39:36 -0500
commita07b8a31908ef8cc56e430449f374cdf997cd4fb (patch)
tree6bc53c121cb2c1c719aa9e47a3a9f29a5f59e111 /src/core/hle/service/service.cpp
parentc26cfaeac4cd86bfcdea2aea82ac6979bcd8144b (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.cpp84
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);
}
}