diff options
Diffstat (limited to 'src/core')
71 files changed, 68 insertions, 4139 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 168037e5dc..1b578f67ba 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -460,18 +460,6 @@ add_library(core STATIC hle/service/am/service/application_proxy.h hle/service/am/service/application_proxy_service.cpp hle/service/am/service/application_proxy_service.h - hle/service/am/service/performance_functions.cpp - hle/service/am/service/performance_functions.h - hle/service/am/service/gpu_session.cpp - hle/service/am/service/gpu_session.h - hle/service/am/service/graphics_session.cpp - hle/service/am/service/graphics_session.h - hle/service/am/service/graphics_supervisor.cpp - hle/service/am/service/graphics_supervisor.h - hle/service/am/service/graphics_supervisor_protocol.h - hle/service/am/service/graphics_syncpoint_cache.h - hle/service/am/service/graphics_scaling_parameters_functions.cpp - hle/service/am/service/graphics_scaling_parameters_functions.h hle/service/am/service/audio_controller.cpp hle/service/am/service/audio_controller.h hle/service/am/service/common_state_getter.cpp @@ -867,8 +855,6 @@ add_library(core STATIC hle/service/nvdrv/devices/nvhost_ctrl_gpu.h hle/service/nvdrv/devices/nvhost_gpu.cpp hle/service/nvdrv/devices/nvhost_gpu.h - hle/service/nvdrv/devices/nvhost_tsg_gpu.cpp - hle/service/nvdrv/devices/nvhost_tsg_gpu.h hle/service/nvdrv/devices/nvhost_nvdec.cpp hle/service/nvdrv/devices/nvhost_nvdec.h hle/service/nvdrv/devices/nvhost_nvdec_common.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index b5190e40cf..894857a86d 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -194,7 +194,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s // Callbacks config.callbacks = std::addressof(*m_cb); - config.enable_pauth_returns = m_cb->m_process && m_cb->m_process->UsesPAuthReturns(); // Memory if (page_table) { @@ -445,8 +444,6 @@ void ArmDynarmic64::ClearInstructionCache() { } void ArmDynarmic64::InvalidateCacheRange(u64 addr, std::size_t size) { - // Refetch the source page too, including instructions restored by the debugger. - m_cb->last_code_addr = u64(-1); m_jit->InvalidateCacheRange(addr, size); } diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 0087548978..97c2706abb 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -21,7 +21,6 @@ #include "common/scope_exit.h" #include "common/settings.h" #include "core/device_memory.h" -#include "core/hle/kernel/board/nintendo/nx/k_system_control.h" #include "core/device_memory_manager.h" #include "core/memory.h" @@ -173,7 +172,7 @@ template <typename Traits> DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_) : physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} , device_inter{nullptr} - , compressed_device_addr(Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() >> Memory::YUZU_PAGEBITS) + , compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) , tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) { impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>(); diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index b528523e67..63ca9d3fe3 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -20,10 +20,6 @@ ProgramMetadata::ProgramMetadata() = default; ProgramMetadata::~ProgramMetadata() = default; Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { - uses_extended_acid = false; - if (!file) { - return Loader::ResultStatus::ErrorBadNPDMHeader; - } const std::size_t total_size = file->GetSize(); if (total_size < sizeof(Header)) { return Loader::ResultStatus::ErrorBadNPDMHeader; @@ -33,55 +29,17 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { return Loader::ResultStatus::ErrorBadNPDMHeader; } - if (npdm_header.magic != std::array{'M', 'E', 'T', 'A'}) { - return Loader::ResultStatus::ErrorBadNPDMHeader; - } - - const auto contains = [](u64 offset, u64 size, u64 limit) { - return offset <= limit && size <= limit - offset; - }; - const u64 acid_offset = npdm_header.acid_offset; - const u64 acid_size = npdm_header.acid_size; - const u64 aci_offset = npdm_header.aci_offset; - const u64 aci_size = npdm_header.aci_size; - if (acid_offset < sizeof(Header) || !contains(acid_offset, acid_size, total_size)) { + if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) { return Loader::ResultStatus::ErrorBadACIDHeader; } - const auto read_acid = [&](u64 body_offset) { - return contains(body_offset, sizeof(AcidHeader), acid_size) && - file->ReadObject(&acid_header, acid_offset + body_offset) == sizeof(AcidHeader) && - acid_header.magic == std::array{'A', 'C', 'I', 'D'}; - }; - if (!read_acid(0x200)) { - if (!read_acid(0x300)) { - return Loader::ResultStatus::ErrorBadACIDHeader; - } - uses_extended_acid = true; - } - - if (aci_offset < sizeof(Header) || !contains(aci_offset, aci_size, total_size) || - aci_size < sizeof(AciHeader) || - sizeof(AciHeader) != file->ReadObject(&aci_header, aci_offset) || - aci_header.magic != std::array{'A', 'C', 'I', '0'}) { + if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) { return Loader::ResultStatus::ErrorBadACIHeader; } - constexpr u64 fac_read_size = 4 + sizeof(FileAccessControl::permissions) + - sizeof(FileAccessControl::unknown); - if (acid_header.fac_size < fac_read_size || - !contains(acid_header.fac_offset, acid_header.fac_size, acid_size)) { - return Loader::ResultStatus::ErrorBadFileAccessControl; - } - constexpr u64 fah_read_size = 4 + sizeof(FileAccessHeader::permissions) + 4 * sizeof(u32); - if (aci_header.fah_size < fah_read_size || - !contains(aci_header.fah_offset, aci_header.fah_size, aci_size)) { - return Loader::ResultStatus::ErrorBadFileAccessHeader; - } - // Load acid_file_access per-component instead of the entire struct, since this struct does not // reflect the layout of the real data. - std::size_t current_offset = acid_offset + acid_header.fac_offset; + std::size_t current_offset = acid_header.fac_offset; if (sizeof(FileAccessControl::version) != file->ReadBytes(&acid_file_access.version, sizeof(FileAccessControl::version), current_offset)) { @@ -99,7 +57,7 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { } // Load aci_file_access per-component instead of the entire struct, same as acid_file_access - current_offset = aci_offset + aci_header.fah_offset; + current_offset = aci_header.fah_offset; if (sizeof(FileAccessHeader::version) != file->ReadBytes(&aci_file_access.version, sizeof(FileAccessHeader::version), current_offset)) { @@ -131,10 +89,6 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { return Loader::ResultStatus::ErrorBadFileAccessHeader; } - if (aci_header.kac_size % sizeof(u32) != 0 || - !contains(aci_header.kac_offset, aci_header.kac_size, aci_size)) { - return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors; - } aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); const u64 read_size = aci_header.kac_size; const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset; @@ -174,7 +128,6 @@ void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address u32 main_thread_stack_size, u64 title_id, u64 filesystem_permissions, u32 system_resource_size, KernelCapabilityDescriptors capabilities) { - uses_extended_acid = false; npdm_header.has_64_bit_instructions.Assign(is_64_bit); npdm_header.address_space_type.Assign(address_space); npdm_header.main_thread_priority = static_cast<u8>(main_thread_prio); diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index eb4cf11894..115e6d6cd1 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -77,9 +77,6 @@ public: u8 GetMainThreadCore() const; u32 GetMainThreadStackSize() const; u64 GetTitleID() const; - bool UsesExtendedAcid() const { - return uses_extended_acid; - } u64 GetFilesystemPermissions() const; u32 GetSystemResourceSize() const; PoolPartition GetPoolPartition() const; @@ -119,6 +116,8 @@ private: static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); struct AcidHeader { + std::array<u8, 0x100> signature; + std::array<u8, 0x100> nca_modulus; std::array<char, 4> magic; u32_le nca_size; std::array<u8, 0x4> reserved; @@ -140,7 +139,7 @@ private: INSERT_PADDING_BYTES(0x8); }; - static_assert(sizeof(AcidHeader) == 0x40, "ACID header body size is wrong"); + static_assert(sizeof(AcidHeader) == 0x240, "ACID header structure size is wrong"); struct AciHeader { std::array<char, 4> magic; @@ -178,7 +177,6 @@ private: }; Header npdm_header{}; - bool uses_extended_acid{}; AciHeader aci_header{}; AcidHeader acid_header{}; diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 2abca31d87..f6543bdb3a 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -195,9 +195,7 @@ enum class KMemoryAttribute : u8 { Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked), - GpuAccessible = static_cast<u8>(Svc::MemoryAttribute::GpuAccessible), - - SetMask = Uncached | PermissionLocked | GpuAccessible, + SetMask = Uncached | PermissionLocked, }; DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute); diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index fd5d2c2dce..2a0a7d49d5 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -2102,8 +2102,7 @@ Result KPageTableBase::SetMemoryAttribute(KProcessAddress addr, size_t size, KMe constexpr KMemoryAttribute AttributeTestMask = ~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared); const KMemoryState state_test_mask = - (True(mask & (KMemoryAttribute::Uncached | KMemoryAttribute::GpuAccessible)) - ? KMemoryState::FlagCanChangeAttribute + (True(mask & KMemoryAttribute::Uncached) ? KMemoryState::FlagCanChangeAttribute : KMemoryState::None) | (True(mask & KMemoryAttribute::PermissionLocked) ? KMemoryState::FlagCanPermissionLock : KMemoryState::None); diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 813d8e9402..27d1b1bfc4 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -1260,7 +1260,6 @@ Result KProcess::LoadFromMetadata(KernelCore& kernel, const FileSys::ProgramMeta // Assign remaining properties. m_ideal_core_id = metadata.GetMainThreadCore(); - m_uses_pauth_returns = metadata.UsesExtendedAcid(); // Set up emulation context. this->InitializeInterfaces(kernel); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index ca96591ed0..b5693751c3 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -135,7 +135,6 @@ private: bool m_is_signaled : 1 = false; bool m_is_initialized : 1 = false; bool m_is_application : 1 = false; - bool m_uses_pauth_returns : 1 = false; bool m_is_default_application_system_resource : 1 = false; bool m_is_suspended : 1 = false; bool m_is_immortal : 1 = false; @@ -249,10 +248,6 @@ public: return m_is_application; } - bool UsesPAuthReturns() const { - return m_uses_pauth_returns; - } - bool IsDefaultApplicationSystemResource() const { return m_is_default_application_system_resource; } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index c7beb9c3ce..4f1d849209 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -120,13 +120,13 @@ struct KernelCore::Impl { } void TerminateAllProcesses() { - auto& kernel = system.Kernel(); - // Applet cleanup can remove processes while termination waits for its threads. - auto processes = kernel.GetProcessList(); - for (auto& process : processes) { - process->Terminate(kernel); - kernel.RemoveProcess(process.GetPointerUnsafe()); + std::scoped_lock lk{process_list_lock}; + for (auto& process : process_list) { + process->Terminate(system.Kernel()); + process->Close(system.Kernel()); + process = nullptr; } + process_list.clear(); } void Shutdown(KernelCore& kernel) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c91ae43dd8..3542d5a3b6 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -2370,10 +2370,6 @@ static void Call32(Core::System& system, u32 imm, std::span<uint64_t, 8> args) { static void Call64(Core::System& system, u32 imm, std::span<uint64_t, 8> args) { switch (SvcId(imm)) { - case SvcId::SetGpuMemoryAttribute: - args[0] = SetGpuMemoryAttribute(system, args[0], args[1], - static_cast<u32>(args[2]), static_cast<u32>(args[3])).raw; - return; case SvcId::SetHeapSize: return SvcWrap_SetHeapSize64(system, args); case SvcId::SetMemoryPermission: return SvcWrap_SetMemoryPermission64(system, args); case SvcId::SetMemoryAttribute: return SvcWrap_SetMemoryAttribute64(system, args); diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 422b8b8719..356c1c109a 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -22,7 +22,6 @@ namespace Kernel::Svc { Result SetHeapSize(Core::System& system, uint64_t* out_address, uint64_t size); Result SetMemoryPermission(Core::System& system, uint64_t address, uint64_t size, MemoryPermission perm); Result SetMemoryAttribute(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr); -Result SetGpuMemoryAttribute(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr); Result MapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size); Result UnmapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size); Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address); @@ -505,7 +504,6 @@ enum class SvcId : u32 { CreateResourceLimit = 0x7d, SetResourceLimitLimitValue = 0x7e, CallSecureMonitor = 0x7f, - SetGpuMemoryAttribute = 0x80, MapInsecureMemory = 0x90, UnmapInsecureMemory = 0x91, }; diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp index aea7407542..863bb58fa8 100644 --- a/src/core/hle/kernel/svc/svc_info.cpp +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -14,32 +14,11 @@ #include "core/hle/kernel/svc_version.h" namespace Kernel::Svc { -namespace { -// Identified from the extended SDK's RemoteRegionManager initialization. -constexpr InfoType RemoteRegionAddress = static_cast<InfoType>(29); -constexpr InfoType RemoteRegionSize = static_cast<InfoType>(30); -constexpr InfoType RemoteMemoryUsed = static_cast<InfoType>(31); -constexpr InfoType RemoteMemoryTotal = static_cast<InfoType>(32); -constexpr InfoType MemoryMappingAlignment = static_cast<InfoType>(33); -} // namespace /// Gets system/memory information for the current process Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle, u64 info_sub_id) { LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}", info_id_type, info_sub_id, handle); u32 info_id = u32(info_id_type); - if (info_id_type == RemoteRegionAddress || info_id_type == RemoteRegionSize || - info_id_type == RemoteMemoryUsed || info_id_type == RemoteMemoryTotal || - info_id_type == MemoryMappingAlignment) { - R_UNLESS(info_sub_id == 0, ResultInvalidEnumValue); - const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable(); - KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), handle); - R_UNLESS(process.IsNotNull(), ResultInvalidHandle); - R_UNLESS(process->UsesPAuthReturns(), ResultInvalidEnumValue); - // Remote mappings are absent; ordinary mappings use the kernel page size. - *result = info_id_type == MemoryMappingAlignment ? PageSize : 0; - LOG_DEBUG(Kernel_SVC, "Extended memory info_id={:#x}, value={:#x}", info_id, *result); - R_SUCCEED(); - } switch (info_id_type) { case InfoType::CoreMask: case InfoType::PriorityMask: @@ -145,8 +124,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle default: break; } - LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#x}, sub_id={:#x}, handle={:#x}", - info_id, info_sub_id, handle); + LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id); R_THROW(ResultInvalidEnumValue); } case InfoType::DebuggerAttached: @@ -280,8 +258,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); } default: - LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#x}, sub_id={:#x}, handle={:#x}", - info_id, info_sub_id, handle); + LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id); R_THROW(ResultInvalidEnumValue); } } diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp index fadde6c093..875590d6f7 100644 --- a/src/core/hle/kernel/svc/svc_ipc.cpp +++ b/src/core/hle/kernel/svc/svc_ipc.cpp @@ -13,7 +13,6 @@ #include "core/hle/kernel/k_scoped_resource_reservation.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/svc.h" #include "core/hle/kernel/svc_results.h" @@ -143,23 +142,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes /// Makes a blocking IPC call to a service. Result SendSyncRequest(Core::System& system, Handle session_handle) { - auto& process = GetCurrentProcess(system.Kernel()); - if (!process.UsesPAuthReturns()) { - R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle)); - } - const auto tls = GetCurrentThread(system.Kernel()).GetTlsAddress(); - const u64 header = process.GetMemory().Read64(tls); - const bool trace_tipc = (header & 0xffff) >= 16; - if (trace_tipc) { - LOG_DEBUG(Kernel_SVC, "TIPC send handle={:#x}, header={:#x}, special={:#x}", - session_handle, header, process.GetMemory().Read64(tls + 8)); - } - const auto result = SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle); - if (trace_tipc) { - LOG_DEBUG(Kernel_SVC, "TIPC receive result={:#x}, header={:#x}, data={:#x}", - result.raw, process.GetMemory().Read64(tls), process.GetMemory().Read64(tls + 8)); - } - R_RETURN(result); + R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle)); } Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size, diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp index 53c0d43456..970912fe50 100644 --- a/src/core/hle/kernel/svc/svc_memory.cpp +++ b/src/core/hle/kernel/svc/svc_memory.cpp @@ -126,31 +126,6 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); // Set the memory attribute. - const auto result = page_table.SetMemoryAttribute(address, size, - static_cast<KMemoryAttribute>(mask), - static_cast<KMemoryAttribute>(attr)); - if (GetCurrentProcess(system.Kernel()).UsesPAuthReturns()) { - LOG_DEBUG(Kernel_SVC, "native memory attribute result={:#x}", result.raw); - } - R_RETURN(result); -} - -// Captured native SDK callers set bit 0x20 before registering GPU-accessible -// memory and clear it during destruction. This tracks eligibility on real mapped -// memory; GPU mappings and submission remain separate operations. -Result SetGpuMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, u32 attr) { - auto& process = GetCurrentProcess(system.Kernel()); - R_UNLESS(process.UsesPAuthReturns(), ResultNotImplemented); - LOG_DEBUG(Kernel_SVC, "GPU memory address={:#x}, size={:#x}, mask={:#x}, attr={:#x}", - address, size, mask, attr); - R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); - R_UNLESS(size > 0 && Common::IsAligned(size, PageSize), ResultInvalidSize); - R_UNLESS(address < address + size, ResultInvalidCurrentMemory); - constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::GpuAccessible); - R_UNLESS((attr & ~mask) == 0 && ((mask | attr) & ~SupportedMask) == 0, - ResultInvalidCombination); - auto& page_table = process.GetPageTable(); - R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); R_RETURN(page_table.SetMemoryAttribute(address, size, static_cast<KMemoryAttribute>(mask), static_cast<KMemoryAttribute>(attr))); } diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp index 2f72f60215..2b019c52d1 100644 --- a/src/core/hle/kernel/svc/svc_synchronization.cpp +++ b/src/core/hle/kernel/svc/svc_synchronization.cpp @@ -15,24 +15,13 @@ namespace Kernel::Svc { -namespace { -void LogInvalidNativeSyncHandle(Core::System& system, const char* operation, Handle handle) { - if (!GetCurrentProcess(system.Kernel()).UsesPAuthReturns()) { return; } - const auto& context = GetCurrentThread(system.Kernel()).GetContext(); - LOG_DEBUG(Kernel_SVC, "native {} invalid handle={:#x}, pc={:#x}, lr={:#x}, fp={:#x}", - operation, handle, context.pc, context.lr, context.fp); -} -} - /// Close a handle Result CloseHandle(Core::System& system, Handle handle) { LOG_TRACE(Kernel_SVC, "Closing handle {:#08x}", handle); // Remove the handle. - if (!GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(system.Kernel(), handle)) { - LogInvalidNativeSyncHandle(system, "CloseHandle", handle); - R_THROW(ResultInvalidHandle); - } + R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(system.Kernel(), handle), + ResultInvalidHandle); R_SUCCEED(); } @@ -60,7 +49,6 @@ Result ResetSignal(Core::System& system, Handle handle) { } } - LogInvalidNativeSyncHandle(system, "ResetSignal", handle); R_THROW(ResultInvalidHandle); } @@ -84,10 +72,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha R_UNLESS(GetCurrentMemory(system.Kernel()).ReadBlock(user_handles, handles.data(), sizeof(Handle) * num_handles), ResultInvalidPointer); // Convert the handles to objects. - if (!handle_table.GetMultipleObjects<KSynchronizationObject>(system.Kernel(), objs.data(), handles.data(), num_handles)) { - for (s32 i = 0; i < num_handles; ++i) { LogInvalidNativeSyncHandle(system, "WaitSynchronization set", handles[i]); } - R_THROW(ResultInvalidHandle); - } + R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(system.Kernel(), objs.data(), handles.data(), num_handles), ResultInvalidHandle); } // Ensure handles are closed when we're done. diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index b4eb0b5719..dbca13dd26 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -16,11 +16,8 @@ namespace Kernel::Svc { namespace { -bool IsValidVirtualCoreId(const KProcess& process, int32_t core_id) { - // Extended processes use the kernel's existing virtual-core mapping. - const auto core_count = process.UsesPAuthReturns() ? Core::Hardware::NumVirtualCores - : Core::Hardware::NUM_CPU_CORES; - return 0 <= core_id && core_id < static_cast<int32_t>(core_count); +constexpr bool IsValidVirtualCoreId(int32_t core_id) { + return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); } } // Anonymous namespace @@ -41,7 +38,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u } // Validate arguments. - R_UNLESS(IsValidVirtualCoreId(process, core_id), ResultInvalidCoreId); + R_UNLESS(IsValidVirtualCoreId(core_id), ResultInvalidCoreId); R_UNLESS(((1ull << core_id) & process.GetCoreMask()) != 0, ResultInvalidCoreId); R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority, @@ -265,7 +262,7 @@ Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id R_UNLESS(affinity_mask != 0, ResultInvalidCombination); // Validate the core id. - if (IsValidVirtualCoreId(GetCurrentProcess(system.Kernel()), core_id)) { + if (IsValidVirtualCoreId(core_id)) { R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination); } else { R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare, diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index c7b68cc0df..b0f4796da4 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -50,7 +50,6 @@ enum class MemoryAttribute : u32 { DeviceShared = (1 << 2), Uncached = (1 << 3), PermissionLocked = (1 << 4), - GpuAccessible = (1 << 5), }; DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index 69d2876363..571fa65219 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -84,7 +84,6 @@ struct Applet { // Application functions bool game_play_recording_supported{}; bool media_playback_state{}; - bool graphics_scale_changed_notification_enabled{}; GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled}; bool jit_service_launched{}; bool application_crash_report_enabled{}; diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index f86806dd3c..90d292c4a2 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -118,8 +118,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ {220, nullptr, "Unknown220"}, // [20.0.0+] {300, nullptr, "CreateMovieWriter"}, // [19.0.0+] {310, nullptr, "Unknown310"}, // [20.0.0+] - {320, D<&IApplicationFunctions::GetScreenShotAndContinuousRecordingInitiallyEnabled>, - "GetScreenShotAndContinuousRecordingInitiallyEnabled"}, + {320, nullptr, "Unknown320"}, // [20.0.0+] {330, D<&IApplicationFunctions::Unknown330>, "Unknown330"}, // [20.0.0+] {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, @@ -545,17 +544,6 @@ Result IApplicationFunctions::GetUnknownEvent210( R_SUCCEED(); } -Result IApplicationFunctions::GetScreenShotAndContinuousRecordingInitiallyEnabled( - Out<bool> out_screenshot_enabled, Out<bool> out_recording_enabled) { - std::scoped_lock lk{m_applet->lock}; - *out_screenshot_enabled = m_applet->screenshot_permission != ScreenshotPermission::Disable; - *out_recording_enabled = m_applet->game_play_recording_supported && - m_applet->game_play_recording_state == GamePlayRecordingState::Enabled; - LOG_DEBUG(Service_AM, "called, screenshot={}, recording={}", *out_screenshot_enabled, - *out_recording_enabled); - R_SUCCEED(); -} - Result IApplicationFunctions::Unknown330(Out<u8> out) { LOG_DEBUG(Service_AM, "called"); *out = 0; diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h index 172b712f14..b97ed7e25e 100644 --- a/src/core/hle/service/am/service/application_functions.h +++ b/src/core/hle/service/am/service/application_functions.h @@ -82,8 +82,6 @@ private: Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result CreateApplicationAndRequestToStart(u64 application_id); Result GetUnknownEvent210(OutCopyHandle<Kernel::KReadableEvent> out_event); - Result GetScreenShotAndContinuousRecordingInitiallyEnabled( - Out<bool> out_screenshot_enabled, Out<bool> out_recording_enabled); Result Unknown330(Out<u8> out); Result PrepareForJit(); diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp index 02502ae764..df438e1e17 100644 --- a/src/core/hle/service/am/service/application_proxy.cpp +++ b/src/core/hle/service/am/service/application_proxy.cpp @@ -11,9 +11,7 @@ #include "core/hle/service/am/service/common_state_getter.h" #include "core/hle/service/am/service/debug_functions.h" #include "core/hle/service/am/service/display_controller.h" -#include "core/hle/service/am/service/graphics_scaling_parameters_functions.h" #include "core/hle/service/am/service/library_applet_creator.h" -#include "core/hle/service/am/service/performance_functions.h" #include "core/hle/service/am/service/process_winding_controller.h" #include "core/hle/service/am/service/self_controller.h" #include "core/hle/service/am/service/window_controller.h" @@ -35,9 +33,6 @@ IApplicationProxy::IApplicationProxy(Core::System& system_, std::shared_ptr<Appl {10, D<&IApplicationProxy::GetProcessWindingController>, "GetProcessWindingController"}, {11, D<&IApplicationProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"}, {20, D<&IApplicationProxy::GetApplicationFunctions>, "GetApplicationFunctions"}, - {40, D<&IApplicationProxy::GetPerformanceFunctions>, "GetPerformanceFunctions"}, - {60, D<&IApplicationProxy::GetGraphicsScalingParametersFunctions>, - "GetGraphicsScalingParametersFunctions"}, {1000, D<&IApplicationProxy::GetDebugFunctions>, "GetDebugFunctions"}, }; // clang-format on @@ -111,18 +106,4 @@ Result IApplicationProxy::GetApplicationFunctions( R_SUCCEED(); } -Result IApplicationProxy::GetPerformanceFunctions( - Out<SharedPointer<IPerformanceFunctions>> out_functions) { - LOG_DEBUG(Service_AM, "called"); - *out_functions = std::make_shared<IPerformanceFunctions>(system); - R_SUCCEED(); -} - -Result IApplicationProxy::GetGraphicsScalingParametersFunctions( - Out<SharedPointer<IGraphicsScalingParametersFunctions>> out_functions) { - LOG_DEBUG(Service_AM, "called"); - *out_functions = std::make_shared<IGraphicsScalingParametersFunctions>(system, m_applet); - R_SUCCEED(); -} - } // namespace Service::AM diff --git a/src/core/hle/service/am/service/application_proxy.h b/src/core/hle/service/am/service/application_proxy.h index aa1ab1938a..b56f597605 100644 --- a/src/core/hle/service/am/service/application_proxy.h +++ b/src/core/hle/service/am/service/application_proxy.h @@ -17,9 +17,7 @@ class IApplicationFunctions; class ICommonStateGetter; class IDebugFunctions; class IDisplayController; -class IGraphicsScalingParametersFunctions; class ILibraryAppletCreator; -class IPerformanceFunctions; class IProcessWindingController; class ISelfController; class IWindowController; @@ -45,10 +43,6 @@ private: Result GetApplicationFunctions( Out<SharedPointer<IApplicationFunctions>> out_application_functions); - Result GetPerformanceFunctions(Out<SharedPointer<IPerformanceFunctions>> out_functions); - Result GetGraphicsScalingParametersFunctions( - Out<SharedPointer<IGraphicsScalingParametersFunctions>> out_functions); - private: WindowSystem& m_window_system; Kernel::KProcess* const m_process; diff --git a/src/core/hle/service/am/service/application_proxy_service.cpp b/src/core/hle/service/am/service/application_proxy_service.cpp index da11cce94d..7727951d8c 100644 --- a/src/core/hle/service/am/service/application_proxy_service.cpp +++ b/src/core/hle/service/am/service/application_proxy_service.cpp @@ -19,8 +19,6 @@ IApplicationProxyService::IApplicationProxyService(Core::System& system_, : ServiceFramework{system_, "appletOE"}, m_window_system{window_system} { static const FunctionInfo functions[] = { {0, D<&IApplicationProxyService::OpenApplicationProxy>, "OpenApplicationProxy"}, - {10, D<&IApplicationProxyService::OpenApplicationProxyWithFlags>, - "OpenApplicationProxyWithFlags"}, }; RegisterHandlers(functions); } @@ -42,15 +40,6 @@ Result IApplicationProxyService::OpenApplicationProxy( } } -Result IApplicationProxyService::OpenApplicationProxyWithFlags( - Out<SharedPointer<IApplicationProxy>> out_application_proxy, u32 flags, ClientProcessId pid, - InCopyHandle<Kernel::KProcess> process_handle) { - LOG_DEBUG(Service_AM, "called, flags={:#x}", flags); - // The extended SDK supplies an additional u32 before the process ID. - R_UNLESS(flags == 0 || flags == 2, ResultUnknown); - R_RETURN(OpenApplicationProxy(out_application_proxy, pid, process_handle)); -} - std::shared_ptr<Applet> IApplicationProxyService::GetAppletFromProcessId(ProcessId process_id) { return m_window_system.GetByAppletResourceUserId(process_id.pid); } diff --git a/src/core/hle/service/am/service/application_proxy_service.h b/src/core/hle/service/am/service/application_proxy_service.h index 5812f6133a..fce860a7c1 100644 --- a/src/core/hle/service/am/service/application_proxy_service.h +++ b/src/core/hle/service/am/service/application_proxy_service.h @@ -26,10 +26,6 @@ private: Result OpenApplicationProxy(Out<SharedPointer<IApplicationProxy>> out_application_proxy, ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle); - Result OpenApplicationProxyWithFlags( - Out<SharedPointer<IApplicationProxy>> out_application_proxy, u32 flags, ClientProcessId pid, - InCopyHandle<Kernel::KProcess> process_handle); - private: std::shared_ptr<Applet> GetAppletFromProcessId(ProcessId pid); diff --git a/src/core/hle/service/am/service/gpu_memory_registration.h b/src/core/hle/service/am/service/gpu_memory_registration.h deleted file mode 100644 index 37ac7fd377..0000000000 --- a/src/core/hle/service/am/service/gpu_memory_registration.h +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once -#include <optional> -#include <span> -#include "common/common_types.h" - -namespace Service::AM { -struct GpuMemoryRegistration { - u64 begin; - u64 end; - u8 flags; - u8 device; -}; - -constexpr std::optional<GpuMemoryRegistration> DecodeGpuMemoryRegistration(std::span<const u32> packet) { - if (packet.size() < 8 || packet[0] != 0x75 || packet[1] != 6) return std::nullopt; - // The SDK writes STRB at offsets 0x18 and 0x1c, leaving three padding bytes. - return GpuMemoryRegistration{u64{packet[2]} | (u64{packet[3]} << 32), - u64{packet[4]} | (u64{packet[5]} << 32), static_cast<u8>(packet[6]), static_cast<u8>(packet[7])}; -} -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/gpu_session.cpp b/src/core/hle/service/am/service/gpu_session.cpp deleted file mode 100644 index a83381d7bc..0000000000 --- a/src/core/hle/service/am/service/gpu_session.cpp +++ /dev/null @@ -1,633 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include <algorithm> -#include <cstring> -#include <mutex> -#include <vector> -#include "common/swap.h" -#include "common/alignment.h" -#include "core/hle/service/nvdrv/core/nvmap.h" -#include "core/core.h" -#include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/svc_results.h" -#include "core/hle/service/am/service/graphics_session.h" -#include "core/hle/service/am/service/gpu_session.h" -#include "core/hle/service/am/service/gpu_memory_registration.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" -#include "core/hle/service/nvdrv/devices/native_gpu_kind.h" - -namespace Service::AM { -struct GpuDeviceRegistry { - GpuDeviceRegistry(Core::System& system, std::shared_ptr<Nvidia::Module> module_, Kernel::KProcess* process_) - : context{system, "GpuDevices"}, module{std::move(module_)}, process{process_} { - process->Open(context.kernel); - } - ~GpuDeviceRegistry() { - for (const auto& fd : address_spaces) { if (fd) { module->Close(*fd); } } - if (nv_session) { module->GetContainer().CloseSession(Nvidia::NvCore::SessionId{*nv_session}); } - process->Close(context.kernel); - for (auto* event : events) { context.CloseEvent(event); } - } - KernelHelpers::ServiceContext context; - std::array<Kernel::KEvent*, 64> events{}; - struct Device { u64 name; u64 debug_label; }; - std::mutex mutex; - std::shared_ptr<Nvidia::Module> module; - Kernel::KProcess* process; - std::array<std::optional<s32>, 8> address_spaces{}; - std::array<bool, 8> address_space_registered{}; - std::optional<size_t> nv_session; - std::array<std::optional<Device>, 256> devices{}; -}; -namespace { -class IGpuWorker final : public ServiceFramework<IGpuWorker> { -public: - IGpuWorker(Core::System& system, std::shared_ptr<Nvidia::Module> module, - Kernel::KProcess* process, std::shared_ptr<GpuDeviceRegistry> devices) - : ServiceFramework{system, "IGpuWorker"}, m_devices{std::move(devices)}, - m_module{std::move(module)}, m_process{process} { - m_process->Open(kernel); - static const FunctionInfo functions[] = { - {101, &IGpuWorker::CreateGpuAccessibleMemory, "CreateGpuAccessibleMemory"}, - {102, &IGpuWorker::DestroyGpuAccessibleMemory, "DestroyGpuAccessibleMemory"}, - {211, &IGpuWorker::NormalMapGpuMemory4K, "NormalMapGpuMemory4K"}, - {212, &IGpuWorker::NormalModifyGpuMemory4K, "NormalModifyGpuMemory4K"}, - {213, &IGpuWorker::NormalUnmapGpuMemory4K, "NormalUnmapGpuMemory4K"}, - {214, &IGpuWorker::CreateSparseRegion, "CreateSparseRegion"}, - {215, &IGpuWorker::DestroySparseRegion, "DestroySparseRegion"}, - }; - RegisterHandlersTipc(functions); - } - ~IGpuWorker() override { - for (const auto& mapping : m_mappings) { - if (auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(mapping.as_id)) { - as->UnmapNative4K(mapping.address, mapping.size); - } - } - for (const auto& region : m_sparse_regions) { - if (auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(region.as_id)) { - as->DestroyNativeSparseRegion(region.address, region.size); - } - } - auto& nvmap = m_module->GetContainer().GetNvMapFile(); - for (const auto id : m_memory) { - if (const auto info = nvmap.FreeHandle(id, false); info && info->can_unlock) { - const auto result = m_process->GetPageTable().UnlockForDeviceAddressSpace( - info->address, info->size); - if (result.IsError()) { - LOG_ERROR(Service_AM, "GPU memory cleanup failed: {:#x}", result.raw); - } - } - } - if (m_nv_session) { - m_module->GetContainer().CloseSession(Nvidia::NvCore::SessionId{*m_nv_session}); - } - m_process->Close(kernel); - } - Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) override { - if (!ctx.IsTipc() || handlers_tipc.contains(ctx.GetCommand())) { - return ServiceFrameworkBase::HandleSyncRequest(session, ctx); - } - const auto guard = LockService(); - InvokeRequestTipc(ctx); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultNotImplemented.raw); - return system.IsPoweredOn() ? ctx.WriteToOutgoingCommandBuffer() : ResultSuccess; - } -private: - struct Mapping { u32 as_id; u64 address; u64 size; u32 memory_id; }; - std::vector<Mapping> m_mappings; - std::vector<Mapping> m_sparse_regions; - static u64 ReadPair(const u32* packet, size_t index) { - return u64{packet[index]} | (u64{packet[index + 1]} << 32); - } - bool OwnsAddressSpace(u32 id) const { - for (size_t i = 0; i < m_devices->address_spaces.size(); ++i) { - if (m_devices->address_spaces[i] && - static_cast<u32>(*m_devices->address_spaces[i]) == id && - m_devices->address_space_registered[i]) { return true; } - } - return false; - } - void CreateSparseRegion(HLERequestContext& ctx) { - const std::scoped_lock device_lock{m_devices->mutex}; - const auto* p = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - const u64 begin = ReadPair(p, 3), end = ReadPair(p, 5); - const u32 notification = p[11]; - if (p[0] == 0xe6 && p[1] == 10 && notification < m_devices->events.size() && m_devices->events[notification]) { - result = Kernel::ResultNotFound; - const u32 shift = p[7] & 0xff, device = p[9] & 0xff; - if (OwnsAddressSpace(p[2]) && device < m_devices->devices.size() && m_devices->devices[device]) { - result = Kernel::ResultInvalidArgument; - if ((shift == 12 || shift == 16) && (p[8] & 0xff) <= 1 && (p[10] & 0xff) <= 1 && - end >= begin && end != ~u64{0} && Common::IsAligned(begin, 1ULL << shift) && - Common::IsAligned(end - begin + 1, 1ULL << shift)) { - const auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(p[2]); - result = as && as->CreateNativeSparseRegion(begin, end - begin + 1) == Nvidia::NvResult::Success - ? ResultSuccess : Kernel::ResultInvalidCurrentMemory; - if (result.IsSuccess()) { m_sparse_regions.push_back({p[2], begin, end - begin + 1, 0}); } - } - } - m_devices->events[notification]->Signal(kernel); - } - LOG_DEBUG(Service_AM, "GPU sparse region as={}, VA={:#x}..{:#x}, result={:#x}", p[2], begin, end, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void DestroySparseRegion(HLERequestContext& ctx) { - const std::scoped_lock device_lock{m_devices->mutex}; - const auto* p = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - const u64 begin = ReadPair(p, 3), end = ReadPair(p, 5); - const u32 notification = p[8]; - if (p[0] == 0xe7 && p[1] == 7 && (p[7] & 0xff) <= 1 && - notification < m_devices->events.size() && m_devices->events[notification]) { - const auto it = std::find_if(m_sparse_regions.begin(), m_sparse_regions.end(), [&](const auto& region) { - return region.as_id == p[2] && region.address == begin && end >= begin && region.size == end - begin + 1; - }); - result = Kernel::ResultNotFound; - if (it != m_sparse_regions.end()) { - const auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(p[2]); - if (as && as->DestroyNativeSparseRegion(begin, it->size) == Nvidia::NvResult::Success) { - std::erase_if(m_mappings, [&](const auto& mapping) { - return mapping.as_id == p[2] && mapping.address >= begin && mapping.address <= end; - }); - m_sparse_regions.erase(it); - result = ResultSuccess; - } - } - m_devices->events[notification]->Signal(kernel); - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - Result MapMemory(const u32* packet, u8& selected_kind) { - R_UNLESS(OwnsAddressSpace(packet[2]), Kernel::ResultNotFound); - R_UNLESS(std::find(m_memory.begin(), m_memory.end(), packet[7]) != m_memory.end(), - Kernel::ResultNotFound); - R_UNLESS(packet[15] < m_devices->devices.size() && m_devices->devices[packet[15]], - Kernel::ResultNotFound); - R_UNLESS(packet[10] == 0 && packet[11] <= 1 && packet[14] == 0 && packet[16] <= 1, - Kernel::ResultNotImplemented); - const auto kind = Nvidia::Devices::SelectNativeMemoryKind( - static_cast<u8>(packet[12]), static_cast<u8>(packet[13])); - R_UNLESS(kind, Kernel::ResultNotImplemented); - const u64 begin = ReadPair(packet, 3), end = ReadPair(packet, 5); - R_UNLESS(end >= begin && end != ~u64{0}, Kernel::ResultInvalidSize); - R_UNLESS(m_mappings.size() < 1024, Kernel::ResultOutOfResource); - const auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(packet[2]); - R_UNLESS(as, Kernel::ResultNotFound); - const auto result = as->MapNative4K(packet[7], begin, ReadPair(packet, 8), - end - begin + 1, kind->backend); - R_UNLESS(result == Nvidia::NvResult::Success, Kernel::ResultInvalidCurrentMemory); - m_mappings.push_back({packet[2], begin, end - begin + 1, packet[7]}); - selected_kind = kind->selected; - R_SUCCEED(); - } - void NormalMapGpuMemory4K(HLERequestContext& ctx) { - std::array<u32, 18> words; - std::copy_n(ctx.CommandBuffer(), words.size(), words.begin()); - // These arguments are single-byte TIPC fields; SDK padding is not zeroed. - for (size_t i = 10; i <= 16; ++i) { words[i] &= 0xff; } - const auto* packet = words.data(); - const std::scoped_lock device_lock{m_devices->mutex}; - Result result = Kernel::ResultInvalidCombination; - u8 selected_kind{}; - if (packet[0] == 0xe3 && packet[1] == 16 && packet[17] < m_devices->events.size() && - m_devices->events[packet[17]]) { - result = MapMemory(packet, selected_kind); - m_devices->events[packet[17]]->Signal(kernel); - LOG_DEBUG(Service_AM, "GPU map4K as={}, memory={}, VA={:#x}..{:#x}, permission={}, cache={}, kinds={:#x}/{:#x}, mode={}, sync={}, notification={}, selected={:#x}, result={:#x}", - packet[2], packet[7], ReadPair(packet, 3), ReadPair(packet, 5), packet[10], - packet[11], packet[12], packet[13], packet[14], packet[16], packet[17], selected_kind, result.raw); - } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push<u32>(selected_kind); - } - void NormalModifyGpuMemory4K(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const std::scoped_lock device_lock{m_devices->mutex}; - Result result = Kernel::ResultInvalidCombination; - u8 selected{}; - const u64 begin = ReadPair(packet, 3), end = ReadPair(packet, 5); - const auto requested = static_cast<u8>(packet[7]); - const auto fallback = static_cast<u8>(packet[8]); - if (packet[0] == 0xe4 && packet[1] == 9 && (packet[9] & 0xff) <= 1 && - packet[10] < m_devices->events.size() && m_devices->events[packet[10]]) { - result = Kernel::ResultNotFound; - const auto kind = Nvidia::Devices::SelectNativeMemoryKind(requested, fallback); - const auto owner = std::find_if(m_mappings.begin(), m_mappings.end(), [&](const auto& mapping) { - return mapping.as_id == packet[2] && begin >= mapping.address && - begin - mapping.address < mapping.size && end >= begin && - end - mapping.address < mapping.size; - }); - if (kind && OwnsAddressSpace(packet[2]) && owner != m_mappings.end()) { - const auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(packet[2]); - if (as && as->ModifyNativeKind(begin, end - begin + 1, requested, fallback) == - Nvidia::NvResult::Success) { - selected = kind->selected; - result = ResultSuccess; - } - } - m_devices->events[packet[10]]->Signal(kernel); - } - LOG_DEBUG(Service_AM, "GPU modify4K as={}, VA={:#x}..{:#x}, kinds={:#x}/{:#x}, sync={}, notification={}, selected={}, result={:#x}", - packet[2], begin, end, requested, fallback, packet[9], packet[10], selected, result.raw); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push<u32>(selected); - } - void NormalUnmapGpuMemory4K(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const std::scoped_lock device_lock{m_devices->mutex}; - Result result = Kernel::ResultInvalidCombination; - u64 unmapped{}; - if (packet[0] == 0xe5 && packet[1] == 7 && (packet[7] & 0xff) <= 1 && - packet[8] < m_devices->events.size() && m_devices->events[packet[8]]) { - const u64 begin = ReadPair(packet, 3), end = ReadPair(packet, 5); - const auto it = std::find_if(m_mappings.begin(), m_mappings.end(), [&](const auto& mapping) { - return mapping.as_id == packet[2] && mapping.address == begin && - end >= begin && end - begin + 1 == mapping.size; - }); - result = Kernel::ResultNotFound; - if (it != m_mappings.end()) { - const auto as = m_module->GetDevice<Nvidia::Devices::nvhost_as_gpu>(it->as_id); - result = Kernel::ResultInvalidState; - if (as && as->UnmapNative4K(it->address, it->size) == Nvidia::NvResult::Success) { - unmapped = it->size; - m_mappings.erase(it); - result = ResultSuccess; - } - } - m_devices->events[packet[8]]->Signal(kernel); - LOG_DEBUG(Service_AM, "GPU unmap4K as={}, VA={:#x}..{:#x}, bytes={:#x}, result={:#x}", - packet[2], begin, end, unmapped, result.raw); - } - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result.raw); - rb.Push(unmapped); - } - Result RegisterMemory(u64 begin, u64 end, u32 flags, u32 device, u32& id) { - R_UNLESS(flags == 0, Kernel::ResultNotImplemented); - R_UNLESS(device < m_devices->devices.size(), Kernel::ResultNotFound); - const std::scoped_lock device_lock{m_devices->mutex}; - R_UNLESS(m_devices->devices[device], Kernel::ResultNotFound); - R_UNLESS(end >= begin && end != ~u64{0}, Kernel::ResultInvalidSize); - const u64 size = end - begin + 1; - R_UNLESS(Common::IsAligned(begin, Kernel::PageSize), Kernel::ResultInvalidAddress); - R_UNLESS(Common::IsAligned(size, Kernel::PageSize), Kernel::ResultInvalidSize); - R_UNLESS(m_memory.size() < 1024, Kernel::ResultOutOfResource); - auto& page_table = m_process->GetPageTable(); - R_UNLESS(page_table.Contains(begin, size), Kernel::ResultInvalidCurrentMemory); - for (u64 address = begin; address <= end;) { - Kernel::KMemoryInfo info{}; - Kernel::Svc::PageInfo page{}; - R_TRY(page_table.QueryInfo(&info, &page, address)); - R_UNLESS(True(info.m_attribute & Kernel::KMemoryAttribute::GpuAccessible), - Kernel::ResultInvalidCurrentMemory); - const u64 next = info.m_address + info.m_size; - R_UNLESS(next > address, Kernel::ResultInvalidCurrentMemory); - address = next; - } - if (!m_nv_session) { - m_nv_session = m_module->GetContainer().OpenSession(m_process).id; - } - auto& nvmap = m_module->GetContainer().GetNvMapFile(); - std::shared_ptr<Nvidia::NvCore::NvMap::Handle> handle; - R_UNLESS(nvmap.CreateHandle(size, handle) == Nvidia::NvResult::Success, - Kernel::ResultOutOfResource); - bool is_io{}; - const auto lock_result = page_table.LockForMapDeviceAddressSpace( - &is_io, begin, size, Kernel::KMemoryPermission::None, true, true); - if (lock_result.IsError()) { - nvmap.FreeHandle(handle->id, false); - R_RETURN(lock_result); - } - const auto alloc_result = handle->Alloc({}, Kernel::PageSize, 0, begin, - Nvidia::NvCore::SessionId{*m_nv_session}); - if (alloc_result != Nvidia::NvResult::Success) { - page_table.UnlockForDeviceAddressSpace(begin, size); - nvmap.FreeHandle(handle->id, false); - R_RETURN(Kernel::ResultOutOfResource); - } - id = handle->id; - m_memory.push_back(id); - R_SUCCEED(); - } - Result ReleaseMemory(u32 id, u32 notification) { - const std::scoped_lock device_lock{m_devices->mutex}; - R_UNLESS(notification < m_devices->events.size() && m_devices->events[notification], - Kernel::ResultInvalidCombination); - const auto it = std::find(m_memory.begin(), m_memory.end(), id); - R_UNLESS(it != m_memory.end(), Kernel::ResultNotFound); - auto& nvmap = m_module->GetContainer().GetNvMapFile(); - auto handle = nvmap.GetHandle(id); - R_UNLESS(handle, Kernel::ResultNotFound); - { - const std::scoped_lock handle_lock{handle->mutex}; - // No asynchronous GPU work is implemented yet. Never report release - // completion for memory still retained or pinned by another user. - R_UNLESS(handle->pins == 0 && handle->internal_dupes == 0 && handle->dupes == 1, - Kernel::ResultBusy); - } - // FreeHandle uses the final shared reference to decide when CPU memory - // may be unshared. Drop this temporary inspection reference first. - handle.reset(); - const auto info = nvmap.FreeHandle(id, false); - R_UNLESS(info && info->can_unlock, Kernel::ResultInvalidState); - m_memory.erase(it); - R_TRY(m_process->GetPageTable().UnlockForDeviceAddressSpace(info->address, info->size)); - m_devices->events[notification]->Signal(kernel); - R_SUCCEED(); - } - void DestroyGpuAccessibleMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const auto result = packet[0] == 0x76 && packet[1] == 2 - ? ReleaseMemory(packet[2], packet[3]) : Kernel::ResultInvalidCombination; - LOG_DEBUG(Service_AM, "GPU memory release id={}, notification={}, result={:#x}", - packet[2], packet[3], result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void CreateGpuAccessibleMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - u32 id{}; - Result result = Kernel::ResultInvalidCombination; - if (const auto request = DecodeGpuMemoryRegistration(std::span<const u32>{packet, 8})) { - result = RegisterMemory(request->begin, request->end, request->flags, request->device, id); - LOG_DEBUG(Service_AM, "GPU memory {:#x}..{:#x}, device={}, raw_device={:#x}, flags={}, id={}, result={:#x}", - request->begin, request->end, request->device, packet[7], request->flags, id, result.raw); - } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push(id); - } - std::shared_ptr<GpuDeviceRegistry> m_devices; - std::optional<size_t> m_nv_session; - std::vector<u32> m_memory; - std::shared_ptr<Nvidia::Module> m_module; - Kernel::KProcess* const m_process; -}; -} // namespace - -IGpuSession::IGpuSession(Core::System& system, std::shared_ptr<Nvidia::Module> module, - Kernel::KProcess* process, bool legacy_protocol) - : ServiceFramework{system, "IGpuSession"}, - m_process{process}, m_module{std::move(module)}, m_legacy_protocol{legacy_protocol} { - m_process->Open(kernel); - m_devices = std::make_shared<GpuDeviceRegistry>(system, m_module, m_process); - static const FunctionInfo functions[] = { - {11, &IGpuSession::GetNotificationRange, "GetNotificationRange"}, - {12, &IGpuSession::GetNotificationEvent, "GetNotificationEvent"}, - {21, &IGpuSession::RequestWorker, "RequestWorker"}, - {22, &IGpuSession::AcquireWorker, "AcquireWorker"}, - {33, &IGpuSession::CreateAddressSpace, "CreateAddressSpace"}, - {41, &IGpuSession::CreateGraphicsDevice, "CreateGraphicsDevice"}, - {42, &IGpuSession::DestroyGraphicsDevice, "DestroyGraphicsDevice"}, - {201, &IGpuSession::GetAddressSpacePartition, "GetLegacyGsvPartition"}, - {202, &IGpuSession::GetAddressSpacePartition, "GetGsvPartition"}, - {203, &IGpuSession::GetAddressSpacePartition, "GetUserPartition"}, - }; - RegisterHandlersTipc(functions); -} - -IGpuSession::~IGpuSession() { - if (m_worker) { m_worker->Close(kernel); } - m_process->Close(kernel); -} - -Result IGpuSession::HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) { - if (!ctx.IsTipc() || handlers_tipc.contains(ctx.GetCommand())) { - return ServiceFrameworkBase::HandleSyncRequest(session, ctx); - } - const auto guard = LockService(); - InvokeRequestTipc(ctx); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultNotImplemented.raw); - return system.IsPoweredOn() ? ctx.WriteToOutgoingCommandBuffer() : ResultSuccess; -} - -Result IGpuSession::RegisterAddressSpace(u32 id) { - const auto guard = LockService(); - const std::scoped_lock device_lock{m_devices->mutex}; - for (size_t i = 0; i < m_devices->address_spaces.size(); ++i) { - if (m_devices->address_spaces[i] && static_cast<u32>(*m_devices->address_spaces[i]) == id) { - m_devices->address_space_registered[i] = true; - R_SUCCEED(); - } - } - R_THROW(Kernel::ResultNotFound); -} - -void IGpuSession::GetAddressSpacePartition(HLERequestContext& ctx) { - const std::scoped_lock device_lock{m_devices->mutex}; - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - std::array<u64, 9> values{}; - const bool legacy_user = m_legacy_protocol && (ctx.GetCommand() == 201 || ctx.GetCommand() == 202); - const bool user = legacy_user || ctx.GetCommand() == 203; - if (!user) { - values[0] = ~u64{0}; // No supervisor-owned partition is reserved by this adapter. - } - if (packet[0] == 16 + ctx.GetCommand() && packet[1] == 1) { - const u32 id = packet[2]; - const bool exists = std::any_of(m_devices->address_spaces.begin(), m_devices->address_spaces.end(), - [id](const auto& fd) { return fd && static_cast<u32>(*fd) == id; }); - result = exists ? ResultSuccess : Kernel::ResultNotFound; - if (exists && user) { - std::array<u8, 64> input{}; - std::array<u8, 64> output{}; - const u32_le buffer_size{48}; - std::memcpy(input.data() + 8, &buffer_size, sizeof(buffer_size)); - Nvidia::Ioctl command{}; - command.raw = 0xc0404108; - if (m_module->Ioctl1(static_cast<s32>(id), command, input, output) == - Nvidia::NvResult::Success) { - std::array<Nvidia::Devices::nvhost_as_gpu::VaRegion, 2> regions; - std::memcpy(regions.data(), output.data() + 16, sizeof(regions)); - const u64 end = regions[1].offset + regions[1].pages * regions[1].page_size - 1; - if (legacy_user) { - const auto& region = regions[ctx.GetCommand() == 201 ? 0 : 1]; - values[0] = region.offset; - values[1] = region.offset + region.pages * region.page_size - 1; - } else { - values[0] = 1; - values[1] = regions[0].offset; - values[2] = end; - } - } else { - result = Kernel::ResultInvalidState; - } - } - LOG_DEBUG(Service_AM, "GPU partition command={}, id={}, begin={:#x}, end={:#x}, result={:#x}", - ctx.GetCommand(), id, user ? values[1] : values[0], - user ? values[2] : values[1], result.raw); - } - IPC::ResponseBuilder rb{ctx, user && !legacy_user ? 20U : 6U}; - rb.Push(result.raw); - for (size_t i = 0; i < (user && !legacy_user ? values.size() : size_t{2}); ++i) { - rb.Push(values[i]); - } -} - -void IGpuSession::CreateAddressSpace(HLERequestContext& ctx) { - const std::scoped_lock device_lock{m_devices->mutex}; - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - u32 id{}; - if (packet[0] == 0x31 && packet[1] == 1 && packet[2] == 0) { - result = Kernel::ResultOutOfResource; - for (auto& slot : m_devices->address_spaces) { - if (slot) { - continue; - } - if (!m_devices->nv_session) { - m_devices->nv_session = m_module->GetContainer().OpenSession(m_process).id; - } - const auto fd = m_module->Open("/dev/nvhost-as-gpu", - Nvidia::NvCore::SessionId{*m_devices->nv_session}); - if (fd == Nvidia::INVALID_NVDRV_FD) { - break; - } - std::array<u8, 40> input{}; - std::array<u8, 40> output{}; - const u32_le big_page_size{0x20000}; - std::memcpy(input.data() + 8, &big_page_size, sizeof(big_page_size)); - Nvidia::Ioctl command{}; - command.raw = 0x40284109; - const auto nv_result = m_module->Ioctl1(fd, command, input, output); - if (nv_result == Nvidia::NvResult::Success) { - slot = fd; - id = fd; - result = ResultSuccess; - } else { - m_module->Close(fd); - result = Kernel::ResultInvalidState; - } - break; - } - } - LOG_DEBUG(Service_AM, "GPU address space id={}, result={:#x}", id, result.raw); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push(id); -} - -void IGpuSession::CreateGraphicsDevice(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - u32 id{}; - if (packet[0] == 0x39 && packet[1] == 6) { - const u64 name = u64{packet[2]} | (u64{packet[3]} << 32); - const u64 flags = u64{packet[4]} | (u64{packet[5]} << 32); - const u64 debug_label = u64{packet[6]} | (u64{packet[7]} << 32); - result = flags == 0 ? Kernel::ResultOutOfResource : Kernel::ResultNotImplemented; - if (flags == 0) { - const std::scoped_lock device_lock{m_devices->mutex}; - for (u32 candidate = 1; candidate < m_devices->devices.size(); ++candidate) { - if (!m_devices->devices[candidate]) { - id = candidate; - m_devices->devices[id] = GpuDeviceRegistry::Device{name, debug_label}; - result = ResultSuccess; - break; - } - } - } - LOG_DEBUG(Service_AM, "graphics device name={:#x}, flags={:#x}, id={}, result={:#x}", - name, flags, id, result.raw); - } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push(id); -} - -void IGpuSession::DestroyGraphicsDevice(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x3a && packet[1] == 1) { - const u8 id = static_cast<u8>(packet[2]); - const std::scoped_lock device_lock{m_devices->mutex}; - result = m_devices->devices[id] ? ResultSuccess : Kernel::ResultNotFound; - m_devices->devices[id].reset(); - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGpuSession::GetNotificationRange(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const Result result = packet[0] == 0x1b && packet[1] == 0 - ? ResultSuccess : Kernel::ResultInvalidCombination; - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result.raw); - rb.Push<u32>(0); - rb.Push<u32>(static_cast<u32>(m_devices->events.size() - 1)); - LOG_DEBUG(Service_AM, "GPU notification range 0..{}", m_devices->events.size() - 1); -} - -void IGpuSession::GetNotificationEvent(HLERequestContext& ctx) { - const std::scoped_lock device_lock{m_devices->mutex}; - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - Kernel::KReadableEvent* readable{}; - if (packet[0] == 0x1c && packet[1] == 1 && packet[2] < m_devices->events.size()) { - const u32 index = packet[2]; - if (!m_devices->events[index]) { m_devices->events[index] = m_devices->context.CreateEvent("GpuNotification"); } - if (m_devices->events[index]) { - readable = &m_devices->events[index]->GetReadableEvent(); - readable->Open(kernel); - result = ResultSuccess; - } else { result = Kernel::ResultOutOfResource; } - LOG_DEBUG(Service_AM, "GPU notification event index={}, result={:#x}", index, result.raw); - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(readable); -} - -void IGpuSession::RequestWorker(HLERequestContext& ctx) { - const std::scoped_lock device_lock{m_devices->mutex}; - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x25 && packet[1] == 1 && packet[2] < m_devices->events.size()) { - const u32 index = packet[2]; - if (!m_devices->events[index]) { - result = Kernel::ResultInvalidState; - } else { - result = ResultSuccess; - if (!m_worker) { - result = CreateGraphicsSession(system, ctx, - std::make_shared<IGpuWorker>(system, m_module, m_process, m_devices), &m_worker); - } - if (result.IsSuccess()) { m_devices->events[index]->Signal(kernel); } - } - LOG_DEBUG(Service_AM, "GPU worker request index={}, result={:#x}", index, result.raw); - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGpuSession::AcquireWorker(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - Kernel::KClientSession* client{}; - if (packet[0] == 0x26 && packet[1] == 0) { - if (m_worker) { - client = m_worker; - client->Open(kernel); - result = ResultSuccess; - } else { result = Kernel::ResultInvalidState; } - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(client); - LOG_DEBUG(Service_AM, "GPU worker acquisition result={:#x}", result.raw); -} -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/gpu_session.h b/src/core/hle/service/am/service/gpu_session.h deleted file mode 100644 index e8f580bef1..0000000000 --- a/src/core/hle/service/am/service/gpu_session.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include <optional> -#include "core/hle/service/service.h" -#include "core/hle/service/kernel_helpers.h" - -namespace Kernel { -class KClientSession; -class KProcess; -class KEvent; -} -namespace Service::Nvidia { class Module; } -namespace Service::AM { - -struct GpuDeviceRegistry; - -class IGpuSession final : public ServiceFramework<IGpuSession> { -public: - IGpuSession(Core::System& system, std::shared_ptr<Nvidia::Module> module, - Kernel::KProcess* process, bool legacy_protocol = false); - ~IGpuSession() override; - Result RegisterAddressSpace(u32 id); - Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) override; - -private: - void GetAddressSpacePartition(HLERequestContext& ctx); - void CreateAddressSpace(HLERequestContext& ctx); - void CreateGraphicsDevice(HLERequestContext& ctx); - void DestroyGraphicsDevice(HLERequestContext& ctx); - void GetNotificationRange(HLERequestContext& ctx); - void GetNotificationEvent(HLERequestContext& ctx); - void RequestWorker(HLERequestContext& ctx); - void AcquireWorker(HLERequestContext& ctx); - - std::shared_ptr<GpuDeviceRegistry> m_devices; - Kernel::KClientSession* m_worker{}; - Kernel::KProcess* const m_process; - std::shared_ptr<Nvidia::Module> m_module; - const bool m_legacy_protocol; -}; -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_scaling_parameters_functions.cpp b/src/core/hle/service/am/service/graphics_scaling_parameters_functions.cpp deleted file mode 100644 index 931c81850d..0000000000 --- a/src/core/hle/service/am/service/graphics_scaling_parameters_functions.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/am/applet.h" -#include "core/hle/service/am/service/graphics_scaling_parameters_functions.h" -#include "core/hle/service/cmif_serialization.h" - -namespace Service::AM { - -IGraphicsScalingParametersFunctions::IGraphicsScalingParametersFunctions( - Core::System& system, std::shared_ptr<Applet> applet) - : ServiceFramework{system, "IGraphicsScalingParametersFunctions"}, m_applet{std::move(applet)} { - static const FunctionInfo functions[] = { - {10, D<&IGraphicsScalingParametersFunctions::GetGraphicsScalingParameters>, - "GetGraphicsScalingParameters"}, - {1000, nullptr, "SetSupportingGraphicsScalingParametersForDebug"}, - {1010, nullptr, "SetGpuQuotaForDebug"}, - {1020, nullptr, "ClearGpuQuotaForDebug"}, - {1030, nullptr, "SetScreenRatioForDebug"}, - {1040, nullptr, "ClearScreenRatioForDebug"}, - {1050, D<&IGraphicsScalingParametersFunctions::SetGraphicsScaleChangedNotificationEnabled>, - "SetGraphicsScaleChangedNotificationEnabled"}, - }; - RegisterHandlers(functions); -} - -IGraphicsScalingParametersFunctions::~IGraphicsScalingParametersFunctions() = default; - -Result IGraphicsScalingParametersFunctions::GetGraphicsScalingParameters( - Out<GraphicsScalingParameters> out_parameters) { - // Prototype policy: lowest SDK scale (10 maps to public scale 0), unit screen ratio. - // The ratio field interpretation is inferred from the adjacent debug API. - *out_parameters = {10, 1, 1}; - LOG_DEBUG(Service_AM, "called, prototype scale=10, screen ratio=1/1"); - R_SUCCEED(); -} - -Result IGraphicsScalingParametersFunctions::SetGraphicsScaleChangedNotificationEnabled(bool enabled) { - LOG_DEBUG(Service_AM, "called, enabled={}", enabled); - std::scoped_lock lk{m_applet->lock}; - m_applet->graphics_scale_changed_notification_enabled = enabled; - R_SUCCEED(); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_scaling_parameters_functions.h b/src/core/hle/service/am/service/graphics_scaling_parameters_functions.h deleted file mode 100644 index 7c92cb53f4..0000000000 --- a/src/core/hle/service/am/service/graphics_scaling_parameters_functions.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "core/hle/service/cmif_types.h" -#include "core/hle/service/service.h" - -namespace Service::AM { - -struct Applet; - -struct GraphicsScalingParameters { - u32 graphics_scale; - u32 screen_ratio_numerator; - u32 screen_ratio_denominator; -}; -static_assert(sizeof(GraphicsScalingParameters) == 12); - -class IGraphicsScalingParametersFunctions final - : public ServiceFramework<IGraphicsScalingParametersFunctions> { -public: - explicit IGraphicsScalingParametersFunctions(Core::System& system, - std::shared_ptr<Applet> applet); - ~IGraphicsScalingParametersFunctions() override; - -private: - Result GetGraphicsScalingParameters(Out<GraphicsScalingParameters> out_parameters); - Result SetGraphicsScaleChangedNotificationEnabled(bool enabled); - - const std::shared_ptr<Applet> m_applet; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_session.cpp b/src/core/hle/service/am/service/graphics_session.cpp deleted file mode 100644 index 902e7f9706..0000000000 --- a/src/core/hle/service/am/service/graphics_session.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/core.h" -#include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_session.h" -#include "core/hle/kernel/svc_results.h" -#include "core/hle/service/am/service/graphics_session.h" -#include "core/hle/service/server_manager.h" - -namespace Service::AM { -Result CreateGraphicsSession(Core::System& system, HLERequestContext& ctx, - std::shared_ptr<SessionRequestHandler> handler, - Kernel::KClientSession** out_client) { - auto& kernel = system.Kernel(); - Kernel::KScopedResourceReservation reservation( - kernel, Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax); - R_UNLESS(reservation.Succeeded(), Kernel::ResultLimitReached); - auto* session = Kernel::KSession::Create(kernel); - R_UNLESS(session != nullptr, Kernel::ResultOutOfResource); - session->Initialize(kernel, nullptr, 0); - reservation.Commit(); - Kernel::KSession::Register(kernel, session); - auto& server = ctx.GetManager()->GetServerManager(); - auto manager = std::make_shared<SessionRequestManager>(kernel, server); - manager->SetSessionHandler(std::move(handler)); - R_TRY(server.RegisterSession(&session->GetServerSession(), std::move(manager))); - *out_client = &session->GetClientSession(); - R_SUCCEED(); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_session.h b/src/core/hle/service/am/service/graphics_session.h deleted file mode 100644 index 698b5ee8ee..0000000000 --- a/src/core/hle/service/am/service/graphics_session.h +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <memory> -#include "core/hle/result.h" - -namespace Core { class System; } -namespace Kernel { class KClientSession; } -namespace Service { -class HLERequestContext; -class SessionRequestHandler; -} -namespace Service::AM { -Result CreateGraphicsSession(Core::System& system, HLERequestContext& ctx, - std::shared_ptr<SessionRequestHandler> handler, - Kernel::KClientSession** out_client); -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_supervisor.cpp b/src/core/hle/service/am/service/graphics_supervisor.cpp deleted file mode 100644 index 7759f575e7..0000000000 --- a/src/core/hle/service/am/service/graphics_supervisor.cpp +++ /dev/null @@ -1,1612 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include <algorithm> -#include <atomic> -#include <bit> -#include <array> -#include "common/alignment.h" -#include "core/hle/service/nvdrv/core/nvmap.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hle/service/nvnflinger/buffer_queue_producer.h" -#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" -#include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_session.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/svc_results.h" -#include "core/hle/service/am/service/graphics_supervisor.h" -#include "core/hle/service/am/service/graphics_session.h" -#include "core/hle/service/am/service/gpu_session.h" -#include "core/hle/service/am/service/graphics_supervisor_protocol.h" -#include "core/hle/service/am/service/graphics_syncpoint_cache.h" -#include "core/hle/service/am/service/native_image_layout.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvdrv/nvdrv_interface.h" -#include "core/hle/service/nvdrv/core/container.h" -#include "core/hle/service/nvdrv/core/syncpoint_manager.h" -#include "core/hle/service/server_manager.h" -#include "core/hle/service/sm/sm.h" -#include "core/hle/service/vi/application_display_service.h" -#include "core/hle/service/vi/container.h" -#include "core/hle/service/vi/manager_root_service.h" -#include "core/hle/service/vi/vi_types.h" - -namespace Service::AM { -struct NativeImageRegistry { - struct Image { - u32 id; - u32 memory_id; - std::array<u32, 16> preference; - u64 size; - bool window_pool{}; - }; - NativeImageRegistry(Kernel::KernelCore& kernel_, Kernel::KProcess* process_) - : kernel{kernel_}, process{process_} { - process->Open(kernel); - } - ~NativeImageRegistry() { - if (module) { - for (const auto& image : images) { - ReleaseMemory(image.memory_id); - } - } - if (memory_session) { - module->GetContainer().CloseSession(*memory_session); - } - process->Close(kernel); - } - void ReleaseMemory(u32 id) { - const auto info = module->GetContainer().GetNvMapFile().FreeHandle(id, true); - if (info && info->can_unlock && info->address != 0) { - process->GetPageTable().UnlockForDeviceAddressSpace(info->address, info->size); - } - } - Kernel::KernelCore& kernel; - Kernel::KProcess* process; - std::mutex mutex; - std::shared_ptr<Nvidia::Module> module; - std::optional<Nvidia::NvCore::SessionId> memory_session; - std::vector<Image> images; -}; -using NativeImage = NativeImageRegistry::Image; - - -namespace { - -// TIPC payloads follow a 32-bit result, so these handlers push result.raw. - -class ISyncpointSupervisor final : public ServiceFramework<ISyncpointSupervisor> { -public: - ISyncpointSupervisor(Core::System& system, std::shared_ptr<Nvidia::Module> module) - : ServiceFramework{system, "ISyncpointSupervisor"}, m_module{std::move(module)}, - m_context{system, "NativeSyncpoint"} { - static const FunctionInfo functions[] = { - {0, &ISyncpointSupervisor::ExchangeClientToken, "ExchangeClientToken"}, - {1, &ISyncpointSupervisor::GetMaximumEventCount, "GetMaximumEventCount"}, - {21, &ISyncpointSupervisor::SetEventCount, "SetEventCount"}, - {22, &ISyncpointSupervisor::GetEvent, "GetEvent"}, - {201, &ISyncpointSupervisor::ArmEvent, "ArmEvent"}, - {202, &ISyncpointSupervisor::CancelEvent, "CancelEvent"}, - {103, &ISyncpointSupervisor::QueryCounter, "QueryCounter"}, - }; - RegisterHandlersTipc(functions); - } - - ~ISyncpointSupervisor() override { - if (m_poll) system.CoreTiming().UnscheduleEvent(m_poll); - for (auto& event : m_events) m_context.CloseEvent(event.event); - } - - Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) override { - if (!ctx.IsTipc() || handlers_tipc.contains(ctx.GetCommand())) { - return ServiceFrameworkBase::HandleSyncRequest(session, ctx); - } - const auto guard = LockService(); - LOG_DEBUG(Service_AM, "syncpoint unsupported command={}, words={:08x}", ctx.GetCommand(), - fmt::join(std::span<const u32>(ctx.CommandBuffer() + 2, std::min<u32>(ctx.CommandBuffer()[1], 32)), ",")); - InvokeRequestTipc(ctx); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultNotImplemented.raw); - return system.IsPoweredOn() ? ctx.WriteToOutgoingCommandBuffer() : ResultSuccess; - } - -private: - static constexpr u32 MaximumEventCount = 63; - - void ExchangeClientToken(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const Result result = packet[0] == 0x10 && packet[1] == 2 - ? ResultSuccess : Kernel::ResultInvalidCombination; - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result.raw); - rb.Push(token); - LOG_DEBUG(Service_AM, "syncpoint token={:#x}, result={:#x}", token, result.raw); - } - void GetMaximumEventCount(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const Result result = packet[0] == 0x11 && packet[1] == 0 - ? ResultSuccess : Kernel::ResultInvalidCombination; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push(MaximumEventCount); - LOG_DEBUG(Service_AM, "syncpoint maximum events={}", MaximumEventCount); - } - - void SetEventCount(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x25 && packet[1] == 1 && packet[2] <= MaximumEventCount) { - m_event_count = packet[2]; - result = ResultSuccess; - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - LOG_DEBUG(Service_AM, "syncpoint event count={}, result={:#x}", m_event_count, result.raw); - } - - void GetEvent(HLERequestContext& ctx) { - const auto* p = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - Kernel::KReadableEvent* readable{}; - if (p[0] == 0x26 && p[1] == 1 && p[2] < m_event_count) { - auto& event = m_events[p[2]]; - if (!event.event) event.event = m_context.CreateEvent("SyncpointCompletion"); - result = event.event ? ResultSuccess : Kernel::ResultOutOfResource; - if (event.event) { - readable = &event.event->GetReadableEvent(); - readable->Open(kernel); - } - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(readable); - } - void RefreshEvents() { - auto& manager = m_module->GetContainer().GetSyncpointManager(); - for (auto& event : m_events) { - if (!event.pending) continue; - const auto current = manager.ReadAllocatedSyncpoint(event.id); - if (current && event.last_observed != *current) { - event.last_observed = *current; - LOG_INFO(Service_AM, "Syncpoint wait progress: id={}, current={}, target={}", event.id, *current, event.threshold); - } - if (current && static_cast<s32>(*current - event.threshold) >= 0) { - event.pending = false; - event.event->Signal(kernel); - } - } - } - void ArmEvent(HLERequestContext& ctx) { - const auto* p = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - const u32 id = ~p[3]; - if (p[0] == 0xd9 && p[1] == 5 && p[2] < m_event_count && - p[5] == 0 && p[6] == 0 && - m_module->GetContainer().GetSyncpointManager().ReadAllocatedSyncpoint(id)) { - auto& event = m_events[p[2]]; - if (!event.event) event.event = m_context.CreateEvent("SyncpointCompletion"); - result = event.event ? ResultSuccess : Kernel::ResultOutOfResource; - if (event.event) { - event.event->Clear(kernel); - event.id = id; - event.threshold = p[4]; - event.last_observed = ~u32{0}; - LOG_INFO(Service_AM, "Arm syncpoint wait: slot={}, id={}, target={}", p[2], id, event.threshold); - event.pending = true; - RefreshEvents(); - if (!m_poll) { - m_poll = Core::Timing::CreateEvent("NativeSyncpointCompletion", - [this](s64, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - RefreshEvents(); - return std::nullopt; - }); - system.CoreTiming().ScheduleLoopingEvent(std::chrono::milliseconds{1}, - std::chrono::milliseconds{1}, m_poll); - } - } - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - LOG_DEBUG(Service_AM, "syncpoint arm event={}, id={}, threshold={}, result={:#x}", - p[2], id, p[4], result.raw); - } - void CancelEvent(HLERequestContext& ctx) { - const auto* p = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (p[0] == 0xda && p[1] == 1 && p[2] < m_event_count) { - auto& event = m_events[p[2]]; - event.pending = false; - // The native SDK waits for this event after cancelling a timed wait - // before it releases the event slot. Cancellation acknowledges that - // the wait is retired; it does not advance the syncpoint counter. - if (event.event) event.event->Signal(kernel); - result = ResultSuccess; - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - - void QueryCounter(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - u64 value{}; - if (packet[0] == 0x77 && packet[1] == 1) { - const u32 id = ~packet[2]; - auto current = m_module->GetContainer().GetSyncpointManager().ReadAllocatedSyncpoint(id); - result = current ? ResultSuccess : Kernel::ResultNotFound; - value = current.value_or(0); - LOG_DEBUG(Service_AM, "query syncpoint id={}, value={}, result={:#x}", id, value, result.raw); - } - IPC::ResponseBuilder rb{ctx, 5}; - rb.Push(result.raw); - rb.Push(value); - rb.Push<u32>(0); // Existing NVDRV counters use 32-bit modular comparisons. - } - - std::shared_ptr<Nvidia::Module> m_module; - u32 m_event_count{}; - struct CompletionEvent { - Kernel::KEvent* event{}; - u32 id{}; - u32 threshold{}; - u32 last_observed = ~u32{0}; - bool pending{}; - }; - KernelHelpers::ServiceContext m_context; - std::array<CompletionEvent, MaximumEventCount> m_events{}; - std::shared_ptr<Core::Timing::EventType> m_poll; -}; - -class IVi2Session final : public ServiceFramework<IVi2Session> { - struct ClientState { - u64 token{}; - u32 slots{}; - std::optional<u64> display; - std::optional<u64> layer; - u64 available{0x7fff}; - u32 retire_notification{}; - u64 output_encoding{0x14}; - u32 available_notification{}; - std::shared_ptr<android::BufferQueueProducer> producer; - std::array<std::optional<s32>, 15> queued{}; - std::optional<size_t> player_display; - }; - -public: - explicit IVi2Session(Core::System& system, u64 aruid, std::shared_ptr<NativeImageRegistry> images) : ServiceFramework{system, "IVi2Session"}, m_images{std::move(images)}, m_aruid{aruid}, m_context{system, "Vi2Session"} { - static const FunctionInfo functions[] = { - {1, &IVi2Session::GetSessionLimit, "GetNotificationLimit"}, - {11, &IVi2Session::GetNotificationEvent, "GetNotificationEvent"}, - {13, &IVi2Session::ReleaseNotificationEvent, "ReleaseNotificationEvent"}, - {21, &IVi2Session::GetNotifications, "GetNotifications"}, - {31, &IVi2Session::DuplicateVsyncEvent, "GetDisplayEvent"}, - {32, &IVi2Session::DuplicateDisplayEvent, "DuplicateDisplayEvent"}, - {3, &IVi2Session::GetSessionLimit, "GetSessionLimit"}, - {5, &IVi2Session::GetSessionLimit, "GetRequestGroupLimit"}, - {51, &IVi2Session::ConfigureRequestGroups, "ConfigureRequestGroups"}, - {101, &IVi2Session::OpenDisplay, "OpenDisplay"}, - {102, &IVi2Session::CloseDisplay, "CloseDisplay"}, - {111, &IVi2Session::OpenEndpoint, "OpenEndpoint"}, - {112, &IVi2Session::CloseEndpoint, "CloseEndpoint"}, - {201, &IVi2Session::RegisterClientState, "RegisterClientState"}, - {202, &IVi2Session::UnregisterClientState, "UnregisterClientState"}, - {212, &IVi2Session::GetClientAvailability, "GetClientAvailability"}, - {229, &IVi2Session::PresentClientBuffer, "PresentClientBuffer"}, - {228, &IVi2Session::CancelClientBuffer, "CancelClientBuffer"}, - {351, &IVi2Session::SetClientOutputEncoding, "SetClientOutputEncoding"}, - {261, &IVi2Session::SetDisplayCapability, "SetDisplayCapability261"}, - {265, &IVi2Session::SetDisplayCapability, "SetDisplayCapability265"}, - {267, &IVi2Session::SetDisplayCapability, "SetDisplayCapability267"}, - {271, &IVi2Session::SetDisplayCapability, "SetDisplayCapability271"}, - {251, &IVi2Session::BindClientDisplay, "BindClientDisplay"}, - {252, &IVi2Session::UnbindClientDisplay, "UnbindClientDisplay"}, - }; - RegisterHandlersTipc(functions); - } - - ~IVi2Session() override { - if (m_release_poll) system.CoreTiming().UnscheduleEvent(m_release_poll); - if (m_display_service) { - for (auto& state : m_client_states) { - if (state.layer) m_display_service->GetContainer()->DestroyManagedLayer(*state.layer); - } - } - if (m_default_display) { m_display_service->CloseDisplay(*m_default_display); } - for (auto* event : m_display_change_events) m_context.CloseEvent(event); - m_context.CloseEvent(m_event); - } - - Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) override { - if (!ctx.IsTipc() || handlers_tipc.contains(ctx.GetCommand())) { - return ServiceFrameworkBase::HandleSyncRequest(session, ctx); - } - const auto guard = LockService(); - LOG_DEBUG(Service_AM, "VI2 unsupported command={}, words={:08x}", ctx.GetCommand(), - fmt::join(std::span<const u32>(ctx.CommandBuffer() + 2, std::min<u32>(ctx.CommandBuffer()[1], 32)), ",")); - InvokeRequestTipc(ctx); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultNotImplemented.raw); - return system.IsPoweredOn() ? ctx.WriteToOutgoingCommandBuffer() : ResultSuccess; - } -private: - void SetDisplayCapability(HLERequestContext& ctx) { - const auto* p = ctx.CommandBuffer(); - const u64 name = u64{p[2]} | (u64{p[3]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (p[0] == ctx.GetCommand() + 16 && p[1] == 3 && p[4] <= 1) { - result = name == 0x00746c7561666564ULL && m_default_display - ? ResultSuccess : Kernel::ResultNotFound; - if (result.IsSuccess()) { - // Per-client display mode capabilities do not change the host video mode. - m_display_capabilities[(ctx.GetCommand() - 261) / 2] = p[4] != 0; - } - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void DuplicateVsyncEvent(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - Kernel::KReadableEvent* readable{}; - if (packet[0] == 0x2f && packet[1] == 1 && packet[2] < 5) { - result = m_display_service && m_default_display - ? ResultSuccess : Kernel::ResultInvalidState; - if (result.IsSuccess() && !m_vsync_event) { - result = m_display_service->GetDisplayVsyncEvent(&m_vsync_event, *m_default_display); - } - if (result.IsSuccess()) { - if (packet[2] == 0) { - readable = m_vsync_event; - } else { - // Fixed host display properties have no change to report until changed. - auto*& event = m_display_change_events[packet[2] - 1]; - if (!event) event = m_context.CreateEvent("DisplayPropertyChange"); - if (event) readable = &event->GetReadableEvent(); - else result = Kernel::ResultOutOfResource; - } - if (readable) readable->Open(kernel); - } - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(readable); - LOG_DEBUG(Service_AM, "VI2 vsync event result={:#x}", result.raw); - } - void DuplicateDisplayEvent(HLERequestContext& ctx) { - const auto* p = ctx.CommandBuffer(); - auto source = ctx.GetObjectFromHandle<Kernel::KReadableEvent>(ctx.GetCopyHandle(0)); - const Result result = p[0] == 0x30 && p[1] == 0x80000001 && p[4] <= 1 && source.IsNotNull() - ? ResultSuccess : Kernel::ResultInvalidCombination; - Kernel::KReadableEvent* readable{}; - if (result.IsSuccess()) { - readable = source.GetPointerUnsafe(); - readable->Open(kernel); - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(readable); - } - static std::optional<size_t> GetPlayerDisplayIndex(u64 name) { - if ((name & 0xffffffffffffULL) != 0x726579616c70ULL || (name >> 56) != 0) { - return std::nullopt; - } - const u32 player = (name >> 48) & 0xff; - if (player < '1' || player > '3') return std::nullopt; - return player - '1'; - } - Result OpenDefaultDisplay(u64 name, u32 flags) { - if (const auto player = GetPlayerDisplayIndex(name)) { - R_UNLESS(flags < m_notifications.size() * 32, Kernel::ResultInvalidCombination); - R_UNLESS(m_player_display_references[*player] < 1024, Kernel::ResultOutOfResource); - // GameShare's detached player displays retain their own lifetime; - // they do not target the host's default display compositor. - m_player_display_notifications[*player] = flags; - ++m_player_display_references[*player]; - R_SUCCEED(); - } - // The native display token is an eight-byte name; command 101 returns - // only a result. Retain the real VI display ID for later operations. - R_UNLESS(name == 0x00746c7561666564ULL, Kernel::ResultNotFound); - R_UNLESS(flags == 1, Kernel::ResultNotImplemented); - R_UNLESS(m_display_references < 1024, Kernel::ResultOutOfResource); - if (!m_display_service) { - const auto root = system.ServiceManager().GetService<VI::IManagerRootService>("vi:m", true); - R_UNLESS(root, Kernel::ResultInvalidState); - R_TRY(root->GetDisplayService(&m_display_service, VI::Policy::Compositor)); - } - if (!m_default_display) { - u64 id{}; - R_TRY(m_display_service->OpenDefaultDisplay(&id)); - m_default_display = id; - } - m_display_notification = flags; - ++m_display_references; - R_SUCCEED(); - } - void OpenDisplay(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 name = u64{packet[2]} | (u64{packet[3]} << 32); - const auto result = packet[0] == 0x75 && packet[1] == 3 - ? OpenDefaultDisplay(name, packet[4]) : Kernel::ResultInvalidCombination; - LOG_DEBUG(Service_AM, "VI2 open display name={:#x}, flags={:#x}, references={}, result={:#x}", - name, packet[4], m_display_references, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void CloseDisplay(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - const u64 name = u64{packet[2]} | (u64{packet[3]} << 32); - if (packet[0] == 0x76 && packet[1] == 2 && GetPlayerDisplayIndex(name)) { - const auto player = *GetPlayerDisplayIndex(name); - result = Kernel::ResultNotFound; - if (m_player_display_references[player]) { - if (--m_player_display_references[player] == 0) { - const auto notification = m_player_display_notifications[player]; - m_notifications[notification / 32] |= 1U << (notification % 32); - if (m_event) m_event->Signal(kernel); - } - result = ResultSuccess; - } - } else if (packet[0] == 0x76 && packet[1] == 2 && name == 0x00746c7561666564ULL) { - result = Kernel::ResultNotFound; - if (m_default_display && m_display_references) { - result = ResultSuccess; - if (m_display_references == 1) { - result = m_display_service->CloseDisplay(*m_default_display); - if (result.IsSuccess()) { m_default_display.reset(); } - } - if (result.IsSuccess()) { - --m_display_references; - if (!m_display_references) { - m_notifications[m_display_notification / 32] |= 1U << (m_display_notification % 32); - if (m_event) m_event->Signal(kernel); - LOG_DEBUG(Service_AM, "VI2 display retired notification={}", m_display_notification); - } - } - } - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void RegisterClientState(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - const u32 first = packet[4]; - const u32 second = packet[5]; - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0xd9 && packet[1] == 4 && token != 0 && - first < 32 && second < 32 && first != second) { - const u32 slots = (1U << first) | (1U << second); - const auto existing = std::ranges::find(m_client_states, token, &ClientState::token); - const auto free = std::ranges::find(m_client_states, u64{0}, &ClientState::token); - result = Kernel::ResultInvalidState; - if (existing == m_client_states.end() && !(m_client_slots & slots)) { - result = Kernel::ResultOutOfResource; - if (free != m_client_states.end()) { - // The SDK sends its object address as an opaque identity, not a - // service-accessible buffer. Keep the associated slots session-local. - *free = {token, slots}; - free->retire_notification = first; - free->available_notification = second; - m_client_slots |= slots; - result = ResultSuccess; - } - } - } - LOG_DEBUG(Service_AM, "VI2 register client state token={:#x}, slots={}/{}, result={:#x}", - token, first, second, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void UnregisterClientState(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0xda && packet[1] == 2 && token != 0) { - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (state != m_client_states.end()) { - if (m_default_endpoint == token) { m_default_endpoint.reset(); } - if (state->layer) m_display_service->GetContainer()->DestroyManagedLayer(*state->layer); - m_notifications[state->retire_notification / 32] |= 1U << (state->retire_notification % 32); - if (m_event) m_event->Signal(kernel); - LOG_DEBUG(Service_AM, "VI2 client retired token={:#x}, notification={}", token, state->retire_notification); - m_client_slots &= ~state->slots; - *state = {}; - result = ResultSuccess; - } - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void GetClientAvailability(HLERequestContext& ctx) { - RefreshReleasedBuffers(); - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - Result result = Kernel::ResultInvalidCombination; - u64 id{}; - if (packet[0] == 0xe4 && packet[1] == 2 && token) { - auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (state != m_client_states.end() && m_default_display) { - result = ResultSuccess; - if (state->player_display) { - id = 0; - } else if (!state->layer) { - result = m_display_service->GetContainer()->CreateManagedLayer(&id, *m_default_display, m_aruid); - if (result.IsSuccess()) state->layer = id; - } else { - id = *state->layer; - } - } - } - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - const u64 available = result.IsSuccess() ? state->available : 0; - LOG_DEBUG(Service_AM, "VI2 client availability token={:#x}, layer={}, mask={:#x}, result={:#x}", token, id, available, result.raw); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result.raw); - rb.Push(available); - } - void CancelClientBuffer(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - const u32 slot = GraphicsSupervisorProtocol::DecodeBufferSlot(packet[4]); - Result result = Kernel::ResultInvalidCombination; - u64 available{}; - if (packet[0] == 0xf4 && packet[1] == 19 && slot < 15) { - auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (token && state != m_client_states.end()) { - // Unlike present (229), cancel has no image or input GPU fence. - state->available |= u64{1} << slot; - available = state->available; - result = ResultSuccess; - } - } - LOG_DEBUG(Service_AM, "VI2 cancel buffer token={:#x}, slot={}, mask={:#x}, result={:#x}", token, slot, available, result.raw); - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(result.raw); - rb.Push(available); - for (u32 i = 0; i < 4; ++i) rb.Push<u32>(0); - } - void RefreshReleasedBuffers() { - for (auto& state : m_client_states) { - if (!state.producer || !m_images->module) continue; - bool changed{}; - for (size_t slot = 0; slot < state.queued.size(); ++slot) { - if (!state.queued[slot]) continue; - const auto fence = state.producer->GetReleasedBufferFence(*state.queued[slot]); - if (!fence) continue; - bool complete = true; - for (u32 i = 0; i < fence->num_fences; ++i) { - const auto& point = fence->fences[i]; - if (point.id != ~u32{0}) { - const auto counter = m_images->module->GetContainer().GetSyncpointManager().ReadAllocatedSyncpoint(point.id); - if (!counter || static_cast<s32>(*counter - point.value) < 0) { - complete = false; - } - } - } - if (!complete) continue; - state.queued[slot].reset(); - state.available |= u64{1} << slot; - LOG_DEBUG(Service_AM, "Native display buffer retired: slot={}, mask={:#x}", slot, state.available); - changed = true; - } - if (changed) { - m_notifications[state.available_notification / 32] |= 1U << (state.available_notification % 32); - if (m_event) m_event->Signal(kernel); - } - } - } - Result QueueNativeImage(ClientState& state, const u32* packet) { - const u32 slot = GraphicsSupervisorProtocol::DecodeBufferSlot(packet[4]); - R_UNLESS(!state.player_display, Kernel::ResultNotImplemented); - R_UNLESS(state.layer && state.display && slot < 15 && !state.queued[slot], Kernel::ResultInvalidState); - const std::scoped_lock images_lock{m_images->mutex}; - const auto image = std::ranges::find(m_images->images, packet[5], &NativeImage::id); - R_UNLESS(image != m_images->images.end() && image->window_pool && m_images->module, Kernel::ResultNotFound); - // The observed presentation is a four-byte color image with the - // backend's 16-GOB block height and no byte offset within its pool. - R_UNLESS(packet[6] == 0 && packet[7] == image->preference[2] && packet[8] == 3 && - image->preference[4] == 7 && - (state.output_encoding == 0x14 || state.output_encoding == 0x5), - Kernel::ResultNotImplemented); - const u32 fence_id = ~packet[22]; - R_UNLESS(packet[23] == 0 && packet[25] == 0 && - (fence_id == ~u32{0} || m_images->module->GetContainer().GetSyncpointManager().IsSyncpointAllocated(fence_id)), - Kernel::ResultInvalidCombination); - if (!state.producer) { - R_TRY(m_display_service->GetContainer()->GetLayerProducerHandle(&state.producer, *state.layer)); - android::QueueBufferOutput output{}; - R_UNLESS(state.producer->Connect({}, android::NativeWindowApi::Egl, false, &output) == android::Status::NoError, - Kernel::ResultInvalidState); - R_UNLESS(state.producer->SetBufferCount(16) == android::Status::NoError, Kernel::ResultInvalidState); - s32 binder{}; - R_TRY(m_display_service->GetContainer()->OpenLayer(&binder, *state.layer, m_aruid)); - R_TRY(m_display_service->GetContainer()->SetLayerVisibility(*state.layer, true)); - } - auto native = std::make_shared<android::NvGraphicBuffer>(); - native->width = static_cast<s32>((image->preference[2] & 0xffffU) + 1); - native->height = static_cast<s32>((image->preference[2] >> 16) + 1); - native->stride = static_cast<s32>(image->preference[5] * 16); - native->format = android::PixelFormat::Rgba8888; - native->external_format = native->format; - native->buffer_id = image->memory_id; - auto buffer = std::make_shared<android::GraphicBuffer>(m_images->module->GetContainer().GetNvMapFile(), native); - s32 queue_slot{}; - R_UNLESS(state.producer->AttachBuffer(&queue_slot, buffer) == android::Status::NoError, Kernel::ResultOutOfResource); - android::QueueBufferInput input{}; - android::QueueBufferOutput output{}; - input.crop = {0, 0, native->width, native->height}; - input.swap_interval = 1; - input.fence = android::Fence::NoFence(); - if (fence_id != ~u32{0}) { - input.fence.num_fences = 1; - input.fence.fences[0] = {static_cast<s32>(fence_id), packet[24]}; - } - if (state.producer->QueueBuffer(queue_slot, input, &output) != android::Status::NoError) { - state.producer->CancelBuffer(queue_slot, input.fence); - return Kernel::ResultInvalidState; - } - state.queued[slot] = queue_slot; - state.available &= ~(u64{1} << slot); - if (!m_release_poll) { - m_release_poll = Core::Timing::CreateEvent("NativeViBufferRelease", [this](s64, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - RefreshReleasedBuffers(); - return std::nullopt; - }); - system.CoreTiming().ScheduleLoopingEvent(std::chrono::milliseconds{1}, std::chrono::milliseconds{1}, m_release_poll); - } - LOG_INFO(Service_AM, "Native image queued for display: image={}, slot={}, fence={}/{}, dimensions={}x{}", - image->id, slot, fence_id, packet[24], native->width, native->height); - R_SUCCEED(); - } - void PresentClientBuffer(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - RefreshReleasedBuffers(); - Result result = Kernel::ResultInvalidCombination; - u64 available{}; - if (packet[0] == 0xf5 && packet[1] == 24) { - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (token && state != m_client_states.end()) { - result = QueueNativeImage(*state, packet); - available = state->available; - } - } - LOG_DEBUG(Service_AM, "VI2 present token={:#x}, image={}, slot={}, mask={:#x}, result={:#x}", token, packet[5], packet[4], available, result.raw); - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(result.raw); - rb.Push(available); - for (u32 i = 0; i < 4; ++i) rb.Push<u32>(0); - } - void SetClientOutputEncoding(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - const u64 encoding = u64{packet[4]} | (u64{packet[5]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x16f && packet[1] == 4) { - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (token && state != m_client_states.end()) { - result = Kernel::ResultNotImplemented; - // Only the SDK's observed default output encoding is supported. - if (encoding == 0x14 || encoding == 0x5) { - state->output_encoding = encoding; - result = ResultSuccess; - } - } - } - LOG_DEBUG(Service_AM, "VI2 output encoding token={:#x}, encoding={:#x}, result={:#x}", token, encoding, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void BindClientDisplay(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - const u64 name = u64{packet[4]} | (u64{packet[5]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x10b && packet[1] == 4) { - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (token && state != m_client_states.end() && GetPlayerDisplayIndex(name)) { - const auto player = *GetPlayerDisplayIndex(name); - result = Kernel::ResultInvalidState; - if (m_player_display_references[player] && !state->display) { - result = ResultSuccess; - if (state->layer) { - result = m_display_service->GetContainer()->DestroyManagedLayer(*state->layer); - } - if (result.IsSuccess()) { - state->layer.reset(); - state->display = name; - state->player_display = player; - } - } - } else if (token && state != m_client_states.end() && name == 0x00746c7561666564ULL && - m_default_display && m_display_references) { - result = Kernel::ResultInvalidState; - if (!state->display) { - state->display = m_default_display; - result = ResultSuccess; - } - } - } - LOG_DEBUG(Service_AM, "VI2 bind client token={:#x}, display={:#x}, result={:#x}", - token, name, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void UnbindClientDisplay(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 token = u64{packet[2]} | (u64{packet[3]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x10c && packet[1] == 2) { - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (token && state != m_client_states.end() && state->display) { - state->display.reset(); - state->player_display.reset(); - result = ResultSuccess; - } - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void OpenEndpoint(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 name = u64{packet[2]} | (u64{packet[3]} << 32); - const u64 display_name = u64{packet[4]} | (u64{packet[5]} << 32); - const u64 token = u64{packet[6]} | (u64{packet[7]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x7f && packet[1] == 6) { - const auto state = std::ranges::find(m_client_states, token, &ClientState::token); - result = Kernel::ResultNotFound; - if (token && state != m_client_states.end() && state->display && - m_default_display == state->display && name == 0x00746c7561666564ULL && - display_name == name) { - result = Kernel::ResultInvalidState; - if (!m_default_endpoint) { - m_default_endpoint = token; - result = ResultSuccess; - } - } - } - LOG_DEBUG(Service_AM, "VI2 open endpoint name={:#x}, display={:#x}, client={:#x}, result={:#x}", - name, display_name, token, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void CloseEndpoint(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 name = u64{packet[2]} | (u64{packet[3]} << 32); - const u64 display_name = u64{packet[4]} | (u64{packet[5]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x80 && packet[1] == 4 && name == 0x00746c7561666564ULL && - display_name == name) { - result = Kernel::ResultNotFound; - if (m_default_endpoint) { - m_default_endpoint.reset(); - result = ResultSuccess; - } - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void ConfigureRequestGroups(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x43 && packet[1] == 1 && packet[2] < 8) { - m_request_group_limit = packet[2]; - m_request_groups_initialized = true; - result = ResultSuccess; - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void GetSessionLimit(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const Result result = packet[0] == 16 + ctx.GetCommand() && packet[1] == 0 - ? ResultSuccess : Kernel::ResultInvalidCombination; - // The SDK converts this inclusive upper group index into 32 client slots. - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push<u32>(ctx.GetCommand() == 5 ? 7 : 0); - } - - void GetNotifications(HLERequestContext& ctx) { - Result result = Kernel::ResultInvalidSize; - if (ctx.GetWriteBufferSize() >= sizeof(m_notifications)) { - ctx.WriteBuffer(m_notifications); - LOG_DEBUG(Service_AM, "VI2 notifications={:08x}", fmt::join(m_notifications, ",")); - m_notifications.fill(0); - if (m_event) m_event->Clear(kernel); - result = ResultSuccess; - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void ReleaseNotificationEvent(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const auto result = packet[0] == 0x1d && packet[1] == 1 && packet[2] == 0 - ? ResultSuccess : Kernel::ResultInvalidCombination; - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - } - void GetNotificationEvent(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - Kernel::KReadableEvent* readable{}; - if (packet[0] == 0x1b && packet[1] == 1 && packet[2] == 0) { - if (!m_event) { - m_event = m_context.CreateEvent("Vi2Notification"); - } - result = m_event ? ResultSuccess : Kernel::ResultOutOfResource; - if (m_event) { - if (std::ranges::any_of(m_notifications, [](u32 value) { return value != 0; })) m_event->Signal(kernel); - readable = &m_event->GetReadableEvent(); - readable->Open(kernel); - } - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(readable); - } - - std::shared_ptr<NativeImageRegistry> m_images; - std::shared_ptr<Core::Timing::EventType> m_release_poll; - std::array<ClientState, 16> m_client_states{}; - u32 m_client_slots{}; - std::shared_ptr<VI::IApplicationDisplayService> m_display_service; - Kernel::KReadableEvent* m_vsync_event{}; - std::array<Kernel::KEvent*, 4> m_display_change_events{}; - std::optional<u64> m_default_display; - std::array<bool, 6> m_display_capabilities{}; - u32 m_display_references{}; - u32 m_display_notification{}; - std::array<u32, 3> m_player_display_references{}; - std::array<u32, 3> m_player_display_notifications{}; - bool m_request_groups_initialized{}; - u32 m_request_group_limit{}; - std::optional<u64> m_default_endpoint; - std::array<u32, 32> m_notifications{}; - const u64 m_aruid; - KernelHelpers::ServiceContext m_context; - Kernel::KEvent* m_event{}; -}; - -} // namespace - - -IGraphicsSupervisor::IGraphicsSupervisor(Core::System& system, Kernel::KProcess* process, u64 aruid) - : ServiceFramework{system, "IGraphicsSupervisor"}, m_context{system, "GraphicsSupervisor"}, - m_syncpoint_cache_event{m_context}, m_process{process}, m_aruid{aruid} { - m_process->Open(kernel); - m_image_registry = std::make_shared<NativeImageRegistry>(kernel, m_process); - static const FunctionInfo functions[] = { - {4, &IGraphicsSupervisor::Initialize, "Initialize"}, - {11, &IGraphicsSupervisor::BindProcess, "BindProcess"}, - {101, &IGraphicsSupervisor::CompleteBootstrap, "CompleteBootstrap"}, - {115, &IGraphicsSupervisor::ConfigureNativeMemory, "ConfigureNativeMemory"}, - {111, &IGraphicsSupervisor::CreateNativeMemory, "CreateNativeMemory"}, - {112, &IGraphicsSupervisor::DestroyNativeMemory, "DestroyNativeMemory"}, - {114, &IGraphicsSupervisor::BindNativeMemory, "BindNativeMemory"}, - {201, &IGraphicsSupervisor::InitializeSyncpointContext, "InitializeSyncpointContext"}, - {202, &IGraphicsSupervisor::GetSyncpointSession, "GetSyncpointSession"}, - {204, &IGraphicsSupervisor::BindSyncpointMemory, "BindSyncpointMemory"}, - {301, &IGraphicsSupervisor::InitializeDisplayContext, "InitializeDisplayContext"}, - {305, &IGraphicsSupervisor::OpenDisplaySession, "OpenDisplaySession"}, - {314, &IGraphicsSupervisor::ConfigureNativeWindowImagePool, "DestroyNativeWindowAliasImagePool"}, - {315, &IGraphicsSupervisor::ConfigureNativeWindowImagePool, "CreateNativeWindowAliasImagePool"}, - {401, &IGraphicsSupervisor::InitializeGpuContext, "InitializeGpuContext"}, - {402, &IGraphicsSupervisor::InitializeGpuContext, "ActivateGpuContext"}, - {423, &IGraphicsSupervisor::BindNativeGpuMemory, "BindNativeGpuMemory"}, - {429, &IGraphicsSupervisor::CreateNativeImage, "CreateNativeImage"}, - {430, &IGraphicsSupervisor::DestroyNativeImage, "DestroyNativeImage"}, - {434, &IGraphicsSupervisor::RegisterGpuAddressSpace, "RegisterGpuAddressSpace"}, - {497, &IGraphicsSupervisor::OpenGpuSession, "OpenGpuSession"}, - {1101, &IGraphicsSupervisor::InitializeFrameContext, "InitializeFrameContext"}, - {1102, &IGraphicsSupervisor::InitializeFrameContext, "ActivateFrameContext"}, - {1402, &IGraphicsSupervisor::GetNvidiaDriver, "GetNvidiaDriver"}, - {1501, &IGraphicsSupervisor::InitializeImageContext, "InitializeImageContext1501"}, - {435, &IGraphicsSupervisor::CalculateImageRequirements, "CalculateNativeImageSize"}, - {1510, &IGraphicsSupervisor::CalculateImageRequirements, "CalculateImageRequirements"}, - {1502, &IGraphicsSupervisor::InitializeImageContext, "InitializeImageContext1502"}, - }; - RegisterHandlersTipc(functions); -} - -IGraphicsSupervisor::~IGraphicsSupervisor() { - if (m_cache_poll) { - system.CoreTiming().UnscheduleEvent(m_cache_poll); - } - if (m_syncpoint_rw) { - m_syncpoint_rw->Close(kernel); - m_syncpoint_ro->Close(kernel); - } - if (m_nv_module) { - auto& nvmap = m_nv_module->GetContainer().GetNvMapFile(); - for (const auto id : m_native_memories) { - if (const auto info = nvmap.FreeHandle(id, false); info && info->can_unlock && info->address != 0) { - m_process->GetPageTable().UnlockForDeviceAddressSpace(info->address, info->size); - } - } - if (m_native_memory_session) { - m_nv_module->GetContainer().CloseSession(Nvidia::NvCore::SessionId{*m_native_memory_session}); - } - } - m_context.CloseEvent(m_syncpoint_request_event); - m_process->Close(kernel); -} - -Result IGraphicsSupervisor::HandleSyncRequest(Kernel::KServerSession& session, - HLERequestContext& ctx) { - if (!ctx.IsTipc() || handlers_tipc.contains(ctx.GetCommand())) { - return ServiceFrameworkBase::HandleSyncRequest(session, ctx); - } - const auto guard = LockService(); - InvokeRequestTipc(ctx); - // Unsupported TIPC must return an error instead of leaving request data as a reply. - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultNotImplemented.raw); - return system.IsPoweredOn() ? ctx.WriteToOutgoingCommandBuffer() : ResultSuccess; -} - -void IGraphicsSupervisor::Initialize(HLERequestContext& ctx) { - const auto request = GraphicsSupervisorProtocol::DecodeBootstrap( - std::span<const u32>{ctx.CommandBuffer(), IPC::COMMAND_BUFFER_LENGTH}); - Result result = Kernel::ResultInvalidCombination; - if (request) { - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(request->process_handle); - const bool empty_remote_memory = std::ranges::all_of( - request->parameters, [](u64 value) { return value == 0; }); - if (process.IsNull() || process.GetPointerUnsafe() != m_process) { - result = Kernel::ResultInvalidHandle; - } else if (m_phase != Phase::Created) { - result = Kernel::ResultInvalidState; - } else if (request->applet_resource_user_id != m_aruid) { - result = Kernel::ResultInvalidArgument; - } else if ((request->flags != 0x8000000000000001ULL && request->flags != 0x8000000000000005ULL && request->flags != 0x8000000000000006ULL && request->flags != 0x8000000000000007ULL) || !empty_remote_memory) { - result = Kernel::ResultNotImplemented; - } else { - m_phase = Phase::Initialized; - result = ResultSuccess; - } - LOG_DEBUG(Service_AM, "graphics bootstrap aruid={:#x}, flags={:#x}, result={:#x}", - request->applet_resource_user_id, request->flags, result.raw); - } - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::BindProcess(HLERequestContext& ctx) { - Result result = Kernel::ResultInvalidCombination; - const auto* packet = ctx.CommandBuffer(); - if (packet[0] == 0x1b && packet[1] == 0x80000000 && packet[2] == 2) { - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - if (process.IsNull() || process.GetPointerUnsafe() != m_process) { - result = Kernel::ResultInvalidHandle; - } else if (m_phase != Phase::Initialized) { - result = Kernel::ResultInvalidState; - } else { - m_phase = Phase::ProcessBound; - result = ResultSuccess; - } - } - LOG_DEBUG(Service_AM, "graphics process binding result={:#x}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::CompleteBootstrap(HLERequestContext& ctx) { - Result result = Kernel::ResultInvalidCombination; - const auto* packet = ctx.CommandBuffer(); - if (packet[0] == 0x75 && packet[1] == 0) { - if (m_phase != Phase::ProcessBound) { - result = Kernel::ResultInvalidState; - } else { - m_phase = Phase::Ready; - result = ResultSuccess; - } - } - LOG_DEBUG(Service_AM, "graphics bootstrap completion result={:#x}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::InitializeImageContext(HLERequestContext& ctx) { - Result result = Kernel::ResultInvalidCombination; - const auto* packet = ctx.CommandBuffer(); - if (packet[0] == 16 + ctx.GetCommand() && packet[1] == 0) { - if (m_phase != Phase::Ready) { - result = Kernel::ResultInvalidState; - } else if (ctx.GetCommand() == 1501) { - m_image_context_initialized = true; - result = ResultSuccess; - } else if (m_image_context_initialized) { - m_image_formats_initialized = true; - result = ResultSuccess; - } else { - result = Kernel::ResultInvalidState; - } - } - LOG_DEBUG(Service_AM, "empty image context command={}, result={:#x}", - ctx.GetCommand(), result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::OpenGpuSession(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - Kernel::KClientSession* client{}; - const u64 flags = u64{packet[2]} | (u64{packet[3]} << 32); - if (packet[0] == 0x201 && packet[1] == 2 && (flags == 3 || flags == 4)) { - if (m_phase != Phase::Ready || !m_nv_module) { - result = Kernel::ResultInvalidState; - } else { - auto handler = std::make_shared<IGpuSession>(system, m_nv_module, m_process, flags == 3); - result = CreateGraphicsSession(system, ctx, handler, &client); - if (result.IsSuccess()) { - if (flags == 3) { m_image_formats_initialized = true; } - std::erase_if(m_gpu_sessions, [](const auto& item) { return item.expired(); }); - m_gpu_sessions.emplace_back(handler); - } - } - } - LOG_DEBUG(Service_AM, "GPU control session flags={:#x}, result={:#x}", flags, result.raw); - // This SDK reads the move-handle slot before the result, including on failure. - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(client); -} - -void IGraphicsSupervisor::RegisterGpuAddressSpace(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x1c2 && packet[1] == 1) { - result = m_gpu_context_ready ? Kernel::ResultNotFound : Kernel::ResultInvalidState; - if (m_gpu_context_ready) { - for (const auto& item : m_gpu_sessions) { - if (auto handler = item.lock()) { - result = handler->RegisterAddressSpace(packet[2]); - if (result.IsSuccess()) { - break; - } - } - } - } - } - LOG_DEBUG(Service_AM, "register GPU address space id={}, result={:#x}", packet[2], result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::InitializeDisplayContext(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x13d && packet[1] == 0) { - result = m_phase == Phase::Ready ? ResultSuccess : Kernel::ResultInvalidState; - if (result.IsSuccess()) { - m_display_context_initialized = true; - } - } - LOG_DEBUG(Service_AM, "display context initialization result={:#x}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::OpenDisplaySession(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Kernel::KClientSession* client{}; - const u64 flags = u64{packet[2]} | (u64{packet[3]} << 32); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x141 && packet[1] == 2 && (flags == 0x300010000ULL || flags == 0x300020000ULL)) { - result = m_display_context_initialized ? ResultSuccess : Kernel::ResultInvalidState; - if (result.IsSuccess()) { - result = CreateGraphicsSession(system, ctx, std::make_shared<IVi2Session>(system, m_aruid, m_image_registry), - &client); - } - } - LOG_DEBUG(Service_AM, "VI2 session flags={:#x}, result={:#x}", flags, result.raw); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(result.raw); - ctx.AddMoveObject(client); -} - -void IGraphicsSupervisor::InitializeGpuContext(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 16 + ctx.GetCommand() && packet[1] == 0) { - if (m_phase != Phase::Ready) { - result = Kernel::ResultInvalidState; - } else if (ctx.GetCommand() == 401) { - m_gpu_context_initialized = true; - result = ResultSuccess; - } else if (m_gpu_context_initialized) { - m_gpu_context_ready = true; - result = ResultSuccess; - } else { - result = Kernel::ResultInvalidState; - } - } - LOG_DEBUG(Service_AM, "GPU context command={}, result={:#x}", ctx.GetCommand(), result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::InitializeFrameContext(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 16 + ctx.GetCommand() && packet[1] == 0) { - if (m_phase != Phase::Ready) { - result = Kernel::ResultInvalidState; - } else if (ctx.GetCommand() == 1101) { - m_frame_context_initialized = true; - result = ResultSuccess; - } else if (m_frame_context_initialized) { - m_frame_context_ready = true; - result = ResultSuccess; - } else { - result = Kernel::ResultInvalidState; - } - } - LOG_DEBUG(Service_AM, "empty frame context command={}, result={:#x}", ctx.GetCommand(), result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::ConfigureNativeMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x83 && packet[1] == 2 && packet[3] == 0 && - (packet[2] == 0 || packet[2] == 2)) { - result = m_phase == Phase::Ready ? ResultSuccess : Kernel::ResultInvalidState; - } - // Clear the optional Gpuk-memory mode: Eden currently provides the NVDRV path. - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result.raw); - rb.Push<u64>(0); - LOG_DEBUG(Service_AM, "native memory uses NVDRV-compatible mode, result={:#x}", result.raw); -} - -void IGraphicsSupervisor::InitializeSyncpointContext(HLERequestContext& ctx) { - Result result = Kernel::ResultInvalidCombination; - const auto* packet = ctx.CommandBuffer(); - if (packet[0] == 0xd9 && packet[1] == 0) { - if (m_phase != Phase::Ready) { - result = Kernel::ResultInvalidState; - } else { - m_syncpoint_context_initialized = true; - result = ResultSuccess; - } - } - LOG_DEBUG(Service_AM, "syncpoint context initialization result={:#x}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::GetSyncpointSession(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidState; - Kernel::KClientSession* client{}; - if (packet[0] == 0xda && packet[1] == 0 && m_syncpoint_context_initialized && m_nv_module) { - result = CreateGraphicsSession(system, ctx, - std::make_shared<ISyncpointSupervisor>(system, m_nv_module), &client); - } - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - return; - } - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess.raw); - rb.PushMoveObjects(ctx, client); - LOG_DEBUG(Service_AM, "opened syncpoint-supervisor session"); -} - -void IGraphicsSupervisor::GetNvidiaDriver(HLERequestContext& ctx) { - const auto Fail = [&](Result result) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); - }; - const auto* packet = ctx.CommandBuffer(); - if (packet[0] != 0x58a || packet[1] != 0) { - Fail(Kernel::ResultInvalidCombination); - return; - } - if (m_phase != Phase::Ready) { - Fail(Kernel::ResultInvalidState); - return; - } - auto source = system.ServiceManager().GetService<Nvidia::NVDRV>("nvdrv:s", true); - if (!source) { - Fail(Kernel::ResultNotFound); - return; - } - m_nv_module = source->GetModule(); - Kernel::KClientSession* client{}; - const auto result = CreateGraphicsSession(system, ctx, - std::make_shared<Nvidia::NVDRV>(system, m_nv_module, "nvdrv:gsv", m_process, m_aruid), - &client); - if (result.IsError()) { - Fail(result); - return; - } - LOG_DEBUG(Service_AM, "opened process-bound NVDRV session for aruid={:#x}", m_aruid); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess.raw); - rb.PushMoveObjects(ctx, client); -} - -void IGraphicsSupervisor::BindSyncpointMemory(HLERequestContext& ctx) { - using namespace GraphicsSyncpointCache; - const auto Fail = [&](Result result) { - IPC::ResponseBuilder rb{ctx, 2, 0, 2}; - rb.Push(result.raw); - ctx.AddMoveObject(nullptr); - ctx.AddMoveObject(nullptr); - }; - const auto* packet = ctx.CommandBuffer(); - if (packet[0] != 0xdc || packet[1] != 0x80000006 || packet[2] != 4 || - packet[5] != 1 || packet[6] != 0x1ff || packet[7] != 0x1000 || packet[8] != 0 || - packet[9] != 0x2000 || packet[10] != 0) { - Fail(Kernel::ResultInvalidCombination); - return; - } - if (!m_syncpoint_context_initialized || !m_nv_module || m_syncpoint_rw) { - Fail(Kernel::ResultInvalidState); - return; - } - auto rw = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0)); - auto ro = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(1)); - if (rw.IsNull() || ro.IsNull() || rw->GetOwner() != m_process || ro->GetOwner() != m_process || - rw.GetPointerUnsafe() == ro.GetPointerUnsafe()) { - Fail(Kernel::ResultInvalidHandle); - return; - } - if (rw->GetSize() != 0x1000 || ro->GetSize() != 0x2000) { - Fail(Kernel::ResultInvalidSize); - return; - } - auto* request_event = m_context.CreateEvent("SyncpointCacheRequest"); - if (request_event == nullptr) { - Fail(Kernel::ResultOutOfResource); - return; - } - m_syncpoint_request_event = request_event; - m_syncpoint_rw = rw.GetPointerUnsafe(); - m_syncpoint_ro = ro.GetPointerUnsafe(); - m_syncpoint_rw->Open(kernel); - m_syncpoint_ro->Open(kernel); - auto& memory = m_process->GetMemory(); - const auto rw_base = rw->GetSourceAddress(); - const auto ro_base = ro->GetSourceAddress(); - const std::array<u8, 0x2000> zeros{}; - memory.WriteBlock(rw_base, zeros.data(), 0x1000); - memory.WriteBlock(ro_base, zeros.data(), 0x2000); - memory.WriteBlock(ro_base, Header.data(), sizeof(Header)); - for (u32 id = 0; id < CounterCount; ++id) { - memory.Write64(ro_base + ValuesOffset + id * sizeof(u64), Unallocated); - } - m_cache_poll = Core::Timing::CreateEvent("GraphicsSyncpointCache", - [this](s64, std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - this->RefreshSyncpointCache(); - return std::nullopt; - }); - system.CoreTiming().ScheduleLoopingEvent(std::chrono::milliseconds{1}, - std::chrono::milliseconds{1}, m_cache_poll); - request_event->Open(kernel); - auto* completed = m_syncpoint_cache_event.GetHandle(); - completed->Open(kernel); - IPC::ResponseBuilder rb{ctx, 2, 0, 2}; - rb.Push(ResultSuccess.raw); - ctx.AddMoveObject(request_event); - ctx.AddMoveObject(completed); - LOG_DEBUG(Service_AM, "bound syncpoint cache: {} real counters, rw={:#x}, ro={:#x}", - CounterCount, GetInteger(rw_base), GetInteger(ro_base)); -} - -void IGraphicsSupervisor::RefreshSyncpointCache() { - using namespace GraphicsSyncpointCache; - if (!m_syncpoint_rw || !system.IsPoweredOn()) { - return; - } - auto& memory = m_process->GetMemory(); - const auto rw = m_syncpoint_rw->GetSourceAddress(); - const auto ro = m_syncpoint_ro->GetSourceAddress(); - const u64 requests = memory.Read64(rw + GroupRequestOffset); - const u64 acknowledgements = memory.Read64(ro + GroupAckOffset); - u64 groups = (requests ^ acknowledgements) & ((1ULL << GroupCount) - 1); - if (groups == 0) { - return; - } - auto& syncpoints = m_nv_module->GetContainer().GetSyncpointManager(); - while (groups) { - const u32 group = static_cast<u32>(std::countr_zero(groups)); - groups &= groups - 1; - const u64 requested_slots = memory.Read64(rw + SlotRequestOffset + group * sizeof(u64)); - u64 slots = requested_slots ^ memory.Read64(ro + SlotAckOffset + group * sizeof(u64)); - while (slots) { - const u32 bit = static_cast<u32>(std::countr_zero(slots)); - slots &= slots - 1; - const u32 id = group * 64 + bit; - const auto address = ro + ValuesOffset + id * sizeof(u64); - const auto value = syncpoints.ReadAllocatedSyncpoint(id); - const u64 updated = m_counter_values[id].Observe(value); - memory.Write64(address, updated); - } - std::atomic_thread_fence(std::memory_order_release); - memory.Write64(ro + SlotAckOffset + group * sizeof(u64), requested_slots); - } - std::atomic_thread_fence(std::memory_order_release); - memory.Write64(ro + GroupAckOffset, requests); - m_syncpoint_request_event->Clear(kernel); - m_syncpoint_cache_event.Signal(kernel); -} - -void IGraphicsSupervisor::CalculateImageRequirements(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - std::optional<NativeImageRequirements> requirements; - const bool gpu_image = ctx.GetCommand() == 435; - if (packet[0] == (gpu_image ? 0x1c3U : 0x5f6U) && packet[1] == 17 && - packet[3] == (gpu_image ? 1U : 2U)) { - result = Kernel::ResultInvalidState; - if (m_phase == Phase::Ready && m_image_formats_initialized) { - requirements = CalculateNativeImageRequirements(std::span{packet + 2, 16}, packet[18]); - result = requirements ? ResultSuccess : Kernel::ResultNotImplemented; - } - } - LOG_DEBUG(Service_AM, "native image requirements dimensions={:#x}, block={}, pitch={}, size={:#x}, alignment={:#x}, result={:#x}", - packet[4], packet[6], packet[7], requirements ? requirements->size : 0, - requirements ? requirements->alignment : 0, result.raw); - IPC::ResponseBuilder rb{ctx, gpu_image ? 4U : 6U}; - rb.Push(result.raw); - rb.Push<u64>(requirements ? requirements->size : 0); - if (!gpu_image) { rb.Push<u64>(requirements ? requirements->alignment : 0); } -} -void IGraphicsSupervisor::CreateNativeMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const u64 size = u64{packet[3]} | (u64{packet[4]} << 32); - Result result = Kernel::ResultInvalidCombination; - u32 id{}; - if (packet[0] == 0x7f && packet[1] == 3 && (packet[2] & 0xff) == 3) { - result = Kernel::ResultInvalidState; - if (m_phase == Phase::Ready && m_image_formats_initialized && m_nv_module) { - result = Kernel::ResultInvalidSize; - if (size && size <= (1ULL << 32) && Common::IsAligned(size, Kernel::PageSize)) { - result = Kernel::ResultOutOfResource; - if (m_native_memories.size() < 1024) { - auto& nvmap = m_nv_module->GetContainer().GetNvMapFile(); - std::shared_ptr<Nvidia::NvCore::NvMap::Handle> handle; - if (nvmap.CreateHandle(size, handle) == Nvidia::NvResult::Success) { - id = handle->id; - m_native_memories.push_back(id); - result = ResultSuccess; - } - } - } - } - } - LOG_DEBUG(Service_AM, "native memory create size={:#x}, id={}, result={:#x}", size, id, result.raw); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push(id); -} - -Result IGraphicsSupervisor::BindNativeMemoryBacking(u32 id, u64 address, u64 size, u64 flags, u64 offset) { - R_UNLESS(m_nv_module && std::ranges::find(m_native_memories, id) != m_native_memories.end(), - Kernel::ResultNotFound); - R_UNLESS((flags == 0 || flags == 2) && offset == 0, Kernel::ResultNotImplemented); - auto handle = m_nv_module->GetContainer().GetNvMapFile().GetHandle(id); - R_UNLESS(handle && !handle->allocated, Kernel::ResultInvalidState); - R_UNLESS(size && size == handle->size && address != 0 && address + size > address, - Kernel::ResultInvalidSize); - R_UNLESS(Common::IsAligned(address, Kernel::PageSize), Kernel::ResultInvalidAddress); - auto& page_table = m_process->GetPageTable(); - R_UNLESS(page_table.Contains(address, size), Kernel::ResultInvalidCurrentMemory); - if (flags == 2) { - for (u64 current = address; current < address + size;) { - Kernel::KMemoryInfo info{}; - Kernel::Svc::PageInfo page{}; - R_TRY(page_table.QueryInfo(&info, &page, current)); - R_UNLESS(True(info.m_attribute & Kernel::KMemoryAttribute::GpuAccessible), - Kernel::ResultInvalidCurrentMemory); - const u64 next = info.m_address + info.m_size; - R_UNLESS(next > current, Kernel::ResultInvalidCurrentMemory); - current = next; - } - } - bool is_io{}; - R_TRY(page_table.LockForMapDeviceAddressSpace(&is_io, address, size, - Kernel::KMemoryPermission::None, true, true)); - if (is_io) { - page_table.UnlockForDeviceAddressSpace(address, size); - R_RETURN(Kernel::ResultNotImplemented); - } - if (!m_native_memory_session) { - m_native_memory_session = m_nv_module->GetContainer().OpenSession(m_process).id; - } - const auto result = handle->Alloc({}, Kernel::PageSize, 0, address, - Nvidia::NvCore::SessionId{*m_native_memory_session}); - if (result != Nvidia::NvResult::Success) { - page_table.UnlockForDeviceAddressSpace(address, size); - R_RETURN(Kernel::ResultOutOfResource); - } - R_SUCCEED(); -} - -void IGraphicsSupervisor::BindNativeMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const auto pair = [&](size_t i) { return u64{packet[i]} | (u64{packet[i + 1]} << 32); }; - const auto result = packet[0] == 0x82 && packet[1] == 9 - ? BindNativeMemoryBacking(packet[2], pair(3), pair(5), pair(7), pair(9)) - : Kernel::ResultInvalidCombination; - LOG_DEBUG(Service_AM, "native memory bind id={}, address={:#x}, size={:#x}, flags={:#x}, result={:#x}", - packet[2], pair(3), pair(5), pair(7), result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -Result IGraphicsSupervisor::ReleaseNativeMemory(u32 id) { - const auto it = std::ranges::find(m_native_memories, id); - R_UNLESS(m_nv_module && it != m_native_memories.end(), Kernel::ResultNotFound); - auto& nvmap = m_nv_module->GetContainer().GetNvMapFile(); - auto handle = nvmap.GetHandle(id); - R_UNLESS(handle, Kernel::ResultNotFound); - { - const std::scoped_lock lock{handle->mutex}; - R_UNLESS(handle->pins == 0 && handle->internal_dupes == 0 && handle->dupes == 1, - Kernel::ResultBusy); - } - const bool allocated = handle->allocated; - handle.reset(); - const auto info = nvmap.FreeHandle(id, false); - m_native_memories.erase(it); - if (allocated) { - R_UNLESS(info && info->can_unlock, Kernel::ResultInvalidState); - R_TRY(m_process->GetPageTable().UnlockForDeviceAddressSpace(info->address, info->size)); - } - R_SUCCEED(); -} - -void IGraphicsSupervisor::DestroyNativeMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const auto result = packet[0] == 0x80 && packet[1] == 1 - ? ReleaseNativeMemory(packet[2]) : Kernel::ResultInvalidCombination; - LOG_DEBUG(Service_AM, "native memory destroy id={}, result={:#x}", packet[2], result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::BindNativeGpuMemory(HLERequestContext& ctx) { - const auto* packet = ctx.CommandBuffer(); - const auto pair = [&](size_t i) { return u64{packet[i]} | (u64{packet[i + 1]} << 32); }; - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x1b7 && packet[1] == 12 && pair(10) == 0 && pair(12) == 0) { - result = Kernel::ResultNotFound; - if (m_nv_module) { - auto& container = m_nv_module->GetContainer(); - const auto parent = container.GetNvMapFile().GetHandle(packet[7]); - if (parent && parent->allocated) { - const auto* session = container.GetSession(parent->session_id); - const u64 offset = pair(8); - const u64 address = pair(3); - const u64 size = pair(5); - if (session && session->process == m_process && offset <= parent->size && - size <= parent->size - offset && parent->address + offset >= parent->address && - address == parent->address + offset) { - result = BindNativeMemoryBacking(packet[2], address, size, 2, 0); - } - } - } - } - LOG_DEBUG(Service_AM, "native GPU memory bind id={}, parent={}, address={:#x}, size={:#x}, offset={:#x}, result={:#x}", - packet[2], packet[7], pair(3), pair(5), pair(8), result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::CreateNativeImage(HLERequestContext& ctx) { - const std::scoped_lock images_lock{m_image_registry->mutex}; - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - u32 id{}; - if (packet[0] == 0x1bd && packet[1] == 18 && packet[3] == 1) { - result = Kernel::ResultInvalidState; - if (m_phase == Phase::Ready && m_gpu_context_ready && m_image_formats_initialized && m_nv_module) { - const auto requirements = CalculateNativeImageRequirements(std::span{packet + 2, 16}, packet[18]); - result = Kernel::ResultNotImplemented; - if (requirements) { - result = Kernel::ResultNotFound; - auto& nvmap = m_nv_module->GetContainer().GetNvMapFile(); - const auto memory = nvmap.GetHandle(packet[19]); - if (std::ranges::find(m_native_memories, packet[19]) != m_native_memories.end() && - memory && memory->allocated) { - result = Kernel::ResultInvalidSize; - if (memory->size >= requirements->size) { - result = Kernel::ResultOutOfResource; - if (m_image_registry->images.size() < 1024 && m_next_native_image_id != 0 && - memory->Duplicate(true) == Nvidia::NvResult::Success) { - NativeImage image{m_next_native_image_id++, packet[19], {}, requirements->size}; - std::copy_n(packet + 2, image.preference.size(), image.preference.begin()); - id = image.id; - if (!m_image_registry->module) { - m_image_registry->module = m_nv_module; - m_image_registry->memory_session = m_nv_module->GetContainer().OpenSession(m_process); - } - m_image_registry->images.push_back(image); - result = ResultSuccess; - } - } - } - } - } - } - LOG_DEBUG(Service_AM, "native image create memory={}, count={}, id={}, result={:#x}", - packet[19], packet[18], id, result.raw); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result.raw); - rb.Push(id); -} - -void IGraphicsSupervisor::DestroyNativeImage(HLERequestContext& ctx) { - const std::scoped_lock images_lock{m_image_registry->mutex}; - const auto* packet = ctx.CommandBuffer(); - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == 0x1be && packet[1] == 1) { - const auto image = std::ranges::find(m_image_registry->images, packet[2], &NativeImage::id); - result = Kernel::ResultNotFound; - if (m_nv_module && image != m_image_registry->images.end()) { - result = Kernel::ResultBusy; - if (!image->window_pool) { - m_image_registry->ReleaseMemory(image->memory_id); - m_image_registry->images.erase(image); - result = ResultSuccess; - } - } - } - LOG_DEBUG(Service_AM, "native image destroy id={}, result={:#x}", packet[2], result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -void IGraphicsSupervisor::ConfigureNativeWindowImagePool(HLERequestContext& ctx) { - const std::scoped_lock images_lock{m_image_registry->mutex}; - const auto* packet = ctx.CommandBuffer(); - const bool create = ctx.GetCommand() == 315; - Result result = Kernel::ResultInvalidCombination; - if (packet[0] == (create ? 0x14bU : 0x14aU) && packet[1] == (create ? 2U : 1U)) { - const auto image = std::ranges::find(m_image_registry->images, packet[2], &NativeImage::id); - result = Kernel::ResultNotFound; - if (image != m_image_registry->images.end()) { - result = Kernel::ResultNotImplemented; - if (!create || packet[3] == 1) { - result = Kernel::ResultInvalidState; - if (image->window_pool != create) { - image->window_pool = create; - result = ResultSuccess; - } - } - } - } - LOG_DEBUG(Service_AM, "native window image pool id={}, create={}, result={:#x}", - packet[2], create, result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.raw); -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_supervisor.h b/src/core/hle/service/am/service/graphics_supervisor.h deleted file mode 100644 index 4163c2c150..0000000000 --- a/src/core/hle/service/am/service/graphics_supervisor.h +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <vector> - -#include "core/hle/service/service.h" -#include "core/hle/service/am/service/graphics_syncpoint_cache.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/os/event.h" - -namespace Kernel { -class KProcess; -class KTransferMemory; -class KEvent; -} - -namespace Core::Timing { -struct EventType; -} -namespace Service::Nvidia { -class Module; -} - -namespace Service::AM { -struct NativeImageRegistry; - -class IGpuSession; - -class IGraphicsSupervisor final : public ServiceFramework<IGraphicsSupervisor> { -public: - explicit IGraphicsSupervisor(Core::System& system, Kernel::KProcess* process, u64 aruid); - ~IGraphicsSupervisor() override; - Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) override; - -private: - enum class Phase { Created, Initialized, ProcessBound, Ready }; - void Initialize(HLERequestContext& ctx); - void BindProcess(HLERequestContext& ctx); - void CompleteBootstrap(HLERequestContext& ctx); - void InitializeImageContext(HLERequestContext& ctx); - void GetNvidiaDriver(HLERequestContext& ctx); - void CalculateImageRequirements(HLERequestContext& ctx); - void CreateNativeMemory(HLERequestContext& ctx); - void CreateNativeImage(HLERequestContext& ctx); - void DestroyNativeImage(HLERequestContext& ctx); - void ConfigureNativeWindowImagePool(HLERequestContext& ctx); - void BindNativeMemory(HLERequestContext& ctx); - void BindNativeGpuMemory(HLERequestContext& ctx); - void DestroyNativeMemory(HLERequestContext& ctx); - Result BindNativeMemoryBacking(u32 id, u64 address, u64 size, u64 flags, u64 offset); - Result ReleaseNativeMemory(u32 id); - void ConfigureNativeMemory(HLERequestContext& ctx); - void InitializeFrameContext(HLERequestContext& ctx); - void RegisterGpuAddressSpace(HLERequestContext& ctx); - void OpenGpuSession(HLERequestContext& ctx); - void OpenDisplaySession(HLERequestContext& ctx); - void InitializeDisplayContext(HLERequestContext& ctx); - void InitializeGpuContext(HLERequestContext& ctx); - void InitializeSyncpointContext(HLERequestContext& ctx); - void GetSyncpointSession(HLERequestContext& ctx); - void BindSyncpointMemory(HLERequestContext& ctx); - void RefreshSyncpointCache(); - - std::vector<std::weak_ptr<IGpuSession>> m_gpu_sessions; - std::shared_ptr<Nvidia::Module> m_nv_module; - std::optional<size_t> m_native_memory_session; - std::vector<u32> m_native_memories; - std::shared_ptr<NativeImageRegistry> m_image_registry; - u32 m_next_native_image_id{1}; - std::shared_ptr<Core::Timing::EventType> m_cache_poll; - std::array<GraphicsSyncpointCache::CounterValue, GraphicsSyncpointCache::CounterCount> - m_counter_values{}; - Kernel::KTransferMemory* m_syncpoint_rw{}; - Kernel::KTransferMemory* m_syncpoint_ro{}; - Kernel::KEvent* m_syncpoint_request_event{}; - KernelHelpers::ServiceContext m_context; - Event m_syncpoint_cache_event; - bool m_syncpoint_context_initialized{}; - Kernel::KProcess* const m_process; - const u64 m_aruid; - Phase m_phase{Phase::Created}; - bool m_display_context_initialized{}; - bool m_gpu_context_initialized{}; - bool m_gpu_context_ready{}; - bool m_frame_context_initialized{}; - bool m_frame_context_ready{}; - bool m_image_context_initialized{}; - bool m_image_formats_initialized{}; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/graphics_supervisor_protocol.h b/src/core/hle/service/am/service/graphics_supervisor_protocol.h deleted file mode 100644 index 7762ced531..0000000000 --- a/src/core/hle/service/am/service/graphics_supervisor_protocol.h +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include <optional> -#include <span> -#include "common/common_types.h" - -namespace Service::AM::GraphicsSupervisorProtocol { - -constexpr u8 DecodeBufferSlot(u32 packed) { - return static_cast<u8>(packed); -} - -struct BootstrapRequest { - u32 process_handle; - u64 applet_resource_user_id; - u64 flags; - std::array<u64, 4> parameters; -}; - -inline std::optional<BootstrapRequest> DecodeBootstrap(std::span<const u32> packet) { - // Captured remote-memory bootstrap: TIPC 4, one copied handle, 48 data bytes. - if (packet.size() < 16 || packet[0] != 0x14 || packet[1] != 0x8000000c || - packet[2] != 2) { - return std::nullopt; - } - const auto Read64 = [&](size_t index) { - return u64{packet[index]} | (u64{packet[index + 1]} << 32); - }; - return BootstrapRequest{packet[3], Read64(4), Read64(6), - {Read64(8), Read64(10), Read64(12), Read64(14)}}; -} - -} // namespace Service::AM::GraphicsSupervisorProtocol diff --git a/src/core/hle/service/am/service/graphics_syncpoint_cache.h b/src/core/hle/service/am/service/graphics_syncpoint_cache.h deleted file mode 100644 index 23361b006c..0000000000 --- a/src/core/hle/service/am/service/graphics_syncpoint_cache.h +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include <limits> -#include <optional> -#include "common/common_types.h" - -namespace Service::AM::GraphicsSyncpointCache { -constexpr u32 ValuesOffset = 0x40; -constexpr u32 GroupAckOffset = 0x640; -constexpr u32 SlotAckOffset = 0x648; -constexpr u32 GroupRequestOffset = 0; -constexpr u32 SlotRequestOffset = 8; -constexpr u32 CounterCount = 192; -constexpr u32 GroupCount = (CounterCount + 63) / 64; -constexpr u64 Unallocated = (std::numeric_limits<u64>::max)(); - -constexpr std::array<u32, 8> Header{ - 1, 0, CounterCount - 1, ValuesOffset, GroupRequestOffset, - GroupAckOffset, SlotRequestOffset, SlotAckOffset}; -static_assert(ValuesOffset + CounterCount * sizeof(u64) <= GroupAckOffset); -static_assert(SlotAckOffset + GroupCount * sizeof(u64) <= 0x2000); -static_assert(SlotRequestOffset + GroupCount * sizeof(u64) <= 0x1000); - -constexpr u64 ExtendCounter(u64 previous, u32 current) { - if (previous == Unallocated) { - return current; - } - u64 high = previous & 0xffffffff00000000ULL; - if (current < static_cast<u32>(previous)) { - high += 1ULL << 32; - } - return high | current; -} -struct CounterValue { - u64 previous{Unallocated}; - - u64 Observe(std::optional<u32> current) { - if (!current) { - return Unallocated; - } - previous = ExtendCounter(previous, *current); - return previous; - } -}; -} // namespace Service::AM::GraphicsSyncpointCache diff --git a/src/core/hle/service/am/service/native_image_layout.h b/src/core/hle/service/am/service/native_image_layout.h deleted file mode 100644 index eb45fc470d..0000000000 --- a/src/core/hle/service/am/service/native_image_layout.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <optional> -#include <span> -#include "common/common_types.h" - -namespace Service::AM { - -struct NativeImageRequirements { - u64 size; - u64 alignment; -}; - -// Experimental inter-module block image layout, restricted to the observed -// four-byte color format. Row pitch is in 64-byte GOBs; block height is in pixels. -inline std::optional<NativeImageRequirements> CalculateNativeImageRequirements( - std::span<const u32> preference, u32 count) { - if (preference.size() != 16 || count != 1 || preference[0] != 0 || - (preference[1] != 1 && preference[1] != 2) || preference[3] != 0 || preference[6] != 1 || - preference[7] != 1 || preference[4] < 3 || preference[4] > 8) { - return std::nullopt; - } - for (size_t i = 8; i < preference.size(); ++i) { - if (preference[i] != 0) { return std::nullopt; } - } - const u64 width = (preference[2] & 0xffffU) + 1; - const u64 height = (preference[2] >> 16) + 1; - const u64 pitch_gobs = preference[5]; - if (pitch_gobs < (width * 4 + 63) / 64 || pitch_gobs > 4096) { - return std::nullopt; - } - const u64 block_height = 1ULL << preference[4]; - const u64 aligned_height = (height + block_height - 1) & ~(block_height - 1); - return NativeImageRequirements{pitch_gobs * 64 * aligned_height, 64 * block_height}; -} - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/performance_functions.cpp b/src/core/hle/service/am/service/performance_functions.cpp deleted file mode 100644 index 71a46cc5b5..0000000000 --- a/src/core/hle/service/am/service/performance_functions.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "core/hle/service/am/service/performance_functions.h" - -namespace Service::AM { - -IPerformanceFunctions::IPerformanceFunctions(Core::System& system) - : ServiceFramework{system, "IPerformanceFunctions"} { - static const FunctionInfo functions[] = { - {0, nullptr, "IsCpuOverclockEnabled"}, - {1, nullptr, "SetCpuOverclockEnabled"}, - }; - RegisterHandlers(functions); -} - -IPerformanceFunctions::~IPerformanceFunctions() = default; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/performance_functions.h b/src/core/hle/service/am/service/performance_functions.h deleted file mode 100644 index 12195d1217..0000000000 --- a/src/core/hle/service/am/service/performance_functions.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service::AM { - -class IPerformanceFunctions final : public ServiceFramework<IPerformanceFunctions> { -public: - explicit IPerformanceFunctions(Core::System& system); - ~IPerformanceFunctions() override; -}; - -} // namespace Service::AM diff --git a/src/core/hle/service/am/service/self_controller.cpp b/src/core/hle/service/am/service/self_controller.cpp index 45b9120093..3a654b9daa 100644 --- a/src/core/hle/service/am/service/self_controller.cpp +++ b/src/core/hle/service/am/service/self_controller.cpp @@ -5,17 +5,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging.h" -#include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_session.h" -#include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_thread.h" #include "core/hle/result.h" #include "core/hle/service/am/am_results.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/service/self_controller.h" -#include "core/hle/service/am/service/graphics_supervisor.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/server_manager.h" #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/nvnflinger/nvnflinger.h" @@ -80,7 +73,6 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> {230, D<&ISelfController::Unknown230>, "Unknown230"}, {240, D<&ISelfController::Unknown240>, "Unknown240"}, {1000, nullptr, "GetDebugStorageChannel"}, - {2000, &ISelfController::GetGraphicsSupervisor, "GetGraphicsSupervisor"}, }; // clang-format on @@ -92,47 +84,10 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> } ISelfController::~ISelfController() { - if (m_graphics_supervisor_session != nullptr) { - m_graphics_supervisor_session->Close(kernel); - } std::scoped_lock lk{m_applet->lock}; m_applet->display_layer_manager.Finalize(); } -void ISelfController::GetGraphicsSupervisor(HLERequestContext& ctx) { - if (m_graphics_supervisor_session != nullptr) { - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(ctx, m_graphics_supervisor_session); - return; - } - Kernel::KScopedResourceReservation reservation( - kernel, Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax); - if (!reservation.Succeeded()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultLimitReached); - return; - } - auto* session = Kernel::KSession::Create(kernel); - if (session == nullptr) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultOutOfResource); - return; - } - session->Initialize(kernel, nullptr, 0); - reservation.Commit(); - Kernel::KSession::Register(kernel, session); - auto& server = ctx.GetManager()->GetServerManager(); - auto manager = std::make_shared<SessionRequestManager>(kernel, server); - manager->SetSessionHandler(std::make_shared<IGraphicsSupervisor>(system, m_process, m_applet->aruid.pid)); - server.RegisterSession(&session->GetServerSession(), std::move(manager)); - LOG_DEBUG(Service_AM, "created graphics-supervisor session"); - m_graphics_supervisor_session = &session->GetClientSession(); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(ctx, m_graphics_supervisor_session); -} - Result ISelfController::Exit() { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/am/service/self_controller.h b/src/core/hle/service/am/service/self_controller.h index 52970ed4a8..c9f480f575 100644 --- a/src/core/hle/service/am/service/self_controller.h +++ b/src/core/hle/service/am/service/self_controller.h @@ -12,7 +12,6 @@ namespace Kernel { class KReadableEvent; -class KClientSession; } namespace Service::Capture { @@ -31,7 +30,6 @@ public: ~ISelfController() override; private: - void GetGraphicsSupervisor(HLERequestContext& ctx); Result Exit(); Result LockExit(); Result UnlockExit(); @@ -72,7 +70,6 @@ private: Result Unknown230(u32 in_val, Out<u16> out_val); Result Unknown240(u32 in_val); - Kernel::KClientSession* m_graphics_supervisor_session{}; Kernel::KProcess* const m_process; const std::shared_ptr<Applet> m_applet; }; diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 63d578462f..fde766a275 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -5,8 +5,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" -#include "core/hle/kernel/svc_results.h" -#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/audio/audio_controller.h" #include "core/hle/service/audio/audio_in_manager.h" @@ -20,57 +18,6 @@ namespace Service::Audio { -class INativeAudioManager final : public ServiceFramework<INativeAudioManager> { -public: - INativeAudioManager(Core::System& system_, std::shared_ptr<IAudioInManager> input, - std::shared_ptr<IAudioOutManager> output, - std::shared_ptr<IAudioRendererManager> renderer) - : ServiceFramework{system_, "aud:u"}, m_input{std::move(input)}, - m_output{std::move(output)}, m_renderer{std::move(renderer)} { - static const FunctionInfo functions[] = { - {0, &INativeAudioManager::GetAudioInManager, "GetAudioInManager"}, - {1, &INativeAudioManager::GetAudioOutManager, "GetAudioOutManager"}, - {2, &INativeAudioManager::GetAudioRendererManager, "GetAudioRendererManager"}, - }; - RegisterHandlers(functions); - } - - Result HandleSyncRequest(Kernel::KServerSession& session, HLERequestContext& ctx) override { - if (!ctx.IsTipc()) { - return ServiceFrameworkBase::HandleSyncRequest(session, ctx); - } - const auto guard = LockService(); - LOG_WARNING(Service_Audio, "Native audio unsupported command={}, words={:08x}", ctx.GetCommand(), - fmt::join(std::span<const u32>(ctx.CommandBuffer() + 2, - std::min<u32>(ctx.CommandBuffer()[1], 32)), ",")); - InvokeRequestTipc(ctx); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Kernel::ResultNotImplemented.raw); - return system.IsPoweredOn() ? ctx.WriteToOutgoingCommandBuffer() : ResultSuccess; - } - -private: - void GetAudioInManager(HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(ctx, m_input); - } - void GetAudioOutManager(HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(ctx, m_output); - } - void GetAudioRendererManager(HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "Native user client opens existing audio renderer manager"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(ctx, m_renderer); - } - std::shared_ptr<IAudioInManager> m_input; - std::shared_ptr<IAudioOutManager> m_output; - std::shared_ptr<IAudioRendererManager> m_renderer; -}; - class IAudioOutManagerForApplet final : public ServiceFramework<IAudioOutManagerForApplet> { public: explicit IAudioOutManagerForApplet(Core::System& system_) @@ -246,23 +193,18 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30); - auto audio_input = std::make_shared<IAudioInManager>(system); - server_manager->RegisterNamedService("audin:u", audio_input, 30); + server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30); server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30); - auto audio_output = std::make_shared<IAudioOutManager>(system); - server_manager->RegisterNamedService("audout:u", audio_output, 30); + server_manager->RegisterNamedService("audout:u", std::make_shared<IAudioOutManager>(system), 30); server_manager->RegisterNamedService("audout:a", std::make_shared<IAudioOutManagerForApplet>(system), 30); server_manager->RegisterNamedService("auddev", std::make_shared<IAudioSnoopManager>(system), 30); // Depends on audout:u and audin:u on ctor! server_manager->RegisterNamedService("audctl", std::make_shared<IAudioController>(system), 30); server_manager->RegisterNamedService("audrec:a", std::make_shared<IFinalOutputRecorderManagerForApplet>(system), 30); server_manager->RegisterNamedService("audrec:u", std::make_shared<IFinalOutputRecorderManager>(system), 30); - auto audio_renderer = std::make_shared<IAudioRendererManager>(system); - server_manager->RegisterNamedService("audren:u", audio_renderer, 30); + server_manager->RegisterNamedService("audren:u", std::make_shared<IAudioRendererManager>(system), 30); server_manager->RegisterNamedService("audren:a", std::make_shared<IAudioRendererManagerForApplet>(system), 30); server_manager->RegisterNamedService("hwopus", std::make_shared<IHardwareOpusDecoderManager>(system), 25); - server_manager->RegisterNamedService("aud:u", - std::make_shared<INativeAudioManager>(system, audio_input, audio_output, audio_renderer), 30); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 7ddb944abe..9689bce8dd 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -132,7 +132,6 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r {210, C<&IHidServer::EndPermitVibrationSession>, "EndPermitVibrationSession"}, {211, C<&IHidServer::IsVibrationDeviceMounted>, "IsVibrationDeviceMounted"}, {212, C<&IHidServer::SendVibrationValueInBool>, "SendVibrationValueInBool"}, - {214, C<&IHidServer::SendNativeVibrationValues>, "SendNativeVibrationValues"}, {300, C<&IHidServer::ActivateConsoleSixAxisSensor>, "ActivateConsoleSixAxisSensor"}, {301, C<&IHidServer::StartConsoleSixAxisSensor>, "StartConsoleSixAxisSensor"}, {302, C<&IHidServer::StopConsoleSixAxisSensor>, "StopConsoleSixAxisSensor"}, @@ -238,11 +237,6 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r {3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+ {3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+ {3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+ - {3110, C<&IHidServer::RegisterJcmsMemory>, "RegisterJcmsMemory"}, - {3136, C<&IHidServer::SetNmouseButtonConfigJoyLeftJcms>, "SetNmouseButtonConfigJoyLeftJcms"}, - {3137, C<&IHidServer::SetNmouseButtonConfigJoyRightJcms>, "SetNmouseButtonConfigJoyRightJcms"}, - {3138, C<&IHidServer::SetMergedNmouseSourceTypes>, "SetMergedNmouseSourceTypes"}, - {3139, C<&IHidServer::RegisterNativeInputMemory>, "RegisterNativeInputMemory"}, {3150, C<&IHidServer::SetMouseLibraryVersion>, "SetMouseLibraryVersion"}, //21.0.0+ // What? -- {12010, nullptr, "SetButtonConfigLeft"}, }; @@ -251,11 +245,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r RegisterHandlers(functions); } -IHidServer::~IHidServer() { - for (const auto& registration : native_input_memories) { - registration.memory->Close(kernel); - } -} +IHidServer::~IHidServer() = default; Result IHidServer::CreateAppletResource(OutInterface<IAppletResource> out_applet_resource, ClientAppletResourceUserId aruid) { @@ -994,17 +984,6 @@ Result IHidServer::SendVibrationValues( R_SUCCEED(); } -Result IHidServer::SendNativeVibrationValues( - u16 flags, AppletResourceUserId aruid, - InArray<Core::HID::VibrationDeviceHandle, BufferAttr_HipcPointer> vibration_handles, - InArray<Core::HID::VibrationValue, BufferAttr_HipcPointer> vibration_values) { - static u32 diagnostic_batches{}; - if (diagnostic_batches++ < 8) LOG_INFO(Service_HID, "Native vibration batch flags={:#x}, aruid={}, handles={}, values={}", - flags, aruid.pid, vibration_handles.size(), vibration_values.size()); - R_UNLESS(flags == 0 || flags == 2, IPC::ResultNotSupported); - R_RETURN(SendVibrationValues(ClientAppletResourceUserId{aruid.pid}, vibration_handles, vibration_values)); -} - Result IHidServer::SendVibrationGcErmCommand( Core::HID::VibrationDeviceHandle vibration_device_handle, ClientAppletResourceUserId aruid, Core::HID::VibrationGcErmCommand gc_erm_command) { @@ -1492,63 +1471,6 @@ Result IHidServer::SetTouchScreenResolution(u32 width, u32 height, R_SUCCEED(); } -Result IHidServer::RegisterNativeInputMemory( - u8 mode, u32 parameter0, u32 parameter1, u32 parameter2, - ClientAppletResourceUserId aruid, u64 size, - InCopyHandle<Kernel::KTransferMemory> memory) { - LOG_INFO(Service_HID, "Native input memory mode={}, parameters={:#x}/{:#x}/{:#x}, aruid={}, size={:#x}", - mode, parameter0, parameter1, parameter2, aruid.pid, size); - R_UNLESS(memory.Get() && memory->GetOwner()->UsesPAuthReturns() && - memory->GetOwner()->GetProcessId() == aruid.pid, Kernel::ResultInvalidHandle); - R_UNLESS(mode <= 1 && size && size == memory->GetSize() && size <= 0x1000000, - Kernel::ResultInvalidSize); - R_UNLESS(native_input_memories.size() < 64, Kernel::ResultOutOfResource); - const auto existing = std::ranges::find(native_input_memories, memory.Get(), &NativeInputMemory::memory); - R_UNLESS(existing == native_input_memories.end(), Kernel::ResultInvalidState); - memory->Open(kernel); - native_input_memories.push_back({memory.Get(), aruid.pid, size, {mode, parameter0, parameter1, parameter2}}); - // Retain the real guest-owned input buffer. No Joy-Con mouse samples are - // produced while those devices are disconnected. - R_SUCCEED(); -} - -Result IHidServer::RegisterJcmsMemory(ClientAppletResourceUserId aruid, u64 size, - InCopyHandle<Kernel::KTransferMemory> memory) { - LOG_INFO(Service_HID, "Native Joy-Con mouse memory: aruid={}, size={:#x}", aruid.pid, size); - return RegisterNativeInputMemory(1, 0, 0, 0, aruid, size, memory); -} - -Result IHidServer::SetMergedNmouseSourceTypes(u32 source_types, ClientAppletResourceUserId aruid) { - LOG_INFO(Service_HID, "Native merged mouse sources: types={:#x}, aruid={}", source_types, aruid.pid); - R_UNLESS((source_types & ~6U) == 0, Kernel::ResultInvalidCombination); - const auto memory = std::ranges::find_if(native_input_memories, [&](const auto& registration) { - return registration.aruid == aruid.pid && registration.parameters[0] == 0; - }); - R_UNLESS(memory != native_input_memories.end(), Kernel::ResultNotFound); - memory->merged_source_types = source_types; - R_SUCCEED(); -} - -Result IHidServer::SetNmouseButtonConfiguration(ClientAppletResourceUserId aruid, u64 configuration, - size_t side) { - LOG_INFO(Service_HID, "Native Joy-Con mouse buttons: side={}, config={:#x}, aruid={}", - side, configuration, aruid.pid); - const auto memory = std::ranges::find_if(native_input_memories, [&](const auto& registration) { - return registration.aruid == aruid.pid && registration.parameters[0] == 0; - }); - R_UNLESS(memory != native_input_memories.end(), Kernel::ResultNotFound); - memory->jcms_button_configurations[side] = configuration; - R_SUCCEED(); -} - -Result IHidServer::SetNmouseButtonConfigJoyLeftJcms(ClientAppletResourceUserId aruid, u64 configuration) { - return SetNmouseButtonConfiguration(aruid, configuration, 0); -} - -Result IHidServer::SetNmouseButtonConfigJoyRightJcms(ClientAppletResourceUserId aruid, u64 configuration) { - return SetNmouseButtonConfiguration(aruid, configuration, 1); -} - Result IHidServer::SetMouseLibraryVersion(ClientAppletResourceUserId aruid) { LOG_INFO(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h index 45097f361d..57a1a04b9c 100644 --- a/src/core/hle/service/hid/hid_server.h +++ b/src/core/hle/service/hid/hid_server.h @@ -6,8 +6,6 @@ #pragma once -#include <array> -#include <vector> #include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" #include "hid_core/hid_types.h" @@ -177,10 +175,6 @@ private: ClientAppletResourceUserId aruid, InArray<Core::HID::VibrationDeviceHandle, BufferAttr_HipcPointer> vibration_handles, InArray<Core::HID::VibrationValue, BufferAttr_HipcPointer> vibration_values); - Result SendNativeVibrationValues( - u16 flags, AppletResourceUserId aruid, - InArray<Core::HID::VibrationDeviceHandle, BufferAttr_HipcPointer> vibration_handles, - InArray<Core::HID::VibrationValue, BufferAttr_HipcPointer> vibration_values); Result SendVibrationGcErmCommand(Core::HID::VibrationDeviceHandle vibration_device_handle, ClientAppletResourceUserId aruid, Core::HID::VibrationGcErmCommand gc_erm_command); @@ -271,24 +265,6 @@ private: Result SetTouchScreenResolution(u32 width, u32 height, ClientAppletResourceUserId aruid); Result SetMouseLibraryVersion(ClientAppletResourceUserId aruid); - Result RegisterNativeInputMemory(u8 mode, u32 parameter0, u32 parameter1, u32 parameter2, - ClientAppletResourceUserId aruid, u64 size, - InCopyHandle<Kernel::KTransferMemory> memory); - Result RegisterJcmsMemory(ClientAppletResourceUserId aruid, u64 size, - InCopyHandle<Kernel::KTransferMemory> memory); - Result SetMergedNmouseSourceTypes(u32 source_types, ClientAppletResourceUserId aruid); - Result SetNmouseButtonConfigJoyLeftJcms(ClientAppletResourceUserId aruid, u64 configuration); - Result SetNmouseButtonConfigJoyRightJcms(ClientAppletResourceUserId aruid, u64 configuration); - Result SetNmouseButtonConfiguration(ClientAppletResourceUserId aruid, u64 configuration, size_t side); - struct NativeInputMemory { - Kernel::KTransferMemory* memory; - u64 aruid; - u64 size; - std::array<u32, 4> parameters; - u32 merged_source_types{}; - std::array<u64, 2> jcms_button_configurations{}; - }; - std::vector<NativeInputMemory> native_input_memories; std::shared_ptr<ResourceManager> resource_manager; std::shared_ptr<HidFirmwareSettings> firmware_settings; }; diff --git a/src/core/hle/service/ngc/ngc.cpp b/src/core/hle/service/ngc/ngc.cpp index 2c79336d93..e79108a1eb 100644 --- a/src/core/hle/service/ngc/ngc.cpp +++ b/src/core/hle/service/ngc/ngc.cpp @@ -7,7 +7,6 @@ #include "common/string_util.h" #include "core/core.h" #include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/result.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/cmif_types.h" @@ -183,8 +182,6 @@ public: explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") { // clang-format off static const FunctionInfo functions[] = { - {1, D<&IUserShimScopedObject::GetRequiredMemorySize>, "GetRequiredMemorySize"}, - {300, D<&IUserShimScopedObject::GetGuestHidSharedMemory>, "GetGuestHidSharedMemory"}, {450, nullptr, "InitializeForSaveData"}, {451, nullptr, "FinalizeForSaveData"}, {452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"}, @@ -198,50 +195,6 @@ public: RegisterHandlers(functions); } - ~IUserShimScopedObject() override { - if (m_guest_hid_memory) { - m_guest_hid_memory->Close(kernel); - } - } - - struct GuestHidMemoryConfig { - u64 size; - u64 offset; - }; - - Result GetRequiredMemorySize(Out<u32> out_size, u8 type, u8 mode, s32 guest_count) { - LOG_INFO(Service_NGC, "GameShare work memory query type={}, mode={}, guests={}", type, mode, guest_count); - R_UNLESS(type <= 1 && mode == 1 && guest_count >= 0 && guest_count <= 3, - IPC::ResultNotSupported); - // The disconnected implementation reserves one page for its session - // control state. Streaming/encoding work is not implemented. - *out_size = 0x1000; - R_SUCCEED(); - } - - Result GetGuestHidSharedMemory(Out<GuestHidMemoryConfig> out_config, - OutCopyHandle<Kernel::KSharedMemory> out_memory) { - // Three guest pad blocks, touch rings, and JCMS rings. Empty rings and - // zero style sets describe no connected GameShare guests. - constexpr u64 GuestHidMemorySize = 0x84000; - if (!m_guest_hid_memory) { - auto* memory = Kernel::KSharedMemory::Create(kernel); - const auto result = memory->Initialize(kernel, system.DeviceMemory(), nullptr, - Kernel::Svc::MemoryPermission::ReadWrite, Kernel::Svc::MemoryPermission::Read, - GuestHidMemorySize); - if (R_FAILED(result)) { - memory->Close(kernel); - R_RETURN(result); - } - Kernel::KSharedMemory::Register(kernel, memory); - m_guest_hid_memory = memory; - } - *out_config = GuestHidMemoryConfig{GuestHidMemorySize, 0}; - *out_memory = m_guest_hid_memory; - LOG_DEBUG(Service_NGC, "GameShare guest HID memory size={:#x}, no guests connected", GuestHidMemorySize); - R_SUCCEED(); - } - Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) { LOG_WARNING(Service_NGC, "stubbed"); R_THROW(IPC::ResultNotSupported); @@ -257,9 +210,6 @@ public: // to implement R_SUCCEED(); } - -private: - Kernel::KSharedMemory* m_guest_hid_memory{}; }; class IUserService final : public ServiceFramework<IUserService> { diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp index 8b929a7ef3..c4c4c25932 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp @@ -63,14 +63,6 @@ void SyncpointManager::FreeSyncpoint(u32 id) { syncpoint.reserved = false; } -std::optional<u32> SyncpointManager::ReadAllocatedSyncpoint(u32 id) { - std::lock_guard lock(reservation_lock); - if (id >= SyncpointCount || !syncpoints[id].reserved) { - return std::nullopt; - } - return UpdateMin(id); -} - bool SyncpointManager::IsSyncpointAllocated(u32 id) const { return (id < SyncpointCount) && syncpoints[id].reserved; } diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h index 42e1d547b8..7728ff5967 100644 --- a/src/core/hle/service/nvdrv/core/syncpoint_manager.h +++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h @@ -7,7 +7,6 @@ #include <array> #include <atomic> #include <mutex> -#include <optional> #include "common/common_types.h" #include "core/hle/service/nvdrv/nvdata.h" @@ -39,8 +38,6 @@ enum class ChannelType : u32 { */ class SyncpointManager final { public: - std::optional<u32> ReadAllocatedSyncpoint(u32 id); - explicit SyncpointManager(Tegra::Host1x::Host1x& host1x); ~SyncpointManager(); diff --git a/src/core/hle/service/nvdrv/devices/gpu_address_space_layout.h b/src/core/hle/service/nvdrv/devices/gpu_address_space_layout.h deleted file mode 100644 index a54ebb18d7..0000000000 --- a/src/core/hle/service/nvdrv/devices/gpu_address_space_layout.h +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "common/common_types.h" - -namespace Service::Nvidia::Devices { - -struct GpuAddressSpaceLayout { - u64 start; - u64 split; - u64 end; -}; - -constexpr GpuAddressSpaceLayout GetGpuAddressSpaceLayout(bool extended) { - // Leave room for other 4 KiB allocations beyond the native bootstrap - // reservation of 0x7ff8000000 bytes. - // Both layouts fit within the GPU's reported 40-bit virtual address width. - return {0x8000000, extended ? 3ULL << 38 : 1ULL << 34, - extended ? 1ULL << 40 : 1ULL << 37}; -} - -constexpr u32 GetGpuAddressSpaceBigPageSize(bool extended, u32 requested) { - // The native SDK passes its preferred allocation size here. A preference for - // ordinary 4 KiB mappings still needs a separate large-page pool in NVDRV. - return extended && requested == 0x1000 ? 0x10000 : requested; -} - -constexpr u64 GpuPageAddress(u32 page, u32 page_bits) { - return static_cast<u64>(page) << page_bits; -} - -} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/native_gpu_characteristics.h b/src/core/hle/service/nvdrv/devices/native_gpu_characteristics.h deleted file mode 100644 index 44f45cab75..0000000000 --- a/src/core/hle/service/nvdrv/devices/native_gpu_characteristics.h +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include <cstring> -#include <span> -#include "common/common_types.h" -#include "common/swap.h" - -namespace Service::Nvidia::Devices { - -inline constexpr size_t NativeGpuCharacteristicsSize = 0xd0; - -inline std::array<u8, NativeGpuCharacteristicsSize> MakeNativeGpuCharacteristics( - std::span<const u8, 0xa0> legacy) { - std::array<u8, NativeGpuCharacteristicsSize> result{}; - // The GPU description and engine classes share the legacy prefix. The native - // SDK consumes signed ioctl interface levels where the legacy ABI has topology. - std::memcpy(result.data(), legacy.data(), 0x70); - // Select the SDK's native control ABI, whose normal mappings support - // 4 KiB CPU/GPU aliases. Engine execution remains a separate implementation. - const u32_le native_arch{0x170}; - const u32_le native_impl{0xe}; - std::memcpy(result.data(), &native_arch, sizeof(native_arch)); - std::memcpy(result.data() + 4, &native_impl, sizeof(native_impl)); - // Native NVN's built-in shader table accepts Ampere SM 8.x. Carrying the - // inherited Maxwell SM 5.3 value here rejects its initialization shaders. - const u32_le native_sm_version{0x807}; - std::memcpy(result.data() + 0x3c, &native_sm_version, sizeof(native_sm_version)); - std::memcpy(result.data() + 0x40, &native_sm_version, sizeof(native_sm_version)); - // Native NVN maps CPU buffers into its shared VA reservation. Those buffers - // are 4 KiB aligned, so large pages must remain optional for allocation. - const u32_le preferred_page_size{0x1000}; - std::memcpy(result.data() + 0x28, &preferred_page_size, sizeof(preferred_page_size)); - u32_le available_page_sizes; - std::memcpy(&available_page_sizes, legacy.data() + 0x34, sizeof(available_page_sizes)); - available_page_sizes = (static_cast<u32>(available_page_sizes) & 0x10000) | 0x1000; - std::memcpy(result.data() + 0x34, &available_page_sizes, sizeof(available_page_sizes)); - u64_le flags; - std::memcpy(&flags, legacy.data() + 0x50, sizeof(flags)); - // Native SDK class enumeration filters 2D/3D/compute/inline families - // with bits 40..43 before selecting context identities from its profile. - // The native SDK gates ModifyKind (AS ioctl 12) on capability bit 34. - flags = static_cast<u64>(flags) | (1ULL << 8) | (1ULL << 22) | (1ULL << 26) | (1ULL << 34) | (0xfULL << 40); - std::memcpy(result.data() + 0x50, &flags, sizeof(flags)); - constexpr std::array<s16, 9> levels{ - 0x1c, // GPU control: GetGpuTime - 11, // TSG: bind/unbind channel with native subcontext IDs - -1, // GPU debugger device is unavailable - 0x1d, // Channel: SetTimeslice - 0x14, // Address space: Remap - 0, 0, 0, 0, // Additional interfaces have not been implemented. - }; - for (size_t i = 0; i < levels.size(); ++i) { - const u16_le value{static_cast<u16>(levels[i])}; - std::memcpy(result.data() + 0x70 + i * sizeof(value), &value, sizeof(value)); - } - // The native SDK allocates shared queue subcontexts locally, then binds - // each channel with TSG ioctl 11. Each emulated channel retains its own state. - const u32_le max_subcontexts{64}; - std::memcpy(result.data() + 0xb4, &max_subcontexts, sizeof(max_subcontexts)); - return result; -} - -} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/native_gpu_context.h b/src/core/hle/service/nvdrv/devices/native_gpu_context.h deleted file mode 100644 index 7c7131f211..0000000000 --- a/src/core/hle/service/nvdrv/devices/native_gpu_context.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <optional> -#include "common/common_types.h" - -namespace Service::Nvidia::Devices { - -// Native profile context identities. This classifies allocation metadata; -// engine method execution is handled separately by the GPU frontend. -inline std::optional<u32> GetNativeGpuContextSlot(u32 identity) { - switch (identity) { - case 0xc797: return 1; - case 0xc7c0: return 2; - case 0xc7b5: return 4; - case 0xc56f: return 5; - default: return std::nullopt; - } -} - -} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/native_gpu_kind.h b/src/core/hle/service/nvdrv/devices/native_gpu_kind.h deleted file mode 100644 index ecf3781162..0000000000 --- a/src/core/hle/service/nvdrv/devices/native_gpu_kind.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <optional> -#include "common/common_types.h" -#include "video_core/pte_kind.h" - -namespace Service::Nvidia::Devices { - -inline std::optional<u8> TranslateNativeUncompressedKind(u8 kind, u8 fallback) { - // NVIDIA's Turing MMU uses 6 for GENERIC_MEMORY and 7 for INVALID: - // open-gpu-doc/manuals/turing/tu104/dev_mmu.ref.txt. Eden's older kind - // numbering uses 0xfe for generic memory. Equal kinds need no compression. - if (kind != fallback) { return std::nullopt; } - switch (kind) { - case 0: return static_cast<u8>(Tegra::PTEKind::PITCH); - case 1: return static_cast<u8>(Tegra::PTEKind::Z16); - case 3: return static_cast<u8>(Tegra::PTEKind::S8Z24); - case 6: return static_cast<u8>(Tegra::PTEKind::GENERIC_16BX2); - default: return std::nullopt; - } -} - -struct NativeMemoryKind { - u8 backend; - u8 selected; -}; - -inline std::optional<NativeMemoryKind> SelectNativeMemoryKind(u8 requested, u8 fallback) { - if (const auto kind = TranslateNativeUncompressedKind(requested, fallback)) { - return NativeMemoryKind{*kind, requested}; - } - // The native caller supplies an uncompressed alternative explicitly. Return - // that selected native kind so the SDK invalidates its compressed preference. - if (requested == 8 && fallback == 6) { - return NativeMemoryKind{static_cast<u8>(Tegra::PTEKind::GENERIC_16BX2), 6}; - } - // TU104 kind 0x0c is the compressible S8Z24 variant; kind 3 is - // the caller's uncompressed S8Z24 alternative (dev_mmu.ref.txt). - if (requested == 0xb && fallback == 1) { - return NativeMemoryKind{static_cast<u8>(Tegra::PTEKind::Z16), 1}; - } - if (requested == 0xc && fallback == 3) { - return NativeMemoryKind{static_cast<u8>(Tegra::PTEKind::S8Z24), 3}; - } - return std::nullopt; -} - -} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index cbcee2a938..d23ec63bae 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -27,9 +27,7 @@ namespace Service::Nvidia::Devices { /// implement the ioctl interface. class nvdevice { public: - enum class Type { Other, GpuChannel, GpuTsg, GpuAddressSpace }; - explicit nvdevice(Core::System& system_, Type type_ = Type::Other) : system{system_}, type{type_} {} - Type GetType() const { return type; } + explicit nvdevice(Core::System& system_) : system{system_} {} virtual ~nvdevice() = default; /** @@ -82,8 +80,6 @@ public: protected: Core::System& system; -private: - const Type type; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 0db83215ca..b8a8979315 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -5,24 +5,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <boost/container/small_vector.hpp> -#include <cstdlib> -#include <fstream> -#include <filesystem> -#include "core/frontend/framebuffer_layout.h" -#include "video_core/renderer_base.h" #include "common/adpf.h" #include "common/assert.h" #include "common/logging.h" #include "core/core.h" -#include "core/hle/kernel/k_process.h" #include "core/core_timing.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/perf_stats.h" #include "video_core/gpu.h" -#include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { @@ -74,9 +67,6 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers output_fences.reserve(sorted_layers.size()); for (auto& layer : sorted_layers) { - const auto handle = nvmap.GetHandle(layer.buffer_handle); - const auto* session = handle ? container.GetSession(handle->session_id) : nullptr; - const bool native_tiling = session && session->process->UsesPAuthReturns(); output_layers.emplace_back(Tegra::FramebufferConfig{ .address = nvmap.GetHandleAddress(layer.buffer_handle), .offset = layer.offset, @@ -87,7 +77,6 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers .transform_flags = layer.transform, .crop_rect = layer.crop_rect, .blending = ConvertBlending(layer.blending), - .native_tiling = native_tiling, .layer_stack_mask = layer.layer_stack_mask, }); @@ -98,56 +87,6 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers } } - const char* capture = std::getenv("EDEN_NATIVE_FRAME_CAPTURE"); - const bool sequence = std::getenv("EDEN_NATIVE_FRAME_SEQUENCE") != nullptr; - const bool on_demand = std::getenv("EDEN_NATIVE_FRAME_ON_DEMAND") != nullptr; - const bool requested = on_demand && !output_layers.empty() && - !diagnostic_frame_pending->load() && std::filesystem::exists("native-frame.request"); - if (requested) std::filesystem::remove("native-frame.request"); - const bool sample_frame = requested || (capture && std::string_view{capture} == "1" && !output_layers.empty() && - diagnostic_frame_count < (sequence ? 36000U : 3840U) && ++diagnostic_frame_count != 0 && - (sequence ? (diagnostic_frame_count == 1 || diagnostic_frame_count % 120 == 0) - : !diagnostic_visible_frame->load())); - if (sample_frame && !diagnostic_frame_pending->exchange(true)) { - for (const auto& fence : output_fences) { - LOG_INFO(Service_NVDRV, "Native composite waits for syncpoint={}/{}, current={}", fence.id, fence.value, - system.Host1x().GetSyncpointManager().GetGuestSyncpointValue(fence.id)); - } - LOG_INFO(Service_NVDRV, "Native composite submitted: layers={}, address={:#x}", output_layers.size(), output_layers.front().address); - const auto layout = Layout::DefaultFrameLayout(1920, 1080); - auto pixels = std::make_shared<std::vector<u8>>(layout.width * layout.height * 4); - const bool first = diagnostic_frame_count == 1; - auto pending = diagnostic_frame_pending; - auto visible = diagnostic_visible_frame; - system.GPU().Renderer().RequestScreenshot(pixels->data(), [pixels, layout, first, pending, visible, sequence, on_demand](bool invert_y) { - std::array<u8, 18> header{}; - header[2] = 2; - header[12] = static_cast<u8>(layout.width & 255); - header[13] = static_cast<u8>(layout.width >> 8); - header[14] = static_cast<u8>(layout.height & 255); - header[15] = static_cast<u8>(layout.height >> 8); - header[16] = 32; - header[17] = invert_y ? 8 : 0x28; - bool nonblack{}; - for (size_t i = 0; i < pixels->size(); i += 4) { - if ((*pixels)[i] || (*pixels)[i + 1] || (*pixels)[i + 2]) { - nonblack = true; - break; - } - } - const auto save = [&](const char* path) { - std::ofstream out(path, std::ios::binary); - out.write(reinterpret_cast<const char*>(header.data()), header.size()); - out.write(reinterpret_cast<const char*>(pixels->data()), pixels->size()); - return out.good(); - }; - if (first) save("native-first-frame.tga"); - const bool saved = (first || nonblack || sequence || on_demand) ? save("native-latest-frame.tga") : true; - if (nonblack && !visible->load() && save("native-visible-frame.tga")) visible->store(true); - LOG_INFO(Service_NVDRV, "Captured composited frame: {}x{}, saved={}, nonblack={}", layout.width, layout.height, saved, nonblack); - pending->store(false); - }, layout); - } system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences)); Common::ADPF::ReportFrameInterval(); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index f795144ebe..ff0bfb2ec9 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -6,7 +6,6 @@ #pragma once -#include <atomic> #include <memory> #include <vector> #include "common/common_types.h" @@ -46,9 +45,6 @@ public: Kernel::KEvent* QueryEvent(u32 event_id) override; private: - u32 diagnostic_frame_count{}; - std::shared_ptr<std::atomic_bool> diagnostic_frame_pending{std::make_shared<std::atomic_bool>(false)}; - std::shared_ptr<std::atomic_bool> diagnostic_visible_frame{std::make_shared<std::atomic_bool>(false)}; NvCore::Container& container; NvCore::NvMap& nvmap; }; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 947ea6088f..3c9e42f602 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -6,7 +6,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include <cstring> -#include <limits> #include <utility> #include "common/alignment.h" @@ -14,13 +13,10 @@ #include "common/common_types.h" #include "common/logging.h" #include "core/core.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/service/nvdrv/devices/gpu_address_space_layout.h" #include "core/hle/service/nvdrv/core/container.h" #include "core/hle/service/nvdrv/core/nvmap.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" -#include "core/hle/service/nvdrv/devices/native_gpu_kind.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "video_core/control/channel_state.h" @@ -31,12 +27,10 @@ namespace Service::Nvidia::Devices { nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core) - : nvdevice{system_, Type::GpuAddressSpace}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, + : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{}, gmmu{} {} -nvhost_as_gpu::~nvhost_as_gpu() { - if (m_owner_process) { m_owner_process->Close(system.Kernel()); } -} +nvhost_as_gpu::~nvhost_as_gpu() = default; NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, std::span<u8> output) { @@ -57,10 +51,6 @@ NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> i return WrapFixed(this, &nvhost_as_gpu::GetVARegions1, input, output); case 0x9: return WrapFixed(this, &nvhost_as_gpu::AllocAsEx, input, output); - case 0xb: - return WrapFixed(this, &nvhost_as_gpu::GetSyncpointShimInfo, input, output); - case 0xc: - return WrapFixed(this, &nvhost_as_gpu::ModifyKind, input, output); case 0x14: return WrapVariable(this, &nvhost_as_gpu::Remap, input, output); default: @@ -100,139 +90,11 @@ NvResult nvhost_as_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> i return NvResult::NotImplemented; } -void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { - m_fd = fd; - if (const auto* session = container.GetSession(session_id)) { - m_owner_process = session->process; - m_owner_process->Open(system.Kernel()); - m_extended_abi = m_owner_process->UsesPAuthReturns(); - } -} +void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvhost_as_gpu::OnClose(DeviceFD fd) {} -NvResult nvhost_as_gpu::CreateNativeSparseRegion(u64 address, u64 size) { - if (!m_extended_abi || !size || !Common::IsAligned(address, VM::YUZU_PAGESIZE) || - !Common::IsAligned(size, VM::YUZU_PAGESIZE) || (size >> VM::PAGE_SIZE_BITS) > 0xffffffffULL) { - return NvResult::BadParameter; - } - { - const std::scoped_lock lock{mutex}; - if (!vm.initialised || address < vm.va_range_start || address >= vm.va_range_split || - size > vm.va_range_split - address) { return NvResult::BadParameter; } - const auto next = allocation_map.lower_bound(address); - if ((next != allocation_map.end() && next->first < address + size) || - (next != allocation_map.begin() && std::prev(next)->first + std::prev(next)->second.size > address)) { - return NvResult::BadParameter; - } - } - IoctlAllocSpace reservation{}; - reservation.pages = static_cast<u32>(size >> VM::PAGE_SIZE_BITS); - reservation.page_size = VM::YUZU_PAGESIZE; - reservation.flags = MappingFlags::Fixed | MappingFlags::Sparse; - reservation.offset = address; - return AllocateSpace(reservation); -} - -NvResult nvhost_as_gpu::DestroyNativeSparseRegion(u64 address, u64 size) { - { - const std::scoped_lock lock{mutex}; - const auto it = allocation_map.find(address); - if (!m_extended_abi || it == allocation_map.end() || !it->second.sparse || it->second.size != size) { - return NvResult::BadParameter; - } - } - IoctlFreeSpace release{}; - release.offset = address; - release.pages = static_cast<u32>(size >> VM::PAGE_SIZE_BITS); - release.page_size = VM::YUZU_PAGESIZE; - return FreeSpace(release); -} - -NvResult nvhost_as_gpu::MapNative4K(u32 handle_id, u64 address, u64 buffer_offset, u64 size, u8 kind) { - if (!m_extended_abi || !size || !Common::IsAligned(address, VM::YUZU_PAGESIZE) || - !Common::IsAligned(size, VM::YUZU_PAGESIZE) || - !Common::IsAligned(buffer_offset, VM::YUZU_PAGESIZE)) { - return NvResult::BadParameter; - } - const auto handle = nvmap.GetHandle(handle_id); - if (!handle || buffer_offset > handle->orig_size || size > handle->orig_size - buffer_offset) { - return NvResult::BadParameter; - } - bool sparse_reservation = false; - { - const std::scoped_lock lock{mutex}; - if (!vm.initialised || address < vm.va_range_start || address >= vm.va_range_split || - size > vm.va_range_split - address) { - return NvResult::BadParameter; - } - const auto upper = allocation_map.upper_bound(address); - if (upper != allocation_map.begin()) { - const auto owner = std::prev(upper); - sparse_reservation = owner->second.sparse && address >= owner->first && - address - owner->first < owner->second.size && size <= owner->second.size - (address - owner->first); - } - const auto next = allocation_map.lower_bound(address); - if (!sparse_reservation && ((next != allocation_map.end() && next->first < address + size) || - (next != allocation_map.begin() && - std::prev(next)->first + std::prev(next)->second.size > address))) { - return NvResult::BadParameter; - } - } - IoctlAllocSpace reservation{}; - reservation.pages = static_cast<u32>(size >> VM::PAGE_SIZE_BITS); - reservation.page_size = VM::YUZU_PAGESIZE; - reservation.flags = MappingFlags::Fixed; - reservation.offset = address; - const auto reserved = sparse_reservation ? NvResult::Success : AllocateSpace(reservation); - if (reserved != NvResult::Success) { return reserved; } - IoctlMapBufferEx mapping{}; - mapping.flags = MappingFlags::Fixed; - mapping.kind = kind; - mapping.handle = handle_id; - mapping.page_size = VM::YUZU_PAGESIZE; - mapping.buffer_offset = static_cast<s64>(buffer_offset); - mapping.mapping_size = size; - mapping.offset = static_cast<s64>(address); - const auto result = MapBufferEx(mapping); - if (result != NvResult::Success && !sparse_reservation) { - nvmap.UnpinHandle(handle_id); - IoctlFreeSpace release{}; - release.offset = address; - release.pages = reservation.pages; - release.page_size = VM::YUZU_PAGESIZE; - FreeSpace(release); - } - return result; -} - -NvResult nvhost_as_gpu::UnmapNative4K(u64 address, u64 size) { - if (!m_extended_abi || !size || !Common::IsAligned(address, VM::YUZU_PAGESIZE) || - !Common::IsAligned(size, VM::YUZU_PAGESIZE)) { - return NvResult::BadParameter; - } - bool sparse_mapping = false; - { - const std::scoped_lock lock{mutex}; - const auto it = mapping_map.find(address); - sparse_mapping = it != mapping_map.end() && it->second.sparse_alloc && it->second.size == size; - } - if (sparse_mapping) { - IoctlUnmapBuffer unmap{}; - unmap.offset = address; - return UnmapBuffer(unmap); - } - IoctlFreeSpace release{}; - release.offset = address; - release.pages = static_cast<u32>(size >> VM::PAGE_SIZE_BITS); - release.page_size = VM::YUZU_PAGESIZE; - return FreeSpace(release); -} - - NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { - LOG_DEBUG(Service_NVDRV, "called, flags={:#x}, as_fd={}, big_page_size={:#x}, reserved={:#x}, VA={:#x}/{:#x}/{:#x}", - params.flags, params.as_fd, params.big_page_size, params.reserved, - params.va_range_start, params.va_range_split, params.va_range_end); + LOG_DEBUG(Service_NVDRV, "called, big_page_size={:#x}", params.big_page_size); std::scoped_lock lock(mutex); @@ -241,43 +103,21 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { return NvResult::InvalidState; } - std::shared_ptr<nvhost_as_gpu> shared_parent; - if (m_extended_abi && params.as_fd != 0) { - if (params.as_fd == m_fd || params.flags != 5 || params.big_page_size != 0x1000 || - params.reserved != 0 || params.va_range_split != 0) { return NvResult::BadParameter; } - const auto device = module.GetDevice<nvdevice>(params.as_fd); - if (!device || device->GetType() != Type::GpuAddressSpace) { return NvResult::BadParameter; } - shared_parent = std::static_pointer_cast<nvhost_as_gpu>(device); - if (!shared_parent->m_extended_abi || !shared_parent->vm.initialised || - shared_parent->m_owner_process != m_owner_process || - params.va_range_start < shared_parent->vm.va_range_start || - params.va_range_start >= params.va_range_end || - params.va_range_end > shared_parent->vm.va_range_split) { return NvResult::BadParameter; } - } - - const u32 big_page_size = GetGpuAddressSpaceBigPageSize(m_extended_abi, params.big_page_size); - if (big_page_size) { - if (!std::has_single_bit(big_page_size)) { - LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: {:#x}!", big_page_size); + if (params.big_page_size) { + if (!std::has_single_bit(params.big_page_size)) { + LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: {:#x}!", params.big_page_size); return NvResult::BadValue; } - if ((big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { - LOG_ERROR(Service_NVDRV, "Unsupported big page size: {:#x}!", big_page_size); + if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) { + LOG_ERROR(Service_NVDRV, "Unsupported big page size: {:#x}!", params.big_page_size); return NvResult::BadValue; } - vm.big_page_size = big_page_size; - vm.big_page_size_bits = static_cast<u32>(std::countr_zero(big_page_size)); - - vm.va_range_start = big_page_size << VM::VA_START_SHIFT; - } + vm.big_page_size = params.big_page_size; + vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size)); - if (m_extended_abi && !params.va_range_start) { - const auto layout = GetGpuAddressSpaceLayout(true); - vm.va_range_start = layout.start; - vm.va_range_split = layout.split; - vm.va_range_end = layout.end; + vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT; } // If this is unspecified then default values should be used @@ -287,12 +127,6 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { vm.va_range_end = params.va_range_end; } - if (shared_parent) { - // Native flags 5 expose the parent's small-page region as a separate - // reservation view. GPU mappings must resolve through the same page table. - vm.va_range_split = params.va_range_end; - } - const u64 max_big_page_bits = Common::Log2Ceil(vm.va_range_end); const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)}; @@ -301,17 +135,11 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) { const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)}; const auto end_big_pages{ - static_cast<u32>(vm.va_range_end >> vm.big_page_size_bits)}; + static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)}; vm.big_page_allocator.emplace(start_big_pages, end_big_pages); - if (shared_parent) { - m_shared_parent = std::move(shared_parent); - gmmu = m_shared_parent->gmmu; - LOG_DEBUG(Service_NVDRV, "native AS view fd={} shares page table with fd={}", m_fd, params.as_fd); - } else { - gmmu = std::make_shared<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS); - system.GPU().InitAddressSpace(*gmmu); - } + gmmu = std::make_unique<Tegra::MemoryManager>(system, max_big_page_bits, vm.va_range_split, vm.big_page_size_bits, VM::PAGE_SIZE_BITS); + system.GPU().InitAddressSpace(*gmmu); vm.initialised = true; return NvResult::Success; @@ -331,7 +159,7 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) { return NvResult::BadValue; } - if (!m_extended_abi && params.page_size != vm.big_page_size && + if (params.page_size != vm.big_page_size && ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) { UNIMPLEMENTED_MSG("Sparse small pages are not implemented!"); return NvResult::NotImplemented; @@ -356,7 +184,7 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) { u64 size{static_cast<u64>(params.pages) * params.page_size}; if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) { - gmmu->MapSparse(params.offset, size, params.page_size != VM::YUZU_PAGESIZE); + gmmu->MapSparse(params.offset, size); } allocation_map[params.offset] = { @@ -389,7 +217,6 @@ bool nvhost_as_gpu::FreeMappingLocked(u64 offset) noexcept { gmmu->Unmap(offset, mapping.size); } mapping_map.erase(offset); - map_buffer_offsets.erase(static_cast<s64>(offset)); return true; } return false; @@ -424,77 +251,6 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) { return NvResult::BadValue; } -NvResult nvhost_as_gpu::GetNativeSyncpointShim(u64& address, u32& stride, u32& count) { - if (!m_extended_abi || !vm.initialised) { return NvResult::NotSupported; } - address = vm.va_range_split - VM::YUZU_PAGESIZE; - if (!m_syncpoint_shim_reserved) { - IoctlAllocSpace allocation{}; - allocation.pages = 1; - allocation.page_size = VM::YUZU_PAGESIZE; - allocation.flags = MappingFlags::Fixed; - allocation.offset = address; - const auto result = AllocateSpace(allocation); - if (result != NvResult::Success) { return result; } - m_syncpoint_shim_reserved = true; - gmmu->SetNativeSyncpointShim(address); - } - stride = sizeof(u32); - count = 192; - return NvResult::Success; -} - -NvResult nvhost_as_gpu::GetSyncpointShimInfo(IoctlSyncpointShimInfo& params) { - u64 address{}; - u32 stride{}, count{}; - const auto result = GetNativeSyncpointShim(address, stride, count); - params.address = address; - params.stride = stride; - params.count = count; - LOG_DEBUG(Service_NVDRV, "native syncpoint shim address={:#x}, stride={}, count={}, result={}", - address, stride, count, static_cast<u32>(result)); - return result; -} - -NvResult nvhost_as_gpu::ModifyNativeKind(u64 address, u64 size, u8 requested, u8 fallback) { - IoctlModifyKind params{}; - params.compressed_kind = requested; - params.uncompressed_kind = fallback; - params.mapping_base = address; - params.size = size; - return ModifyKind(params); -} - -NvResult nvhost_as_gpu::ModifyKind(IoctlModifyKind& params) { - const u32 requested = params.compressed_kind; - const u32 fallback = params.uncompressed_kind; - const u64 base = params.mapping_base; - const u64 offset = params.buffer_offset; - const u64 size = params.size; - LOG_DEBUG(Service_NVDRV, "native modify kind base={:#x}, offset={:#x}, size={:#x}, kinds={:#x}/{:#x}", - base, offset, size, requested, fallback); - if (!m_extended_abi || params.reserved != 0 || fallback > 255 || - (requested > 255 && requested != 0xffff) || !size || - offset > std::numeric_limits<u64>::max() - base || - !Common::IsAligned(base + offset, VM::YUZU_PAGESIZE) || - !Common::IsAligned(size, VM::YUZU_PAGESIZE)) { - return NvResult::BadParameter; - } - const auto kind = SelectNativeMemoryKind(static_cast<u8>(requested == 0xffff ? fallback : requested), - static_cast<u8>(fallback)); - if (!kind) { return NvResult::NotSupported; } - const std::scoped_lock lock{mutex}; - const u64 address = base + offset; - auto it = mapping_map.upper_bound(address); - if (!vm.initialised || it == mapping_map.begin()) { return NvResult::BadParameter; } - --it; - const auto& mapping = it->second; - const u64 delta = address - it->first; - if (delta >= mapping.size || size > mapping.size - delta) { return NvResult::BadParameter; } - gmmu->Map(address, mapping.ptr + delta, size, static_cast<Tegra::PTEKind>(kind->backend), - mapping.big_page); - return NvResult::Success; -} - NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) { LOG_DEBUG(Service_NVDRV, "called, num_entries={:#x}", entries.size()); @@ -658,13 +414,7 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) { NvResult nvhost_as_gpu::BindChannel(IoctlBindChannel& params) { LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); - const auto device = module.GetDevice<nvdevice>(params.fd); - if (!device || device->GetType() != nvdevice::Type::GpuChannel) { return NvResult::BadParameter; } - const auto gpu_channel_device = std::static_pointer_cast<nvhost_gpu>(device); - if (!vm.initialised || !gmmu || (gpu_channel_device->channel_state->initialized && - gpu_channel_device->channel_state->memory_manager != gmmu.get())) { return NvResult::InvalidState; } - gpu_channel_device->channel_state->memory_manager_owner = - std::shared_ptr<Tegra::MemoryManager>(module.GetDevice<nvhost_as_gpu>(m_fd), gmmu.get()); + auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd); gpu_channel_device->channel_state->memory_manager = gmmu.get(); return NvResult::Success; } @@ -674,13 +424,13 @@ void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) { params.regions = std::array<VaRegion, 2>{ VaRegion{ - .offset = GpuPageAddress(vm.small_page_allocator->GetVAStart(), VM::PAGE_SIZE_BITS), + .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS, .page_size = VM::YUZU_PAGESIZE, ._pad0_{}, .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(), }, VaRegion{ - .offset = GpuPageAddress(vm.big_page_allocator->GetVAStart(), vm.big_page_size_bits), + .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits, .page_size = vm.big_page_size, ._pad0_{}, .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(), diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 8c7306e42a..b13b1c9efa 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -64,13 +64,6 @@ public: Kernel::KEvent* QueryEvent(u32 event_id) override; - NvResult CreateNativeSparseRegion(u64 address, u64 size); - NvResult DestroyNativeSparseRegion(u64 address, u64 size); - NvResult MapNative4K(u32 handle, u64 address, u64 buffer_offset, u64 size, u8 kind); - NvResult UnmapNative4K(u64 address, u64 size); - NvResult GetNativeSyncpointShim(u64& address, u32& stride, u32& count); - NvResult ModifyNativeKind(u64 address, u64 size, u8 requested, u8 fallback); - struct VaRegion { u64 offset; u32 page_size; @@ -152,25 +145,6 @@ private: static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2, "IoctlGetVaRegions is incorrect size"); - struct IoctlSyncpointShimInfo { - u64_le address; - u32_le stride; - u32_le count; - }; - static_assert(sizeof(IoctlSyncpointShimInfo) == 16); - NvResult GetSyncpointShimInfo(IoctlSyncpointShimInfo& params); - - struct IoctlModifyKind { - u16_le compressed_kind; - u16_le uncompressed_kind; - u32_le reserved; - u64_le buffer_offset; - u64_le size; - u64_le mapping_base; - }; - static_assert(sizeof(IoctlModifyKind) == 32); - NvResult ModifyKind(IoctlModifyKind& params); - NvResult AllocAsEx(IoctlAllocAsEx& params); NvResult AllocateSpace(IoctlAllocSpace& params); NvResult Remap(std::span<IoctlRemapEntry> params); @@ -222,12 +196,6 @@ private: std::map<u64, Allocation> allocation_map; std::mutex mutex; //!< Locks all AS operations - bool m_extended_abi{}; - bool m_syncpoint_shim_reserved{}; - DeviceFD m_fd{-1}; - Kernel::KProcess* m_owner_process{}; - std::shared_ptr<nvhost_as_gpu> m_shared_parent; - struct VM { static constexpr u32 YUZU_PAGESIZE{0x1000}; static constexpr u32 PAGE_SIZE_BITS{static_cast<u32>(std::countr_zero<u32>(YUZU_PAGESIZE))}; @@ -251,7 +219,7 @@ private: bool initialised{}; } vm; - std::shared_ptr<Tegra::MemoryManager> gmmu; + std::unique_ptr<Tegra::MemoryManager> gmmu; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 6684352b00..14cdd8fda4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -4,7 +4,6 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <algorithm> #include <cstring> #include "common/assert.h" #include "common/logging.h" @@ -12,7 +11,6 @@ #include "core/core_timing.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" -#include "core/hle/service/nvdrv/devices/native_gpu_characteristics.h" #include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { @@ -96,25 +94,6 @@ NvResult nvhost_ctrl_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> case 'G': switch (command.cmd) { case 0x5: - if (command.raw == 0xc0e04705) { - constexpr size_t packet_size = 0x10 + NativeGpuCharacteristicsSize; - if (input.size() < packet_size || output.size() < packet_size || - inline_output.size() < NativeGpuCharacteristicsSize) { - return NvResult::BadParameter; - } - IoctlCharacteristics legacy{}; - GetCharacteristics3(legacy, {}); - const auto native = MakeNativeGpuCharacteristics( - std::span<const u8, 0xa0>{reinterpret_cast<const u8*>(&legacy.gc), 0xa0}); - std::fill(output.begin(), output.end(), u8{}); - std::fill(inline_output.begin(), inline_output.end(), u8{}); - std::memcpy(output.data(), input.data(), 0x10); - const u64_le size{NativeGpuCharacteristicsSize}; - std::memcpy(output.data(), &size, sizeof(size)); - std::memcpy(output.data() + 0x10, native.data(), native.size()); - std::memcpy(inline_output.data(), native.data(), native.size()); - return NvResult::Success; - } return WrapFixedInlOut(this, &nvhost_ctrl_gpu::GetCharacteristics3, input, output, inline_output); case 0x6: diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 6b9678e440..1e571be4c9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -4,8 +4,6 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <cstdlib> -#include <fstream> #include <cstring> #include "common/assert.h" #include "common/logging.h" @@ -16,13 +14,11 @@ #include "core/hle/service/nvdrv/core/syncpoint_manager.h" #include "core/hle/service/nvdrv/devices/ioctl_serialization.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" -#include "core/hle/service/nvdrv/devices/native_gpu_context.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/memory.h" #include "video_core/control/channel_state.h" #include "video_core/engines/puller.h" #include "video_core/gpu.h" -#include "video_core/memory_manager.h" #include "video_core/host1x/host1x.h" namespace Service::Nvidia::Devices { @@ -37,7 +33,7 @@ Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, NvCore::Container& core_) - : nvdevice{system_, Type::GpuChannel}, events_interface{events_interface_}, core{core_}, + : nvdevice{system_}, events_interface{events_interface_}, core{core_}, syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()}, channel_state{system.GPU().AllocateChannel()} { channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false); @@ -132,7 +128,6 @@ NvResult nvhost_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> inpu void nvhost_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) { sessions[fd] = session_id; - channel_state->native_abi = UsesNativeAbi(); } void nvhost_gpu::OnClose(DeviceFD fd) { @@ -285,9 +280,6 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) { s32_le ctx_class_number_index = GetObjectContextClassNumberIndex(static_cast<CtxClasses>(params.class_num)); - if (const auto native_slot = GetNativeGpuContextSlot(params.class_num)) { - if (UsesNativeAbi()) { ctx_class_number_index = *native_slot; } - } if (ctx_class_number_index < 0) { LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#x}", params.class_num); @@ -319,13 +311,13 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildWaitComman } static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementCommandList( - NvFence fence, u32 increment_count = 2) { + NvFence fence) { boost::container::small_vector<Tegra::CommandHeader, 512> result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1, Tegra::SubmissionMode::Increasing), {}}; - for (u32 count = 0; count < increment_count; ++count) { + for (u32 count = 0; count < 2; ++count) { result.push_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1, Tegra::SubmissionMode::Increasing)); result.push_back( @@ -336,12 +328,12 @@ static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementC } static boost::container::small_vector<Tegra::CommandHeader, 512> BuildIncrementWithWfiCommandList( - NvFence fence, u32 increment_count = 2) { + NvFence fence) { boost::container::small_vector<Tegra::CommandHeader, 512> result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1, Tegra::SubmissionMode::Increasing), {}}; - auto increment_list{BuildIncrementCommandList(fence, increment_count)}; + auto increment_list{BuildIncrementCommandList(fence)}; result.insert(result.end(), increment_list.begin(), increment_list.end()); return result; } @@ -359,41 +351,6 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL system.GPU().InitChannel(*channel_state, channel_state->program_id); } - if (UsesNativeAbi() && native_submit_logs++ < 8) { - LOG_DEBUG(Service_NVDRV, "native submit channel={}, initialized={}, entries={}, flags={:#x}, fence={}:{}", - channel_state->bind_id, channel_state->initialized, entries.command_lists.size(), - params.flags.raw, params.fence.id, params.fence.value); - for (size_t i = 0; i < std::min<size_t>(entries.command_lists.size(), 4); ++i) { - const auto& entry = entries.command_lists[i]; - std::array<u32, 16> words{}; - const size_t bytes = std::min<size_t>(entry.size * sizeof(u32), sizeof(words)); - const bool mapped = channel_state->memory_manager && bytes && - channel_state->memory_manager->IsFullyMappedRange(entry.addr, bytes); - if (mapped) { channel_state->memory_manager->ReadBlockUnsafe(entry.addr, words.data(), bytes); } - if (mapped && entry.size > 1000 && std::getenv("EDEN_NATIVE_FRAME_CAPTURE")) { - std::vector<u32> contents(static_cast<size_t>(entry.size)); - channel_state->memory_manager->ReadBlockUnsafe(entry.addr, contents.data(), contents.size() * sizeof(u32)); - std::ofstream dump("native-init-channel-" + std::to_string(channel_state->bind_id) + "-" + std::to_string(i) + ".bin", std::ios::binary); - dump.write(reinterpret_cast<const char*>(contents.data()), contents.size() * sizeof(u32)); - } - LOG_DEBUG(Service_NVDRV, "native pushbuffer entry={} raw={:#x}, address={:#x}, words={}, mapped={}, data={:08x}", - i, entry.raw, static_cast<u64>(entry.addr), static_cast<u64>(entry.size), - mapped, fmt::join(words, ",")); - } - } - - if (UsesNativeAbi() && std::getenv("EDEN_NATIVE_FRAME_CAPTURE") && native_submit_logs < 100) { - for (size_t i = 0; i < entries.command_lists.size(); ++i) { - const auto& entry = entries.command_lists[i]; - const auto bytes = static_cast<size_t>(entry.size) * sizeof(u32); - if (!bytes || bytes > 0x100000 || !channel_state->memory_manager->IsFullyMappedRange(entry.addr, bytes)) continue; - std::vector<u32> contents(static_cast<size_t>(entry.size)); - channel_state->memory_manager->ReadBlockUnsafe(entry.addr, contents.data(), bytes); - std::ofstream dump("native-submit-" + std::to_string(channel_state->bind_id) + "-" + std::to_string(native_submit_logs) + "-" + std::to_string(i) + ".bin", std::ios::binary); - dump.write(reinterpret_cast<const char*>(contents.data()), bytes); - } - } - const auto bind_id = channel_state->bind_id; auto& flags = params.flags; @@ -408,8 +365,6 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL } } - const u32 native_completion = UsesNativeAbi() && flags.increment_value.Value() && !flags.fence_increment.Value() ? params.fence.value : 0; - if (native_completion > 64) return NvResult::BadParameter; params.fence.id = channel_syncpoint; u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) + @@ -427,14 +382,6 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL } } - if (native_completion) { - // The native C56F stream reports its completion count without emitting - // host syncpoint methods. Order actual increments after that GPU work. - gpu.PushGPUEntries(bind_id, Tegra::CommandList{flags.suppress_wfi.Value() - ? BuildIncrementCommandList(params.fence, native_completion) - : BuildIncrementWithWfiCommandList(params.fence, native_completion)}); - LOG_DEBUG(Service_NVDRV, "Native ordered completion: syncpoint={}, increments={}, target={}", channel_syncpoint, native_completion, params.fence.value); - } flags.raw = 0; return NvResult::Success; @@ -516,46 +463,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { return sm_exception_breakpoint_pause_report_event; case 3: return error_notifier_event; - case 7: - // The native SDK requests this event immediately before configuring - // CHANNEL_SET_ERROR_NOTIFIER, then wraps it in its channel error wait. - return UsesNativeAbi() ? error_notifier_event : nullptr; default: LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); return nullptr; } } -NvResult nvhost_gpu::BindToTsg(DeviceFD group, Kernel::KProcess* process, std::shared_ptr<Tegra::NativeMacroContext> macros) { - const std::scoped_lock lock{tsg_mutex}; - if (tsg_group) { return NvResult::Busy; } - bool owned{}; - for (const auto& [fd, session_id] : sessions) { - const auto* session = core.GetSession(session_id); - owned |= session && session->process == process; - } - if (!owned) { return NvResult::AccessDenied; } - if (channel_state->native_abi) { - channel_state->native_macro_context = std::move(macros); - if (channel_state->payload) { - channel_state->payload->maxwell_3d.SetNativeMacroContext(channel_state->native_macro_context); - } - } - tsg_group = group; - return NvResult::Success; -} - -void nvhost_gpu::UnbindFromTsg(DeviceFD group) { - const std::scoped_lock lock{tsg_mutex}; - if (tsg_group == group) { tsg_group.reset(); } -} - -bool nvhost_gpu::UsesNativeAbi() const { - for (const auto& [fd, session_id] : sessions) { - const auto* session = core.GetSession(session_id); - if (session && session->process && session->process->UsesPAuthReturns()) { return true; } - } - return false; -} - } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 8219e4afea..70a6b1bd68 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -7,8 +7,6 @@ #pragma once #include <memory> -#include <mutex> -#include <optional> #include <vector> #include "common/bit_field.h" #include "common/common_funcs.h" @@ -22,10 +20,7 @@ namespace Core::Memory { class Memory; } -namespace Kernel { class KProcess; } - namespace Tegra { -struct NativeMacroContext; namespace Control { struct ChannelState; } @@ -63,12 +58,9 @@ public: void OnClose(DeviceFD fd) override; Kernel::KEvent* QueryEvent(u32 event_id) override; - NvResult BindToTsg(DeviceFD group, Kernel::KProcess* process, std::shared_ptr<Tegra::NativeMacroContext> macros); - void UnbindFromTsg(DeviceFD group); private: friend class nvhost_as_gpu; - bool UsesNativeAbi() const; enum class CtxClasses : u32_le { Ctx2D = 0x902D, Ctx3D = 0xB197, @@ -226,9 +218,6 @@ private: NvCore::NvMap& nvmap; std::shared_ptr<Tegra::Control::ChannelState> channel_state; ::Common::unordered_map<DeviceFD, NvCore::SessionId> sessions; - std::mutex tsg_mutex; - std::optional<DeviceFD> tsg_group; - u32 native_submit_logs{}; u32 channel_syncpoint; std::mutex channel_mutex; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_tsg_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_tsg_gpu.cpp deleted file mode 100644 index ae9d2a50f5..0000000000 --- a/src/core/hle/service/nvdrv/devices/nvhost_tsg_gpu.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#include <algorithm> -#include <array> -#include <cstring> -#include "common/logging.h" -#include "core/core.h" -#include "video_core/macro.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvdrv/devices/nvhost_gpu.h" -#include "core/hle/service/nvdrv/devices/nvhost_tsg_gpu.h" - -namespace Service::Nvidia::Devices { - -nvhost_tsg_gpu::nvhost_tsg_gpu(Core::System& system, Module& module_, NvCore::Container& core_) - : nvdevice{system, Type::GpuTsg}, native_macros{std::make_shared<Tegra::NativeMacroContext>()}, module{module_}, core{core_} { native_macros->engine.native_turing = true; native_macros->compute_engine.native_turing = true; } - -nvhost_tsg_gpu::~nvhost_tsg_gpu() { OnClose(0); } - -void nvhost_tsg_gpu::OnOpen(NvCore::SessionId id, DeviceFD fd) { - group_fd = fd; - if (const auto* session = core.GetSession(id)) { - process = session->process; - process->Open(system.Kernel()); - } -} - -void nvhost_tsg_gpu::OnClose(DeviceFD fd) { - for (const auto& [channel_fd, channel] : channels) { channel->UnbindFromTsg(group_fd); } - channels.clear(); - if (process) { - process->Close(system.Kernel()); - process = nullptr; - } -} - - -NvResult nvhost_tsg_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<u8> output) { - std::array<u32, 8> words{}; - if (!input.empty()) { std::memcpy(words.data(), input.data(), std::min(input.size(), sizeof(words))); } - LOG_DEBUG(Service_NVDRV, "native TSG ioctl={:#x}, input={}, output={}, words={:#x},{:#x},{:#x},{:#x},{:#x},{:#x},{:#x},{:#x}", - command.raw, input.size(), output.size(), words[0], words[1], words[2], words[3], - words[4], words[5], words[6], words[7]); - if (command.raw != 0xc0045401 && command.raw != 0xc0045402 && command.raw != 0xc018540b) { return NvResult::NotImplemented; } - const size_t packet_size = command.raw == 0xc018540b ? 24 : 4; - if (!process || input.size() != packet_size || output.size() != packet_size) { - return NvResult::BadParameter; - } - if (packet_size == 24 && (words[1] >= 64 || words[2] || words[3] || words[4] || words[5])) { - return NvResult::BadParameter; - } - const DeviceFD channel_fd = static_cast<s32>(words[0]); - if (command.cmd == 1 || command.cmd == 11) { - if (channels.contains(channel_fd)) { return NvResult::Busy; } - if (channels.size() >= 64) { return NvResult::InsufficientMemory; } - const auto device = module.GetDevice<nvdevice>(channel_fd); - if (!device || device->GetType() != Type::GpuChannel) { return NvResult::BadParameter; } - const auto channel = std::static_pointer_cast<nvhost_gpu>(device); - const auto result = channel->BindToTsg(fd, process, native_macros); - if (result != NvResult::Success) { return result; } - channels.emplace(channel_fd, channel); - } else { - const auto it = channels.find(channel_fd); - if (it == channels.end()) { return NvResult::BadParameter; } - it->second->UnbindFromTsg(fd); - channels.erase(it); - } - std::copy(input.begin(), input.end(), output.begin()); - return NvResult::Success; -} - -NvResult nvhost_tsg_gpu::Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::span<u8> output) { - return NvResult::NotImplemented; -} - -NvResult nvhost_tsg_gpu::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<u8> output, std::span<u8> inline_output) { - return NvResult::NotImplemented; -} - -} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_tsg_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_tsg_gpu.h deleted file mode 100644 index d64c5df3e0..0000000000 --- a/src/core/hle/service/nvdrv/devices/nvhost_tsg_gpu.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <map> -#include <memory> -#include "core/hle/service/nvdrv/devices/nvdevice.h" - -namespace Tegra { struct NativeMacroContext; } -namespace Kernel { class KProcess; } -namespace Service::Nvidia { class Module; } - -namespace Service::Nvidia::Devices { -class nvhost_gpu; - -class nvhost_tsg_gpu final : public nvdevice { -public: - nvhost_tsg_gpu(Core::System& system, Module& module, NvCore::Container& core); - ~nvhost_tsg_gpu() override; - NvResult Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<u8> output) override; - NvResult Ioctl2(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<const u8> inline_input, std::span<u8> output) override; - NvResult Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> input, - std::span<u8> output, std::span<u8> inline_output) override; - void OnOpen(NvCore::SessionId session, DeviceFD fd) override; - void OnClose(DeviceFD fd) override; -private: - std::shared_ptr<Tegra::NativeMacroContext> native_macros; - Module& module; - NvCore::Container& core; - Kernel::KProcess* process{}; - DeviceFD group_fd{-1}; - std::map<DeviceFD, std::shared_ptr<nvhost_gpu>> channels; -}; - -} // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 9873d07c24..b252a2efa5 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -18,8 +18,6 @@ #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_tsg_gpu.h" -#include "core/hle/kernel/k_process.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" @@ -135,10 +133,6 @@ Module::Module(Core::System& system) auto device = std::make_shared<Devices::nvhost_gpu>(system, events_interface, container); return open_files.emplace(fd, std::move(device)).first; }; - builders["/dev/nvhost-tsg-gpu"] = [this, &system](DeviceFD fd) { - auto device = std::make_shared<Devices::nvhost_tsg_gpu>(system, *this, container); - return open_files.emplace(fd, std::move(device)).first; - }; builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { auto device = std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface); return open_files.emplace(fd, std::move(device)).first; @@ -169,12 +163,7 @@ Module::Module(Core::System& system) }; } -Module::~Module() { - // Devices (including TSG-retained channels) release events and core state. - // Destroy them while those module members are still alive. - for (const auto& [fd, device] : open_files) { device->OnClose(fd); } - open_files.clear(); -} +Module::~Module() {} NvResult Module::VerifyFD(DeviceFD fd) const { if (fd < 0) { @@ -191,12 +180,6 @@ NvResult Module::VerifyFD(DeviceFD fd) const { } DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_id) { - if (device_name == "/dev/nvhost-tsg-gpu") { - const auto* session = container.GetSession(session_id); - if (!session || !session->process || !session->process->UsesPAuthReturns()) { - return INVALID_NVDRV_FD; - } - } auto it = builders.find(device_name); if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 3f81720a39..5fae68adbb 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -72,10 +72,6 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) { const auto input_buffer = ctx.ReadBuffer(0); const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); - if (nv_result != NvResult::Success && ctx.GetThread().GetOwnerProcess()->UsesPAuthReturns()) { - LOG_DEBUG(Service_NVDRV, "native ioctl failure fd={}, ioctl={:#x}, result={}, caller={:#x}", - fd, command.raw, static_cast<u32>(nv_result), ctx.GetThread().GetContext().lr); - } if (command.is_out != 0) { ctx.WriteBuffer(output_buffer); } @@ -103,10 +99,6 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) { const auto nv_result = nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); - if (nv_result != NvResult::Success && ctx.GetThread().GetOwnerProcess()->UsesPAuthReturns()) { - LOG_DEBUG(Service_NVDRV, "native ioctl failure fd={}, ioctl={:#x}, result={}, caller={:#x}", - fd, command.raw, static_cast<u32>(nv_result), ctx.GetThread().GetContext().lr); - } if (command.is_out != 0) { ctx.WriteBuffer(output_buffer); } @@ -134,11 +126,6 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) { const auto nv_result = nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, inline_output_buffer); - if (command.raw == 0xc0e04705) { - LOG_DEBUG(Service_NVDRV, "native characteristics input={}, output={}, inline={}, result={}", - input_buffer.size(), output_buffer.size(), inline_output_buffer.size(), - static_cast<u32>(nv_result)); - } if (command.is_out != 0) { ctx.WriteBuffer(output_buffer, 0); ctx.WriteBuffer(inline_output_buffer, 1); @@ -198,10 +185,6 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto fd = rp.Pop<DeviceFD>(); const auto event_id = rp.Pop<u32>(); - if (ctx.GetThread().GetOwnerProcess()->UsesPAuthReturns()) { - LOG_DEBUG(Service_NVDRV, "native QueryEvent fd={}, id={}, caller={:#x}", fd, event_id, - ctx.GetThread().GetContext().lr); - } if (!is_initialized) { ServiceError(ctx, NvResult::NotInitialized); @@ -282,14 +265,6 @@ NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* RegisterHandlers(functions); } -NVDRV::NVDRV(Core::System& system, std::shared_ptr<Module> module, const char* name, - Kernel::KProcess* process, u64 aruid) - : NVDRV{system, std::move(module), name} { - session_id = nvdrv->GetContainer().OpenSession(process); - pid = aruid; - is_initialized = true; -} - NVDRV::~NVDRV() { if (is_initialized) { auto& container = nvdrv->GetContainer(); diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index d6fe4c18f5..f628fca7df 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h @@ -17,8 +17,6 @@ namespace Service::Nvidia { class NVDRV final : public ServiceFramework<NVDRV> { public: explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name); - explicit NVDRV(Core::System& system, std::shared_ptr<Module> module, const char* name, - Kernel::KProcess* process, u64 aruid); ~NVDRV() override; std::shared_ptr<Module> GetModule() const { diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 9de8abaeb6..c3eb2796e3 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -393,15 +393,6 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out return Status::NoError; } -std::optional<Fence> BufferQueueProducer::GetReleasedBufferFence(s32 slot) { - const std::scoped_lock lock{core->mutex}; - if (slot < 0 || static_cast<size_t>(slot) >= slots.size() || - slots[slot].buffer_state != BufferState::Free) { - return std::nullopt; - } - return slots[slot].fence; -} - Status BufferQueueProducer::AttachBuffer(s32* out_slot, const std::shared_ptr<GraphicBuffer>& buffer) { if (out_slot == nullptr) { diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index bf815eb8d8..697de0ac9b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -12,7 +12,6 @@ #include <condition_variable> #include <memory> #include <mutex> -#include <optional> #include "common/common_funcs.h" #include "common/cpu_features.h" @@ -58,7 +57,6 @@ public: Kernel::KReadableEvent* GetNativeHandle(u32 type_id) override; public: - std::optional<Fence> GetReleasedBufferFence(s32 slot); Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf); Status SetBufferCount(s32 buffer_count); Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width, diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a7e7e10199..b4fe2b3ed5 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -4,16 +4,12 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <chrono> -#include <limits> #include <fmt/ranges.h> #include "common/assert.h" #include "common/logging.h" #include "common/settings.h" #include "core/core.h" #include "core/hle/ipc.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/service.h" @@ -71,26 +67,6 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx, fmt::format_to(std::back_inserter(buf), ", [{}]={:#x}", i, cmd_buf[i]); buf.push_back('}'); - if (ctx.GetThread().GetOwnerProcess()->UsesPAuthReturns()) { - const auto& context = ctx.GetThread().GetContext(); - LOG_DEBUG(Service, "Unsupported IPC caller pc={:#x}, lr={:#x}, sp={:#x}, fp={:#x}", - context.pc, context.lr, context.sp, context.fp); - u64 frame = context.fp; - for (u32 depth = 0; depth < 8 && frame != 0; ++depth) { - if (frame > (std::numeric_limits<u64>::max)() - 15 || - !ctx.GetMemory().IsValidVirtualAddress(frame) || - !ctx.GetMemory().IsValidVirtualAddress(frame + 15)) { - break; - } - const u64 next = ctx.GetMemory().Read64(frame); - LOG_DEBUG(Service, "Unsupported IPC frame {}: return={:#x}", depth, - ctx.GetMemory().Read64(frame + 8)); - if (next <= frame) { - break; - } - frame = next; - } - } system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name, service_name); UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); if (Settings::values.use_auto_stub) { @@ -106,7 +82,7 @@ void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) { if (info == nullptr || info->handler_callback == nullptr) return ReportUnimplementedFunction(ctx, info); - LOG_DEBUG(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); + LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); handler_invoker(this, info->handler_callback, ctx); } @@ -116,7 +92,7 @@ void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) { if (info == nullptr || info->handler_callback == nullptr) return ReportUnimplementedFunction(ctx, info); - LOG_DEBUG(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); + LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer())); handler_invoker(this, info->handler_callback, ctx); } diff --git a/src/core/launch_params.cpp b/src/core/launch_params.cpp index 814a2cb553..a0483226ec 100644 --- a/src/core/launch_params.cpp +++ b/src/core/launch_params.cpp @@ -94,7 +94,7 @@ LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wch break; case 'u': { // Launch game with a specific user - bool argument_ok = isdigit(static_cast<unsigned char>(optarg[0])) != 0; + bool argument_ok = isdigit(optarg[0]); p.selected_user = atoi(optarg); if (!argument_ok) { // try to look it up by username, only finds the first username that matches. diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 1dce245f7f..5e2ec7e0b9 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -13,7 +13,6 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/romfs_factory.h" -#include "core/file_sys/romfs.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/filesystem/filesystem.h" @@ -130,14 +129,10 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( : AppLoader(directory->GetFile("main")) , dir(std::move(directory)) , override_update(override_update_) - , standalone(false) {} FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) { - const auto directory = dir_file->GetContainingDirectory(); - if (dir_file->GetName() == "main" && directory && - AppLoader_NSO::IdentifyType(dir_file) == FileType::NSO && - FileSys::IsDirectoryExeFS(directory)) { + if (FileSys::IsDirectoryExeFS(dir_file->GetContainingDirectory())) { return FileType::DeconstructedRomDirectory; } @@ -187,11 +182,11 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } metadata.Print(); - // Extended ACID programs currently require the Dynarmic compatibility path. + // Enable NCE only for applications with 39-bit address space. const bool is_39bit = metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit; const bool is_application = metadata.GetPoolPartition() == FileSys::PoolPartition::Application; - Settings::SetNceEnabled(is_39bit && !metadata.UsesExtendedAcid()); + Settings::SetNceEnabled(is_39bit); const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", @@ -272,39 +267,11 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } is_loaded = true; - if (standalone) { - title_id = metadata.GetTitleID(); - system.GetFileSystemController().RegisterProcess( - process.GetProcessId(), {}, - std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(), - system.GetFileSystemController())); - } return {ResultStatus::Success, LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}}; } ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& out_dir) { - if (romfs == nullptr && standalone) { - const auto directory = file->GetContainingDirectory(); - for (const auto& entry : directory->GetFiles()) { - if (entry->GetExtension() == "romfs") { - romfs = entry; - break; - } - } - if (romfs == nullptr) { - auto loose = directory->GetSubdirectory("romfs"); - if (!loose && directory->GetName() == "exefs") { - const auto parent = directory->GetParentDirectory(); - if (parent) { - loose = parent->GetSubdirectory("romfs"); - } - } - if (loose) { - romfs = FileSys::CreateRomFS(std::move(loose)); - } - } - } if (romfs == nullptr) { return ResultStatus::ErrorNoRomFS; } diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index aba89935a3..7283db0662 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -62,7 +62,6 @@ private: std::string name; u64 title_id{}; bool override_update; - bool standalone{true}; Modules modules; }; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index d802d15e9f..92e2e2f602 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -125,10 +125,6 @@ bool HasApplicationProgramContent(const std::shared_ptr<FileSys::NSP>& nsp) { } // namespace FileType IdentifyFile(FileSys::VirtualFile file) { - if (const auto romdir_type = - IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { - return *romdir_type; - } if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) { return *nsp_type; } else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) { @@ -143,6 +139,9 @@ FileType IdentifyFile(FileSys::VirtualFile file) { return *kip_type; } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { return *nso_type; + } else if (const auto romdir_type = + IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { + return *romdir_type; } else { return FileType::Unknown; } |
