From 525ef02e8aafd1163e28e97e9cf6db4450477e8b Mon Sep 17 00:00:00 2001 From: DH Date: Tue, 31 Oct 2023 21:28:40 +0300 Subject: [PATCH] [rpcsx-os] fork: implement vm and vfs fork stub metadbg device implement notification device implement sys_pipe --- orbis-kernel/CMakeLists.txt | 1 + orbis-kernel/include/orbis/KernelContext.hpp | 12 +++ orbis-kernel/include/orbis/pipe.hpp | 13 +++ .../include/orbis/thread/ProcessOps.hpp | 1 + orbis-kernel/include/orbis/utils/SharedCV.hpp | 2 +- orbis-kernel/src/KernelContext.cpp | 5 +- orbis-kernel/src/pipe.cpp | 61 ++++++++++++ orbis-kernel/src/sys/sys_fork.cpp | 41 +------- orbis-kernel/src/sys/sys_pipe.cpp | 8 +- rpcsx-os/CMakeLists.txt | 1 + rpcsx-os/io-devices.hpp | 1 + rpcsx-os/iodev/dmem.cpp | 2 +- rpcsx-os/iodev/metadbg.cpp | 41 ++++++++ rpcsx-os/iodev/notification.cpp | 53 +++++++++- rpcsx-os/iodev/urandom.cpp | 4 +- rpcsx-os/main.cpp | 6 +- rpcsx-os/ops.cpp | 96 +++++++++++++++++++ rpcsx-os/thread.cpp | 19 +--- rpcsx-os/vfs.cpp | 47 ++++++--- rpcsx-os/vfs.hpp | 1 + rpcsx-os/vm.cpp | 39 ++++++++ rpcsx-os/vm.hpp | 1 + 22 files changed, 379 insertions(+), 76 deletions(-) create mode 100644 orbis-kernel/include/orbis/pipe.hpp create mode 100644 orbis-kernel/src/pipe.cpp create mode 100644 rpcsx-os/iodev/metadbg.cpp diff --git a/orbis-kernel/CMakeLists.txt b/orbis-kernel/CMakeLists.txt index c4f8f453b..72183968a 100644 --- a/orbis-kernel/CMakeLists.txt +++ b/orbis-kernel/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(obj.orbis-utils-ipc OBJECT ) add_library(obj.orbis-kernel OBJECT src/module.cpp + src/pipe.cpp src/sysvec.cpp src/evf.cpp src/KernelContext.cpp diff --git a/orbis-kernel/include/orbis/KernelContext.hpp b/orbis-kernel/include/orbis/KernelContext.hpp index 3987dbc04..bea774556 100644 --- a/orbis-kernel/include/orbis/KernelContext.hpp +++ b/orbis-kernel/include/orbis/KernelContext.hpp @@ -1,6 +1,7 @@ #pragma once #include "evf.hpp" #include "ipmi.hpp" +#include "orbis/utils/IdMap.hpp" #include "osem.hpp" #include "utils/LinkedNode.hpp" #include "utils/SharedCV.hpp" @@ -54,6 +55,15 @@ public: void deleteProcess(Process *proc); Process *findProcessById(pid_t pid) const; + utils::LinkedNode *getProcessList() { + return m_processes; + } + + long allocatePid() { + std::lock_guard lock(m_thread_id_mtx); + return m_thread_id_map.emplace(0).first; + } + long getTscFreq(); void *kalloc(std::size_t size, @@ -161,6 +171,8 @@ private: std::atomic m_tsc_freq{0}; + shared_mutex m_thread_id_mtx; + OwningIdMap m_thread_id_map; mutable shared_mutex m_proc_mtx; utils::LinkedNode *m_processes = nullptr; diff --git a/orbis-kernel/include/orbis/pipe.hpp b/orbis-kernel/include/orbis/pipe.hpp new file mode 100644 index 000000000..edb832938 --- /dev/null +++ b/orbis-kernel/include/orbis/pipe.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "KernelAllocator.hpp" +#include "file.hpp" +#include "utils/Rc.hpp" + +namespace orbis { +struct Pipe final : File { + kvector data; +}; + +Ref createPipe(); +} // namespace orbis diff --git a/orbis-kernel/include/orbis/thread/ProcessOps.hpp b/orbis-kernel/include/orbis/thread/ProcessOps.hpp index 86f408c0c..5ec8c87c3 100644 --- a/orbis-kernel/include/orbis/thread/ProcessOps.hpp +++ b/orbis-kernel/include/orbis/thread/ProcessOps.hpp @@ -70,6 +70,7 @@ struct ProcessOps { SysResult (*thr_wake)(Thread *thread, slong id); SysResult (*thr_set_name)(Thread *thread, slong id, ptr name); + SysResult (*fork)(Thread *thread, slong status); SysResult (*exit)(Thread *thread, sint status); SysResult (*processNeeded)(Thread *thread); diff --git a/orbis-kernel/include/orbis/utils/SharedCV.hpp b/orbis-kernel/include/orbis/utils/SharedCV.hpp index c75e9048a..c378a3539 100644 --- a/orbis-kernel/include/orbis/utils/SharedCV.hpp +++ b/orbis-kernel/include/orbis/utils/SharedCV.hpp @@ -7,7 +7,7 @@ namespace orbis { inline namespace utils { // IPC-ready lightweight condition variable -class shared_cv { +class shared_cv final { enum : unsigned { c_waiter_mask = 0xffff, c_signal_mask = 0x7fff0000, diff --git a/orbis-kernel/src/KernelContext.cpp b/orbis-kernel/src/KernelContext.cpp index 26a2689c2..a1407ee00 100644 --- a/orbis-kernel/src/KernelContext.cpp +++ b/orbis-kernel/src/KernelContext.cpp @@ -14,7 +14,7 @@ KernelContext &g_context = *[]() -> KernelContext * { auto ptr = mmap(reinterpret_cast(0x200'0000'0000), 0x1'0000'0000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - if (!ptr) + if (ptr == MAP_FAILED) std::abort(); return new (ptr) KernelContext; @@ -163,6 +163,9 @@ void KernelContext::kfree(void *ptr, std::size_t size) { ~(__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1); if (!size) std::abort(); + if ((uintptr_t)ptr == 0x2000001a2b0) { + std::fprintf(stderr, "free %p-%p (%zu)\n", ptr, (char *)ptr + size, size); + } std::memset(ptr, 0xcc, size); pthread_mutex_lock(&m_heap_mtx); diff --git a/orbis-kernel/src/pipe.cpp b/orbis-kernel/src/pipe.cpp new file mode 100644 index 000000000..bdc4edc85 --- /dev/null +++ b/orbis-kernel/src/pipe.cpp @@ -0,0 +1,61 @@ +#include "pipe.hpp" +#include "error/ErrorCode.hpp" +#include "file.hpp" +#include "uio.hpp" +#include +#include + +static orbis::ErrorCode pipe_read(orbis::File *file, orbis::Uio *uio, + orbis::Thread *thread) { + auto pipe = static_cast(file); + while (true) { + if (pipe->data.empty()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + std::lock_guard lock(pipe->mtx); + + if (pipe->data.empty()) { + continue; + } + + for (auto vec : std::span(uio->iov, uio->iovcnt)) { + auto size = std::min(pipe->data.size(), vec.len); + uio->offset += size; + std::memcpy(vec.base, pipe->data.data(), size); + + if (pipe->data.size() == size) { + break; + } + + std::memmove(pipe->data.data(), pipe->data.data() + size, + pipe->data.size() - size); + pipe->data.resize(pipe->data.size() - size); + } + + break; + } + return {}; +} + +static orbis::ErrorCode pipe_write(orbis::File *file, orbis::Uio *uio, + orbis::Thread *thread) { + auto pipe = static_cast(file); + std::lock_guard lock(pipe->mtx); + + for (auto vec : std::span(uio->iov, uio->iovcnt)) { + auto offset = pipe->data.size(); + pipe->data.resize(offset + vec.len); + std::memcpy(pipe->data.data(), vec.base, vec.len); + } + uio->resid = 0; + return {}; +} + +static orbis::FileOps pipe_ops = {.read = pipe_read, .write = pipe_write}; + +orbis::Ref orbis::createPipe() { + auto result = knew(); + result->ops = &pipe_ops; + return result; +} diff --git a/orbis-kernel/src/sys/sys_fork.cpp b/orbis-kernel/src/sys/sys_fork.cpp index 681d581f9..2747306e7 100644 --- a/orbis-kernel/src/sys/sys_fork.cpp +++ b/orbis-kernel/src/sys/sys_fork.cpp @@ -1,6 +1,5 @@ #include "KernelContext.hpp" #include "sys/sysproto.hpp" -#include "utils/Logs.hpp" #include #include @@ -11,42 +10,8 @@ orbis::SysResult orbis::sys_pdfork(Thread *thread, ptr fdp, sint flags) { orbis::SysResult orbis::sys_vfork(Thread *thread) { return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_rfork(Thread *thread, sint flags) { - ORBIS_LOG_TODO(__FUNCTION__, flags); - - int hostPid = ::fork(); - if (hostPid) { - thread->retval[0] = 10001; - thread->retval[1] = 0; - } else { - auto process = g_context.createProcess(10001); - std::lock_guard lock(thread->tproc->fileDescriptors.mutex); - process->sysent = thread->tproc->sysent; - process->onSysEnter = thread->tproc->onSysEnter; - process->onSysExit = thread->tproc->onSysExit; - process->ops = thread->tproc->ops; - process->isSystem = thread->tproc->isSystem; - for (auto [id, mod] : thread->tproc->modulesMap) { - if (!process->modulesMap.insert(id, mod)) { - std::abort(); - } - } - - for (auto [id, mod] : thread->tproc->fileDescriptors) { - if (!process->fileDescriptors.insert(id, mod)) { - std::abort(); - } - } - - auto [baseId, newThread] = process->threadsMap.emplace(); - newThread->tproc = process; - newThread->tid = process->pid + baseId; - newThread->state = orbis::ThreadState::RUNNING; - newThread->context = thread->context; - newThread->fsBase = thread->fsBase; - - orbis::g_currentThread = newThread; - newThread->retval[0] = 0; - newThread->retval[1] = 1; + if (auto fork = thread->tproc->ops->fork) { + return fork(thread, flags); } - return {}; + return ErrorCode::NOSYS; } diff --git a/orbis-kernel/src/sys/sys_pipe.cpp b/orbis-kernel/src/sys/sys_pipe.cpp index 411b3215b..82a6ba811 100644 --- a/orbis-kernel/src/sys/sys_pipe.cpp +++ b/orbis-kernel/src/sys/sys_pipe.cpp @@ -1,3 +1,9 @@ #include "sys/sysproto.hpp" +#include -orbis::SysResult orbis::sys_pipe(Thread *thread) { return ErrorCode::NOSYS; } +orbis::SysResult orbis::sys_pipe(Thread *thread) { + auto pipe = createPipe(); + thread->retval[0] = thread->tproc->fileDescriptors.insert(pipe); + thread->retval[1] = thread->tproc->fileDescriptors.insert(pipe); + return {}; +} diff --git a/rpcsx-os/CMakeLists.txt b/rpcsx-os/CMakeLists.txt index f64e343d6..242e64bab 100644 --- a/rpcsx-os/CMakeLists.txt +++ b/rpcsx-os/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(rpcsx-os iodev/hmd_snsr.cpp iodev/icc_configuration.cpp iodev/mbus.cpp + iodev/metadbg.cpp iodev/notification.cpp iodev/npdrm.cpp iodev/null.cpp diff --git a/rpcsx-os/io-devices.hpp b/rpcsx-os/io-devices.hpp index 4e250f1ad..d7bfd3735 100644 --- a/rpcsx-os/io-devices.hpp +++ b/rpcsx-os/io-devices.hpp @@ -28,3 +28,4 @@ IoDevice *createMBusCharacterDevice(); IoDevice *createBtCharacterDevice(); IoDevice *createXptCharacterDevice(); IoDevice *createCdCharacterDevice(); +IoDevice *createMetaDbgCharacterDevice(); diff --git a/rpcsx-os/iodev/dmem.cpp b/rpcsx-os/iodev/dmem.cpp index 37b348efc..dfb47a186 100644 --- a/rpcsx-os/iodev/dmem.cpp +++ b/rpcsx-os/iodev/dmem.cpp @@ -156,8 +156,8 @@ static orbis::ErrorCode dmem_ioctl(orbis::File *file, std::uint64_t request, } } - thread->where(); ORBIS_LOG_FATAL("Unhandled dmem ioctl", device->index, request); + thread->where(); return {}; } diff --git a/rpcsx-os/iodev/metadbg.cpp b/rpcsx-os/iodev/metadbg.cpp new file mode 100644 index 000000000..c1020f439 --- /dev/null +++ b/rpcsx-os/iodev/metadbg.cpp @@ -0,0 +1,41 @@ +#include "io-device.hpp" +#include "orbis/KernelAllocator.hpp" +#include "orbis/file.hpp" +#include "orbis/utils/Logs.hpp" +#include +#include + +struct MetaDbgFile : orbis::File {}; + +static orbis::ErrorCode metadbg_ioctl(orbis::File *file, std::uint64_t request, + void *argp, orbis::Thread *thread) { + + ORBIS_LOG_FATAL("Unhandled metadbg ioctl", request); + return {}; +} +static orbis::ErrorCode metadbg_read(orbis::File *file, orbis::Uio *uio, orbis::Thread *thread) { + ORBIS_LOG_TODO(__FUNCTION__); + + std::this_thread::sleep_for(std::chrono::days(1)); + return {}; +} + +static const orbis::FileOps fileOps = { + .ioctl = metadbg_ioctl, + .read = metadbg_read, +}; + +struct MetaDbgDevice : IoDevice { + orbis::ErrorCode open(orbis::Ref *file, const char *path, + std::uint32_t flags, std::uint32_t mode, + orbis::Thread *thread) override { + auto newFile = orbis::knew(); + newFile->ops = &fileOps; + newFile->device = this; + + *file = newFile; + return {}; + } +}; + +IoDevice *createMetaDbgCharacterDevice() { return orbis::knew(); } diff --git a/rpcsx-os/iodev/notification.cpp b/rpcsx-os/iodev/notification.cpp index 09a087b3d..13030d698 100644 --- a/rpcsx-os/iodev/notification.cpp +++ b/rpcsx-os/iodev/notification.cpp @@ -1,13 +1,19 @@ #include "io-device.hpp" #include "orbis/KernelAllocator.hpp" #include "orbis/file.hpp" +#include "orbis/uio.hpp" #include "orbis/utils/Logs.hpp" +#include "orbis/utils/SharedMutex.hpp" #include +#include +#include #include struct NotificationFile : orbis::File {}; struct NotificationDevice : IoDevice { int index; + orbis::shared_mutex mutex; + orbis::kvector data; NotificationDevice(int index) : index(index) {} orbis::ErrorCode open(orbis::Ref *file, const char *path, @@ -23,14 +29,57 @@ static orbis::ErrorCode notification_ioctl(orbis::File *file, std::uint64_t requ } static orbis::ErrorCode notification_read(orbis::File *file, orbis::Uio *uio, orbis::Thread *thread) { - ORBIS_LOG_FATAL("Unhandled notification_read"); - std::this_thread::sleep_for(std::chrono::hours(120)); + auto dev = dynamic_cast(file->device.get()); + ORBIS_LOG_FATAL(__FUNCTION__, dev->index); + + while (true) { + if (dev->data.empty()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + std::lock_guard lock(dev->mutex); + + if (dev->data.empty()) { + continue; + } + + for (auto vec : std::span(uio->iov, uio->iovcnt)) { + auto size = std::min(dev->data.size(), vec.len); + uio->offset += size; + std::memcpy(vec.base, dev->data.data(), size); + + if (dev->data.size() == size) { + break; + } + + std::memmove(dev->data.data(), dev->data.data() + size, dev->data.size() - size); + dev->data.resize(dev->data.size() - size); + } + + break; + } + return {}; +} + +static orbis::ErrorCode notification_write(orbis::File *file, orbis::Uio *uio, orbis::Thread *thread) { + auto dev = dynamic_cast(file->device.get()); + ORBIS_LOG_FATAL(__FUNCTION__, dev->index); + + std::lock_guard lock(dev->mutex); + + for (auto vec : std::span(uio->iov, uio->iovcnt)) { + auto offset = dev->data.size(); + dev->data.resize(offset + vec.len); + std::memcpy(dev->data.data(), vec.base, vec.len); + } + uio->resid = 0; return {}; } static const orbis::FileOps fileOps = { .ioctl = notification_ioctl, .read = notification_read, + .write = notification_write, }; orbis::ErrorCode NotificationDevice::open(orbis::Ref *file, const char *path, diff --git a/rpcsx-os/iodev/urandom.cpp b/rpcsx-os/iodev/urandom.cpp index 01239e342..e10e6cd3e 100644 --- a/rpcsx-os/iodev/urandom.cpp +++ b/rpcsx-os/iodev/urandom.cpp @@ -11,7 +11,7 @@ struct UrandomDevice : public IoDevice { }; struct UrandomFile : public orbis::File {}; -static orbis::ErrorCode zero_read(orbis::File *file, orbis::Uio *uio, +static orbis::ErrorCode urandom_read(orbis::File *file, orbis::Uio *uio, orbis::Thread *) { for (auto entry : std::span(uio->iov, uio->iovcnt)) { std::memset(entry.base, 0, entry.len); @@ -22,7 +22,7 @@ static orbis::ErrorCode zero_read(orbis::File *file, orbis::Uio *uio, } static const orbis::FileOps ops = { - .read = zero_read, + .read = urandom_read, }; orbis::ErrorCode UrandomDevice::open(orbis::Ref *file, diff --git a/rpcsx-os/main.cpp b/rpcsx-os/main.cpp index 1e7b9231a..e1a6e3f3a 100644 --- a/rpcsx-os/main.cpp +++ b/rpcsx-os/main.cpp @@ -355,6 +355,7 @@ static int ps4Exec(orbis::Thread *mainThread, rx::vfs::addDevice("ajm", createAjmCharacterDevice()); rx::vfs::addDevice("urandom", createUrandomCharacterDevice()); rx::vfs::addDevice("mbus", createMBusCharacterDevice()); + rx::vfs::addDevice("metadbg", createMetaDbgCharacterDevice()); rx::vfs::addDevice("bt", createBtCharacterDevice()); rx::vfs::addDevice("xpt0", createXptCharacterDevice()); rx::vfs::addDevice("cd0", createXptCharacterDevice()); @@ -645,8 +646,9 @@ int main(int argc, const char *argv[]) { } // rx::vm::printHostStats(); - auto initProcess = orbis::g_context.createProcess(asRoot ? 1 : 10); - pthread_setname_np(pthread_self(), "10.MAINTHREAD"); + orbis::g_context.allocatePid(); + auto initProcess = orbis::g_context.createProcess(asRoot ? 1 : 11); + pthread_setname_np(pthread_self(), "11.MAINTHREAD"); std::thread{[] { pthread_setname_np(pthread_self(), "Bridge"); diff --git a/rpcsx-os/ops.cpp b/rpcsx-os/ops.cpp index 720a16ea0..d4c875b2f 100644 --- a/rpcsx-os/ops.cpp +++ b/rpcsx-os/ops.cpp @@ -12,6 +12,8 @@ #include "orbis/umtx.hpp" #include "orbis/utils/Logs.hpp" #include "orbis/utils/Rc.hpp" +#include "orbis/utils/SharedCV.hpp" +#include "orbis/utils/SharedMutex.hpp" #include "orbis/vm.hpp" #include "thread.hpp" #include "vfs.hpp" @@ -19,15 +21,19 @@ #include #include #include +#include +#include #include #include #include +#include #include #include using namespace orbis; extern "C" void __register_frame(const void *); +void setupSigHandlers(); namespace { static std::pair> @@ -617,6 +623,95 @@ SysResult processNeeded(Thread *thread) { return {}; } +SysResult fork(Thread *thread, slong flags) { + ORBIS_LOG_TODO(__FUNCTION__, flags); + + auto childPid = g_context.allocatePid() * 10000 + 1; + auto mtx = knew(); + auto cv = knew(); + + int hostPid = ::fork(); + + if (hostPid) { + mtx->lock(); + cv->wait(*mtx); + + kdelete(cv); + kdelete(mtx); + + thread->retval[0] = childPid; + thread->retval[1] = 0; + return{}; + } + + auto process = g_context.createProcess(childPid); + process->sysent = thread->tproc->sysent; + process->onSysEnter = thread->tproc->onSysEnter; + process->onSysExit = thread->tproc->onSysExit; + process->ops = thread->tproc->ops; + process->isSystem = thread->tproc->isSystem; + process->parentProcess = thread->tproc; + for (auto [id, mod] : thread->tproc->modulesMap) { + if (!process->modulesMap.insert(id, mod)) { + std::abort(); + } + } + + if (false) { + std::lock_guard lock(thread->tproc->fileDescriptors.mutex); + for (auto [id, mod] : thread->tproc->fileDescriptors) { + if (!process->fileDescriptors.insert(id, mod)) { + std::abort(); + } + } + } + + rx::vm::fork(thread->tproc->pid); + rx::vfs::fork(); + + cv->notify_one(*mtx); + + auto [baseId, newThread] = process->threadsMap.emplace(); + newThread->tproc = process; + newThread->tid = process->pid + baseId; + newThread->state = orbis::ThreadState::RUNNING; + newThread->context = thread->context; + newThread->fsBase = thread->fsBase; + + orbis::g_currentThread = newThread; + newThread->retval[0] = 0; + newThread->retval[1] = 1; + + thread = orbis::g_currentThread; + + setupSigHandlers(); + rx::thread::initialize(); + + if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, + (void *)0x100'0000'0000, ~0ull - 0x100'0000'0000, nullptr)) { + perror("prctl failed\n"); + std::exit(-1); + } + + auto ttyFd = + ::open(("tty-" + std::to_string(thread->tproc->pid) + ".txt").c_str(), + O_CREAT | O_TRUNC | O_WRONLY, 0666); + auto logFd = + ::open(("log-" + std::to_string(thread->tproc->pid) + ".txt").c_str(), + O_CREAT | O_TRUNC | O_WRONLY, 0666); + + dup2(logFd, 1); + dup2(logFd, 2); + + auto tty = createFdWrapDevice(ttyFd); + + rx::vfs::addDevice("stdout", tty); + rx::vfs::addDevice("stderr", tty); + rx::vfs::addDevice("deci_stdout", tty); + rx::vfs::addDevice("deci_stderr", tty); + return{}; +} + SysResult registerEhFrames(Thread *thread) { for (auto [id, module] : thread->tproc->modulesMap) { if (module->ehFrame != nullptr) { @@ -670,6 +765,7 @@ ProcessOps rx::procOpsTable = { .thr_suspend = thr_suspend, .thr_wake = thr_wake, .thr_set_name = thr_set_name, + .fork = fork, .exit = exit, .processNeeded = processNeeded, .registerEhFrames = registerEhFrames, diff --git a/rpcsx-os/thread.cpp b/rpcsx-os/thread.cpp index a5c59f0c3..fa42f1c26 100644 --- a/rpcsx-os/thread.cpp +++ b/rpcsx-os/thread.cpp @@ -1,13 +1,12 @@ #include "thread.hpp" -#include "backtrace.hpp" #include "orbis/sys/sysentry.hpp" +#include "orbis/thread/Process.hpp" #include "orbis/thread/Thread.hpp" #include #include #include #include #include -#include #include #include #include @@ -33,8 +32,6 @@ static auto setContext = [] { return setContextStorage.getCode(); }(); -void setupSigHandlers(); - static __attribute__((no_stack_protector)) void handleSigSys(int sig, siginfo_t *info, void *ucontext) { if (auto hostFs = _readgsbase_u64()) { @@ -46,19 +43,7 @@ handleSigSys(int sig, siginfo_t *info, void *ucontext) { auto thread = orbis::g_currentThread; auto prevContext = std::exchange(thread->context, ucontext); orbis::syscall_entry(thread); - if (thread != orbis::g_currentThread) { - thread = orbis::g_currentThread; - - setupSigHandlers(); - rx::thread::initialize(); - - if (prctl(PR_SET_SYSCALL_USER_DISPATCH, PR_SYS_DISPATCH_ON, - (void *)0x100'0000'0000, ~0ull - 0x100'0000'0000, nullptr)) { - perror("prctl failed\n"); - exit(-1); - } - } - + thread = orbis::g_currentThread; thread->context = prevContext; _writefsbase_u64(thread->fsBase); } diff --git a/rpcsx-os/vfs.cpp b/rpcsx-os/vfs.cpp index db6ce7d68..b9974f6d6 100644 --- a/rpcsx-os/vfs.cpp +++ b/rpcsx-os/vfs.cpp @@ -9,8 +9,6 @@ #include #include -static std::map, std::greater<>> sMountsMap; - struct DevFs : IoDevice { std::map> devices; @@ -25,7 +23,6 @@ struct DevFs : IoDevice { return orbis::ErrorCode::NOENT; } }; -static orbis::Ref sDevFs; struct ProcFs : IoDevice { orbis::ErrorCode open(orbis::Ref *file, const char *path, @@ -37,19 +34,43 @@ struct ProcFs : IoDevice { } }; +static orbis::shared_mutex gMountMtx; +static std::map, std::greater<>> gMountsMap; +static orbis::Ref gDevFs; + +void rx::vfs::fork() { + std::lock_guard lock(gMountMtx); + + // NOTE: do not decrease reference counter, it managed by parent process + auto parentDevFs = gDevFs.release(); + + for (auto &mount : gMountsMap) { + mount.second->incRef(); // increase reference for new process + } + + gDevFs = orbis::knew(); + gMountsMap.emplace("/dev/", gDevFs); + gMountsMap.emplace("/proc/", orbis::knew()); + + for (auto &fs : parentDevFs->devices) { + gDevFs->devices[fs.first] = fs.second; + } +} + void rx::vfs::initialize() { - sDevFs = orbis::knew(); - sMountsMap.emplace("/dev/", sDevFs); - sMountsMap.emplace("/proc/", orbis::knew()); + gDevFs = orbis::knew(); + gMountsMap.emplace("/dev/", gDevFs); + gMountsMap.emplace("/proc/", orbis::knew()); } void rx::vfs::deinitialize() { - sDevFs = nullptr; - sMountsMap.clear(); + gDevFs = nullptr; + gMountsMap.clear(); } void rx::vfs::addDevice(std::string name, IoDevice *device) { - sDevFs->devices[std::move(name)] = device; + std::lock_guard lock(gMountMtx); + gDevFs->devices[std::move(name)] = device; } static std::pair, std::string> @@ -59,7 +80,9 @@ get(const std::filesystem::path &guestPath) { orbis::Ref device; std::string_view prefix; - for (auto &mount : sMountsMap) { + std::lock_guard lock(gMountMtx); + + for (auto &mount : gMountsMap) { if (!path.starts_with(mount.first)) { continue; } @@ -79,7 +102,9 @@ orbis::SysResult rx::vfs::mount(const std::filesystem::path &guestPath, mp += "/"; } - auto [it, inserted] = sMountsMap.emplace(std::move(mp), dev); + std::lock_guard lock(gMountMtx); + + auto [it, inserted] = gMountsMap.emplace(std::move(mp), dev); if (!inserted) { return orbis::ErrorCode::EXIST; diff --git a/rpcsx-os/vfs.hpp b/rpcsx-os/vfs.hpp index 6ba69a805..b342a77d2 100644 --- a/rpcsx-os/vfs.hpp +++ b/rpcsx-os/vfs.hpp @@ -8,6 +8,7 @@ struct IoDevice; namespace rx::vfs { +void fork(); void initialize(); void deinitialize(); void addDevice(std::string name, IoDevice *device); diff --git a/rpcsx-os/vm.cpp b/rpcsx-os/vm.cpp index 46b5e1bb2..72dfad33d 100644 --- a/rpcsx-os/vm.cpp +++ b/rpcsx-os/vm.cpp @@ -634,6 +634,45 @@ static void reserve(std::uint64_t startAddress, std::uint64_t endAddress) { gBlocks[blockIndex - kFirstBlock].setFlags(firstPage, pagesCount, kAllocated); } +void rx::vm::fork(std::uint64_t pid) { + gMemoryShm = ::shm_open(("/rpcsx-os-memory-" + std::to_string(pid)).c_str(), + O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + + if (gMemoryShm == -1) { + std::fprintf(stderr, "Memory: failed to open /rpcsx-os-memory\n"); + std::abort(); + } + + if (::ftruncate64(gMemoryShm, kMemorySize) < 0) { + std::fprintf(stderr, "Memory: failed to allocate /rpcsx-os-memory\n"); + std::abort(); + } + + for (auto address = kMinAddress; address < kMaxAddress; + address += kPageSize) { + auto prot = gBlocks[(address >> kBlockShift) - kFirstBlock].getProtection( + (address & kBlockMask) >> rx::vm::kPageShift); + + if (prot & kMapProtCpuAll) { + auto mapping = utils::map(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, + gMemoryShm, address - kMinAddress); + assert(mapping != MAP_FAILED); + + utils::protect(reinterpret_cast(address), kPageSize, PROT_READ); + std::memcpy(mapping, reinterpret_cast(address), kPageSize); + utils::unmap(mapping, kPageSize); + utils::unmap(reinterpret_cast(address), kPageSize); + + mapping = utils::map(reinterpret_cast(address), kPageSize, + prot & kMapProtCpuAll, MAP_FIXED | MAP_SHARED, + gMemoryShm, address - kMinAddress); + assert(mapping != MAP_FAILED); + } + + // TODO: copy gpu memory? + } +} + void rx::vm::initialize() { std::printf("Memory: initialization\n"); diff --git a/rpcsx-os/vm.hpp b/rpcsx-os/vm.hpp index 04e4461fa..850c6e9f0 100644 --- a/rpcsx-os/vm.hpp +++ b/rpcsx-os/vm.hpp @@ -67,6 +67,7 @@ std::string mapFlagsToString(std::int32_t flags); std::string mapProtToString(std::int32_t prot); void printHostStats(); +void fork(std::uint64_t pid); void initialize(); void deinitialize(); void *map(void *addr, std::uint64_t len, std::int32_t prot, std::int32_t flags,