diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 1a6007bf..566f8ce1 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -29,7 +29,7 @@ namespace Libraries::GnmDriver { using namespace AmdGpu; -enum GnmEventIdents : u64 { +enum GnmEventType : u64 { Compute0RelMem = 0x00, Compute1RelMem = 0x01, Compute2RelMem = 0x02, @@ -337,6 +337,12 @@ static inline u32* ClearContextState(u32* cmdbuf) { return cmdbuf + ClearStateSequence.size(); } +static inline bool IsValidEventType(Platform::InterruptId id) { + return (static_cast<u32>(id) >= static_cast<u32>(Platform::InterruptId::Compute0RelMem) && + static_cast<u32>(id) <= static_cast<u32>(Platform::InterruptId::Compute6RelMem)) || + static_cast<u32>(id) == static_cast<u32>(Platform::InterruptId::GfxEop); +} + s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { LOG_TRACE(Lib_GnmDriver, "called"); @@ -347,8 +353,7 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { EqueueEvent kernel_event{}; kernel_event.event.ident = id; kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore; - // The library only sets EV_ADD but it is suspected the kernel driver forces EV_CLEAR - kernel_event.event.flags = SceKernelEvent::Flags::Clear; + kernel_event.event.flags = SceKernelEvent::Flags::Add; kernel_event.event.fflags = 0; kernel_event.event.data = id; kernel_event.event.udata = udata; @@ -357,11 +362,15 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) { Platform::IrqC::Instance()->Register( static_cast<Platform::InterruptId>(id), [=](Platform::InterruptId irq) { - ASSERT_MSG(irq == static_cast<Platform::InterruptId>(id), - "An unexpected IRQ occured"); // We need to convert IRQ# to event id and do - // proper filtering in trigger function - eq->TriggerEvent(static_cast<GnmEventIdents>(id), SceKernelEvent::Filter::GraphicsCore, - nullptr); + ASSERT_MSG(irq == static_cast<Platform::InterruptId>(id), "An unexpected IRQ occured"); + + // We need to convert IRQ# to event id + if (!IsValidEventType(irq)) + return; + + // Event data is expected to be an event type as per sceGnmGetEqEventType. + eq->TriggerEvent(static_cast<GnmEventType>(id), SceKernelEvent::Filter::GraphicsCore, + reinterpret_cast<void*>(id)); }, eq); return ORBIS_OK; @@ -476,7 +485,7 @@ s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { return ORBIS_KERNEL_ERROR_EBADF; } - eq->RemoveEvent(id); + eq->RemoveEvent(id, SceKernelEvent::Filter::GraphicsCore); Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), eq); return ORBIS_OK; @@ -1000,9 +1009,13 @@ int PS4_SYSV_ABI sceGnmGetDebugTimestamp() { return ORBIS_OK; } -int PS4_SYSV_ABI sceGnmGetEqEventType() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev) { + LOG_TRACE(Lib_GnmDriver, "called"); + + auto data = sceKernelGetEventData(ev); + ASSERT(static_cast<GnmEventType>(data) == GnmEventType::GfxEop); + + return data; } int PS4_SYSV_ABI sceGnmGetEqTimeStamp() { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 017dbe3a..d1548332 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -85,7 +85,7 @@ int PS4_SYSV_ABI sceGnmGetCoredumpMode(); int PS4_SYSV_ABI sceGnmGetCoredumpProtectionFaultTimestamp(); int PS4_SYSV_ABI sceGnmGetDbgGcHandle(); int PS4_SYSV_ABI sceGnmGetDebugTimestamp(); -int PS4_SYSV_ABI sceGnmGetEqEventType(); +int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev); int PS4_SYSV_ABI sceGnmGetEqTimeStamp(); int PS4_SYSV_ABI sceGnmGetGpuBlockStatus(); u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency(); diff --git a/src/core/libraries/kernel/equeue.cpp b/src/core/libraries/kernel/equeue.cpp index 42a8eed8..3ae77e46 100644 --- a/src/core/libraries/kernel/equeue.cpp +++ b/src/core/libraries/kernel/equeue.cpp @@ -12,6 +12,8 @@ namespace Libraries::Kernel { +// Events are uniquely identified by id and filter. + bool EqueueInternal::AddEvent(EqueueEvent& event) { std::scoped_lock lock{m_mutex}; @@ -27,12 +29,13 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) { return true; } -bool EqueueInternal::RemoveEvent(u64 id) { +bool EqueueInternal::RemoveEvent(u64 id, s16 filter) { bool has_found = false; std::scoped_lock lock{m_mutex}; - const auto& it = - std::ranges::find_if(m_events, [id](auto& ev) { return ev.event.ident == id; }); + const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) { + return ev.event.ident == id && ev.event.filter == filter; + }); if (it != m_events.cend()) { m_events.erase(it); has_found = true; @@ -68,7 +71,7 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { if (ev->flags & SceKernelEvent::Flags::OneShot) { for (auto ev_id = 0u; ev_id < count; ++ev_id) { - RemoveEvent(ev->ident); + RemoveEvent(ev->ident, ev->filter); } } @@ -94,8 +97,11 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) { int count = 0; for (auto& event : m_events) { if (event.IsTriggered()) { + // Event should not trigger again + event.ResetTriggerState(); + if (event.event.flags & SceKernelEvent::Flags::Clear) { - event.Reset(); + event.Clear(); } ev[count++] = event.event; if (count == num) { @@ -334,7 +340,7 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) { return ORBIS_KERNEL_ERROR_EBADF; } - if (!eq->RemoveEvent(id)) { + if (!eq->RemoveEvent(id, SceKernelEvent::Filter::User)) { return ORBIS_KERNEL_ERROR_ENOENT; } return ORBIS_OK; @@ -344,6 +350,10 @@ s16 PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) { return ev->filter; } +u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev) { + return ev->data; +} + void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); @@ -356,6 +366,7 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent); LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId); LIB_FUNCTION("23CPPI1tyBY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventFilter); + LIB_FUNCTION("kwGyyjohI50", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventData); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/equeue.h b/src/core/libraries/kernel/equeue.h index 5a13bdec..f8759137 100644 --- a/src/core/libraries/kernel/equeue.h +++ b/src/core/libraries/kernel/equeue.h @@ -66,8 +66,11 @@ struct EqueueEvent { std::chrono::steady_clock::time_point time_added; std::unique_ptr<boost::asio::steady_timer> timer; - void Reset() { + void ResetTriggerState() { is_triggered = false; + } + + void Clear() { event.fflags = 0; event.data = 0; } @@ -83,7 +86,7 @@ struct EqueueEvent { } bool operator==(const EqueueEvent& ev) const { - return ev.event.ident == event.ident; + return ev.event.ident == event.ident && ev.event.filter == event.filter; } private: @@ -99,7 +102,7 @@ public: } bool AddEvent(EqueueEvent& event); - bool RemoveEvent(u64 id); + bool RemoveEvent(u64 id, s16 filter); int WaitForEvents(SceKernelEvent* ev, int num, u32 micros); bool TriggerEvent(u64 ident, s16 filter, void* trigger_data); int GetTriggeredEvents(SceKernelEvent* ev, int num); @@ -122,6 +125,8 @@ private: using SceKernelUseconds = u32; using SceKernelEqueue = EqueueInternal*; +u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev); + void RegisterEventQueue(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel