From b6556e83ee96b1686a9b6eac8f6bd2bdb9c819c9 Mon Sep 17 00:00:00 2001 From: DH Date: Wed, 20 Nov 2024 23:11:55 +0300 Subject: [PATCH] ps5: collect type of process and firmware add initial gc and dce ioctls implementation --- orbis-kernel/include/orbis/KernelContext.hpp | 11 +- orbis-kernel/include/orbis/module/Module.hpp | 8 ++ orbis-kernel/include/orbis/thread/Process.hpp | 7 + orbis-kernel/src/sys/sys_event.cpp | 4 + orbis-kernel/src/sys/sys_sysctl.cpp | 12 ++ rpcsx/iodev/dce.cpp | 34 +++++ rpcsx/iodev/gc.cpp | 130 ++++++++++++++++++ rpcsx/iodev/icc_power.cpp | 5 + rpcsx/linker.cpp | 25 ++++ rpcsx/main.cpp | 89 +++++++----- rpcsx/ops.cpp | 13 +- 11 files changed, 297 insertions(+), 41 deletions(-) diff --git a/orbis-kernel/include/orbis/KernelContext.hpp b/orbis-kernel/include/orbis/KernelContext.hpp index dc3102039..239b64f19 100644 --- a/orbis-kernel/include/orbis/KernelContext.hpp +++ b/orbis-kernel/include/orbis/KernelContext.hpp @@ -48,6 +48,12 @@ struct UmtxChain { uint notify_n(const UmtxKey &key, sint count); }; +enum class FwType : std::uint8_t { + Unknown, + Ps4, + Ps5, +}; + class alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) KernelContext final { public: KernelContext(); @@ -192,7 +198,10 @@ public: shared_mutex regMgrMtx; kmap regMgrInt; - std::vector> dialogs{}; + std::vector> dialogs{}; + + FwType fwType = FwType::Unknown; + bool isDevKit = false; private: shared_mutex m_heap_mtx; diff --git a/orbis-kernel/include/orbis/module/Module.hpp b/orbis-kernel/include/orbis/module/Module.hpp index 1e8e5b925..70114029b 100644 --- a/orbis-kernel/include/orbis/module/Module.hpp +++ b/orbis-kernel/include/orbis/module/Module.hpp @@ -15,6 +15,12 @@ namespace orbis { struct Thread; struct Process; +enum class DynType : std::uint8_t { + None, + Ps4, + Ps5, +}; + struct ModuleNeeded { utils::kstring name; std::uint16_t version; @@ -98,6 +104,8 @@ struct Module final { uint16_t flags{}; uint64_t entryPoint{}; + DynType dynType = DynType::None; + uint32_t phNum{}; uint64_t phdrAddress{}; diff --git a/orbis-kernel/include/orbis/thread/Process.hpp b/orbis-kernel/include/orbis/thread/Process.hpp index 54c689d1f..9bc984b9a 100644 --- a/orbis-kernel/include/orbis/thread/Process.hpp +++ b/orbis-kernel/include/orbis/thread/Process.hpp @@ -44,6 +44,12 @@ struct NamedMemoryRange { } }; +enum class ProcessType : std::uint8_t { + FreeBsd, + Ps4, + Ps5, +}; + struct Process final { KernelContext *context = nullptr; pid_t pid = -1; @@ -54,6 +60,7 @@ struct Process final { Process *parentProcess = nullptr; shared_mutex mtx; int vmId = -1; + ProcessType type = ProcessType::FreeBsd; void (*onSysEnter)(Thread *thread, int id, uint64_t *args, int argsCount) = nullptr; void (*onSysExit)(Thread *thread, int id, uint64_t *args, int argsCount, diff --git a/orbis-kernel/src/sys/sys_event.cpp b/orbis-kernel/src/sys/sys_event.cpp index 2c88d1898..14847da4b 100644 --- a/orbis-kernel/src/sys/sys_event.cpp +++ b/orbis-kernel/src/sys/sys_event.cpp @@ -189,6 +189,10 @@ static SysResult keventChange(KQueue *kq, KEvent &change, Thread *thread) { // hp3d idle nodeIt->triggered = true; kq->cv.notify_all(kq->mtx); + } else if (g_context.fwType == FwType::Ps5 && + change.filter == kEvFiltGraphicsCore && change.ident == 0) { + nodeIt->triggered = true; + kq->cv.notify_all(kq->mtx); } return {}; diff --git a/orbis-kernel/src/sys/sys_sysctl.cpp b/orbis-kernel/src/sys/sys_sysctl.cpp index f71d58eb8..9fd20e08c 100644 --- a/orbis-kernel/src/sys/sys_sysctl.cpp +++ b/orbis-kernel/src/sys/sys_sysctl.cpp @@ -202,6 +202,18 @@ SysResult kern_sysctl(Thread *thread, ptr name, uint namelen, } } + if (name[0] == kern && name[1] == proc && name[2] == 55) { + if (g_context.fwType != FwType::Ps5) { + return orbis::ErrorCode::INVAL; + } + + if (oldlenp && old && *oldlenp == 4) { + return uwrite(ptr(old), + thread->tproc->type == ProcessType::Ps5 ? 1 + : 0); + } + } + if (name[0] == kern && name[1] == proc && name[2] == 36) { Process *process = thread->tproc; if (process->pid != name[3]) { diff --git a/rpcsx/iodev/dce.cpp b/rpcsx/iodev/dce.cpp index 74e4a3734..c5b415d88 100644 --- a/rpcsx/iodev/dce.cpp +++ b/rpcsx/iodev/dce.cpp @@ -256,6 +256,11 @@ static void initDceMemory(DceDevice *device) { device->dmemOffset = start; } +static orbis::ErrorCode dce_mmap(orbis::File *file, void **address, + std::uint64_t size, std::int32_t prot, + std::int32_t flags, std::int64_t offset, + orbis::Thread *thread); + static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request, void *argp, orbis::Thread *thread) { auto device = static_cast(file->device.get()); @@ -263,6 +268,35 @@ static orbis::ErrorCode dce_ioctl(orbis::File *file, std::uint64_t request, auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice}; auto &gpuCtx = gpu.getContext(); + // std::this_thread::sleep_for(std::chrono::seconds(5)); + + if (orbis::g_context.fwType == orbis::FwType::Ps5) { + if (request == 0x80308217) { + auto args = reinterpret_cast(argp); + + if (args->id == 9) { + ORBIS_LOG_NOTICE("dce: FlipControl allocate", args->id, args->padding, + args->arg2, args->ptr, args->size, args->arg5, + args->arg6); + + void *address; + ORBIS_RET_ON_ERROR( + dce_mmap(file, &address, vm::kPageSize, + vm::kMapProtCpuReadWrite | vm::kMapProtGpuAll, + vm::kMapFlagShared, 0, thread)); + + *(void **)args->ptr = address; + *(std::uint64_t *)args->arg5 = vm::kPageSize; + + return {}; + } + + ORBIS_LOG_FATAL("dce: unimplemented 0x80308217 request", args->id, + args->padding, args->arg2, args->ptr, args->size, + args->arg5, args->arg6); + } + } + if (request == 0xc0308203) { // returns: // PERM diff --git a/rpcsx/iodev/gc.cpp b/rpcsx/iodev/gc.cpp index c133194d1..8de3ffede 100644 --- a/rpcsx/iodev/gc.cpp +++ b/rpcsx/iodev/gc.cpp @@ -77,6 +77,136 @@ static orbis::ErrorCode gc_ioctl(orbis::File *file, std::uint64_t request, *reinterpret_cast(argp) = device->submitArea; break; + case 0xc004812e: { + if (orbis::g_context.fwType != orbis::FwType::Ps5) { + return orbis::ErrorCode::INVAL; + } + + struct Args { + std::uint16_t unk; + std::uint16_t flag; + }; + + auto args = reinterpret_cast(argp); + args->unk = 0; + args->flag = 0; + return {}; + } + + case 0xc0488131: { + if (orbis::g_context.fwType != orbis::FwType::Ps5) { + return orbis::ErrorCode::INVAL; + } + + // ps5 submit header + struct Args { + orbis::uint32_t unk0; + orbis::uint32_t contextControl[3]; + orbis::uint32_t cmds[3 * 4]; + orbis::uint64_t status; + }; + + static_assert(sizeof(Args) == 72); + + auto args = reinterpret_cast(argp); + + // ORBIS_LOG_ERROR("gc submit header", args->status, args->unk0, + // args->contextControl[0], args->contextControl[1], + // args->contextControl[2]); + + // for (std::size_t i = 0; i < 3; ++i) { + // auto offset = i * 4; + // ORBIS_LOG_ERROR("gc submit header cmd", i, args->cmds[offset], + // args->cmds[offset + 1], args->cmds[offset + 2], + // args->cmds[offset + 3]); + // } + + // thread->where(); + + if (auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice}) { + if (args->contextControl[0]) { + gpu.submitGfxCommand(gcFile->gfxPipe, + orbis::g_currentThread->tproc->vmId, + {args->contextControl, 3}); + } + + for (std::size_t i = 0; i < 3; ++i) { + auto offset = i * 4; + + if (args->cmds[offset] == 0) { + continue; + } + + ORBIS_LOG_ERROR("submit header", i, args->status, args->cmds[offset], + args->cmds[offset + 1], args->cmds[offset + 2], + args->cmds[offset + 3]); + gpu.submitGfxCommand(gcFile->gfxPipe, + orbis::g_currentThread->tproc->vmId, + {args->cmds + offset, 4}); + } + + gpu.waitForIdle(); + args->status = 0; + } else { + return orbis::ErrorCode::BUSY; + } + + return {}; + } + + case 0xc0188132: { + if (orbis::g_context.fwType != orbis::FwType::Ps5) { + return orbis::ErrorCode::INVAL; + } + + struct Submit { + std::uint64_t unk0; + std::uint64_t unk1; + std::uint64_t address; + std::uint64_t size; + }; + + struct Args { + orbis::uint32_t unk0; // ringId? + orbis::uint32_t count; + orbis::ptr submits; + orbis::uint32_t status; + orbis::uint32_t padding; + }; + + static_assert(sizeof(Args) == 24); + auto args = reinterpret_cast(argp); + + // ORBIS_LOG_ERROR("gc submit", args->unk0, args->count, args->submits, + // args->status, args->padding); + + // for (std::size_t i = 0; i < args->count / 2; ++i) { + // ORBIS_LOG_ERROR("gc submit cmd", i, args->submits[i].address, + // args->submits[i].size, args->submits[i].unk0, + // args->submits[i].unk1); + // } + + if (auto gpu = amdgpu::DeviceCtl{orbis::g_context.gpuDevice}) { + for (unsigned i = 0; i < args->count / 2; ++i) { + auto addressLo = static_cast(args->submits[i].address); + auto addressHi = static_cast(args->submits[i].address >> 32); + auto size = static_cast(args->submits[i].size); + + gpu.submitGfxCommand(gcFile->gfxPipe, + orbis::g_currentThread->tproc->vmId, { + {0xc0023f00, addressLo, addressHi, size} + }); + } + + gpu.waitForIdle(); + } else { + return orbis::ErrorCode::BUSY; + } + + args->status = 0; + return {}; + } + case 0xc0108102: { // submit? struct Args { orbis::uint32_t arg0; diff --git a/rpcsx/iodev/icc_power.cpp b/rpcsx/iodev/icc_power.cpp index 14277b08f..6daec7478 100644 --- a/rpcsx/iodev/icc_power.cpp +++ b/rpcsx/iodev/icc_power.cpp @@ -47,8 +47,13 @@ static orbis::ErrorCode icc_power_ioctl(orbis::File *file, return {}; } +orbis::ErrorCode icc_stat(orbis::File *file, orbis::Stat *sb, orbis::Thread *thread) { + return{}; +} + static const orbis::FileOps fileOps = { .ioctl = icc_power_ioctl, + .stat = icc_stat, }; orbis::ErrorCode IccPowerDevice::open(orbis::Ref *file, diff --git a/rpcsx/linker.cpp b/rpcsx/linker.cpp index 39576519a..3d3448203 100644 --- a/rpcsx/linker.cpp +++ b/rpcsx/linker.cpp @@ -663,6 +663,9 @@ Ref rx::linker::loadModule(std::span image, std::unordered_map idToModuleIndex; std::unordered_map idToLibraryIndex; + bool hasPs4Dyn = false; + bool hasPs5Dyn = false; + for (auto dyn : dyns) { if (dyn.d_tag == kElfDynamicTypeSceModuleInfo || dyn.d_tag == kElfDynamicTypeSceModuleInfo1) { @@ -686,6 +689,11 @@ Ref rx::linker::loadModule(std::span image, if (dyn.d_tag == kElfDynamicTypeSceNeededModule || dyn.d_tag == kElfDynamicTypeSceNeededModule1) { + if (dyn.d_tag == kElfDynamicTypeSceNeededModule) { + hasPs4Dyn = true; + } else { + hasPs5Dyn = true; + } auto [it, inserted] = idToModuleIndex.try_emplace( dyn.d_un.d_val >> 48, result->neededModules.size()); @@ -713,6 +721,13 @@ Ref rx::linker::loadModule(std::span image, lib.name = strtab + static_cast(dyn.d_un.d_val); lib.isExport = dyn.d_tag == kElfDynamicTypeSceExportLib || dyn.d_tag == kElfDynamicTypeSceExportLib1; + + if (dyn.d_tag == kElfDynamicTypeSceExportLib || + dyn.d_tag == kElfDynamicTypeSceImportLib) { + hasPs4Dyn = true; + } else { + hasPs5Dyn = true; + } } else if (dyn.d_tag == kElfDynamicTypeSceExportLibAttr || dyn.d_tag == kElfDynamicTypeSceImportLibAttr) { auto [it, inserted] = idToLibraryIndex.try_emplace( @@ -787,6 +802,16 @@ Ref rx::linker::loadModule(std::span image, } } + if (hasPs4Dyn && hasPs5Dyn) { + std::fprintf(stderr, "unexpected import type\n"); + std::abort(); + } + if (hasPs4Dyn) { + result->dynType = orbis::DynType::Ps4; + } else if (hasPs5Dyn) { + result->dynType = orbis::DynType::Ps5; + } + if (symtab != nullptr && symtabSize > 0) { auto sceSymtab = std::span(symtab, symtabSize); diff --git a/rpcsx/main.cpp b/rpcsx/main.cpp index 95a4346f4..807342014 100644 --- a/rpcsx/main.cpp +++ b/rpcsx/main.cpp @@ -322,7 +322,7 @@ static void onSysExit(orbis::Thread *thread, int id, uint64_t *args, funlockfile(stderr); } -static void ps4InitDev() { +static void guestInitDev() { auto dmem1 = createDmemCharacterDevice(1); orbis::g_context.dmemDevice = dmem1; @@ -431,6 +431,11 @@ static void ps4InitDev() { vfs::addDevice("cayman/reg", createCaymanRegCharacterDevice()); vfs::addDevice("hctrl", createHidCharacterDevice()); + if (orbis::g_context.fwType == orbis::FwType::Ps5) { + vfs::addDevice("iccnvs4", createIccPowerCharacterDevice()); + vfs::addDevice("ajmi", createAjmCharacterDevice()); + } + // mbus->emitEvent({ // .system = 2, // .eventId = 1, @@ -454,7 +459,7 @@ static void ps4InitDev() { orbis::g_context.blockpoolDevice = createBlockPoolDevice(); } -static void ps4InitFd(orbis::Thread *mainThread) { +static void guestInitFd(orbis::Thread *mainThread) { orbis::Ref stdinFile; orbis::Ref stdoutFile; orbis::Ref stderrFile; @@ -486,9 +491,9 @@ struct ExecEnv { std::uint64_t interpBase; }; -int ps4Exec(orbis::Thread *mainThread, ExecEnv execEnv, - orbis::utils::Ref executableModule, - std::span argv, std::span envp) { +int guestExec(orbis::Thread *mainThread, ExecEnv execEnv, + orbis::utils::Ref executableModule, + std::span argv, std::span envp) { const auto stackEndAddress = 0x7'ffff'c000ull; const auto stackSize = 0x40000 * 32; auto stackStartAddress = stackEndAddress - stackSize; @@ -518,6 +523,12 @@ int ps4Exec(orbis::Thread *mainThread, ExecEnv execEnv, envpOffsets.push_back(0); + if (executableModule->dynType == orbis::DynType::Ps4) { + mainThread->tproc->type = orbis::ProcessType::Ps4; + } else if (executableModule->dynType == orbis::DynType::Ps5) { + mainThread->tproc->type = orbis::ProcessType::Ps5; + } + // clang-format off std::uint64_t auxv[] = { AT_ENTRY, executableModule->entryPoint, @@ -565,7 +576,7 @@ int ps4Exec(orbis::Thread *mainThread, ExecEnv execEnv, std::abort(); } -struct Ps4ProcessParam { +struct ProcessParam { orbis::size_t size; orbis::uint32_t magic; orbis::uint32_t version; @@ -578,23 +589,17 @@ struct Ps4ProcessParam { orbis::ptr libcParam; }; -ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread, - const orbis::Ref &executableModule, - bool isSystem) { +ExecEnv guestCreateExecEnv(orbis::Thread *mainThread, + const orbis::Ref &executableModule, + bool isSystem) { std::uint64_t interpBase = 0; std::uint64_t entryPoint = executableModule->entryPoint; if (mainThread->tproc->processParam != nullptr && - mainThread->tproc->processParamSize >= sizeof(Ps4ProcessParam)) { + mainThread->tproc->processParamSize >= sizeof(ProcessParam)) { auto processParam = - reinterpret_cast(mainThread->tproc->processParam); - - auto sdkVersion = processParam // - + sizeof(uint64_t) // size - + sizeof(uint32_t) // magic - + sizeof(uint32_t); // entryCount - - mainThread->tproc->sdkVersion = *(uint32_t *)sdkVersion; + reinterpret_cast(mainThread->tproc->processParam); + mainThread->tproc->sdkVersion = processParam->sdkVersion; } if (orbis::g_context.sdkVersion == 0 && mainThread->tproc->sdkVersion != 0) { @@ -604,8 +609,7 @@ ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread, mainThread->tproc->sdkVersion = orbis::g_context.sdkVersion; } - if (executableModule->type == rx::linker::kElfTypeExec || - executableModule->type == rx::linker::kElfTypeSceExec) { + if (executableModule->dynType == orbis::DynType::None) { return {.entryPoint = entryPoint, .interpBase = interpBase}; } @@ -671,6 +675,14 @@ ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread, std::printf("fw sdk version: %x\n", orbis::g_context.fwSdkVersion); } + if (orbis::g_context.fwType == orbis::FwType::Unknown) { + if (libkernel->dynType == orbis::DynType::Ps4) { + orbis::g_context.fwType = orbis::FwType::Ps4; + } else { + orbis::g_context.fwType = orbis::FwType::Ps5; + } + } + libkernel->id = mainThread->tproc->modulesMap.insert(libkernel); interpBase = reinterpret_cast(libkernel->base); entryPoint = libkernel->entryPoint; @@ -678,11 +690,12 @@ ExecEnv ps4CreateExecEnv(orbis::Thread *mainThread, return {.entryPoint = entryPoint, .interpBase = interpBase}; } -int ps4Exec(orbis::Thread *mainThread, - orbis::utils::Ref executableModule, - std::span argv, std::span envp) { - auto execEnv = ps4CreateExecEnv(mainThread, executableModule, true); - return ps4Exec(mainThread, execEnv, std::move(executableModule), argv, envp); +int guestExec(orbis::Thread *mainThread, + orbis::utils::Ref executableModule, + std::span argv, std::span envp) { + auto execEnv = guestCreateExecEnv(mainThread, executableModule, true); + return guestExec(mainThread, execEnv, std::move(executableModule), argv, + envp); } static void usage(const char *argv0) { @@ -774,7 +787,7 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path, rx::thread::initialize(); rx::thread::setupThisThread(); - ps4InitFd(newThread); + guestInitFd(newThread); orbis::Ref socket; createSocket(&socket, "", 1, 1, 0); @@ -808,7 +821,7 @@ static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path, rx::thread::initialize(); rx::thread::setupSignalStack(); rx::thread::setupThisThread(); - ps4Exec(thread, executableModule, argv, envv); + guestExec(thread, executableModule, argv, envv); }); thread->handle.join(); @@ -968,6 +981,7 @@ int main(int argc, const char *argv[]) { if (guestArgv.empty()) { guestArgv.emplace_back("/mini-syscore.elf"); isSystem = true; + asRoot = true; } rx::thread::initialize(); @@ -1083,10 +1097,10 @@ int main(int argc, const char *argv[]) { return 1; } - ps4InitDev(); - ps4InitFd(mainThread); + guestInitDev(); + guestInitFd(mainThread); - auto execEnv = ps4CreateExecEnv(mainThread, executableModule, isSystem); + auto execEnv = guestCreateExecEnv(mainThread, executableModule, isSystem); // data transfer mode // 0 - normal @@ -1135,7 +1149,8 @@ int main(int argc, const char *argv[]) { ipmi::createSysCoreObjects(initProcess); ipmi::createGnmCompositorObjects(initProcess); ipmi::createShellCoreObjects(initProcess); - if (enableAudioIpmi) { + + if (enableAudioIpmi || orbis::g_context.fwType == orbis::FwType::Ps5) { ipmi::createAudioSystemObjects(initProcess); } @@ -1152,7 +1167,7 @@ int main(int argc, const char *argv[]) { initProcess->cwd = "/app0/"; - if (!enableAudioIpmi) { + if (orbis::g_context.fwType != orbis::FwType::Ps5 && !enableAudioIpmi) { launchDaemon(mainThread, "/system/sys/orbis_audiod.elf", {"/system/sys/orbis_audiod.elf"}, {}, { @@ -1196,14 +1211,20 @@ int main(int argc, const char *argv[]) { } } + if (orbis::g_context.fwType == orbis::FwType::Ps5 && !isSystem) { + ipmi::createIpmiServer(initProcess, "SceSysAvControlIpc"); + ipmi::createShm("SceAvControl", 0xa02, 0x1a4, 4096); + ipmi::createEventFlag("SceAvControlEvf", 0x121, 0); + } + mainThread->hostTid = ::gettid(); mainThread->nativeHandle = pthread_self(); orbis::g_currentThread = mainThread; rx::thread::setupSignalStack(); rx::thread::setupThisThread(); - status = - ps4Exec(mainThread, execEnv, std::move(executableModule), guestArgv, {}); + status = guestExec(mainThread, execEnv, std::move(executableModule), + guestArgv, {}); vm::deinitialize(); rx::thread::deinitialize(); diff --git a/rpcsx/ops.cpp b/rpcsx/ops.cpp index daf71a08d..7c01e325c 100644 --- a/rpcsx/ops.cpp +++ b/rpcsx/ops.cpp @@ -42,9 +42,9 @@ extern bool allowMonoDebug; extern "C" void __register_frame(const void *); void setupSigHandlers(); -int ps4Exec(orbis::Thread *mainThread, - orbis::utils::Ref executableModule, - std::span argv, std::span envp); +int guestExec(orbis::Thread *mainThread, + orbis::utils::Ref executableModule, + std::span argv, std::span envp); namespace { static std::pair> @@ -722,7 +722,7 @@ SysResult processNeeded(Thread *thread) { } } - for (auto needed : allNeeded) { + for (const auto &needed : allNeeded) { auto [result, neededModule] = loadPrx(thread, needed, false, loadedObjects, loadedModules, needed); @@ -736,7 +736,7 @@ SysResult processNeeded(Thread *thread) { module->importedModules.clear(); module->importedModules.reserve(module->neededModules.size()); - for (auto mod : module->neededModules) { + for (const auto &mod : module->neededModules) { if (auto it = loadedModules.find(std::string_view(mod.name)); it != loadedModules.end()) { module->importedModules.emplace_back(it->second); @@ -787,6 +787,7 @@ SysResult fork(Thread *thread, slong flags) { process->parentProcess = thread->tproc; process->authInfo = thread->tproc->authInfo; process->sdkVersion = thread->tproc->sdkVersion; + process->type = thread->tproc->type; for (auto [id, mod] : thread->tproc->modulesMap) { if (!process->modulesMap.insert(id, mod)) { std::abort(); @@ -919,7 +920,7 @@ SysResult execve(Thread *thread, ptr fname, ptr> argv, thread->nativeHandle = pthread_self(); orbis::g_currentThread = thread; - ps4Exec(thread, executableModule, _argv, _envv); + guestExec(thread, executableModule, _argv, _envv); }).join(); std::abort(); }