aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-27 15:27:35 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-27 15:27:35 -0400
commit5cb4786f3e25136145fa71c17da7a65be35ff7dd (patch)
tree25609a1954783e0687c9a717789638585003e59b /src
parent559dfe7bb24d95fbbc32fd571c7f53e13e42342c (diff)
nvdrv: stub device for nvgpu nodes Switch 1 never had
Unknown /dev/nvhost-* nodes (first: /dev/nvhost-tsg-gpu, opened by the Switch 2 NvRm during NVN device init) open as nx2_stub_device: every ioctl succeeds with the input echoed and the rest zeroed, and is logged. With this NVN device init completes and the title reaches nn::wsi::ounce::native::NativeWindow::Acquire (first frame).
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hle/service/nvdrv/devices/nx2_stub_device.h54
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp15
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h2
4 files changed, 72 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 231f0db451..6058ce82ce 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -866,6 +866,7 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvhost_vic.h
hle/service/nvdrv/devices/nvmap.cpp
hle/service/nvdrv/devices/nvmap.h
+ hle/service/nvdrv/devices/nx2_stub_device.h
hle/service/nvdrv/nvdata.h
hle/service/nvdrv/nvdrv.cpp
hle/service/nvdrv/nvdrv.h
diff --git a/src/core/hle/service/nvdrv/devices/nx2_stub_device.h b/src/core/hle/service/nvdrv/devices/nx2_stub_device.h
new file mode 100644
index 0000000000..1dc2f8b7fa
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nx2_stub_device.h
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <algorithm>
+#include <string>
+
+#include "common/logging.h"
+#include "core/hle/service/nvdrv/devices/nvdevice.h"
+
+namespace Service::Nvidia::Devices {
+
+// Device node that Switch 2 nvgpu drivers open but Switch 1 never had (e.g. /dev/nvhost-tsg-gpu).
+// Every ioctl succeeds with a zeroed reply and is logged, so the driver's use of it shows up.
+class nx2_stub_device final : public nvdevice {
+public:
+ explicit nx2_stub_device(Core::System& system_, std::string name_)
+ : nvdevice{system_}, name{std::move(name_)} {}
+
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<u8> output) override {
+ return Stub(fd, command, input, output, {});
+ }
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input,
+ std::span<const u8> inline_input, std::span<u8> output) override {
+ return Stub(fd, command, input, output, {});
+ }
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
+ std::span<u8> inline_output) override {
+ return Stub(fd, command, input, output, inline_output);
+ }
+
+ void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override {
+ LOG_WARNING(Service_NVDRV, "(STUBBED) {} opened as fd {}", name, fd);
+ }
+ void OnClose(DeviceFD fd) override {}
+
+private:
+ NvResult Stub(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output,
+ std::span<u8> inline_output) {
+ LOG_WARNING(Service_NVDRV, "(STUBBED) {} fd={} ioctl={:08X} group={:01X} command={:02X} in={:#x}",
+ name, fd, command.raw, command.group, command.cmd, input.size());
+ // Echo the input (in/out ioctls keep their fields), zero the rest.
+ std::fill(output.begin(), output.end(), u8{0});
+ std::copy_n(input.begin(), std::min(input.size(), output.size()), output.begin());
+ std::fill(inline_output.begin(), inline_output.end(), u8{0});
+ return NvResult::Success;
+ }
+
+ std::string name;
+};
+
+} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index b252a2efa5..5cac2693c5 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -23,6 +23,7 @@
#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/hle/service/nvdrv/devices/nx2_stub_device.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/nvdrv/nvmemp.h"
@@ -125,6 +126,11 @@ void LoopProcess(Core::System& system) {
Module::Module(Core::System& system)
: container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} {
+ nx2_stub_builder = [this, &system](DeviceFD fd, const std::string& name) {
+ auto device = std::make_shared<Devices::nx2_stub_device>(system, name);
+ open_files.emplace(fd, device);
+ return std::static_pointer_cast<Devices::nvdevice>(device);
+ };
builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) {
auto device = std::make_shared<Devices::nvhost_as_gpu>(system, *this, container);
return open_files.emplace(fd, std::move(device)).first;
@@ -181,6 +187,15 @@ NvResult Module::VerifyFD(DeviceFD fd) const {
DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_id) {
auto it = builders.find(device_name);
+ if (it == builders.end() && device_name.starts_with("/dev/nvhost-")) {
+ // Newer nvgpu nodes used by Switch 2 drivers (e.g. /dev/nvhost-tsg-gpu): serve them with
+ // a logged stub instead of failing the open.
+ LOG_WARNING(Service_NVDRV, "Opening unknown device {} as a Switch 2 stub", device_name);
+ const DeviceFD fd = next_fd++;
+ auto device = nx2_stub_builder(fd, device_name);
+ device->OnOpen(session_id, fd);
+ return fd;
+ }
if (it == builders.end()) {
LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
return INVALID_NVDRV_FD;
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index f503f108c3..ba8ab6a40e 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -98,6 +98,8 @@ private:
friend class EventInterface;
/// Manages syncpoints on the host
+ /// Builds a logged stub for nvgpu device nodes only Switch 2 drivers use.
+ std::function<std::shared_ptr<Devices::nvdevice>(DeviceFD, const std::string&)> nx2_stub_builder;
NvCore::Container container;
/// Id to use for the next open file descriptor.