]> Zhao Yanbai Git Server - minix.git/commitdiff
- Add support for the ucontext system calls (getcontext, setcontext,
authorThomas Veerman <thomas@minix3.org>
Fri, 12 Mar 2010 15:58:41 +0000 (15:58 +0000)
committerThomas Veerman <thomas@minix3.org>
Fri, 12 Mar 2010 15:58:41 +0000 (15:58 +0000)
  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.

33 files changed:
include/Makefile
include/arch/i386/Makefile
include/arch/i386/mcontext.h [new file with mode: 0644]
include/minix/callnr.h
include/minix/com.h
include/minix/syslib.h
include/signal.h
include/sys/Makefile
include/sys/ucontext.h [new file with mode: 0644]
include/ucontext.h [new file with mode: 0644]
kernel/config.h
kernel/system.c
kernel/system.h
kernel/system/Makefile
kernel/system/do_mcontext.c [new file with mode: 0644]
lib/libc/arch/i386/misc/Makefile.inc
lib/libc/arch/i386/misc/ucontext.S [new file with mode: 0644]
lib/libc/other/Makefile.inc
lib/libc/other/_mcontext.c [new file with mode: 0644]
lib/libc/posix/Makefile.inc
lib/libc/posix/_ucontext.c [new file with mode: 0644]
lib/libsys/Makefile
lib/libsys/sys_mcontext.c [new file with mode: 0644]
man/man3/getcontext.3 [new file with mode: 0644]
man/man3/makecontext.3 [new file with mode: 0644]
servers/pm/Makefile
servers/pm/mcontext.c [new file with mode: 0644]
servers/pm/proto.h
servers/pm/table.c
servers/vm/break.c
test/Makefile
test/run
test/test51.c [new file with mode: 0644]

index ee34594135d4f5caf4344beeaa9e5fcea790c9af..d23e024be1fb8e9607f28553619b040d4f6e299b 100644 (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 \
index b3887ea76dde7e86ee406621570f52279634a5c8..dacd0bfaa4989013a6dd9062106bfb9554a08b6d 100644 (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>
diff --git a/include/arch/i386/mcontext.h b/include/arch/i386/mcontext.h
new file mode 100644 (file)
index 0000000..312308d
--- /dev/null
@@ -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 */
+
index b175f9adb0b05a3f96094dd2fcec24a49ed837fd..a4d28ef0a551f1891568485c3c7ca563b12f0869 100644 (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
index e9674d7c074d7c81371d299a66ec4342b189fbd1..3642e3fac922936da10b6b704f5a06ea867feb08 100644 (file)
 #  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. */
index 4bb08288a7ac350d270cb9b425b555e168cba22c..a11b821598200682392c2d72eb7e03f47b95fcb3 100644 (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 */
 
index 00f02de01844122bcbc0034e3f10dda3d20df822..5d701e6590f69792bb74f9e15acba72a50865a2f 100644 (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. */
index caf81424ea9086aa7402603953441044196d41d5..0ac955c2bac703d46ca7a62dc1afae4c9eaec3bb 100644 (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>
diff --git a/include/sys/ucontext.h b/include/sys/ucontext.h
new file mode 100644 (file)
index 0000000..446ff84
--- /dev/null
@@ -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 */
+
diff --git a/include/ucontext.h b/include/ucontext.h
new file mode 100644 (file)
index 0000000..1c91d30
--- /dev/null
@@ -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 */
+
index c246eff5b8079580052fdda960facba1c8ab027e..ef6f2897ea97776de2e42f95c854c42b70a8ae6b 100644 (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
index 82c0c175295fd7fae4d617b53dbd8ede9adc33f8..e7e4bea5c5e99f875b936570d206455a27d17c1f 100644 (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
 }
 
index 00e7cd08a04ea370ab88be551e542f544dbc424e..951b4ea6a16d8f839cd6494be734bd979387471e 100644 (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 */
 
index f2d0081b2f692a2b68bdb0675f5b7aa1f8b65134..8ddd1af7abf08d082b879ec9f81199409a9873ac 100644 (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
diff --git a/kernel/system/do_mcontext.c b/kernel/system/do_mcontext.c
new file mode 100644 (file)
index 0000000..9dd322f
--- /dev/null
@@ -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
index d915c3a942a5e7b4ac43800a2960d71d67395a4c..57b0f604cc1f4adde60efc51c7e392ca895b3299 100644 (file)
@@ -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 (file)
index 0000000..623faaf
--- /dev/null
@@ -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 */
+
+
index e5a45152029d5f79fb2d7cfc678ecaf1d669d39c..04e56303db3a56f6b646f875011ff4a87a139bd3 100644 (file)
@@ -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 (file)
index 0000000..e940d2c
--- /dev/null
@@ -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));
+}
+
index dd7912d60dc718de53934106a1bfa899988e413e..7c6833c25a9b0b44e2820002b3a7c658941c7355 100644 (file)
@@ -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 (file)
index 0000000..1101335
--- /dev/null
@@ -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 */
+}
+
index afd80727b1827c2a2ae9417c74433038447e8d24..dc408dbc1000d012ea4aad42bf2d10e83cca3936 100644 (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 \
diff --git a/lib/libsys/sys_mcontext.c b/lib/libsys/sys_mcontext.c
new file mode 100644 (file)
index 0000000..164c066
--- /dev/null
@@ -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 (file)
index 0000000..7e2279b
--- /dev/null
@@ -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
diff --git a/man/man3/makecontext.3 b/man/man3/makecontext.3
new file mode 100644 (file)
index 0000000..151376c
--- /dev/null
@@ -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
index dbb2891ec7c74385adee551bf76b0cd307c47afa..c48759275493a3f9bcbcedf8c309a57ee9c23b87 100644 (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)
diff --git a/servers/pm/mcontext.c b/servers/pm/mcontext.c
new file mode 100644 (file)
index 0000000..f89f6fc
--- /dev/null
@@ -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);
+}
+
index cb979aacc439ebdff45b7c592ca548f8f330d1f8..2d3d32811130104c6b4177075bc35a40a48eb070 100644 (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)                                    );
index 8ea3ba5c736621b22e5ba09f695e28de5ee541b0..5fd8bf94c4baf7a0d6e44d91cd54a83ccf66b368 100644 (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   */
index da9e2c7aee89a814814ca2b9ce85b1f120264ab2..4f00c9e28600083c7157591c6c434726e9408cd6 100644 (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;
index ff10a34af3ffaeb7541e7d5524227af497d9b8eb..0d471dcd9dc9f3c50c1a1c00e32f85441782d654 100644 (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
+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
 
index 11cb0a2ccfb4b9b5f7c8dc2939b5569eff889d22..59a2499e88a952f1e7fc1f4434d25ad71481bbb5 100755 (executable)
--- 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 (file)
index 0000000..6527fc4
--- /dev/null
@@ -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);
+  }
+}
+