From 8bae44a90b6023c714f4395c498f2a926c377a9f Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 19 May 2024 23:28:41 +0300 Subject: [PATCH] sceKernelCreateEventFlag , sceKernelWaitEventFlag implementation --- CMakeLists.txt | 1 + .../kernel/event_flag/event_flag.cpp | 81 ++++++++++++++++++- .../libraries/kernel/event_flag/event_flag.h | 11 +-- .../kernel/event_flag/event_flag_codes.h | 14 ++++ .../kernel/event_flag/event_flag_obj.cpp | 68 +++++++++++++++- .../kernel/event_flag/event_flag_obj.h | 25 +++++- 6 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 src/core/libraries/kernel/event_flag/event_flag_codes.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 691fdb5d..82a52bc0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp set(KERNEL_LIB src/core/libraries/kernel/event_flag/event_flag.cpp src/core/libraries/kernel/event_flag/event_flag.h + src/core/libraries/kernel/event_flag/event_flag_codes.h src/core/libraries/kernel/event_flag/event_flag_obj.cpp src/core/libraries/kernel/event_flag/event_flag_obj.h src/core/libraries/kernel/cpu_management.cpp diff --git a/src/core/libraries/kernel/event_flag/event_flag.cpp b/src/core/libraries/kernel/event_flag/event_flag.cpp index 91054e54..7c78dfc3 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.cpp +++ b/src/core/libraries/kernel/event_flag/event_flag.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" @@ -10,7 +11,43 @@ namespace Libraries::Kernel { int PS4_SYSV_ABI sceKernelCreateEventFlag(OrbisKernelEventFlag* ef, const char* pName, u32 attr, u64 initPattern, const OrbisKernelEventFlagOptParam* pOptParam) { - LOG_ERROR(Kernel_Event, "(STUBBED) called"); + LOG_INFO(Kernel_Event, "called name = {} attr = {:#x} initPattern = {:#x}", pName, attr, + initPattern); + if (ef == nullptr || pName == nullptr) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + if (pOptParam || !pName || + attr > (ORBIS_KERNEL_EVF_ATTR_MULTI | ORBIS_KERNEL_EVF_ATTR_TH_PRIO)) { + return SCE_KERNEL_ERROR_EINVAL; + } + + if (strlen(pName) >= 32) { + return ORBIS_KERNEL_ERROR_ENAMETOOLONG; + } + + bool single = true; + bool fifo = true; + + switch (attr) { + case 0x10: + case 0x11: + single = true; + fifo = true; + break; + case 0x20: + case 0x21: + single = false; + fifo = true; + break; + case 0x22: + single = false; + fifo = false; + break; + default: + UNREACHABLE(); + } + + *ef = new EventFlagInternal(std::string(pName), single, fifo, initPattern); return ORBIS_OK; } int PS4_SYSV_ABI sceKernelDeleteEventFlag(OrbisKernelEventFlag ef) { @@ -45,8 +82,46 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, } int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern, u32 waitMode, u64* pResultPat, OrbisKernelUseconds* pTimeout) { - LOG_ERROR(Kernel_Event, "(STUBBED) called"); - return ORBIS_OK; + LOG_INFO(Kernel_Event, "called bitPattern = {:#x} waitMode = {:#x}", bitPattern, waitMode); + if (ef == nullptr) { + return ORBIS_KERNEL_ERROR_ESRCH; + } + + if (bitPattern == 0) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + + int wait = 0; + int clear = 0; + switch (waitMode & 0xf) { + case 0x01: + wait = ORBIS_KERNEL_EVF_WAITMODE_AND; + break; + case 0x02: + wait = ORBIS_KERNEL_EVF_WAITMODE_OR; + break; + default: + UNREACHABLE(); + } + + switch (waitMode & 0xf0) { + case 0x10: + clear = ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL; + break; + case 0x20: + clear = ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT; + break; + default: + clear = 0; // not clear + } + + int result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout); + + if (result != ORBIS_OK) { + LOG_ERROR(Kernel_Event, "returned {}", result); + } + + return result; } void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("PZku4ZrXJqg", "libkernel", 1, "libkernel", 1, 1, sceKernelCancelEventFlag); diff --git a/src/core/libraries/kernel/event_flag/event_flag.h b/src/core/libraries/kernel/event_flag/event_flag.h index 042a940e..2147e3f1 100644 --- a/src/core/libraries/kernel/event_flag/event_flag.h +++ b/src/core/libraries/kernel/event_flag/event_flag.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "event_flag_codes.h" #include "event_flag_obj.h" namespace Core::Loader { @@ -12,16 +13,6 @@ class SymbolsResolver; namespace Libraries::Kernel { -constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01; -constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02; -constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10; -constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20; - -constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10; -constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20; - using OrbisKernelUseconds = u32; using OrbisKernelEventFlag = EventFlagInternal*; diff --git a/src/core/libraries/kernel/event_flag/event_flag_codes.h b/src/core/libraries/kernel/event_flag/event_flag_codes.h new file mode 100644 index 00000000..92b265c8 --- /dev/null +++ b/src/core/libraries/kernel/event_flag/event_flag_codes.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +constexpr int ORBIS_KERNEL_EVF_ATTR_TH_FIFO = 0x01; +constexpr int ORBIS_KERNEL_EVF_ATTR_TH_PRIO = 0x02; +constexpr int ORBIS_KERNEL_EVF_ATTR_SINGLE = 0x10; +constexpr int ORBIS_KERNEL_EVF_ATTR_MULTI = 0x20; + +constexpr int ORBIS_KERNEL_EVF_WAITMODE_AND = 0x01; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_OR = 0x02; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL = 0x10; +constexpr int ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT = 0x20; \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp index 30174c9a..3d66de4f 100644 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.cpp +++ b/src/core/libraries/kernel/event_flag/event_flag_obj.cpp @@ -1,6 +1,72 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "event_flag_obj.h" -namespace Libraries::Kernel {} \ No newline at end of file +namespace Libraries::Kernel { +int EventFlagInternal::Wait(u64 bits, int wait_mode, int clear_mode, u64* result, u32* ptr_micros) { + std::unique_lock lock{m_mutex}; + + uint32_t micros = 0; + bool infinitely = true; + if (ptr_micros != nullptr) { + micros = *ptr_micros; + infinitely = false; + } + + if (m_single_thread && m_waiting_threads > 0) { + return ORBIS_KERNEL_ERROR_EPERM; + } + + auto const start = std::chrono::system_clock::now(); + m_waiting_threads++; + auto waitFunc = [this, wait_mode, bits] { + return (m_status == Status::Canceled || m_status == Status::Deleted || + (wait_mode == ORBIS_KERNEL_EVF_WAITMODE_AND && (m_bits & bits) == bits) || + (wait_mode == ORBIS_KERNEL_EVF_WAITMODE_OR && (m_bits & bits) != 0)); + }; + + if (infinitely) { + m_cond_var.wait(lock, waitFunc); + } else { + if (!m_cond_var.wait_for(lock, std::chrono::microseconds(micros), waitFunc)) { + if (result != nullptr) { + *result = m_bits; + } + *ptr_micros = 0; + --m_waiting_threads; + return ORBIS_KERNEL_ERROR_ETIMEDOUT; + } + } + --m_waiting_threads; + if (result != nullptr) { + *result = m_bits; + } + + auto elapsed = std::chrono::duration_cast( + std::chrono::system_clock::now() - start) + .count(); + if (result != nullptr) { + *result = m_bits; + } + + if (ptr_micros != nullptr) { + *ptr_micros = (elapsed >= micros ? 0 : micros - elapsed); + } + + if (m_status == Status::Canceled) { + return ORBIS_KERNEL_ERROR_ECANCELED; + } else if (m_status == Status::Deleted) { + return ORBIS_KERNEL_ERROR_EACCES; + } + + if (clear_mode == ORBIS_KERNEL_EVF_WAITMODE_CLEAR_ALL) { + m_bits = 0; + } else if (clear_mode == ORBIS_KERNEL_EVF_WAITMODE_CLEAR_PAT) { + m_bits &= ~bits; + } + + return ORBIS_OK; +} +} // namespace Libraries::Kernel \ No newline at end of file diff --git a/src/core/libraries/kernel/event_flag/event_flag_obj.h b/src/core/libraries/kernel/event_flag/event_flag_obj.h index aa18fc7e..4c23f9ef 100644 --- a/src/core/libraries/kernel/event_flag/event_flag_obj.h +++ b/src/core/libraries/kernel/event_flag/event_flag_obj.h @@ -2,7 +2,30 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include +#include +#include "common/types.h" +#include "event_flag_codes.h" namespace Libraries::Kernel { -class EventFlagInternal {}; + +class EventFlagInternal { +public: + EventFlagInternal(const std::string& name, bool single, bool fifo, uint64_t bits) + : m_name(name), m_single_thread(single), m_fifo(fifo), m_bits(bits){}; + + int Wait(u64 bits, int wait_mode, int clear_mode, u64* result, u32* ptr_micros); + +private: + enum class Status { Set, Canceled, Deleted }; + + std::mutex m_mutex; + std::condition_variable m_cond_var; + Status m_status = Status::Set; + int m_waiting_threads = 0; + std::string m_name; + bool m_single_thread = false; + bool m_fifo = false; + u64 m_bits = 0; +}; } // namespace Libraries::Kernel \ No newline at end of file