diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h index 541a07cc7..b55763c74 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/ThreadManager.h @@ -11,6 +11,7 @@ $end_info$ #include "Common/SHMStats.h" #include "LinuxSyscalls/Types.h" +#include "LinuxSyscalls/x32/IoctlEmulation.h" #include #include @@ -125,6 +126,9 @@ struct ThreadStateObject : public FEXCore::Allocator::FEXAllocOperators { FEXCore::Core::CPUState::gdt_segment gdt[32] {}; size_t ldt_entry_count {}; FEXCore::Core::CPUState::gdt_segment* ldt_entries {}; + + // 32-bit FD cache for DRM handlers. + fextl::unique_ptr DRMLRUCache {}; }; class ThreadManager final { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/FD.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/FD.cpp index 610a02438..4e9d1c782 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/FD.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/FD.cpp @@ -137,7 +137,7 @@ auto fcntlHandler = [](FEXCore::Core::CpuStateFrame* Frame, int fd, int cmd, uin break; } break; case F_DUPFD: - case F_DUPFD_CLOEXEC: FEX::HLE::x32::CheckAndAddFDDuplication(fd, Result); break; + case F_DUPFD_CLOEXEC: FEX::HLE::x32::CheckAndAddFDDuplication(Frame, fd, Result); break; case F_GETFL: { Result = FEX::HLE::RemapToX86Flags(Result); break; @@ -509,7 +509,7 @@ void RegisterFD(FEX::HLE::SyscallHandler* Handler) { REGISTER_SYSCALL_IMPL_X32(dup, [](FEXCore::Core::CpuStateFrame* Frame, int oldfd) -> uint64_t { uint64_t Result = ::dup(oldfd); if (Result != -1) { - CheckAndAddFDDuplication(oldfd, Result); + CheckAndAddFDDuplication(Frame, oldfd, Result); } SYSCALL_ERRNO(); }); @@ -517,7 +517,7 @@ void RegisterFD(FEX::HLE::SyscallHandler* Handler) { REGISTER_SYSCALL_IMPL_X32(dup2, [](FEXCore::Core::CpuStateFrame* Frame, int oldfd, int newfd) -> uint64_t { uint64_t Result = ::dup2(oldfd, newfd); if (Result != -1) { - CheckAndAddFDDuplication(oldfd, newfd); + CheckAndAddFDDuplication(Frame, oldfd, newfd); } SYSCALL_ERRNO(); }); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.cpp index 87ce436ba..4b9e7822e 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.cpp @@ -37,14 +37,14 @@ static void UnhandledIoctl(const char* Type, int fd, uint32_t cmd, uint32_t args } namespace BasicHandler { - uint32_t BasicHandler(int fd, uint32_t cmd, uint32_t args) { + uint32_t BasicHandler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { uint64_t Result = ::ioctl(fd, cmd, args); SYSCALL_ERRNO(); } } // namespace BasicHandler namespace V4l2 { - uint32_t V4l2Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t V4l2Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { case _IOC_NR(FEX_VIDIOC_G_FMT): { fex_v4l2_format* format = reinterpret_cast(args); @@ -277,114 +277,35 @@ namespace V4l2 { } // namespace V4l2 namespace DRM { - uint32_t AddAndRunHandler(int fd, uint32_t cmd, uint32_t args); - void AssignDeviceTypeToFD(int fd, const drm_version& Version); + uint32_t AddAndRunHandler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args); + void AssignDeviceTypeToFD(FEXCore::Core::CpuStateFrame* Frame, int fd, const drm_version& Version); - template - class LRUCacheFDCache { - public: - LRUCacheFDCache() { - // Set the last element to our handler - // This element will always be the last one - LRUCache[LRUSize] = LRUObject {-1, AddAndRunHandler}; + DRMLRUCacheFDCache::HandlerType FindHandler(FEXCore::Core::CpuStateFrame* Frame, int32_t FD) { + auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + if (!Thread->DRMLRUCache) { + Thread->DRMLRUCache = fextl::make_unique(); } - - using HandlerType = uint32_t (*)(int fd, uint32_t cmd, uint32_t args); - void SetFDHandler(uint32_t FD, HandlerType Handler) { - FDToHandler[FD] = Handler; - } - - void DuplicateFD(int fd, int NewFD) { - auto it = FDToHandler.find(fd); - if (it != FDToHandler.end()) { - FDToHandler[NewFD] = it->second; - } - } - - HandlerType FindHandler(int32_t FD) { - HandlerType Handler {}; - for (size_t i = 0; i < LRUSize; ++i) { - auto& it = LRUCache[i]; - if (it.FD == FD) { - if (i == 0) { - // If we are the first in the queue then just return it - return it.Handler; - } - Handler = it.Handler; - break; - } - } - - if (Handler) { - AddToFront(FD, Handler); - return Handler; - } - return LRUCache[LRUSize].Handler; - } - - uint32_t AddAndRunMapHandler(int fd, uint32_t cmd, uint32_t args) { - // Couldn't find in cache, check map - { - auto it = FDToHandler.find(fd); - if (it != FDToHandler.end()) { - // Found, add to the cache - AddToFront(fd, it->second); - return it->second(fd, cmd, args); - } - } - - // Wasn't found in map, query it - drm_version Host_Version {}; - Host_Version.name = reinterpret_cast(alloca(128)); - Host_Version.name_len = 128; - uint64_t Result = ioctl(fd, DRM_IOCTL_VERSION, &Host_Version); - - // Add it to the map and double check that it was added - // Next time around when the ioctl is used then it will be added to cache - if (Result != -1) { - AssignDeviceTypeToFD(fd, Host_Version); - } - - auto it = FDToHandler.find(fd); - - if (it == FDToHandler.end()) { - // We don't understand this DRM ioctl - return -EPERM; - } - Result = it->second(fd, cmd, args); - SYSCALL_ERRNO(); - } - - private: - void AddToFront(int32_t FD, HandlerType Handler) { - // Push the element to the front if we found one - // First copy all the other elements back one - // Ensuring the final element isn't written over - memmove(&LRUCache[1], &LRUCache[0], (LRUSize - 1) * sizeof(LRUCache[0])); - // Now set the first element to the one we just found - LRUCache[0] = LRUObject {FD, Handler}; - } - - struct LRUObject { - int32_t FD; - HandlerType Handler; - }; - // With four elements total (3 + 1) then this is a single cacheline in size - LRUObject LRUCache[LRUSize + 1]; - fextl::map FDToHandler; - }; - - static LRUCacheFDCache<3> FDToHandler; - - uint32_t AddAndRunHandler(int fd, uint32_t cmd, uint32_t args) { - return FDToHandler.AddAndRunMapHandler(fd, cmd, args); + return Thread->DRMLRUCache->FindHandler(FD); } - void CheckAndAddFDDuplication(int fd, int NewFD) { - FDToHandler.DuplicateFD(fd, NewFD); + uint32_t AddAndRunHandler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { + auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + if (!Thread->DRMLRUCache) { + Thread->DRMLRUCache = fextl::make_unique(); + } + + return Thread->DRMLRUCache->AddAndRunMapHandler(Frame, fd, cmd, args); } - uint32_t AMDGPU_Handler(int fd, uint32_t cmd, uint32_t args) { + void CheckAndAddFDDuplication(FEXCore::Core::CpuStateFrame* Frame, int fd, int NewFD) { + auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + if (!Thread->DRMLRUCache) { + Thread->DRMLRUCache = fextl::make_unique(); + } + Thread->DRMLRUCache->DuplicateFD(fd, NewFD); + } + + uint32_t AMDGPU_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { case _IOC_NR(FEX_DRM_IOCTL_AMDGPU_GEM_METADATA): { AMDGPU::fex_drm_amdgpu_gem_metadata* val = reinterpret_cast(args); @@ -419,7 +340,7 @@ namespace DRM { return -EPERM; } - uint32_t RADEON_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t RADEON_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { case _IOC_NR(FEX_DRM_IOCTL_RADEON_CP_INIT): { RADEON::fex_drm_radeon_init_t* val = reinterpret_cast(args); @@ -554,7 +475,7 @@ namespace DRM { return -EPERM; } - uint32_t MSM_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t MSM_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { case _IOC_NR(FEX_DRM_IOCTL_MSM_WAIT_FENCE): { MSM::fex_drm_msm_wait_fence* val = reinterpret_cast(args); @@ -590,7 +511,7 @@ namespace DRM { return -EPERM; } - uint32_t Nouveau_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t Nouveau_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { #define _BASIC_META(x) case _IOC_NR(x): #define _BASIC_META_VAR(x, args...) case _IOC_NR(x): @@ -616,7 +537,7 @@ namespace DRM { return -EPERM; } - uint32_t I915_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t I915_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { #define SIMPLE(enum, type) \ case _IOC_NR(FEX_##enum): { \ I915::fex_##type* guest = reinterpret_cast(args); \ @@ -661,7 +582,7 @@ namespace DRM { return -EPERM; } - uint32_t Panfrost_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t Panfrost_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { #define _BASIC_META(x) case _IOC_NR(x): #define _BASIC_META_VAR(x, args...) case _IOC_NR(x): @@ -686,7 +607,7 @@ namespace DRM { return -EPERM; } - uint32_t Lima_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t Lima_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { #define _BASIC_META(x) case _IOC_NR(x): #define _BASIC_META_VAR(x, args...) case _IOC_NR(x): @@ -711,7 +632,7 @@ namespace DRM { return -EPERM; } - uint32_t VC4_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t VC4_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { case _IOC_NR(FEX_DRM_IOCTL_VC4_PERFMON_GET_VALUES): { FEX::HLE::x32::VC4::fex_drm_vc4_perfmon_get_values* val = reinterpret_cast(args); @@ -747,7 +668,7 @@ namespace DRM { return -EPERM; } - uint32_t V3D_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t V3D_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { case _IOC_NR(FEX_DRM_IOCTL_V3D_SUBMIT_CSD): { FEX::HLE::x32::V3D::fex_drm_v3d_submit_csd* val = reinterpret_cast(args); @@ -783,7 +704,7 @@ namespace DRM { return -EPERM; } - uint32_t Virtio_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t Virtio_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { switch (_IOC_NR(cmd)) { #define _BASIC_META(x) case _IOC_NR(x): #define _BASIC_META_VAR(x, args...) case _IOC_NR(x): @@ -808,46 +729,51 @@ namespace DRM { return -EPERM; } - uint32_t Default_Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t Default_Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { // Default handler assumes everything is correct and doesn't need to do any work. uint64_t Result = ::ioctl(fd, cmd, args); SYSCALL_ERRNO(); } - void AssignDeviceTypeToFD(int fd, const drm_version& Version) { + void AssignDeviceTypeToFD(FEXCore::Core::CpuStateFrame* Frame, int fd, const drm_version& Version) { if (Version.name) { + auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame); + if (!Thread->DRMLRUCache) { + Thread->DRMLRUCache = fextl::make_unique(); + } + const std::string_view Name(Version.name, Version.name_len); if (Name == "amdgpu") { - FDToHandler.SetFDHandler(fd, AMDGPU_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, AMDGPU_Handler); } else if (Name == "radeon") { - FDToHandler.SetFDHandler(fd, RADEON_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, RADEON_Handler); } else if (Name == "msm") { - FDToHandler.SetFDHandler(fd, MSM_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, MSM_Handler); } else if (Name == "nouveau") { - FDToHandler.SetFDHandler(fd, Nouveau_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, Nouveau_Handler); } else if (Name == "i915") { - FDToHandler.SetFDHandler(fd, I915_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, I915_Handler); } else if (Name == "panfrost") { - FDToHandler.SetFDHandler(fd, Panfrost_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, Panfrost_Handler); } else if (Name == "lima") { - FDToHandler.SetFDHandler(fd, Lima_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, Lima_Handler); } else if (Name == "vc4") { - FDToHandler.SetFDHandler(fd, VC4_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, VC4_Handler); } else if (Name == "v3d") { - FDToHandler.SetFDHandler(fd, V3D_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, V3D_Handler); } else if (Name == "virtio_gpu") { - FDToHandler.SetFDHandler(fd, Virtio_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, Virtio_Handler); } else { // Known safe drm drivers. if (!(Name == "asahi" || Name == "panthor" || Name == "xe")) { LogMan::Msg::IFmt("Unknown DRM device: '{}'. Using default passthrough", Version.name); } - FDToHandler.SetFDHandler(fd, Default_Handler); + Thread->DRMLRUCache->SetFDHandler(fd, Default_Handler); } } } - uint32_t Handler(int fd, uint32_t cmd, uint32_t args) { + uint32_t Handler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { #define SIMPLE(enum, type) \ case _IOC_NR(FEX_##enum): { \ DRM::fex_##type* guest = reinterpret_cast(args); \ @@ -867,7 +793,7 @@ namespace DRM { uint64_t Result = ::ioctl(fd, DRM_IOCTL_VERSION, &Host_Version); if (Result != -1) { *version = Host_Version; - AssignDeviceTypeToFD(fd, Host_Version); + AssignDeviceTypeToFD(Frame, fd, Host_Version); } SYSCALL_ERRNO(); break; @@ -925,8 +851,8 @@ namespace DRM { case DRM_COMMAND_BASE ...(DRM_COMMAND_END - 1): { // This is the space of the DRM device commands - auto it = FDToHandler.FindHandler(fd); - return it(fd, cmd, args); + auto it = FindHandler(Frame, fd); + return it(Frame, fd, cmd, args); break; } default: @@ -944,13 +870,91 @@ namespace DRM { } } // namespace DRM -using HandlerType = uint32_t (*)(int fd, uint32_t cmd, uint32_t args); +DRMLRUCacheFDCache::DRMLRUCacheFDCache() { + // Set the last element to our handler + // This element will always be the last one + LRUCache[LRUSize] = LRUObject {-1, DRM::AddAndRunHandler}; +} -std::array Handlers = []() consteval { +void DRMLRUCacheFDCache::SetFDHandler(uint32_t FD, HandlerType Handler) { + FDToHandler[FD] = Handler; +} + +void DRMLRUCacheFDCache::DuplicateFD(int fd, int NewFD) { + auto it = FDToHandler.find(fd); + if (it != FDToHandler.end()) { + FDToHandler[NewFD] = it->second; + } +} + +DRMLRUCacheFDCache::HandlerType DRMLRUCacheFDCache::FindHandler(int32_t FD) { + HandlerType Handler {}; + for (size_t i = 0; i < LRUSize; ++i) { + auto& it = LRUCache[i]; + if (it.FD == FD) { + if (i == 0) { + // If we are the first in the queue then just return it + return it.Handler; + } + Handler = it.Handler; + break; + } + } + + if (Handler) { + AddToFront(FD, Handler); + return Handler; + } + return LRUCache[LRUSize].Handler; +} + +void DRMLRUCacheFDCache::AddToFront(int32_t FD, HandlerType Handler) { + // Push the element to the front if we found one + // First copy all the other elements back one + // Ensuring the final element isn't written over + memmove(&LRUCache[1], &LRUCache[0], (LRUSize - 1) * sizeof(LRUCache[0])); + // Now set the first element to the one we just found + LRUCache[0] = LRUObject {FD, Handler}; +} + +uint32_t DRMLRUCacheFDCache::AddAndRunMapHandler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args) { + // Couldn't find in cache, check map + { + auto it = FDToHandler.find(fd); + if (it != FDToHandler.end()) { + // Found, add to the cache + AddToFront(fd, it->second); + return it->second(Frame, fd, cmd, args); + } + } + + // Wasn't found in map, query it + drm_version Host_Version {}; + Host_Version.name = reinterpret_cast(alloca(128)); + Host_Version.name_len = 128; + uint64_t Result = ioctl(fd, DRM_IOCTL_VERSION, &Host_Version); + + // Add it to the map and double check that it was added + // Next time around when the ioctl is used then it will be added to cache + if (Result != -1) { + DRM::AssignDeviceTypeToFD(Frame, fd, Host_Version); + } + + auto it = FDToHandler.find(fd); + + if (it == FDToHandler.end()) { + // We don't understand this DRM ioctl + return -EPERM; + } + Result = it->second(Frame, fd, cmd, args); + SYSCALL_ERRNO(); +} + +std::array Handlers = []() consteval { using namespace DRM; using namespace sockios; using namespace V4l2; - std::array Handlers {}; + std::array Handlers {}; ///< Default fill handlers with BasicHandler. for (auto& Handler : Handlers) { @@ -999,10 +1003,10 @@ std::array Handlers = []() consteval { }(); uint32_t ioctl32(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t request, uint32_t args) { - return Handlers[_IOC_TYPE(request)](fd, request, args); + return Handlers[_IOC_TYPE(request)](Frame, fd, request, args); } -void CheckAndAddFDDuplication(int fd, int NewFD) { - DRM::CheckAndAddFDDuplication(fd, NewFD); +void CheckAndAddFDDuplication(FEXCore::Core::CpuStateFrame* Frame, int fd, int NewFD) { + DRM::CheckAndAddFDDuplication(Frame, fd, NewFD); } } // namespace FEX::HLE::x32 diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.h index f3d058266..ae7ff1134 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/IoctlEmulation.h @@ -1,5 +1,7 @@ // SPDX-License-Identifier: MIT #pragma once +#include + #include namespace FEXCore::Core { @@ -7,6 +9,29 @@ struct CpuStateFrame; } namespace FEX::HLE::x32 { +class DRMLRUCacheFDCache final { +public: + using HandlerType = uint32_t (*)(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args); + DRMLRUCacheFDCache(); + void SetFDHandler(uint32_t FD, HandlerType Handler); + void DuplicateFD(int fd, int NewFD); + HandlerType FindHandler(int32_t FD); + uint32_t AddAndRunMapHandler(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t cmd, uint32_t args); + +protected: + constexpr static size_t LRUSize = 3; + void AddToFront(int32_t FD, HandlerType Handler); + + struct LRUObject { + int32_t FD; + HandlerType Handler; + }; + // With four elements total (3 + 1) then this is a single cacheline in size + LRUObject LRUCache[LRUSize + 1]; + + fextl::map FDToHandler; +}; + uint32_t ioctl32(FEXCore::Core::CpuStateFrame* Frame, int fd, uint32_t request, uint32_t args); -void CheckAndAddFDDuplication(int fd, int NewFD); +void CheckAndAddFDDuplication(FEXCore::Core::CpuStateFrame* Frame, int fd, int NewFD); } // namespace FEX::HLE::x32