ntdll: Support AVX context in fault exceptions on Linux i386.
Signed-off-by: Paul Gofman <pgofman@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
2c3719c7a9
commit
9aa885bf37
|
@ -5444,7 +5444,7 @@ static DWORD test_extended_context_handler(EXCEPTION_RECORD *rec, EXCEPTION_REGI
|
|||
/* Since we got xstates enabled by OS this cpuid level should be supported. */
|
||||
__cpuidex(regs, 0xd, 1);
|
||||
compaction = regs[0] & 2;
|
||||
todo_wine_if(sizeof(void *) == 4)
|
||||
|
||||
ok((context->ContextFlags & (CONTEXT_FULL | CONTEXT_XSTATE)) == (CONTEXT_FULL | CONTEXT_XSTATE),
|
||||
"Got unexpected ContextFlags %#x.\n", context->ContextFlags);
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
@ -56,6 +57,8 @@
|
|||
#include "ntstatus.h"
|
||||
#define WIN32_NO_STATUS
|
||||
#include "windef.h"
|
||||
#include "winternl.h"
|
||||
#include "ddk/wdm.h"
|
||||
#include "wine/asm.h"
|
||||
#include "wine/exception.h"
|
||||
#include "unix_private.h"
|
||||
|
@ -122,6 +125,10 @@ typedef struct ucontext
|
|||
} ucontext_t;
|
||||
#endif /* HAVE_SYS_UCONTEXT_H */
|
||||
|
||||
#ifndef FP_XSTATE_MAGIC1
|
||||
#define FP_XSTATE_MAGIC1 0x46505853
|
||||
#endif
|
||||
|
||||
#define EAX_sig(context) ((context)->uc_mcontext.gregs[REG_EAX])
|
||||
#define EBX_sig(context) ((context)->uc_mcontext.gregs[REG_EBX])
|
||||
#define ECX_sig(context) ((context)->uc_mcontext.gregs[REG_ECX])
|
||||
|
@ -145,6 +152,7 @@ typedef struct ucontext
|
|||
|
||||
#define FPU_sig(context) ((FLOATING_SAVE_AREA*)((context)->uc_mcontext.fpregs))
|
||||
#define FPUX_sig(context) (FPU_sig(context) && !((context)->uc_mcontext.fpregs->status >> 16) ? (XSAVE_FORMAT *)(FPU_sig(context) + 1) : NULL)
|
||||
#define XState_sig(fpu) (((unsigned int *)fpu->Reserved4)[12] == FP_XSTATE_MAGIC1 ? (XSTATE *)(fpu + 1) : NULL)
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* custom signal restorer since we may have unmapped the one in vdso, and bionic doesn't check for that */
|
||||
|
@ -208,6 +216,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
|
||||
#define FPU_sig(context) NULL /* FIXME */
|
||||
#define FPUX_sig(context) NULL /* FIXME */
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
|
||||
#elif defined (__OpenBSD__)
|
||||
|
||||
|
@ -238,6 +247,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
|
||||
#define FPU_sig(context) NULL /* FIXME */
|
||||
#define FPUX_sig(context) NULL /* FIXME */
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
|
||||
#define T_MCHK T_MACHK
|
||||
#define T_XMMFLT T_XFTRAP
|
||||
|
@ -283,6 +293,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
|
||||
#define FPU_sig(context) NULL /* FIXME */
|
||||
#define FPUX_sig(context) NULL /* FIXME */
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
|
||||
#elif defined (__APPLE__)
|
||||
|
||||
|
@ -310,6 +321,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
#define ERROR_sig(context) ((context)->uc_mcontext->__es.__err)
|
||||
#define FPU_sig(context) NULL
|
||||
#define FPUX_sig(context) ((XSAVE_FORMAT *)&(context)->uc_mcontext->__fs.__fpu_fcw)
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
#else
|
||||
#define EAX_sig(context) ((context)->uc_mcontext->ss.eax)
|
||||
#define EBX_sig(context) ((context)->uc_mcontext->ss.ebx)
|
||||
|
@ -331,6 +343,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
#define ERROR_sig(context) ((context)->uc_mcontext->es.err)
|
||||
#define FPU_sig(context) NULL
|
||||
#define FPUX_sig(context) ((XSAVE_FORMAT *)&(context)->uc_mcontext->fs.fpu_fcw)
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
#endif
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
|
@ -361,6 +374,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
|
||||
#define FPU_sig(context) NULL
|
||||
#define FPUX_sig(context) ((XSAVE_FORMAT *)&((context)->uc_mcontext.__fpregs))
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
|
||||
#define T_MCHK T_MCA
|
||||
#define T_XMMFLT T_XMM
|
||||
|
@ -393,6 +407,7 @@ static inline int set_thread_area( struct modify_ldt_s *ptr )
|
|||
|
||||
#define FPU_sig(context) ((FLOATING_SAVE_AREA *)&(context)->uc_mcontext.fpregs.fp_reg_set.fpchip_state)
|
||||
#define FPUX_sig(context) NULL
|
||||
#define XState_sig(context) NULL /* FIXME */
|
||||
|
||||
#else
|
||||
#error You must define the signal context functions for your platform
|
||||
|
@ -752,10 +767,11 @@ static void fpux_to_fpu( FLOATING_SAVE_AREA *fpu, const XSAVE_FORMAT *fpux )
|
|||
*
|
||||
* Build a context structure from the signal info.
|
||||
*/
|
||||
static inline void save_context( CONTEXT *context, const ucontext_t *sigcontext )
|
||||
static inline void save_context( struct xcontext *xcontext, const ucontext_t *sigcontext )
|
||||
{
|
||||
FLOATING_SAVE_AREA *fpu = FPU_sig(sigcontext);
|
||||
XSAVE_FORMAT *fpux = FPUX_sig(sigcontext);
|
||||
CONTEXT *context = &xcontext->c;
|
||||
|
||||
memset(context, 0, sizeof(*context));
|
||||
context->ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
|
||||
|
@ -792,6 +808,11 @@ static inline void save_context( CONTEXT *context, const ucontext_t *sigcontext
|
|||
context->ContextFlags |= CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS;
|
||||
memcpy( context->ExtendedRegisters, fpux, sizeof(*fpux) );
|
||||
if (!fpu) fpux_to_fpu( &context->FloatSave, fpux );
|
||||
xcontext->xstate = XState_sig(fpux);
|
||||
}
|
||||
else
|
||||
{
|
||||
xcontext->xstate = NULL;
|
||||
}
|
||||
if (!fpu && !fpux) save_fpu( context );
|
||||
}
|
||||
|
@ -802,10 +823,11 @@ static inline void save_context( CONTEXT *context, const ucontext_t *sigcontext
|
|||
*
|
||||
* Restore the signal info from the context.
|
||||
*/
|
||||
static inline void restore_context( const CONTEXT *context, ucontext_t *sigcontext )
|
||||
static inline void restore_context( const struct xcontext *xcontext, ucontext_t *sigcontext )
|
||||
{
|
||||
FLOATING_SAVE_AREA *fpu = FPU_sig(sigcontext);
|
||||
XSAVE_FORMAT *fpux = FPUX_sig(sigcontext);
|
||||
const CONTEXT *context = &xcontext->c;
|
||||
|
||||
x86_thread_data()->dr0 = context->Dr0;
|
||||
x86_thread_data()->dr1 = context->Dr1;
|
||||
|
@ -831,7 +853,18 @@ static inline void restore_context( const CONTEXT *context, ucontext_t *sigconte
|
|||
SS_sig(sigcontext) = context->SegSs;
|
||||
|
||||
if (fpu) *fpu = context->FloatSave;
|
||||
if (fpux) memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) );
|
||||
if (fpux)
|
||||
{
|
||||
XSTATE *src_xs, *dst_xs;
|
||||
|
||||
memcpy( fpux, context->ExtendedRegisters, sizeof(*fpux) );
|
||||
|
||||
if ((dst_xs = XState_sig(fpux)) && (src_xs = xstate_from_context( context )))
|
||||
{
|
||||
memcpy( &dst_xs->YmmContext, &src_xs->YmmContext, sizeof(dst_xs->YmmContext) );
|
||||
dst_xs->Mask |= src_xs->Mask;
|
||||
}
|
||||
}
|
||||
if (!fpu && !fpux) restore_fpu( context );
|
||||
}
|
||||
|
||||
|
@ -1442,48 +1475,76 @@ static BOOL check_atl_thunk( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONT
|
|||
*
|
||||
* Setup the exception record and context on the thread stack.
|
||||
*/
|
||||
static void *setup_exception_record( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, CONTEXT *context )
|
||||
static void *setup_exception_record( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, struct xcontext *xcontext )
|
||||
{
|
||||
void *stack = init_handler( sigcontext );
|
||||
|
||||
rec->ExceptionAddress = (void *)EIP_sig( sigcontext );
|
||||
save_context( context, sigcontext );
|
||||
save_context( xcontext, sigcontext );
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* setup_raise_exception
|
||||
*
|
||||
* Change context to setup a call to a raise exception function.
|
||||
*/
|
||||
static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
|
||||
EXCEPTION_RECORD *rec, CONTEXT *context )
|
||||
EXCEPTION_RECORD *rec, struct xcontext *xcontext )
|
||||
{
|
||||
struct
|
||||
CONTEXT *context = &xcontext->c;
|
||||
size_t stack_size;
|
||||
|
||||
struct stack_layout
|
||||
{
|
||||
EXCEPTION_RECORD *rec_ptr; /* first arg for KiUserExceptionDispatcher */
|
||||
CONTEXT *context_ptr; /* second arg for KiUserExceptionDispatcher */
|
||||
CONTEXT context;
|
||||
CONTEXT_EX context_ex;
|
||||
EXCEPTION_RECORD rec;
|
||||
DWORD ebp;
|
||||
DWORD eip;
|
||||
char xstate[0];
|
||||
} *stack;
|
||||
|
||||
C_ASSERT( (offsetof(struct stack_layout, xstate) == sizeof(struct stack_layout)) );
|
||||
|
||||
NTSTATUS status = send_debug_event( rec, context, TRUE );
|
||||
|
||||
if (status == DBG_CONTINUE || status == DBG_EXCEPTION_HANDLED)
|
||||
{
|
||||
restore_context( context, sigcontext );
|
||||
restore_context( xcontext, sigcontext );
|
||||
return;
|
||||
}
|
||||
|
||||
/* fix up instruction pointer in context for EXCEPTION_BREAKPOINT */
|
||||
if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) context->Eip--;
|
||||
|
||||
stack = virtual_setup_exception( stack_ptr, sizeof(*stack), rec );
|
||||
stack_size = sizeof(*stack);
|
||||
if (xcontext->xstate)
|
||||
{
|
||||
stack_size += (ULONG_PTR)stack_ptr - (((ULONG_PTR)stack_ptr
|
||||
- sizeof(XSTATE)) & ~(ULONG_PTR)63);
|
||||
}
|
||||
|
||||
stack = virtual_setup_exception( stack_ptr, stack_size, rec );
|
||||
stack->rec = *rec;
|
||||
stack->context = *context;
|
||||
|
||||
if (xcontext->xstate)
|
||||
{
|
||||
XSTATE *dst_xs = (XSTATE *)stack->xstate;
|
||||
|
||||
assert(!((ULONG_PTR)dst_xs & 63));
|
||||
context_init_xstate( &stack->context, stack->xstate );
|
||||
dst_xs->CompactionMask = user_shared_data->XState.CompactionEnabled ? 0x8000000000000004 : 0;
|
||||
if (xcontext->xstate->Mask & 4)
|
||||
{
|
||||
dst_xs->Mask = 4;
|
||||
memcpy( &dst_xs->YmmContext, &xcontext->xstate->YmmContext, sizeof(dst_xs->YmmContext) );
|
||||
}
|
||||
}
|
||||
|
||||
stack->rec_ptr = &stack->rec;
|
||||
stack->context_ptr = &stack->context;
|
||||
ESP_sig(sigcontext) = (DWORD)stack;
|
||||
|
@ -1506,9 +1567,10 @@ static void setup_raise_exception( ucontext_t *sigcontext, void *stack_ptr,
|
|||
*/
|
||||
static void setup_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec )
|
||||
{
|
||||
CONTEXT context;
|
||||
void *stack = setup_exception_record( sigcontext, rec, &context );
|
||||
setup_raise_exception( sigcontext, stack, rec, &context );
|
||||
struct xcontext xcontext;
|
||||
void *stack = setup_exception_record( sigcontext, rec, &xcontext );
|
||||
|
||||
setup_raise_exception( sigcontext, stack, rec, &xcontext );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1623,8 +1685,10 @@ static inline DWORD get_fpu_code( const CONTEXT *context )
|
|||
* Handle an interrupt.
|
||||
*/
|
||||
static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, void *stack,
|
||||
EXCEPTION_RECORD *rec, CONTEXT *context )
|
||||
EXCEPTION_RECORD *rec, struct xcontext *xcontext )
|
||||
{
|
||||
CONTEXT *context = &xcontext->c;
|
||||
|
||||
switch(interrupt)
|
||||
{
|
||||
case 0x2d:
|
||||
|
@ -1649,7 +1713,7 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
|
|||
rec->ExceptionInformation[0] = context->Eax;
|
||||
rec->ExceptionInformation[1] = context->Ecx;
|
||||
rec->ExceptionInformation[2] = context->Edx;
|
||||
setup_raise_exception( sigcontext, stack, rec, context );
|
||||
setup_raise_exception( sigcontext, stack, rec, xcontext );
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
|
@ -1665,9 +1729,9 @@ static BOOL handle_interrupt( unsigned int interrupt, ucontext_t *sigcontext, vo
|
|||
static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
EXCEPTION_RECORD rec = { 0 };
|
||||
CONTEXT context;
|
||||
struct xcontext xcontext;
|
||||
ucontext_t *ucontext = sigcontext;
|
||||
void *stack = setup_exception_record( sigcontext, &rec, &context );
|
||||
void *stack = setup_exception_record( sigcontext, &rec, &xcontext );
|
||||
|
||||
switch (TRAP_sig(ucontext))
|
||||
{
|
||||
|
@ -1687,8 +1751,8 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
case TRAP_x86_PROTFLT: /* General protection fault */
|
||||
{
|
||||
WORD err = ERROR_sig(ucontext);
|
||||
if (!err && (rec.ExceptionCode = is_privileged_instr( &context ))) break;
|
||||
if ((err & 7) == 2 && handle_interrupt( err >> 3, ucontext, stack, &rec, &context )) return;
|
||||
if (!err && (rec.ExceptionCode = is_privileged_instr( &xcontext.c ))) break;
|
||||
if ((err & 7) == 2 && handle_interrupt( err >> 3, ucontext, stack, &rec, &xcontext )) return;
|
||||
rec.ExceptionCode = EXCEPTION_ACCESS_VIOLATION;
|
||||
rec.NumberParameters = 2;
|
||||
rec.ExceptionInformation[0] = 0;
|
||||
|
@ -1697,7 +1761,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
else
|
||||
{
|
||||
rec.ExceptionInformation[1] = 0xffffffff;
|
||||
if (check_invalid_gs( ucontext, &context )) return;
|
||||
if (check_invalid_gs( ucontext, &xcontext.c )) return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1714,7 +1778,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags,
|
||||
&flags, sizeof(flags), NULL );
|
||||
if (!(flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION) &&
|
||||
check_atl_thunk( ucontext, &rec, &context ))
|
||||
check_atl_thunk( ucontext, &rec, &xcontext.c ))
|
||||
return;
|
||||
|
||||
/* send EXCEPTION_EXECUTE_FAULT only if data execution prevention is enabled */
|
||||
|
@ -1723,7 +1787,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
break;
|
||||
case TRAP_x86_ALIGNFLT: /* Alignment check exception */
|
||||
/* FIXME: pass through exception handler first? */
|
||||
if (context.EFlags & 0x00040000)
|
||||
if (xcontext.c.EFlags & 0x00040000)
|
||||
{
|
||||
EFL_sig(ucontext) &= ~0x00040000; /* disable AC flag */
|
||||
return;
|
||||
|
@ -1742,7 +1806,7 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
rec.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION;
|
||||
break;
|
||||
}
|
||||
setup_raise_exception( ucontext, stack, &rec, &context );
|
||||
setup_raise_exception( ucontext, stack, &rec, &xcontext );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1754,9 +1818,9 @@ static void segv_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
EXCEPTION_RECORD rec = { 0 };
|
||||
CONTEXT context;
|
||||
struct xcontext xcontext;
|
||||
ucontext_t *ucontext = sigcontext;
|
||||
void *stack = setup_exception_record( sigcontext, &rec, &context );
|
||||
void *stack = setup_exception_record( sigcontext, &rec, &xcontext );
|
||||
|
||||
switch (TRAP_sig(ucontext))
|
||||
{
|
||||
|
@ -1765,15 +1829,15 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
/* when single stepping can't tell whether this is a hw bp or a
|
||||
* single step interrupt. try to avoid as much overhead as possible
|
||||
* and only do a server call if there is any hw bp enabled. */
|
||||
if (!(context.EFlags & 0x100) || (context.Dr7 & 0xff))
|
||||
if (!(xcontext.c.EFlags & 0x100) || (xcontext.c.Dr7 & 0xff))
|
||||
{
|
||||
/* (possible) hardware breakpoint, fetch the debug registers */
|
||||
DWORD saved_flags = context.ContextFlags;
|
||||
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
NtGetContextThread( GetCurrentThread(), &context );
|
||||
context.ContextFlags |= saved_flags; /* restore flags */
|
||||
DWORD saved_flags = xcontext.c.ContextFlags;
|
||||
xcontext.c.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
NtGetContextThread( GetCurrentThread(), &xcontext.c );
|
||||
xcontext.c.ContextFlags |= saved_flags; /* restore flags */
|
||||
}
|
||||
context.EFlags &= ~0x100; /* clear single-step flag */
|
||||
xcontext.c.EFlags &= ~0x100; /* clear single-step flag */
|
||||
break;
|
||||
case TRAP_x86_BPTFLT: /* Breakpoint exception */
|
||||
rec.ExceptionAddress = (char *)rec.ExceptionAddress - 1; /* back up over the int3 instruction */
|
||||
|
@ -1786,7 +1850,7 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
rec.ExceptionInformation[2] = 0; /* FIXME */
|
||||
break;
|
||||
}
|
||||
setup_raise_exception( sigcontext, stack, &rec, &context );
|
||||
setup_raise_exception( sigcontext, stack, &rec, &xcontext );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1798,9 +1862,9 @@ static void trap_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
EXCEPTION_RECORD rec = { 0 };
|
||||
CONTEXT context;
|
||||
struct xcontext xcontext;
|
||||
ucontext_t *ucontext = sigcontext;
|
||||
void *stack = setup_exception_record( sigcontext, &rec, &context );
|
||||
void *stack = setup_exception_record( sigcontext, &rec, &xcontext );
|
||||
|
||||
switch (TRAP_sig(ucontext))
|
||||
{
|
||||
|
@ -1811,8 +1875,8 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
|
||||
break;
|
||||
case TRAP_x86_ARITHTRAP: /* Floating point exception */
|
||||
rec.ExceptionCode = get_fpu_code( &context );
|
||||
rec.ExceptionAddress = (void *)context.FloatSave.ErrorOffset;
|
||||
rec.ExceptionCode = get_fpu_code( &xcontext.c );
|
||||
rec.ExceptionAddress = (void *)xcontext.c.FloatSave.ErrorOffset;
|
||||
break;
|
||||
case TRAP_x86_CACHEFLT: /* SIMD exception */
|
||||
/* TODO:
|
||||
|
@ -1832,7 +1896,7 @@ static void fpe_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
rec.ExceptionCode = EXCEPTION_FLT_INVALID_OPERATION;
|
||||
break;
|
||||
}
|
||||
setup_raise_exception( sigcontext, stack, &rec, &context );
|
||||
setup_raise_exception( sigcontext, stack, &rec, &xcontext );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1880,12 +1944,12 @@ static void quit_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
|||
*/
|
||||
static void usr1_handler( int signal, siginfo_t *siginfo, void *sigcontext )
|
||||
{
|
||||
CONTEXT context;
|
||||
struct xcontext xcontext;
|
||||
|
||||
init_handler( sigcontext );
|
||||
save_context( &context, sigcontext );
|
||||
wait_suspend( &context );
|
||||
restore_context( &context, sigcontext );
|
||||
save_context( &xcontext, sigcontext );
|
||||
wait_suspend( &xcontext.c );
|
||||
restore_context( &xcontext, sigcontext );
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue