Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux: Converts passthrough syscalls to generator script #3355

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,10 @@ Arm64Emitter::Arm64Emitter(FEXCore::Context::ContextImpl *ctx, void* EmissionPtr
// Only setup the disassembler if enabled.
// vixl's decoder is expensive to setup.
if (Disassemble()) {
DisasmBuffer = static_cast<char*>(FEXCore::Allocator::malloc(DISASM_BUFFER_SIZE));
Disasm = fextl::make_unique<vixl::aarch64::Disassembler>(DisasmBuffer, DISASM_BUFFER_SIZE);
DisasmDecoder = fextl::make_unique<vixl::aarch64::Decoder>();
DisasmDecoder->AppendVisitor(&Disasm);
DisasmDecoder->AppendVisitor(Disasm.get());
}
#endif

Expand Down
12 changes: 11 additions & 1 deletion FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ class Arm64Emitter : public FEXCore::ARMEmitter::Emitter {
protected:
Arm64Emitter(FEXCore::Context::ContextImpl *ctx, void* EmissionPtr = nullptr, size_t size = 0);

virtual ~Arm64Emitter() {
#ifdef VIXL_DISASSEMBLER
if (DisasmBuffer) {
FEXCore::Allocator::free(DisasmBuffer);
}
#endif
}

FEXCore::Context::ContextImpl *EmitterCTX;
vixl::aarch64::CPU CPU;

Expand Down Expand Up @@ -233,7 +241,9 @@ class Arm64Emitter : public FEXCore::ARMEmitter::Emitter {
#endif

#ifdef VIXL_DISASSEMBLER
vixl::aarch64::Disassembler Disasm;
char *DisasmBuffer{};
constexpr static int DISASM_BUFFER_SIZE {256};
fextl::unique_ptr<vixl::aarch64::Disassembler> Disasm;
fextl::unique_ptr<vixl::aarch64::Decoder> DisasmDecoder;

FEX_CONFIG_OPT(Disassemble, DISASSEMBLE);
Expand Down
2 changes: 1 addition & 1 deletion FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ void Dispatcher::EmitDispatcher() {
const auto DisasmEnd = GetCursorAddress<const vixl::aarch64::Instruction*>();
for (auto PCToDecode = DisasmBegin; PCToDecode < DisasmEnd; PCToDecode += 4) {
DisasmDecoder->Decode(PCToDecode);
auto Output = Disasm.GetOutput();
auto Output = Disasm->GetOutput();
LogMan::Msg::IFmt("{}", Output);
}
}
Expand Down
2 changes: 1 addition & 1 deletion FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Dispatcher final : public Arm64Emitter {
static fextl::unique_ptr<Dispatcher> Create(FEXCore::Context::ContextImpl *CTX);

Dispatcher(FEXCore::Context::ContextImpl *ctx);
~Dispatcher();
virtual ~Dispatcher();

/**
* @name Dispatch Helper functions
Expand Down
2 changes: 1 addition & 1 deletion FEXCore/Source/Interface/Core/JIT/Arm64/JIT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry,
LogMan::Msg::IFmt("Disassemble Begin");
for (auto PCToDecode = DisasmBegin; PCToDecode < DisasmEnd; PCToDecode += 4) {
DisasmDecoder->Decode(PCToDecode);
auto Output = Disasm.GetOutput();
auto Output = Disasm->GetOutput();
LogMan::Msg::IFmt("{}", Output);
}
LogMan::Msg::IFmt("Disassemble End");
Expand Down
220 changes: 220 additions & 0 deletions Scripts/GenerateSyscallHandlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#!/usr/bin/python3
from dataclasses import dataclass, field
import json
import struct
import sys
from json_config_parse import parse_json
import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)

@dataclass
class SyscallDefinition:
Name: str
CustomHandler: bool
HandlerType: str
ArgCount: int
Flags: str
MinimumKernel: str
SyscallRedirect: str

def __init__(self, Name, CustomHandler, HandlerType, ArgCount, Flags, MinimumKernel, SyscallRedirect):
self.Name = Name
self.CustomHandler = CustomHandler
self.HandlerType = HandlerType
self.ArgCount = ArgCount
self.Flags = Flags
self.MinimumKernel = MinimumKernel
self.SyscallRedirect = SyscallRedirect

SyscallDefinitionsCommon = {}
SyscallDefinitionsx64 = {}
SyscallDefinitionsx32 = {}

def ParseTable(json_object, table, name):
data = json_object[name]
for data_key, data_val in data.items():
name = data_key

argcount = 0
flags = ""
MinimumKernel = ""
SyscallRedirect = ""
CustomHandler = False

if "CustomHandler" in data_val:
CustomHandler = bool(data_val["CustomHandler"])

if "ArchSpecific" in data_val:
if bool(data_val["ArchSpecific"]):
continue

if not CustomHandler:
if not "ArgCount" in data_val:
logging.critical("Syscall {} doesn't have argument count".format(name))

if not "Flags" in data_val:
logging.critical("Syscall {} doesn't have flags".format(name))

argcount = data_val["ArgCount"]
flags = data_val["Flags"]

if "MinimumKernel" in data_val:
MinimumKernel = data_val["MinimumKernel"]

Splits = MinimumKernel.split(".")
if len(Splits) != 3:
logging.critical("Syscall {} has invalid kernel version as '{}'. Expecting format 'Major.Minor.Patch'".format(name, MinimumKernel))

if "SyscallRedirect" in data_val:
SyscallRedirect = data_val["SyscallRedirect"]

table[name] = SyscallDefinition(name, CustomHandler, "Common", argcount, flags, MinimumKernel, SyscallRedirect)

def ParseJson(JsonText):
json_object = json.loads(JsonText)

if not "Common" in json_object:
logging.critical ("Need to have common syscalls")

if not "x64" in json_object:
logging.critical ("Need to have x64 syscalls")

if not "x32" in json_object:
logging.critical ("Need to have x32 syscalls")

ParseTable(json_object, SyscallDefinitionsCommon, "Common")
ParseTable(json_object, SyscallDefinitionsx64, "x64")
ParseTable(json_object, SyscallDefinitionsx32, "x32")

def PrintHandlers(output_file, wrapper_name, table, impl_flags, impl, redirect_impl_flags, redirect_impl):
output_file.write("#ifdef {}\n".format(wrapper_name))
output_file.write("#undef {}\n".format(wrapper_name))
for data_key, data_val in table.items():
if data_val.CustomHandler:
continue

HasMinimumKernel = data_val.MinimumKernel != ""
HasSyscallRedirect = data_val.SyscallRedirect != ""

which_impl_flags = impl_flags
which_impl = impl

if HasSyscallRedirect:
which_impl_flags = redirect_impl_flags
which_impl = redirect_impl

if HasMinimumKernel:
Splits = data_val.MinimumKernel.split(".")
output_file.write ("if (Handler->IsHostKernelVersionAtLeast({}, {}, {})) {{\n".format(Splits[0], Splits[1], Splits[2]))

if HasSyscallRedirect:
output_file.write ("{}({}, {}, {},\n".format(which_impl_flags, data_key, data_val.SyscallRedirect, data_val.Flags))
else:
output_file.write ("{}({}, {},\n".format(which_impl_flags, data_key, data_val.Flags))
output_file.write (" SyscallPassthrough{}<SYSCALL_DEF({})>);\n".format(data_val.ArgCount, data_key))

if HasMinimumKernel:
output_file.write ("}\n")
output_file.write ("else {\n")

output_file.write (" {}({}, UnimplementedSyscallSafe);\n".format(which_impl, data_key))
output_file.write ("}\n")

output_file.write("#endif\n")

def PrintPassthroughHandlers(output_file):
output_file.write("#ifdef PASSTHROUGH_HANDLERS\n")
output_file.write("#undef PASSTHROUGH_HANDLERS\n")

output_file.write("#ifdef _M_ARM_64\n")
for i in range(0, 8):
output_file.write("template<int syscall_num>\n");
output_file.write("uint64_t SyscallPassthrough{}(FEXCore::Core::CpuStateFrame *Frame".format(i))

for j in range(0, i):
output_file.write(", uint64_t arg{}".format(j + 1))

output_file.write(") {\n")
if i > 0:
for j in range(0, i):
output_file.write(" register uint64_t x{} asm (\"x{}\") = arg{};\n".format(j, j, j + 1))
else:
output_file.write(" register uint64_t x0 asm (\"x0\");\n")

output_file.write(" register int x8 asm (\"x8\") = syscall_num;\n");
output_file.write(" __asm volatile(R\"(\n")
output_file.write(" svc #0;\n")
output_file.write(" )\"\n")
output_file.write(" : \"=r\" (x0)\n")
output_file.write(" : \"r\" (x8)\n")
for j in range(0, i):
output_file.write(" , \"r\" (x{})\n".format(j))

output_file.write(" : \"memory\");\n")

output_file.write(" return x0;\n")
output_file.write("}\n\n")

output_file.write("#else\n")

for i in range(0, 8):
output_file.write("template<int syscall_num>\n")
output_file.write("uint64_t SyscallPassthrough{}(FEXCore::Core::CpuStateFrame *Frame".format(i))

for j in range(0, i):
output_file.write(", uint64_t arg{}".format(j + 1))

output_file.write(") {\n")

output_file.write(" uint64_t Result = ::syscall(syscall_num")
for j in range(0, i):
output_file.write(", arg{}".format(j + 1))

output_file.write(");\n")

output_file.write(" SYSCALL_ERRNO();\n")
output_file.write("}\n")
output_file.write("#endif\n")
output_file.write("#endif\n")

def main():
if sys.version_info[0] < 3:
logging.critical ("Python 3 or a more recent version is required.")

if (len(sys.argv) < 3):
print ("usage: %s <Syscall description json> <output file>" % (sys.argv[0]))

JsonDescPath = sys.argv[1]
OutputFilePath = sys.argv[2]

JsonFile = open(JsonDescPath, "r")
JsonText = JsonFile.read()
JsonFile.close()

ParseJson(JsonText)

output_file = open(OutputFilePath, "w")
PrintPassthroughHandlers(output_file)

PrintHandlers(output_file,
"SYSCALL_COMMON_IMPL",
SyscallDefinitionsCommon,
"REGISTER_SYSCALL_IMPL_PASS_FLAGS", "REGISTER_SYSCALL_IMPL",
"<UNSUPPORTED>", "<UNSUPPORTED>")
PrintHandlers(output_file,
"SYSCALL_X64_IMPL",
SyscallDefinitionsx64,
"REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS", "REGISTER_SYSCALL_IMPL_X64",
"<UNSUPPORTED>", "<UNSUPPORTED>")
PrintHandlers(output_file,
"SYSCALL_X32_IMPL",
SyscallDefinitionsx32,
"REGISTER_SYSCALL_IMPL_X32_PASS_FLAGS", "REGISTER_SYSCALL_IMPL_X32",
"REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS", "REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL")

output_file.close()

if __name__ == "__main__":
# execute only if run as a script
sys.exit(main())
28 changes: 14 additions & 14 deletions Source/Tools/LinuxEmulation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,41 +32,40 @@ set (SRCS
LinuxSyscalls/x32/IoctlEmulation.cpp
LinuxSyscalls/x64/EPoll.cpp
LinuxSyscalls/x64/FD.cpp
LinuxSyscalls/x64/IO.cpp
LinuxSyscalls/x64/Ioctl.cpp
LinuxSyscalls/x64/Info.cpp
LinuxSyscalls/x64/Memory.cpp
LinuxSyscalls/x64/Msg.cpp
LinuxSyscalls/x64/NotImplemented.cpp
LinuxSyscalls/x64/Semaphore.cpp
LinuxSyscalls/x64/Sched.cpp
LinuxSyscalls/x64/Signals.cpp
LinuxSyscalls/x64/Socket.cpp
LinuxSyscalls/x64/Thread.cpp
LinuxSyscalls/x64/Syscalls.cpp
LinuxSyscalls/x64/Time.cpp
LinuxSyscalls/Syscalls/EPoll.cpp
LinuxSyscalls/Syscalls/FD.cpp
LinuxSyscalls/Syscalls/FS.cpp
LinuxSyscalls/Syscalls/Generated.cpp
LinuxSyscalls/Syscalls/Info.cpp
LinuxSyscalls/Syscalls/IO.cpp
LinuxSyscalls/Syscalls/IOUring.cpp
LinuxSyscalls/Syscalls/Key.cpp
LinuxSyscalls/Syscalls/Memory.cpp
LinuxSyscalls/Syscalls/Msg.cpp
LinuxSyscalls/Syscalls/Namespace.cpp
LinuxSyscalls/Syscalls/Sched.cpp
LinuxSyscalls/Syscalls/Semaphore.cpp
LinuxSyscalls/Syscalls/SHM.cpp
LinuxSyscalls/Syscalls/Signals.cpp
LinuxSyscalls/Syscalls/Socket.cpp
LinuxSyscalls/Syscalls/Thread.cpp
LinuxSyscalls/Syscalls/Time.cpp
LinuxSyscalls/Syscalls/Timer.cpp
LinuxSyscalls/Syscalls/NotImplemented.cpp
LinuxSyscalls/Syscalls/Stubs.cpp)

set(OUTPUT_LINUXSYSCALLS_FOLDER "${CMAKE_CURRENT_BINARY_DIR}/")

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/GeneratedSyscallHandlers.inl"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/SyscallDescription.json"
DEPENDS "${PROJECT_SOURCE_DIR}/Scripts/GenerateSyscallHandlers.py"
COMMAND "python3" ARGS "${PROJECT_SOURCE_DIR}/Scripts/GenerateSyscallHandlers.py"
"${CMAKE_CURRENT_SOURCE_DIR}/SyscallDescription.json"
"${CMAKE_CURRENT_BINARY_DIR}/GeneratedSyscallHandlers.inl")

add_custom_target(GeneratedSyscallHandlers DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/GeneratedSyscallHandlers.inl")

add_library(LinuxEmulation STATIC ${SRCS})
add_dependencies(LinuxEmulation GeneratedSyscallHandlers)

target_compile_options(LinuxEmulation
PRIVATE
Expand All @@ -82,6 +81,7 @@ PRIVATE
target_include_directories(LinuxEmulation
PRIVATE
${CMAKE_BINARY_DIR}/generated
${CMAKE_CURRENT_BINARY_DIR}/
${CMAKE_CURRENT_SOURCE_DIR}/
${PROJECT_SOURCE_DIR}/External/drm-headers/include/
)
Expand Down
10 changes: 1 addition & 9 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,14 @@ class SignalDelegator;
void RegisterFS(FEX::HLE::SyscallHandler *Handler);
void RegisterInfo(FEX::HLE::SyscallHandler *Handler);
void RegisterIO(FEX::HLE::SyscallHandler *Handler);
void RegisterIOUring(FEX::HLE::SyscallHandler *Handler);
void RegisterKey(FEX::HLE::SyscallHandler *Handler);
void RegisterMemory(FEX::HLE::SyscallHandler *Handler);
void RegisterMsg(FEX::HLE::SyscallHandler *Handler);
void RegisterNamespace(FEX::HLE::SyscallHandler *Handler);
void RegisterNuma(FEX::HLE::SyscallHandler *Handler);
void RegisterSched(FEX::HLE::SyscallHandler *Handler);
void RegisterSemaphore(FEX::HLE::SyscallHandler *Handler);
void RegisterSHM(FEX::HLE::SyscallHandler *Handler);
void RegisterSignals(FEX::HLE::SyscallHandler *Handler);
void RegisterSocket(FEX::HLE::SyscallHandler *Handler);
void RegisterThread(FEX::HLE::SyscallHandler *Handler);
void RegisterTime(FEX::HLE::SyscallHandler *Handler);
void RegisterTimer(FEX::HLE::SyscallHandler *Handler);
void RegisterNotImplemented(FEX::HLE::SyscallHandler *Handler);
void RegisterStubs(FEX::HLE::SyscallHandler *Handler);
void RegisterGenerated(FEX::HLE::SyscallHandler *Handler);

uint64_t UnimplementedSyscall(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber);
uint64_t UnimplementedSyscallSafe(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber);
Expand Down
9 changes: 1 addition & 8 deletions Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/EPoll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ meta: LinuxSyscalls|syscalls-shared ~ Syscall implementations shared between x86
tags: LinuxSyscalls|syscalls-shared
$end_info$
*/

#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include "LinuxSyscalls/x32/Syscalls.h"
Expand All @@ -19,16 +18,10 @@ namespace FEX::HLE {
void RegisterEpoll(FEX::HLE::SyscallHandler *Handler) {
using namespace FEXCore::IR;

REGISTER_SYSCALL_IMPL_PASS_FLAGS(epoll_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
REGISTER_SYSCALL_IMPL_FLAGS(epoll_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int size) -> uint64_t {
uint64_t Result = epoll_create(size);
SYSCALL_ERRNO();
});

REGISTER_SYSCALL_IMPL_PASS_FLAGS(epoll_create1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
[](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
uint64_t Result = epoll_create1(flags);
SYSCALL_ERRNO();
});
}
}
Loading
Loading