mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-09 18:00:21 +02:00
This follows discussions from #3413. Followup commits add clang-format file, script and blame ignore lists.
431 lines
11 KiB
C++
431 lines
11 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#pragma once
|
|
|
|
#include <FEXCore/Utils/CompilerDefs.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <signal.h>
|
|
|
|
namespace FEXCore {
|
|
namespace x86_64 {
|
|
// uc_flags flags
|
|
///< Has extended FP state
|
|
constexpr uint64_t UC_FP_XSTATE = (1ULL << 0);
|
|
///< Set when kernel saves SS register from 64bit code
|
|
constexpr uint64_t UC_SIGCONTEXT_SS = (1ULL << 1);
|
|
///< Set when kernel will strictly restore the SS
|
|
constexpr uint64_t UC_STRICT_RESTORE_SS = (1ULL << 2);
|
|
|
|
///< Describes the signal stack
|
|
struct FEX_PACKED stack_t {
|
|
void* ss_sp;
|
|
int32_t ss_flags;
|
|
uint32_t : 32;
|
|
size_t ss_size;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::stack_t) == 24, "This needs to be the right size");
|
|
|
|
/**
|
|
* Describes the software specific bytes added at the end of the
|
|
* fpstate to identify whether or not an extended context area is
|
|
* present and what kind of extended features are present in said
|
|
* context area.
|
|
*/
|
|
struct FEX_PACKED fpx_sw_bytes {
|
|
static constexpr uint32_t FP_XSTATE_MAGIC = 0x46505853;
|
|
|
|
enum FeatureFlag : uint32_t {
|
|
FEATURE_FP = 1U << 0,
|
|
FEATURE_SSE = 1U << 1,
|
|
FEATURE_YMM = 1U << 2,
|
|
FEATURE_BNDREGS = 1U << 3,
|
|
FEATURE_BNDCSR = 1U << 4,
|
|
FEATURE_OPMASK = 1U << 5,
|
|
FEATURE_ZMM_Hi256 = 1U << 6,
|
|
FEATURE_Hi16_ZMM = 1U << 7,
|
|
FEATURE_PT_UNIMPL = 1U << 8,
|
|
FEATURE_PKRU = 1U << 9,
|
|
FEATURE_PASID = 1U << 10,
|
|
FEATURE_RESERVED11 = 1U << 11,
|
|
FEATURE_RESERVED12 = 1U << 12,
|
|
FEATURE_RESERVED13 = 1U << 13,
|
|
FEATURE_RESERVED14 = 1U << 14,
|
|
FEATURE_LBR = 1U << 15,
|
|
FEATURE_RESERVED16 = 1U << 16,
|
|
FEATURE_XTILE_CFG = 1U << 17,
|
|
FEATURE_XTILE_DATA = 1U << 18,
|
|
};
|
|
|
|
bool HasExtendedContext() const {
|
|
return magic1 == FP_XSTATE_MAGIC;
|
|
}
|
|
|
|
bool HasYMMH() const {
|
|
return (xfeatures & FEATURE_YMM) != 0;
|
|
}
|
|
|
|
// If magic1 is set to FP_XSTATE_MAGIC, then the encompassing
|
|
// frame is an xstate frame. If 0, then it's a legacy frame.
|
|
uint32_t magic1;
|
|
|
|
// Total size of the fpstate area
|
|
// - magic1 = 0 -> sizeof(fpstate)
|
|
// - magic1 = FP_XSTATE_MAGIC -> sizeof(xstate) + extensions (if any)
|
|
uint32_t extended_size;
|
|
|
|
// Feature bitmask describing supported features.
|
|
uint64_t xfeatures;
|
|
|
|
// Actual XSAVE state size, based on above xfeatures
|
|
uint32_t xstate_size;
|
|
|
|
// Reserved data
|
|
uint32_t padding[7];
|
|
};
|
|
static_assert(sizeof(fpx_sw_bytes) == 48);
|
|
|
|
struct FEX_PACKED _libc_fpstate {
|
|
// This is in FXSAVE format
|
|
uint16_t fcw;
|
|
uint16_t fsw;
|
|
uint16_t ftw;
|
|
uint16_t fop;
|
|
uint64_t fip;
|
|
uint64_t fdp;
|
|
uint32_t mxcsr;
|
|
uint32_t mxcsr_mask;
|
|
__uint128_t _st[8];
|
|
__uint128_t _xmm[16];
|
|
uint32_t _res[12];
|
|
|
|
// Linux uses 12 of the bytes relegated for software purposes
|
|
// to store info describing any existing XSAVE context data.
|
|
fpx_sw_bytes sw_reserved;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::_libc_fpstate) == 512, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED xstate_header {
|
|
uint64_t xfeatures;
|
|
uint64_t reserved1[2];
|
|
uint64_t reserved2[5];
|
|
};
|
|
static_assert(sizeof(xstate_header) == 64);
|
|
|
|
struct FEX_PACKED ymmh_state {
|
|
__uint128_t ymmh_space[16];
|
|
};
|
|
static_assert(sizeof(ymmh_state) == 256);
|
|
|
|
/**
|
|
* Extended state that includes both the main fpstate
|
|
* and the extended state.
|
|
*/
|
|
struct FEX_PACKED xstate {
|
|
_libc_fpstate fpstate;
|
|
xstate_header xstate_hdr;
|
|
ymmh_state ymmh;
|
|
};
|
|
static_assert(sizeof(xstate) == 832);
|
|
|
|
///< The order of these must match the GNU ordering
|
|
enum ContextRegs {
|
|
FEX_REG_R8 = 0,
|
|
FEX_REG_R9,
|
|
FEX_REG_R10,
|
|
FEX_REG_R11,
|
|
FEX_REG_R12,
|
|
FEX_REG_R13,
|
|
FEX_REG_R14,
|
|
FEX_REG_R15,
|
|
FEX_REG_RDI,
|
|
FEX_REG_RSI,
|
|
FEX_REG_RBP,
|
|
FEX_REG_RBX,
|
|
FEX_REG_RDX,
|
|
FEX_REG_RAX,
|
|
FEX_REG_RCX,
|
|
FEX_REG_RSP,
|
|
FEX_REG_RIP,
|
|
FEX_REG_EFL,
|
|
FEX_REG_CSGSFS,
|
|
FEX_REG_ERR,
|
|
FEX_REG_TRAPNO,
|
|
FEX_REG_OLDMASK,
|
|
FEX_REG_CR2,
|
|
};
|
|
static_assert(FEX_REG_CR2 == 22, "Oops");
|
|
|
|
struct FEX_PACKED mcontext_t {
|
|
uint64_t gregs[23];
|
|
FEXCore::x86_64::_libc_fpstate* fpregs;
|
|
uint64_t __reserved[8];
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::mcontext_t) == 256, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED sigset_t {
|
|
uint64_t val[16];
|
|
};
|
|
static_assert(sizeof(FEXCore::x86_64::sigset_t) == 128, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED ucontext_t {
|
|
uint64_t uc_flags;
|
|
FEXCore::x86_64::ucontext_t* uc_link;
|
|
FEXCore::x86_64::stack_t uc_stack;
|
|
FEXCore::x86_64::mcontext_t uc_mcontext;
|
|
FEXCore::x86_64::sigset_t uc_sigmask;
|
|
};
|
|
static_assert(offsetof(FEXCore::x86_64::ucontext_t, uc_mcontext) == 40, "Needs to be correct");
|
|
|
|
static_assert(sizeof(FEXCore::x86_64::ucontext_t) == 424, "This needs to be the right size");
|
|
} // namespace x86_64
|
|
|
|
namespace x86 {
|
|
// uc_flags flags
|
|
///< Has extended FP state
|
|
constexpr uint64_t UC_FP_XSTATE = (1ULL << 0);
|
|
|
|
///< The order of these must match the GNU ordering
|
|
enum ContextRegs {
|
|
FEX_REG_GS = 0,
|
|
FEX_REG_FS,
|
|
FEX_REG_ES,
|
|
FEX_REG_DS,
|
|
FEX_REG_RDI,
|
|
FEX_REG_RSI,
|
|
FEX_REG_RBP,
|
|
FEX_REG_RSP,
|
|
FEX_REG_RBX,
|
|
FEX_REG_RDX,
|
|
FEX_REG_RCX,
|
|
FEX_REG_RAX,
|
|
FEX_REG_TRAPNO,
|
|
FEX_REG_ERR,
|
|
FEX_REG_EIP,
|
|
FEX_REG_CS,
|
|
FEX_REG_EFL,
|
|
FEX_REG_UESP,
|
|
FEX_REG_SS
|
|
};
|
|
static_assert(FEX_REG_SS == 18, "Oops");
|
|
|
|
union sigval_t {
|
|
int sival_int;
|
|
uint32_t sival_ptr; // XXX: Should be compat_ptr<void>
|
|
};
|
|
|
|
struct FEX_PACKED siginfo_t {
|
|
int si_signo;
|
|
int si_errno;
|
|
int si_code;
|
|
union {
|
|
uint32_t pad[29];
|
|
/* tgkill siginfo_t */
|
|
struct {
|
|
int32_t pid;
|
|
int32_t uid;
|
|
} _kill;
|
|
/* SIGPOLL */
|
|
struct {
|
|
int32_t band;
|
|
int32_t fd;
|
|
} _poll;
|
|
/* SIGILL, SIGFPE, SIGSEGV, SIBUS */
|
|
struct {
|
|
uint32_t addr;
|
|
} _sigfault;
|
|
/* SIGCHLD */
|
|
struct {
|
|
int32_t pid;
|
|
int32_t uid;
|
|
int32_t status;
|
|
int32_t utime;
|
|
int32_t stime;
|
|
} _sigchld;
|
|
/* RT signals */
|
|
struct {
|
|
int32_t pid;
|
|
int32_t uid;
|
|
union {
|
|
int32_t sival_int;
|
|
uint32_t sival_ptr; // compat_ptr
|
|
} sigval;
|
|
} _rt;
|
|
/* SIGALRM, SIGVTALRM */
|
|
struct {
|
|
int tid;
|
|
int overrun;
|
|
FEXCore::x86::sigval_t sigval;
|
|
} _timer;
|
|
/* SIGSYS */
|
|
struct {
|
|
uint32_t call_addr; // compat_ptr
|
|
int32_t syscall;
|
|
uint32_t arch;
|
|
} _sigsys;
|
|
} _sifields;
|
|
|
|
union HostSigInfo_t {
|
|
// This anonymous struct needs to match the host definition
|
|
struct {
|
|
uint32_t si_signo;
|
|
uint32_t si_errno;
|
|
uint32_t si_code;
|
|
|
|
uint32_t __pad0;
|
|
|
|
// Pad[28] is a union for all the sifields
|
|
uint32_t _pad[28];
|
|
} FEXDef;
|
|
::siginfo_t host {};
|
|
};
|
|
static_assert(sizeof(HostSigInfo_t) == 128, "This needs to be the right size");
|
|
|
|
siginfo_t() = delete;
|
|
|
|
operator ::siginfo_t() const {
|
|
// The definition of siginfo_t changes depending on the host environment
|
|
// It is guaranteed to be 128 bytes and the kernel interface is the same for all of them
|
|
// Since we only run on Linux
|
|
HostSigInfo_t val {};
|
|
|
|
val.FEXDef.si_signo = si_signo;
|
|
val.FEXDef.si_errno = si_errno;
|
|
val.FEXDef.si_code = si_code;
|
|
|
|
// Host siginfo has a pad member that is set to zeros
|
|
val.FEXDef.__pad0 = 0;
|
|
|
|
// Copy over the union
|
|
// The union is different sizes on 64-bit versus 32-bit
|
|
memcpy(val.FEXDef._pad, _sifields.pad, std::min(sizeof(val.FEXDef._pad), sizeof(_sifields.pad)));
|
|
|
|
return val.host;
|
|
}
|
|
|
|
siginfo_t(::siginfo_t val) {
|
|
HostSigInfo_t host;
|
|
host.host = val;
|
|
|
|
si_signo = host.FEXDef.si_signo;
|
|
si_errno = host.FEXDef.si_errno;
|
|
si_code = host.FEXDef.si_code;
|
|
|
|
// Copy over the union
|
|
// The union is different sizes on 64-bit versus 32-bit
|
|
memcpy(_sifields.pad, host.FEXDef._pad, std::min(sizeof(host.FEXDef._pad), sizeof(_sifields.pad)));
|
|
}
|
|
static_assert(offsetof(::siginfo_t, si_signo) == offsetof(HostSigInfo_t, FEXDef.si_signo), "si_signo in wrong location?");
|
|
static_assert(offsetof(::siginfo_t, si_errno) == offsetof(HostSigInfo_t, FEXDef.si_errno), "si_errno in wrong location?");
|
|
static_assert(offsetof(::siginfo_t, si_code) == offsetof(HostSigInfo_t, FEXDef.si_code), "si_code in wrong location?");
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::siginfo_t) == 128, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED stack_t {
|
|
uint32_t ss_sp; // XXX: should be compat_ptr<void>
|
|
int ss_flags;
|
|
uint32_t ss_size;
|
|
};
|
|
|
|
static_assert(sizeof(FEXCore::x86::stack_t) == 12, "This needs to be the right size");
|
|
|
|
struct FEX_PACKED mcontext_t {
|
|
uint32_t gregs[19];
|
|
uint32_t fpregs; // XXX: should be compat_ptr<FEXCore::x86::_libc_fpstate>
|
|
uint32_t oldmask;
|
|
uint32_t cr2;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::mcontext_t) == 88, "This needs to be the right size");
|
|
|
|
struct _libc_fpreg {
|
|
uint16_t significand[4];
|
|
uint16_t exponent;
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::_libc_fpreg) == 10, "This needs to be the right size");
|
|
|
|
// Same layout on both x86 and x86_64
|
|
using fpx_sw_bytes = x86_64::fpx_sw_bytes;
|
|
using xstate_header = x86_64::xstate_header;
|
|
using ymmh_state = x86_64::ymmh_state;
|
|
|
|
enum fpstate_magic {
|
|
// Legacy fpstate
|
|
MAGIC_FPU = 0xFFFF'0000,
|
|
// Contains extended state information
|
|
MAGIC_XFPSTATE = 0x0,
|
|
};
|
|
struct FEX_PACKED _libc_fpstate {
|
|
uint32_t fcw;
|
|
uint32_t fsw;
|
|
uint32_t ftw;
|
|
uint32_t fop;
|
|
uint32_t cssel;
|
|
uint32_t dataoff;
|
|
uint32_t datasel;
|
|
FEXCore::x86::_libc_fpreg _st[8];
|
|
uint32_t status;
|
|
|
|
// Extended FPU data
|
|
uint32_t pad[6]; // Ignored FXSR data
|
|
uint32_t mxcsr;
|
|
uint32_t reserved;
|
|
__uint128_t _st_pad[8]; // Ignored st data
|
|
__uint128_t _xmm[8]; // First 8 XMM registers
|
|
uint32_t pad2[44]; // Second 8 XMM registers plus padding
|
|
fpx_sw_bytes sw_reserved; // extended state encoding
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::_libc_fpstate) == 624, "This needs to be the right size");
|
|
|
|
/**
|
|
* Extended state that includes both the main fpstate
|
|
* and the extended state.
|
|
*/
|
|
struct FEX_PACKED xstate {
|
|
_libc_fpstate fpstate;
|
|
xstate_header xstate_hdr;
|
|
ymmh_state ymmh;
|
|
};
|
|
static_assert(sizeof(xstate) == 944);
|
|
|
|
struct FEX_PACKED ucontext_t {
|
|
uint32_t uc_flags;
|
|
uint32_t uc_link; // XXX: should be a compat_ptr<FEXCore::x86::ucontext_t>
|
|
FEXCore::x86::stack_t uc_stack;
|
|
FEXCore::x86::mcontext_t uc_mcontext;
|
|
FEXCore::x86_64::sigset_t uc_sigmask; // This matches across architectures
|
|
};
|
|
static_assert(sizeof(FEXCore::x86::ucontext_t) == 236, "This needs to be the right size");
|
|
|
|
///< Non-rt signal context.
|
|
//
|
|
// Needs to match the format expected from signal handlers without SA_SIGINFO set.
|
|
struct sigcontext {
|
|
uint32_t gs;
|
|
uint32_t fs;
|
|
uint32_t es;
|
|
uint32_t ds;
|
|
uint32_t di;
|
|
uint32_t si;
|
|
uint32_t bp;
|
|
uint32_t sp;
|
|
uint32_t bx;
|
|
uint32_t dx;
|
|
uint32_t cx;
|
|
uint32_t ax;
|
|
uint32_t trapno;
|
|
uint32_t err;
|
|
uint32_t ip;
|
|
uint32_t cs;
|
|
uint32_t flags;
|
|
uint32_t sp_at_signal;
|
|
uint32_t ss;
|
|
|
|
uint32_t fpstate;
|
|
uint32_t oldmask;
|
|
uint32_t cr2;
|
|
};
|
|
} // namespace x86
|
|
} // namespace FEXCore
|