aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 19:50:18 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 20:49:49 -0400
commit04a64dee831e4ba94f63e0b8b77e26d181a71966 (patch)
treeaf64b87359d5385a90bdb7b1dfbd196f75302b7b /src
parentfe662906633671768910486001e1ea959e43a083 (diff)
sm: give Switch 2 programs a stub for services Eden does not have
Newer Mario Kart World updates open chat:u (GameChat) while booting. Eden never registers it, so the request was deferred until the service shows up and the game sat on its loading screen forever. For Switch 2 callers, hand out an INx2StubInterface session instead, whose commands are all auto-stubbed like their unknown commands on existing services.
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/sm/sm.cpp43
1 files changed, 43 insertions, 0 deletions
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index c75c5437df..60f0dd89cb 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -11,11 +11,19 @@
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_port.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_port.h"
+#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/nx2_stub_interface.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/sm/sm_controller.h"
@@ -170,6 +178,30 @@ static std::string PopServiceName(IPC::RequestParser& rp) {
return result;
}
+namespace {
+
+// A session served by a Switch 2 stub interface named after the missing service (see
+// nx2_stub_interface.h), set up like a session handed out by a service.
+Kernel::KClientSession* CreateStubSession(Core::System& system, HLERequestContext& ctx,
+ const std::string& name) {
+ auto& kernel = system.Kernel();
+ ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
+ kernel, Kernel::LimitableResource::SessionCountMax, 1));
+
+ auto* session = Kernel::KSession::Create(kernel);
+ session->Initialize(kernel, nullptr, 0);
+ Kernel::KSession::Register(kernel, session);
+
+ auto manager =
+ std::make_shared<SessionRequestManager>(kernel, ctx.GetManager()->GetServerManager());
+ manager->SetSessionHandler(std::make_shared<INx2StubInterface>(
+ system, INx2StubInterface::InternName(fmt::format("{}{}", Nx2StubPrefix, name))));
+ ctx.GetManager()->GetServerManager().RegisterSession(&session->GetServerSession(), manager);
+ return &session->GetClientSession();
+}
+
+} // namespace
+
Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx) {
if (!ctx.GetManager()->GetIsInitializedForSm()) {
return Service::SM::ResultInvalidClient;
@@ -187,6 +219,17 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
}
if (port_result != ResultSuccess) {
+ // Switch 2 programs also ask for services Eden does not have at all (newer Mario Kart
+ // World updates open chat:u, GameChat, while booting). Nothing ever registers those, so
+ // waiting would hang them for good: hand out a placeholder whose commands are all
+ // auto-stubbed, like their unknown commands on existing services.
+ const auto* const caller = ctx.GetThread().GetOwnerProcess();
+ if (caller != nullptr && caller->IsSwitch2()) {
+ LOG_WARNING(Service_SM, "(STUBBED) Switch 2 service {} does not exist, returning a stub",
+ name);
+ *out_client_session = CreateStubSession(system, ctx, name);
+ return ResultSuccess;
+ }
LOG_INFO(Service_SM, "Waiting for service {} to become available", name);
ctx.SetIsDeferred();
return Service::SM::ResultNotRegistered;