Merge branch 'main' into fontlib

This commit is contained in:
georgemoralis 2025-05-16 13:46:21 +03:00 committed by GitHub
commit 9569c29644
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 727 additions and 327 deletions

4
.gitmodules vendored
View file

@ -30,10 +30,6 @@
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
shallow = true
[submodule "externals/winpthreads"]
path = externals/winpthreads
url = https://github.com/shadps4-emu/winpthreads.git
shallow = true
[submodule "externals/magic_enum"]
path = externals/magic_enum
url = https://github.com/Neargye/magic_enum.git

View file

@ -54,9 +54,9 @@ else()
endif()
if (ARCHITECTURE STREQUAL "x86_64")
# Target the same CPU architecture as the PS4, to maintain the same level of compatibility.
# Exclude SSE4a as it is only available on AMD CPUs.
add_compile_options(-march=btver2 -mtune=generic -mno-sse4a)
# Target x86-64-v3 CPU architecture as this is a good balance between supporting performance critical
# instructions like AVX2 and maintaining support for older CPUs.
add_compile_options(-march=x86-64-v3)
endif()
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
@ -239,13 +239,6 @@ if (APPLE)
endif()
list(POP_BACK CMAKE_MODULE_PATH)
# Note: Windows always has these functions through winpthreads
include(CheckSymbolExists)
check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMEDLOCK)
if(HAVE_PTHREAD_MUTEX_TIMEDLOCK OR WIN32)
add_compile_options(-DHAVE_PTHREAD_MUTEX_TIMEDLOCK)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
# libc++ requires -fexperimental-library to enable std::jthread and std::stop_token support.
include(CheckCXXSymbolExists)
@ -1162,7 +1155,7 @@ if (ENABLE_QT_GUI)
endif()
if (WIN32)
target_link_libraries(shadps4 PRIVATE mincore winpthreads)
target_link_libraries(shadps4 PRIVATE mincore)
if (MSVC)
# MSVC likes putting opinions on what people can use, disable:

View file

@ -21,9 +21,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
- A processor with at least 4 cores and 6 threads
- Above 2.5 GHz frequency
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
- A CPU supporting the x86-64-v3 baseline.
- **Intel**: Haswell generation or newer
- **AMD**: Jaguar generation or newer
- **AMD**: Excavator generation or newer
- **Apple**: Rosetta 2 on macOS 15.4 or newer
### GPU

View file

@ -137,12 +137,6 @@ if (NOT TARGET Zydis::Zydis)
add_subdirectory(zydis)
endif()
# Winpthreads
if (WIN32)
add_subdirectory(winpthreads)
target_include_directories(winpthreads INTERFACE winpthreads/include)
endif()
# sirit
add_subdirectory(sirit)
if (WIN32)

@ -1 +0,0 @@
Subproject commit f35b0948d36a736e6a2d052ae295a3ffde09703f

View file

@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <ctime>
#include <string>
#include <thread>
@ -104,14 +105,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
SetThreadPriority(handle, windows_priority);
}
static void AccurateSleep(std::chrono::nanoseconds duration) {
bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
const bool interruptible) {
const auto begin_sleep = std::chrono::high_resolution_clock::now();
LARGE_INTEGER interval{
.QuadPart = -1 * (duration.count() / 100u),
};
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
const auto ret = WaitForSingleObjectEx(timer, INFINITE, interruptible);
::CloseHandle(timer);
if (remaining) {
const auto end_sleep = std::chrono::high_resolution_clock::now();
const auto sleep_time = end_sleep - begin_sleep;
*remaining = duration > sleep_time ? duration - sleep_time : std::chrono::nanoseconds(0);
}
return ret == WAIT_OBJECT_0;
}
#else
@ -134,8 +145,24 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
pthread_setschedparam(this_thread, scheduling_type, &params);
}
static void AccurateSleep(std::chrono::nanoseconds duration) {
std::this_thread::sleep_for(duration);
bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
const bool interruptible) {
timespec request = {
.tv_sec = duration.count() / 1'000'000'000,
.tv_nsec = duration.count() % 1'000'000'000,
};
timespec remain;
int ret;
while ((ret = nanosleep(&request, &remain)) < 0 && errno == EINTR) {
if (interruptible) {
break;
}
request = remain;
}
if (remaining) {
*remaining = std::chrono::nanoseconds(remain.tv_sec * 1'000'000'000 + remain.tv_nsec);
}
return ret == 0 || errno != EINTR;
}
#endif
@ -196,9 +223,9 @@ AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
: target_interval(target_interval) {}
void AccurateTimer::Start() {
auto begin_sleep = std::chrono::high_resolution_clock::now();
const auto begin_sleep = std::chrono::high_resolution_clock::now();
if (total_wait.count() > 0) {
AccurateSleep(total_wait);
AccurateSleep(total_wait, nullptr, false);
}
start_time = std::chrono::high_resolution_clock::now();
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);

View file

@ -25,6 +25,9 @@ void SetCurrentThreadName(const char* name);
void SetThreadName(void* thread, const char* name);
bool AccurateSleep(std::chrono::nanoseconds duration, std::chrono::nanoseconds* remaining,
bool interruptible);
class AccurateTimer {
std::chrono::nanoseconds target_interval{};
std::chrono::nanoseconds total_wait{};

View file

@ -179,7 +179,7 @@ s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t add
auto* wait_reg_mem = reinterpret_cast<PM4CmdWaitRegMem*>(cmdbuf);
wait_reg_mem->header = PM4Type3Header{PM4ItOpcode::WaitRegMem, 5};
wait_reg_mem->raw = (is_mem << 4u) | (cmp_func & 7u);
wait_reg_mem->poll_addr_lo = u32(addr & addr_mask);
wait_reg_mem->poll_addr_lo_raw = u32(addr & addr_mask);
wait_reg_mem->poll_addr_hi = u32(addr >> 32u);
wait_reg_mem->ref = ref;
wait_reg_mem->mask = mask;

View file

@ -12,12 +12,25 @@
namespace Libraries::Kernel {
extern boost::asio::io_context io_context;
extern void KernelSignalRequest();
static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
// Events are uniquely identified by id and filter.
bool EqueueInternal::AddEvent(EqueueEvent& event) {
std::scoped_lock lock{m_mutex};
event.time_added = std::chrono::steady_clock::now();
if (event.event.filter == SceKernelEvent::Filter::Timer ||
event.event.filter == SceKernelEvent::Filter::HrTimer) {
// HrTimer events are offset by the threshold of time at the end that we spinlock for
// greater accuracy.
const auto offset =
event.event.filter == SceKernelEvent::Filter::HrTimer ? HrTimerSpinlockThresholdUs : 0u;
event.timer_interval = std::chrono::microseconds(event.event.data - offset);
}
const auto& it = std::ranges::find(m_events, event);
if (it != m_events.cend()) {
@ -29,6 +42,47 @@ bool EqueueInternal::AddEvent(EqueueEvent& event) {
return true;
}
bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
void (*callback)(SceKernelEqueue, const SceKernelEvent&)) {
std::scoped_lock lock{m_mutex};
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()) {
return false;
}
const auto& event = *it;
ASSERT(event.event.filter == SceKernelEvent::Filter::Timer ||
event.event.filter == SceKernelEvent::Filter::HrTimer);
if (!it->timer) {
it->timer = std::make_unique<boost::asio::steady_timer>(io_context, event.timer_interval);
} else {
// If the timer already exists we are scheduling a reoccurrence after the next period.
// Set the expiration time to the previous occurrence plus the period.
it->timer->expires_at(it->timer->expiry() + event.timer_interval);
}
it->timer->async_wait(
[this, event_data = event.event, callback](const boost::system::error_code& ec) {
if (ec) {
if (ec != boost::system::errc::operation_canceled) {
LOG_ERROR(Kernel_Event, "Timer callback error: {}", ec.message());
} else {
// Timer was cancelled (removed) before it triggered
LOG_DEBUG(Kernel_Event, "Timer cancelled");
}
return;
}
callback(this, event_data);
});
KernelSignalRequest();
return true;
}
bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
bool has_found = false;
std::scoped_lock lock{m_mutex};
@ -152,18 +206,14 @@ int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
return count;
}
extern boost::asio::io_context io_context;
extern void KernelSignalRequest();
bool EqueueInternal::EventExists(u64 id, s16 filter) {
std::scoped_lock lock{m_mutex};
static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
return ev.event.ident == id && ev.event.filter == filter;
});
static void SmallTimerCallback(const boost::system::error_code& error, SceKernelEqueue eq,
SceKernelEvent kevent) {
static EqueueEvent event;
event.event = kevent;
event.event.data = HrTimerSpinlockThresholdUs;
eq->AddSmallTimer(event);
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
return it != m_events.cend();
}
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
@ -243,6 +293,14 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
return ORBIS_OK;
}
static void HrTimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
static EqueueEvent event;
event.event = kevent;
event.event.data = HrTimerSpinlockThresholdUs;
eq->AddSmallTimer(event);
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
}
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
@ -273,17 +331,10 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
}
event.timer = std::make_unique<boost::asio::steady_timer>(
io_context, std::chrono::microseconds(total_us - HrTimerSpinlockThresholdUs));
event.timer->async_wait(std::bind(SmallTimerCallback, std::placeholders::_1, eq, event.event));
if (!eq->AddEvent(event)) {
if (!eq->AddEvent(event) ||
!eq->ScheduleEvent(id, SceKernelEvent::Filter::HrTimer, HrTimerCallback)) {
return ORBIS_KERNEL_ERROR_ENOMEM;
}
KernelSignalRequest();
return ORBIS_OK;
}
@ -300,6 +351,57 @@ int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) {
}
}
static void TimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
if (eq->EventExists(kevent.ident, kevent.filter)) {
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::Timer, kevent.udata);
if (!(kevent.flags & SceKernelEvent::Flags::OneShot)) {
// Reschedule the event for its next period.
eq->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback);
}
}
}
int PS4_SYSV_ABI sceKernelAddTimerEvent(SceKernelEqueue eq, int id, SceKernelUseconds usec,
void* udata) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
EqueueEvent event{};
event.event.ident = static_cast<u64>(id);
event.event.filter = SceKernelEvent::Filter::Timer;
event.event.flags = SceKernelEvent::Flags::Add;
event.event.fflags = 0;
event.event.data = usec;
event.event.udata = udata;
if (eq->EventExists(event.event.ident, event.event.filter)) {
eq->RemoveEvent(id, SceKernelEvent::Filter::Timer);
LOG_DEBUG(Kernel_Event,
"Timer event already exists, removing it: queue name={}, queue id={}",
eq->GetName(), event.event.ident);
}
LOG_DEBUG(Kernel_Event, "Added timing event: queue name={}, queue id={}, usec={}, pointer={:x}",
eq->GetName(), event.event.ident, usec, reinterpret_cast<uintptr_t>(udata));
if (!eq->AddEvent(event) ||
!eq->ScheduleEvent(id, SceKernelEvent::Filter::Timer, TimerCallback)) {
return ORBIS_KERNEL_ERROR_ENOMEM;
}
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelDeleteTimerEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
return eq->RemoveEvent(id, SceKernelEvent::Filter::Timer) ? ORBIS_OK
: ORBIS_KERNEL_ERROR_ENOENT;
}
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
@ -380,6 +482,8 @@ void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
LIB_FUNCTION("R74tt43xP6k", "libkernel", 1, "libkernel", 1, 1, sceKernelAddHRTimerEvent);
LIB_FUNCTION("J+LF6LwObXU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteHRTimerEvent);
LIB_FUNCTION("57ZK+ODEXWY", "libkernel", 1, "libkernel", 1, 1, sceKernelAddTimerEvent);
LIB_FUNCTION("YWQFUyXIVdU", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteTimerEvent);
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
LIB_FUNCTION("mJ7aghmgvfc", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventId);

View file

@ -21,6 +21,9 @@ namespace Libraries::Kernel {
class EqueueInternal;
struct EqueueEvent;
using SceKernelUseconds = u32;
using SceKernelEqueue = EqueueInternal*;
struct SceKernelEvent {
enum Filter : s16 {
None = 0,
@ -77,6 +80,7 @@ struct EqueueEvent {
SceKernelEvent event;
void* data = nullptr;
std::chrono::steady_clock::time_point time_added;
std::chrono::microseconds timer_interval;
std::unique_ptr<boost::asio::steady_timer> timer;
void ResetTriggerState() {
@ -133,6 +137,8 @@ public:
}
bool AddEvent(EqueueEvent& event);
bool ScheduleEvent(u64 id, s16 filter,
void (*callback)(SceKernelEqueue, const SceKernelEvent&));
bool RemoveEvent(u64 id, s16 filter);
int WaitForEvents(SceKernelEvent* ev, int num, u32 micros);
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
@ -152,6 +158,8 @@ public:
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
bool EventExists(u64 id, s16 filter);
private:
std::string m_name;
std::mutex m_mutex;
@ -160,9 +168,6 @@ private:
std::condition_variable m_cond;
};
using SceKernelUseconds = u32;
using SceKernelEqueue = EqueueInternal*;
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);

View file

@ -108,6 +108,9 @@ void SetPosixErrno(int e) {
case EACCES:
g_posix_errno = POSIX_EACCES;
break;
case EFAULT:
g_posix_errno = POSIX_EFAULT;
break;
case EINVAL:
g_posix_errno = POSIX_EINVAL;
break;

View file

@ -290,6 +290,12 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
directMemoryEndOut);
}
int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end) {
LOG_DEBUG(Kernel_Vmm, "called, addr = {}", fmt::ptr(addr));
auto* memory = Core::Memory::Instance();
return memory->IsStack(std::bit_cast<VAddr>(addr), start, end);
}
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut) {
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
@ -636,6 +642,7 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("7oxv3PPCumo", "libkernel", 1, "libkernel", 1, 1, sceKernelReserveVirtualRange);
LIB_FUNCTION("BC+OG5m9+bw", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemoryType);
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize);
LIB_FUNCTION("yDBwVAolDgg", "libkernel", 1, "libkernel", 1, 1, sceKernelIsStack);
LIB_FUNCTION("NcaWUxfMNIQ", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedDirectMemory);
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory);
LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection);

View file

@ -158,6 +158,7 @@ void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func[]);
int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut,
void** directMemoryStartOut,
void** directMemoryEndOut);
int PS4_SYSV_ABI sceKernelIsStack(void* addr, void** start, void** end);
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut);

View file

@ -315,7 +315,7 @@ int PS4_SYSV_ABI sceKernelPollEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
auto result = ef->Poll(bitPattern, wait, clear, pResultPat);
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_EBUSY) {
LOG_ERROR(Kernel_Event, "returned {}", result);
LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
}
return result;
@ -361,7 +361,7 @@ int PS4_SYSV_ABI sceKernelWaitEventFlag(OrbisKernelEventFlag ef, u64 bitPattern,
u32 result = ef->Wait(bitPattern, wait, clear, pResultPat, pTimeout);
if (result != ORBIS_OK && result != ORBIS_KERNEL_ERROR_ETIMEDOUT) {
LOG_ERROR(Kernel_Event, "returned {:#x}", result);
LOG_DEBUG(Kernel_Event, "returned {:#x}", result);
}
return result;

View file

@ -5,24 +5,23 @@
#include "common/assert.h"
#include "common/native_clock.h"
#include "common/thread.h"
#include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/posix_error.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/libs.h"
#ifdef _WIN64
#include <pthread_time.h>
#include <windows.h>
#include "common/ntapi.h"
#else
#if __APPLE__
#include <date/tz.h>
#endif
#include <ctime>
#include <sys/resource.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#endif
@ -52,88 +51,116 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() {
return clock->GetUptime();
}
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
#ifdef _WIN64
const auto start_time = std::chrono::high_resolution_clock::now();
auto total_wait_time = std::chrono::microseconds(microseconds);
while (total_wait_time.count() > 0) {
auto wait_time = std::chrono::ceil<std::chrono::milliseconds>(total_wait_time).count();
u64 res = SleepEx(static_cast<u64>(wait_time), true);
if (res == WAIT_IO_COMPLETION) {
auto elapsedTime = std::chrono::high_resolution_clock::now() - start_time;
auto elapsedMicroseconds =
std::chrono::duration_cast<std::chrono::microseconds>(elapsedTime).count();
total_wait_time = std::chrono::microseconds(microseconds - elapsedMicroseconds);
} else {
break;
static s32 posix_nanosleep_impl(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp,
const bool interruptible) {
if (!rqtp || rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1'000'000'000) {
SetPosixErrno(EINVAL);
return -1;
}
const auto duration = std::chrono::nanoseconds(rqtp->tv_sec * 1'000'000'000 + rqtp->tv_nsec);
std::chrono::nanoseconds remain;
const auto uninterrupted = Common::AccurateSleep(duration, &remain, interruptible);
if (rmtp) {
rmtp->tv_sec = remain.count() / 1'000'000'000;
rmtp->tv_nsec = remain.count() % 1'000'000'000;
}
if (!uninterrupted) {
SetPosixErrno(EINTR);
return -1;
}
return 0;
#else
timespec start;
timespec remain;
start.tv_sec = microseconds / 1000000;
start.tv_nsec = (microseconds % 1000000) * 1000;
timespec* requested = &start;
int ret = 0;
do {
ret = nanosleep(requested, &remain);
requested = &remain;
} while (ret != 0);
return ret;
#endif
}
int PS4_SYSV_ABI posix_usleep(u32 microseconds) {
return sceKernelUsleep(microseconds);
}
u32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
std::this_thread::sleep_for(std::chrono::seconds(seconds));
return 0;
}
#ifdef _WIN64
#ifndef CLOCK_REALTIME
#define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_PROCESS_CPUTIME_ID
#define CLOCK_PROCESS_CPUTIME_ID 2
#endif
#ifndef CLOCK_THREAD_CPUTIME_ID
#define CLOCK_THREAD_CPUTIME_ID 3
#endif
#ifndef CLOCK_REALTIME_COARSE
#define CLOCK_REALTIME_COARSE 5
#endif
#ifndef CLOCK_MONOTONIC_COARSE
#define CLOCK_MONOTONIC_COARSE 6
#endif
#define DELTA_EPOCH_IN_100NS 116444736000000000ULL
static u64 FileTimeTo100Ns(FILETIME& ft) {
return *reinterpret_cast<u64*>(&ft);
s32 PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
return posix_nanosleep_impl(rqtp, rmtp, true);
}
static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
s32 PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
if (const auto ret = posix_nanosleep_impl(rqtp, rmtp, false); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI posix_usleep(u32 microseconds) {
const OrbisKernelTimespec ts = {
.tv_sec = microseconds / 1'000'000,
.tv_nsec = (microseconds % 1'000'000) * 1'000,
};
return posix_nanosleep(&ts, nullptr);
}
s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds) {
const OrbisKernelTimespec ts = {
.tv_sec = microseconds / 1'000'000,
.tv_nsec = (microseconds % 1'000'000) * 1'000,
};
return sceKernelNanosleep(&ts, nullptr);
}
u32 PS4_SYSV_ABI posix_sleep(u32 seconds) {
const OrbisKernelTimespec ts = {
.tv_sec = seconds,
.tv_nsec = 0,
};
OrbisKernelTimespec rm;
if (const auto ret = posix_nanosleep(&ts, &rm); ret < 0) {
return *__Error() == POSIX_EINTR ? rm.tv_sec + (rm.tv_nsec == 0 ? 0 : 1) : seconds;
}
return 0;
}
s32 PS4_SYSV_ABI sceKernelSleep(u32 seconds) {
return sceKernelUsleep(seconds * 1'000'000);
}
s32 PS4_SYSV_ABI posix_clock_gettime(u32 clock_id, OrbisKernelTimespec* ts) {
if (ts == nullptr) {
SetPosixErrno(EFAULT);
return -1;
}
if (clock_id == ORBIS_CLOCK_PROCTIME) {
const auto us = sceKernelGetProcessTime();
ts->tv_sec = static_cast<s64>(us / 1'000'000);
ts->tv_nsec = static_cast<s64>((us % 1'000'000) * 1000);
return 0;
}
if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
clock_id = ORBIS_CLOCK_MONOTONIC;
}
#ifdef _WIN32
static const auto FileTimeTo100Ns = [](FILETIME& ft) { return *reinterpret_cast<u64*>(&ft); };
switch (clock_id) {
case CLOCK_REALTIME:
case CLOCK_REALTIME_COARSE: {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE: {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
const u64 ns = FileTimeTo100Ns(ft) - DELTA_EPOCH_IN_100NS;
GetSystemTimePreciseAsFileTime(&ft);
static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
case CLOCK_MONOTONIC:
case CLOCK_MONOTONIC_COARSE: {
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST: {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
static constexpr u64 DeltaEpochIn100ns = 116444736000000000ULL;
const u64 ns = FileTimeTo100Ns(ft) - DeltaEpochIn100ns;
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
case ORBIS_CLOCK_UPTIME:
case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST: {
static LARGE_INTEGER pf = [] {
LARGE_INTEGER res{};
QueryPerformanceFrequency(&pf);
@ -141,43 +168,53 @@ static s32 clock_gettime(u32 clock_id, struct timespec* ts) {
}();
LARGE_INTEGER pc{};
QueryPerformanceCounter(&pc);
if (!QueryPerformanceCounter(&pc)) {
SetPosixErrno(EFAULT);
return -1;
}
ts->tv_sec = pc.QuadPart / pf.QuadPart;
ts->tv_nsec = ((pc.QuadPart % pf.QuadPart) * 1000'000'000) / pf.QuadPart;
return 0;
}
case CLOCK_PROCESS_CPUTIME_ID: {
case ORBIS_CLOCK_THREAD_CPUTIME_ID: {
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
return EFAULT;
if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
SetPosixErrno(EFAULT);
return -1;
}
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
case CLOCK_THREAD_CPUTIME_ID: {
case ORBIS_CLOCK_VIRTUAL: {
FILETIME ct, et, kt, ut;
if (!GetThreadTimes(GetCurrentThread(), &ct, &et, &kt, &ut)) {
return EFAULT;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
SetPosixErrno(EFAULT);
return -1;
}
const u64 ns = FileTimeTo100Ns(ut) + FileTimeTo100Ns(kt);
const u64 ns = FileTimeTo100Ns(ut);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
case ORBIS_CLOCK_PROF: {
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
SetPosixErrno(EFAULT);
return -1;
}
const u64 ns = FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
return 0;
}
default:
return EINVAL;
SetPosixErrno(EFAULT);
return -1;
}
}
#endif
int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* ts) {
if (ts == nullptr) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
clockid_t pclock_id = CLOCK_MONOTONIC;
#else
clockid_t pclock_id;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
@ -185,7 +222,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
break;
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST:
#ifndef __APPLE__
#ifdef CLOCK_REALTIME_COARSE
pclock_id = CLOCK_REALTIME_COARSE;
#else
pclock_id = CLOCK_REALTIME;
@ -199,7 +236,7 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
break;
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST:
#ifndef __APPLE__
#ifdef CLOCK_MONOTONIC_COARSE
pclock_id = CLOCK_MONOTONIC_COARSE;
#else
pclock_id = CLOCK_MONOTONIC;
@ -208,102 +245,155 @@ int PS4_SYSV_ABI orbis_clock_gettime(s32 clock_id, struct OrbisKernelTimespec* t
case ORBIS_CLOCK_THREAD_CPUTIME_ID:
pclock_id = CLOCK_THREAD_CPUTIME_ID;
break;
case ORBIS_CLOCK_PROCTIME: {
const auto us = sceKernelGetProcessTime();
ts->tv_sec = us / 1'000'000;
ts->tv_nsec = (us % 1'000'000) * 1000;
return 0;
}
case ORBIS_CLOCK_VIRTUAL: {
#ifdef _WIN64
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
return EFAULT;
}
const u64 ns = FileTimeTo100Ns(ut);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
#else
struct rusage ru;
rusage ru;
const auto res = getrusage(RUSAGE_SELF, &ru);
if (res < 0) {
return res;
SetPosixErrno(EFAULT);
return -1;
}
ts->tv_sec = ru.ru_utime.tv_sec;
ts->tv_nsec = ru.ru_utime.tv_usec * 1000;
#endif
return 0;
}
case ORBIS_CLOCK_PROF: {
#ifdef _WIN64
FILETIME ct, et, kt, ut;
if (!GetProcessTimes(GetCurrentProcess(), &ct, &et, &kt, &ut)) {
return EFAULT;
}
const u64 ns = FileTimeTo100Ns(kt);
ts->tv_sec = ns / 10'000'000;
ts->tv_nsec = (ns % 10'000'000) * 100;
#else
struct rusage ru;
rusage ru;
const auto res = getrusage(RUSAGE_SELF, &ru);
if (res < 0) {
return res;
SetPosixErrno(EFAULT);
return -1;
}
ts->tv_sec = ru.ru_stime.tv_sec;
ts->tv_nsec = ru.ru_stime.tv_usec * 1000;
#endif
return 0;
}
case ORBIS_CLOCK_EXT_NETWORK:
case ORBIS_CLOCK_EXT_DEBUG_NETWORK:
case ORBIS_CLOCK_EXT_AD_NETWORK:
case ORBIS_CLOCK_EXT_RAW_NETWORK:
pclock_id = CLOCK_MONOTONIC;
LOG_ERROR(Lib_Kernel, "unsupported = {} using CLOCK_MONOTONIC", clock_id);
break;
default:
return EINVAL;
SetPosixErrno(EFAULT);
return -1;
}
timespec t{};
int result = clock_gettime(pclock_id, &t);
const auto result = clock_gettime(pclock_id, &t);
ts->tv_sec = t.tv_sec;
ts->tv_nsec = t.tv_nsec;
return result;
if (result < 0) {
SetPosixErrno(errno);
return -1;
}
return 0;
#endif
}
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp) {
const auto res = orbis_clock_gettime(clock_id, tp);
if (res < 0) {
return ErrnoToSceKernelError(res);
s32 PS4_SYSV_ABI sceKernelClockGettime(const u32 clock_id, OrbisKernelTimespec* ts) {
if (const auto ret = posix_clock_gettime(clock_id, ts); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
int PS4_SYSV_ABI posix_nanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
const auto* request = reinterpret_cast<const timespec*>(rqtp);
auto* remain = reinterpret_cast<timespec*>(rmtp);
return nanosleep(request, remain);
s32 PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
if (res == nullptr) {
SetPosixErrno(EFAULT);
return -1;
}
if (clock_id == ORBIS_CLOCK_EXT_NETWORK || clock_id == ORBIS_CLOCK_EXT_DEBUG_NETWORK ||
clock_id == ORBIS_CLOCK_EXT_AD_NETWORK || clock_id == ORBIS_CLOCK_EXT_RAW_NETWORK) {
LOG_ERROR(Lib_Kernel, "Unsupported clock type {}, using CLOCK_MONOTONIC", clock_id);
clock_id = ORBIS_CLOCK_MONOTONIC;
}
#ifdef _WIN32
switch (clock_id) {
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST: {
DWORD timeAdjustment;
DWORD timeIncrement;
BOOL isTimeAdjustmentDisabled;
if (!GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled)) {
SetPosixErrno(EFAULT);
return -1;
}
res->tv_sec = 0;
res->tv_nsec = timeIncrement * 100;
return 0;
}
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
case ORBIS_CLOCK_UPTIME:
case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST: {
LARGE_INTEGER pf;
if (!QueryPerformanceFrequency(&pf)) {
SetPosixErrno(EFAULT);
return -1;
}
res->tv_sec = 0;
res->tv_nsec =
std::max(static_cast<s32>((1000000000 + (pf.QuadPart >> 1)) / pf.QuadPart), 1);
return 0;
}
default:
UNREACHABLE();
}
#else
clockid_t pclock_id;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
pclock_id = CLOCK_REALTIME;
break;
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_REALTIME_FAST:
#ifdef CLOCK_REALTIME_COARSE
pclock_id = CLOCK_REALTIME_COARSE;
#else
pclock_id = CLOCK_REALTIME;
#endif
break;
case ORBIS_CLOCK_UPTIME:
case ORBIS_CLOCK_UPTIME_PRECISE:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
pclock_id = CLOCK_MONOTONIC;
break;
case ORBIS_CLOCK_UPTIME_FAST:
case ORBIS_CLOCK_MONOTONIC_FAST:
#ifdef CLOCK_MONOTONIC_COARSE
pclock_id = CLOCK_MONOTONIC_COARSE;
#else
pclock_id = CLOCK_MONOTONIC;
#endif
break;
default:
UNREACHABLE();
}
timespec t{};
const auto result = clock_getres(pclock_id, &t);
res->tv_sec = t.tv_sec;
res->tv_nsec = t.tv_nsec;
if (result < 0) {
SetPosixErrno(errno);
return -1;
}
return 0;
#endif
}
int PS4_SYSV_ABI sceKernelNanosleep(const OrbisKernelTimespec* rqtp, OrbisKernelTimespec* rmtp) {
if (!rqtp || !rmtp) {
return ORBIS_KERNEL_ERROR_EFAULT;
s32 PS4_SYSV_ABI sceKernelClockGetres(const u32 clock_id, OrbisKernelTimespec* res) {
if (const auto ret = posix_clock_getres(clock_id, res); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
return posix_nanosleep(rqtp, rmtp);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
if (!tp) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
s32 PS4_SYSV_ABI posix_gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
#ifdef _WIN64
if (tp) {
FILETIME filetime;
GetSystemTimePreciseAsFileTime(&filetime);
@ -318,28 +408,8 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
tp->tv_sec = ticks / TICKS_PER_SECOND;
tp->tv_usec = ticks % TICKS_PER_SECOND;
#else
timeval tv;
gettimeofday(&tv, nullptr);
tp->tv_sec = tv.tv_sec;
tp->tv_usec = tv.tv_usec;
#endif
return ORBIS_OK;
}
int PS4_SYSV_ABI gettimeofday(OrbisKernelTimeval* tp, OrbisKernelTimezone* tz) {
// FreeBSD docs mention that the kernel generally does not track these values
// and they are usually returned as zero.
if (tz) {
tz->tz_minuteswest = 0;
tz->tz_dsttime = 0;
}
return sceKernelGettimeofday(tp);
}
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
#ifdef _WIN64
ASSERT(tz);
if (tz) {
static int tzflag = 0;
if (!tzflag) {
_tzset();
@ -347,57 +417,54 @@ s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
#else
struct timezone tzz;
struct timeval tv;
gettimeofday(&tv, &tzz);
timeval tv;
const auto ret = gettimeofday(&tv, &tzz);
if (tp) {
tp->tv_sec = tv.tv_sec;
tp->tv_usec = tv.tv_usec;
}
if (tz) {
tz->tz_dsttime = tzz.tz_dsttime;
tz->tz_minuteswest = tzz.tz_minuteswest;
}
if (ret < 0) {
SetPosixErrno(errno);
return -1;
}
return 0;
#endif
}
s32 PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
if (const auto ret = posix_gettimeofday(tp, nullptr); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
return ORBIS_OK;
}
int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
if (res == nullptr) {
return ORBIS_KERNEL_ERROR_EFAULT;
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz) {
if (const auto ret = posix_gettimeofday(nullptr, tz); ret < 0) {
return ErrnoToSceKernelError(*__Error());
}
clockid_t pclock_id = CLOCK_REALTIME;
switch (clock_id) {
case ORBIS_CLOCK_REALTIME:
case ORBIS_CLOCK_REALTIME_PRECISE:
case ORBIS_CLOCK_REALTIME_FAST:
pclock_id = CLOCK_REALTIME;
break;
case ORBIS_CLOCK_SECOND:
case ORBIS_CLOCK_MONOTONIC:
case ORBIS_CLOCK_MONOTONIC_PRECISE:
case ORBIS_CLOCK_MONOTONIC_FAST:
pclock_id = CLOCK_MONOTONIC;
break;
default:
UNREACHABLE();
}
timespec t{};
int result = clock_getres(pclock_id, &t);
res->tv_sec = t.tv_sec;
res->tv_nsec = t.tv_nsec;
if (result == 0) {
return ORBIS_OK;
}
return ORBIS_KERNEL_ERROR_EINVAL;
}
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, int* dst_seconds) {
s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, s32* dst_seconds) {
LOG_INFO(Kernel, "called");
if (timezone) {
sceKernelGettimezone(timezone);
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
if (seconds)
if (seconds) {
*seconds = param_1;
if (dst_seconds)
}
if (dst_seconds) {
*dst_seconds = timezone->tz_dsttime * 60;
}
} else {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@ -415,7 +482,7 @@ Common::NativeClock* GetClock() {
} // namespace Dev
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
struct OrbisTimesec* st, u64* dst_sec) {
LOG_TRACE(Kernel, "Called");
#ifdef __APPLE__
@ -444,28 +511,35 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
void RegisterTime(Core::Loader::SymbolsResolver* sym) {
clock = std::make_unique<Common::NativeClock>();
initial_ptc = clock->GetUptime();
// POSIX
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("0wu33hunNdE", "libkernel", 1, "libkernel", 1, 1, posix_sleep);
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, posix_sleep);
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime);
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
LIB_FUNCTION("smIj7eqzZE8", "libkernel", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, posix_gettimeofday);
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, posix_gettimeofday);
// Orbis
LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime);
LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter);
LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1,
sceKernelGetProcessTimeCounterFrequency);
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
LIB_FUNCTION("1j3S3n-tTW4", "libkernel", 1, "libkernel", 1, 1, sceKernelGetTscFrequency);
LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
LIB_FUNCTION("n88vx3C5nW8", "libkernel", 1, "libkernel", 1, 1, gettimeofday);
LIB_FUNCTION("n88vx3C5nW8", "libScePosix", 1, "libkernel", 1, 1, gettimeofday);
LIB_FUNCTION("QvsZxomvUHs", "libkernel", 1, "libkernel", 1, 1, sceKernelNanosleep);
LIB_FUNCTION("1jfXLRVzisc", "libkernel", 1, "libkernel", 1, 1, sceKernelUsleep);
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("QcteRwbsnV0", "libScePosix", 1, "libkernel", 1, 1, posix_usleep);
LIB_FUNCTION("-ZR+hG7aDHw", "libkernel", 1, "libkernel", 1, 1, sceKernelSleep);
LIB_FUNCTION("0wu33hunNdE", "libScePosix", 1, "libkernel", 1, 1, sceKernelSleep);
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", 1, 1, posix_nanosleep);
LIB_FUNCTION("QBi7HCK03hw", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGettime);
LIB_FUNCTION("wRYVA5Zolso", "libkernel", 1, "libkernel", 1, 1, sceKernelClockGetres);
LIB_FUNCTION("ejekcaNQNq0", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimeofday);
LIB_FUNCTION("kOcnerypnQA", "libkernel", 1, "libkernel", 1, 1, sceKernelGettimezone);
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, orbis_clock_gettime);
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, orbis_clock_gettime);
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
}

View file

@ -75,14 +75,14 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter();
u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency();
u64 PS4_SYSV_ABI sceKernelReadTsc();
int PS4_SYSV_ABI sceKernelClockGettime(s32 clock_id, OrbisKernelTimespec* tp);
s32 PS4_SYSV_ABI sceKernelClockGettime(u32 clock_id, OrbisKernelTimespec* tp);
s32 PS4_SYSV_ABI sceKernelGettimezone(OrbisKernelTimezone* tz);
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, int* dst_seconds);
s32 PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
OrbisKernelTimezone* timezone, s32* dst_seconds);
int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
s32 PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time, OrbisTimesec* st,
u64* dst_sec);
int PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
s32 PS4_SYSV_ABI sceKernelUsleep(u32 microseconds);
void RegisterTime(Core::Loader::SymbolsResolver* sym);

View file

@ -380,8 +380,7 @@ s32 PS4_SYSV_ABI sceNgs2GeomApply(const OrbisNgs2GeomListenerWork* listener,
s32 PS4_SYSV_ABI sceNgs2PanInit(OrbisNgs2PanWork* work, const float* aSpeakerAngle, float unitAngle,
u32 numSpeakers) {
LOG_ERROR(Lib_Ngs2, "aSpeakerAngle = {}, unitAngle = {}, numSpeakers = {}", *aSpeakerAngle,
unitAngle, numSpeakers);
LOG_ERROR(Lib_Ngs2, "unitAngle = {}, numSpeakers = {}", unitAngle, numSpeakers);
return ORBIS_OK;
}

View file

@ -949,4 +949,33 @@ int MemoryManager::GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut,
return ORBIS_OK;
}
int MemoryManager::IsStack(VAddr addr, void** start, void** end) {
auto vma_handle = FindVMA(addr);
if (vma_handle == vma_map.end()) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
const VirtualMemoryArea& vma = vma_handle->second;
if (!vma.Contains(addr, 0) || vma.IsFree()) {
return ORBIS_KERNEL_ERROR_EACCES;
}
auto stack_start = 0ul;
auto stack_end = 0ul;
if (vma.type == VMAType::Stack) {
stack_start = vma.base;
stack_end = vma.base + vma.size;
}
if (start != nullptr) {
*start = reinterpret_cast<void*>(stack_start);
}
if (end != nullptr) {
*end = reinterpret_cast<void*>(stack_end);
}
return ORBIS_OK;
}
} // namespace Core

View file

@ -223,6 +223,8 @@ public:
void InvalidateMemory(VAddr addr, u64 size) const;
int IsStack(VAddr addr, void** start, void** end);
private:
VMAHandle FindVMA(VAddr target) {
return std::prev(vma_map.upper_bound(target));

View file

@ -53,7 +53,7 @@ void Translator::S_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
ir.CompositeConstruct(ir.GetScalarReg(sbase), ir.GetScalarReg(sbase + 1));
IR::ScalarReg dst_reg{inst.dst[0].code};
for (u32 i = 0; i < num_dwords; i++) {
ir.SetScalarReg(dst_reg++, ir.ReadConst(base, ir.Imm32(dword_offset + i)));
ir.SetScalarReg(dst_reg + i, ir.ReadConst(base, ir.Imm32(dword_offset + i)));
}
}
@ -75,7 +75,7 @@ void Translator::S_BUFFER_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
IR::ScalarReg dst_reg{inst.dst[0].code};
for (u32 i = 0; i < num_dwords; i++) {
const IR::U32 index = ir.IAdd(dword_offset, ir.Imm32(i));
ir.SetScalarReg(dst_reg++, ir.ReadConstBuffer(vsharp, index));
ir.SetScalarReg(dst_reg + i, ir.ReadConstBuffer(vsharp, index));
}
}

View file

@ -989,13 +989,22 @@ void Translator::V_CMP_NE_U64(const GcnInst& inst) {
}
};
const IR::U1 src0{get_src(inst.src[0])};
ASSERT(inst.src[1].field == OperandField::ConstZero); // src0 != 0
auto op = [&inst, this](auto x) {
switch (inst.src[1].field) {
case OperandField::ConstZero:
return x;
case OperandField::SignedConstIntNeg:
return ir.LogicalNot(x);
default:
UNREACHABLE_MSG("unhandled V_CMP_NE_U64 source argument {}", u32(inst.src[1].field));
}
};
switch (inst.dst[1].field) {
case OperandField::VccLo:
ir.SetVcc(src0);
ir.SetVcc(op(src0));
break;
case OperandField::ScalarGPR:
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), src0);
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), op(src0));
break;
default:
UNREACHABLE();

View file

@ -584,7 +584,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
break;
}
case PM4ItOpcode::EventWrite: {
// const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header);
const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header);
LOG_DEBUG(Render_Vulkan,
"Encountered EventWrite: event_type = {}, event_index = {}",
magic_enum::enum_name(event->event_type.Value()),
magic_enum::enum_name(event->event_index.Value()));
if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) {
// TODO: handle proper synchronization, for now signal that update is done
// immediately
regs.cp_strmout_cntl.offset_update_done = 1;
}
break;
}
case PM4ItOpcode::EventWriteEos: {
@ -696,10 +705,10 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
const u64* wait_addr = wait_reg_mem->Address<u64*>();
if (vo_port->IsVoLabel(wait_addr) &&
num_submits == mapped_queues[GfxQueueId].submits.size()) {
vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(); });
vo_port->WaitVoLabel([&] { return wait_reg_mem->Test(regs.reg_array); });
break;
}
while (!wait_reg_mem->Test()) {
while (!wait_reg_mem->Test(regs.reg_array)) {
YIELD_GFX();
}
break;
@ -732,6 +741,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
}
break;
}
case PM4ItOpcode::StrmoutBufferUpdate: {
const auto* strmout = reinterpret_cast<const PM4CmdStrmoutBufferUpdate*>(header);
LOG_WARNING(Render_Vulkan,
"Unimplemented IT_STRMOUT_BUFFER_UPDATE, update_memory = {}, "
"source_select = {}, buffer_select = {}",
strmout->update_memory.Value(),
magic_enum::enum_name(strmout->source_select.Value()),
strmout->buffer_select.Value());
break;
}
default:
UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}",
static_cast<u32>(opcode), count);
@ -866,8 +885,9 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq
}
case PM4ItOpcode::SetQueueReg: {
const auto* set_data = reinterpret_cast<const PM4CmdSetQueueReg*>(header);
UNREACHABLE_MSG("Encountered compute SetQueueReg: vqid = {}, reg_offset = {:#x}",
LOG_WARNING(Render, "Encountered compute SetQueueReg: vqid = {}, reg_offset = {:#x}",
set_data->vqid.Value(), set_data->reg_offset.Value());
break;
}
case PM4ItOpcode::DispatchDirect: {
const auto* dispatch_direct = reinterpret_cast<const PM4CmdDispatchDirect*>(header);
@ -934,7 +954,7 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq
case PM4ItOpcode::WaitRegMem: {
const auto* wait_reg_mem = reinterpret_cast<const PM4CmdWaitRegMem*>(header);
ASSERT(wait_reg_mem->engine.Value() == PM4CmdWaitRegMem::Engine::Me);
while (!wait_reg_mem->Test()) {
while (!wait_reg_mem->Test(regs.reg_array)) {
YIELD_ASC(vqid);
}
break;

View file

@ -1175,6 +1175,14 @@ struct Liverpool {
BitField<22, 2, u32> onchip;
};
union StreamOutControl {
u32 raw;
struct {
u32 offset_update_done : 1;
u32 : 31;
};
};
union StreamOutConfig {
u32 raw;
struct {
@ -1378,7 +1386,9 @@ struct Liverpool {
AaConfig aa_config;
INSERT_PADDING_WORDS(0xA318 - 0xA2F8 - 1);
ColorBuffer color_buffers[NumColorBuffers];
INSERT_PADDING_WORDS(0xC242 - 0xA390);
INSERT_PADDING_WORDS(0xC03F - 0xA390);
StreamOutControl cp_strmout_cntl;
INSERT_PADDING_WORDS(0xC242 - 0xC040);
PrimitiveType primitive_type;
INSERT_PADDING_WORDS(0xC24C - 0xC243);
u32 num_indices;
@ -1668,6 +1678,7 @@ static_assert(GFX6_3D_REG_INDEX(color_buffers[0].base_address) == 0xA318);
static_assert(GFX6_3D_REG_INDEX(color_buffers[0].pitch) == 0xA319);
static_assert(GFX6_3D_REG_INDEX(color_buffers[0].slice) == 0xA31A);
static_assert(GFX6_3D_REG_INDEX(color_buffers[7].base_address) == 0xA381);
static_assert(GFX6_3D_REG_INDEX(cp_strmout_cntl) == 0xC03F);
static_assert(GFX6_3D_REG_INDEX(primitive_type) == 0xC242);
static_assert(GFX6_3D_REG_INDEX(num_instances) == 0xC24D);
static_assert(GFX6_3D_REG_INDEX(vgt_tf_memory_base) == 0xc250);

View file

@ -246,6 +246,46 @@ struct PM4CmdNop {
};
};
enum class SourceSelect : u32 {
BufferOffset = 0,
VgtStrmoutBufferFilledSize = 1,
SrcAddress = 2,
None = 3,
};
struct PM4CmdStrmoutBufferUpdate {
PM4Type3Header header;
union {
BitField<0, 1, u32> update_memory;
BitField<1, 2, SourceSelect> source_select;
BitField<8, 2, u32> buffer_select;
u32 control;
};
union {
BitField<2, 30, u32> dst_address_lo;
BitField<0, 2, u32> swap_dst;
};
u32 dst_address_hi;
union {
u32 buffer_offset;
BitField<2, 30, u32> src_address_lo;
BitField<0, 2, u32> swap_src;
};
u32 src_address_hi;
template <typename T = u64>
T DstAddress() const {
ASSERT(update_memory.Value() == 1);
return reinterpret_cast<T>(dst_address_lo.Value() | u64(dst_address_hi & 0xFFFF) << 32);
}
template <typename T = u64>
T SrcAddress() const {
ASSERT(source_select.Value() == SourceSelect::SrcAddress);
return reinterpret_cast<T>(src_address_lo.Value() | u64(src_address_hi & 0xFFFF) << 32);
}
};
struct PM4CmdDrawIndexOffset2 {
PM4Type3Header header;
u32 max_size; ///< Maximum number of indices
@ -303,6 +343,80 @@ static u64 GetGpuClock64() {
return static_cast<u64>(ticks);
}
// VGT_EVENT_INITIATOR.EVENT_TYPE
enum class EventType : u32 {
SampleStreamoutStats1 = 1,
SampleStreamoutStats2 = 2,
SampleStreamoutStats3 = 3,
CacheFlushTs = 4,
ContextDone = 5,
CacheFlush = 6,
CsPartialFlush = 7,
VgtStreamoutSync = 8,
VgtStreamoutReset = 10,
EndOfPipeIncrDe = 11,
EndOfPipeIbEnd = 12,
RstPixCnt = 13,
VsPartialFlush = 15,
PsPartialFlush = 16,
FlushHsOutput = 17,
FlushLsOutput = 18,
CacheFlushAndInvTsEvent = 20,
ZpassDone = 21,
CacheFlushAndInvEvent = 22,
PerfcounterStart = 23,
PerfcounterStop = 24,
PipelineStatStart = 25,
PipelineStatStop = 26,
PerfcounterSample = 27,
FlushEsOutput = 28,
FlushGsOutput = 29,
SamplePipelineStat = 30,
SoVgtStreamoutFlush = 31,
SampleStreamoutStats = 32,
ResetVtxCnt = 33,
VgtFlush = 36,
ScSendDbVpz = 39,
BottomOfPipeTs = 40,
DbCacheFlushAndInv = 42,
FlushAndInvDbDataTs = 43,
FlushAndInvDbMeta = 44,
FlushAndInvCbDataTs = 45,
FlushAndInvCbMeta = 46,
CsDone = 47,
PsDone = 48,
FlushAndInvCbPixelData = 49,
ThreadTraceStart = 51,
ThreadTraceStop = 52,
ThreadTraceFlush = 54,
ThreadTraceFinish = 55,
PixelPipeStatControl = 56,
PixelPipeStatDump = 57,
PixelPipeStatReset = 58,
};
enum class EventIndex : u32 {
Other = 0,
ZpassDone = 1,
SamplePipelineStat = 2,
SampleStreamoutStatSx = 3,
CsVsPsPartialFlush = 4,
EopReserved = 5,
EosReserved = 6,
CacheFlush = 7,
};
struct PM4CmdEventWrite {
PM4Type3Header header;
union {
u32 event_control;
BitField<0, 6, EventType> event_type; ///< Event type written to VGT_EVENT_INITIATOR
BitField<8, 4, EventIndex> event_index; ///< Event index
BitField<20, 1, u32> inv_l2; ///< Send WBINVL2 op to the TC L2 cache when EVENT_INDEX = 0111
};
u32 address[];
};
struct PM4CmdEventWriteEop {
PM4Type3Header header;
union {
@ -474,7 +588,12 @@ struct PM4CmdWaitRegMem {
BitField<8, 1, Engine> engine;
u32 raw;
};
u32 poll_addr_lo;
union {
BitField<0, 16, u32> reg;
BitField<2, 30, u32> poll_addr_lo;
BitField<0, 2, u32> swap;
u32 poll_addr_lo_raw;
};
u32 poll_addr_hi;
u32 ref;
u32 mask;
@ -482,31 +601,36 @@ struct PM4CmdWaitRegMem {
template <typename T = u32*>
T Address() const {
return std::bit_cast<T>((uintptr_t(poll_addr_hi) << 32) | poll_addr_lo);
return std::bit_cast<T>((uintptr_t(poll_addr_hi) << 32) | (poll_addr_lo << 2));
}
bool Test() const {
u32 Reg() const {
return reg.Value();
}
bool Test(const std::array<u32, Liverpool::NumRegs>& regs) const {
u32 value = mem_space.Value() == MemSpace::Memory ? *Address() : regs[Reg()];
switch (function.Value()) {
case Function::Always: {
return true;
}
case Function::LessThan: {
return (*Address() & mask) < ref;
return (value & mask) < ref;
}
case Function::LessThanEqual: {
return (*Address() & mask) <= ref;
return (value & mask) <= ref;
}
case Function::Equal: {
return (*Address() & mask) == ref;
return (value & mask) == ref;
}
case Function::NotEqual: {
return (*Address() & mask) != ref;
return (value & mask) != ref;
}
case Function::GreaterThanEqual: {
return (*Address() & mask) >= ref;
return (value & mask) >= ref;
}
case Function::GreaterThan: {
return (*Address() & mask) > ref;
return (value & mask) > ref;
}
case Function::Reserved:
[[fallthrough]];