diff --git a/src/signal.c b/src/signal.c index d598463..9d89936 100644 --- a/src/signal.c +++ b/src/signal.c @@ -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; +}