From bef0e3eb6334a89e1619435038d9cb4233b15445 Mon Sep 17 00:00:00 2001 From: Thomas Veerman Date: Fri, 12 Mar 2010 15:58:41 +0000 Subject: [PATCH] - 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. --- include/Makefile | 2 +- include/arch/i386/Makefile | 4 +- include/arch/i386/mcontext.h | 23 ++ include/minix/callnr.h | 2 + include/minix/com.h | 7 +- include/minix/syslib.h | 5 + include/signal.h | 13 ++ include/sys/Makefile | 2 +- include/sys/ucontext.h | 22 ++ include/ucontext.h | 21 ++ kernel/config.h | 1 + kernel/system.c | 4 + kernel/system.h | 7 + kernel/system/Makefile | 3 +- kernel/system/do_mcontext.c | 94 ++++++++ lib/libc/arch/i386/misc/Makefile.inc | 4 +- lib/libc/arch/i386/misc/ucontext.S | 222 ++++++++++++++++++ lib/libc/other/Makefile.inc | 1 + lib/libc/other/_mcontext.c | 26 +++ lib/libc/posix/Makefile.inc | 1 + lib/libc/posix/_ucontext.c | 197 ++++++++++++++++ lib/libsys/Makefile | 1 + lib/libsys/sys_mcontext.c | 32 +++ man/man3/getcontext.3 | 96 ++++++++ man/man3/makecontext.3 | 75 +++++++ servers/pm/Makefile | 2 +- servers/pm/mcontext.c | 26 +++ servers/pm/proto.h | 4 + servers/pm/table.c | 4 +- servers/vm/break.c | 12 +- test/Makefile | 13 +- test/run | 4 +- test/test51.c | 321 +++++++++++++++++++++++++++ 33 files changed, 1232 insertions(+), 19 deletions(-) create mode 100644 include/arch/i386/mcontext.h create mode 100644 include/sys/ucontext.h create mode 100644 include/ucontext.h create mode 100644 kernel/system/do_mcontext.c create mode 100644 lib/libc/arch/i386/misc/ucontext.S create mode 100644 lib/libc/other/_mcontext.c create mode 100644 lib/libc/posix/_ucontext.c create mode 100644 lib/libsys/sys_mcontext.c create mode 100644 man/man3/getcontext.3 create mode 100644 man/man3/makecontext.3 create mode 100644 servers/pm/mcontext.c create mode 100644 test/test51.c diff --git a/include/Makefile b/include/Makefile index ee3459413..d23e024be 100644 --- a/include/Makefile +++ b/include/Makefile @@ -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 \ diff --git a/include/arch/i386/Makefile b/include/arch/i386/Makefile index b3887ea76..dacd0bfaa 100644 --- a/include/arch/i386/Makefile +++ b/include/arch/i386/Makefile @@ -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 diff --git a/include/arch/i386/mcontext.h b/include/arch/i386/mcontext.h new file mode 100644 index 000000000..312308d27 --- /dev/null +++ b/include/arch/i386/mcontext.h @@ -0,0 +1,23 @@ +#ifndef _MACHINE_MCONTEXT_H +#define _MACHINE_MCONTEXT_H 1 + +#include +#include + +#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 */ + diff --git a/include/minix/callnr.h b/include/minix/callnr.h index b175f9adb..a4d28ef0a 100644 --- a/include/minix/callnr.h +++ b/include/minix/callnr.h @@ -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 diff --git a/include/minix/com.h b/include/minix/com.h index e9674d7c0..3642e3fac 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -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. */ diff --git a/include/minix/syslib.h b/include/minix/syslib.h index 4bb08288a..a11b82159 100644 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -19,6 +19,7 @@ #include #include +#include /* 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 */ diff --git a/include/signal.h b/include/signal.h index 00f02de01..5d701e659 100644 --- a/include/signal.h +++ b/include/signal.h @@ -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. */ diff --git a/include/sys/Makefile b/include/sys/Makefile index caf81424e..0ac955c2b 100644 --- a/include/sys/Makefile +++ b/include/sys/Makefile @@ -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 diff --git a/include/sys/ucontext.h b/include/sys/ucontext.h new file mode 100644 index 000000000..446ff84e6 --- /dev/null +++ b/include/sys/ucontext.h @@ -0,0 +1,22 @@ +#ifndef _SYS_UCONTEXT_H +#define _SYS_UCONTEXT_H 1 + +#include +#include + +#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 */ + diff --git a/include/ucontext.h b/include/ucontext.h new file mode 100644 index 000000000..1c91d309b --- /dev/null +++ b/include/ucontext.h @@ -0,0 +1,21 @@ +#ifndef _UCONTEXT_H +#define _UCONTEXT_H 1 + +#include + +_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 */ + diff --git a/kernel/config.h b/kernel/config.h index c246eff5b..ef6f2897e 100644 --- a/kernel/config.h +++ b/kernel/config.h @@ -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 diff --git a/kernel/system.c b/kernel/system.c index 82c0c1752..e7e4bea5c 100644 --- a/kernel/system.c +++ b/kernel/system.c @@ -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 } diff --git a/kernel/system.h b/kernel/system.h index 00e7cd08a..951b4ea6a 100644 --- a/kernel/system.h +++ b/kernel/system.h @@ -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 */ diff --git a/kernel/system/Makefile b/kernel/system/Makefile index f2d0081b2..8ddd1af7a 100644 --- a/kernel/system/Makefile +++ b/kernel/system/Makefile @@ -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 diff --git a/kernel/system/do_mcontext.c b/kernel/system/do_mcontext.c new file mode 100644 index 000000000..9dd322f80 --- /dev/null +++ b/kernel/system/do_mcontext.c @@ -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 +#include + +#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 diff --git a/lib/libc/arch/i386/misc/Makefile.inc b/lib/libc/arch/i386/misc/Makefile.inc index d915c3a94..57b0f604c 100644 --- a/lib/libc/arch/i386/misc/Makefile.inc +++ b/lib/libc/arch/i386/misc/Makefile.inc @@ -20,4 +20,6 @@ SRCS+= \ io_outsl.S \ io_outsw.S \ io_outw.S \ - oneC_sum.S + oneC_sum.S \ + ucontext.S + diff --git a/lib/libc/arch/i386/misc/ucontext.S b/lib/libc/arch/i386/misc/ucontext.S new file mode 100644 index 000000000..623faaf29 --- /dev/null +++ b/lib/libc/arch/i386/misc/ucontext.S @@ -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 ! */ +#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 */ +#define MCF_MAGIC 0xc0ffee + +/* Values from */ +#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 */ + + diff --git a/lib/libc/other/Makefile.inc b/lib/libc/other/Makefile.inc index e5a451520..04e56303d 100644 --- a/lib/libc/other/Makefile.inc +++ b/lib/libc/other/Makefile.inc @@ -22,6 +22,7 @@ SRCS+= \ _getsysinfo.c \ _lseek64.c \ _mapdriver.c \ + _mcontext.c \ _mount.c \ _reboot.c \ _sbrk.c \ diff --git a/lib/libc/other/_mcontext.c b/lib/libc/other/_mcontext.c new file mode 100644 index 000000000..e940d2c79 --- /dev/null +++ b/lib/libc/other/_mcontext.c @@ -0,0 +1,26 @@ +/* + * mcontext.c + */ +#include +#include +#include + +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)); +} + diff --git a/lib/libc/posix/Makefile.inc b/lib/libc/posix/Makefile.inc index dd7912d60..7c6833c25 100644 --- a/lib/libc/posix/Makefile.inc +++ b/lib/libc/posix/Makefile.inc @@ -91,6 +91,7 @@ SRCS+= \ _time.c \ _times.c \ _truncate.c \ + _ucontext.c \ _umask.c \ _uname.c \ _unlink.c \ diff --git a/lib/libc/posix/_ucontext.c b/lib/libc/posix/_ucontext.c new file mode 100644 index 000000000..110133566 --- /dev/null +++ b/lib/libc/posix/_ucontext.c @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +_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 */ +} + diff --git a/lib/libsys/Makefile b/lib/libsys/Makefile index afd80727b..dc408dbc1 100644 --- a/lib/libsys/Makefile +++ b/lib/libsys/Makefile @@ -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 \ diff --git a/lib/libsys/sys_mcontext.c b/lib/libsys/sys_mcontext.c new file mode 100644 index 000000000..164c06636 --- /dev/null +++ b/lib/libsys/sys_mcontext.c @@ -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; +} + diff --git a/man/man3/getcontext.3 b/man/man3/getcontext.3 new file mode 100644 index 000000000..7e2279b52 --- /dev/null +++ b/man/man3/getcontext.3 @@ -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 + +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 . +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 diff --git a/man/man3/makecontext.3 b/man/man3/makecontext.3 new file mode 100644 index 000000000..151376c43 --- /dev/null +++ b/man/man3/makecontext.3 @@ -0,0 +1,75 @@ +.TH MAKECONTEXT 3 "Mar 2, 2010" +.SH NAME +makecontext, swapcontext \- manipulate user contexts +.SH SYNOPSIS +.nf +.ft B +#include + +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 diff --git a/servers/pm/Makefile b/servers/pm/Makefile index dbb2891ec..c48759275 100644 --- a/servers/pm/Makefile +++ b/servers/pm/Makefile @@ -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) diff --git a/servers/pm/mcontext.c b/servers/pm/mcontext.c new file mode 100644 index 000000000..f89f6fce6 --- /dev/null +++ b/servers/pm/mcontext.c @@ -0,0 +1,26 @@ +#include "pm.h" +#include +#include +#include +#include +#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); +} + diff --git a/servers/pm/proto.h b/servers/pm/proto.h index cb979aacc..2d3d32811 100644 --- a/servers/pm/proto.h +++ b/servers/pm/proto.h @@ -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) ); diff --git a/servers/pm/table.c b/servers/pm/table.c index 8ea3ba5c7..5fd8bf94c 100644 --- a/servers/pm/table.c +++ b/servers/pm/table.c @@ -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 */ diff --git a/servers/vm/break.c b/servers/vm/break.c index da9e2c7ae..4f00c9e28 100644 --- a/servers/vm/break.c +++ b/servers/vm/break.c @@ -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; diff --git a/test/Makefile b/test/Makefile index ff10a34af..0d471dcd9 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 +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 diff --git a/test/run b/test/run index 11cb0a2cc..59a2499e8 100755 --- a/test/run +++ b/test/run @@ -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 diff --git a/test/test51.c b/test/test51.c new file mode 100644 index 000000000..6527fc4ff --- /dev/null +++ b/test/test51.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +_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); + } +} + -- 2.44.0