From 0fdc6c437012d7f0d681e45c339df7607d7cf7fe Mon Sep 17 00:00:00 2001 From: polyproxy <47796739+polybiusproxy@users.noreply.github.com> Date: Sat, 28 Dec 2024 13:48:36 +0100 Subject: [PATCH] libraries/fiber: implement context switching --- src/core/libraries/fiber/fiber.cpp | 98 ++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp index 7bb81b61e..4123219ae 100644 --- a/src/core/libraries/fiber/fiber.cpp +++ b/src/core/libraries/fiber/fiber.cpp @@ -221,11 +221,12 @@ s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { +s32 PS4_SYSV_ABI sceFiberRunImpl(OrbisFiber* fiber, void* addr_context, u64 size_context, + u64 arg_on_run_to, u64* arg_on_return) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber & 7) { + if ((u64)fiber & 7 || (u64)addr_context & 15) { return ORBIS_FIBER_ERROR_ALIGNMENT; } if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { @@ -237,6 +238,42 @@ s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_r return ORBIS_FIBER_ERROR_PERMISSION; } + /* Caller wants to attach context and run. */ + if (addr_context != nullptr || size_context != 0) { + if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) { + return ORBIS_FIBER_ERROR_RANGE; + } + if (size_context & 15) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (!addr_context || !size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->addr_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + + fiber->addr_context = addr_context; + fiber->size_context = size_context; + + fiber->context_start = addr_context; + fiber->context_end = + reinterpret_cast(reinterpret_cast(addr_context) + size_context); + + /* Apply signature to start of stack */ + *(u64*)addr_context = kFiberStackSignature; + + if (fiber->flags & FiberFlags::ContextSizeCheck) { + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + *stack_ptr++ = kFiberStackSizeCheck; + } + } + } + FiberState expected = FiberState::Idle; if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { return ORBIS_FIBER_ERROR_STATE; @@ -288,11 +325,12 @@ s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_r return ORBIS_OK; } -s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { +s32 PS4_SYSV_ABI sceFiberSwitchImpl(OrbisFiber* fiber, void* addr_context, u64 size_context, + u64 arg_on_run_to, u64* arg_on_run) { if (!fiber) { return ORBIS_FIBER_ERROR_NULL; } - if ((u64)fiber & 7) { + if ((u64)fiber & 7 || (u64)addr_context & 15) { return ORBIS_FIBER_ERROR_ALIGNMENT; } if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) { @@ -304,6 +342,42 @@ s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_o return ORBIS_FIBER_ERROR_PERMISSION; } + /* Caller wants to attach context and switch. */ + if (addr_context != nullptr || size_context != 0) { + if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) { + return ORBIS_FIBER_ERROR_RANGE; + } + if (size_context & 15) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (!addr_context || !size_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->addr_context) { + return ORBIS_FIBER_ERROR_INVALID; + } + + fiber->addr_context = addr_context; + fiber->size_context = size_context; + + fiber->context_start = addr_context; + fiber->context_end = + reinterpret_cast(reinterpret_cast(addr_context) + size_context); + + /* Apply signature to start of stack */ + *(u64*)addr_context = kFiberStackSignature; + + if (fiber->flags & FiberFlags::ContextSizeCheck) { + u64* stack_start = reinterpret_cast(fiber->context_start); + u64* stack_end = reinterpret_cast(fiber->context_end); + + u64* stack_ptr = stack_start + 1; + while (stack_ptr < stack_end) { + *stack_ptr++ = kFiberStackSizeCheck; + } + } + } + FiberState expected = FiberState::Idle; if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) { return ORBIS_FIBER_ERROR_STATE; @@ -462,9 +536,18 @@ s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) { + return sceFiberRunImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_return); +} + +s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) { + return sceFiberSwitchImpl(fiber, nullptr, 0, arg_on_run_to, arg_on_run); +} + void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); - LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberInitialize); // _sceFiberInitializeWithInternalOptionImpl LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); @@ -473,6 +556,11 @@ void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); + LIB_FUNCTION("avfGJ94g36Q", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberRunImpl); // _sceFiberAttachContextAndRun + LIB_FUNCTION("ZqhZFuzKT6U", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberSwitchImpl); // _sceFiberAttachContextAndSwitch + LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberStartContextSizeCheck);