swapcontext, and makecontext).
- Fix VM to not erroneously think the stack segment and data segment have
collided when a user-space thread invokes brk().
- Add test51 to test ucontext functionality.
- Add man pages for ucontext system calls.
regexp.h setjmp.h sgtty.h signal.h stdarg.h stddef.h \
stdint.h stdio.h stdlib.h string.h strings.h sysexits.h \
syslog.h tar.h termcap.h termios.h time.h timers.h tools.h \
- ttyent.h unistd.h utime.h utmp.h
+ ttyent.h ucontext.h unistd.h utime.h utmp.h
INCS+= arpa/inet.h
INCS+= minix/a.out.h minix/bitmap.h minix/callnr.h minix/cdrom.h \
minix/com.h minix/config.h minix/const.h minix/cpufeature.h \
INCSDIR= /usr/include/i386
INCS= archtypes.h bios.h cmos.h cpu.h diskparm.h fpu.h int86.h \
- interrupt.h memory.h partition.h pci.h ports.h stackframe.h \
- vm.h
+ interrupt.h mcontext.h memory.h partition.h pci.h ports.h \
+ stackframe.h vm.h
.include <minix.kinc.mk>
--- /dev/null
+#ifndef _MACHINE_MCONTEXT_H
+#define _MACHINE_MCONTEXT_H 1
+
+#include <machine/fpu.h>
+#include <machine/stackframe.h>
+
+#define MCF_MAGIC 0xc0ffee
+
+/* Context to describe processor state */
+typedef struct __mcontext {
+ int mc_magic;
+ struct stackframe_s mc_p_reg;
+#if (_MINIX_CHIP == _CHIP_INTEL)
+ union fpu_state_u mc_fpu_state;
+#endif
+ short mc_fpu_flags;
+} mcontext_t;
+
+_PROTOTYPE( int setmcontext, (const mcontext_t *mcp) );
+_PROTOTYPE( int getmcontext, (mcontext_t *mcp) );
+
+#endif /* _MACHINE_MCONTEXT_H */
+
#define ITIMER 64
#define GETGROUPS 65
#define SETGROUPS 66
+#define GETMCONTEXT 67
+#define SETMCONTEXT 68
/* Posix signal handling. */
#define SIGACTION 71
# define SYS_SAFEMAP (KERNEL_CALL + 47) /* sys_safemap() */
# define SYS_SAFEREVMAP (KERNEL_CALL + 48) /* sys_saferevmap() sys_saferevmap2() */
# define SYS_SAFEUNMAP (KERNEL_CALL + 49) /* sys_safeunmap() */
+# define SYS_GETMCONTEXT (KERNEL_CALL + 50) /* sys_getmcontext() */
+# define SYS_SETMCONTEXT (KERNEL_CALL + 51) /* sys_setmcontext() */
-#define NR_SYS_CALLS 50 /* number of system calls */
+#define NR_SYS_CALLS 52 /* number of system calls */
#define SYS_CALL_MASK_SIZE BITMAP_CHUNKS(NR_SYS_CALLS)
/* Field names for SYS_MEMSET. */
#define SIG_MAP m2_l1 /* used by kernel to pass signal bit map */
#define SIG_CTXT_PTR m2_p1 /* pointer to info to restore signal context */
-/* Field names for SYS_FORK, _EXEC, _EXIT, _NEWMAP. */
+/* Field names for SYS_FORK, _EXEC, _EXIT, _NEWMAP, GETMCONTEXT, SETMCONTEXT.*/
#define PR_ENDPT m1_i1 /* indicates a process */
#define PR_PRIORITY m1_i2 /* process priority */
#define PR_SLOT m1_i2 /* indicates a process slot */
*/
#define PR_FORK_FLAGS m1_i3 /* optional flags for fork operation */
#define PR_FORK_MSGADDR m1_p1 /* reply message address of forked child */
+#define PR_CTX_PTR m1_p1 /* pointer to mcontext_t structure */
/* Flags for PR_FORK_FLAGS. */
#define PFF_VMINHIBIT 0x01 /* Don't schedule until release by VM. */
#include <minix/safecopies.h>
#include <minix/sef.h>
+#include <machine/mcontext.h>
/* Forward declaration */
struct reg86u;
void *ctl_ptr, void *mem_ptr) );
_PROTOTYPE( int sys_profbuf, (void *ctl_ptr, void *mem_ptr) );
+/* machine context */
+_PROTOTYPE( int sys_getmcontext, (endpoint_t proc, mcontext_t *mcp) );
+_PROTOTYPE( int sys_setmcontext, (endpoint_t proc, mcontext_t *mcp) );
+
#endif /* _SYSLIB_H */
#define FPE_FLTINV 7 /* floating-point invalid operation */
#define FPE_FLTSUB 8 /* subscript out of range */
+typedef struct sigaltstack {
+ void *ss_sp;
+ int ss_flags;
+ size_t ss_size;
+} stack_t;
+
+#define MINSIGSTKSZ 2048 /* Minimal stack size is 2k */
+
+/* Fields for ss_flags */
+#define SS_ONSTACK 1 /* Process is executing on an alternate stack */
+#define SS_DISABLE 2 /* Alternate stack is disabled */
+
+
#endif /* _POSIX_SOURCE */
/* POSIX and ANSI function prototypes. */
mount.h mtio.h param.h ptrace.h queue.h resource.h \
select.h sem.h shm.h sigcontext.h signal.h socket.h \
soundcard.h statfs.h stat.h svrctl.h timeb.h \
- time.h times.h types.h uio.h un.h utsname.h video.h vm.h \
+ time.h times.h types.h ucontext.h uio.h un.h utsname.h video.h vm.h \
wait.h
.include <minix.kinc.mk>
--- /dev/null
+#ifndef _SYS_UCONTEXT_H
+#define _SYS_UCONTEXT_H 1
+
+#include <signal.h>
+#include <machine/mcontext.h>
+
+#define NCARGS 6
+
+#define UCF_SWAPPED 001 /* Context has been swapped in by swapcontext(3) */
+#define UCF_IGNFPU 002 /* Ignore FPU context by get or setcontext(3) */
+#define UCF_IGNSIGM 004 /* Ignore signal mask by get or setcontext(3) */
+typedef struct __ucontext ucontext_t;
+struct __ucontext {
+ unsigned int uc_flags; /* Properties of ucontext */
+ ucontext_t *uc_link; /* Next context to resume when current is finished */
+ mcontext_t uc_mcontext; /* Machine state */
+ sigset_t uc_sigmask; /* Signals blocked in this context */
+ stack_t uc_stack; /* The stack used by this context */
+};
+
+#endif /* _SYS_UCONTEXT_H */
+
--- /dev/null
+#ifndef _UCONTEXT_H
+#define _UCONTEXT_H 1
+
+#include <sys/ucontext.h>
+
+_PROTOTYPE( void makecontext, (ucontext_t *ucp, void (*func)(void),
+ int argc, ...) );
+_PROTOTYPE( int swapcontext, (ucontext_t *oucp,
+ const ucontext_t *ucp) );
+_PROTOTYPE( int getcontext, (ucontext_t *ucp) );
+_PROTOTYPE( int setcontext, (const ucontext_t *ucp) );
+
+_PROTOTYPE( void resumecontext, (ucontext_t *ucp) );
+
+/* These functions get and set ucontext structure through PM/kernel. They don't
+ * manipulate the stack. */
+_PROTOTYPE( int getuctx, (ucontext_t *ucp) );
+_PROTOTYPE( int setuctx, (const ucontext_t *ucp) );
+
+#endif /* _UCONTEXT_H */
+
#define USE_PHYSCOPY 1 /* copy using physical addressing */
#define USE_MEMSET 1 /* write char to a given memory area */
#define USE_RUNCTL 1 /* control stop flags of a process */
+#define USE_MCONTEXT 1 /* enable getting and setting of mach context*/
/* Length of program names stored in the process table. This is only used
* for the debugging dumps that can be generated with the IS server. The PM
map(SYS_READBIOS, do_readbios); /* read from BIOS locations */
map(SYS_IOPENABLE, do_iopenable); /* Enable I/O */
map(SYS_SDEVIO, do_sdevio); /* phys_insb, _insw, _outsb, _outsw */
+
+ /* Machine state switching. */
+ map(SYS_SETMCONTEXT, do_setmcontext); /* set machine context */
+ map(SYS_GETMCONTEXT, do_getmcontext); /* get machine context */
#endif
}
_PROTOTYPE( int do_cprofile, (struct proc * caller, message *m_ptr) );
_PROTOTYPE( int do_profbuf, (struct proc * caller, message *m_ptr) );
+_PROTOTYPE( int do_getmcontext, (struct proc * caller, message *m_ptr) );
+_PROTOTYPE( int do_setmcontext, (struct proc * caller, message *m_ptr) );
+#if ! USE_MCONTEXT
+#define do_getmcontext do_unused
+#define do_setmcontext do_unused
+#endif
+
#endif /* SYSTEM_H */
do_sprofile.o \
do_cprofile.o \
do_profbuf.o \
- do_vmctl.o
+ do_vmctl.o \
+ do_mcontext.o
build $(SYSTEM): $(SYSTEM)($(OBJECTS))
aal cr $@ *.o
--- /dev/null
+/* The kernel calls that are implemented in this file:
+ * m_type: SYS_SETMCONTEXT
+ * m_type: SYS_GETMCONTEXT
+ *
+ * The parameters for these kernel calls are:
+ * m1_i1: PR_ENDPT # proc endpoint doing call
+ * m1_p1: PR_MEM_PTR # pointer to mcontext structure
+ *
+ */
+
+#include "../system.h"
+#include <string.h>
+#include <machine/mcontext.h>
+
+#if USE_MCONTEXT
+/*===========================================================================*
+ * do_getmcontext *
+ *===========================================================================*/
+PUBLIC int do_getmcontext(struct proc * caller, message * m_ptr)
+{
+/* Retrieve machine context of a process */
+
+ register struct proc *rp;
+ int proc_nr, r;
+ mcontext_t mc;
+
+ if (! isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
+ if (iskerneln(proc_nr)) return(EPERM);
+ rp = proc_addr(proc_nr);
+
+#if (_MINIX_CHIP == _CHIP_INTEL)
+ if (!(rp->p_misc_flags & MF_FPU_INITIALIZED))
+ return(OK); /* No state to copy */
+#endif
+
+ /* Get the mcontext structure into our address space. */
+ if ((r = data_copy(m_ptr->PR_ENDPT, (vir_bytes) m_ptr->PR_CTX_PTR, KERNEL,
+ (vir_bytes) &mc, (phys_bytes) sizeof(struct __mcontext))) != OK)
+ return(r);
+
+#if (_MINIX_CHIP == _CHIP_INTEL)
+ /* Copy FPU state */
+ mc.mc_fpu_flags = 0;
+ if (rp->p_misc_flags & MF_FPU_INITIALIZED) {
+ mc.mc_fpu_flags = 0 | rp->p_misc_flags & MF_FPU_INITIALIZED;
+ memcpy(&(mc.mc_fpu_state), rp->p_fpu_state.fpu_save_area_p,
+ FPU_XFP_SIZE);
+ }
+#endif
+
+
+ /* Copy the mcontext structure to the user's address space. */
+ if ((r = data_copy(KERNEL, (vir_bytes) &mc, m_ptr->PR_ENDPT,
+ (vir_bytes) m_ptr->PR_CTX_PTR,
+ (phys_bytes) sizeof(struct __mcontext))) != OK)
+ return(r);
+
+ return(OK);
+}
+
+
+/*===========================================================================*
+ * do_setmcontext *
+ *===========================================================================*/
+PUBLIC int do_setmcontext(struct proc * caller, message * m_ptr)
+{
+/* Set machine context of a process */
+
+ register struct proc *rp;
+ int proc_nr, r;
+ mcontext_t mc;
+
+ if (!isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
+ rp = proc_addr(proc_nr);
+
+ /* Get the mcontext structure into our address space. */
+ if ((r = data_copy(m_ptr->PR_ENDPT, (vir_bytes) m_ptr->PR_CTX_PTR, KERNEL,
+ (vir_bytes) &mc, (phys_bytes) sizeof(struct __mcontext))) != OK)
+ return(r);
+
+#if (_MINIX_CHIP == _CHIP_INTEL)
+ /* Copy FPU state */
+ if (mc.mc_fpu_flags & MF_FPU_INITIALIZED) {
+ rp->p_misc_flags |= MF_FPU_INITIALIZED;
+ memcpy(rp->p_fpu_state.fpu_save_area_p, &(mc.mc_fpu_state),
+ FPU_XFP_SIZE);
+ } else
+ rp->p_misc_flags &= ~MF_FPU_INITIALIZED;
+#endif
+
+ return(OK);
+}
+
+#endif
io_outsl.S \
io_outsw.S \
io_outw.S \
- oneC_sum.S
+ oneC_sum.S \
+ ucontext.S
+
--- /dev/null
+
+#ifdef __ACK__
+.text
+begtext:
+#ifdef __ACK__
+.rom
+#else
+.data
+#endif
+begrom:
+.data
+begdata:
+.bss
+begbss:
+#endif
+
+.extern _getuctx
+.extern _setuctx
+.extern _resumecontext
+
+/* Offsets into ucontext_t structure. Keep in sync with <sys/ucontext.h>! */
+#define UC_FLAGS 0
+#define UC_LINK UC_FLAGS + 4
+#define MCTX UC_LINK + 4
+#define MAGIC MCTX
+#define GS MAGIC+4
+#define FS GS+2
+#define ES FS+2
+#define DS ES+2
+#define DI DS+2
+#define SI DI+4
+#define BP SI+4
+#define ST BP+4 /* Hole for another SP */
+#define BX ST+4
+#define DX BX+4
+#define CX DX+4
+#define AX CX+4
+#define RETADR AX+4
+#define PC RETADR+4
+#define CS PC+4
+#define PSW CS+4
+#define SP PSW+4
+#define SS SP+4
+
+
+/* MCF_MAGIC value from <mcontext.h> */
+#define MCF_MAGIC 0xc0ffee
+
+/* Values from <sys/ucontext.h> */
+#define UCF_IGNFPU 0x002
+#define UCF_IGNSIGM 0x004
+
+
+/* EINVAL from errno.h */
+#define EFAULT 14
+#define EINVAL 22
+
+/* int getcontext(ucontext_t *ucp)
+ * Initialise the structure pointed to by ucp to the current user context
+ * of the calling thread. */
+
+
+.text
+
+.globl _getcontext
+.balign 16
+_getcontext:
+ /* In case a process does not use the FPU and is neither interested in
+ * saving its signal mask, then we can skip the context switch to
+ * PM and kernel altogether and only save general-purpose registers. */
+
+ mov (%esp), %ecx /* Save return address:
+ * When setcontext or swapcontext is called,
+ * we jump to this address and continue
+ * running. */
+
+ mov 4(%esp), %edx /* edx = ucp */
+ /* Check null pointer */
+ cmp $0, %edx /* edx == NULL? */
+ jne 3f /* Not null, continue */
+ movl $EFAULT, (_errno)
+ xor %eax, %eax
+ dec %eax /* return -1 */
+ ret
+
+3: /* Check flags */
+ push %ecx /* save ecx */
+ push %ebx /* save ebx */
+ lea UC_FLAGS(%edx), %ebx /* ebx = &(ucp->uc_flags) */
+ mov (%ebx), %ecx /* ecx = ucp->uc_flags */
+ mov $UCF_IGNFPU, %eax
+ or $UCF_IGNSIGM, %eax
+ cmp %eax, %ecx /* is UCF_IGNFPU or UCF_IGNSIGM set? */
+ pop %ebx /* restore ebx */
+ pop %ecx /* restore ecx */
+ jz 1f /* Both are set, skip getuctx */
+
+0:
+ push %ecx /* Save ecx */
+ push %edx
+ call _getuctx /* getuctx(ucp) */
+ pop %edx /* clean up stack and restore edx */
+ pop %ecx /* Restore ecx */
+
+1:
+ /* Save the context */
+ mov 4(%esp), %edx /* edx = ucp */
+ pop %eax /* retaddr */
+ mov %eax, PC(%edx) /* Save real RTA in mcp struct */
+ mov %esp, SP(%edx) /* Save stack pointer (now pointing to ucp) */
+ /* Save GP registers */
+ mov %ebp, BP(%edx) /* Save EBP */
+ mov %esi, SI(%edx) /* Save ESI */
+ mov %edi, DI(%edx) /* Save EDI */
+ mov %ebx, BX(%edx) /* Save EBX */
+ mov %ecx, CX(%edx) /* Save ECX */
+ movl $MCF_MAGIC, MAGIC(%edx) /* Set magic value */
+ push %eax /* Restore retaddr */
+
+ xor %eax, %eax /* Return 0 */
+
+2:
+ add $4, %esp /* Remove stale (setcontext) RTA */
+ jmp *%ecx /* Restore return address */
+
+
+/* int setcontext(const ucontext_t *ucp)
+ * Restore the user context pointed to by ucp. A successful call to
+ * setcontext does not return; program execution resumes at the point
+ * specified by the ucp argument. If ucp was created with getcontext(),
+ * program execution continues as if the corresponding call of getcontext()
+ * had just returned. If ucp was created with makecontext(), program
+ * execution continues with the function passed to makecontext(). */
+
+.text
+
+.globl _setcontext
+.balign 16
+_setcontext:
+ /* In case a process does not use the FPU and is neither interested in
+ * restoring its signal mask, then we can skip the context switch to
+ * PM and kernel altogether and restore state here. */
+
+ mov 4(%esp), %edx /* edx = ucp */
+
+ /* Check null pointer */
+ cmp $0, %edx /* edx == NULL? */
+ jnz 3f /* Not null, continue */
+ movl $EFAULT, (_errno)
+ xor %eax, %eax
+ dec %eax /* return -1 */
+ ret
+
+3: /* Check flags */
+ push %ebx /* save ebx */
+ lea MAGIC(%edx), %ebx /* ebx = &(ucp->mc_context.mc_magic) */
+ mov (%ebx), %ecx /* ecx = ucp->mc_context.mc_magic */
+ pop %ebx /* restore ebx */
+ cmp $MCF_MAGIC, %ecx /* is the magic value set (is context valid)?*/
+ jz 4f /* is set, proceed */
+ movl $EINVAL, (_errno) /* not set, return error code */
+ xor %eax, %eax
+ dec %eax /* return -1 */
+ ret
+
+
+4: push %ebx /* save ebx */
+ lea UC_FLAGS(%edx), %ebx /* ebx = &(ucp->uc_flags) */
+ mov (%ebx), %ecx /* ecx = ucp->uc_flags */
+ pop %ebx /* restore ebx */
+ mov $UCF_IGNFPU, %eax
+ or $UCF_IGNSIGM, %eax
+ cmp %eax, %ecx /* Are UCF_IGNFPU and UCF_IGNSIGM flags set? */
+ jz 1f /* Both are set, so don't bother restoring FPU
+ * state and signal mask */
+
+0: push %ecx /* Save ecx */
+ push %edx
+ call _setuctx /* setuctx(ucp) */
+ pop %edx /* Clean up stack and restore edx */
+ pop %ecx /* Restore ecx */
+
+1: /* Restore the registers */
+ mov 4(%esp), %edx /* edx = ucp */
+ mov CX(%edx), %ecx /* Restore ECX */
+ mov BX(%edx), %ebx /* Restore EBX */
+ mov DI(%edx), %edi /* Restore EDI */
+ mov SI(%edx), %esi /* Restore ESI */
+ mov BP(%edx), %ebp /* Restore EBP */
+ mov SP(%edx), %esp /* Restore stack pointer */
+
+2:
+ jmp *PC(%edx) /* Push RTA onto stack so we can return to it */
+
+
+/* void ctx_start((void *func)(int arg1, ..., argn), arg1, ..., argn,
+ * ucontext_t *ucp)
+ * A wrapper to start function `func'. ESI register will contain a pointer
+ * to ucp on the stack. By setting ESP to ESI, we effectively 'remove' all
+ * arguments to `func' from the stack. Finally, a call to resumecontext
+ * will start the next context in the linked list (or exit the program if
+ * there is no context). */
+
+.text
+
+.globl _ctx_start
+.balign 16
+_ctx_start:
+ /* 0(esp) -> func
+ * 4(esp) -> arg1
+ * ...
+ * 4*n(esp) -> argn
+ * 4*(n+1)(esp) -> ucp */
+
+ pop %eax /* eax = func */
+ call *%eax /* func(arg1, ..., argn) */
+ mov %esi, %esp /* Clean up stack */
+ /* ucp is now at the top of the stack again */
+ call _resumecontext /* resumecontext(ucp) */
+ ret /* never reached */
+
+
_getsysinfo.c \
_lseek64.c \
_mapdriver.c \
+ _mcontext.c \
_mount.c \
_reboot.c \
_sbrk.c \
--- /dev/null
+/*
+ * mcontext.c
+ */
+#include <lib.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+PUBLIC int setmcontext(const mcontext_t *mcp)
+{
+ message m;
+
+ m.m1_p1 = (char *) mcp;
+
+ return(_syscall(MM, SETMCONTEXT, &m));
+}
+
+
+PUBLIC int getmcontext(mcontext_t *mcp)
+{
+ message m;
+
+ m.m1_p1 = (char *) mcp;
+
+ return(_syscall(MM, GETMCONTEXT, &m));
+}
+
_time.c \
_times.c \
_truncate.c \
+ _ucontext.c \
_umask.c \
_uname.c \
_unlink.c \
--- /dev/null
+#include <lib.h>
+#include <machine/stackframe.h>
+#include <ucontext.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+
+_PROTOTYPE( void ctx_start, (void (*)(void), int, ...) );
+
+/*===========================================================================*
+ * setuctx *
+ *===========================================================================*/
+PUBLIC int setuctx(const ucontext_t *ucp)
+{
+ int r;
+
+ if (ucp == NULL) {
+ errno = EFAULT;
+ return(-1);
+ }
+
+ if (!(ucp->uc_flags & UCF_IGNSIGM)) {
+ /* Set signal mask */
+ if ((r = sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL)) == -1)
+ return(r);
+ }
+
+ if (!(ucp->uc_flags & UCF_IGNFPU)) {
+ if ((r = setmcontext(&(ucp->uc_mcontext))) == -1)
+ return(r);
+ }
+
+ return(0);
+}
+
+
+/*===========================================================================*
+ * getuctx *
+ *===========================================================================*/
+PUBLIC int getuctx(ucontext_t *ucp)
+{
+ int r;
+
+ if (ucp == NULL) {
+ errno = EFAULT;
+ return(-1);
+ }
+
+ if (!(ucp->uc_flags & UCF_IGNSIGM)) {
+ /* Get signal mask */
+ if ((r = sigprocmask(0, NULL, &ucp->uc_sigmask)) == -1)
+ return(r);
+ }
+
+ if (!(ucp->uc_flags & UCF_IGNFPU)) {
+ if ((r = getmcontext(&(ucp->uc_mcontext))) != 0)
+ return(r);
+ }
+
+ return(0);
+}
+
+
+/*===========================================================================*
+ * makecontext *
+ *===========================================================================*/
+PUBLIC void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
+{
+ va_list ap;
+ unsigned int *stack_top, *stack_guard;
+
+ /* There are a number of situations that are erroneous, but we can't actually
+ tell the caller something is wrong, because this is a void function.
+ Instead, mcontext_t contains a magic field that has to be set
+ properly before it can be used. */
+ if (ucp == NULL) {
+ return;
+ } else if ((ucp->uc_stack.ss_sp == NULL) ||
+ (ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
+ ucp->uc_mcontext.mc_magic = 0;
+ ucp->uc_mcontext.mc_p_reg.sp = 0;
+ return;
+ }
+
+ if (ucp->uc_mcontext.mc_magic == MCF_MAGIC) {
+#if (_MINIX_CHIP == _CHIP_INTEL)
+ /* The caller provides a pointer to a stack that we can use to run our
+ context on. When the context starts, control is given to a wrapped
+ start routine, which calls a function and cleans up the stack
+ afterwards. The wrapper needs the address of that function on the
+ stack.
+ The stack will be prepared as follows:
+ func() - start routine
+ arg1 - first argument
+ ...
+ argn - last argument
+ ucp - context, esp points here when `func' returns
+ _ctx_start pops the address of `func' from the stack and calls it.
+ The stack will then be setup with all arguments for `func'. When
+ `func' returns, _ctx_start cleans up the stack such that ucp is at
+ the top of the stack, ready to be used by resumecontext.
+ Resumecontext, in turn, checks whether another context is ready to
+ be executed (i.e., uc_link != NULL) or exit(2)s the process. */
+
+ /* Find the top of the stack from which we grow downwards. */
+ stack_top = (unsigned int *) ((uintptr_t ) ucp->uc_stack.ss_sp +
+ ucp->uc_stack.ss_size);
+
+ /* Align the arguments to 16 bytes (we might lose a few bytes of stack
+ space here).*/
+ stack_top = (unsigned int *) ((uintptr_t) stack_top & ~0xf);
+
+ /* Make room for 'func', the `func' routine arguments, and ucp. */
+ stack_top -= (1 + argc + 1);
+
+ /* Adjust the machine context to point to the top of this stack and the
+ program counter to the context start wrapper. */
+ ucp->uc_mcontext.mc_p_reg.fp = 0; /* Clear frame pointer */
+ ucp->uc_mcontext.mc_p_reg.sp = (reg_t) stack_top;
+ ucp->uc_mcontext.mc_p_reg.pc = (reg_t) ctx_start;
+
+ *stack_top++ = (uintptr_t) func;
+
+ /* Copy arguments to the stack. */
+ va_start(ap, argc);
+ while (argc-- > 0) {
+ *stack_top++ = va_arg(ap, uintptr_t);
+ }
+ va_end(ap);
+
+ /* Store ucp on the stack */
+ *stack_top = (uintptr_t) ucp;
+
+ /* Set ESI to point to the base of the stack where ucp is stored, so
+ that the wrapper function knows how to clean up the stack after
+ calling `func' (i.e., how to adjust ESP). */
+ ucp->uc_mcontext.mc_p_reg.si = (reg_t) stack_top;
+
+
+ /* If we ran out of stack space, invalidate stack pointer. Eventually,
+ swapcontext will choke on this and return ENOMEM. */
+ if (stack_top == ucp->uc_stack.ss_sp)
+ ucp->uc_mcontext.mc_p_reg.sp = 0;
+
+#else
+# error "Unsupported platform"
+#endif
+ }
+}
+
+
+/*===========================================================================*
+ * swapcontext *
+ *===========================================================================*/
+PUBLIC int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
+{
+ int r;
+ unsigned int *stack_guard;
+
+ if ((oucp == NULL) || (ucp == NULL)) {
+ errno = EFAULT;
+ return(-1);
+ }
+
+ if (ucp->uc_mcontext.mc_p_reg.sp == 0) {
+ /* No stack space. Bail out. */
+ errno = ENOMEM;
+ return(-1);
+ }
+
+ oucp->uc_flags &= ~UCF_SWAPPED;
+ r = getcontext(oucp);
+ if ((r == 0) && !(oucp->uc_flags & UCF_SWAPPED)) {
+ oucp->uc_flags |= UCF_SWAPPED;
+ r = setcontext(ucp);
+ }
+
+ return(r);
+}
+
+
+/*===========================================================================*
+ * resumecontext *
+ *===========================================================================*/
+PUBLIC void resumecontext(ucontext_t *ucp)
+{
+ if (ucp->uc_link == NULL) exit(0);
+
+ /* Error handling? Where should the error go to? */
+ (void) setcontext((const ucontext_t *) ucp->uc_link);
+
+ exit(1); /* Never reached */
+}
+
sef_ping.c \
sef_init.c \
sys_abort.c \
+ sys_mcontext.c \
sys_cprof.c \
sys_endsig.c \
sys_eniop.c \
--- /dev/null
+#include "syslib.h"
+
+PUBLIC int sys_getmcontext(proc, mcp)
+endpoint_t proc; /* process retrieving context */
+mcontext_t *mcp; /* where to store context */
+{
+/* A process wants to store its context in mcp. */
+
+ message m;
+ int r;
+
+ m.PR_ENDPT = proc;
+ m.PR_CTX_PTR = (char *) mcp;
+ r = _kernel_call(SYS_GETMCONTEXT, &m);
+ return r;
+}
+
+PUBLIC int sys_setmcontext(proc, mcp)
+endpoint_t proc; /* process setting context */
+mcontext_t *mcp; /* where to get context from */
+{
+/* A process wants to restore context stored in ucp. */
+
+ message m;
+ int r;
+
+ m.PR_ENDPT = proc;
+ m.PR_CTX_PTR = (char *) mcp;
+ r = _kernel_call(SYS_SETMCONTEXT, &m);
+ return r;
+}
+
--- /dev/null
+.TH GETCONTEXT 3 "Mar 2, 2010"
+.SH NAME
+getcontext, setcontext \- get and set current user context
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ucontext.h>
+
+int getcontext(ucontext\_t *\fIucp\fP)
+int setcontext(const ucontext\_t *\fIoucp\fP)
+.SH DESCRIPTION
+The
+.BR makecontext (3)
+,
+.BR swapcontext (3)
+,
+.BR getcontext (3)
+, and
+.BR setcontext (3)
+together form a set of functions that allow user-level context switching between multiple threads of control within a process.
+.PP
+The \fIucontext_t\fP type is a structure that has at least the following members:
+.in +4
+.nf
+
+typedef struct __ucontext {
+ ucontext_t *uc_link;
+ sigset_t uc_sigmask;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ ...
+} ucontext_t;
+
+.fi
+.in
+with \fIsigset_t\fP and \fIstack_t\fP defined in
+.IR <signal.h> .
+Here \fIuc_link\fP points to the context that will be resumed when the current context returns (if \fIuc_link\fP is NULL, the process exits), \fIsigset_t\fP is the set of signals blocks in this context, \fIuc_stack\fP is the stack used by this context (when the context was modified by
+.BR makecontext (3)),
+and \fIuc_mcontext\fP is the machine-specific representation of the saved context. The \fImcontext_t\fP type is machine-dependent and opaque.
+.PP
+MINIX 3 has an additional \fIuc_flags\fP member that supports the following flags:
+.PP
+.in +2
+.nf
+UCF_IGNSIGM /* Current signal mask is not stored or restored */
+UCF_IGNFPU /* FPU state is not stored or restored for this context */
+.fi
+.in
+.PP
+Not storing and restoring the signal mask and/or FPU state speeds up context switching considerably.
+.PP
+
+The
+.BR getcontext ()
+function initializes the structure pointed to by \fIucp\fP to the current user context of the calling thread.
+.PP
+The
+.BR setcontext ()
+function restores the user context pointed to by \fIucp\fP. A succesful call does not return; program execution resumes at the point specified by the \fIucp\fP argument passed to
+.BR setcontext ().
+The \fIucp\fP argument should be created either by a prior call to
+.BR getcontext ()
+or
+.BR makecontext ().
+If the \fIucp\fP argument was created with
+.BR getcontext (),
+program execution continues as if the corresponding call of
+.BR getcontext ()
+had just returned. If the \fIucp\fP argument was created with
+.BR makecontext (),
+program execution continues with the function passed to
+.BR makecontext ().
+
+.SH "RETURN VALUE"
+When successful,
+.BR getcontext ()
+returns 0 and
+.BR setcontext ()
+does not return. Otherwise, both return -1 and
+.I errno
+is set to indicate the error.
+
+.SH "ERRORS"
+.TP 15
+[EINVAL]
+The context is not properly initialized.
+.TP 15
+[EFAULT]
+\fIucp\fP is a NULL pointer.
+
+.SH "SEE ALSO"
+.BR makecontext (3).
+
+.SH "AUTHORS"
+Thomas Veerman
--- /dev/null
+.TH MAKECONTEXT 3 "Mar 2, 2010"
+.SH NAME
+makecontext, swapcontext \- manipulate user contexts
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ucontext.h>
+
+void makecontext(ucontext\_t *\fIucp\fP, void \fI(*func)(void)\fP, int \fIargc\fP, ...)
+int swapcontext(ucontext\_t *\fIoucp\fP, const ucontext\_t *\fIucp\fP)
+.SH DESCRIPTION
+The
+.BR makecontext (3)
+,
+.BR swapcontext (3)
+,
+.BR getcontext (3)
+, and
+.BR setcontext (3)
+together form a set of functions that allow user-level context switching between multiple threads of control within a process.
+.PP
+The
+.BR makecontext ()
+function modifies the user thread pointed to by
+.I ucp
+to continue execution by invoking function
+.I func
+and passing that function a number of
+.I argc
+integer arguments. The value of
+.I argc
+must match the number of integer arguments passed to
+.I func
+, otherwise the behavior is undefined. Context
+.I ucp
+must have been initialized by a call to
+.BR getcontext (3)
+and have a stack allocated for it. The address of the stack must be assigned to \fIucp\->uc_stack.ss_size\fP and the size of the stack to \fIucp\->uc_stack.ss_size\fP. The \fIucp\->uc_link\fP member is used to determine which successor context is run after the context modified by
+.BR makecontext ()
+returns. If left NULL, the process exits.
+.PP
+The
+.BR swapcontext ()
+function saves the current context in the context structure pointed to by
+.I oucp
+and sets the context to the context structure pointed to by \fIucp\fP.
+
+.SH "RETURN VALUE"
+When successful,
+.BR swapcontext ()
+returns 0. Otherwise, -1 is returned and
+.I errno
+is set to indicate the error. Note that a succesful call to
+.BR swapcontext ()
+actually does not return. Only after returning to the context that called
+.BR swapcontext ()
+, it appears as if
+.BR swapcontext ()
+returned 0.
+
+.SH "ERRORS"
+.TP 15
+[EFAULT]
+Either the \fIucp\fP or \fIoucp\fP is a NULL pointer.
+.TP 15
+[EINVAL]
+The context is not properly initialized.
+.TP 15
+[ENOMEM]
+The \fIucp\fP argument does not have enough stack left to complete the operation.
+.SH "SEE ALSO"
+.BR getcontext (3).
+
+.SH "AUTHORS"
+Thomas Veerman
OBJ = main.o forkexit.o break.o exec.o time.o timers.o alarm.o \
signal.o utility.o table.o trace.o getset.o misc.o \
- profile.o dma.o
+ profile.o dma.o mcontext.o
# build local binary
all build: $(SERVER)
--- /dev/null
+#include "pm.h"
+#include <minix/callnr.h>
+#include <minix/endpoint.h>
+#include <minix/com.h>
+#include <minix/vm.h>
+#include "mproc.h"
+#include "param.h"
+
+
+/*===========================================================================*
+ * do_setmcontext *
+ *===========================================================================*/
+PUBLIC int do_setmcontext()
+{
+ return sys_setmcontext(who_e, (mcontext_t *) m_in.m1_p1);
+}
+
+
+/*===========================================================================*
+ * do_getmcontext *
+ *===========================================================================*/
+PUBLIC int do_getmcontext()
+{
+ return sys_getmcontext(who_e, (mcontext_t *) m_in.m1_p1);
+}
+
_PROTOTYPE( int main, (void) );
_PROTOTYPE( void setreply, (int proc_nr, int result) );
+/* mcontext.c */
+_PROTOTYPE( int do_getmcontext, (void) );
+_PROTOTYPE( int do_setmcontext, (void) );
+
/* misc.c */
_PROTOTYPE( int do_reboot, (void) );
_PROTOTYPE( int do_procstat, (void) );
do_itimer, /* 64 = itimer */
do_get, /* 65 = getgroups */
do_set, /* 66 = setgroups */
- no_sys, /* 67 = unused */
- no_sys, /* 68 = unused */
+ do_getmcontext, /* 67 = getmcontext */
+ do_setmcontext, /* 68 = setmcontext */
no_sys, /* 69 = unused */
no_sys, /* 70 = unused */
do_sigaction, /* 71 = sigaction */
register struct mem_map *mem_sp, *mem_dp;
vir_clicks sp_click, gap_base, sp_lower, old_clicks;
- int changed, r;
+ int changed, r, sp_in_dp;
long base_of_stack, sp_delta; /* longs avoid certain problems */
mem_dp = &rmp->vm_arch.vm_seg[D]; /* pointer to data segment map */
return(ENOMEM); /* sp too high */
}
+ /* In order to support user-space libraries, processes might change sp to
+ point to somewhere inside the data segment. If that's the case, be careful
+ not to erroneously think that the data and stack have collided. */
+ sp_in_dp = (mem_dp->mem_vir <= sp_click) &&
+ (mem_dp->mem_vir + mem_dp->mem_len >= sp_click);
+
/* Compute size of gap between stack and data segments. */
sp_delta = (long) mem_sp->mem_vir - (long) sp_click;
- sp_lower = (sp_delta > 0 ? sp_click : mem_sp->mem_vir);
+ sp_lower = ((sp_delta > 0 && !sp_in_dp) ? sp_click : mem_sp->mem_vir);
/* Add a safety margin for future stack growth. Impossible to do right. */
#define SAFETY_BYTES (384 * sizeof(char *))
}
/* Update stack length and origin due to change in stack pointer. */
- if (sp_delta > 0) {
+ if (sp_delta > 0 && !sp_in_dp) {
mem_sp->mem_vir -= sp_delta;
mem_sp->mem_phys -= sp_delta;
mem_sp->mem_len += sp_delta;
GCC = /usr/gnu/bin/gcc
CFLAGS= -O -D_MINIX -D_POSIX_SOURCE
CFLAGS-GCC= $(CFLAGS) -Wall
+CFLAGS-GCCFPU= $(CFLAGS-GCC) -W -mhard-float
OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
test10 test12 test13 test14 test15 test16 test17 test18 test19 \
test21 test22 test23 test25 test26 test27 test28 test29 \
test30 test31 test32 test34 test35 test36 test37 test38 \
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
- test42 test44 test45 test47 test48 test49 test50
+ test42 test44 test45 test47 test48 test49 test50 test51
BIGOBJ= test20 test24
ROOTOBJ= test11 test33 test43 test46
-GCCOBJ= test45-gcc test49-gcc
+GCCOBJ= test45-gcc test49-gcc
+GCCFPUOBJ= test51-gcc
-all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(ROOTOBJ)
+all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(GCCFPUOBJ) $(ROOTOBJ)
chmod 755 *.sh run
$(OBJ):
$(GCCOBJ):
[ ! -x $(GCC) ] || $(GCC) $(CFLAGS-GCC) -o $@ ${@:S/-gcc//}.c
+$(GCCFPUOBJ):
+ [ ! -x $(GCC) ] || $(GCC) $(CFLAGS-GCCFPU) -o $@ ${@:S/-gcc//}.c
+
$(ROOTOBJ):
$(CC) $(CFLAGS) $@.c
@install -c -o root -m 4755 a.out $@
test49: test49.c
test49-gcc: test49.c
test50: test50.c
+test51: test51.c
+test51-gcc: test51.c
# Print test welcome message
clr
-echo "Running POSIX compliance test suite. There are 54 tests in total."
+echo "Running POSIX compliance test suite. There are 56 tests in total."
echo " "
# Run all the tests, keeping track of who failed.
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
- 41 42 43 44 45 45-gcc 46 47 48 49 49-gcc 50 sh1.sh sh2.sh
+ 41 42 43 44 45 45-gcc 46 47 48 49 49-gcc 50 51 51-gcc sh1.sh sh2.sh
do
if [ -x ./test$i ]
then
--- /dev/null
+/* Test51.c
+ *
+ * Test getcontext, setcontext, makecontext, and swapcontext system calls.
+ *
+ * Part of this test is somewhat based on the GNU GCC ucontext test set.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ucontext.h>
+#include <math.h>
+#include <fenv.h>
+
+_PROTOTYPE( void do_calcs, (void) );
+_PROTOTYPE( void do_child, (void) );
+_PROTOTYPE( void do_parent, (void) );
+_PROTOTYPE( void err, (int subtest, int error_no) );
+_PROTOTYPE( void func1, (int a, int b, int c, int d, int e, int f, int g,
+ int h, int i, int j, int k, int l, int m, int n,
+ int o, int p, int q, int r, int s, int t, int u,
+ int v, int w, int x, int y, int z, int aa, int bb));
+_PROTOTYPE( void func2, (void) );
+_PROTOTYPE( void just_exit, (void) );
+_PROTOTYPE( void test_brk, (void) );
+_PROTOTYPE( void verify_main_reenter, (void) );
+
+#define ERROR_MAX 5
+#define SSIZE 32768
+#define ROUNDS 10
+#define SWAPS 10
+
+int errct = 0;
+ucontext_t ctx[3];
+int entered_func1, entered_func2, reentered_main, entered_overflow;
+
+static char st_stack[SSIZE];
+static volatile int shift, global;
+
+void do_calcs(void)
+{
+ float a, b, c, d, e;
+ float foo, bar;
+ int i;
+
+ a = 1.1;
+ b = 2.2;
+ c = 3.3;
+ d = 4.4;
+ e = 5.5;
+
+ foo = a * b; /* 2.42 */
+ bar = c * d; /* 14.52 */
+
+ i = 0;
+ while(i < ROUNDS) {
+ foo += c; /* 5.72 */
+ foo *= d; /* 25.168 */
+ foo /= e; /* 4.5760 */
+ bar -= a; /* 13.42 */
+ bar *= b; /* 29.524 */
+ bar /= e; /* 5.3680 */
+
+ /* Undo */
+ foo *= e;
+ foo /= d;
+ foo -= c;
+
+ bar *= e;
+ bar /= b;
+ bar += a;
+
+ i++;
+ }
+ if(fabs(foo - (a * b)) > 0.0001) err(8, 1);
+ if(fabs(bar - (c * d)) > 0.0001) err(8, 2);
+}
+
+void do_child(void)
+{
+ int s;
+ s = 1;
+
+ /* Initialize FPU context and verify it's set to round to nearest. */
+ if (fegetround() != FE_TONEAREST) err(9, 1);
+
+ /* Now we change the rounding to something else, and this should be preserved
+ between context swaps. */
+ if (fesetround(FE_DOWNWARD) != 0) err(9, 2);
+
+ while(s < SWAPS) {
+ s++;
+ if (swapcontext(&ctx[2], &ctx[1]) == -1) err(9, 2);
+ do_calcs();
+ if (fegetround() != FE_DOWNWARD) err(9, 4);
+ }
+}
+
+void do_parent(void)
+{
+ int s;
+ s = 1;
+
+ /* Initialize FPU context and verify it's set to round to nearest. */
+ if (fegetround() != FE_TONEAREST) err(10, 1);
+
+ /* Now we change the rounding to something else, and this should be preserved
+ between context swaps. */
+ if (fesetround(FE_UPWARD) != 0) err(10, 2);
+
+ while(s < SWAPS) {
+ do_calcs();
+ if (fegetround() != FE_UPWARD) err(10, 3);
+ s++;
+ if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 4);
+ }
+ /* Returning to main thread through uc_link */
+}
+
+void fail(void)
+{
+ /* Shouldn't get here */
+ err(5, 1);
+}
+
+void func1(int a, int b, int c, int d, int e, int f, int g,
+ int h, int i, int j, int k, int l, int m, int n,
+ int o, int p, int q, int r, int s, int t, int u,
+ int v, int w, int x, int y, int z, int aa, int bb)
+{
+ if ( a != (0x0000001 << shift) || b != (0x0000004 << shift) ||
+ c != (0x0000010 << shift) || d != (0x0000040 << shift) ||
+ e != (0x0000100 << shift) || f != (0x0000400 << shift) ||
+ g != (0x0001000 << shift) || h != (0x0004000 << shift) ||
+ i != (0x0010000 << shift) || j != (0x0040000 << shift) ||
+ k != (0x0100000 << shift) || l != (0x0400000 << shift) ||
+ m != (0x1000000 << shift) || n != (0x4000000 << shift) ||
+ o != (0x0000002 << shift) || p != (0x0000008 << shift) ||
+ q != (0x0000020 << shift) || r != (0x0000080 << shift) ||
+ s != (0x0000200 << shift) || t != (0x0000800 << shift) ||
+ u != (0x0002000 << shift) || v != (0x0008000 << shift) ||
+ w != (0x0020000 << shift) || x != (0x0080000 << shift) ||
+ y != (0x0200000 << shift) || z != (0x0800000 << shift) ||
+ aa != (0x2000000 << shift) || bb != (0x8000000 << shift) ) {
+ err(2, 1);
+ }
+
+ if (shift && swapcontext (&ctx[1], &ctx[2]) != 0) err(2, 2);
+ shift++;
+ entered_func1++;
+}
+
+void func2(void)
+{
+ if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1);
+ entered_func2++;
+}
+
+void just_exit(void)
+{
+ if (errct == 0) printf("ok\n");
+ _exit(1);
+}
+
+void test_brk(void)
+{
+ char *big_stack;
+
+ big_stack = malloc(16 * 1024 * 1024); /* 16 MB */
+ /* If this fails, it is likely brk system call failed due stack/data segments
+ collision detection. Unless the system really is low on memory, this is an
+ error. */
+ if (big_stack == NULL) err(7, 1);
+}
+
+void verify_main_reenter(void)
+{
+ if (reentered_main == 0) err(4, 1);
+ if (errct == 0) printf("ok\n");
+}
+
+
+int main(void)
+{
+#ifdef __GNUC__
+ printf("Test 51 (GCC) ");
+#else
+ printf("Test 51 (ACK) ");
+#endif
+ fflush(stdout);
+
+ atexit(verify_main_reenter);
+
+ /* Save current context in ctx[0] */
+ if (getcontext(&ctx[0]) != 0) {
+ /* Don't verify reentering main, not going to happen */
+ atexit(just_exit);
+ err(1, 1);
+ }
+
+ ctx[1] = ctx[0];
+ ctx[1].uc_stack.ss_sp = st_stack;
+ ctx[1].uc_stack.ss_size = SSIZE;
+ ctx[1].uc_link = &ctx[0]; /* When done running, return here */
+
+ /* ctx[1] is going to run func1 and then return here (uc_link). */
+ /* We'll see later on whether makecontext worked. */
+ makecontext(&ctx[1], (void (*) (void)) func1, 28,
+ (0x0000001 << shift), (0x0000004 << shift),
+ (0x0000010 << shift), (0x0000040 << shift),
+ (0x0000100 << shift), (0x0000400 << shift),
+ (0x0001000 << shift), (0x0004000 << shift),
+ (0x0010000 << shift), (0x0040000 << shift),
+ (0x0100000 << shift), (0x0400000 << shift),
+ (0x1000000 << shift), (0x4000000 << shift),
+ (0x0000002 << shift), (0x0000008 << shift),
+ (0x0000020 << shift), (0x0000080 << shift),
+ (0x0000200 << shift), (0x0000800 << shift),
+ (0x0002000 << shift), (0x0008000 << shift),
+ (0x0020000 << shift), (0x0080000 << shift),
+ (0x0200000 << shift), (0x0800000 << shift),
+ (0x2000000 << shift), (0x8000000 << shift));
+
+ if (++global == 1) {
+ /* First time we're here. Let's run ctx[1] and return to ctx[0] when
+ * we're done. Note that we return to above the 'makecontext' call. */
+ if (setcontext(&ctx[1]) != 0) err(1, 2);
+ }
+ if (global != 2) {
+ /* When ++global was 1 we let ctx[1] run and returned to ctx[0], so
+ above ++global is executed again and should've become 2. */
+ err(1, 3);
+ }
+
+ /* Setup ctx[2] to run func2 */
+ if (getcontext(&ctx[2]) != 0) err(1, 4);
+ ctx[2].uc_stack.ss_sp = malloc(SSIZE);
+ ctx[2].uc_stack.ss_size = SSIZE;
+ ctx[2].uc_link = &ctx[1];
+ makecontext(&ctx[2], (void (*) (void)) func2, 0);
+
+ /* Now things become tricky. ctx[2] is set up such that when it finishes
+ running, and starts ctx[1] again. However, func1 swaps back to func2. Then,
+ when func2 has finished running, we continue with ctx[1] and, finally, we
+ return to ctx[0]. */
+ if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 5); /* makecontext failed? */
+ reentered_main = 1;
+
+ /* The call graph is as follows:
+ *
+ * ########
+ * /--------># main #
+ * 7 /----########----\
+ * | | ^ |
+ * | 1 2 3
+ * | V | V
+ * #########----/ #########
+ * # func1 #<-------4-------# func2 #
+ * #########--------5------>#########
+ * ^ |
+ * | |
+ * \---------6--------/
+ *
+ * Main calls func1, func1 increases entered_func1, and returns to main. Main
+ * calls func2, swaps to func1, swaps to func2, which increases entered_func2,
+ * continues with func1, which increases entered_func1 again, continues to
+ * main, where reentered_main is set to 1. In effect, entered_func1 == 2,
+ * entered_func2 == 1, reentered_main == 1. Verify that. */
+
+ if (entered_func1 != 2) err(1, 6);
+ if (entered_func2 != 1) err(1, 7);
+ /* reentered_main == 1 is verified upon exit */
+
+ /* Try to allocate too small a stack */
+ free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
+ if (getcontext(&ctx[2]) != 0) err(1, 8);
+ ctx[2].uc_stack.ss_sp = malloc(MINSIGSTKSZ-1);
+ ctx[2].uc_stack.ss_size = MINSIGSTKSZ-1;
+ ctx[2].uc_link = &ctx[0];
+ makecontext(&ctx[2], (void (*) (void)) fail, 0);
+ /* Because makecontext is void, we can only detect an error by trying to use
+ the invalid context */
+ if (swapcontext(&ctx[0], &ctx[2]) == 0) err(1, 9);
+
+ /* Try to allocate a huge stack to force the usage of brk/sbrk system call
+ to enlarge the data segment. Because we are fiddling with the stack
+ pointer, the OS might think the stack segment and data segment have
+ collided and kill us. This is wrong and therefore the following should
+ work. */
+ free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
+ if (getcontext(&ctx[2]) != 0) err(1, 14);
+ ctx[2].uc_stack.ss_sp = malloc(8 * 1024 * 1024); /* 8 MB */
+ ctx[2].uc_stack.ss_size = 8 * 1024 * 1024;
+ ctx[2].uc_link = &ctx[0];
+ makecontext(&ctx[2], (void (*) (void)) test_brk, 0);
+ if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 15);
+
+ ctx[1].uc_link = &ctx[0];
+ ctx[2].uc_link = NULL;
+ makecontext(&ctx[1], (void (*) (void)) do_parent, 0);
+ makecontext(&ctx[2], (void (*) (void)) do_child, 0);
+ if (swapcontext(&ctx[0], &ctx[2]) == -1) err(1, 16);
+
+ return(0);
+}
+
+/* We can't use a global subtest variable reliably, because threads might
+ change the value when we reenter a thread (i.e., report wrong subtest
+ number). */
+void err(int subtest, int error_no)
+{
+ printf("Subtest %d, error %d\n", subtest, error_no);
+
+ if (errct++ > ERROR_MAX) {
+ printf("Too many errors, test aborted\n");
+ exit(1);
+ }
+}
+