Browse Source

Add {get,set,make,swap}context functions to C library.

This change adds support for the functions that work with ucontext
structures, including getcontext, setcontext, makecontext, and
swapcontext. This change even adds support routines for x64 since it was
all paged in mentally, though it has yet to be tested.
Evan Green 7 years ago
parent
commit
b228f3a92f

+ 16 - 9
apps/libc/dynamic/Makefile

@@ -141,6 +141,7 @@ OBJS = assert.o             \
        time.o               \
        times.o              \
        tmpfile.o            \
+       ucontext.o           \
        uio.o                \
        uname.o              \
        usershel.o           \
@@ -151,21 +152,27 @@ OBJS = assert.o             \
        wstream.o            \
        wstring.o            \
 
-ARMV7_OBJS = armv7/fenva.o    \
+ARMV7_OBJS = armv7/contexta.o \
+             armv7/contextc.o \
+             armv7/fenva.o    \
              armv7/fenvc.o    \
              armv7/setjmpa.o  \
              armv7/tlsaddr.o  \
 
 ARMV6_OBJS = $(ARMV7_OBJS)
 
-X86_OBJS = x86/fenv.o    \
-           x86/fenvc.o   \
-           x86/setjmpa.o \
-           x86/tlsaddr.o \
-
-X64_OBJS = x64/fenv.o    \
-           x64/setjmpa.o \
-           x86/fenvc.o   \
+X86_OBJS = x86/contexta.o \
+           x86/contextc.o \
+           x86/fenv.o     \
+           x86/fenvc.o    \
+           x86/setjmpa.o  \
+           x86/tlsaddr.o  \
+
+X64_OBJS = x64/contexta.o \
+           x64/contextc.o \
+           x64/fenv.o     \
+           x64/setjmpa.o  \
+           x86/fenvc.o    \
 
 EXTRA_SRC_DIRS = x86 x64 armv7 math pthread
 

+ 253 - 0
apps/libc/dynamic/armv7/contexta.S

@@ -0,0 +1,253 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    contexta.S
+
+Abstract:
+
+    This module implements functionality for manipulating ucontext structures.
+
+Author:
+
+    Evan Green 9-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+##
+## ------------------------------------------------------------------- Includes
+##
+
+#include <minoca/kernel/arm.inc>
+
+##
+## ---------------------------------------------------------------- Definitions
+##
+
+##
+## ----------------------------------------------------------------------- Code
+##
+
+ASSEMBLY_FILE_HEADER
+.fpu vfpv3
+
+##
+## LIBC_API
+## int
+## getcontext (
+##     ucontext_t *Context
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the current user context into the given structure,
+    including the machine registers, signal mask, and execution stack pointer.
+    If restored, the returned context will appear to execute at the return from
+    this function.
+
+Arguments:
+
+    Context - Supplies a pointer where the current context is saved.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+EXPORTED_FUNCTION getcontext
+    str     %r1, [%r0, #(SIGNAL_CONTEXT_SIZE + TRAP_R1)]    @ Save R1.
+    str     %r2, [%r0, #(SIGNAL_CONTEXT_SIZE + TRAP_R2)]    @ Save R2.
+    str     %r3, [%r0, #(SIGNAL_CONTEXT_SIZE + TRAP_R3)]    @ Save R3.
+    mov     %r2, %r0                        @ Copy R0 into R2.
+    add     %r0, %r0, #SIGNAL_CONTEXT_SIZE  @ Get to the TRAP_FRAME part.
+    eor     %r1, %r1, %r1                   @ Clear R1.
+    mov     %r3, %sp                        @ Get SP in a regular register.
+    stmia   %r0!, {%r1, %r3, %lr}           @ Save SVC Sp, User SP, and User LR.
+    stmia   %r0!, {%r1}                     @ Save a zeroed R0.
+    stmia   %r0!, {%r1}                     @ Save a zeroed exception CPSR.
+    add     %r0, %r0, #12                   @ Skip R1-R3.
+    stmia   %r0!, {%r4-%r12}                @ Save R4-R12.
+    stmia   %r0!, {%r1, %lr}                @ Save zeroed SVC link, and PC.
+    mrs     %r1, CPSR                       @ Get the flags.
+    stmia   %r0!, {%r1}                     @ Save the CPSR.
+    mov     %r0, %r2                        @ Get the context back.
+    mov     %r1, %sp                        @ Stack pointer as second argument.
+    b       ClpGetContext                   @ Tail call to clpgetcontext.
+
+END_FUNCTION getcontext
+
+##
+## LIBC_API
+## int
+## setcontext (
+##     const ucontext_t *Context
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores a previous execution context into the current
+    processor.
+
+Arguments:
+
+    Context - Supplies a pointer to the previously saved context to restore.
+
+Return Value:
+
+    Does not return on success, as execution continues from the new context.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+EXPORTED_FUNCTION setcontext
+    stmdb   %sp!, {%r0}         @ Save R0.
+    bl      ClpSetContext       @ Call the C helper.
+    ldmia   %sp!, {%r0}         @ Restore R0.
+    add     %r2, %r0, #(SIGNAL_CONTEXT_SIZE + TRAP_USERSP) @ Get to user SP.
+    ldmia   %r2!, {%r3-%r7}     @ Get User SP, lr, R0, exception CPSR, and R1.
+    mov     %sp, %r3            @ Restore SP.
+    mov     %lr, %r4            @ Restore LR.
+    mov     %r0, %r5            @ Restore R0.
+    mov     %r1, %r7            @ Restore R1.
+    add     %r2, %r2, #8        @ Skip R2 and R3.
+    ldmia   %r2!, {%r4-%r12}    @ Restore R4-R12.
+    add     %r2, %r2, #4        @ Skip Svc link.
+    ldmia   %r2!, {%r3}         @ Restore PC into R3. Ignore CPSR.
+    bx      %r3                 @ Jump to PC.
+
+END_FUNCTION setcontext
+
+##
+## __NO_RETURN
+## void
+## ClpContextStart (
+##     void (*StartFunction)(),
+##     ...
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine is a small trampoline that calls the function specified in
+    makecontext.
+
+Arguments:
+
+    StartFunction - Supplies a pointer to the function to call.
+
+    ... - Supplies the arguments the start function takes.
+
+Return Value:
+
+    This routine does not return.
+
+--*/
+
+FUNCTION ClpContextStart
+    ldmia   %sp!, {%r12}            @ Get the function to call.
+    ldmia   %sp!, {%r0-%r3}         @ Pop the register arguments.
+    blx     %r12                    @ Jump to the function to call.
+    mov     %sp, %r4                @ Pop the function and arguments.
+    ldmia   %sp!, {%r0}             @ Pop the context pointer.
+    bl      ClpContextEnd           @ Call the C helper to switch contexts.
+    DEBUGGER_BREAK                  @ Execution should never reach here.
+
+END_FUNCTION ClpContextStart
+
+##
+## VOID
+## ClpSaveVfp (
+##     PFPU_CONTEXT Context,
+##     BOOL SimdSupport
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the Vector Floating Point unit state.
+
+Arguments:
+
+    Context - Supplies a pointer where the context will be saved.
+
+    SimdSupport - Supplies a boolean indicating whether the VFP unit contains
+        32 64-bit registers (TRUE) or 16 64-bit registers (FALSE).
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION ClpSaveVfp
+    stc     p11, c0, [%r0], #16*8       @ Save D0-D15 (stmia).
+    cmp     %r1, #0                     @ Test for no SIMD support.
+    ITE(ne)                             @ If equal then else.
+    stclne  p11, c0, [%r0], #16*8       @ Save D16-D31 if SIMD support.
+    addeq   %r0, %r0, #16*8             @ Skip those registers if not.
+    vmrs    %r2, FPSCR                  @ Get FPSCR.
+    str     %r2, [%r0]                  @ Store it.
+    bx      %lr                         @ Return.
+
+END_FUNCTION ClpSaveVfp
+
+##
+## VOID
+## ClpRestoreVfp (
+##     PFPU_CONTEXT Context,
+##     BOOL SimdSupport
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores the Vector Floating Point unit state into the
+    hardware.
+
+Arguments:
+
+    Context - Supplies a pointer to the context to restore.
+
+    SimdSupport - Supplies a boolean indicating whether the VFP unit contains
+        32 64-bit registers (TRUE) or 16 64-bit registers (FALSE).
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION ClpRestoreVfp
+    ldc     p11, c0, [%r0], #16*8       @ Restore D0-D15 (ldmia).
+    cmp     %r1, #0                     @ Test for no SIMD support.
+    ITE(ne)                             @ If equal then else.
+    ldclne  p11, c0, [%r0], #16*8       @ Restore D16-D31 if SIMD support.
+    addeq   %r0, %r0, #16*8             @ Skip those registers if not.
+    ldr     %r2, [%r0]                  @ Get FPSCR.
+    vmsr    FPSCR, %r2                  @ Restore FPSCR.
+    bx      %lr                         @ Return.
+
+END_FUNCTION ClpRestoreVfp
+

+ 382 - 0
apps/libc/dynamic/armv7/contextc.c

@@ -0,0 +1,382 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    contextc.c
+
+Abstract:
+
+    This module implements C support for working with ucontext structures in
+    the C library.
+
+Author:
+
+    Evan Green 9-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+//
+// ------------------------------------------------------------------- Includes
+//
+
+#include "../libcp.h"
+#include <alloca.h>
+#include <errno.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <minoca/kernel/arm.h>
+
+//
+// ---------------------------------------------------------------- Definitions
+//
+
+//
+// ------------------------------------------------------ Data Type Definitions
+//
+
+//
+// ----------------------------------------------- Internal Function Prototypes
+//
+
+__NO_RETURN
+VOID
+ClpContextEnd (
+    ucontext_t *Context
+    );
+
+__NO_RETURN
+void
+ClpContextStart (
+    void (*StartFunction)(),
+    ...
+    );
+
+VOID
+ClpSaveVfp (
+    PFPU_CONTEXT Context,
+    BOOL SimdSupport
+    );
+
+VOID
+ClpRestoreVfp (
+    PFPU_CONTEXT Context,
+    BOOL SimdSupport
+    );
+
+//
+// -------------------------------------------------------------------- Globals
+//
+
+//
+// ------------------------------------------------------------------ Functions
+//
+
+LIBC_API
+void
+makecontext (
+    ucontext_t *Context,
+    void (*StartFunction)(),
+    int ArgumentCount,
+    ...
+    )
+
+/*++
+
+Routine Description:
+
+    This routine modifies an initialized context to call the function provided
+    with the given arguments.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StartFunction - Supplies a pointer to the function to call when the
+        context is restored.
+
+    ArgumentCount - Supplies the number of int sized arguments supplied.
+
+    ... - Supplies the remaining arguments to pass to the function.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PUINTN Argument;
+    va_list ArgumentList;
+    UINTN Index;
+    UINTN Minimum;
+    PVOID StackTop;
+    PTRAP_FRAME TrapFrame;
+
+    if (Context == NULL) {
+        return;
+    }
+
+    //
+    // Create a stack that looks like this (starting with the last pushed):
+    // ClpContextStart
+    // StartFunction
+    // Argument1 (16 byte aligned)
+    // ...
+    // ArgumentN
+    // Context.
+    //
+
+    StackTop = (PVOID)(Context->uc_stack.ss_sp + Context->uc_stack.ss_size -
+                       sizeof(UINTN));
+
+    //
+    // At a minimum, push arguments to account for all the register passed
+    // arguments.
+    //
+
+    Minimum = 4;
+    if (ArgumentCount > Minimum) {
+        Minimum = ArgumentCount;
+    }
+
+    StackTop -= (Minimum + 1) * sizeof(UINTN);
+    StackTop = ALIGN_POINTER_DOWN(StackTop, 16);
+    StackTop -= 2 * sizeof(UINTN);
+    Argument = (PUINTN)StackTop;
+    *Argument = (UINTN)ClpContextStart;
+    Argument += 1;
+    *Argument = (UINTN)StartFunction;
+    Argument += 1;
+    va_start(ArgumentList, ArgumentCount);
+    for (Index = 0; Index < ArgumentCount; Index += 1) {
+        *Argument = va_arg(ArgumentList, UINTN);
+        Argument += 1;
+    }
+
+    va_end(ArgumentList);
+
+    //
+    // If there are fewer argumens than passed via register, pad it out.
+    //
+
+    while (Index < Minimum) {
+        *Argument = 0;
+        Argument += 1;
+        Index += 1;
+    }
+
+    //
+    // Make sure the stack is aligned.
+    //
+
+    if ((Index & 0x1) != 0) {
+        *Argument = 0;
+        Argument += 1;
+    }
+
+    *Argument = (UINTN)Context;
+
+    //
+    // Set the registers to point at the top of the stack.
+    //
+
+    TrapFrame = (PTRAP_FRAME)&(Context->uc_mcontext.gregs);
+    TrapFrame->R4 = (UINTN)Argument;
+    TrapFrame->R11 = 0;
+    TrapFrame->R7 = 0;
+    TrapFrame->UserSp = (UINTN)StackTop + sizeof(PVOID);
+    TrapFrame->Pc = (UINTN)ClpContextStart;
+    TrapFrame->Cpsr &= ~(PSR_FLAG_IT_STATE | PSR_FLAG_THUMB);
+    if ((TrapFrame->Pc & ARM_THUMB_BIT) != 0) {
+        TrapFrame->Cpsr |= PSR_FLAG_THUMB;
+    }
+
+    return;
+}
+
+int
+ClpGetContext (
+    ucontext_t *Context,
+    void *StackPointer
+    )
+
+/*++
+
+Routine Description:
+
+    This routine stores the current FPU and general context into the given
+    structure. The assembly code that calls this routine is responsible for
+    saving the general registers.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StackPointer - Supplies the current stack pointer.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+{
+
+    BOOL Aligned;
+    int Error;
+    PFPU_CONTEXT FpuContext;
+    BOOL SimdSupport;
+    void *StackBase;
+    size_t StackSize;
+    pthread_attr_t ThreadAttribute;
+
+    Error = pthread_getattr_np(pthread_self(), &ThreadAttribute);
+    if (Error != 0) {
+        errno = Error;
+        return -1;
+    }
+
+    Error = pthread_attr_getstack(&ThreadAttribute, &StackBase, &StackSize);
+    if (Error != 0) {
+        errno = Error;
+        return -1;
+    }
+
+    if (StackBase == NULL) {
+        StackBase = StackPointer;
+    }
+
+    Context->uc_flags = 0;
+    Context->uc_stack.ss_sp = StackPointer;
+    Context->uc_stack.ss_flags = 0;
+    Context->uc_stack.ss_size = StackSize;
+
+    //
+    // TODO: Enable this when sigaltstack is implemented.
+    //
+
+#if 0
+
+    //
+    // If currently on the signal stack, then the thread parameters aren't
+    // correct.
+    //
+
+    if (sigaltstack(NULL, &SignalStack) == 0) {
+        if ((SignalStack.ss_flags & SS_ONSTACK) != 0) {
+            Context->uc_stack = SignalStack;
+        }
+    }
+
+#endif
+
+    sigprocmask(0, NULL, &(Context->uc_sigmask));
+
+    //
+    // Save the floating point context if it exists. If it's not aligned,
+    // it will have to be saved into an aligned buffer and then copied.
+    //
+
+    if (OsTestProcessorFeature(OsArmVfp) != FALSE) {
+        Context->uc_flags |= SIGNAL_CONTEXT_FLAG_FPU_VALID;
+        SimdSupport = OsTestProcessorFeature(OsArmNeon32);
+        Aligned = IS_POINTER_ALIGNED(&(Context->uc_mcontext.fpregs),
+                                     __FPSTATE_ALIGNMENT);
+
+        if (Aligned != FALSE) {
+            FpuContext = (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs);
+
+        } else {
+            FpuContext = alloca(__FPSTATE_SIZE + __FPSTATE_ALIGNMENT);
+            FpuContext = ALIGN_POINTER_UP(FpuContext, __FPSTATE_ALIGNMENT);
+        }
+
+        //
+        // Save the floating point state.
+        //
+
+        ClpSaveVfp(FpuContext, SimdSupport);
+        if (FpuContext != (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs)) {
+            memcpy(&(Context->uc_mcontext.fpregs),
+                   FpuContext,
+                   sizeof(Context->uc_mcontext.fpregs));
+        }
+    }
+
+    pthread_attr_destroy(&ThreadAttribute);
+    return 0;
+}
+
+void
+ClpSetContext (
+    const ucontext_t *Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine restores the user context set in the given structure.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PFPU_CONTEXT FpuContext;
+    BOOL SimdSupport;
+
+    //
+    // Restore the floating point support if it exists.
+    //
+
+    if (((Context->uc_flags & SIGNAL_CONTEXT_FLAG_FPU_VALID) != 0) &&
+        (OsTestProcessorFeature(OsArmVfp) != FALSE)) {
+
+        SimdSupport = OsTestProcessorFeature(OsArmNeon32);
+
+        //
+        // If the structure causes the floating point context not to be aligned,
+        // allocate a temporary structure, align it, and copy the data in.
+        //
+
+        FpuContext = (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs);
+        if (!IS_POINTER_ALIGNED(FpuContext, __FPSTATE_ALIGNMENT)) {
+            FpuContext = alloca(__FPSTATE_SIZE + __FPSTATE_ALIGNMENT);
+            FpuContext = ALIGN_POINTER_UP(FpuContext, __FPSTATE_ALIGNMENT);
+            memcpy(FpuContext,
+                   &(Context->uc_mcontext.fpregs),
+                   sizeof(FPU_CONTEXT));
+        }
+
+        //
+        // Restore the floating point context using the appropriate mechanism.
+        //
+
+        ClpRestoreVfp(FpuContext, SimdSupport);
+    }
+
+    sigprocmask(SIG_SETMASK, &(Context->uc_sigmask), NULL);
+    return;
+}
+
+//
+// --------------------------------------------------------- Internal Functions
+//
+

+ 7 - 0
apps/libc/dynamic/build.ck

@@ -121,6 +121,7 @@ function build() {
         "time.c",
         "times.c",
         "tmpfile.c",
+        "ucontext.c",
         "uio.c",
         "uname.c",
         "usershel.c",
@@ -148,6 +149,8 @@ function build() {
 
     if ((arch == "armv7") || (arch == "armv6")) {
         arch_sources = [
+            "armv7/contexta.S",
+            "armv7/contextc.c",
             "armv7/fenva.S",
             "armv7/fenvc.c",
             "armv7/setjmpa.S",
@@ -156,6 +159,8 @@ function build() {
 
     } else if (arch == "x86") {
         arch_sources = [
+            "x86/contexta.S",
+            "x86/contextc.c",
             "x86/fenv.S",
             "x86/fenvc.c",
             "x86/setjmpa.S",
@@ -164,6 +169,8 @@ function build() {
 
     } else if (arch == "x64") {
         arch_sources = [
+            "x64/contexta.S",
+            "x64/contextc.c",
             "x64/fenv.S",
             "x64/fenvc.c",
             "x64/setjmpa.S",

+ 60 - 0
apps/libc/dynamic/pthread/pthread.c

@@ -972,6 +972,66 @@ Return Value:
     return pthread_gettid_np(pthread_self());
 }
 
+PTHREAD_API
+int
+pthread_getattr_np (
+    pthread_t ThreadId,
+    pthread_attr_t *Attribute
+    )
+
+/*++
+
+Routine Description:
+
+    This routine returns the current attributes for a given thread.
+
+Arguments:
+
+    ThreadId - Supplies the thread to get attributes for.
+
+    Attribute - Supplies a pointer where the attributes will be returned. The
+        detach state, stack size, stack base, and guard size may be different
+        from when the thread was created to reflect their actual values.
+
+Return Value:
+
+    0 on success.
+
+    Returns an error number on failure.
+
+--*/
+
+{
+
+    int Error;
+    struct rlimit Limit;
+    int OldError;
+    PPTHREAD Thread;
+
+    Thread = (PPTHREAD)ThreadId;
+    if (Thread->State == PthreadStateDetached) {
+        Thread->Attribute.Flags |= PTHREAD_FLAG_DETACHED;
+    }
+
+    //
+    // For the main thread, try to get the stack size.
+    //
+
+    if (Thread->Attribute.StackSize == 0) {
+        OldError = errno;
+        if (getrlimit(RLIMIT_STACK, &Limit) < 0) {
+            Error = errno;
+            errno = OldError;
+            return Error;
+        }
+
+        Thread->Attribute.StackSize = Limit.rlim_cur;
+    }
+
+    memcpy(Attribute, &(Thread->Attribute), sizeof(PTHREAD_ATTRIBUTE));
+    return 0;
+}
+
 PTHREAD_API
 void
 __pthread_cleanup_push (

+ 151 - 0
apps/libc/dynamic/ucontext.c

@@ -0,0 +1,151 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    ucontext.c
+
+Abstract:
+
+    This module implements architecture independent functions related to
+    manipulating ucontext structures.
+
+Author:
+
+    Evan Green 8-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+//
+// ------------------------------------------------------------------- Includes
+//
+
+#include "libcp.h"
+#include <errno.h>
+#include <signal.h>
+#include <ucontext.h>
+
+//
+// ---------------------------------------------------------------- Definitions
+//
+
+//
+// ------------------------------------------------------ Data Type Definitions
+//
+
+//
+// ----------------------------------------------- Internal Function Prototypes
+//
+
+//
+// -------------------------------------------------------------------- Globals
+//
+
+//
+// ------------------------------------------------------------------ Functions
+//
+
+LIBC_API
+int
+swapcontext (
+    ucontext_t *OldContext,
+    ucontext_t *Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine saves the current context, and sets the given new context
+    with a backlink to the original context.
+
+Arguments:
+
+    OldContext - Supplies a pointer where the currently running context will
+        be saved on success.
+
+    Context - Supplies a pointer to the new context to apply. A link to the
+        context running before this call will be saved in this context.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set contain more information.
+
+--*/
+
+{
+
+    int Status;
+
+    if ((OldContext == NULL) || (Context == NULL)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    OldContext->uc_flags &= ~SIGNAL_CONTEXT_FLAG_SWAPPED;
+    Status = getcontext(OldContext);
+
+    //
+    // Everything below this comment actually runs twice. The first time it is
+    // run the swapped flag is just recently cleared. In that case go run the
+    // new context. When the new context returns (via makecontext's function
+    // returning), it will return right here. The swapped flag will have been
+    // set so this routine doesn't go set the same context again.
+    //
+
+    if ((Status == 0) &&
+        ((OldContext->uc_flags & SIGNAL_CONTEXT_FLAG_SWAPPED) == 0)) {
+
+        OldContext->uc_flags |= SIGNAL_CONTEXT_FLAG_SWAPPED;
+        Status = setcontext(Context);
+    }
+
+    return Status;
+}
+
+__NO_RETURN
+VOID
+ClpContextEnd (
+    ucontext_t *Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine is called after the function entered via makecontext +
+    setcontext returns. It sets the next context, or exits the process if there
+    is no next context.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+Return Value:
+
+    This routine does not return. It either sets a new context or exits the
+    process.
+
+--*/
+
+{
+
+    if (Context->uc_link == NULL) {
+        exit(0);
+    }
+
+    setcontext((const ucontext_t *)Context->uc_link);
+    abort();
+}
+
+//
+// --------------------------------------------------------- Internal Functions
+//
+

+ 292 - 0
apps/libc/dynamic/x64/contexta.S

@@ -0,0 +1,292 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    contexta.S
+
+Abstract:
+
+    This module implements assembly functionality for working with ucontext
+    structures.
+
+Author:
+
+    Evan Green 9-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+##
+## ------------------------------------------------------------------- Includes
+##
+
+#include <minoca/kernel/x64.inc>
+
+##
+## ---------------------------------------------------------------- Definitions
+##
+
+##
+## ----------------------------------------------------------------------- Code
+##
+
+ASSEMBLY_FILE_HEADER
+
+##
+## LIBC_API
+## int
+## getcontext (
+##     ucontext_t *Context
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the current user context into the given structure,
+    including the machine registers, signal mask, and execution stack pointer.
+    If restored, the returned context will appear to execute at the return from
+    this function.
+
+Arguments:
+
+    Context - Supplies a pointer where the current context is saved.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+EXPORTED_FUNCTION(getcontext)
+    movq    %rdi, %rax              # Get the context.
+    addq    $SIGNAL_CONTEXT_SIZE, %rax  # Get to the TRAP_FRAME part.
+    movq    %rcx, TRAP_RCX(%rax)    # Save rcx.
+    xorq    %rcx, %rcx              # Clear a register.
+    movw    %cs, %cx                # Get DS.
+    movq    %rcx, TRAP_CS(%rax)     # Save CS.
+    movw    %ds, %cx                # Get DS.
+    movq    %rcx, TRAP_DS(%rax)     # Save DS.
+    movw    %es, %cx                # Get ES.
+    movq    %rcx, TRAP_ES(%rax)     # Save ES.
+    movw    %fs, %cx                # Get FS.
+    movq    %rcx, TRAP_FS(%rax)     # Save FS.
+    movw    %gs, %cx                # Get GS.
+    movq    %rcx, TRAP_GS(%rax)     # Save GS.
+    movw    %ss, %cx                # Get SS.
+    movq    %rcx, TRAP_SS(%rax)     # Save SS.
+    xorq    %rcx, %rcx              # Clear a register.
+    movq    %rcx, TRAP_RAX(%rax)    # Save a zeroed rax.
+    movq    %rbx, TRAP_RBX(%rax)    # Save rbx.
+    movq    %rdx, TRAP_RDX(%rax)    # Save rdx.
+    movq    %rsi, TRAP_RSI(%rax)    # Save rsi.
+    movq    %rdi, TRAP_RDI(%rax)    # Save rdi.
+    movq    %rbp, TRAP_RBP(%rax)    # Save rbp.
+    movq    %r8, TRAP_R8(%rax)      # Save r8.
+    movq    %r9, TRAP_R9(%rax)      # Save r9.
+    movq    %r10, TRAP_R10(%rax)    # Save r10.
+    movq    %r11, TRAP_R11(%rax)    # Save r11.
+    movq    %r12, TRAP_R12(%rax)    # Save r12.
+    movq    %r13, TRAP_R13(%rax)    # Save r13.
+    movq    %r14, TRAP_R14(%rax)    # Save r14.
+    movq    %r15, TRAP_R15(%rax)    # Save r15.
+    movq    %rcx, TRAP_ERRORCODE(%rax)  # Save zeroed error code.
+    movq    (%rsp), %rcx            # Get the return address.
+    movq    %rcx, TRAP_RIP(%rax)    # Save the instruction pointer.
+    pushfq                          # Push eflags.
+    popq    %rcx                    # Get eflags.
+    movq    %rcx, TRAP_EFLAGS(%rax) # Save eflags.
+    leaq    4(%rsp), %rcx           # Get the stack pointer (w/o return addr).
+    movq    %rcx, TRAP_ESP(%rax)    # Save that as rsp.
+    movq    %rsp, %rsi              # Set stack pointer as second arg.
+    call    ClpGetContext           # Call the C helper.
+    ret                             # Return whatever the C routine returned.
+
+END_FUNCTION(getcontext)
+
+##
+## LIBC_API
+## int
+## setcontext (
+##     const ucontext_t *Context
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores a previous execution context into the current
+    processor.
+
+Arguments:
+
+    Context - Supplies a pointer to the previously saved context to restore.
+
+Return Value:
+
+    Does not return on success, as execution continues from the new context.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+EXPORTED_FUNCTION(setcontext)
+    pushq   %rdi                    # Save the argument.
+    call    ClpSetContext           # Call the C helper.
+    popq    %rcx                    # Restore the argument.
+    addq    $SIGNAL_CONTEXT_SIZE, %rcx  # Get to the TRAP_FRAME part.
+    movq    TRAP_DS(%rcx), %rax     # Get DS.
+    movw    %ax, %ds                # Restore DS.
+    movq    TRAP_ES(%rcx), %rax     # Get ES.
+    movw    %ax, %es                # Restore ES.
+    movq    TRAP_FS(%rcx), %rax     # Get FS.
+    movw    %ax, %fs                # Restore FS.
+    movq    TRAP_GS(%rcx), %rax     # Get GS.
+    movw    %ax, %gs                # Restore GS.
+    movq    TRAP_SS(%rcx), %rax     # Get SS.
+    movw    %ax, %ss                # Restore SS.
+    movq    TRAP_R15(%rcx), %r15    # Restore r15.
+    movq    TRAP_R14(%rcx), %r14    # Restore r14.
+    movq    TRAP_R13(%rcx), %r13    # Restore r13.
+    movq    TRAP_R12(%rcx), %r12    # Restore r12.
+    movq    TRAP_R11(%rcx), %r11    # Restore r11.
+    movq    TRAP_R10(%rcx), %r10    # Restore r10.
+    movq    TRAP_R9(%rcx), %r9      # Restore r9.
+    movq    TRAP_R8(%rcx), %r8      # Restore r8.
+    movq    TRAP_RBP(%rcx), %rbp    # Restore rbp.
+    movq    TRAP_RDI(%rcx), %rdi    # Restore rdi.
+    movq    TRAP_RSI(%rcx), %rsi    # Restore rsi.
+    movq    TRAP_RDX(%rcx), %rdx    # Restore rdx.
+    movq    TRAP_RBX(%rcx), %rbx    # Restore rbx.
+    movq    TRAP_EFLAGS(%rcx), %rax # Get eflags.
+    pushq   %rax                    # Push eflags.
+    popfq                           # Pop eflags off the stack.
+    movq    TRAP_RAX(%rcx), %rax    # Restore rax as return value.
+
+    ##
+    ## This last part gets a little fishy depending on where the context
+    ## structure is. If the new rsp is on the same stack but greater than this
+    ## one, then this code runs the risk of taking a signal, which might
+    ## clobber the context before restoring RIP can be done. Hopefully that
+    ## doesn't happen.
+    ##
+
+    movq    TRAP_RSP(%rcx), %rsp    # Restore stack pointer.
+    jmp     *TRAP_RIP(%rcx)         # Return.
+
+END_FUNCTION(setcontext)
+
+##
+## __NO_RETURN
+## void
+## ClpContextStart (
+##     void (*StartFunction)(),
+##     ...
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine is a small trampoline that calls the function specified in
+    makecontext.
+
+Arguments:
+
+    StartFunction - Supplies a pointer to the function to call.
+
+    ... - Supplies the arguments the start function takes.
+
+Return Value:
+
+    This routine does not return.
+
+--*/
+
+FUNCTION(ClpContextStart)
+    popq    %rax                    # Get the function to call.
+    popq    %rdi                    # Pop argument 1.
+    popq    %rsi                    # Pop argument 2.
+    popq    %rdx                    # Pop argument 3.
+    popq    %rcx                    # Pop argument 4.
+    popq    %r8                     # Pop argument 5.
+    popq    %r9                     # Pop argument 6.
+    callq   *%rax                   # Make it rain.
+    movq    %r12, %rsp              # Pop the function and all arguments off.
+    call    ClpContextEnd           # Call the C helper to switch contexts.
+    hlt                             # Execution should never reach here.
+
+END_FUNCTION(ClpContextStart)
+
+##
+## VOID
+## ClpFxSave (
+##     PFPU_CONTEXT Buffer
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the current x87 FPU, MMX, XMM, and MXCSR registers to a
+    512 byte memory location.
+
+Arguments:
+
+    Buffer - Supplies a pointer to the buffer where the information will be
+        saved. This buffer must be 16-byte aligned.
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION(ClpFxSave)
+    fxsave  (%rdi)          # Save the state into there.
+    ret
+
+END_FUNCTION(ClpFxSave)
+
+##
+## VOID
+## ClpFxRestore (
+##     PFPU_CONTEXT Buffer
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores the current x87 FPU, MMX, XMM, and MXCSR registers
+    from a 512 byte memory location.
+
+Arguments:
+
+    Buffer - Supplies a pointer to the buffer where the information will be
+        loaded from. This buffer must be 16-byte aligned.
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION(ClpFxRestore)
+    fxrstor (%rdi)          # Load the state from there.
+    ret
+
+END_FUNCTION(ClpFxRestore)
+

+ 364 - 0
apps/libc/dynamic/x64/contextc.c

@@ -0,0 +1,364 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    contextc.c
+
+Abstract:
+
+    This module implements C support for working with ucontext structures in
+    the C library.
+
+Author:
+
+    Evan Green 9-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+//
+// ------------------------------------------------------------------- Includes
+//
+
+#include "../libcp.h"
+#include <alloca.h>
+#include <errno.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <minoca/kernel/x64.h>
+
+//
+// ---------------------------------------------------------------- Definitions
+//
+
+//
+// ------------------------------------------------------ Data Type Definitions
+//
+
+//
+// ----------------------------------------------- Internal Function Prototypes
+//
+
+__NO_RETURN
+VOID
+ClpContextEnd (
+    ucontext_t *Context
+    );
+
+__NO_RETURN
+void
+ClpContextStart (
+    void (*StartFunction)(),
+    ...
+    );
+
+VOID
+ClpFxSave (
+    PFPU_CONTEXT Buffer
+    );
+
+VOID
+ClpFxRestore (
+    PFPU_CONTEXT Buffer
+    );
+
+//
+// -------------------------------------------------------------------- Globals
+//
+
+//
+// ------------------------------------------------------------------ Functions
+//
+
+//
+// TODO: Test these ucontext routines once x64 is running.
+//
+
+LIBC_API
+void
+makecontext (
+    ucontext_t *Context,
+    void (*StartFunction)(),
+    int ArgumentCount,
+    ...
+    )
+
+/*++
+
+Routine Description:
+
+    This routine modifies an initialized context to call the function provided
+    with the given arguments.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StartFunction - Supplies a pointer to the function to call when the
+        context is restored.
+
+    ArgumentCount - Supplies the number of int sized arguments supplied.
+
+    ... - Supplies the remaining arguments to pass to the function.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PUINTN Argument;
+    va_list ArgumentList;
+    UINTN Index;
+    UINTN Minimum;
+    PVOID StackTop;
+    PTRAP_FRAME TrapFrame;
+
+    if (Context == NULL) {
+        return;
+    }
+
+    //
+    // Create a stack that looks like this (starting with the last pushed):
+    // ClpContextStart
+    // StartFunction
+    // Argument1 (16 byte aligned)
+    // ...
+    // ArgumentN
+    // Context.
+    //
+
+    StackTop = (PVOID)(Context->uc_stack.ss_sp + Context->uc_stack.ss_size -
+                       sizeof(UINTN));
+
+    //
+    // At a minimum, push arguments to account for all the register passed
+    // arguments.
+    //
+
+    Minimum = 6;
+    if (ArgumentCount > Minimum) {
+        Minimum = ArgumentCount;
+    }
+
+    StackTop -= (Minimum + 1) * sizeof(UINTN);
+    StackTop = ALIGN_POINTER_DOWN(StackTop, 16);
+    StackTop -= 2 * sizeof(UINTN);
+    Argument = (PUINTN)StackTop;
+    *Argument = (UINTN)ClpContextStart;
+    Argument += 1;
+    *Argument = (UINTN)StartFunction;
+    Argument += 1;
+    va_start(ArgumentList, ArgumentCount);
+    for (Index = 0; Index < ArgumentCount; Index += 1) {
+        *Argument = va_arg(ArgumentList, UINTN);
+        Argument += 1;
+    }
+
+    va_end(ArgumentList);
+
+    //
+    // If there are fewer argumens than passed via register, pad it out.
+    //
+
+    while (Index < Minimum) {
+        *Argument = 0;
+        Argument += 1;
+        Index += 1;
+    }
+
+    //
+    // Make sure the stack is aligned.
+    //
+
+    if ((Index & 0x1) != 0) {
+        *Argument = 0;
+        Argument += 1;
+    }
+
+    *Argument = (UINTN)Context;
+
+    //
+    // Set the registers to point at the top of the stack.
+    //
+
+    TrapFrame = (PTRAP_FRAME)&(Context->uc_mcontext.gregs);
+    TrapFrame->R12 = (UINTN)Argument;
+    TrapFrame->Rbp = 0;
+    TrapFrame->Rsp = (UINTN)StackTop + sizeof(PVOID);
+    TrapFrame->Rip = (UINTN)ClpContextStart;
+    return;
+}
+
+int
+ClpGetContext (
+    ucontext_t *Context,
+    void *StackPointer
+    )
+
+/*++
+
+Routine Description:
+
+    This routine stores the current FPU and general context into the given
+    structure. The assembly code that calls this routine is responsible for
+    saving the general registers.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StackPointer - Supplies the current stack pointer.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+{
+
+    BOOL Aligned;
+    int Error;
+    PFPU_CONTEXT FpuContext;
+    void *StackBase;
+    size_t StackSize;
+    pthread_attr_t ThreadAttribute;
+
+    Error = pthread_getattr_np(pthread_self(), &ThreadAttribute);
+    if (Error != 0) {
+        errno = Error;
+        return -1;
+    }
+
+    Error = pthread_attr_getstack(&ThreadAttribute, &StackBase, &StackSize);
+    if (Error != 0) {
+        errno = Error;
+        return -1;
+    }
+
+    if (StackBase == NULL) {
+        StackBase = StackPointer;
+    }
+
+    Context->uc_flags = SIGNAL_CONTEXT_FLAG_FPU_VALID;
+    Context->uc_stack.ss_sp = StackPointer;
+    Context->uc_stack.ss_flags = 0;
+    Context->uc_stack.ss_size = StackSize;
+
+    //
+    // TODO: Enable this when sigaltstack is implemented.
+    //
+
+#if 0
+
+    //
+    // If currently on the signal stack, then the thread parameters aren't
+    // correct.
+    //
+
+    if (sigaltstack(NULL, &SignalStack) == 0) {
+        if ((SignalStack.ss_flags & SS_ONSTACK) != 0) {
+            Context->uc_stack = SignalStack;
+        }
+    }
+
+#endif
+
+    sigprocmask(0, NULL, &(Context->uc_sigmask));
+
+    //
+    // Get the FPU context buffer. If it's not aligned, it will have to be
+    // saved into an aligned buffer and then copied.
+    //
+
+    Aligned = IS_POINTER_ALIGNED(&(Context->uc_mcontext.fpregs),
+                                 __FPSTATE_ALIGNMENT);
+
+    if (Aligned != FALSE) {
+        FpuContext = (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs);
+
+    } else {
+        FpuContext = alloca(__FPSTATE_SIZE + __FPSTATE_ALIGNMENT);
+        FpuContext = ALIGN_POINTER_UP(FpuContext, __FPSTATE_ALIGNMENT);
+    }
+
+    //
+    // Save the floating point state.
+    //
+
+    ClpFxSave(FpuContext);
+    if (FpuContext != (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs)) {
+        memcpy(&(Context->uc_mcontext.fpregs),
+               FpuContext,
+               sizeof(Context->uc_mcontext.fpregs));
+    }
+
+    pthread_attr_destroy(&ThreadAttribute);
+    return 0;
+}
+
+void
+ClpSetContext (
+    const ucontext_t *Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine restores the user context set in the given structure.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PFPU_CONTEXT FpuContext;
+
+    //
+    // If the structure causes the floating point context not to be aligned,
+    // allocate a temporary structure, align it, and copy the data in.
+    //
+
+    if ((Context->uc_flags & SIGNAL_CONTEXT_FLAG_FPU_VALID) != 0) {
+        FpuContext = (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs);
+        if (!IS_POINTER_ALIGNED(FpuContext, __FPSTATE_ALIGNMENT)) {
+            FpuContext = alloca(__FPSTATE_SIZE + __FPSTATE_ALIGNMENT);
+            FpuContext = ALIGN_POINTER_UP(FpuContext, __FPSTATE_ALIGNMENT);
+            memcpy(FpuContext,
+                   &(Context->uc_mcontext.fpregs),
+                   sizeof(FPU_CONTEXT));
+        }
+
+        //
+        // Restore the floating point context using the appropriate mechanism.
+        //
+
+        ClpFxRestore(FpuContext);
+    }
+
+    sigprocmask(SIG_SETMASK, &(Context->uc_sigmask), NULL);
+    return;
+}
+
+//
+// --------------------------------------------------------- Internal Functions
+//
+

+ 345 - 0
apps/libc/dynamic/x86/contexta.S

@@ -0,0 +1,345 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    contexta.S
+
+Abstract:
+
+    This module implements assembly functionality for working with ucontext
+    structure.
+
+Author:
+
+    Evan Green 8-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+##
+## ------------------------------------------------------------------- Includes
+##
+
+#include <minoca/kernel/x86.inc>
+
+##
+## ---------------------------------------------------------------- Definitions
+##
+
+##
+## ----------------------------------------------------------------------- Code
+##
+
+##
+## .text specifies that this code belongs in the executable section.
+##
+## .code32 specifies that this is 32-bit protected mode code.
+##
+
+.text
+.code32
+
+##
+## LIBC_API
+## int
+## getcontext (
+##     ucontext_t *Context
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the current user context into the given structure,
+    including the machine registers, signal mask, and execution stack pointer.
+    If restored, the returned context will appear to execute at the return from
+    this function.
+
+Arguments:
+
+    Context - Supplies a pointer where the current context is saved.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+EXPORTED_FUNCTION(getcontext)
+    movl    4(%esp), %eax           # Get the context.
+    addl    $SIGNAL_CONTEXT_SIZE, %eax  # Get to the TRAP_FRAME part.
+    movl    %ecx, TRAP_ECX(%eax)    # Save ecx.
+    xorl    %ecx, %ecx              # Clear a register.
+    movw    %cs, %cx                # Get DS.
+    movl    %ecx, TRAP_CS(%eax)     # Save CS.
+    movw    %ds, %cx                # Get DS.
+    movl    %ecx, TRAP_DS(%eax)     # Save DS.
+    movw    %es, %cx                # Get ES.
+    movl    %ecx, TRAP_ES(%eax)     # Save ES.
+    movw    %fs, %cx                # Get FS.
+    movl    %ecx, TRAP_FS(%eax)     # Save FS.
+    movw    %gs, %cx                # Get GS.
+    movl    %ecx, TRAP_GS(%eax)     # Save GS.
+    movw    %ss, %cx                # Get SS.
+    movl    %ecx, TRAP_SS(%eax)     # Save SS.
+    xorl    %ecx, %ecx              # Clear a register.
+    movl    %ecx, TRAP_EAX(%eax)    # Save a zeroed eax.
+    movl    %ebx, TRAP_EBX(%eax)    # Save ebx.
+    movl    %edx, TRAP_EDX(%eax)    # Save edx.
+    movl    %esi, TRAP_ESI(%eax)    # Save esi.
+    movl    %edi, TRAP_EDI(%eax)    # Save edi.
+    movl    %ebp, TRAP_EBP(%eax)    # Save ebp.
+    movl    %ecx, TRAP_ERRORCODE(%eax)  # Save zeroed error code.
+    movl    (%esp), %ecx            # Get the return address.
+    movl    %ecx, TRAP_EIP(%eax)    # Save the instruction pointer.
+    pushfl                          # Push eflags.
+    popl    %ecx                    # Get eflags.
+    movl    %ecx, TRAP_EFLAGS(%eax) # Save eflags.
+    leal    4(%esp), %ecx           # Get the stack pointer (w/o return addr).
+    movl    %ecx, TRAP_ESP(%eax)    # Save that as esp.
+    pushl   %ecx                    # Push stack pointer.
+    movl    8(%esp), %eax           # Get the context pointer.
+    pushl   %eax                    # Push it as the first argument.
+    call    ClpGetContext           # Call the C helper.
+    addl    $8, %esp                # Pop the arguments.
+    ret                             # Return whatever the C routine returned.
+
+END_FUNCTION(getcontext)
+
+##
+## LIBC_API
+## int
+## setcontext (
+##     const ucontext_t *Context
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores a previous execution context into the current
+    processor.
+
+Arguments:
+
+    Context - Supplies a pointer to the previously saved context to restore.
+
+Return Value:
+
+    Does not return on success, as execution continues from the new context.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+EXPORTED_FUNCTION(setcontext)
+    movl    4(%esp), %eax           # Get the argument.
+    pushl   %eax
+    call    ClpSetContext           # Call the C helper.
+    movl    8(%esp), %ecx           # Get the argument again.
+    addl    $SIGNAL_CONTEXT_SIZE, %ecx  # Get to the TRAP_FRAME part.
+    movl    TRAP_DS(%ecx), %eax     # Get DS.
+    movw    %ax, %ds                # Restore DS.
+    movl    TRAP_ES(%ecx), %eax     # Get ES.
+    movw    %ax, %es                # Restore ES.
+    movl    TRAP_FS(%ecx), %eax     # Get FS.
+    movw    %ax, %fs                # Restore FS.
+    movl    TRAP_GS(%ecx), %eax     # Get GS.
+    movw    %ax, %gs                # Restore GS.
+    movl    TRAP_SS(%ecx), %eax     # Get SS.
+    movw    %ax, %ss                # Restore SS.
+    movl    TRAP_EBP(%ecx), %ebp    # Restore ebp.
+    movl    TRAP_EDI(%ecx), %edi    # Restore edi.
+    movl    TRAP_ESI(%ecx), %esi    # Restore esi.
+    movl    TRAP_EDX(%ecx), %edx    # Restore edx.
+    movl    TRAP_EBX(%ecx), %ebx    # Restore ebx.
+    movl    TRAP_EFLAGS(%ecx), %eax # Get eflags.
+    movl    %eax, (%esp)            # "Push" eflags into old unpopped argument.
+    popfl                           # Pop eflags/old argument off the stack.
+    movl    TRAP_EAX(%ecx), %eax    # Restore eax as return value.
+
+    ##
+    ## This last part gets a little fishy depending on where the context
+    ## structure is. If the new esp is on the same stack but greater than this
+    ## one, then this code runs the risk of taking a signal, which might
+    ## clobber the context before restoring EIP can be done. Hopefully that
+    ## doesn't happen.
+    ##
+
+    movl    TRAP_ESP(%ecx), %esp    # Restore stack pointer.
+    jmp     *TRAP_EIP(%ecx)         # Return.
+
+END_FUNCTION(setcontext)
+
+##
+## __NO_RETURN
+## void
+## ClpContextStart (
+##     void (*StartFunction)(),
+##     ...
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine is a small trampoline that calls the function specified in
+    makecontext.
+
+Arguments:
+
+    StartFunction - Supplies a pointer to the function to call.
+
+    ... - Supplies the arguments the start function takes.
+
+Return Value:
+
+    This routine does not return.
+
+--*/
+
+FUNCTION(ClpContextStart)
+    popl    %eax                    # Get the function to call.
+    call    *%eax                   # Make it rain.
+    movl    %esi, %esp              # Pop the function and all arguments off.
+    call    ClpContextEnd           # Call the C helper to switch contexts.
+    hlt                             # Execution should never reach here.
+
+END_FUNCTION(ClpContextStart)
+
+##
+## VOID
+## ClpFxSave (
+##     PFPU_CONTEXT Buffer
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the current x87 FPU, MMX, XMM, and MXCSR registers to a
+    512 byte memory location.
+
+Arguments:
+
+    Buffer - Supplies a pointer to the buffer where the information will be
+        saved. This buffer must be 16-byte aligned.
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION(ClpFxSave)
+    movl    4(%esp), %eax   # Get the buffer parameter.
+    fxsave  (%eax)          # Save the state into there.
+    ret
+
+END_FUNCTION(ClpFxSave)
+
+##
+## VOID
+## ClpFxRestore (
+##     PFPU_CONTEXT Buffer
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores the current x87 FPU, MMX, XMM, and MXCSR registers
+    from a 512 byte memory location.
+
+Arguments:
+
+    Buffer - Supplies a pointer to the buffer where the information will be
+        loaded from. This buffer must be 16-byte aligned.
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION(ClpFxRestore)
+    movl    4(%esp), %eax   # Get the buffer parameter.
+    fxrstor (%eax)          # Load the state from there.
+    ret
+
+END_FUNCTION(ClpFxRestore)
+
+##
+## VOID
+## ClpFSave (
+##     PFPU_CONTEXT Buffer
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine saves the current x87 FPU (floating point unit) state.
+
+Arguments:
+
+    Buffer - Supplies a pointer to the buffer where the information will be
+        saved. This buffer must be 16-byte aligned.
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION(ClpFSave)
+    movl    4(%esp), %eax   # Get the buffer parameter.
+    fsave  (%eax)           # Save the state into there.
+    ret
+
+END_FUNCTION(ClpFSave)
+
+##
+## VOID
+## ClpFRestore (
+##     PFPU_CONTEXT Buffer
+##     )
+##
+
+/*++
+
+Routine Description:
+
+    This routine restores the x87 FPU (floating point unit) state.
+
+Arguments:
+
+    Buffer - Supplies a pointer to the buffer where the information will be
+        loaded from. This buffer must be 16-byte aligned.
+
+Return Value:
+
+    None.
+
+--*/
+
+FUNCTION(ClpFRestore)
+    movl    4(%esp), %eax   # Get the buffer parameter.
+    frstor (%eax)           # Load the state from there.
+    ret
+
+END_FUNCTION(ClpFRestore)
+

+ 351 - 0
apps/libc/dynamic/x86/contextc.c

@@ -0,0 +1,351 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    contextc.c
+
+Abstract:
+
+    This module implements C support for working with ucontext structures in
+    the C library.
+
+Author:
+
+    Evan Green 8-Sep-2016
+
+Environment:
+
+    User Mode C Library
+
+--*/
+
+//
+// ------------------------------------------------------------------- Includes
+//
+
+#include "../libcp.h"
+#include <alloca.h>
+#include <errno.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <minoca/kernel/x86.h>
+
+//
+// ---------------------------------------------------------------- Definitions
+//
+
+//
+// ------------------------------------------------------ Data Type Definitions
+//
+
+//
+// ----------------------------------------------- Internal Function Prototypes
+//
+
+__NO_RETURN
+VOID
+ClpContextEnd (
+    ucontext_t *Context
+    );
+
+__NO_RETURN
+void
+ClpContextStart (
+    void (*StartFunction)(),
+    ...
+    );
+
+VOID
+ClpFxSave (
+    PFPU_CONTEXT Buffer
+    );
+
+VOID
+ClpFxRestore (
+    PFPU_CONTEXT Buffer
+    );
+
+VOID
+ClpFSave (
+    PFPU_CONTEXT Buffer
+    );
+
+VOID
+ClpFRestore (
+    PFPU_CONTEXT Buffer
+    );
+
+//
+// -------------------------------------------------------------------- Globals
+//
+
+//
+// ------------------------------------------------------------------ Functions
+//
+
+LIBC_API
+void
+makecontext (
+    ucontext_t *Context,
+    void (*StartFunction)(),
+    int ArgumentCount,
+    ...
+    )
+
+/*++
+
+Routine Description:
+
+    This routine modifies an initialized context to call the function provided
+    with the given arguments.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StartFunction - Supplies a pointer to the function to call when the
+        context is restored.
+
+    ArgumentCount - Supplies the number of int sized arguments supplied.
+
+    ... - Supplies the remaining arguments to pass to the function.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PUINTN Argument;
+    va_list ArgumentList;
+    UINTN Index;
+    PVOID StackTop;
+    PTRAP_FRAME TrapFrame;
+
+    if (Context == NULL) {
+        return;
+    }
+
+    //
+    // Create a stack that looks like this (starting with the last pushed):
+    // ClpContextStart
+    // StartFunction
+    // Argument1 (16 byte aligned)
+    // ...
+    // ArgumentN
+    // Context.
+    //
+
+    StackTop = (PVOID)(Context->uc_stack.ss_sp + Context->uc_stack.ss_size -
+                       sizeof(UINTN));
+
+    StackTop -= (ArgumentCount + 1) * sizeof(UINTN);
+    StackTop = ALIGN_POINTER_DOWN(StackTop, 16);
+    StackTop -= 2 * sizeof(UINTN);
+    Argument = (PUINTN)StackTop;
+    *Argument = (UINTN)ClpContextStart;
+    Argument += 1;
+    *Argument = (UINTN)StartFunction;
+    Argument += 1;
+    va_start(ArgumentList, ArgumentCount);
+    for (Index = 0; Index < ArgumentCount; Index += 1) {
+        *Argument = va_arg(ArgumentList, UINTN);
+        Argument += 1;
+    }
+
+    va_end(ArgumentList);
+    *Argument = (UINTN)Context;
+
+    //
+    // Set the registers to point at the top of the stack.
+    //
+
+    TrapFrame = (PTRAP_FRAME)&(Context->uc_mcontext.gregs);
+    TrapFrame->Esi = (UINTN)Argument;
+    TrapFrame->Ebp = 0;
+    TrapFrame->Esp = (UINTN)StackTop + sizeof(PVOID);
+    TrapFrame->Eip = (UINTN)ClpContextStart;
+    return;
+}
+
+int
+ClpGetContext (
+    ucontext_t *Context,
+    void *StackPointer
+    )
+
+/*++
+
+Routine Description:
+
+    This routine stores the current FPU and general context into the given
+    structure. The assembly code that calls this routine is responsible for
+    saving the general registers.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StackPointer - Supplies the current stack pointer.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+{
+
+    BOOL Aligned;
+    int Error;
+    PFPU_CONTEXT FpuContext;
+    void *StackBase;
+    size_t StackSize;
+    pthread_attr_t ThreadAttribute;
+
+    Error = pthread_getattr_np(pthread_self(), &ThreadAttribute);
+    if (Error != 0) {
+        errno = Error;
+        return -1;
+    }
+
+    Error = pthread_attr_getstack(&ThreadAttribute, &StackBase, &StackSize);
+    if (Error != 0) {
+        errno = Error;
+        return -1;
+    }
+
+    if (StackBase == NULL) {
+        StackBase = StackPointer;
+    }
+
+    Context->uc_flags = SIGNAL_CONTEXT_FLAG_FPU_VALID;
+    Context->uc_stack.ss_sp = StackPointer;
+    Context->uc_stack.ss_flags = 0;
+    Context->uc_stack.ss_size = StackSize;
+
+    //
+    // TODO: Enable this when sigaltstack is implemented.
+    //
+
+#if 0
+
+    //
+    // If currently on the signal stack, then the thread parameters aren't
+    // correct.
+    //
+
+    if (sigaltstack(NULL, &SignalStack) == 0) {
+        if ((SignalStack.ss_flags & SS_ONSTACK) != 0) {
+            Context->uc_stack = SignalStack;
+        }
+    }
+
+#endif
+
+    sigprocmask(0, NULL, &(Context->uc_sigmask));
+
+    //
+    // Get the FPU context buffer. If it's not aligned, it will have to be
+    // saved into an aligned buffer and then copied.
+    //
+
+    Aligned = IS_POINTER_ALIGNED(&(Context->uc_mcontext.fpregs),
+                                 __FPSTATE_ALIGNMENT);
+
+    if (Aligned != FALSE) {
+        FpuContext = (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs);
+
+    } else {
+        FpuContext = alloca(__FPSTATE_SIZE + __FPSTATE_ALIGNMENT);
+        FpuContext = ALIGN_POINTER_UP(FpuContext, __FPSTATE_ALIGNMENT);
+    }
+
+    //
+    // Save the floating point state.
+    //
+
+    if (OsTestProcessorFeature(OsX86FxSave) != FALSE) {
+        ClpFxSave(FpuContext);
+
+    } else {
+        ClpFSave(FpuContext);
+    }
+
+    if (FpuContext != (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs)) {
+        memcpy(&(Context->uc_mcontext.fpregs),
+               FpuContext,
+               sizeof(Context->uc_mcontext.fpregs));
+    }
+
+    pthread_attr_destroy(&ThreadAttribute);
+    return 0;
+}
+
+void
+ClpSetContext (
+    const ucontext_t *Context
+    )
+
+/*++
+
+Routine Description:
+
+    This routine restores the user context set in the given structure.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    PFPU_CONTEXT FpuContext;
+
+    //
+    // Restore the floating point context using the appropriate mechanism.
+    //
+
+    if ((Context->uc_flags & SIGNAL_CONTEXT_FLAG_FPU_VALID) != 0) {
+
+        //
+        // If the structure causes the floating point context not to be aligned,
+        // allocate a temporary structure, align it, and copy the data in.
+        //
+
+        FpuContext = (PFPU_CONTEXT)&(Context->uc_mcontext.fpregs);
+        if (!IS_POINTER_ALIGNED(FpuContext, __FPSTATE_ALIGNMENT)) {
+            FpuContext = alloca(__FPSTATE_SIZE + __FPSTATE_ALIGNMENT);
+            FpuContext = ALIGN_POINTER_UP(FpuContext, __FPSTATE_ALIGNMENT);
+            memcpy(FpuContext,
+                   &(Context->uc_mcontext.fpregs),
+                   sizeof(FPU_CONTEXT));
+        }
+
+        if (OsTestProcessorFeature(OsX86FxSave) != FALSE) {
+            ClpFxRestore(FpuContext);
+
+        } else {
+            ClpFRestore(FpuContext);
+        }
+    }
+
+    sigprocmask(SIG_SETMASK, &(Context->uc_sigmask), NULL);
+    return;
+}
+
+//
+// --------------------------------------------------------- Internal Functions
+//
+

+ 2 - 0
apps/libc/include/libcbase.h

@@ -67,6 +67,8 @@ extern "C" {
 
 #define __PACKED __attribute__((__packed__))
 #define __NO_RETURN __attribute__((__noreturn__))
+#define __ALIGNED(_Alignment) __attribute__((aligned(_Alignment)))
+#define __ALIGNED16 __ALIGNED(16)
 #define __THREAD __thread
 
 #ifdef __ELF__

+ 29 - 0
apps/libc/include/pthread.h

@@ -1765,6 +1765,35 @@ Return Value:
 
 --*/
 
+PTHREAD_API
+int
+pthread_getattr_np (
+    pthread_t ThreadId,
+    pthread_attr_t *Attribute
+    );
+
+/*++
+
+Routine Description:
+
+    This routine returns the current attributes for a given thread.
+
+Arguments:
+
+    ThreadId - Supplies the thread to get attributes for.
+
+    Attribute - Supplies a pointer where the attributes will be returned. The
+        detach state, stack size, stack base, and guard size may be different
+        from when the thread was created to reflect their actual values.
+
+Return Value:
+
+    0 on success.
+
+    Returns an error number on failure.
+
+--*/
+
 PTHREAD_API
 void
 __pthread_cleanup_push (

+ 22 - 0
apps/libc/include/signal.h

@@ -491,6 +491,28 @@ struct timespec {
     long tv_nsec;
 };
 
+/*++
+
+Structure Description:
+
+    This structure stores information about a C stack.
+
+Members:
+
+    ss_sp - Stores the stack pointer.
+
+    ss_flags - Stores a bitfield of flags. See SS_* definitions.
+
+    ss_size - Stores the size of the stack.
+
+--*/
+
+typedef struct sigaltstack {
+    void *ss_sp;
+    int ss_flags;
+    size_t ss_size;
+} stack_t;
+
 //
 // Define the type that is sent as a parameter with real time signals. It's
 // always at least as big as the larger of an integer and a pointer.

+ 291 - 0
apps/libc/include/ucontext.h

@@ -0,0 +1,291 @@
+/*++
+
+Copyright (c) 2016 Minoca Corp. All Rights Reserved
+
+Module Name:
+
+    ucontext.h
+
+Abstract:
+
+    This header contains definitions for manipulating the user machine context.
+
+Author:
+
+    Evan Green 31-Aug-2016
+
+--*/
+
+#ifndef _UCONTEXT_H
+#define _UCONTEXT_H
+
+//
+// ------------------------------------------------------------------- Includes
+//
+
+#include <signal.h>
+
+//
+// ---------------------------------------------------------------- Definitions
+//
+
+#ifdef __cplusplus
+
+extern "C" {
+
+#endif
+
+#if defined(__i386)
+
+//
+// See the TRAP_FRAME structure for the register definitions.
+//
+
+#define NGREG 17
+
+//
+// See the FPU_CONTEXT structure for the register definitions.
+//
+
+#define __FPSTATE_SIZE 512
+#define __FPSTATE_ALIGNMENT 64
+
+#elif defined(__amd64)
+
+//
+// TODO: Define the TRAP_FRAME for x64.
+//
+
+#define NGREG 23
+#define __FPSTATE_SIZE 512
+#define __FPSTATE_ALIGNMENT 64
+
+//
+// ARM-specific register state.
+//
+
+#elif defined(__arm__)
+
+//
+// See the TRAP_FRAME structure for the register definitions.
+//
+
+#define NGREG 20
+
+//
+// See the FPU_CONTEXT structure for the register definitions.
+//
+
+#define __FPSTATE_SIZE 0x110
+#define __FPSTATE_ALIGNMENT 16
+
+#else
+
+#error Unknown architecture.
+
+#endif
+
+//
+// ------------------------------------------------------ Data Type Definitions
+//
+
+//
+// Define the type for a single general purpose register.
+//
+
+typedef long int greg_t;
+
+//
+// Define the type that contains all the general purpose registers.
+//
+
+typedef greg_t gregset_t[NGREG];
+
+//
+// Define the type of the floating point registers.
+//
+
+typedef struct {
+    unsigned char Data[__FPSTATE_SIZE];
+} fpregset_t;
+
+/*++
+
+Structure Description:
+
+    This structure stores the entire processor context.
+
+Members:
+
+    gregs - Stores the general registers.
+
+    fpregs - Stores the floating point register context.
+
+--*/
+
+typedef struct {
+    gregset_t gregs;
+    fpregset_t fpregs;
+} mcontext_t;
+
+/*++
+
+Structure Description:
+
+    This structure stores the user mode machine context. This lines up with the
+    SIGNAL_CONTEXT_* structures used by the kernel.
+
+Members:
+
+    uc_flags - Stores a bitfield of flags.
+
+    uc_link - Stores a pointer to the context that is resumed when this context
+        returns.
+
+    uc_stack - Stores the stack used by this context.
+
+    uc_sigmask - Stores the set of signals that are blocked when this context
+        is active.
+
+    uc_mcontext - Stores the machine specific context.
+
+--*/
+
+typedef struct ucontext {
+    unsigned long int uc_flags;
+    struct ucontext *uc_link;
+    stack_t uc_stack;
+    sigset_t uc_sigmask;
+    mcontext_t uc_mcontext;
+} ucontext_t;
+
+//
+// -------------------------------------------------------------------- Globals
+//
+
+//
+// -------------------------------------------------------- Function Prototypes
+//
+
+LIBC_API
+int
+getcontext (
+    ucontext_t *Context
+    );
+
+/*++
+
+Routine Description:
+
+    This routine saves the current user context into the given structure,
+    including the machine registers, signal mask, and execution stack pointer.
+    If restored, the returned context will appear to execute at the return from
+    this function.
+
+Arguments:
+
+    Context - Supplies a pointer where the current context is saved.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+LIBC_API
+int
+setcontext (
+    const ucontext_t *Context
+    );
+
+/*++
+
+Routine Description:
+
+    This routine restores a previous execution context into the current
+    processor.
+
+Arguments:
+
+    Context - Supplies a pointer to the previously saved context to restore.
+
+Return Value:
+
+    Does not return on success, as execution continues from the new context.
+
+    -1 on failure, and errno will be set to contain more information.
+
+--*/
+
+LIBC_API
+void
+makecontext (
+    ucontext_t *Context,
+    void (*StartFunction)(),
+    int ArgumentCount,
+    ...
+    );
+
+/*++
+
+Routine Description:
+
+    This routine modifies an initialized context to call the function provided
+    with the given arguments.
+
+Arguments:
+
+    Context - Supplies a pointer to the context.
+
+    StartFunction - Supplies a pointer to the function to call when the
+        context is restored.
+
+    ArgumentCount - Supplies the number of int sized arguments supplied.
+
+    ... - Supplies the remaining arguments to pass to the function.
+
+Return Value:
+
+    None.
+
+--*/
+
+LIBC_API
+int
+swapcontext (
+    ucontext_t *OldContext,
+    ucontext_t *Context
+    );
+
+/*++
+
+Routine Description:
+
+    This routine saves the current context, and sets the given new context
+    with a backlink to the original context.
+
+Arguments:
+
+    OldContext - Supplies a pointer where the currently running context will
+        be saved on success.
+
+    Context - Supplies a pointer to the new context to apply. A link to the
+        context running before this call will be saved in this context.
+
+Return Value:
+
+    0 on success.
+
+    -1 on failure, and errno will be set contain more information.
+
+--*/
+
+#ifdef __cplusplus
+
+}
+
+#endif
+#endif
+

+ 11 - 20
apps/osbase/armv7/features.c

@@ -42,6 +42,14 @@ Environment:
 // -------------------------------------------------------------------- Globals
 //
 
+ULONG OsProcessorFeatureMasks[OsArmFeatureCount] = {
+    0,
+    ARM_FEATURE_V7,
+    ARM_FEATURE_VFP2,
+    ARM_FEATURE_VFP3,
+    ARM_FEATURE_NEON32
+};
+
 //
 // ------------------------------------------------------------------ Functions
 //
@@ -77,28 +85,11 @@ Return Value:
     ULONG Mask;
 
     Data = OspGetUserSharedData();
-    switch (Feature) {
-    case OsArmArmv7:
-        Mask = ARM_FEATURE_V7;
-        break;
-
-    case OsArmVfp:
-        Mask = ARM_FEATURE_VFP2;
-        break;
-
-    case OsArmVfp3:
-        Mask = ARM_FEATURE_VFP3;
-        break;
-
-    case OsArmNeon32:
-        Mask = ARM_FEATURE_NEON32;
-        break;
-
-    default:
-        Mask = 0;
-        break;
+    if (Feature >= OsArmFeatureCount) {
+        return FALSE;
     }
 
+    Mask = OsProcessorFeatureMasks[Feature];
     if ((Data->ProcessorFeatures & Mask) != 0) {
         return TRUE;
     }

+ 10 - 12
apps/osbase/x86/features.c

@@ -42,6 +42,13 @@ Environment:
 // -------------------------------------------------------------------- Globals
 //
 
+ULONG OsProcessorFeatureMasks[OsX86FeatureCount] = {
+    0,
+    X86_FEATURE_SYSENTER,
+    X86_FEATURE_I686,
+    X86_FEATURE_FXSAVE
+};
+
 //
 // ------------------------------------------------------------------ Functions
 //
@@ -77,20 +84,11 @@ Return Value:
     ULONG Mask;
 
     Data = OspGetUserSharedData();
-    switch (Feature) {
-    case OsX86Sysenter:
-        Mask = X86_FEATURE_SYSENTER;
-        break;
-
-    case OsX86I686:
-        Mask = X86_FEATURE_I686;
-        break;
-
-    default:
-        Mask = 0;
-        break;
+    if (Feature >= OsX86FeatureCount) {
+        return FALSE;
     }
 
+    Mask = OsProcessorFeatureMasks[Feature];
     if ((Data->ProcessorFeatures & Mask) != 0) {
         return TRUE;
     }

+ 214 - 1
apps/testapps/sigtest/sigtest.c

@@ -26,6 +26,7 @@ Environment:
 //
 
 #include <minoca/lib/minocaos.h>
+#include <alloca.h>
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -34,6 +35,7 @@ Environment:
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/wait.h>
+#include <ucontext.h>
 #include <unistd.h>
 
 //
@@ -56,6 +58,8 @@ Environment:
 #define DEFAULT_CHILD_PROCESS_COUNT 3
 #define DEFAULT_THREAD_COUNT 1
 
+#define SIGNAL_TEST_CONTEXT_STACK_SIZE 16384
+
 //
 // ---------------------------------------------------------------- Definitions
 //
@@ -71,7 +75,7 @@ Environment:
     "  -p, --threads <count> -- Set the number of threads to spin up to \n"    \
     "      simultaneously run the test.\n"                                     \
     "  -t, --test -- Set the test to perform. Valid values are all, \n"        \
-    "      waitpid, sigchld, quickwait, and nested.\n"                         \
+    "      waitpid, sigchld, quickwait, nested, and context.\n"                \
     "  --debug -- Print lots of information about what's happening.\n"         \
     "  --quiet -- Print only errors.\n"                                        \
     "  --help -- Print this help text and exit.\n"                             \
@@ -95,6 +99,7 @@ typedef enum _SIGNAL_TEST_TYPE {
     SignalTestSigchld,
     SignalTestQuickWait,
     SignalTestNested,
+    SignalTestContext,
 } SIGNAL_TEST_TYPE, *PSIGNAL_TEST_TYPE;
 
 typedef enum _SIGNAL_TEST_WAIT_TYPE {
@@ -178,6 +183,23 @@ TestNestedSignalHandler (
     void *Ignored
     );
 
+ULONG
+RunSetContextTest (
+    VOID
+    );
+
+ULONG
+TestContextSwap (
+    BOOL Exit
+    );
+
+VOID
+TestMakecontext (
+    ucontext_t *OldContext,
+    ucontext_t *NextContext,
+    INT Identifier
+    );
+
 //
 // -------------------------------------------------------------------- Globals
 //
@@ -220,6 +242,8 @@ PSTR SignalTestWaitTypeStrings[SignalTestWaitTypeCount] = {
     "sigtimedwait"
 };
 
+int SigtestContextHits;
+
 //
 // ------------------------------------------------------------------ Functions
 //
@@ -344,6 +368,9 @@ Return Value:
             } else if (strcasecmp(optarg, "nested") == 0) {
                 Test = SignalTestNested;
 
+            } else if (strcasecmp(optarg, "context") == 0) {
+                Test = SignalTestContext;
+
             } else {
                 PRINT_ERROR("Invalid test: %s.\n", optarg);
                 Status = 1;
@@ -426,6 +453,10 @@ Return Value:
         Failures += RunNestedSignalsTest();
     }
 
+    if ((Test == SignalTestAll) || (Test == SignalTestContext)) {
+        Failures += RunSetContextTest();
+    }
+
     //
     // Wait for any children.
     //
@@ -1750,3 +1781,185 @@ Return Value:
     return;
 }
 
+ULONG
+RunSetContextTest (
+    VOID
+    )
+
+/*++
+
+Routine Description:
+
+    This routine tests the ucontext related functions.
+
+Arguments:
+
+    None.
+
+Return Value:
+
+    Returns the number of failures.
+
+--*/
+
+{
+
+    ULONG Failures;
+
+    Failures = TestContextSwap(TRUE);
+    Failures += TestContextSwap(FALSE);
+    return Failures;
+}
+
+ULONG
+TestContextSwap (
+    BOOL Exit
+    )
+
+/*++
+
+Routine Description:
+
+    This routine tests the ucontext related functions.
+
+Arguments:
+
+    Exit - Supplies a boolean indicating whether to test a context swap that
+        exits or returns.
+
+Return Value:
+
+    Returns the number of failures.
+
+--*/
+
+{
+
+    pid_t Child;
+    ucontext_t Context1;
+    ucontext_t Context2;
+    ucontext_t MainContext;
+    int Status;
+
+    Child = fork();
+    if (Child < 0) {
+        PRINT_ERROR("Failed to fork\n");
+        return 1;
+
+    } else if (Child > 0) {
+        if (waitpid(Child, &Status, 0) != Child) {
+            PRINT_ERROR("Failed to wait\n");
+            return 1;
+        }
+
+        if ((!WIFEXITED(Status)) || (WEXITSTATUS(Status) != 0)) {
+            PRINT_ERROR("Child exited with %x\n", Status);
+            return 1;
+        }
+
+        return 0;
+    }
+
+    //
+    // This is the child.
+    //
+
+    SigtestContextHits = 0;
+    if (getcontext(&Context1) != 0) {
+        PRINT_ERROR("getcontext failed");
+        exit(1);
+    }
+
+    Context1.uc_stack.ss_sp = alloca(SIGNAL_TEST_CONTEXT_STACK_SIZE);
+    Context1.uc_stack.ss_size = SIGNAL_TEST_CONTEXT_STACK_SIZE;
+    Context1.uc_link = &MainContext;
+    makecontext(&Context1,
+                TestMakecontext,
+                3,
+                &Context1,
+                &Context2,
+                5);
+
+    if (getcontext(&Context2) != 0) {
+        PRINT_ERROR("getcontext failed");
+        exit(1);
+    }
+
+    Context2.uc_stack.ss_sp = alloca(SIGNAL_TEST_CONTEXT_STACK_SIZE);
+    Context2.uc_stack.ss_size = SIGNAL_TEST_CONTEXT_STACK_SIZE;
+    Context2.uc_link = NULL;
+    if (Exit == FALSE) {
+        Context2.uc_link = &Context1;
+    }
+
+    makecontext(&Context2,
+                TestMakecontext,
+                3,
+                &Context2,
+                &Context1,
+                10);
+
+    DEBUG_PRINT("MainContext swapping\n");
+    SigtestContextHits += 1;
+    if (swapcontext(&MainContext, &Context2) != 0) {
+        PRINT_ERROR("swapcontext failed.\n");
+        exit(1);
+    }
+
+    SigtestContextHits += 1;
+    if (Exit != FALSE) {
+        PRINT_ERROR("Main context returned instead of exited!\n");
+        exit(1);
+    }
+
+    if (SigtestContextHits != ((5 * 2) + (10 * 2) + 2)) {
+        PRINT_ERROR("Context hits were %d.\n", SigtestContextHits);
+        exit(1);
+    }
+
+    DEBUG_PRINT("MainContext exiting\n");
+    exit(0);
+    return 0;
+}
+
+VOID
+TestMakecontext (
+    ucontext_t *OldContext,
+    ucontext_t *NextContext,
+    INT Identifier
+    )
+
+/*++
+
+Routine Description:
+
+    This routine swaps contexts.
+
+Arguments:
+
+    OldContext - Supplies a pointer to the previous context.
+
+    NextContext - Supplies a pointer to the next context.
+
+    Identifier - Supplies an identifier for this context.
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+
+    DEBUG_PRINT("Context %d: Swapping\n", Identifier);
+    SigtestContextHits += Identifier;
+    if (swapcontext(OldContext, NextContext) != 0) {
+        PRINT_ERROR("Swapcontext failed from %d\n", Identifier);
+        exit(1);
+    }
+
+    DEBUG_PRINT("Context %d: Exiting\n", Identifier);
+    SigtestContextHits += Identifier;
+    return;
+}
+

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

@@ -657,7 +657,7 @@ Members:
 struct _FPU_CONTEXT {
     ULONGLONG Registers[32];
     ULONG Fpscr;
-} PACKED;
+} PACKED ALIGNED16;
 
 /*++
 

+ 6 - 0
include/minoca/kernel/arm.inc

@@ -185,6 +185,12 @@ Environment:
 #define TRAP_CPSR 76
 #define TRAP_FRAME_SIZE 80
 
+##
+## Define the size of the common SIGNAL_CONTEXT structure.
+##
+
+#define SIGNAL_CONTEXT_SIZE 32
+
 ##
 ## Define the size of the PROCESSOR_CONTEXT structure.
 ##

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

@@ -129,6 +129,12 @@ Author:
 
 #define X86_FEATURE_I686     0x00000004
 
+//
+// This bit is set if the processor supports fxsave/fxrstor instructions.
+//
+
+#define X86_FEATURE_FXSAVE   0x00000008
+
 //
 // This bit is set if the kernel is ARMv7.
 //

+ 10 - 2
include/minoca/kernel/ksignals.h

@@ -327,6 +327,13 @@ Author:
 
 #define SIGNAL_CONTEXT_FLAG_FPU_VALID 0x00000002
 
+//
+// This flag is set by user mode if the given context has already been swapped
+// in.
+//
+
+#define SIGNAL_CONTEXT_FLAG_SWAPPED 0x00000004
+
 //
 // Define the child process signal reason codes.
 //
@@ -535,7 +542,8 @@ 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.
+    specific version of the signal context. This lines up with the ucontext
+    structure in the C library.
 
 Members:
 
@@ -555,7 +563,7 @@ typedef struct _SIGNAL_CONTEXT {
     PVOID Next;
     SIGNAL_STACK Stack;
     SIGNAL_SET Mask;
-} PACKED SIGNAL_CONTEXT, *PSIGNAL_CONTEXT;
+} SIGNAL_CONTEXT, *PSIGNAL_CONTEXT;
 
 //
 // -------------------------------------------------------------------- Globals

+ 37 - 0
include/minoca/kernel/x64.inc

@@ -55,6 +55,43 @@ Environment:
 
 #define CONTEXT_SWAP_MAGIC 0x9A8A7A6A5A4A3A2A
 
+//
+// Definition for the TRAP_FRAME structure and the exception stack directly
+// above it.
+//
+
+#define TRAP_DS             0
+#define TRAP_ES             4
+#define TRAP_FS             8
+#define TRAP_GS             12
+#define TRAP_SS             16
+
+#define TRAP_RAX            24
+#define TRAP_RBX            32
+#define TRAP_RCX            40
+#define TRAP_RDX            48
+#define TRAP_RSI            56
+#define TRAP_RDI            64
+#define TRAP_RBP            72
+#define TRAP_R8             80
+#define TRAP_R9             88
+#define TRAP_R10            96
+#define TRAP_R11            104
+#define TRAP_R12            112
+#define TRAP_R13            120
+#define TRAP_R14            128
+#define TRAP_R15            136
+#define TRAP_ERRORCODE      144
+#define TRAP_RIP            152
+#define TRAP_CS             160
+#define TRAP_EFLAGS         168
+#define TRAP_RSP            176
+
+#define TRAP_FRAME_SIZE     184
+
+#define PROCESSOR_CONTEXT_SIZE 0x60
+#define SIGNAL_CONTEXT_SIZE 28
+
 //
 // Define the minimum and maximum external interrupt vectors.
 //

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

@@ -204,7 +204,7 @@ Author:
 // Define the required alignment for FPU context.
 //
 
-#define FPU_CONTEXT_ALIGNMENT 16
+#define FPU_CONTEXT_ALIGNMENT 64
 
 //
 // Define MSR values.
@@ -596,7 +596,7 @@ struct _FPU_CONTEXT {
     UCHAR Xmm6[16];
     UCHAR Xmm7[16];
     UCHAR Padding[224];
-} PACKED;
+} PACKED ALIGNED64;
 
 /*++
 

+ 1 - 0
include/minoca/kernel/x86.inc

@@ -134,6 +134,7 @@ Environment:
 #define TRAP_FRAME_SIZE     68
 
 #define PROCESSOR_CONTEXT_SIZE 0x60
+#define SIGNAL_CONTEXT_SIZE 32
 
 ##
 ## Define the minimum and maximum external interrupt vectors.

+ 3 - 0
include/minoca/lib/minocaos.h

@@ -93,12 +93,15 @@ typedef enum _OS_ARM_PROCESSOR_FEATURE {
     OsArmVfp,
     OsArmVfp3,
     OsArmNeon32,
+    OsArmFeatureCount
 } OS_ARM_PROCESSOR_FEATURE, *POS_ARM_PROCESSOR_FEATURE;
 
 typedef enum _OS_X86_PROCESSOR_FEATURE {
     OsX86FeatureInvalid,
     OsX86Sysenter,
     OsX86I686,
+    OsX86FxSave,
+    OsX86FeatureCount
 } OS_X86_PROCESSOR_FEATURE, *POS_X86_PROCESSOR_FEATURE;
 
 typedef

+ 1 - 1
kernel/ps/armv7/psarch.c

@@ -172,7 +172,7 @@ Return Value:
 
     Thread = KeGetCurrentThread();
     ContextSp = ALIGN_RANGE_DOWN(TrapFrame->UserSp - sizeof(SIGNAL_CONTEXT_ARM),
-                                 STACK_ALIGNMENT);
+                                 FPU_CONTEXT_ALIGNMENT);
 
     Context = (PVOID)ContextSp;
     Flags = 0;

+ 1 - 1
kernel/ps/x86/psarch.c

@@ -187,7 +187,7 @@ Return Value:
 
     Thread = KeGetCurrentThread();
     ContextSp = ALIGN_RANGE_DOWN(TrapFrame->Esp - sizeof(SIGNAL_CONTEXT_X86),
-                                 STACK_ALIGNMENT);
+                                 FPU_CONTEXT_ALIGNMENT);
 
     Context = (PVOID)ContextSp;
     Flags = 0;

+ 17 - 0
kernel/x86/archsupc.c

@@ -152,6 +152,14 @@ Return Value:
         }
     }
 
+    //
+    // Remember if the processor supports the fxsave instruction.
+    //
+
+    if ((Edx & X86_CPUID_BASIC_EDX_FX_SAVE_RESTORE) != 0) {
+        Data->ProcessorFeatures |= X86_FEATURE_FXSAVE;
+    }
+
     return;
 }
 
@@ -185,6 +193,15 @@ Return Value:
 
     AllocationSize = sizeof(FPU_CONTEXT) + FPU_CONTEXT_ALIGNMENT;
     Context = MmAllocateNonPagedPool(AllocationSize, AllocationTag);
+    if (Context == NULL) {
+        return NULL;
+    }
+
+    //
+    // Zero out the buffer to avoid leaking kernel pool to user mode.
+    //
+
+    RtlZeroMemory(Context, AllocationSize);
     return Context;
 }