Browse Source

Add support to restart system calls after signals.

This change adds support for the SA_RESTART flag used with sigaction. Right
now the only system call that supports restarting is
PsSysWaitForChildProcess. Support for the others will be added shortly.

A system call restart is viable if the system call returns a new KSTATUS -
STATUS_RESTART_SYSTEM_CALL. A system call looking to dispatch a signal
must pass its return value, paramters, and system call number. The signal
application code will evaluate the return value and system call type to
decide whether or not a restart is viable. If so, it will set a flag in
the signal context to alert user mode of the possibility of a restart and
save the system call number and paramters in the signal context. If the user
mode signal handler OKs the restart, it leaves the flag; if it
does not allow the restart, it will clear the flag. Then on restore, if
the flag is still set, the main system call handler is called again with
the saved parameters.

This change also makes sure that a system calls return value is preserved
across a handled signal, even if a restart isn't in play. This will allow
the system calls to return a status or parameter via a register.
Chris Stevens 7 years ago
parent
commit
1ac49f4f26

+ 2 - 1
apps/libc/dynamic/kerror.c

@@ -164,7 +164,8 @@ int ClpStatusToErrorNumber[] = {
     EPROTONOSUPPORT,
     EDOM,
     ENODEV,
-    EDEADLK
+    EDEADLK,
+    EINTR
 };
 
 //

+ 15 - 4
apps/libc/dynamic/signals.c

@@ -66,7 +66,7 @@ Environment:
 // ----------------------------------------------- Internal Function Prototypes
 //
 
-VOID
+BOOL
 ClpHandleSignal (
     PSIGNAL_PARAMETERS SignalInformation
     );
@@ -1781,7 +1781,7 @@ Return Value:
 // --------------------------------------------------------- Internal Functions
 //
 
-VOID
+BOOL
 ClpHandleSignal (
     PSIGNAL_PARAMETERS SignalInformation
     )
@@ -1799,7 +1799,9 @@ Arguments:
 
 Return Value:
 
-    None.
+    TRUE if an interrupted function can restart.
+
+    FALSE otherwise.
 
 --*/
 
@@ -1901,7 +1903,16 @@ Return Value:
                             &SignalMask);
     }
 
-    return;
+    //
+    // Report whether or not the restart flag was set so that the system can
+    // restart a function if required.
+    //
+
+    if ((Flags & SA_RESTART) != 0) {
+        return TRUE;
+    }
+
+    return FALSE;
 }
 
 int

+ 8 - 3
apps/osbase/armv7/osbasea.S

@@ -111,6 +111,7 @@ END_FUNCTION OsSystemCall
 ## VOID
 ## OspSignalHandler (
 ##     PSIGNAL_PARAMETERS Parameters
+##     PSIGNAL_CONTEXT Context
 ##     )
 ##
 
@@ -120,11 +121,14 @@ Routine Description:
 
     This routine is called directly by the kernel when a signal occurs. It
     marshals the parameters and calls the C routine for handling the signal.
+    The parameters are stored on the stack with the signal parameters followed
+    by the signal context.
 
 Arguments:
 
-    Parameters - Supplies a pointer to the signal parameters. Beyond that on
-        the stack is the restore structure.
+    Parameters - Supplies a pointer to the signal parameters.
+
+    Context - Supplies a pointer to the signal context from the kernel.
 
 Return Value:
 
@@ -133,7 +137,8 @@ Return Value:
 --*/
 
 FUNCTION OspSignalHandler
-    mov     %r0, %sp            @ Get the argument at the top of the stack.
+    mov     %r0, %sp            @ Get the signal parameters.
+    add     %r1, %sp, #SIGNAL_PARAMETERS_SIZE @ Get the signal context.
     bl      OspProcessSignal    @ The parameters are already set up. Just call.
     add     %sp, #SIGNAL_PARAMETERS_SIZE    @ Pop the signal parameters.
     mov     %r1, %sp            @ Get stack pointer.

+ 18 - 2
apps/osbase/osbase.c

@@ -3356,7 +3356,8 @@ Return Value:
 
 VOID
 OspProcessSignal (
-    PSIGNAL_PARAMETERS Parameters
+    PSIGNAL_PARAMETERS Parameters,
+    PSIGNAL_CONTEXT Context
     )
 
 /*++
@@ -3369,6 +3370,8 @@ Arguments:
 
     Parameters - Supplies a pointer to the signal parameters from the kernel.
 
+    Context - Supplies a pointer to the signal context from the kernel.
+
 Return Value:
 
     None.
@@ -3377,11 +3380,24 @@ Return Value:
 
 {
 
+    BOOL RestartAllowed;
     PSIGNAL_HANDLER_ROUTINE SignalHandler;
 
+    RestartAllowed = FALSE;
     SignalHandler = OsSignalHandler;
     if (SignalHandler != NULL) {
-        SignalHandler(Parameters);
+        RestartAllowed = SignalHandler(Parameters);
+    }
+
+    //
+    // Clear the restart flag if it's set but the handler does not allow
+    // restarts.
+    //
+
+    if (((Context->Flags & SIGNAL_CONTEXT_FLAG_RESTART) != 0) &&
+        (RestartAllowed == FALSE)) {
+
+        Context->Flags &= ~SIGNAL_CONTEXT_FLAG_RESTART;
     }
 
     return;

+ 4 - 2
apps/osbase/osbasep.h

@@ -209,12 +209,14 @@ Routine Description:
 
     This routine is called directly by the kernel when a signal occurs. It
     marshals the parameters and calls the C routine for handling the signal.
+    The parameters are stored on the stack with the signal parameters followed
+    by the signal context.
 
 Arguments:
 
-    SignalNumber - Supplies the index of the signal that occurred.
+    Parameters - Supplies a pointer to the signal parameters.
 
-    SignalParameter - Supplies the optional signal parameter.
+    Context - Supplies a pointer to the signal context from the kernel.
 
 Return Value:
 

+ 2 - 0
apps/osbase/x64/osbasea.S

@@ -83,6 +83,8 @@ Routine Description:
 
     This routine is called directly by the kernel when a signal occurs. It
     marshals the parameters and calls the C routine for handling the signal.
+    The parameters are stored on the stack with the signal parameters followed
+    by the signal context.
 
 Arguments:
 

+ 11 - 3
apps/osbase/x86/osbasea.S

@@ -132,7 +132,8 @@ END_FUNCTION(OspSysenterSystemCall)
 ##
 ## VOID
 ## OspSignalHandler (
-##     PSIGNAL_PARAMETERS Parameters
+##     PSIGNAL_PARAMETERS Parameters,
+##     PSIGNAL_CONTEXT Context
 ##     )
 ##
 
@@ -142,12 +143,16 @@ Routine Description:
 
     This routine is called directly by the kernel when a signal occurs. It
     marshals the parameters and calls the C routine for handling the signal.
+    The parameters are stored on the stack with the signal parameters followed
+    by the signal context.
 
 Arguments:
 
     Parameters - Supplies a pointer to the signal parameters. Beyond that on
         the stack is the restore structure.
 
+    Context - Supplies a pointer to the signal context from the kernel.
+
 Return Value:
 
     None.
@@ -161,9 +166,12 @@ FUNCTION(OspSignalHandler)
     ## be saved and restored by any C routine called.
     ##
 
-    pushl   %esp                # Push a pointer to the signal parameters.
+    movl    %esp, %eax          # Get a pointer to the signal parameters.
+    leal    SIGNAL_PARAMETERS_SIZE(%eax), %ecx # Get a pointer to the context.
+    pushl   %ecx                # Push a pointer to the signal context.
+    pushl   %eax                # Push a pointer to the signal parameters.
     call    OspProcessSignal    # Call out to the C routine to handle it.
-    addl    $(SIGNAL_PARAMETERS_SIZE + 4), %esp # Pop argument + signal params.
+    addl    $(SIGNAL_PARAMETERS_SIZE + 8), %esp # Pop arguments + signal params.
     pushl   %esp                # Push system call parameter.
     pushl   %esp                # It's a pointer to a pointer.
     pushl   $SystemCallRestoreContext # Push system call number.

+ 28 - 1
include/minoca/kernel/arch.h

@@ -27,15 +27,42 @@ Author:
 
 #define ARCH_POOL_TAG 0x68637241 // 'hcrA'
 
+//
+// Define the signal context flags.
+//
+
+#define SIGNAL_CONTEXT_FLAG_RESTART 0x00000001
+
 //
 // ------------------------------------------------------ Data Type Definitions
 //
 
 typedef struct _TRAP_FRAME TRAP_FRAME, *PTRAP_FRAME;
-typedef struct _SIGNAL_CONTEXT SIGNAL_CONTEXT, *PSIGNAL_CONTEXT;
 typedef struct _PROCESSOR_CONTEXT PROCESSOR_CONTEXT, *PPROCESSOR_CONTEXT;
 typedef struct _FPU_CONTEXT FPU_CONTEXT, *PFPU_CONTEXT;
 
+/*++
+
+Structure Description:
+
+    This structure outlines the state saved by the kernel when a user mode
+    signal is dispatched. This is usually embedded within an architecture
+    specific version of the signal context.
+
+Members:
+
+    Signal - Stores the signal number that fired.
+
+    Flags - Stores a bitmask of signal context flags. See SIGNAL_CONTEXT_FLAG_*
+        for definitions.
+
+--*/
+
+typedef struct _SIGNAL_CONTEXT {
+    ULONG Signal;
+    ULONG Flags;
+} PACKED SIGNAL_CONTEXT, *PSIGNAL_CONTEXT;
+
 //
 // -------------------------------------------------------------------- Globals
 //

+ 4 - 4
include/minoca/kernel/arm.h

@@ -704,14 +704,14 @@ Structure Description:
 
 Members:
 
-    Signal - Stores the signal number that fired.
+    Common - Stores the common signal context information.
 
     Registers - Stores the previous state of the thread's registers.
 
 --*/
 
-struct _SIGNAL_CONTEXT {
-    ULONG Signal;
+typedef struct _SIGNAL_CONTEXT_ARM {
+    SIGNAL_CONTEXT Common;
     ULONG R0;
     ULONG R1;
     ULONG R2;
@@ -721,7 +721,7 @@ struct _SIGNAL_CONTEXT {
     ULONG Lr;
     ULONG Pc;
     ULONG Cpsr;
-} PACKED;
+} PACKED SIGNAL_CONTEXT_ARM, *PSIGNAL_CONTEXT_ARM;
 
 /*++
 

+ 34 - 0
include/minoca/kernel/ke.h

@@ -3554,3 +3554,37 @@ Return Value:
 
 --*/
 
+INTN
+KeSystemCallHandler (
+    ULONG SystemCallNumber,
+    PVOID SystemCallParameter,
+    PTRAP_FRAME TrapFrame
+    );
+
+/*++
+
+Routine Description:
+
+    This routine responds to requests from user mode entered via a system call.
+    It may also be called by the restore system call in order to restart a
+    system call. This should not be seen as a general way to invoke system call
+    behavior from inside the kernel.
+
+Arguments:
+
+    SystemCallNumber - Supplies the system call number.
+
+    SystemCallParameter - Supplies a pointer to the parameters supplied with
+        the system call.
+
+    TrapFrame - Supplies a pointer to the trap frame generated by this jump
+        from user mode to kernel mode.
+
+Return Value:
+
+    STATUS_SUCCESS or positive integer on success.
+
+    Error status code on failure.
+
+--*/
+

+ 60 - 8
include/minoca/kernel/ps.h

@@ -86,10 +86,32 @@ Author:
 // This macro dispatches pending signals on the given thread if there are any.
 //
 
-#define PsDispatchPendingSignals(_Thread, _TrapFrame)     \
-    ((_Thread)->SignalPending == ThreadNoSignalPending) ? \
-    FALSE :                                               \
-    PsDispatchPendingSignalsOnCurrentThread(_TrapFrame)
+#define PsDispatchPendingSignals(_Thread, _TrapFrame)          \
+    ((_Thread)->SignalPending == ThreadNoSignalPending) ?      \
+    FALSE :                                                    \
+    PsDispatchPendingSignalsOnCurrentThread(_TrapFrame,        \
+                                            0,                 \
+                                            NULL,              \
+                                            SystemCallInvalid)
+
+//
+// This macro dispatches pending signals on the given thread if there are any,
+// preserving the system call information so that is can be resumed or
+// restarted.
+//
+
+#define PsDispatchPendingSignalsForSystemCall(_Thread,              \
+                                              _TrapFrame,           \
+                                              _SystemCallResult,    \
+                                              _SystemCallParameter, \
+                                              _SystemCallNumber)    \
+                                                                    \
+    ((_Thread)->SignalPending == ThreadNoSignalPending) ?           \
+    FALSE :                                                         \
+    PsDispatchPendingSignalsOnCurrentThread(_TrapFrame,             \
+                                            _SystemCallResult,      \
+                                            _SystemCallParameter,   \
+                                            _SystemCallNumber)
 
 //
 // This macro performs a quick inline check to see if any of the runtime timers
@@ -2318,8 +2340,8 @@ Routine Description:
 Arguments:
 
     SystemCallParameter - Supplies a pointer to the parameters supplied with
-        the system call. This structure will be a stack-local copy of the
-        actual parameters passed from user-mode.
+        the system call. This is a pointer to the actual parameters passed from
+        user mode.
 
 Return Value:
 
@@ -2762,7 +2784,10 @@ Return Value:
 
 BOOL
 PsDispatchPendingSignalsOnCurrentThread (
-    PTRAP_FRAME TrapFrame
+    PTRAP_FRAME TrapFrame,
+    INTN SystemCallResult,
+    PVOID SystemCallParameter,
+    ULONG SystemCallNumber
     );
 
 /*++
@@ -2777,6 +2802,18 @@ Arguments:
     TrapFrame - Supplies a pointer to the current trap frame. If this trap frame
         is not destined for user mode, this function exits immediately.
 
+    SystemCallResult - Supplies the result of the system call that is
+        attempting to dispatch a pending signal. This is only valid if the
+        system call number is valid.
+
+    SystemCallParameter - Supplies a pointer to the parameters of the system
+        call that is attempting to dispatch a pending signal. This is a pointer
+        to user mode. This is only valid if the system call number if valid.
+
+    SystemCallNumber - Supplies the number of the system call that is
+        attempting to dispatch a pending signal. Supplied SystemCallInvalid if
+        the caller is not a system call.
+
 Return Value:
 
     FALSE if no signals are pending.
@@ -2817,7 +2854,10 @@ Return Value:
 VOID
 PsApplySynchronousSignal (
     PTRAP_FRAME TrapFrame,
-    PSIGNAL_PARAMETERS SignalParameters
+    PSIGNAL_PARAMETERS SignalParameters,
+    INTN SystemCallResult,
+    PVOID SystemCallParameter,
+    ULONG SystemCallNumber
     );
 
 /*++
@@ -2835,6 +2875,18 @@ Arguments:
 
     SignalParameters - Supplies a pointer to the signal information to apply.
 
+    SystemCallResult - Supplies the result of the system call that is
+        attempting to dispatch a pending signal. This is only valid if the
+        system call number is valid.
+
+    SystemCallParameter - Supplies a pointer to the parameters of the system
+        call that is attempting to dispatch a pending signal. This is a pointer
+        to user mode. This is only valid if the system call number if valid.
+
+    SystemCallNumber - Supplies the number of the system call that is
+        attempting to dispatch a pending signal. Supplied SystemCallInvalid if
+        the caller is not a system call.
+
 Return Value:
 
     None.

+ 14 - 0
include/minoca/kernel/syscall.h

@@ -20,6 +20,20 @@ Author:
 // ------------------------------------------------------------------- Includes
 //
 
+//
+// --------------------------------------------------------------------- Macros
+//
+
+//
+// This macro determines whether or not a system call is eligible for being
+// restarted.
+//
+
+#define IS_SYSTEM_CALL_RESTARTABLE(_SystemCallNumber, _SystemCallResult) \
+    (((_SystemCallResult) == STATUS_RESTART_SYSTEM_CALL) &&              \
+     ((_SystemCallNumber) != SystemCallRestoreContext) &&                \
+     ((_SystemCallNumber) != SystemCallExecuteImage))
+
 //
 // ---------------------------------------------------------------- Definitions
 //

+ 4 - 4
include/minoca/kernel/x86.h

@@ -635,21 +635,21 @@ Structure Description:
 
 Members:
 
-    Signal - Stores the signal number that fired.
+    Common - Stores the common signal context information.
 
     Registers - Stores the previous state of the thread's registers.
 
 --*/
 
-struct _SIGNAL_CONTEXT {
-    ULONG Signal;
+typedef struct _SIGNAL_CONTEXT_X86 {
+    SIGNAL_CONTEXT Common;
     ULONG Eax;
     ULONG Ecx;
     ULONG Edx;
     ULONG Eflags;
     ULONG Eip;
     ULONG Esp;
-} PACKED;
+} PACKED SIGNAL_CONTEXT_X86, *PSIGNAL_CONTEXT_X86;
 
 /*++
 

+ 4 - 2
include/minoca/lib/minocaos.h

@@ -102,7 +102,7 @@ typedef enum _OS_X86_PROCESSOR_FEATURE {
 } OS_X86_PROCESSOR_FEATURE, *POS_X86_PROCESSOR_FEATURE;
 
 typedef
-VOID
+BOOL
 (*PSIGNAL_HANDLER_ROUTINE) (
     PSIGNAL_PARAMETERS SignalInformation
     );
@@ -122,7 +122,9 @@ Arguments:
 
 Return Value:
 
-    None.
+    TRUE if an interrupted function can restart.
+
+    FALSE otherwise.
 
 --*/
 

+ 1 - 0
include/minoca/lib/status.h

@@ -132,6 +132,7 @@ Author:
 #define STATUS_DOMAIN_ERROR               (LONG)-109
 #define STATUS_MEDIA_CHANGED              (LONG)-110
 #define STATUS_DEADLOCK                   (LONG)-111
+#define STATUS_RESTART_SYSTEM_CALL        (LONG)-112
 
 //
 // ------------------------------------------------------ Data Type Definitions

+ 12 - 2
kernel/io/userio.c

@@ -1574,11 +1574,21 @@ PollEnd:
     if (RestoreSignalMask != FALSE) {
 
         //
-        // Queue up signals before resetting the mask.
+        // Queue up signals before resetting the mask. The poll system call
+        // cannot be restarted, so there is no need to provide the system call
+        // parameters.
         //
 
         PsCheckRuntimeTimers(Thread);
-        PsDispatchPendingSignals(Thread, Thread->TrapFrame);
+
+        ASSERT(Status != STATUS_RESTART_SYSTEM_CALL);
+
+        PsDispatchPendingSignalsForSystemCall(Thread,
+                                              Thread->TrapFrame,
+                                              Status,
+                                              NULL,
+                                              SystemCallPoll);
+
         PsSetSignalMask(&OldSignalSet, NULL);
     }
 

+ 39 - 17
kernel/ke/syscall.c

@@ -114,9 +114,7 @@ SYSTEM_CALL_TABLE_ENTRY KeSystemCallTable[SystemCallCount] = {
     {PsSysSetSignalBehavior,
         sizeof(SYSTEM_CALL_SET_SIGNAL_BEHAVIOR),
         sizeof(SYSTEM_CALL_SET_SIGNAL_BEHAVIOR)},
-    {PsSysWaitForChildProcess,
-        sizeof(SYSTEM_CALL_WAIT_FOR_CHILD),
-        sizeof(SYSTEM_CALL_WAIT_FOR_CHILD)},
+    {PsSysWaitForChildProcess, 0, 0},
     {PsSysSuspendExecution,
         sizeof(SYSTEM_CALL_SUSPEND_EXECUTION),
         sizeof(SYSTEM_CALL_SUSPEND_EXECUTION)},
@@ -291,6 +289,9 @@ KeSystemCallHandler (
 Routine Description:
 
     This routine responds to requests from user mode entered via a system call.
+    It may also be called by the restore system call in order to restart a
+    system call. This should not be seen as a general way to invoke system call
+    behavior from inside the kernel.
 
 Arguments:
 
@@ -312,9 +313,11 @@ Return Value:
 
 {
 
-    KSTATUS CopyStatus;
     PSYSTEM_CALL_TABLE_ENTRY Handler;
     SYSTEM_CALL_PARAMETER_UNION LocalParameters;
+    USHORT OriginalThreadFlags;
+    CYCLE_ACCOUNT PreviousCycleAccount;
+    INTN Result;
     KSTATUS Status;
     PKTHREAD Thread;
 
@@ -322,8 +325,10 @@ Return Value:
     // Begin charging kernel mode for cycles.
     //
 
-    KeBeginCycleAccounting(CycleAccountKernel);
+    Status = STATUS_SUCCESS;
+    PreviousCycleAccount = KeBeginCycleAccounting(CycleAccountKernel);
     Thread = KeGetCurrentThread();
+    OriginalThreadFlags = Thread->Flags;
     Thread->Flags |= THREAD_FLAG_IN_SYSTEM_CALL;
     Thread->TrapFrame = TrapFrame;
 
@@ -359,7 +364,7 @@ Return Value:
         // Call the handler.
         //
 
-        Status = Handler->HandlerRoutine(&LocalParameters);
+        Result = Handler->HandlerRoutine(&LocalParameters);
 
         //
         // Copy the local parameters back into user mode.
@@ -369,32 +374,49 @@ Return Value:
 
             ASSERT(Handler->CopyOutSize <= Handler->CopyInSize);
 
-            CopyStatus = MmCopyToUserMode(SystemCallParameter,
-                                          &LocalParameters,
-                                          Handler->CopyOutSize);
+            Status = MmCopyToUserMode(SystemCallParameter,
+                                      &LocalParameters,
+                                      Handler->CopyOutSize);
 
-            if (!KSUCCESS(CopyStatus)) {
+            if (!KSUCCESS(Status)) {
                 PsSignalThread(Thread, SIGNAL_ACCESS_VIOLATION, NULL, TRUE);
-                Status = CopyStatus;
                 goto SystemCallHandlerEnd;
             }
         }
 
+    //
+    // Even if there is no data to copy in, still pass the system call
+    // parameters along. The handler may be doing something special with them.
+    //
+
     } else {
-        Status = Handler->HandlerRoutine(NULL);
+        Result = Handler->HandlerRoutine(SystemCallParameter);
     }
 
 SystemCallHandlerEnd:
+    if (!KSUCCESS(Status)) {
+        Result = Status;
+    }
+
     PsCheckRuntimeTimers(Thread);
-    PsDispatchPendingSignals(Thread, TrapFrame);
-    Thread->Flags &= ~THREAD_FLAG_IN_SYSTEM_CALL;
+    PsDispatchPendingSignalsForSystemCall(Thread,
+                                          TrapFrame,
+                                          Result,
+                                          SystemCallParameter,
+                                          SystemCallNumber);
 
     //
-    // Return to charging user mode for cycles.
+    // Return to the previous thread state and cycle account. A restarted
+    // system call can come in on top of the context restore system call after
+    // a signal is handled.
     //
 
-    KeBeginCycleAccounting(CycleAccountUser);
-    return Status;
+    if ((OriginalThreadFlags & THREAD_FLAG_IN_SYSTEM_CALL) == 0) {
+        Thread->Flags &= ~THREAD_FLAG_IN_SYSTEM_CALL;
+    }
+
+    KeBeginCycleAccounting(PreviousCycleAccount);
+    return Result;
 }
 
 //

+ 16 - 1
kernel/mm/testmm/stubs.c

@@ -744,7 +744,10 @@ Return Value:
 
 BOOL
 PsDispatchPendingSignalsOnCurrentThread (
-    PTRAP_FRAME TrapFrame
+    PTRAP_FRAME TrapFrame,
+    INTN SystemCallResult,
+    PVOID SystemCallParameter,
+    ULONG SystemCallNumber
     )
 
 /*++
@@ -759,6 +762,18 @@ Arguments:
     TrapFrame - Supplies a pointer to the current trap frame. If this trap frame
         is not destined for user mode, this function exits immediately.
 
+    SystemCallResult - Supplies the result of the system call that is
+        attempting to dispatch a pending signal. This is only valid if the
+        system call number is valid.
+
+    SystemCallParameter - Supplies a pointer to the parameters of the system
+        call that is attempting to dispatch a pending signal. This is a pointer
+        to user mode. This is only valid if the system call number if valid.
+
+    SystemCallNumber - Supplies the number of the system call that is
+        attempting to dispatch a pending signal. Supplied SystemCallInvalid if
+        the caller is not a system call.
+
 Return Value:
 
     FALSE if no signals are pending.

+ 82 - 13
kernel/ps/armv7/psarch.c

@@ -122,7 +122,10 @@ Return Value:
 VOID
 PsApplySynchronousSignal (
     PTRAP_FRAME TrapFrame,
-    PSIGNAL_PARAMETERS SignalParameters
+    PSIGNAL_PARAMETERS SignalParameters,
+    INTN SystemCallResult,
+    PVOID SystemCallParameter,
+    ULONG SystemCallNumber
     )
 
 /*++
@@ -140,6 +143,18 @@ Arguments:
 
     SignalParameters - Supplies a pointer to the signal information to apply.
 
+    SystemCallResult - Supplies the result of the system call that is
+        attempting to dispatch a pending signal. This is only valid if the
+        system call number is valid.
+
+    SystemCallParameter - Supplies a pointer to the parameters of the system
+        call that is attempting to dispatch a pending signal. This is a pointer
+        to user mode. This is only valid if the system call number if valid.
+
+    SystemCallNumber - Supplies the number of the system call that is
+        attempting to dispatch a pending signal. Supplied SystemCallInvalid if
+        the caller is not a system call.
+
 Return Value:
 
     None.
@@ -148,7 +163,7 @@ Return Value:
 
 {
 
-    SIGNAL_CONTEXT Context;
+    SIGNAL_CONTEXT_ARM Context;
     KSTATUS Status;
     KSTATUS Status2;
     PKTHREAD Thread;
@@ -162,8 +177,8 @@ Return Value:
     // call. Volatile registers don't matter in this case.
     //
 
-    RtlZeroMemory(&Context, sizeof(SIGNAL_CONTEXT));
-    Context.Signal = SignalParameters->SignalNumber;
+    RtlZeroMemory(&Context, sizeof(SIGNAL_CONTEXT_ARM));
+    Context.Common.Signal = SignalParameters->SignalNumber;
     Context.Pc = TrapFrame->Pc;
     Context.Sp = TrapFrame->UserSp;
     Context.Lr = TrapFrame->UserLink;
@@ -176,14 +191,47 @@ Return Value:
         Context.R12 = TrapFrame->R12;
     }
 
+    //
+    // If this signal is being applied in the middle of a system call, preserve
+    // the system call's return value.
+    //
+
+    if (SystemCallNumber != SystemCallInvalid) {
+
+        //
+        // If the result indicates that the system call is restartable, then
+        // let user mode know by setting the restart flag in the context and
+        // save the system call number and parameters in volatile registers.
+        // This should not be done for restore context or execute image, as
+        // their result values are unsigned and may overlap with the restart
+        // system call status. Convert the system call restart return value
+        // into an interrupted status in case the signal call handler does not
+        // allow restarting.
+        //
+
+        if (IS_SYSTEM_CALL_RESTARTABLE(SystemCallNumber, SystemCallResult)) {
+            Context.R0 = STATUS_INTERRUPTED;
+            Context.R1 = SystemCallNumber;
+            Context.R2 = (ULONG)SystemCallParameter;
+            Context.Common.Flags |= SIGNAL_CONTEXT_FLAG_RESTART;
+
+        //
+        // Otherwise just preserve the system call result.
+        //
+
+        } else {
+            Context.R0 = SystemCallResult;
+        }
+    }
+
     //
     // Mark the signal as running so that more don't come down on the thread
     // while it's servicing this one.
     //
 
-    ASSERT(!IS_SIGNAL_SET(Thread->RunningSignals, Context.Signal));
+    ASSERT(!IS_SIGNAL_SET(Thread->RunningSignals, Context.Common.Signal));
 
-    ADD_SIGNAL(Thread->RunningSignals, Context.Signal);
+    ADD_SIGNAL(Thread->RunningSignals, Context.Common.Signal);
 
     //
     // Copy the signal frame onto the stack.
@@ -191,10 +239,10 @@ Return Value:
     //
 
     TrapFrame->UserSp = ALIGN_RANGE_DOWN(TrapFrame->UserSp, STACK_ALIGNMENT);
-    TrapFrame->UserSp -= sizeof(SIGNAL_CONTEXT);
+    TrapFrame->UserSp -= sizeof(SIGNAL_CONTEXT_ARM);
     Status = MmCopyToUserMode((PVOID)(TrapFrame->UserSp),
                               &Context,
-                              sizeof(SIGNAL_CONTEXT));
+                              sizeof(SIGNAL_CONTEXT_ARM));
 
     TrapFrame->UserSp -= sizeof(SIGNAL_PARAMETERS);
     Status2 = MmCopyToUserMode((PVOID)(TrapFrame->UserSp),
@@ -207,7 +255,11 @@ Return Value:
                               TrapFrame,
                               Thread->OwningProcess);
 
-        PsDispatchPendingSignalsOnCurrentThread(TrapFrame);
+        PsDispatchPendingSignalsOnCurrentThread(TrapFrame,
+                                                SystemCallResult,
+                                                SystemCallParameter,
+                                                SystemCallNumber);
+
         return;
     }
 
@@ -247,12 +299,16 @@ Return Value:
 
 {
 
-    SIGNAL_CONTEXT Context;
+    SIGNAL_CONTEXT_ARM Context;
+    INTN Result;
     KSTATUS Status;
     PKTHREAD Thread;
 
     Thread = KeGetCurrentThread();
-    Status = MmCopyFromUserMode(&Context, UserContext, sizeof(SIGNAL_CONTEXT));
+    Status = MmCopyFromUserMode(&Context,
+                                (PSIGNAL_CONTEXT_ARM)UserContext,
+                                sizeof(SIGNAL_CONTEXT_ARM));
+
     if (!KSUCCESS(Status)) {
         PsSignalThread(Thread, SIGNAL_ACCESS_VIOLATION, NULL, TRUE);
         return 0;
@@ -260,7 +316,7 @@ Return Value:
 
     ASSERT((Context.Cpsr & ARM_MODE_MASK) == ARM_MODE_USER);
 
-    REMOVE_SIGNAL(Thread->RunningSignals, Context.Signal);
+    REMOVE_SIGNAL(Thread->RunningSignals, Context.Common.Signal);
     TrapFrame->R0 = Context.R0;
     TrapFrame->R1 = Context.R1;
     TrapFrame->R2 = Context.R2;
@@ -270,7 +326,20 @@ Return Value:
     TrapFrame->UserLink = Context.Lr;
     TrapFrame->Pc = Context.Pc;
     TrapFrame->Cpsr = Context.Cpsr;
-    return TrapFrame->R0;
+    Result = TrapFrame->R0;
+
+    //
+    // If the signal context indicates that a restart is necessary, then fire
+    // off the system call again. The trap frame must be restored before the
+    // restart or else the context saved on application of another signal would
+    // cause the next restore to return to the signal handler.
+    //
+
+    if ((Context.Common.Flags & SIGNAL_CONTEXT_FLAG_RESTART) != 0) {
+        Result = KeSystemCallHandler(Context.R1, (PVOID)Context.R2, TrapFrame);
+    }
+
+    return Result;
 }
 
 VOID

+ 84 - 25
kernel/ps/signals.c

@@ -730,8 +730,8 @@ Routine Description:
 Arguments:
 
     SystemCallParameter - Supplies a pointer to the parameters supplied with
-        the system call. This structure will be a stack-local copy of the
-        actual parameters passed from user-mode.
+        the system call. This is a pointer to the actual parameters passed from
+        user mode.
 
 Return Value:
 
@@ -744,7 +744,9 @@ Return Value:
 {
 
     PKPROCESS ChildProcess;
-    PSYSTEM_CALL_WAIT_FOR_CHILD Parameters;
+    KSTATUS CopyStatus;
+    SYSTEM_CALL_WAIT_FOR_CHILD Parameters;
+    BOOL SignalApplied;
     PSIGNAL_PARAMETERS SignalParameters;
     PSIGNAL_QUEUE_ENTRY SignalQueueEntry;
     KSTATUS Status;
@@ -752,13 +754,23 @@ Return Value:
 
     ASSERT(KeGetRunLevel() == RunLevelLow);
 
-    Parameters = (PSYSTEM_CALL_WAIT_FOR_CHILD)SystemCallParameter;
+    //
+    // Copy the parameters in from user mode.
+    //
+
+    Status = MmCopyFromUserMode(&Parameters,
+                                SystemCallParameter,
+                                sizeof(SYSTEM_CALL_WAIT_FOR_CHILD));
+
+    if (!KSUCCESS(Status)) {
+        goto SysWaitForChildProcessEnd;
+    }
 
     //
     // The caller must have specified one of the three required wait flags.
     //
 
-    if ((Parameters->Flags & SYSTEM_CALL_WAIT_FLAG_CHILD_MASK) == 0) {
+    if ((Parameters.Flags & SYSTEM_CALL_WAIT_FLAG_CHILD_MASK) == 0) {
         Status = STATUS_INVALID_PARAMETER;
         goto SysWaitForChildProcessEnd;
     }
@@ -777,7 +789,7 @@ Return Value:
         //
 
         Status = PspValidateWaitParameters(Thread->OwningProcess,
-                                           Parameters->ChildPid);
+                                           Parameters.ChildPid);
 
         if (!KSUCCESS(Status)) {
             goto SysWaitForChildProcessEnd;
@@ -787,8 +799,8 @@ Return Value:
         // Attempt to pull a child signal off one of the queues.
         //
 
-        SignalQueueEntry = PspGetChildSignalEntry(Parameters->ChildPid,
-                                                  Parameters->Flags);
+        SignalQueueEntry = PspGetChildSignalEntry(Parameters.ChildPid,
+                                                  Parameters.Flags);
 
         if (SignalQueueEntry != NULL) {
             SignalParameters = &(SignalQueueEntry->Parameters);
@@ -796,19 +808,19 @@ Return Value:
             ASSERT(SignalParameters->SignalNumber ==
                    SIGNAL_CHILD_PROCESS_ACTIVITY);
 
-            Parameters->ChildPid = SignalParameters->FromU.SendingProcess;
-            Parameters->Reason = SignalParameters->SignalCode;
+            Parameters.ChildPid = SignalParameters->FromU.SendingProcess;
+            Parameters.Reason = SignalParameters->SignalCode;
 
-            ASSERT(Parameters->Reason != 0);
+            ASSERT(Parameters.Reason != 0);
 
-            Parameters->ChildExitValue = SignalParameters->Parameter;
+            Parameters.ChildExitValue = SignalParameters->Parameter;
             Status = STATUS_SUCCESS;
-            if (Parameters->ResourceUsage != NULL) {
+            if (Parameters.ResourceUsage != NULL) {
                 ChildProcess = PARENT_STRUCTURE(SignalQueueEntry,
                                                 KPROCESS,
                                                 ChildSignal);
 
-                Status = MmCopyToUserMode(Parameters->ResourceUsage,
+                Status = MmCopyToUserMode(Parameters.ResourceUsage,
                                           &(ChildProcess->ResourceUsage),
                                           sizeof(RESOURCE_USAGE));
             }
@@ -832,7 +844,7 @@ Return Value:
         // then bail out now.
         //
 
-        if ((Parameters->Flags &
+        if ((Parameters.Flags &
              SYSTEM_CALL_WAIT_FLAG_RETURN_IMMEDIATELY) != 0) {
 
             Status = STATUS_NO_DATA_AVAILABLE;
@@ -853,7 +865,21 @@ Return Value:
         //
 
         PsCheckRuntimeTimers(Thread);
-        if (PsDispatchPendingSignals(Thread, Thread->TrapFrame) != FALSE) {
+        SignalApplied = PsDispatchPendingSignalsForSystemCall(
+                                                Thread,
+                                                Thread->TrapFrame,
+                                                STATUS_RESTART_SYSTEM_CALL,
+                                                SystemCallParameter,
+                                                SystemCallWaitForChildProcess);
+
+        if (SignalApplied != FALSE) {
+
+            //
+            // The system call handler also attempts to dispatch signals.
+            // Prevent a second restart attempt from coming in on top of the
+            // one initiated above.
+            //
+
             Status = STATUS_INTERRUPTED;
             break;
         }
@@ -861,10 +887,18 @@ Return Value:
 
 SysWaitForChildProcessEnd:
     if (!KSUCCESS(Status)) {
-        Parameters->ChildPid = -1;
+        Parameters.ChildPid = -1;
+    }
+
+    Parameters.Status = Status;
+    CopyStatus = MmCopyToUserMode(SystemCallParameter,
+                                  &Parameters,
+                                  sizeof(SYSTEM_CALL_WAIT_FOR_CHILD));
+
+    if (!KSUCCESS(CopyStatus) && KSUCCESS(Status)) {
+        Status = CopyStatus;
     }
 
-    Parameters->Status = Status;
     return Status;
 }
 
@@ -1092,10 +1126,6 @@ Return Value:
             }
         }
 
-        if (ApplySignal != FALSE) {
-            PsApplySynchronousSignal(Thread->TrapFrame, &SignalParameters);
-        }
-
         if (Parameters->SignalParameters != NULL) {
             CopyStatus = MmCopyToUserMode(Parameters->SignalParameters,
                                           &SignalParameters,
@@ -1103,9 +1133,19 @@ Return Value:
 
             if (!KSUCCESS(CopyStatus)) {
                 Status = CopyStatus;
-                goto SysSuspendExecutionEnd;
             }
         }
+
+        if (ApplySignal != FALSE) {
+
+            ASSERT(Status != STATUS_RESTART_SYSTEM_CALL);
+
+            PsApplySynchronousSignal(Thread->TrapFrame,
+                                     &SignalParameters,
+                                     Status,
+                                     NULL,
+                                     SystemCallSuspendExecution);
+        }
     }
 
 SysSuspendExecutionEnd:
@@ -1490,7 +1530,10 @@ Return Value:
 
 BOOL
 PsDispatchPendingSignalsOnCurrentThread (
-    PTRAP_FRAME TrapFrame
+    PTRAP_FRAME TrapFrame,
+    INTN SystemCallResult,
+    PVOID SystemCallParameter,
+    ULONG SystemCallNumber
     )
 
 /*++
@@ -1505,6 +1548,18 @@ Arguments:
     TrapFrame - Supplies a pointer to the current trap frame. If this trap frame
         is not destined for user mode, this function exits immediately.
 
+    SystemCallResult - Supplies the result of the system call that is
+        attempting to dispatch a pending signal. This is only valid if the
+        system call number is valid.
+
+    SystemCallParameter - Supplies a pointer to the parameters of the system
+        call that is attempting to dispatch a pending signal. This is a pointer
+        to user mode. This is only valid if the system call number if valid.
+
+    SystemCallNumber - Supplies the number of the system call that is
+        attempting to dispatch a pending signal. Supplied SystemCallInvalid if
+        the caller is not a system call.
+
 Return Value:
 
     FALSE if no signals are pending.
@@ -1527,7 +1582,11 @@ Return Value:
         }
 
         Applied = TRUE;
-        PsApplySynchronousSignal(TrapFrame, &SignalParameters);
+        PsApplySynchronousSignal(TrapFrame,
+                                 &SignalParameters,
+                                 SystemCallResult,
+                                 SystemCallParameter,
+                                 SystemCallNumber);
     }
 
     return Applied;

+ 84 - 13
kernel/ps/x86/psarch.c

@@ -137,7 +137,10 @@ Return Value:
 VOID
 PsApplySynchronousSignal (
     PTRAP_FRAME TrapFrame,
-    PSIGNAL_PARAMETERS SignalParameters
+    PSIGNAL_PARAMETERS SignalParameters,
+    INTN SystemCallResult,
+    PVOID SystemCallParameter,
+    ULONG SystemCallNumber
     )
 
 /*++
@@ -155,6 +158,18 @@ Arguments:
 
     SignalParameters - Supplies a pointer to the signal information to apply.
 
+    SystemCallResult - Supplies the result of the system call that is
+        attempting to dispatch a pending signal. This is only valid if the
+        system call number is valid.
+
+    SystemCallParameter - Supplies a pointer to the parameters of the system
+        call that is attempting to dispatch a pending signal. This is a pointer
+        to user mode. This is only valid if the system call number if valid.
+
+    SystemCallNumber - Supplies the number of the system call that is
+        attempting to dispatch a pending signal. Supplied SystemCallInvalid if
+        the caller is not a system call.
+
 Return Value:
 
     None.
@@ -163,7 +178,7 @@ Return Value:
 
 {
 
-    SIGNAL_CONTEXT Context;
+    SIGNAL_CONTEXT_X86 Context;
     KSTATUS Status;
     KSTATUS Status2;
     PKTHREAD Thread;
@@ -175,8 +190,8 @@ Return Value:
     // call. Volatile registers don't matter in this case.
     //
 
-    RtlZeroMemory(&Context, sizeof(SIGNAL_CONTEXT));
-    Context.Signal = SignalParameters->SignalNumber;
+    RtlZeroMemory(&Context, sizeof(SIGNAL_CONTEXT_X86));
+    Context.Common.Signal = SignalParameters->SignalNumber;
     Context.Eip = TrapFrame->Eip;
     Context.Esp = TrapFrame->Esp;
     Context.Eflags = IA32_EFLAG_ALWAYS_1 | IA32_EFLAG_IF;
@@ -190,24 +205,57 @@ Return Value:
         Context.Eflags = TrapFrame->Eflags;
     }
 
+    //
+    // If this signal is being applied in the middle of a system call, preserve
+    // the system call's return value.
+    //
+
+    if (SystemCallNumber != SystemCallInvalid) {
+
+        //
+        // If the result indicates that the system call is restartable, then
+        // let user mode know by setting the restart flag in the context and
+        // save the system call number and parameters in volatile registers.
+        // This should not be done for restore context or execute image, as
+        // their result values are unsigned and may overlap with the restart
+        // system call status. Convert the system call restart return value
+        // into an interrupted status in case the signal call handler does not
+        // allow restarting.
+        //
+
+        if (IS_SYSTEM_CALL_RESTARTABLE(SystemCallNumber, SystemCallResult)) {
+            Context.Eax = STATUS_INTERRUPTED;
+            Context.Ecx = SystemCallNumber;
+            Context.Edx = (ULONG)SystemCallParameter;
+            Context.Common.Flags |= SIGNAL_CONTEXT_FLAG_RESTART;
+
+        //
+        // Otherwise just preserve the system call result.
+        //
+
+        } else {
+            Context.Eax = SystemCallResult;
+        }
+    }
+
     //
     // Mark the signal as running so that more don't come down on the thread
     // while it's servicing this one.
     //
 
-    ASSERT(!IS_SIGNAL_SET(Thread->RunningSignals, Context.Signal));
+    ASSERT(!IS_SIGNAL_SET(Thread->RunningSignals, Context.Common.Signal));
 
-    ADD_SIGNAL(Thread->RunningSignals, Context.Signal);
+    ADD_SIGNAL(Thread->RunningSignals, Context.Common.Signal);
 
     //
     // Copy the signal frame onto the stack.
     // TODO: Support an alternate signal stack.
     //
 
-    TrapFrame->Esp -= sizeof(SIGNAL_CONTEXT);
+    TrapFrame->Esp -= sizeof(SIGNAL_CONTEXT_X86);
     Status = MmCopyToUserMode((PVOID)(TrapFrame->Esp),
                               &Context,
-                              sizeof(SIGNAL_CONTEXT));
+                              sizeof(SIGNAL_CONTEXT_X86));
 
     TrapFrame->Esp -= sizeof(SIGNAL_PARAMETERS);
     Status2 = MmCopyToUserMode((PVOID)(TrapFrame->Esp),
@@ -220,7 +268,11 @@ Return Value:
                               TrapFrame,
                               Thread->OwningProcess);
 
-        PsDispatchPendingSignalsOnCurrentThread(TrapFrame);
+        PsDispatchPendingSignalsOnCurrentThread(TrapFrame,
+                                                SystemCallResult,
+                                                SystemCallParameter,
+                                                SystemCallNumber);
+
         return;
     }
 
@@ -255,25 +307,44 @@ Return Value:
 
 {
 
-    SIGNAL_CONTEXT Context;
+    SIGNAL_CONTEXT_X86 Context;
+    INTN Result;
     KSTATUS Status;
     PKTHREAD Thread;
 
     Thread = KeGetCurrentThread();
-    Status = MmCopyFromUserMode(&Context, UserContext, sizeof(SIGNAL_CONTEXT));
+    Status = MmCopyFromUserMode(&Context,
+                                (PSIGNAL_CONTEXT_X86)UserContext,
+                                sizeof(SIGNAL_CONTEXT_X86));
+
     if (!KSUCCESS(Status)) {
         PsSignalThread(Thread, SIGNAL_ACCESS_VIOLATION, NULL, TRUE);
         return 0;
     }
 
-    REMOVE_SIGNAL(Thread->RunningSignals, Context.Signal);
+    REMOVE_SIGNAL(Thread->RunningSignals, Context.Common.Signal);
     TrapFrame->Eax = Context.Eax;
     TrapFrame->Ecx = Context.Ecx;
     TrapFrame->Edx = Context.Edx;
     TrapFrame->Eflags = Context.Eflags;
     TrapFrame->Eip = Context.Eip;
     TrapFrame->Esp = Context.Esp;
-    return TrapFrame->Eax;
+    Result = TrapFrame->Eax;
+
+    //
+    // If the signal context indicates that a restart is necessary, then fire
+    // off the system call again. The trap frame must be restored before the
+    // restart or else the context saved on application of another signal would
+    // cause the next restore to return to the signal handler.
+    //
+
+    if ((Context.Common.Flags & SIGNAL_CONTEXT_FLAG_RESTART) != 0) {
+        Result = KeSystemCallHandler(Context.Ecx,
+                                     (PVOID)Context.Edx,
+                                     TrapFrame);
+    }
+
+    return Result;
 }
 
 VOID