diff --git a/kernel/arch/aarch64/signal/sigframe.h b/kernel/arch/aarch64/signal/sigframe.h new file mode 100644 index 00000000..0f546a74 --- /dev/null +++ b/kernel/arch/aarch64/signal/sigframe.h @@ -0,0 +1,96 @@ +#ifndef STELLUX_ARCH_AARCH64_SIGNAL_SIGFRAME_H +#define STELLUX_ARCH_AARCH64_SIGNAL_SIGFRAME_H + +#include "common/types.h" +#include "signals/signal_types.h" + +namespace aarch64 { + +constexpr int32_t SI_USER = 0; + +// Delivered to SA_SIGINFO handlers. Only si_signo and si_code are filled, +// sender identity stays zero because standard signals carry no queue. +struct siginfo { + int32_t si_signo; + int32_t si_errno; + int32_t si_code; + int32_t __pad0; + int32_t si_pid; + uint32_t si_uid; + uint8_t __pad[128 - 24]; +}; + +static_assert(__builtin_offsetof(siginfo, si_signo) == 0x00); +static_assert(__builtin_offsetof(siginfo, si_code) == 0x08); +static_assert(__builtin_offsetof(siginfo, si_pid) == 0x10); +static_assert(__builtin_offsetof(siginfo, si_uid) == 0x14); +static_assert(sizeof(siginfo) == 128); + +constexpr uint32_t FPSIMD_MAGIC = 0x46508001; + +// FP/SIMD block for the mcontext reserved area. Field order is the kernel +// ABI (fpsr/fpcr before vregs), unlike sched::fpu_state, so delivery converts. +struct fpsimd_context { + uint32_t magic; + uint32_t size; + uint32_t fpsr; + uint32_t fpcr; + uint8_t vregs[32][16]; // V0-V31, 128-bit each +}; + +static_assert(__builtin_offsetof(fpsimd_context, fpsr) == 0x08); +static_assert(__builtin_offsetof(fpsimd_context, fpcr) == 0x0C); +static_assert(__builtin_offsetof(fpsimd_context, vregs) == 0x10); +static_assert(sizeof(fpsimd_context) == 528); + +// Interrupted registers, matching the kernel sigcontext / musl mcontext_t. +// __reserved holds the fpsimd_context, 16-byte aligned per the ABI. +struct alignas(16) sigcontext { + uint64_t fault_address; + uint64_t regs[31]; // x0-x30 + uint64_t sp; + uint64_t pc; + uint64_t pstate; + uint64_t __pad0; // aligns __reserved to 16 + uint8_t __reserved[4096]; +}; + +static_assert(__builtin_offsetof(sigcontext, regs) == 0x08); +static_assert(__builtin_offsetof(sigcontext, sp) == 0x100); +static_assert(__builtin_offsetof(sigcontext, pc) == 0x108); +static_assert(__builtin_offsetof(sigcontext, pstate) == 0x110); +static_assert(__builtin_offsetof(sigcontext, __reserved) == 0x120); +static_assert(sizeof(sigcontext) == 4384); + +// uc_sigmask is 8 bytes plus reserved space so uc_mcontext lands at the +// offset the 16-byte-aligned sigcontext requires. +struct ucontext { + uint64_t uc_flags; + uint64_t uc_link; + uint64_t ss_sp; // uc_stack.ss_sp + uint32_t ss_flags; // uc_stack.ss_flags + uint32_t __pad0; + uint64_t ss_size; // uc_stack.ss_size + signals::sig_set_t uc_sigmask; // blocked mask saved across the handler + uint8_t __sigmask_reserved[128]; + sigcontext uc_mcontext; +}; + +static_assert(__builtin_offsetof(ucontext, uc_sigmask) == 0x28); +static_assert(__builtin_offsetof(ucontext, uc_mcontext) == 0xB0); +static_assert(sizeof(ucontext) == 4560); + +// Written to the user stack at delivery: siginfo first, then the context. +struct rt_sigframe { + siginfo info; + ucontext uc; +}; + +static_assert(__builtin_offsetof(rt_sigframe, uc) == 0x80); +static_assert(__builtin_offsetof(rt_sigframe, uc) + + __builtin_offsetof(ucontext, uc_mcontext) == 304); +static_assert(sizeof(rt_sigframe) == 4688); + +} // namespace aarch64 + +#endif // STELLUX_ARCH_AARCH64_SIGNAL_SIGFRAME_H diff --git a/kernel/arch/x86_64/signal/sigframe.h b/kernel/arch/x86_64/signal/sigframe.h new file mode 100644 index 00000000..d895ae73 --- /dev/null +++ b/kernel/arch/x86_64/signal/sigframe.h @@ -0,0 +1,78 @@ +#ifndef STELLUX_ARCH_X86_64_SIGNAL_SIGFRAME_H +#define STELLUX_ARCH_X86_64_SIGNAL_SIGFRAME_H + +#include "common/types.h" +#include "signals/signal_types.h" + +namespace x86 { + +constexpr int32_t SI_USER = 0; + +// Delivered to SA_SIGINFO handlers. Only si_signo and si_code are filled, +// sender identity stays zero because standard signals carry no queue. +struct siginfo { + int32_t si_signo; + int32_t si_errno; + int32_t si_code; + int32_t __pad0; + int32_t si_pid; + uint32_t si_uid; + uint8_t __pad[128 - 24]; +}; + +static_assert(__builtin_offsetof(siginfo, si_signo) == 0x00); +static_assert(__builtin_offsetof(siginfo, si_code) == 0x08); +static_assert(__builtin_offsetof(siginfo, si_pid) == 0x10); +static_assert(__builtin_offsetof(siginfo, si_uid) == 0x14); +static_assert(sizeof(siginfo) == 128); + +// Saved registers. Field order overlays musl's mcontext_t gregs[] and the +// kernel sigcontext so a handler reads gregs[REG_x] as the matching value. +struct sigcontext { + uint64_t r8, r9, r10, r11, r12, r13, r14, r15; // gregs[0..7] + uint64_t rdi, rsi, rbp, rbx, rdx, rax, rcx, rsp; // gregs[8..15] + uint64_t rip; // gregs[16] + uint64_t eflags; // gregs[17] + uint16_t cs, gs, fs, ss; // gregs[18], packed as csgsfs + uint64_t err, trapno, oldmask, cr2; // gregs[19..22] + uint64_t fpstate; // address of a 512-byte FXSAVE image, 0 if none + uint64_t __reserved1[8]; +}; + +static_assert(__builtin_offsetof(sigcontext, rsp) == 15 * 8); // REG_RSP +static_assert(__builtin_offsetof(sigcontext, rip) == 16 * 8); // REG_RIP +static_assert(__builtin_offsetof(sigcontext, eflags) == 17 * 8); // REG_EFL +static_assert(__builtin_offsetof(sigcontext, cs) == 18 * 8); // REG_CSGSFS +static_assert(__builtin_offsetof(sigcontext, cr2) == 22 * 8); // REG_CR2 +static_assert(sizeof(sigcontext) == 256); + +struct ucontext { + uint64_t uc_flags; + uint64_t uc_link; + uint64_t ss_sp; // uc_stack.ss_sp + uint32_t ss_flags; // uc_stack.ss_flags + uint32_t __pad0; + uint64_t ss_size; // uc_stack.ss_size + sigcontext uc_mcontext; + signals::sig_set_t uc_sigmask; // blocked mask saved across the handler +}; + +static_assert(__builtin_offsetof(ucontext, uc_mcontext) == 0x28); +static_assert(__builtin_offsetof(ucontext, uc_sigmask) == 0x128); +static_assert(sizeof(ucontext) == 304); + +// Written to the user stack at delivery. The FXSAVE image sits separately +// above the frame, which needs stricter alignment than the frame base. +struct rt_sigframe { + uint64_t pretcode; // restorer address, the handler's return address + ucontext uc; + siginfo info; +}; + +static_assert(__builtin_offsetof(rt_sigframe, uc) == 0x08); +static_assert(__builtin_offsetof(rt_sigframe, info) == 0x138); +static_assert(sizeof(rt_sigframe) == 440); + +} // namespace x86 + +#endif // STELLUX_ARCH_X86_64_SIGNAL_SIGFRAME_H diff --git a/kernel/tests/signals/sigframe.test.cpp b/kernel/tests/signals/sigframe.test.cpp new file mode 100644 index 00000000..faf77a7a --- /dev/null +++ b/kernel/tests/signals/sigframe.test.cpp @@ -0,0 +1,25 @@ +#define STLX_TEST_TIER TIER_UTIL + +#include "stlx_unit_test.h" +#include "signal/sigframe.h" + +// The frame layout is enforced by static_asserts in signal/sigframe.h. +// These runtime checks confirm the header compiles for the active arch. + +TEST_SUITE(sigframe); + +#ifdef __x86_64__ +TEST(sigframe, x86_frame_matches_musl_abi) { + EXPECT_EQ(sizeof(x86::rt_sigframe), 440u); + EXPECT_EQ(sizeof(x86::sigcontext), 256u); + EXPECT_EQ(__builtin_offsetof(x86::rt_sigframe, info), 312u); +} +#endif + +#ifdef __aarch64__ +TEST(sigframe, aarch64_frame_matches_musl_abi) { + EXPECT_EQ(sizeof(aarch64::rt_sigframe), 4688u); + EXPECT_EQ(sizeof(aarch64::fpsimd_context), 528u); + EXPECT_EQ(__builtin_offsetof(aarch64::ucontext, uc_mcontext), 176u); +} +#endif