mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2024-12-29 11:06:07 +00:00
core/libraries: HLE fiber reimplementation
This commit is contained in:
parent
8d8bb05055
commit
69d35697bc
|
@ -16,7 +16,7 @@ if (NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(shadPS4)
|
project(shadPS4 CXX C ASM)
|
||||||
|
|
||||||
# Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory.
|
# Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory.
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
|
@ -392,7 +392,8 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp
|
||||||
src/core/libraries/usbd/usbd.h
|
src/core/libraries/usbd/usbd.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
|
set(FIBER_LIB src/core/libraries/fiber/fiber_context.s
|
||||||
|
src/core/libraries/fiber/fiber.cpp
|
||||||
src/core/libraries/fiber/fiber.h
|
src/core/libraries/fiber/fiber.h
|
||||||
src/core/libraries/fiber/fiber_error.h
|
src/core/libraries/fiber/fiber_error.h
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,255 +6,447 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/libraries/fiber/fiber_error.h"
|
#include "core/libraries/fiber/fiber_error.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "core/tls.h"
|
|
||||||
|
|
||||||
#ifdef _WIN64
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Libraries::Fiber {
|
namespace Libraries::Fiber {
|
||||||
|
|
||||||
static constexpr u64 kFiberSignature = 0x054ad954;
|
static constexpr u32 kFiberSignature0 = 0xdef1649c;
|
||||||
|
static constexpr u32 kFiberSignature1 = 0xb37592a0;
|
||||||
|
static constexpr u32 kFiberOptSignature = 0xbb40e64d;
|
||||||
|
static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca;
|
||||||
|
static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef;
|
||||||
|
|
||||||
thread_local SceFiber* gCurrentFiber = nullptr;
|
thread_local OrbisFiberContext* g_fiber_ctx = nullptr;
|
||||||
thread_local void* gFiberThread = nullptr;
|
static std::atomic<u32> context_size_check = false;
|
||||||
|
|
||||||
void FiberEntry(void* param) {
|
extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp");
|
||||||
SceFiber* fiber = static_cast<SceFiber*>(param);
|
extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp");
|
||||||
u64 argRun = 0;
|
extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data,
|
||||||
u64 argRet = 0;
|
bool set_fpu) asm("_sceFiberSwitchEntry");
|
||||||
|
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit");
|
||||||
|
|
||||||
gCurrentFiber = fiber;
|
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) {
|
||||||
|
OrbisFiberContext* ctx = g_fiber_ctx;
|
||||||
if (fiber->pArgRun != nullptr) {
|
ctx->return_val = ret;
|
||||||
argRun = *fiber->pArgRun;
|
_sceFiberLongJmp(ctx);
|
||||||
}
|
|
||||||
|
|
||||||
Core::ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun);
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry,
|
void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) {
|
||||||
u64 argOnInitialize, void* addrContext, u64 sizeContext,
|
u64* stack_base = reinterpret_cast<u64*>(ctx->current_fiber->addr_context);
|
||||||
const SceFiberOptParam* optParam) {
|
if (stack_base && *stack_base != kFiberStackSignature) {
|
||||||
LOG_INFO(Lib_Fiber, "called: name = {}", name);
|
UNREACHABLE_MSG("Stack overflow detected in fiber.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to,
|
||||||
|
OrbisFiberContext* ctx) {
|
||||||
|
OrbisFiberContext* fiber_ctx = fiber->context;
|
||||||
|
if (fiber_ctx) {
|
||||||
|
ctx->arg_on_run_to = arg_on_run_to;
|
||||||
|
_sceFiberLongJmp(fiber_ctx);
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
OrbisFiberData data{};
|
||||||
|
if (ctx->prev_fiber) {
|
||||||
|
OrbisFiber* prev_fiber = ctx->prev_fiber;
|
||||||
|
ctx->prev_fiber = nullptr;
|
||||||
|
data.state = reinterpret_cast<u32*>(&prev_fiber->state);
|
||||||
|
} else {
|
||||||
|
data.state = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.entry = fiber->entry;
|
||||||
|
data.arg_on_initialize = fiber->arg_on_initialize;
|
||||||
|
data.arg_on_run_to = arg_on_run_to;
|
||||||
|
data.stack_addr =
|
||||||
|
reinterpret_cast<void*>(reinterpret_cast<u64>(fiber->addr_context) + fiber->size_context);
|
||||||
|
if (fiber->flags & FiberFlags::SetFpuRegs) {
|
||||||
|
data.fpucw = 0x037f;
|
||||||
|
data.mxcsr = 0x9fc0;
|
||||||
|
_sceFiberSwitchEntry(&data, true);
|
||||||
|
} else {
|
||||||
|
_sceFiberSwitchEntry(&data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to,
|
||||||
|
OrbisFiberContext* ctx) {
|
||||||
|
ctx->prev_fiber = cur_fiber;
|
||||||
|
ctx->current_fiber = fiber;
|
||||||
|
|
||||||
|
if (fiber->addr_context == nullptr) {
|
||||||
|
ctx->prev_fiber = nullptr;
|
||||||
|
|
||||||
|
OrbisFiberData data{};
|
||||||
|
data.entry = fiber->entry;
|
||||||
|
data.arg_on_initialize = fiber->arg_on_initialize;
|
||||||
|
data.arg_on_run_to = arg_on_run_to;
|
||||||
|
data.stack_addr = reinterpret_cast<void*>(ctx->rsp & ~15);
|
||||||
|
data.state = reinterpret_cast<u32*>(&cur_fiber->state);
|
||||||
|
|
||||||
|
if (fiber->flags & FiberFlags::SetFpuRegs) {
|
||||||
|
data.fpucw = 0x037f;
|
||||||
|
data.mxcsr = 0x9fc0;
|
||||||
|
_sceFiberSwitchEntry(&data, true);
|
||||||
|
} else {
|
||||||
|
_sceFiberSwitchEntry(&data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx);
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) {
|
||||||
|
ctx->arg_on_return = arg_on_return;
|
||||||
|
_sceFiberLongJmp(ctx);
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
|
||||||
|
u64 arg_on_initialize, void* addr_context, u64 size_context,
|
||||||
|
const OrbisFiberOptParam* opt_param, u32 build_ver) {
|
||||||
if (!fiber || !name || !entry) {
|
if (!fiber || !name || !entry) {
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
|
if ((u64)fiber & 7 || (u64)addr_context & 15) {
|
||||||
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
|
}
|
||||||
|
if (opt_param && (u64)opt_param & 7) {
|
||||||
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
|
}
|
||||||
|
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 (addr_context && !size_context) {
|
||||||
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
if (opt_param && opt_param->magic != kFiberOptSignature) {
|
||||||
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
fiber->signature = kFiberSignature;
|
u32 flags = FiberFlags::None;
|
||||||
|
if (build_ver >= 0x3500000) {
|
||||||
|
flags |= FiberFlags::SetFpuRegs;
|
||||||
|
}
|
||||||
|
if (context_size_check) {
|
||||||
|
flags |= FiberFlags::ContextSizeCheck;
|
||||||
|
}
|
||||||
|
|
||||||
fiber->entry = entry;
|
|
||||||
fiber->argOnInitialize = argOnInitialize;
|
|
||||||
|
|
||||||
fiber->argRun = 0;
|
|
||||||
fiber->pArgRun = &fiber->argRun;
|
|
||||||
fiber->argReturn = 0;
|
|
||||||
fiber->pArgReturn = &fiber->argReturn;
|
|
||||||
|
|
||||||
fiber->sizeContext = sizeContext;
|
|
||||||
|
|
||||||
fiber->state = FiberState::Init;
|
|
||||||
#ifdef _WIN64
|
|
||||||
fiber->handle = CreateFiber(sizeContext, FiberEntry, fiber);
|
|
||||||
#else
|
|
||||||
UNREACHABLE_MSG("Missing implementation");
|
|
||||||
#endif
|
|
||||||
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
||||||
|
|
||||||
return ORBIS_OK;
|
fiber->entry = entry;
|
||||||
}
|
fiber->arg_on_initialize = arg_on_initialize;
|
||||||
|
fiber->addr_context = addr_context;
|
||||||
|
fiber->size_context = size_context;
|
||||||
|
fiber->context = nullptr;
|
||||||
|
fiber->flags = flags;
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam) {
|
/*
|
||||||
LOG_ERROR(Lib_Fiber, "called");
|
A low stack area is problematic, as we can easily
|
||||||
|
cause a stack overflow with our HLE.
|
||||||
if (!optParam) {
|
*/
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
if (size_context && size_context <= 4096) {
|
||||||
|
LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fiber->magic_start = kFiberSignature0;
|
||||||
|
fiber->magic_end = kFiberSignature1;
|
||||||
|
|
||||||
|
if (addr_context != nullptr) {
|
||||||
|
fiber->context_start = addr_context;
|
||||||
|
fiber->context_end =
|
||||||
|
reinterpret_cast<void*>(reinterpret_cast<u64>(addr_context) + size_context);
|
||||||
|
|
||||||
|
/* Apply signature to start of stack */
|
||||||
|
*(u64*)addr_context = kFiberStackSignature;
|
||||||
|
|
||||||
|
if (flags & FiberFlags::ContextSizeCheck) {
|
||||||
|
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
|
||||||
|
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
|
||||||
|
|
||||||
|
u64* stack_ptr = stack_start + 1;
|
||||||
|
while (stack_ptr < stack_end) {
|
||||||
|
*stack_ptr++ = kFiberStackSizeCheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fiber->state = FiberState::Idle;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber) {
|
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) {
|
||||||
LOG_TRACE(Lib_Fiber, "called");
|
if (!opt_param) {
|
||||||
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
|
}
|
||||||
|
if ((u64)opt_param & 7) {
|
||||||
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_param->magic = kFiberOptSignature;
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) {
|
||||||
if (!fiber) {
|
if (!fiber) {
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
if ((u64)fiber % 8 != 0) {
|
if ((u64)fiber & 7) {
|
||||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
}
|
}
|
||||||
if (fiber->signature != kFiberSignature) {
|
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
|
||||||
return ORBIS_FIBER_ERROR_INVALID;
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
if (fiber->state != FiberState::Run) {
|
|
||||||
|
FiberState expected = FiberState::Idle;
|
||||||
|
if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) {
|
||||||
return ORBIS_FIBER_ERROR_STATE;
|
return ORBIS_FIBER_ERROR_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber->signature = 0;
|
|
||||||
fiber->state = FiberState::None;
|
|
||||||
|
|
||||||
#ifdef _WIN64
|
|
||||||
DeleteFiber(fiber->handle);
|
|
||||||
#else
|
|
||||||
UNREACHABLE_MSG("Missing implementation");
|
|
||||||
#endif
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn) {
|
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) {
|
||||||
LOG_TRACE(Lib_Fiber, "called");
|
|
||||||
|
|
||||||
if (!fiber) {
|
if (!fiber) {
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
if ((u64)fiber % 8 != 0) {
|
if ((u64)fiber & 7) {
|
||||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
}
|
}
|
||||||
if (fiber->signature != kFiberSignature) {
|
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
|
||||||
return ORBIS_FIBER_ERROR_INVALID;
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
if (fiber->state == FiberState::Run) {
|
if (g_fiber_ctx) {
|
||||||
|
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
FiberState expected = FiberState::Idle;
|
||||||
|
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
|
||||||
return ORBIS_FIBER_ERROR_STATE;
|
return ORBIS_FIBER_ERROR_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gFiberThread == nullptr) {
|
OrbisFiberContext ctx{};
|
||||||
#ifdef _WIN64
|
ctx.current_fiber = fiber;
|
||||||
gFiberThread = ConvertThreadToFiber(nullptr);
|
ctx.prev_fiber = nullptr;
|
||||||
#else
|
ctx.return_val = 0;
|
||||||
UNREACHABLE_MSG("Missing implementation");
|
|
||||||
#endif
|
g_fiber_ctx = &ctx;
|
||||||
|
|
||||||
|
s32 jmp = _sceFiberSetJmp(&ctx);
|
||||||
|
if (!jmp) {
|
||||||
|
if (fiber->addr_context) {
|
||||||
|
_sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx);
|
||||||
|
__builtin_trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
gCurrentFiber = fiber;
|
OrbisFiberData data{};
|
||||||
|
data.entry = fiber->entry;
|
||||||
if (fiber->pArgRun != nullptr) {
|
data.arg_on_initialize = fiber->arg_on_initialize;
|
||||||
*fiber->pArgRun = argOnRunTo;
|
data.arg_on_run_to = arg_on_run_to;
|
||||||
|
data.stack_addr = reinterpret_cast<void*>(ctx.rsp & ~15);
|
||||||
|
data.state = nullptr;
|
||||||
|
if (fiber->flags & FiberFlags::SetFpuRegs) {
|
||||||
|
data.fpucw = 0x037f;
|
||||||
|
data.mxcsr = 0x9fc0;
|
||||||
|
_sceFiberSwitchEntry(&data, true);
|
||||||
|
} else {
|
||||||
|
_sceFiberSwitchEntry(&data, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber->pArgReturn = argOnReturn;
|
OrbisFiber* cur_fiber = ctx.current_fiber;
|
||||||
fiber->state = FiberState::Run;
|
ctx.current_fiber = nullptr;
|
||||||
#ifdef _WIN64
|
cur_fiber->state = FiberState::Idle;
|
||||||
SwitchToFiber(fiber->handle);
|
|
||||||
#else
|
if (ctx.return_val != 0) {
|
||||||
UNREACHABLE_MSG("Missing implementation");
|
/* Fiber entry returned! This should never happen. */
|
||||||
#endif
|
UNREACHABLE_MSG("Fiber entry function returned.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_on_return) {
|
||||||
|
*arg_on_return = ctx.arg_on_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_fiber_ctx = nullptr;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun) {
|
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) {
|
||||||
LOG_TRACE(Lib_Fiber, "called");
|
|
||||||
|
|
||||||
if (!fiber) {
|
if (!fiber) {
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
if ((u64)fiber % 8 != 0) {
|
if ((u64)fiber & 7) {
|
||||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
}
|
}
|
||||||
if (fiber->signature != kFiberSignature) {
|
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
|
||||||
return ORBIS_FIBER_ERROR_INVALID;
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
if (gCurrentFiber == nullptr) {
|
if (!g_fiber_ctx) {
|
||||||
return ORBIS_FIBER_ERROR_PERMISSION;
|
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||||
}
|
}
|
||||||
if (fiber->state == FiberState::Run) {
|
|
||||||
|
OrbisFiberContext* fiber_ctx = g_fiber_ctx;
|
||||||
|
|
||||||
|
FiberState expected = FiberState::Idle;
|
||||||
|
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
|
||||||
return ORBIS_FIBER_ERROR_STATE;
|
return ORBIS_FIBER_ERROR_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gCurrentFiber->state = FiberState::Suspend;
|
OrbisFiber* cur_fiber = fiber_ctx->current_fiber;
|
||||||
|
if (cur_fiber->addr_context == nullptr) {
|
||||||
|
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, fiber_ctx);
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: argOnRun
|
OrbisFiberContext ctx{};
|
||||||
|
s32 jmp = _sceFiberSetJmp(&ctx);
|
||||||
|
if (!jmp) {
|
||||||
|
cur_fiber->context = &ctx;
|
||||||
|
_sceFiberCheckStackOverflow(fiber_ctx);
|
||||||
|
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, fiber_ctx);
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
*fiber->pArgRun = argOnRunTo;
|
if (fiber_ctx->prev_fiber) {
|
||||||
fiber->state = FiberState::Run;
|
fiber_ctx->prev_fiber->state = FiberState::Idle;
|
||||||
|
fiber_ctx->prev_fiber = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_on_run) {
|
||||||
|
*arg_on_run = fiber_ctx->arg_on_run_to;
|
||||||
|
}
|
||||||
|
|
||||||
gCurrentFiber = fiber;
|
|
||||||
#ifdef _WIN64
|
|
||||||
SwitchToFiber(fiber->handle);
|
|
||||||
#else
|
|
||||||
UNREACHABLE_MSG("Missing implementation");
|
|
||||||
#endif
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber) {
|
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) {
|
||||||
LOG_TRACE(Lib_Fiber, "called");
|
if (!fiber) {
|
||||||
|
|
||||||
if (!fiber || !gCurrentFiber) {
|
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
if (gCurrentFiber->signature != kFiberSignature) {
|
if (!g_fiber_ctx) {
|
||||||
return ORBIS_FIBER_ERROR_PERMISSION;
|
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
*fiber = gCurrentFiber;
|
*fiber = g_fiber_ctx->current_fiber;
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun) {
|
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) {
|
||||||
LOG_TRACE(Lib_Fiber, "called");
|
if (!g_fiber_ctx) {
|
||||||
|
|
||||||
if (gCurrentFiber->signature != kFiberSignature) {
|
|
||||||
return ORBIS_FIBER_ERROR_PERMISSION;
|
return ORBIS_FIBER_ERROR_PERMISSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gCurrentFiber->pArgReturn != nullptr) {
|
OrbisFiberContext* fiber_ctx = g_fiber_ctx;
|
||||||
*gCurrentFiber->pArgReturn = argOnReturn;
|
OrbisFiber* cur_fiber = fiber_ctx->current_fiber;
|
||||||
|
if (cur_fiber->addr_context) {
|
||||||
|
OrbisFiberContext ctx{};
|
||||||
|
s32 jmp = _sceFiberSetJmp(&ctx);
|
||||||
|
if (jmp) {
|
||||||
|
if (fiber_ctx->prev_fiber) {
|
||||||
|
fiber_ctx->prev_fiber->state = FiberState::Idle;
|
||||||
|
fiber_ctx->prev_fiber = nullptr;
|
||||||
|
}
|
||||||
|
if (arg_on_run) {
|
||||||
|
*arg_on_run = fiber_ctx->arg_on_run_to;
|
||||||
|
}
|
||||||
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: argOnRun
|
cur_fiber->context = &ctx;
|
||||||
gCurrentFiber->state = FiberState::Suspend;
|
_sceFiberCheckStackOverflow(fiber_ctx);
|
||||||
gCurrentFiber = nullptr;
|
}
|
||||||
#ifdef _WIN64
|
|
||||||
SwitchToFiber(gFiberThread);
|
_sceFiberTerminate(cur_fiber, arg_on_return, fiber_ctx);
|
||||||
#else
|
__builtin_trap();
|
||||||
UNREACHABLE_MSG("Missing implementation");
|
|
||||||
#endif
|
|
||||||
return ORBIS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo) {
|
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) {
|
||||||
LOG_INFO(Lib_Fiber, "called");
|
if (!fiber || !fiber_info) {
|
||||||
|
|
||||||
if (!fiber || !fiberInfo) {
|
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
|
if ((u64)fiber & 7 || (u64)fiber_info & 7) {
|
||||||
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
|
}
|
||||||
|
if (fiber_info->size != sizeof(OrbisFiberInfo)) {
|
||||||
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
|
||||||
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
fiberInfo->entry = fiber->entry;
|
fiber_info->entry = fiber->entry;
|
||||||
fiberInfo->argOnInitialize = fiber->argOnInitialize;
|
fiber_info->arg_on_initialize = fiber->arg_on_initialize;
|
||||||
fiberInfo->addrContext = nullptr;
|
fiber_info->addr_context = fiber->addr_context;
|
||||||
fiberInfo->sizeContext = fiber->sizeContext;
|
fiber_info->size_context = fiber->size_context;
|
||||||
fiberInfo->sizeContextMargin = 0;
|
strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
||||||
|
|
||||||
|
fiber_info->size_context_margin = -1;
|
||||||
|
if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) {
|
||||||
|
u64 stack_margin = 0;
|
||||||
|
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
|
||||||
|
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
|
||||||
|
|
||||||
|
if (*stack_start == kFiberStackSignature) {
|
||||||
|
u64* stack_ptr = stack_start + 1;
|
||||||
|
while (stack_ptr < stack_end) {
|
||||||
|
if (*stack_ptr == kFiberStackSizeCheck) {
|
||||||
|
stack_ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_margin =
|
||||||
|
reinterpret_cast<u64>(stack_ptr) - reinterpret_cast<u64>(stack_start + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fiber_info->size_context_margin = stack_margin;
|
||||||
|
}
|
||||||
|
|
||||||
strncpy(fiberInfo->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
|
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
|
||||||
LOG_ERROR(Lib_Fiber, "called");
|
|
||||||
|
|
||||||
if (flags != 0) {
|
if (flags != 0) {
|
||||||
return ORBIS_FIBER_ERROR_INVALID;
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 expected = 0;
|
||||||
|
if (!context_size_check.compare_exchange_strong(expected, 1u)) {
|
||||||
|
return ORBIS_FIBER_ERROR_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
|
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
|
||||||
LOG_ERROR(Lib_Fiber, "called");
|
u32 expected = 1;
|
||||||
|
if (!context_size_check.compare_exchange_strong(expected, 0u)) {
|
||||||
|
return ORBIS_FIBER_ERROR_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) {
|
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) {
|
||||||
LOG_INFO(Lib_Fiber, "called, name = {}", name);
|
|
||||||
|
|
||||||
if (!fiber || !name) {
|
if (!fiber || !name) {
|
||||||
return ORBIS_FIBER_ERROR_NULL;
|
return ORBIS_FIBER_ERROR_NULL;
|
||||||
}
|
}
|
||||||
if ((u64)fiber % 8 != 0) {
|
if ((u64)fiber & 7) {
|
||||||
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
return ORBIS_FIBER_ERROR_ALIGNMENT;
|
||||||
}
|
}
|
||||||
|
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
|
||||||
|
return ORBIS_FIBER_ERROR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
|
@ -262,6 +454,7 @@ s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) {
|
||||||
|
|
||||||
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
|
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
|
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
|
||||||
|
LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
|
||||||
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
|
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
|
||||||
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
|
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
|
||||||
|
|
||||||
|
|
|
@ -6,73 +6,113 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace Core::Loader {
|
namespace Core::Loader {
|
||||||
class SymbolsResolver;
|
class SymbolsResolver;
|
||||||
}
|
}
|
||||||
namespace Libraries::Fiber {
|
namespace Libraries::Fiber {
|
||||||
|
|
||||||
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
|
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
|
||||||
|
#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512)
|
||||||
|
|
||||||
typedef void PS4_SYSV_ABI (*SceFiberEntry)(u64 argOnInitialize, u64 argOnRun);
|
typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run);
|
||||||
|
|
||||||
enum FiberState : u32 {
|
enum FiberState : u32 {
|
||||||
None = 0u,
|
Run = 1u,
|
||||||
Init = 1u,
|
Idle = 2u,
|
||||||
Run = 2u,
|
Terminated = 3u,
|
||||||
Suspend = 3u,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SceFiber {
|
enum FiberFlags : u32 {
|
||||||
u64 signature;
|
None = 0x0,
|
||||||
FiberState state;
|
NoUlobjmgr = 0x1,
|
||||||
SceFiberEntry entry;
|
ContextSizeCheck = 0x10,
|
||||||
u64 argOnInitialize;
|
SetFpuRegs = 0x100,
|
||||||
u64 argRun;
|
|
||||||
u64* pArgRun;
|
|
||||||
u64 argReturn;
|
|
||||||
u64* pArgReturn;
|
|
||||||
u64 sizeContext;
|
|
||||||
char name[ORBIS_FIBER_MAX_NAME_LENGTH];
|
|
||||||
void* handle;
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SceFiber) <= 256);
|
|
||||||
|
|
||||||
struct SceFiberInfo {
|
struct OrbisFiber;
|
||||||
u64 size;
|
|
||||||
SceFiberEntry entry;
|
struct OrbisFiberContext {
|
||||||
u64 argOnInitialize;
|
struct {
|
||||||
void* addrContext;
|
u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15;
|
||||||
u64 sizeContext;
|
u16 fpucw;
|
||||||
|
u32 mxcsr;
|
||||||
|
};
|
||||||
|
OrbisFiber* current_fiber;
|
||||||
|
OrbisFiber* prev_fiber;
|
||||||
|
u64 arg_on_run_to;
|
||||||
|
u64 arg_on_return;
|
||||||
|
u64 return_val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisFiberData {
|
||||||
|
OrbisFiberEntry entry;
|
||||||
|
u64 arg_on_initialize;
|
||||||
|
u64 arg_on_run_to;
|
||||||
|
void* stack_addr;
|
||||||
|
u32* state;
|
||||||
|
u16 fpucw;
|
||||||
|
s8 pad[2];
|
||||||
|
u32 mxcsr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OrbisFiber {
|
||||||
|
u32 magic_start;
|
||||||
|
std::atomic<FiberState> state;
|
||||||
|
OrbisFiberEntry entry;
|
||||||
|
u64 arg_on_initialize;
|
||||||
|
void* addr_context;
|
||||||
|
u64 size_context;
|
||||||
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
|
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
|
||||||
u64 sizeContextMargin;
|
OrbisFiberContext* context;
|
||||||
|
u32 flags;
|
||||||
|
void* context_start;
|
||||||
|
void* context_end;
|
||||||
|
u32 magic_end;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SceFiberInfo) <= 128);
|
static_assert(sizeof(OrbisFiber) <= 256);
|
||||||
|
|
||||||
using SceFiberOptParam = void*;
|
struct OrbisFiberInfo {
|
||||||
|
u64 size;
|
||||||
|
OrbisFiberEntry entry;
|
||||||
|
u64 arg_on_initialize;
|
||||||
|
void* addr_context;
|
||||||
|
u64 size_context;
|
||||||
|
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
|
||||||
|
u64 size_context_margin;
|
||||||
|
u8 pad[48];
|
||||||
|
};
|
||||||
|
static_assert(sizeof(OrbisFiberInfo) == 128);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry,
|
struct OrbisFiberOptParam {
|
||||||
u64 argOnInitialize, void* addrContext, u64 sizeContext,
|
u32 magic;
|
||||||
const SceFiberOptParam* optParam);
|
};
|
||||||
|
static_assert(sizeof(OrbisFiberOptParam) <= 128);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam);
|
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
|
||||||
|
u64 arg_on_initialize, void* addr_context, u64 size_context,
|
||||||
|
const OrbisFiberOptParam* opt_param, u32 build_version);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber);
|
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn);
|
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun);
|
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber);
|
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun);
|
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo);
|
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run);
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
|
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
|
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name);
|
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name);
|
||||||
|
|
||||||
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
|
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
|
||||||
} // namespace Libraries::Fiber
|
} // namespace Libraries::Fiber
|
121
src/core/libraries/fiber/fiber_context.s
Normal file
121
src/core/libraries/fiber/fiber_context.s
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
.global _sceFiberSetJmp
|
||||||
|
_sceFiberSetJmp:
|
||||||
|
movq %rax, 0x0(%rdi)
|
||||||
|
|
||||||
|
movq (%rsp), %rdx
|
||||||
|
movq %rdx, 0x10(%rdi)
|
||||||
|
|
||||||
|
movq %rcx, 0x08(%rdi)
|
||||||
|
movq %rbx, 0x18(%rdi)
|
||||||
|
movq %rsp, 0x20(%rdi)
|
||||||
|
movq %rbp, 0x28(%rdi)
|
||||||
|
|
||||||
|
movq %r8, 0x30(%rdi)
|
||||||
|
movq %r9, 0x38(%rdi)
|
||||||
|
movq %r10, 0x40(%rdi)
|
||||||
|
movq %r11, 0x48(%rdi)
|
||||||
|
movq %r12, 0x50(%rdi)
|
||||||
|
movq %r13, 0x58(%rdi)
|
||||||
|
movq %r14, 0x60(%rdi)
|
||||||
|
movq %r15, 0x68(%rdi)
|
||||||
|
|
||||||
|
fnstcw 0x70(%rdi)
|
||||||
|
stmxcsr 0x72(%rdi)
|
||||||
|
|
||||||
|
xor %rax, %rax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global _sceFiberLongJmp
|
||||||
|
_sceFiberLongJmp:
|
||||||
|
# MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f)
|
||||||
|
stmxcsr -0x4(%rsp)
|
||||||
|
movl 0x72(%rdi), %eax
|
||||||
|
andl $0xffffffc0, %eax
|
||||||
|
movl -0x4(%rsp), %ecx
|
||||||
|
andl $0x3f, %ecx
|
||||||
|
xorl %eax, %ecx
|
||||||
|
movl %ecx, -0x4(%rsp)
|
||||||
|
ldmxcsr -0x4(%rsp)
|
||||||
|
|
||||||
|
movq 0x00(%rdi), %rax
|
||||||
|
movq 0x08(%rdi), %rcx
|
||||||
|
movq 0x10(%rdi), %rdx
|
||||||
|
movq 0x18(%rdi), %rbx
|
||||||
|
movq 0x20(%rdi), %rsp
|
||||||
|
movq 0x28(%rdi), %rbp
|
||||||
|
|
||||||
|
movq 0x30(%rdi), %r8
|
||||||
|
movq 0x38(%rdi), %r9
|
||||||
|
movq 0x40(%rdi), %r10
|
||||||
|
movq 0x48(%rdi), %r11
|
||||||
|
movq 0x50(%rdi), %r12
|
||||||
|
movq 0x58(%rdi), %r13
|
||||||
|
movq 0x60(%rdi), %r14
|
||||||
|
movq 0x68(%rdi), %r15
|
||||||
|
|
||||||
|
fldcw 0x70(%rdi)
|
||||||
|
|
||||||
|
# Make the jump and return 1
|
||||||
|
movq %rdx, 0x00(%rsp)
|
||||||
|
movq $0x1, %rax
|
||||||
|
ret
|
||||||
|
|
||||||
|
.global _sceFiberSwitchEntry
|
||||||
|
_sceFiberSwitchEntry:
|
||||||
|
mov %rdi, %r11
|
||||||
|
|
||||||
|
# Set stack address to provided stack
|
||||||
|
movq 0x18(%r11), %rsp
|
||||||
|
movq $0, %rbp
|
||||||
|
|
||||||
|
movq 0x08(%r11), %rdi # data->arg_on_initialize
|
||||||
|
movq 0x10(%r11), %rsi # data->arg_on_run_to
|
||||||
|
movq 0x20(%r11), %r10 # data->state
|
||||||
|
|
||||||
|
# Set previous fiber state to Idle
|
||||||
|
cmpq $0, %r10
|
||||||
|
jz .clear_regs
|
||||||
|
movl $2, (%r10)
|
||||||
|
|
||||||
|
.clear_regs:
|
||||||
|
test %esi, %esi
|
||||||
|
jz .skip_fpu_regs
|
||||||
|
|
||||||
|
ldmxcsr 0x2c(%r11)
|
||||||
|
fldcw 0x28(%r11)
|
||||||
|
|
||||||
|
.skip_fpu_regs:
|
||||||
|
movq 0x00(%r11), %r11 # data->entry
|
||||||
|
|
||||||
|
xorl %eax, %eax
|
||||||
|
xorl %ebx, %ebx
|
||||||
|
xorl %ecx, %ecx
|
||||||
|
xorl %edx, %edx
|
||||||
|
xorq %r8, %r8
|
||||||
|
xorq %r9, %r9
|
||||||
|
xorq %r10, %r10
|
||||||
|
xorq %r12, %r12
|
||||||
|
xorq %r13, %r13
|
||||||
|
xorq %r14, %r14
|
||||||
|
xorq %r15, %r15
|
||||||
|
pxor %mm0, %mm0
|
||||||
|
pxor %mm1, %mm1
|
||||||
|
pxor %mm2, %mm2
|
||||||
|
pxor %mm3, %mm3
|
||||||
|
pxor %mm4, %mm4
|
||||||
|
pxor %mm5, %mm5
|
||||||
|
pxor %mm6, %mm6
|
||||||
|
pxor %mm7, %mm7
|
||||||
|
emms
|
||||||
|
vzeroall
|
||||||
|
|
||||||
|
# Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to)
|
||||||
|
call *%r11
|
||||||
|
|
||||||
|
# Fiber returned, not good
|
||||||
|
movl $1, %edi
|
||||||
|
call _sceFiberForceQuit
|
||||||
|
ret
|
Loading…
Reference in a new issue