Implements initial 32bit syscall support

This adds the initial 32bit syscalls for testing. No where near complete
This commit is contained in:
Ryan Houdek committed 2020-05-15 03:23:15 -07:00
1 parent 667e514262
commit 10087f7757
10 files changed
+362 -3

No files matched your search

+4
View File
@@ -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
+4 -3
View File
@@ -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) {
+29
View File
@@ -0,0 +1,29 @@
#include "Interface/HLE/Syscalls.h"
#include "Interface/HLE/x32/FD.h"
#include "Interface/Context/Context.h"
#include <fcntl.h>
#include <stdint.h>
#include <sys/file.h>
#include <sys/eventfd.h>
#include <sys/inotify.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/timerfd.h>
#include <sys/uio.h>
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<iovec> Host_iovec(iovcnt);
for (int i = 0; i < iovcnt; ++i) {
Host_iovec[i].iov_base = reinterpret_cast<void*>(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();
}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "Interface/HLE/Syscalls.h"
#include <poll.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
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);
}
+50
View File
@@ -0,0 +1,50 @@
#include "Interface/HLE/Syscalls.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/InternalThreadState.h"
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
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<uint64_t>(::mmap(addr, length, prot, flags, fd, offset));
SYSCALL_ERRNO();
#endif
#ifdef MEM_PASSTHROUGH
if (!(flags & MAP_FIXED)) {
uint64_t Result = reinterpret_cast<uint64_t>(::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<uint64_t>(::mmap(reinterpret_cast<void*>(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
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <sys/mman.h>
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);
}
+137
View File
@@ -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<std::tuple<uint16_t, void*, uint8_t>> 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);
}
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "Interface/HLE/FileManagement.h"
#include <FEXCore/HLE/SyscallHandler.h>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <unordered_map>
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);
}
+34
View File
@@ -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 <stdint.h>
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;
}
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <stdint.h>
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);
}