feat: add sigOnSigReturn and sigInstallTrampoline to signal
This commit is contained in:
1 parent
8f2554114f
commit
730467ce1f
1 file changed
+85
@@ -315,3 +315,88 @@ SigOutcome sigConsumePending(Thread* thread, Registers* regs, int* outSignum)
|
||||
newBlocked &= ~(1ull << sigStop);
|
||||
thread->sigBlocked = newBlocked;
|
||||
|
||||
/* Rewrite `regs` in place to enter the shared trampoline: rip is
|
||||
the trampoline's entry, rax is the handler address its
|
||||
`call *%rax` invokes, rdi is signum (the handler's sole SysV
|
||||
argument). regs->rsp (the interrupted user stack) is left
|
||||
untouched -- the trampoline runs on the target's own stack,
|
||||
right where it already was. */
|
||||
regs->rax = handler;
|
||||
regs->rdi = (uint64)signum;
|
||||
regs->rip = sigTrampolineVirtualAddr;
|
||||
|
||||
/* sigFlagSigInfo (signalnum.h): copy the SigInfo sigRaiseInfo
|
||||
attached to this signum into THIS (thread, depth) pair's own
|
||||
slot of the shared sigInfoVirtualAddr page -- a different slot
|
||||
than any still-active outer handler's own siginfo pointer, by
|
||||
the exact same depth-indexed reasoning sigSavedRegs/
|
||||
sigSavedBlocked already use, so a nested sigFlagSigInfo delivery
|
||||
can never clobber an outer one's still-unread copy. rdx is
|
||||
always 0 (no ucontext_t this kernel can expose). Left completely
|
||||
untouched otherwise -- see this function's own doc comment. */
|
||||
if (thread->sigHandlerFlags[signum] & sigFlagSigInfo && sigInfoPageCount > 0) {
|
||||
uint32 slot = thread->id * sigMaxNestDepth + depth;
|
||||
uint32 page = slot / sigInfoSlotsPerPage;
|
||||
uint32 offset = (slot % sigInfoSlotsPerPage) * (uint32)sizeof(SigInfo);
|
||||
SigInfo* dst = (SigInfo*)(sigInfoFrames[page] + offset);
|
||||
*dst = thread->sigPendingInfo[signum];
|
||||
regs->rsi = sigInfoVirtualAddr + (uint64)page * frameSize + offset;
|
||||
regs->rdx = 0;
|
||||
}
|
||||
|
||||
return sigOutcomeHandler;
|
||||
}
|
||||
|
||||
return sigOutcomeNone;
|
||||
}
|
||||
|
||||
bool sigOnSigReturn(Thread* thread, Registers* regs)
|
||||
{
|
||||
if (thread->sigHandlerDepth == 0)
|
||||
return false;
|
||||
|
||||
uint32 depth = thread->sigHandlerDepth - 1;
|
||||
*regs = thread->sigSavedRegs[depth];
|
||||
thread->sigBlocked = thread->sigSavedBlocked[depth];
|
||||
thread->sigHandlerDepth = depth;
|
||||
return true;
|
||||
}
|
||||
|
||||
void sigInstallTrampoline(void)
|
||||
{
|
||||
uint64 frame = pmmAlloc();
|
||||
if (frame == 0)
|
||||
return;
|
||||
|
||||
uint64 length = (uint64)(sigTrampolineEnd - sigTrampolineStart);
|
||||
memset((void*)frame, 0, frameSize);
|
||||
memcpy((void*)frame, sigTrampolineStart, length);
|
||||
|
||||
pagingMap(sigTrampolineVirtualAddr, frame, pagePresent | pageUser);
|
||||
}
|
||||
|
||||
void sigInstallInfoPage(void)
|
||||
{
|
||||
uint64 totalSlots = (uint64)threadCapacity() * sigMaxNestDepth;
|
||||
uint64 pagesNeeded = (totalSlots + sigInfoSlotsPerPage - 1) / sigInfoSlotsPerPage;
|
||||
if (pagesNeeded > sigInfoMaxPages)
|
||||
pagesNeeded = sigInfoMaxPages;
|
||||
|
||||
for (uint32 i = 0; i < pagesNeeded; i++) {
|
||||
uint64 frame = pmmAlloc();
|
||||
if (frame == 0)
|
||||
return;
|
||||
|
||||
memset((void*)frame, 0, frameSize);
|
||||
/* pageUser, no pageWrite: a handler may only ever READ the SigInfo
|
||||
it was handed a pointer to, exactly like the trampoline page
|
||||
above is read+execute, never write -- the kernel is the only
|
||||
writer, via `frame`'s own physical address (see the frame
|
||||
comment above sigInfoFrames), never through this user-facing
|
||||
mapping. */
|
||||
pagingMap(sigInfoVirtualAddr + (uint64)i * frameSize, frame, pagePresent | pageUser);
|
||||
sigInfoFrames[i] = frame;
|
||||
}
|
||||
|
||||
sigInfoPageCount = (uint32)pagesNeeded;
|
||||
}
|
||||
Reference in new issue
Block a user