From c269d23665b8d6d8089ef0bd3cc831a3eb0ac58d Mon Sep 17 00:00:00 2001 From: DH Date: Thu, 6 Jul 2023 14:26:57 +0300 Subject: [PATCH] [rpcsx-os] POC: jump to guest context --- rpcsx-os/CMakeLists.txt | 1 + rpcsx-os/main.cpp | 134 ++++++++-------------------------------- rpcsx-os/thread.cpp | 90 +++++++++++++++++++++++++++ rpcsx-os/thread.hpp | 11 ++++ 4 files changed, 129 insertions(+), 107 deletions(-) create mode 100644 rpcsx-os/thread.cpp create mode 100644 rpcsx-os/thread.hpp diff --git a/rpcsx-os/CMakeLists.txt b/rpcsx-os/CMakeLists.txt index 6ce09b7ad..6feb23646 100644 --- a/rpcsx-os/CMakeLists.txt +++ b/rpcsx-os/CMakeLists.txt @@ -25,6 +25,7 @@ add_executable(rpcsx-os ops.cpp linker.cpp io-device.cpp + thread.cpp vfs.cpp ) target_include_directories(rpcsx-os PUBLIC .) diff --git a/rpcsx-os/main.cpp b/rpcsx-os/main.cpp index 9320653f7..6c134904e 100644 --- a/rpcsx-os/main.cpp +++ b/rpcsx-os/main.cpp @@ -7,6 +7,7 @@ #include "ops.hpp" #include "vfs.hpp" #include "vm.hpp" +#include "thread.hpp" #include #include @@ -25,6 +26,8 @@ #include #include #include +#include +#include #include #include @@ -33,21 +36,12 @@ static int g_gpuPid; -struct LibcInfo { - std::uint64_t textBegin = ~static_cast(0); - std::uint64_t textSize = 0; -}; - -static LibcInfo libcInfo; - struct ThreadParam { void (*startFunc)(void *); void *arg; orbis::Thread *thread; }; -static thread_local orbis::Thread *g_currentThread = nullptr; - static void printStackTrace(ucontext_t *context, int fileno) { unw_cursor_t cursor; @@ -169,15 +163,11 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) { _writefsbase_u64(hostFs); } - // syscall(SYS_arch_prctl, ARCH_GET_GS, &hostFs); - // syscall(SYS_arch_prctl, ARCH_SET_FS, hostFs); - if (sig == SIGSYS) { - // printf("%x: %x\n", tid, thread->tid); - g_currentThread->context = reinterpret_cast(ucontext); - orbis::syscall_entry(g_currentThread); - _writefsbase_u64(g_currentThread->fsBase); - // syscall(SYS_arch_prctl, ARCH_SET_FS, g_currentThread->regs.fs); + auto prevContext = std::exchange(rx::thread::g_current->context, ucontext); + orbis::syscall_entry(rx::thread::g_current); + rx::thread::g_current->context = prevContext; + _writefsbase_u64(rx::thread::g_current->fsBase); return; } @@ -189,20 +179,20 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) { if (sig != SIGINT) { char buf[128] = ""; int len = snprintf(buf, sizeof(buf), " [%s] %u: Signal address=%p\n", - g_currentThread ? "guest" : "host", - g_currentThread ? g_currentThread->tid : ::gettid(), + rx::thread::g_current ? "guest" : "host", + rx::thread::g_current ? rx::thread::g_current->tid : ::gettid(), info->si_addr); write(2, buf, len); if (std::size_t printed = printAddressLocation( - buf, sizeof(buf), g_currentThread, (std::uint64_t)info->si_addr)) { + buf, sizeof(buf), rx::thread::g_current, (std::uint64_t)info->si_addr)) { printed += std::snprintf(buf + printed, sizeof(buf) - printed, "\n"); write(2, buf, printed); } - if (g_currentThread) { - printStackTrace(reinterpret_cast(ucontext), g_currentThread, + if (rx::thread::g_current) { + printStackTrace(reinterpret_cast(ucontext), rx::thread::g_current, 2); } else { printStackTrace(reinterpret_cast(ucontext), 2); @@ -284,29 +274,6 @@ static void setupSigHandlers() { } } -__attribute__((no_stack_protector)) static void * -emuThreadEntryPoint(void *paramsVoid) { - auto params = *reinterpret_cast(paramsVoid); - delete reinterpret_cast(paramsVoid); - - g_currentThread = params.thread; - - std::uint64_t hostFs; - syscall(SYS_arch_prctl, ARCH_GET_FS, &hostFs); - syscall(SYS_arch_prctl, ARCH_SET_GS, hostFs); - - if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, - libcInfo.textBegin, libcInfo.textSize, nullptr)) { - perror("prctl failed\n"); - exit(-1); - } - - syscall(SYS_arch_prctl, ARCH_SET_FS, params.thread->fsBase); - params.startFunc(params.arg); - syscall(SYS_arch_prctl, ARCH_SET_FS, hostFs); - - return nullptr; -} struct StackWriter { std::uint64_t address; @@ -334,23 +301,6 @@ struct StackWriter { } }; -static void createEmuThread(orbis::Thread &thread, uint64_t entryPoint, - uint64_t hostStackSize, uint64_t arg) { - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstack(&attr, thread.stackStart, hostStackSize); - - pthread_t pthread; - auto params = new ThreadParam; - - params->startFunc = (void (*)(void *))entryPoint; - params->arg = (void *)arg; - params->thread = &thread; - - pthread_create(&pthread, &attr, emuThreadEntryPoint, params); - pthread_join(pthread, nullptr); -} - static bool g_traceSyscalls = false; static const char *getSyscallName(orbis::Thread *thread, int sysno) { auto sysvec = thread->tproc->sysent; @@ -508,17 +458,20 @@ static int ps4Exec(orbis::Process *mainProcess, memcpy(arg, auxv, sizeof(auxv)); - ucontext_t currentContext; - getcontext(¤tContext); + auto context = new ucontext_t{}; - createEmuThread( - mainThread, libkernel->entryPoint, - utils::alignDown( - stack.address - - reinterpret_cast(mainThread.stackStart) - 0x1000, - rx::vm::kPageSize), - sp); - return 0; + context->uc_mcontext.gregs[REG_RDI] = sp; + context->uc_mcontext.gregs[REG_RSP] = sp; + + // FIXME: should be at guest user space + context->uc_mcontext.gregs[REG_RDX] = reinterpret_cast(+[] { + std::printf("At exit\n"); + });; + context->uc_mcontext.gregs[REG_RIP] = libkernel->entryPoint; + + mainThread.context = context; + rx::thread::invoke(&mainThread); + std::abort(); } static void usage(const char *argv0) { @@ -609,42 +562,7 @@ int main(int argc, const char *argv[]) { return 1; } - auto processPhdr = [](struct dl_phdr_info *info, size_t, void *data) { - auto path = std::string_view(info->dlpi_name); - auto slashPos = path.rfind('/'); - if (slashPos == std::string_view::npos) { - return 0; - } - - auto name = path.substr(slashPos + 1); - if (name.starts_with("libc.so")) { - std::printf("%s\n", std::string(name).c_str()); - auto libcInfo = reinterpret_cast(data); - - for (std::size_t i = 0; i < info->dlpi_phnum; ++i) { - auto &phdr = info->dlpi_phdr[i]; - - if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) == PF_X) { - libcInfo->textBegin = - std::min(libcInfo->textBegin, phdr.p_vaddr + info->dlpi_addr); - libcInfo->textSize = std::max(libcInfo->textSize, phdr.p_memsz); - } - } - - return 1; - } - - return 0; - }; - - dl_iterate_phdr(processPhdr, &libcInfo); - - std::printf("libc text %zx-%zx\n", libcInfo.textBegin, - libcInfo.textBegin + libcInfo.textSize); - setupSigHandlers(); - // rx::vm::printHostStats(); - rx::vfs::initialize(); int argIndex = 1; @@ -696,6 +614,7 @@ int main(int argc, const char *argv[]) { return 1; } + rx::thread::initialize(); rx::vm::initialize(); runRpsxGpu(); @@ -731,6 +650,7 @@ int main(int argc, const char *argv[]) { // rx::vm::printHostStats(); rx::vm::deinitialize(); + rx::thread::deinitialize(); return status; } diff --git a/rpcsx-os/thread.cpp b/rpcsx-os/thread.cpp new file mode 100644 index 000000000..beb54945b --- /dev/null +++ b/rpcsx-os/thread.cpp @@ -0,0 +1,90 @@ +#include "thread.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +thread_local orbis::Thread *rx::thread::g_current = nullptr; + +struct LibcInfo { + std::uint64_t textBegin = ~static_cast(0); + std::uint64_t textSize = 0; +}; + +static LibcInfo libcInfo; + +void rx::thread::initialize() { + auto processPhdr = [](struct dl_phdr_info *info, size_t, void *data) { + auto path = std::string_view(info->dlpi_name); + auto slashPos = path.rfind('/'); + if (slashPos == std::string_view::npos) { + return 0; + } + + auto name = path.substr(slashPos + 1); + if (name.starts_with("libc.so")) { + std::printf("%s\n", std::string(name).c_str()); + auto libcInfo = reinterpret_cast(data); + + for (std::size_t i = 0; i < info->dlpi_phnum; ++i) { + auto &phdr = info->dlpi_phdr[i]; + + if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) == PF_X) { + libcInfo->textBegin = + std::min(libcInfo->textBegin, phdr.p_vaddr + info->dlpi_addr); + libcInfo->textSize = std::max(libcInfo->textSize, phdr.p_memsz); + } + } + + return 1; + } + + return 0; + }; + + dl_iterate_phdr(processPhdr, &libcInfo); + + std::printf("libc text %zx-%zx\n", libcInfo.textBegin, + libcInfo.textBegin + libcInfo.textSize); +} + +void rx::thread::deinitialize() {} + +void rx::thread::invoke(orbis::Thread *thread) { + g_current = thread; + + std::uint64_t hostFs = _readfsbase_u64(); + _writegsbase_u64(hostFs); + + if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, + libcInfo.textBegin, libcInfo.textSize, nullptr)) { + perror("prctl failed\n"); + exit(-1); + } + + _writefsbase_u64(thread->fsBase); + auto context = reinterpret_cast(thread->context); + + asm volatile("movq $0, %%r8\n" + "movq $0, %%r9\n" + "movq $0, %%r11\n" + "movq $0, %%r12\n" + "movq $0, %%r13\n" + "movq $0, %%r14\n" + "movq %1, %%rsp\n" + "callq *%0\n" + : + : "rm"(context->uc_mcontext.gregs[REG_RIP]), + "rm"(context->uc_mcontext.gregs[REG_RSP]), + "D"(context->uc_mcontext.gregs[REG_RDI]), + "S"(context->uc_mcontext.gregs[REG_RSI]), + "d"(context->uc_mcontext.gregs[REG_RDX]), + "c"(context->uc_mcontext.gregs[REG_RCX]), + "b"(context->uc_mcontext.gregs[REG_RBX]) + : "memory"); + _writefsbase_u64(hostFs); +} diff --git a/rpcsx-os/thread.hpp b/rpcsx-os/thread.hpp new file mode 100644 index 000000000..467ad4e45 --- /dev/null +++ b/rpcsx-os/thread.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "orbis/thread/Thread.hpp" + +namespace rx::thread { +void initialize(); +void deinitialize(); + +extern thread_local orbis::Thread *g_current; +void invoke(orbis::Thread *thread); +}