From 10087f77575f7017510fa7e0d9195ea4636190e3 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 15 May 2020 02:58:34 -0700 Subject: [PATCH] Implements initial 32bit syscall support This adds the initial 32bit syscalls for testing. No where near complete --- External/FEXCore/Source/CMakeLists.txt | 4 + .../FEXCore/Source/Interface/HLE/Syscalls.cpp | 7 +- .../FEXCore/Source/Interface/HLE/x32/FD.cpp | 29 ++++ .../FEXCore/Source/Interface/HLE/x32/FD.h | 22 +++ .../Source/Interface/HLE/x32/Memory.cpp | 50 +++++++ .../FEXCore/Source/Interface/HLE/x32/Memory.h | 12 ++ .../Source/Interface/HLE/x32/Syscalls.cpp | 137 ++++++++++++++++++ .../Source/Interface/HLE/x32/Syscalls.h | 47 ++++++ .../Source/Interface/HLE/x32/Thread.cpp | 34 +++++ .../FEXCore/Source/Interface/HLE/x32/Thread.h | 23 +++ 10 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 External/FEXCore/Source/Interface/HLE/x32/FD.cpp create mode 100644 External/FEXCore/Source/Interface/HLE/x32/FD.h create mode 100644 External/FEXCore/Source/Interface/HLE/x32/Memory.cpp create mode 100644 External/FEXCore/Source/Interface/HLE/x32/Memory.h create mode 100644 External/FEXCore/Source/Interface/HLE/x32/Syscalls.cpp create mode 100644 External/FEXCore/Source/Interface/HLE/x32/Syscalls.h create mode 100644 External/FEXCore/Source/Interface/HLE/x32/Thread.cpp create mode 100644 External/FEXCore/Source/Interface/HLE/x32/Thread.h diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index b2a59e063..90bd5480b 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -78,6 +78,10 @@ set (SRCS Interface/HLE/FileManagement.cpp Interface/HLE/EmulatedFiles/EmulatedFiles.cpp Interface/HLE/Syscalls.cpp + Interface/HLE/x32/Syscalls.cpp + Interface/HLE/x32/FD.cpp + Interface/HLE/x32/Memory.cpp + Interface/HLE/x32/Thread.cpp Interface/HLE/x64/Syscalls.cpp Interface/HLE/Syscalls/EPoll.cpp Interface/HLE/Syscalls/FD.cpp diff --git a/External/FEXCore/Source/Interface/HLE/Syscalls.cpp b/External/FEXCore/Source/Interface/HLE/Syscalls.cpp index 3ef630181..24f17314a 100644 --- a/External/FEXCore/Source/Interface/HLE/Syscalls.cpp +++ b/External/FEXCore/Source/Interface/HLE/Syscalls.cpp @@ -4,6 +4,7 @@ #include "Interface/Core/InternalThreadState.h" #include "Interface/HLE/Syscalls.h" #include "Interface/HLE/x64/Syscalls.h" +#include "Interface/HLE/x32/Syscalls.h" #include "LogManager.h" @@ -147,9 +148,9 @@ SyscallHandler *CreateHandler(Context::OperatingMode Mode, FEXCore::Context::Con if (Mode == Context::MODE_64BIT) { return FEXCore::HLE::x64::CreateHandler(ctx); } - - LogMan::Msg::A("Don't yet handle 32bit syscalls"); - return nullptr; + else { + return FEXCore::HLE::x32::CreateHandler(ctx); + } } uint64_t HandleSyscall(SyscallHandler *Handler, FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args) { diff --git a/External/FEXCore/Source/Interface/HLE/x32/FD.cpp b/External/FEXCore/Source/Interface/HLE/x32/FD.cpp new file mode 100644 index 000000000..a8b27e7cc --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/FD.cpp @@ -0,0 +1,29 @@ +#include "Interface/HLE/Syscalls.h" +#include "Interface/HLE/x32/FD.h" +#include "Interface/Context/Context.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace FEXCore::Core { +struct InternalThreadState; +} + +namespace FEXCore::HLE::x32 { + uint32_t Writev(FEXCore::Core::InternalThreadState *Thread, int fd, const struct iovec_32 *iov, int iovcnt) { + std::vector Host_iovec(iovcnt); + for (int i = 0; i < iovcnt; ++i) { + Host_iovec[i].iov_base = reinterpret_cast(iov[i].iov_base); + Host_iovec[i].iov_len = iov[i].iov_len; + } + uint64_t Result = ::writev(fd, &Host_iovec.at(0), iovcnt); + SYSCALL_ERRNO(); + } +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/FD.h b/External/FEXCore/Source/Interface/HLE/x32/FD.h new file mode 100644 index 000000000..8e16a0bd3 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/FD.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Interface/HLE/Syscalls.h" + +#include +#include +#include +#include +#include + +namespace FEXCore::Core { +struct InternalThreadState; +} + +namespace FEXCore::HLE::x32 { + struct iovec_32 { + uint32_t iov_base; + uint32_t iov_len; + }; + + uint32_t Writev(FEXCore::Core::InternalThreadState *Thread, int fd, const struct iovec_32 *iov, int iovcnt); +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/Memory.cpp b/External/FEXCore/Source/Interface/HLE/x32/Memory.cpp new file mode 100644 index 000000000..a1f75fdb9 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/Memory.cpp @@ -0,0 +1,50 @@ +#include "Interface/HLE/Syscalls.h" +#include "Interface/Context/Context.h" +#include "Interface/Core/InternalThreadState.h" + +#include +#include +#include + +namespace FEXCore::Core { +struct InternalThreadState; +} + +#define MEM_PASSTHROUGH +namespace FEXCore::HLE::x32 { + uint32_t Mmap(FEXCore::Core::InternalThreadState *Thread, void *addr, size_t length, int prot, int flags, int fd, off_t offset) { +#ifdef MAP_32BIT + uint64_t Result = reinterpret_cast(::mmap(addr, length, prot, flags, fd, offset)); + SYSCALL_ERRNO(); +#endif + +#ifdef MEM_PASSTHROUGH + if (!(flags & MAP_FIXED)) { + uint64_t Result = reinterpret_cast(::mmap(addr, length, prot, flags, fd, offset)); + SYSCALL_ERRNO(); + } + else { + // Okay, MAP_32BIT doesn't exist here + // We need to spin through the lower 32bit and try and find a free address + // In the future we should definitely use a bitmap allocator to understand where we can place pages + + // Skip the first page since we can never allocate that + static uint64_t StartingPage = Core::PAGE_SIZE; + constexpr uint64_t Max4GB = 0x1'0000'0000; + flags |= MAP_FIXED_NOREPLACE; + uint64_t Result{}; + for (; StartingPage < Max4GB; StartingPage += Core::PAGE_SIZE) { + Result = reinterpret_cast(::mmap(reinterpret_cast(StartingPage), length, prot, flags, fd, offset)); + if (Result != ~0ULL) { + return Result; + } + } + // If we get here then we are out of memory + return -ENOMEM; + } +#else + return Thread->CTX->SyscallHandler->HandleMMAP(Thread, addr, length, prot, flags, fd, offset); +#endif + } + +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/Memory.h b/External/FEXCore/Source/Interface/HLE/x32/Memory.h new file mode 100644 index 000000000..367759c48 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/Memory.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include + +namespace FEXCore::Core { +struct InternalThreadState; +} + +namespace FEXCore::HLE::x32 { + uint32_t Mmap(FEXCore::Core::InternalThreadState *Thread, void *addr, size_t length, int prot, int flags, int fd, off_t offset); +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/Syscalls.cpp b/External/FEXCore/Source/Interface/HLE/x32/Syscalls.cpp new file mode 100644 index 000000000..b32977559 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/Syscalls.cpp @@ -0,0 +1,137 @@ +#include "Interface/Context/Context.h" + +#include "Interface/HLE/Syscalls.h" +#include "Interface/HLE/x32/Syscalls.h" +#include "Interface/HLE/x32/FD.h" +#include "Interface/HLE/x32/Memory.h" +#include "Interface/HLE/x32/Thread.h" + +#include "Interface/HLE/Syscalls/EPoll.h" +#include "Interface/HLE/Syscalls/FD.h" +#include "Interface/HLE/Syscalls/FS.h" +#include "Interface/HLE/Syscalls/Info.h" +#include "Interface/HLE/Syscalls/Ioctl.h" +#include "Interface/HLE/Syscalls/Memory.h" +#include "Interface/HLE/Syscalls/Numa.h" +#include "Interface/HLE/Syscalls/Sched.h" +#include "Interface/HLE/Syscalls/Semaphore.h" +#include "Interface/HLE/Syscalls/SHM.h" +#include "Interface/HLE/Syscalls/Signals.h" +#include "Interface/HLE/Syscalls/Socket.h" +#include "Interface/HLE/Syscalls/Thread.h" +#include "Interface/HLE/Syscalls/Time.h" +#include "Interface/HLE/Syscalls/Timer.h" + +#include "LogManager.h" + +namespace { + uint64_t Unimplemented(FEXCore::Core::InternalThreadState *Thread) { + LogMan::Msg::A("Unhandled system call"); + return -1; + } + + uint64_t NopSuccess(FEXCore::Core::InternalThreadState *Thread) { + return 0; + } + uint64_t NopFail(FEXCore::Core::InternalThreadState *Thread) { + return -1ULL; + } +} + + +namespace FEXCore::HLE::x32 { + +class x32SyscallHandler final : public FEXCore::SyscallHandler { +public: + x32SyscallHandler(FEXCore::Context::Context *ctx); + + // In the case that the syscall doesn't hit the optimized path then we still need to go here + uint64_t HandleSyscall(FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args) override; + +#ifdef DEBUG_STRACE + void Strace(FEXCore::HLE::SyscallArguments *Args, uint64_t Ret) override; +#endif + +private: + void RegisterSyscallHandlers(); +}; + +#ifdef DEBUG_STRACE +void x32SyscallHandler::Strace(FEXCore::HLE::SyscallArguments *Args, uint64_t Ret) { +} +#endif + +x32SyscallHandler::x32SyscallHandler(FEXCore::Context::Context *ctx) + : SyscallHandler {ctx} { + RegisterSyscallHandlers(); +} + +void x32SyscallHandler::RegisterSyscallHandlers() { + Definitions.resize(FEXCore::HLE::x32::SYSCALL_MAX); + auto cvt = [](auto in) { + union { + decltype(in) val; + void *raw; + } raw; + raw.val = in; + return raw.raw; + }; + + // Clear all definitions + for (auto &Def : Definitions) { + Def.NumArgs = 255; + Def.Ptr = cvt(&Unimplemented); + } + + const std::vector> Syscalls = { + {SYSCALL_READ, cvt(&FEXCore::HLE::Read), 3}, + {SYSCALL_EXIT, cvt(&FEXCore::HLE::Exit), 1}, + {SYSCALL_WRITE, cvt(&FEXCore::HLE::Write), 3}, + {SYSCALL_CLOSE, cvt(&FEXCore::HLE::Close), 1}, + {SYSCALL_ACCESS, cvt(&FEXCore::HLE::Access), 2}, + {SYSCALL_BRK, cvt(&FEXCore::HLE::Brk), 1}, + {SYSCALL_WRITEV, cvt(&FEXCore::HLE::x32::Writev), 3}, + {SYSCALL_MMAP2, cvt(&FEXCore::HLE::x32::Mmap), 6}, + {SYSCALL_GETUID32, cvt(&FEXCore::HLE::Getuid), 0}, + {SYSCALL_GETGID32, cvt(&FEXCore::HLE::Getgid), 0}, + {SYSCALL_GETEUID32, cvt(&FEXCore::HLE::Geteuid), 0}, + {SYSCALL_GETEGID32, cvt(&FEXCore::HLE::Getegid), 0}, + {SYSCALL_FCNTL64, cvt(&FEXCore::HLE::Fcntl), 3}, + {SYSCALL_SET_THREAD_AREA, cvt(&FEXCore::HLE::x32::Set_thread_area), 1}, + {SYSCALL_EXIT_GROUP, cvt(&FEXCore::HLE::Exit_group), 1}, + {SYSCALL_ARCH_PRCTL, cvt(&FEXCore::HLE::Arch_Prctl), 2}, + {SYSCALL_OPENAT, cvt(&FEXCore::HLE::Openat), 4}, + }; + + // Set all the new definitions + for (auto &Syscall : Syscalls) { + auto &Def = Definitions.at(std::get<0>(Syscall)); + LogMan::Throw::A(Def.Ptr == cvt(&Unimplemented), "Oops overwriting sysall problem"); + Def.Ptr = std::get<1>(Syscall); + Def.NumArgs = std::get<2>(Syscall); + } +} + +uint64_t x32SyscallHandler::HandleSyscall(FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args) { + auto &Def = Definitions[Args->Argument[0]]; + //LogMan::Msg::D(">>>>>Syscall: %d", Args->Argument[0]); + switch (Def.NumArgs) { + case 0: return std::invoke(Def.Ptr0, Thread); + case 1: return std::invoke(Def.Ptr1, Thread, Args->Argument[1]); + case 2: return std::invoke(Def.Ptr2, Thread, Args->Argument[1], Args->Argument[2]); + case 3: return std::invoke(Def.Ptr3, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3]); + case 4: return std::invoke(Def.Ptr4, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4]); + case 5: return std::invoke(Def.Ptr5, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5]); + case 6: return std::invoke(Def.Ptr6, Thread, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5], Args->Argument[6]); + default: break; + } + + LogMan::Msg::A("Unhandled syscall: %d", Args->Argument[0]); + return -1; +} + +FEXCore::SyscallHandler *CreateHandler(FEXCore::Context::Context *ctx) { + return new x32SyscallHandler(ctx); +} + +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/Syscalls.h b/External/FEXCore/Source/Interface/HLE/x32/Syscalls.h new file mode 100644 index 000000000..45a9d4792 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/Syscalls.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Interface/HLE/FileManagement.h" +#include + +#include +#include +#include +#include + +namespace FEXCore::Core { +struct InternalThreadState; +} + +namespace FEXCore { +class SyscallHandler; +} + +namespace FEXCore::HLE::x32 { + +///< Enum containing all supported x86 linux syscalls that we support +enum Syscalls { + SYSCALL_EXIT = 1, + SYSCALL_READ = 3, + SYSCALL_WRITE = 4, + SYSCALL_CLOSE = 6, + SYSCALL_ACCESS = 33, + SYSCALL_BRK = 45, + SYSCALL_WRITEV = 146, + SYSCALL_MMAP2 = 192, + SYSCALL_GETUID32 = 199, + SYSCALL_GETGID32 = 200, + SYSCALL_GETEUID32 = 201, + SYSCALL_GETEGID32 = 202, + SYSCALL_SETREUID32 = 203, + SYSCALL_SETREGID32 = 204, + SYSCALL_FCNTL64 = 221, + SYSCALL_SET_THREAD_AREA = 243, + SYSCALL_EXIT_GROUP = 252, + SYSCALL_OPENAT = 295, + SYSCALL_ARCH_PRCTL = 384, + SYSCALL_MAX = 512, +}; + +FEXCore::SyscallHandler *CreateHandler(FEXCore::Context::Context *ctx); + +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/Thread.cpp b/External/FEXCore/Source/Interface/HLE/x32/Thread.cpp new file mode 100644 index 000000000..70059344c --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/Thread.cpp @@ -0,0 +1,34 @@ +#include "Interface/Context/Context.h" +#include "Interface/Core/InternalThreadState.h" +#include "Interface/HLE/Syscalls.h" +#include "Interface/HLE/x32/Thread.h" + +#include + +namespace FEXCore::HLE::x32 { + uint32_t Set_thread_area(FEXCore::Core::InternalThreadState *Thread, struct user_desc *u_info) { + LogMan::Msg::D("Set_thread_area"); + LogMan::Msg::D("\tentry_number: %d", u_info->entry_number); + LogMan::Msg::D("\tbase_addr: 0x%x", u_info->base_addr); + LogMan::Msg::D("\tlimit: 0x%x", u_info->limit); + LogMan::Msg::D("\tseg_32bit: %d", u_info->seg_32bit); + LogMan::Msg::D("\tcontents: %d", u_info->contents); + LogMan::Msg::D("\tread_exec_only: %d", u_info->read_exec_only); + LogMan::Msg::D("\tlimit_in_pages: %d", u_info->limit_in_pages); + LogMan::Msg::D("\tseg_not_present: %d", u_info->seg_not_present); + LogMan::Msg::D("\tuseable: %d", u_info->useable); + + static bool Initialized = false; + if (Initialized == true) { + LogMan::Msg::A("Trying to load a new GDT"); + } + if (u_info->entry_number == -1) { + u_info->entry_number = 12; // Sure? + Initialized = true; + } + // Now we need to update the thread's GDT to handle this change + auto GDT = &Thread->State.State.gdt[u_info->entry_number]; + GDT->base = u_info->base_addr; + return 0; + } +} diff --git a/External/FEXCore/Source/Interface/HLE/x32/Thread.h b/External/FEXCore/Source/Interface/HLE/x32/Thread.h new file mode 100644 index 000000000..cba26fd98 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/x32/Thread.h @@ -0,0 +1,23 @@ +#pragma once +#include + +namespace FEXCore::Core { +struct InternalThreadState; +} + +namespace FEXCore::HLE::x32 { + // We must define this ourselves since it doesn't exist on non-x86 platforms + struct user_desc { + uint32_t entry_number; + uint32_t base_addr; + uint32_t limit; + uint32_t seg_32bit : 1; + uint32_t contents : 2; + uint32_t read_exec_only : 1; + uint32_t limit_in_pages : 1; + uint32_t seg_not_present : 1; + uint32_t useable : 1; + }; + + uint32_t Set_thread_area(FEXCore::Core::InternalThreadState *Thread, struct user_desc *u_info); +}