// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 Skyline Team and Contributors // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h" #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/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" #include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/server_manager.h" #include "video_core/gpu.h" namespace Service::Nvidia { EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {} EventInterface::~EventInterface() = default; Kernel::KEvent* EventInterface::CreateEvent(std::string name) { Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); return new_event; } void EventInterface::FreeEvent(Kernel::KEvent* event) { module.service_context.CloseEvent(event); } void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); auto module = std::make_shared(system); const auto NvdrvInterfaceFactoryForApplication = [&, module] { return std::make_shared(system, module, "nvdrv"); }; const auto NvdrvInterfaceFactoryForApplets = [&, module] { return std::make_shared(system, module, "nvdrv:a"); }; const auto NvdrvInterfaceFactoryForSysmodules = [&, module] { return std::make_shared(system, module, "nvdrv:s"); }; const auto NvdrvInterfaceFactoryForTesting = [&, module] { return std::make_shared(system, module, "nvdrv:t"); }; server_manager->RegisterNamedService("nvdrv", NvdrvInterfaceFactoryForApplication); server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets); server_manager->RegisterNamedService("nvdrv:s", NvdrvInterfaceFactoryForSysmodules); server_manager->RegisterNamedService("nvdrv:t", NvdrvInterfaceFactoryForTesting); server_manager->RegisterNamedService("nvmemp", std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } Module::Module(Core::System& system) : container{system.Host1x()}, service_context{system, "nvdrv"}, events_interface{*this} { builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, *this, container); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, events_interface, container); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, events_interface); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, container); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, container); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, events_interface, container); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, container); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system); return open_files.emplace(fd, std::move(device)).first; }; builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { auto device = std::make_shared(system, container); return open_files.emplace(fd, std::move(device)).first; }; } Module::~Module() {} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } if (open_files.find(fd) == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } return NvResult::Success; } DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_id) { auto it = builders.find(device_name); if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); return INVALID_NVDRV_FD; } const DeviceFD fd = next_fd++; auto& builder = it->second; auto device = builder(fd)->second; device->OnOpen(session_id, fd); if (container.IsSessionActive(session_id)) { open_file_sessions.emplace(fd, session_id); } return fd; } NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, std::span input, std::span output) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } const auto itr = open_files.find(fd); if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } return itr->second->Ioctl1(fd, command, input, output); } NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, std::span input, std::span inline_input, std::span output) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } const auto itr = open_files.find(fd); if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } return itr->second->Ioctl2(fd, command, input, inline_input, output); } NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, std::span input, std::span output, std::span inline_output) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } const auto itr = open_files.find(fd); if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } return itr->second->Ioctl3(fd, command, input, output, inline_output); } NvResult Module::Close(DeviceFD fd) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } const auto itr = open_files.find(fd); if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } itr->second->OnClose(fd); open_files.erase(itr); open_file_sessions.erase(fd); return NvResult::Success; } NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { if (fd < 0) { LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd); return NvResult::InvalidState; } const auto itr = open_files.find(fd); if (itr == open_files.end()) { LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd); return NvResult::NotImplemented; } event = itr->second->QueryEvent(event_id); if (!event) { return NvResult::BadParameter; } return NvResult::Success; } static bool ContainsSession(std::span session_ids, NvCore::SessionId session_id) { return std::ranges::any_of(session_ids, [session_id](const auto candidate) { return candidate.id == session_id.id; }); } static void AppendUniqueSession(std::vector& session_ids, NvCore::SessionId session_id) { if (!ContainsSession(session_ids, session_id)) { session_ids.push_back(session_id); } } size_t Module::CloseFilesForSessions(std::span session_ids) { std::vector fds; fds.reserve(open_file_sessions.size()); for (const auto& [fd, session_id] : open_file_sessions) { if (ContainsSession(session_ids, session_id)) { fds.push_back(fd); } } for (const auto fd : fds) { Close(fd); } return fds.size(); } void Module::CloseSession(NvCore::SessionId session_id) { container.CloseSession(session_id); } void Module::TrackSessionAruid(NvCore::SessionId session_id, u64 aruid) { const bool active = container.IsSessionActive(session_id); if (active) { session_aruids[session_id.id] = aruid; } } std::vector Module::GetSessionIdsForAruid(u64 aruid) const { std::vector session_ids; for (const auto& [session_id, session_aruid] : session_aruids) { if (session_aruid == aruid) { session_ids.push_back(NvCore::SessionId{session_id}); } } return session_ids; } size_t Module::ResetForProcess(Kernel::KProcess* process) { const auto process_id = process != nullptr ? process->GetProcessId() : 0; auto session_ids = container.GetSessionIdsForProcess(process); if (process_id != 0) { for (const auto session_id : GetSessionIdsForAruid(process_id)) { AppendUniqueSession(session_ids, session_id); } } const auto active_session_ids = container.GetActiveSessionIds(); const auto active_before = active_session_ids.size(); const bool has_active_candidate = std::ranges::any_of(session_ids, [this](const auto session_id) { return container.IsSessionActive(session_id); }); bool used_active_sessions = false; if (!has_active_candidate && !active_session_ids.empty()) { for (const auto session_id : active_session_ids) { AppendUniqueSession(session_ids, session_id); } used_active_sessions = true; } const auto closed_files = CloseFilesForSessions(session_ids); const auto closed_sessions = container.CloseSessions(session_ids); for (const auto session_id : session_ids) { if (!container.IsSessionActive(session_id)) { session_aruids.erase(session_id.id); } } if (used_active_sessions) { LOG_WARNING(Service_NVDRV, "NextLoad: NVDRV reset used active sessions because process-owned " "sessions were not found, process_id={}, sessions={}, files={}, active_before={}", process_id, closed_sessions, closed_files, active_before); } return closed_sessions; } } // namespace Service::Nvidia