- Add support for the ucontext system calls (getcontext, setcontext,

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.
This commit is contained in:
Thomas Veerman 2010-03-12 15:58:41 +00:00
parent 3efbb8f133
commit bef0e3eb63
33 changed files with 1232 additions and 19 deletions

View file

@ -8,7 +8,7 @@ INCS= alloca.h ansi.h a.out.h ar.h assert.h configfile.h ctype.h \
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 \

View file

@ -1,7 +1,7 @@
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>

View file

@ -0,0 +1,23 @@
#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 */

View file

@ -56,6 +56,8 @@
#define ITIMER 64
#define GETGROUPS 65
#define SETGROUPS 66
#define GETMCONTEXT 67
#define SETMCONTEXT 68
/* Posix signal handling. */
#define SIGACTION 71

View file

@ -355,8 +355,10 @@
# 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. */
@ -510,7 +512,7 @@
#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 */
@ -522,6 +524,7 @@
*/
#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. */

View file

@ -19,6 +19,7 @@
#include <minix/safecopies.h>
#include <minix/sef.h>
#include <machine/mcontext.h>
/* Forward declaration */
struct reg86u;
@ -250,5 +251,9 @@ _PROTOTYPE( int sys_cprof, (int action, int size, endpoint_t endpt,
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 */

View file

@ -113,6 +113,19 @@ struct sigaction {
#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. */

View file

@ -8,7 +8,7 @@ INCS= asynchio.h dir.h file.h ioc_cmos.h ioc_disk.h \
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>

22
include/sys/ucontext.h Normal file
View file

@ -0,0 +1,22 @@
#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 */

21
include/ucontext.h Normal file
View file

@ -0,0 +1,21 @@
#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 */

View file

@ -42,6 +42,7 @@
#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

View file

@ -236,6 +236,10 @@ PUBLIC void system_init(void)
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
}

View file

@ -192,5 +192,12 @@ _PROTOTYPE( int do_sprofile, (struct proc * caller, message *m_ptr) );
_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 */

View file

@ -57,7 +57,8 @@ OBJECTS = \
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

View file

@ -0,0 +1,94 @@
/* 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

View file

@ -20,4 +20,6 @@ SRCS+= \
io_outsl.S \
io_outsw.S \
io_outw.S \
oneC_sum.S
oneC_sum.S \
ucontext.S

View file

@ -0,0 +1,222 @@
#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 */

View file

@ -22,6 +22,7 @@ SRCS+= \
_getsysinfo.c \
_lseek64.c \
_mapdriver.c \
_mcontext.c \
_mount.c \
_reboot.c \
_sbrk.c \

View file

@ -0,0 +1,26 @@
/*
* 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));
}

View file

@ -91,6 +91,7 @@ SRCS+= \
_time.c \
_times.c \
_truncate.c \
_ucontext.c \
_umask.c \
_uname.c \
_unlink.c \

197
lib/libc/posix/_ucontext.c Normal file
View file

@ -0,0 +1,197 @@
#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 */
}

View file

@ -33,6 +33,7 @@ SRCS= \
sef_ping.c \
sef_init.c \
sys_abort.c \
sys_mcontext.c \
sys_cprof.c \
sys_endsig.c \
sys_eniop.c \

32
lib/libsys/sys_mcontext.c Normal file
View file

@ -0,0 +1,32 @@
#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;
}

96
man/man3/getcontext.3 Normal file
View file

@ -0,0 +1,96 @@
.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

75
man/man3/makecontext.3 Normal file
View file

@ -0,0 +1,75 @@
.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

View file

@ -18,7 +18,7 @@ LDFLAGS = -i
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)

26
servers/pm/mcontext.c Normal file
View file

@ -0,0 +1,26 @@
#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);
}

View file

@ -45,6 +45,10 @@ _PROTOTYPE( int do_set, (void) );
_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) );

View file

@ -78,8 +78,8 @@ _PROTOTYPE (int (*call_vec[]), (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 */

View file

@ -75,7 +75,7 @@ vir_bytes sp; /* new value of sp */
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 */
@ -90,9 +90,15 @@ vir_bytes sp; /* new value of sp */
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 *))
@ -111,7 +117,7 @@ vir_bytes sp; /* new value of sp */
}
/* 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;

View file

@ -4,19 +4,21 @@ CC = exec cc
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
GCCFPUOBJ= test51-gcc
all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(ROOTOBJ)
all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(GCCFPUOBJ) $(ROOTOBJ)
chmod 755 *.sh run
$(OBJ):
@ -28,6 +30,9 @@ $(BIGOBJ):
$(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 $@
@ -99,4 +104,6 @@ test48: test48.c
test49: test49.c
test49-gcc: test49.c
test50: test50.c
test51: test51.c
test51-gcc: test51.c

View file

@ -13,13 +13,13 @@ badones= # list of tests that failed
# 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

321
test/test51.c Normal file
View file

@ -0,0 +1,321 @@
/* 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);
}
}