feat: add SigInfo
This commit is contained in:
1 parent
2b2252dac5
commit
8d2b1df100
1 file changed
+158
@@ -0,0 +1,158 @@
|
|||||||
|
#ifndef FROSTBYTE_SIGNALNUM_H
|
||||||
|
#define FROSTBYTE_SIGNALNUM_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Plain numeric constants shared verbatim between the kernel (signal.h/
|
||||||
|
* signal.c) and userspace (user/ulib.h/ulib.c) -- exactly like syscall.h
|
||||||
|
* already is for syscall numbers. Split out from signal.h specifically so
|
||||||
|
* user code never has to include signal.h itself, which pulls in sched.h's
|
||||||
|
* Thread struct and isr.h's Registers -- kernel-only types a freestanding
|
||||||
|
* user program has no business seeing, let alone compiling against.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Signal numbers. A small, real (not exhaustive) POSIX-shaped subset --
|
||||||
|
enough to exercise catchable delivery, ignore, default-terminate and the
|
||||||
|
one uncatchable case meaningfully, without pretending to implement every
|
||||||
|
job-control signal a real kernel would. Numbering matches Linux/x86-64
|
||||||
|
for anything actually used here, so an unused gap (5, 7, 16) does too. */
|
||||||
|
#define sigHup 1
|
||||||
|
#define sigInt 2
|
||||||
|
#define sigQuit 3
|
||||||
|
#define sigIll 4
|
||||||
|
#define sigAbrt 6
|
||||||
|
#define sigFpe 8
|
||||||
|
#define sigKill 9
|
||||||
|
#define sigUsr1 10
|
||||||
|
#define sigSegv 11
|
||||||
|
#define sigUsr2 12
|
||||||
|
#define sigPipe 13
|
||||||
|
#define sigAlrm 14
|
||||||
|
#define sigTerm 15
|
||||||
|
#define sigChld 17
|
||||||
|
#define sigCont 18
|
||||||
|
#define sigStop 19
|
||||||
|
#define sigTstp 20
|
||||||
|
|
||||||
|
/* Handler-slot sentinels (Thread.sigHandlers[signum] kernel-side,
|
||||||
|
userSigAction's `handler` argument userspace-side), distinct from any
|
||||||
|
real user code address since every user ELF slot starts at
|
||||||
|
userSpaceMin == 0x40000000 (syscall.c), far above 1. */
|
||||||
|
#define sigHandlerDefault 0ull
|
||||||
|
#define sigHandlerIgnore 1ull
|
||||||
|
|
||||||
|
/* sigSetHandler's/userSigAction's failure return -- mirrors real
|
||||||
|
signal(2)'s SIG_ERR, distinguishable from any real sentinel/handler
|
||||||
|
address a caller could have gotten back as the *previous* disposition.
|
||||||
|
Written without a uint64 cast (unlike the kernel-only constants in
|
||||||
|
signal.h) so this header stays includable from userspace, which never
|
||||||
|
sees types.h -- see fs.h's FbStat for the same convention. */
|
||||||
|
#define sigSetFailed (~0ull)
|
||||||
|
|
||||||
|
/* sigProcMask/userSigProcMask "how" values (mirrors POSIX SIG_BLOCK/
|
||||||
|
SIG_UNBLOCK/SIG_SETMASK by shape, not by number). */
|
||||||
|
#define sigMaskBlock 0
|
||||||
|
#define sigMaskUnblock 1
|
||||||
|
#define sigMaskSetMask 2
|
||||||
|
|
||||||
|
/* sigSetHandler's/userSigAction's `flags` argument (mirrors POSIX
|
||||||
|
sigaction's sa_flags by shape, not by bit position -- there is only one
|
||||||
|
flag here). Default (0, what every handler got before this bit existed)
|
||||||
|
is real POSIX's own default: a signal delivered while a thread sits
|
||||||
|
blocked inside a slow syscall (read/write/wait -- see signal.c's
|
||||||
|
sigInRetryableSyscall) does NOT transparently resume that syscall once
|
||||||
|
the handler returns, it makes the syscall return errInterrupted instead,
|
||||||
|
exactly like an EINTR on real Unix. Passing sigFlagRestart opts back into
|
||||||
|
the OTHER behavior (real SIG_RESTART/BSD's original signal(3) semantics):
|
||||||
|
the interrupted syscall is retried, invisibly to the caller, once the
|
||||||
|
handler returns. Only meaningful for a signal that actually interrupts a
|
||||||
|
genuinely retryable syscall; for anything else (plain running code, a
|
||||||
|
sleep, a signal that does not even install a handler) it is silently
|
||||||
|
irrelevant, same as real SA_RESTART. */
|
||||||
|
#define sigFlagRestart (1ull << 0)
|
||||||
|
|
||||||
|
/* Mirrors POSIX sigaction's SA_NODEFER: by default (this bit clear), a
|
||||||
|
signal's own number is added to sigBlocked for as long as ITS handler is
|
||||||
|
running (signal.h's sigConsumePending, "self-blocked like real Unix's
|
||||||
|
implicit default") -- so the same signal raised again while its handler
|
||||||
|
is still active stays merely pending, delivered only once that handler
|
||||||
|
returns, never nested into itself. Passing sigFlagNoDefer for a signal
|
||||||
|
disables exactly that one self-block (nothing else -- sa_mask/other
|
||||||
|
signals' self-blocks are untouched): a second raise of that same signal
|
||||||
|
while its own handler is active is then genuinely deliverable, entering
|
||||||
|
a NESTED instance of its own handler, the same mechanism a completely
|
||||||
|
different unmasked signal already uses to nest (see signal.h's
|
||||||
|
sa_mask paragraph) -- SA_NODEFER only widens which signal numbers that
|
||||||
|
mechanism applies to, it does not introduce a new one. Irrelevant for a
|
||||||
|
signal that never raises itself from inside its own handler. */
|
||||||
|
#define sigFlagNoDefer (1ull << 1)
|
||||||
|
|
||||||
|
/* Mirrors POSIX sigaction's SA_SIGINFO: by default (this bit clear) a
|
||||||
|
handler is called with exactly one argument, the signal number (this
|
||||||
|
kernel's ONLY shape until now, `regs->rdi = signum` in signal.c's
|
||||||
|
sigConsumePending). Passing sigFlagSigInfo for a signal tells
|
||||||
|
sigConsumePending to ALSO populate a real SigInfo (below) describing this
|
||||||
|
specific delivery -- who/what raised it, real Unix's siginfo_t.si_pid/
|
||||||
|
si_code/si_status -- and hand the handler a pointer to it as a SECOND
|
||||||
|
argument (rsi), plus a third (rdx, always 0/NULL here: this kernel has no
|
||||||
|
ucontext_t of its own to expose, unlike a real kernel's SA_SIGINFO third
|
||||||
|
argument). A handler registered with this flag must actually be declared
|
||||||
|
to take (int, SigInfo*, void*) -- see ulib.h's SigInfoHandler/
|
||||||
|
userSigActionInfo -- exactly like real sigaction()'s sa_sigaction union
|
||||||
|
member only being valid to read when SA_SIGINFO was actually passed. */
|
||||||
|
#define sigFlagSigInfo (1ull << 2)
|
||||||
|
|
||||||
|
/* si_code values SigInfo.siCode below can hold -- a small, real (not
|
||||||
|
exhaustive) subset of POSIX's SI_USER/SI_KERNEL/CLD_EXITED/CLD_KILLED/
|
||||||
|
CLD_STOPPED/CLD_CONTINUED, numbered independently of them (this kernel's
|
||||||
|
own small namespace, not a Linux ABI match, unlike the signal numbers
|
||||||
|
above which deliberately do match). siCodeUser: signum was raised by
|
||||||
|
another process's real userSigSend/kill(2)-shaped syscall (siPid is the
|
||||||
|
SENDER's own pid). siCodeKernel: raised by the kernel itself with no
|
||||||
|
specific sending process (SIGALRM's real timer expiry; also sigRaise's
|
||||||
|
own thin-wrapper default for any caller that supplies no detail at all).
|
||||||
|
siCodeChld{Exited,Killed,Stopped,Continued}: sigChld specifically,
|
||||||
|
distinguishing WHY a child's status changed (siPid is the CHILD's pid,
|
||||||
|
siStatus its real exit code for Exited, threadKilledExitCode for Killed,
|
||||||
|
or the job-control signal number for Stopped/Continued) -- real Unix's
|
||||||
|
own WIFEXITED/WIFSIGNALED/WIFSTOPPED/WIFCONTINUED distinction, exposed
|
||||||
|
directly instead of needing a separate macro to decode a packed status
|
||||||
|
word the way real wait(2)'s plain `int status` still does. */
|
||||||
|
#define siCodeUser 0
|
||||||
|
#define siCodeKernel 1
|
||||||
|
#define siCodeChldExited 2
|
||||||
|
#define siCodeChldKilled 3
|
||||||
|
#define siCodeChldStopped 4
|
||||||
|
#define siCodeChldContinued 5
|
||||||
|
|
||||||
|
/* Real siginfo_t, cut down to exactly the fields this kernel ever actually
|
||||||
|
populates (see siCode* above for what each one means per code) -- shared
|
||||||
|
verbatim between kernel and userspace like every other type in this
|
||||||
|
header, which is exactly why it is built from plain `int`, not a
|
||||||
|
types.h-only typedef (see this header's own file comment). siSignum
|
||||||
|
duplicates the handler's own first argument (signum) -- real siginfo_t
|
||||||
|
keeps si_signo too, for the same reason: a single handler function
|
||||||
|
registered against more than one signal number can tell them apart from
|
||||||
|
*info alone, without needing its own first argument at all. Handed to a
|
||||||
|
sigFlagSigInfo handler as a real pointer (signal.c's sigConsumePending)
|
||||||
|
into a small kernel-owned, user-readable (never user-writable) page
|
||||||
|
mapped at a fixed address -- see signal.h's sigInfoVirtualAddr -- the
|
||||||
|
same "one shared page, like a vDSO" trick this kernel's trampoline page
|
||||||
|
already uses, not memory the handler owns or must free. */
|
||||||
|
typedef struct SigInfo {
|
||||||
|
int siSignum;
|
||||||
|
int siCode;
|
||||||
|
int siPid;
|
||||||
|
int siStatus;
|
||||||
|
} SigInfo;
|
||||||
|
|
||||||
|
/* A retryable syscall (sysRead/sysWrite/sysWait -- see syscall.c) that got
|
||||||
|
interrupted by a signal without sigFlagRestart returns this instead of
|
||||||
|
retrying. Mirrors a raw Linux syscall's own convention of returning
|
||||||
|
-errno directly (here, -EINTR, whose real errno value is 4) rather than
|
||||||
|
a separate errno variable, since this kernel has no per-thread errno of
|
||||||
|
its own to set. Written without a uint64 cast, like every other
|
||||||
|
userspace-visible constant in this header, so it stays includable from
|
||||||
|
freestanding user code. */
|
||||||
|
#define errInterrupted (-4)
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in new issue
Block a user