feat: add sigResetState and sigDefaultIsIgnore
This commit is contained in:
1 parent
c885f33d76
commit
26476fc78b
1 file changed
+156
+156
@@ -0,0 +1,156 @@
|
|||||||
|
#include "signal.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "pmm.h"
|
||||||
|
#include "paging.h"
|
||||||
|
|
||||||
|
/* Machine code for the shared trampoline page, sigTrampoline.S -- position
|
||||||
|
independent by construction (no absolute address references at all), so
|
||||||
|
copying its bytes anywhere and mapping them there is enough. */
|
||||||
|
extern char sigTrampolineStart[];
|
||||||
|
extern char sigTrampolineEnd[];
|
||||||
|
|
||||||
|
/* Physical frame addresses backing sigInfoVirtualAddr's page(s), filled in
|
||||||
|
by sigInstallInfoPage -- kept as separate frames (not assumed physically
|
||||||
|
contiguous, unlike their virtual mapping, which IS contiguous by
|
||||||
|
construction: pmmAlloc gives no contiguity guarantee across calls, so
|
||||||
|
sigConsumePending must look up the right frame for a given slot rather
|
||||||
|
than pointer-arithmetic across a single base). Physical frames below the
|
||||||
|
1 GiB boot-time identity map (see sigTrampolineVirtualAddr's own comment)
|
||||||
|
are directly dereferenceable through their own physical value from
|
||||||
|
kernel code, exactly like sigInstallTrampoline's memcpy into `frame`
|
||||||
|
above already relies on. */
|
||||||
|
static uint64 sigInfoFrames[sigInfoMaxPages];
|
||||||
|
static uint32 sigInfoPageCount = 0;
|
||||||
|
|
||||||
|
/* How many SigInfo structs fit in one frameSize page -- shared by
|
||||||
|
sigInstallInfoPage's sizing loop and sigConsumePending's slot lookup, so
|
||||||
|
the two can never disagree about the layout. */
|
||||||
|
#define sigInfoSlotsPerPage (frameSize / sizeof(SigInfo))
|
||||||
|
|
||||||
|
void sigResetState(Thread* thread)
|
||||||
|
{
|
||||||
|
thread->sigPending = 0;
|
||||||
|
thread->sigBlocked = 0;
|
||||||
|
thread->sigHandlerDepth = 0;
|
||||||
|
thread->sigInRetryableSyscall = false;
|
||||||
|
memset(thread->sigHandlers, 0, sizeof(thread->sigHandlers));
|
||||||
|
memset(thread->sigHandlerMasks, 0, sizeof(thread->sigHandlerMasks));
|
||||||
|
memset(thread->sigHandlerFlags, 0, sizeof(thread->sigHandlerFlags));
|
||||||
|
memset(thread->sigPendingInfo, 0, sizeof(thread->sigPendingInfo));
|
||||||
|
memset(thread->sigSavedBlocked, 0, sizeof(thread->sigSavedBlocked));
|
||||||
|
memset(thread->sigSavedRegs, 0, sizeof(thread->sigSavedRegs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Real-world default disposition for signals this kernel actually defines
|
||||||
|
that is Ignore rather than Terminate -- SIGCHLD (no handler installed:
|
||||||
|
nothing happens) and SIGCONT (its actual resume effect already happened
|
||||||
|
unconditionally in sigRaise -- see file comment -- so "no handler" here
|
||||||
|
just means no handler runs, not that nothing happened at all). SIGSTOP
|
||||||
|
never reaches this function to begin with (sigRaise bypasses the whole
|
||||||
|
scan for it, like sigKill), so it is deliberately absent from this list,
|
||||||
|
the same way sigKill already was. SIGTSTP is NOT here -- its default is
|
||||||
|
Stop, not Ignore, see sigDefaultIsStop below. Every other defined signal
|
||||||
|
defaults to Terminate. */
|
||||||
|
static bool sigDefaultIsIgnore(int signum)
|
||||||
|
{
|
||||||
|
switch (signum) {
|
||||||
|
case sigChld:
|
||||||
|
case sigCont:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Real-world default disposition that is Stop rather than Terminate:
|
||||||
|
SIGTSTP only. Unlike SIGSTOP (see file comment / sigRaise), SIGTSTP is
|
||||||
|
fully catchable/blockable/ignorable -- this default only applies when it
|
||||||
|
is neither, exactly like sigDefaultIsIgnore's entries. */
|
||||||
|
static bool sigDefaultIsStop(int signum)
|
||||||
|
{
|
||||||
|
return signum == sigTstp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Whether `signum` would genuinely be delivered (a real handler OR a
|
||||||
|
default disposition of Terminate/Stop -- anything sigConsumePending
|
||||||
|
itself would act on, not silently dismiss) if `thread` returned to ring 3
|
||||||
|
right now. Deliberately mirrors sigConsumePending's own
|
||||||
|
blocked/ignored-dismissal logic for exactly one signal number rather than
|
||||||
|
sharing code with it -- sigConsumePending also mutates sigPending/
|
||||||
|
sigHandlerDepth/the trapframe as it scans, none of which this pure
|
||||||
|
predicate may do, since it runs from sigRaise against a target that is
|
||||||
|
not currently resuming at all. Used solely to decide whether a Sleeping/
|
||||||
|
Blocked target needs early-waking (threadWakeForSignal, sched.c) for this
|
||||||
|
signal -- if this ever drifted out of sync with sigConsumePending's own
|
||||||
|
logic, the failure mode is always benign either way: "wakes for a signal
|
||||||
|
that turns out not to be delivered" is a harmless spurious wake (the
|
||||||
|
target's own retry-syscall loop just re-blocks, exactly like a real
|
||||||
|
spurious wakeup), and "does not wake for one that would have delivered"
|
||||||
|
just falls back to today's behavior of waiting for a natural wake. */
|
||||||
|
static bool sigWouldDeliver(const Thread* thread, int signum)
|
||||||
|
{
|
||||||
|
uint64 bit = 1ull << (uint32)signum;
|
||||||
|
if (thread->sigBlocked & bit)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint64 handler = thread->sigHandlers[signum];
|
||||||
|
if (handler == sigHandlerIgnore ||
|
||||||
|
(handler == sigHandlerDefault && sigDefaultIsIgnore(signum)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sigRaiseInfo(uint32 targetId, int signum, int siCode, int siPid, int siStatus)
|
||||||
|
{
|
||||||
|
if (signum <= 0 || signum >= sigMaxSignals)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* SIGKILL never touches sigPending/sigBlocked at all -- it is
|
||||||
|
delegated straight to the pre-existing unconditional termination
|
||||||
|
path, exactly like real kill(pid, SIGKILL) bypassing every handler/
|
||||||
|
mask a target could have installed. threadKill does its own id
|
||||||
|
validation (range, state), so none is duplicated here. Neither it
|
||||||
|
nor SIGSTOP just below ever reaches a sigHandlers[] entry that could
|
||||||
|
even be sigFlagSigInfo, so the caller-supplied SigInfo fields are
|
||||||
|
simply never used on either of these two paths. */
|
||||||
|
if (signum == sigKill)
|
||||||
|
return threadKill(targetId);
|
||||||
|
|
||||||
|
/* SIGSTOP: uncatchable, unblockable, unignorable, exactly like SIGKILL
|
||||||
|
above -- bypasses sigPending/sigHandlers/sigBlocked entirely and acts
|
||||||
|
on ThreadState directly. threadStop does its own id validation. */
|
||||||
|
if (signum == sigStop)
|
||||||
|
return threadStop(targetId);
|
||||||
|
|
||||||
|
if (targetId == 0 || targetId >= threadCapacity())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Thread* target = threadAt(targetId);
|
||||||
|
if (target->state == threadDead || target->state == threadUnused)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* SIGCONT's resume is likewise not something a mask or a missing
|
||||||
|
handler can defer -- called unconditionally before the generic
|
||||||
|
pending-bit path below, so a Stopped target is moved back to
|
||||||
|
threadRunnable regardless of whether it also has a handler
|
||||||
|
installed. Its return value is intentionally ignored: "target
|
||||||
|
was not actually Stopped" is a harmless no-op at the process-state
|
||||||
|
level (real SIGCONT to a running process), not a reason to skip
|
||||||
|
delivering the signal itself for any installed handler. */
|
||||||
|
if (signum == sigCont)
|
||||||
|
threadContinue(targetId);
|
||||||
|
|
||||||
|
target->sigPending |= (1ull << (uint32)signum);
|
||||||
|
|
||||||
|
/* Attached regardless of whether signum's handler actually has
|
||||||
|
sigFlagSigInfo set (sigConsumePending is what decides whether this
|
||||||
|
ever gets read at all) -- cheap (4 ints), and simpler than threading
|
||||||
|
the handler's own flags in here just to skip it, matching how
|
||||||
|
sigHandlerMasks/sigHandlerFlags are likewise set unconditionally by
|
||||||
|
sigSetHandler and only ever consulted conditionally. */
|
||||||
|
target->sigPendingInfo[signum].siSignum = signum;
|
||||||
|
target->sigPendingInfo[signum].siCode = siCode;
|
||||||
|
target->sigPendingInfo[signum].siPid = siPid;
|
||||||
|
target->sigPendingInfo[signum].siStatus = siStatus;
|
||||||
|
|
||||||
Reference in new issue
Block a user