#include "ops.hpp" #include "io-device.hpp" #include "linker.hpp" #include "orbis/module/ModuleHandle.hpp" #include "orbis/thread/Process.hpp" #include "orbis/thread/Thread.hpp" #include "orbis/utils/Rc.hpp" #include "vfs.hpp" #include "vm.hpp" #include #include #include #include #include #include using namespace orbis; extern "C" void __register_frame(const void *); namespace { orbis::SysResult mmap(orbis::Thread *thread, orbis::caddr_t addr, orbis::size_t len, orbis::sint prot, orbis::sint flags, orbis::sint fd, orbis::off_t pos) { auto result = (void *)-1; if (fd == -1) { result = rx::vm::map(addr, len, prot, flags); } else { Ref handle = static_cast(thread->tproc->fileDescriptors.get(fd)); if (handle == nullptr) { return ErrorCode::BADF; } result = handle->mmap(handle.get(), addr, len, prot, flags, pos); } if (result == (void *)-1) { return ErrorCode::NOMEM; } thread->retval[0] = reinterpret_cast(result); return{}; } orbis::SysResult munmap(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len) { return ErrorCode::INVAL; } orbis::SysResult msync(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len, orbis::sint flags) { return {}; } orbis::SysResult mprotect(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len, orbis::sint prot) { rx::vm::protect((void *)addr, len, prot); return {}; } orbis::SysResult minherit(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len, orbis::sint inherit) { return ErrorCode::INVAL; } orbis::SysResult madvise(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len, orbis::sint behav) { return ErrorCode::INVAL; } orbis::SysResult mincore(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len, orbis::ptr vec) { return ErrorCode::INVAL; } orbis::SysResult mlock(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len) { return {}; } orbis::SysResult mlockall(orbis::Thread *thread, orbis::sint how) { return {}; } orbis::SysResult munlockall(orbis::Thread *thread) { return {}; } orbis::SysResult munlock(orbis::Thread *thread, orbis::ptr addr, orbis::size_t len) { return {}; } orbis::SysResult virtual_query(orbis::Thread *thread, orbis::ptr addr, orbis::sint flags, orbis::ptr info, orbis::ulong infoSize) { if (infoSize != sizeof(rx::vm::VirtualQueryInfo)) { return ErrorCode::INVAL; } if (!rx::vm::virtualQuery(addr, flags, (rx::vm::VirtualQueryInfo *)info)) { return ErrorCode::FAULT; } return {}; } orbis::SysResult open(orbis::Thread *thread, orbis::ptr path, orbis::sint flags, orbis::sint mode) { std::printf("sys_open(%s)\n", path); orbis::Ref instance; auto result = rx::vfs::open(path, flags, mode, &instance); if (result.isError()) { return result; } thread->retval[0] = thread->tproc->fileDescriptors.insert(instance); return {}; } orbis::SysResult close(orbis::Thread *thread, orbis::sint fd) { if (!thread->tproc->fileDescriptors.remove(fd)) { return ErrorCode::BADF; } return {}; } #define IOCPARM_SHIFT 13 /* number of bits for ioctl size */ #define IOCPARM_MASK ((1 << IOCPARM_SHIFT) - 1) /* parameter length mask */ #define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK) #define IOCBASECMD(x) ((x) & ~(IOCPARM_MASK << 16)) #define IOCGROUP(x) (((x) >> 8) & 0xff) #define IOCPARM_MAX (1 << IOCPARM_SHIFT) /* max size of ioctl */ #define IOC_VOID 0x20000000 /* no parameters */ #define IOC_OUT 0x40000000 /* copy out parameters */ #define IOC_IN 0x80000000 /* copy in parameters */ #define IOC_INOUT (IOC_IN | IOC_OUT) #define IOC_DIRMASK (IOC_VOID | IOC_OUT | IOC_IN) #define _IOC(inout, group, num, len) \ ((unsigned long)((inout) | (((len)&IOCPARM_MASK) << 16) | ((group) << 8) | \ (num))) #define _IO(g, n) _IOC(IOC_VOID, (g), (n), 0) #define _IOWINT(g, n) _IOC(IOC_VOID, (g), (n), sizeof(int)) #define _IOR(g, n, t) _IOC(IOC_OUT, (g), (n), sizeof(t)) #define _IOW(g, n, t) _IOC(IOC_IN, (g), (n), sizeof(t)) /* this should be _IORW, but stdio got there first */ #define _IOWR(g, n, t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) static std::string iocGroupToString(unsigned iocGroup) { if (iocGroup >= 128) { const char *sceGroups[] = { "DEV", "DMEM", "GC", "DCE", "UVD", "VCE", "DBGGC", "TWSI", "MDBG", "DEVENV", "AJM", "TRACE", "IBS", "MBUS", "HDMI", "CAMERA", "FAN", "THERMAL", "PFS", "ICC_CONFIG", "IPC", "IOSCHED", "ICC_INDICATOR", "EXFATFS", "ICC_NVS", "DVE", "ICC_POWER", "AV_CONTROL", "ICC_SC_CONFIGURATION", "ICC_DEVICE_POWER", "SSHOT", "DCE_SCANIN", "FSCTRL", "HMD", "SHM", "PHYSHM", "HMDDFU", "BLUETOOTH_HID", "SBI", "S3DA", "SPM", "BLOCKPOOL", "SDK_EVENTLOG", }; if (iocGroup - 127 >= std::size(sceGroups)) { return "'?'"; } return sceGroups[iocGroup - 127]; } if (isprint(iocGroup)) { return "'" + std::string(1, (char)iocGroup) + "'"; } return "'?'"; } static void printIoctl(unsigned long arg) { std::printf("0x%lx { IO%s%s %lu(%s), %lu, %lu }\n", arg, arg & IOC_OUT ? "R" : "", arg & IOC_IN ? "W" : "", IOCGROUP(arg), iocGroupToString(IOCGROUP(arg)).c_str(), arg & 0xFF, IOCPARM_LEN(arg)); } static void ioctlToStream(std::ostream &stream, unsigned long arg) { stream << "0x" << std::hex << arg << " { IO"; if ((arg & IOC_OUT) != 0) { stream << 'R'; } if ((arg & IOC_IN) != 0) { stream << 'W'; } if ((arg & IOC_VOID) != 0) { stream << 'i'; } stream << " 0x" << IOCGROUP(arg); stream << "('" << iocGroupToString(IOCGROUP(arg)) << "'), "; stream << std::dec << (arg & 0xFF) << ", " << IOCPARM_LEN(arg) << " }"; } static std::string ioctlToString(unsigned long arg) { std::ostringstream stream; ioctlToStream(stream, arg); return std::move(stream).str(); } orbis::SysResult ioctl(orbis::Thread *thread, orbis::sint fd, orbis::ulong com, orbis::caddr_t argp) { std::printf("ioctl: %s\n", ioctlToString(com).c_str()); Ref handle = static_cast(thread->tproc->fileDescriptors.get(fd)); if (handle == nullptr) { return ErrorCode::BADF; } auto result = handle->ioctl(handle.get(), com, argp); if (result < 0) { // TODO return ErrorCode::IO; } thread->retval[0] = result; return {}; } orbis::SysResult write(orbis::Thread *thread, orbis::sint fd, orbis::ptr data, orbis::ulong size) { Ref handle = static_cast(thread->tproc->fileDescriptors.get(fd)); if (handle == nullptr) { return ErrorCode::BADF; } auto result = handle->write(handle.get(), data, size); if (result < 0) { // TODO return ErrorCode::IO; } thread->retval[0] = result; return {}; } orbis::SysResult read(orbis::Thread *thread, orbis::sint fd, orbis::ptr data, orbis::ulong size) { return ErrorCode::NOTSUP; } orbis::SysResult pread(orbis::Thread *thread, orbis::sint fd, orbis::ptr data, orbis::ulong size, orbis::ulong offset) { return ErrorCode::NOTSUP; } orbis::SysResult pwrite(orbis::Thread *thread, orbis::sint fd, orbis::ptr data, orbis::ulong size, orbis::ulong offset) { return ErrorCode::NOTSUP; } orbis::SysResult lseek(orbis::Thread *thread, orbis::sint fd, orbis::ulong offset, orbis::sint whence) { return ErrorCode::NOTSUP; } orbis::SysResult ftruncate(orbis::Thread *thread, orbis::sint fd, orbis::off_t length) { return ErrorCode::NOTSUP; } orbis::SysResult truncate(orbis::Thread *thread, orbis::ptr path, orbis::off_t length) { return ErrorCode::NOTSUP; } orbis::SysResult dynlib_get_obj_member(orbis::Thread *thread, orbis::ModuleHandle handle, orbis::uint64_t index, orbis::ptr> addrp) { auto module = thread->tproc->modulesMap.get(handle); if (module == nullptr) { return ErrorCode::INVAL; } switch (index) { case 1: *addrp = module->initProc; return {}; case 8: *addrp = module->moduleParam; return {}; } return ErrorCode::INVAL; } ptr findSymbolById(orbis::Module *module, std::uint64_t id) { for (auto sym : module->symbols) { if (sym.id == id && sym.bind != orbis::SymbolBind::Local) { return (ptr)module->base + sym.address; } } return nullptr; } orbis::SysResult dynlib_dlsym(orbis::Thread *thread, orbis::ModuleHandle handle, orbis::ptr symbol, orbis::ptr> addrp) { std::printf("sys_dynlib_dlsym(%u, '%s')\n", (unsigned)handle, symbol); auto module = thread->tproc->modulesMap.get(handle); if (module == nullptr) { return ErrorCode::INVAL; } std::string_view symView(symbol); if (symView.size() == 11) { if (auto addr = findSymbolById(module, rx::linker::decodeNid(symView))) { *addrp = addr; return {}; } } if (auto addr = findSymbolById(module, rx::linker::encodeFid(symView))) { *addrp = addr; return {}; } return ErrorCode::NOENT; } orbis::SysResult dynlib_do_copy_relocations(orbis::Thread *thread) { // TODO return {}; } orbis::SysResult dynlib_load_prx(orbis::Thread *thread, orbis::ptr name, orbis::uint64_t arg1, orbis::ptr pHandle, orbis::uint64_t arg3) { std::printf("sys_dynlib_load_prx: %s\n", name); auto module = rx::linker::loadModuleFile(name, thread->tproc); thread->tproc->ops->processNeeded(thread); auto result = module->relocate(thread->tproc); if (result.isError()) { thread->tproc->modulesMap.remove(module->id); return result; } *pHandle = module->id; return {}; } orbis::SysResult dynlib_unload_prx(orbis::Thread *thread, orbis::ModuleHandle handle) { return ErrorCode::NOTSUP; } SysResult thr_create(orbis::Thread *thread, orbis::ptr ctxt, ptr arg, orbis::sint flags) { return ErrorCode::NOTSUP; } SysResult thr_new(orbis::Thread *thread, orbis::ptr param, orbis::sint param_size) { return {}; } SysResult thr_exit(orbis::Thread *thread, orbis::ptr state) { return ErrorCode::NOTSUP; } SysResult thr_kill(orbis::Thread *thread, orbis::slong id, orbis::sint sig) { return ErrorCode::NOTSUP; } SysResult thr_kill2(orbis::Thread *thread, orbis::pid_t pid, orbis::slong id, orbis::sint sig) { return ErrorCode::NOTSUP; } SysResult thr_suspend(orbis::Thread *thread, orbis::ptr timeout) { return ErrorCode::NOTSUP; } SysResult thr_wake(orbis::Thread *thread, orbis::slong id) { return ErrorCode::NOTSUP; } SysResult thr_set_name(orbis::Thread *thread, orbis::slong id, orbis::ptr name) { return ErrorCode::NOTSUP; } orbis::SysResult exit(orbis::Thread *thread, orbis::sint status) { std::printf("Requested exit with status %d\n", status); std::exit(status); } SysResult processNeeded(Thread *thread) { while (true) { std::set allNeededModules; auto proc = thread->tproc; std::map loadedModules; for (auto [id, module] : proc->modulesMap) { for (auto mod : module->neededModules) { allNeededModules.insert(mod.name); } loadedModules[module->name] = module; } bool hasLoadedNeeded = false; for (auto &needed : allNeededModules) { if (auto it = loadedModules.find(needed); it != loadedModules.end()) { continue; } hasLoadedNeeded = true; std::printf("loading needed: %s\n", needed.c_str()); bool isLoaded = false; for (auto path : {"/system/common/lib/", "/system/priv/lib/"}) { auto loadedNeeded = rx::linker::loadModuleFile( (std::string(path) + needed + ".sprx").c_str(), proc); if (loadedNeeded == nullptr) { continue; } isLoaded = true; break; } if (!isLoaded) { std::printf("Needed '%s' not found\n", needed.c_str()); return ErrorCode::NOENT; } } if (!hasLoadedNeeded) { thread->tproc->modulesMap.walk( [&loadedModules](ModuleHandle modId, Module *module) { // std::printf("Module '%s' has id %u\n", module->name, // (unsigned)modId); module->importedModules.clear(); module->importedModules.reserve(module->neededModules.size()); for (auto mod : module->neededModules) { module->importedModules.push_back(loadedModules.at(mod.name)); } }); break; } } return {}; } SysResult registerEhFrames(Thread *thread) { for (auto [id, module] : thread->tproc->modulesMap) { __register_frame(module->ehFrameHdr); } return {}; } } // namespace ProcessOps rx::procOpsTable = { .mmap = mmap, .munmap = munmap, .msync = msync, .mprotect = mprotect, .minherit = minherit, .madvise = madvise, .mincore = mincore, .mlock = mlock, .mlockall = mlockall, .munlockall = munlockall, .munlock = munlock, .virtual_query = virtual_query, .open = open, .close = close, .ioctl = ioctl, .write = write, .read = read, .pread = pread, .pwrite = pwrite, .lseek = lseek, .ftruncate = ftruncate, .truncate = truncate, .dynlib_get_obj_member = dynlib_get_obj_member, .dynlib_dlsym = dynlib_dlsym, .dynlib_do_copy_relocations = dynlib_do_copy_relocations, .dynlib_load_prx = dynlib_load_prx, .dynlib_unload_prx = dynlib_unload_prx, .thr_create = thr_create, .thr_new = thr_new, .thr_exit = thr_exit, .thr_kill = thr_kill, .thr_kill2 = thr_kill2, .thr_suspend = thr_suspend, .thr_wake = thr_wake, .thr_set_name = thr_set_name, .exit = exit, .processNeeded = processNeeded, .registerEhFrames = registerEhFrames, };