diff --git a/CMakeLists.txt b/CMakeLists.txt index e632512de..3dc592586 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -228,8 +228,10 @@ find_package(half 1.12.0 MODULE) find_package(magic_enum 0.9.7 CONFIG) find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) -find_package(SDL3 3.1.2 CONFIG) find_package(SDL3_mixer 2.8.1 CONFIG) +if (SDL3_mixer_FOUND) + find_package(SDL3 3.1.2 CONFIG) +endif() find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) @@ -554,6 +556,8 @@ set(FIBER_LIB src/core/libraries/fiber/fiber_context.s src/core/libraries/fiber/fiber_error.h ) +set_source_files_properties(src/core/libraries/fiber/fiber_context.s PROPERTIES COMPILE_OPTIONS -Wno-unused-command-line-argument) + set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp src/core/libraries/videodec/videodec2_impl.h src/core/libraries/videodec/videodec2.cpp @@ -1141,22 +1145,22 @@ if (APPLE) endif() if (WIN32) - target_link_libraries(shadps4 PRIVATE mincore wepoll) + target_link_libraries(shadps4 PRIVATE mincore wepoll wbemuuid) if (MSVC) # MSVC likes putting opinions on what people can use, disable: - add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS) endif() - add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) + add_compile_definitions(NOMINMAX WIN32_LEAN_AND_MEAN) if (MSVC) # Needed for conflicts with time.h of windows.h - add_definitions(-D_TIMESPEC_DEFINED) + add_compile_definitions(_TIMESPEC_DEFINED) endif() # Target Windows 10 RS5 - add_definitions(-DNTDDI_VERSION=0x0A000006 -D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00) + add_compile_definitions(NTDDI_VERSION=0x0A000006 _WIN32_WINNT=0x0A00 WINVER=0x0A00) if (MSVC) target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib) @@ -1188,7 +1192,7 @@ if (WIN32) target_sources(shadps4 PRIVATE src/shadps4.rc) endif() -add_definitions(-DBOOST_ASIO_STANDALONE) +add_compile_definitions(BOOST_ASIO_STANDALONE) target_include_directories(shadps4 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 5f7ae94c4..b6c7c746e 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -152,7 +152,7 @@ endif() # sirit add_subdirectory(sirit) if (WIN32) - target_compile_options(sirit PUBLIC "-Wno-error=unused-command-line-argument") + target_compile_options(sirit PRIVATE "-Wno-error=unused-command-line-argument") endif() # half diff --git a/src/common/config.cpp b/src/common/config.cpp index 4d3e1d877..b0f068142 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -177,7 +177,7 @@ static ConfigEntry isFullscreen(false); static ConfigEntry fullscreenMode("Windowed"); static ConfigEntry presentMode("Mailbox"); static ConfigEntry isHDRAllowed(false); -static ConfigEntry fsrEnabled(true); +static ConfigEntry fsrEnabled(false); static ConfigEntry rcasEnabled(true); static ConfigEntry rcasAttenuation(250); diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index 047828330..e647059f0 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -36,6 +36,7 @@ bool PSF::Open(const std::filesystem::path& filepath) { } const u64 psfSize = file.GetSize(); + ASSERT_MSG(psfSize != 0, "SFO file at {} is empty!", filepath.string()); std::vector psf(psfSize); file.Seek(0); file.Read(psf); diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 7d35add4e..776792041 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "fiber.h" @@ -6,8 +6,8 @@ #include "common/elf_info.h" #include "common/logging/log.h" #include "core/libraries/fiber/fiber_error.h" +#include "core/libraries/kernel/threads/pthread.h" #include "core/libraries/libs.h" -#include "core/tls.h" namespace Libraries::Fiber { @@ -20,7 +20,7 @@ static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef; static std::atomic context_size_check = false; OrbisFiberContext* GetFiberContext() { - return Core::GetTcbBase()->tcb_fiber; + return Libraries::Kernel::g_curthread->tcb->tcb_fiber; } extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp"); @@ -269,7 +269,7 @@ s32 PS4_SYSV_ABI sceFiberRunImpl(OrbisFiber* fiber, void* addr_context, u64 size return ORBIS_FIBER_ERROR_INVALID; } - Core::Tcb* tcb = Core::GetTcbBase(); + Core::Tcb* tcb = Libraries::Kernel::g_curthread->tcb; if (tcb->tcb_fiber) { return ORBIS_FIBER_ERROR_PERMISSION; } diff --git a/src/core/libraries/kernel/time.cpp b/src/core/libraries/kernel/time.cpp index ad07678b2..51f86e2c7 100644 --- a/src/core/libraries/kernel/time.cpp +++ b/src/core/libraries/kernel/time.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/assert.h" @@ -485,25 +486,41 @@ Common::NativeClock* GetClock() { s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, struct OrbisTimesec* st, u64* dst_sec) { LOG_TRACE(Kernel, "Called"); +#ifdef _WIN32 + TIME_ZONE_INFORMATION tz{}; + DWORD res = GetTimeZoneInformation(&tz); + *local_time = time - tz.Bias; + + if (st != nullptr) { + st->t = time; + st->west_sec = -tz.Bias * 60; + st->dst_sec = res == TIME_ZONE_ID_DAYLIGHT ? -_dstbias : 0; + } + + if (dst_sec != nullptr) { + *dst_sec = res == TIME_ZONE_ID_DAYLIGHT ? -_dstbias : 0; + } +#else #ifdef __APPLE__ // std::chrono::current_zone() not available yet. const auto* time_zone = date::current_zone(); #else const auto* time_zone = std::chrono::current_zone(); -#endif +#endif // __APPLE__ auto info = time_zone->get_info(std::chrono::system_clock::now()); *local_time = info.offset.count() + info.save.count() * 60 + time; if (st != nullptr) { st->t = time; - st->west_sec = info.offset.count() * 60; + st->west_sec = info.offset.count(); st->dst_sec = info.save.count() * 60; } if (dst_sec != nullptr) { *dst_sec = info.save.count() * 60; } +#endif // _WIN32 return ORBIS_OK; } @@ -565,4 +582,4 @@ void RegisterTime(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", sceKernelConvertUtcToLocaltime); } -} // namespace Libraries::Kernel \ No newline at end of file +} // namespace Libraries::Kernel diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 2e77eb11f..c2237ed15 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -368,7 +368,7 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul void* Linker::TlsGetAddr(u64 module_index, u64 offset) { std::scoped_lock lk{mutex}; - DtvEntry* dtv_table = GetTcbBase()->tcb_dtv; + DtvEntry* dtv_table = Libraries::Kernel::g_curthread->tcb->tcb_dtv; if (dtv_table[0].counter != dtv_generation_counter) { // Generation counter changed, a dynamic module was either loaded or unloaded. const u32 old_num_dtvs = dtv_table[1].counter; @@ -381,7 +381,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { delete[] dtv_table; // Update TCB pointer. - GetTcbBase()->tcb_dtv = new_dtv_table; + Libraries::Kernel::g_curthread->tcb->tcb_dtv = new_dtv_table; dtv_table = new_dtv_table; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index b84a4b254..6a1e21dbf 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -596,6 +596,10 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory // On real hardware, GPU file mmaps cause a full system crash due to an internal error. ASSERT_MSG(false, "Files cannot be mapped to GPU memory"); } + if (True(prot & MemoryProt::CpuExec)) { + // On real hardware, execute permissions are silently removed. + prot &= ~MemoryProt::CpuExec; + } // Add virtual memory area auto& new_vma = CarveVMA(mapped_addr, size)->second; @@ -802,10 +806,9 @@ s32 MemoryManager::QueryProtection(VAddr addr, void** start, void** end, u32* pr s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea& vma_base, u64 size, MemoryProt prot) { const auto start_in_vma = addr - vma_base.base; - const auto adjusted_size = - vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size; + const auto adjusted_size = std::min(vma_base.size - start_in_vma, size); - if (vma_base.type == VMAType::Free) { + if (vma_base.type == VMAType::Free || vma_base.type == VMAType::PoolReserved) { // On PS4, protecting freed memory does nothing. return adjusted_size; } @@ -837,8 +840,9 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea& vma_base, u64 siz perms |= Core::MemoryPermission::ReadWrite; } - if (vma_base.type == VMAType::Direct || vma_base.type == VMAType::Pooled) { - // On PS4, execute permissions are hidden from direct memory mappings. + if (vma_base.type == VMAType::Direct || vma_base.type == VMAType::Pooled || + vma_base.type == VMAType::File) { + // On PS4, execute permissions are hidden from direct memory and file mappings. // Tests show that execute permissions still apply, so handle this after reading perms. prot &= ~MemoryProt::CpuExec; } @@ -846,6 +850,12 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea& vma_base, u64 siz // Change protection vma_base.prot = prot; + if (vma_base.type == VMAType::Reserved) { + // On PS4, protections change vma_map, but don't apply. + // Return early to avoid protecting memory that isn't mapped in address space. + return adjusted_size; + } + impl.Protect(addr, size, perms); return adjusted_size; @@ -862,22 +872,20 @@ s32 MemoryManager::Protect(VAddr addr, u64 size, MemoryProt prot) { // Ensure the range to modify is valid ASSERT_MSG(IsValidMapping(addr, size), "Attempted to access invalid address {:#x}", addr); - // Validate protection flags - constexpr static MemoryProt valid_flags = - MemoryProt::NoAccess | MemoryProt::CpuRead | MemoryProt::CpuWrite | MemoryProt::CpuExec | - MemoryProt::GpuRead | MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; - - MemoryProt invalid_flags = prot & ~valid_flags; - if (invalid_flags != MemoryProt::NoAccess) { - LOG_ERROR(Kernel_Vmm, "Invalid protection flags"); - return ORBIS_KERNEL_ERROR_EINVAL; - } + // Appropriately restrict flags. + constexpr static MemoryProt flag_mask = + MemoryProt::CpuReadWrite | MemoryProt::CpuExec | MemoryProt::GpuReadWrite; + MemoryProt valid_flags = prot & flag_mask; // Protect all VMAs between addr and addr + size. s64 protected_bytes = 0; while (protected_bytes < size) { auto it = FindVMA(addr + protected_bytes); auto& vma_base = it->second; + if (vma_base.base > addr + protected_bytes) { + // Account for potential gaps in memory map. + protected_bytes += vma_base.base - (addr + protected_bytes); + } auto result = ProtectBytes(addr + protected_bytes, vma_base, size - protected_bytes, prot); if (result < 0) { // ProtectBytes returned an error, return it @@ -913,13 +921,21 @@ s32 MemoryManager::VirtualQuery(VAddr addr, s32 flags, const auto& vma = it->second; info->start = vma.base; info->end = vma.base + vma.size; - info->offset = vma.type == VMAType::Flexible ? 0 : vma.phys_base; + info->offset = 0; info->protection = static_cast(vma.prot); info->is_flexible = vma.type == VMAType::Flexible ? 1 : 0; info->is_direct = vma.type == VMAType::Direct ? 1 : 0; info->is_stack = vma.type == VMAType::Stack ? 1 : 0; info->is_pooled = vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled ? 1 : 0; info->is_committed = vma.IsMapped() ? 1 : 0; + if (vma.type == VMAType::Direct || vma.type == VMAType::Pooled) { + // Offset is only assigned for direct and pooled mappings. + info->offset = vma.phys_base; + } + if (vma.type == VMAType::Reserved || vma.type == VMAType::PoolReserved) { + // Protection is hidden from reserved mappings. + info->protection = 0; + } strncpy(info->name, vma.name.data(), ::Libraries::Kernel::ORBIS_KERNEL_MAXIMUM_NAME_LENGTH); diff --git a/src/core/tls.cpp b/src/core/tls.cpp index 2f7e1a1fd..bcefd6f25 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -46,10 +46,6 @@ void SetTcbBase(void* image_address) { ASSERT(result != 0); } -Tcb* GetTcbBase() { - return reinterpret_cast(TlsGetValue(GetTcbKey())); -} - #elif defined(__APPLE__) && defined(ARCH_X86_64) // Apple x86_64 @@ -149,12 +145,6 @@ void SetTcbBase(void* image_address) { "Failed to store thread LDT page pointer: {}", errno); } -Tcb* GetTcbBase() { - Tcb* tcb; - asm volatile("mov %%fs:0x0, %0" : "=r"(tcb)); - return tcb; -} - #elif defined(ARCH_X86_64) // Other POSIX x86_64 @@ -164,13 +154,6 @@ void SetTcbBase(void* image_address) { ASSERT_MSG(ret == 0, "Failed to set GS base: errno {}", errno); } -Tcb* GetTcbBase() { - void* tcb = nullptr; - const int ret = syscall(SYS_arch_prctl, ARCH_GET_GS, &tcb); - ASSERT_MSG(ret == 0, "Failed to get GS base: errno {}", errno); - return static_cast(tcb); -} - #else // POSIX non-x86_64 @@ -193,10 +176,6 @@ void SetTcbBase(void* image_address) { ASSERT(pthread_setspecific(GetTcbKey(), image_address) == 0); } -Tcb* GetTcbBase() { - return static_cast(pthread_getspecific(GetTcbKey())); -} - #endif thread_local std::once_flag init_tls_flag; diff --git a/src/core/tls.h b/src/core/tls.h index 787744cd3..0ae512a04 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -1,10 +1,13 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include "common/types.h" +#ifdef _WIN32 +#include +#endif namespace Xbyak { class CodeGenerator; @@ -36,9 +39,6 @@ u32 GetTcbKey(); /// Sets the data pointer to the TCB block. void SetTcbBase(void* image_address); -/// Retrieves Tcb structure for the calling thread. -Tcb* GetTcbBase(); - /// Makes sure TLS is initialized for the thread before entering guest. void EnsureThreadInitialized(); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 604efabbd..83633402c 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -1043,20 +1043,25 @@ void Translator::V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const } void Translator::V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst) { - const IR::U64 src0{GetSrc64(inst.src[0])}; - const IR::U64 src1{GetSrc64(inst.src[1])}; + ASSERT(inst.src[1].field == OperandField::ConstZero); + const IR::U1 src0 = [&] { + switch (inst.src[0].field) { + case OperandField::ScalarGPR: + return ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code)); + case OperandField::VccLo: + return ir.GetVcc(); + default: + UNREACHABLE_MSG("src0 = {}", u32(inst.src[0].field)); + } + }(); const IR::U1 result = [&] { switch (op) { case ConditionOp::EQ: - return ir.IEqual(src0, src1); + return ir.LogicalNot(src0); case ConditionOp::LG: // NE - return ir.INotEqual(src0, src1); + return src0; case ConditionOp::GT: - if (src1.IsImmediate() && src1.U64() == 0) { - ASSERT(inst.src[0].field == OperandField::ScalarGPR); - return ir.GroupAny(ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code))); - } - return ir.IGreaterThan(src0, src1, is_signed); + return ir.GroupAny(ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code))); default: UNREACHABLE_MSG("Unsupported V_CMP_U64 condition operation: {}", u32(op)); }