- Add support for the ucontext system calls (getcontext, setcontext,
swapcontext, and makecontext). - Fix VM to not erroneously think the stack segment and data segment have collided when a user-space thread invokes brk(). - Add test51 to test ucontext functionality. - Add man pages for ucontext system calls.
This commit is contained in:
parent
3efbb8f133
commit
bef0e3eb63
33 changed files with 1232 additions and 19 deletions
|
@ -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 \
|
||||
|
|
|
@ -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>
|
||||
|
|
23
include/arch/i386/mcontext.h
Normal file
23
include/arch/i386/mcontext.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef _MACHINE_MCONTEXT_H
|
||||
#define _MACHINE_MCONTEXT_H 1
|
||||
|
||||
#include <machine/fpu.h>
|
||||
#include <machine/stackframe.h>
|
||||
|
||||
#define MCF_MAGIC 0xc0ffee
|
||||
|
||||
/* Context to describe processor state */
|
||||
typedef struct __mcontext {
|
||||
int mc_magic;
|
||||
struct stackframe_s mc_p_reg;
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
union fpu_state_u mc_fpu_state;
|
||||
#endif
|
||||
short mc_fpu_flags;
|
||||
} mcontext_t;
|
||||
|
||||
_PROTOTYPE( int setmcontext, (const mcontext_t *mcp) );
|
||||
_PROTOTYPE( int getmcontext, (mcontext_t *mcp) );
|
||||
|
||||
#endif /* _MACHINE_MCONTEXT_H */
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -8,7 +8,7 @@ INCS= asynchio.h dir.h file.h ioc_cmos.h ioc_disk.h \
|
|||
mount.h mtio.h param.h ptrace.h queue.h resource.h \
|
||||
select.h sem.h shm.h sigcontext.h signal.h socket.h \
|
||||
soundcard.h statfs.h stat.h svrctl.h timeb.h \
|
||||
time.h times.h types.h uio.h un.h utsname.h video.h vm.h \
|
||||
time.h times.h types.h ucontext.h uio.h un.h utsname.h video.h vm.h \
|
||||
wait.h
|
||||
|
||||
.include <minix.kinc.mk>
|
||||
|
|
22
include/sys/ucontext.h
Normal file
22
include/sys/ucontext.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef _SYS_UCONTEXT_H
|
||||
#define _SYS_UCONTEXT_H 1
|
||||
|
||||
#include <signal.h>
|
||||
#include <machine/mcontext.h>
|
||||
|
||||
#define NCARGS 6
|
||||
|
||||
#define UCF_SWAPPED 001 /* Context has been swapped in by swapcontext(3) */
|
||||
#define UCF_IGNFPU 002 /* Ignore FPU context by get or setcontext(3) */
|
||||
#define UCF_IGNSIGM 004 /* Ignore signal mask by get or setcontext(3) */
|
||||
typedef struct __ucontext ucontext_t;
|
||||
struct __ucontext {
|
||||
unsigned int uc_flags; /* Properties of ucontext */
|
||||
ucontext_t *uc_link; /* Next context to resume when current is finished */
|
||||
mcontext_t uc_mcontext; /* Machine state */
|
||||
sigset_t uc_sigmask; /* Signals blocked in this context */
|
||||
stack_t uc_stack; /* The stack used by this context */
|
||||
};
|
||||
|
||||
#endif /* _SYS_UCONTEXT_H */
|
||||
|
21
include/ucontext.h
Normal file
21
include/ucontext.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef _UCONTEXT_H
|
||||
#define _UCONTEXT_H 1
|
||||
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
_PROTOTYPE( void makecontext, (ucontext_t *ucp, void (*func)(void),
|
||||
int argc, ...) );
|
||||
_PROTOTYPE( int swapcontext, (ucontext_t *oucp,
|
||||
const ucontext_t *ucp) );
|
||||
_PROTOTYPE( int getcontext, (ucontext_t *ucp) );
|
||||
_PROTOTYPE( int setcontext, (const ucontext_t *ucp) );
|
||||
|
||||
_PROTOTYPE( void resumecontext, (ucontext_t *ucp) );
|
||||
|
||||
/* These functions get and set ucontext structure through PM/kernel. They don't
|
||||
* manipulate the stack. */
|
||||
_PROTOTYPE( int getuctx, (ucontext_t *ucp) );
|
||||
_PROTOTYPE( int setuctx, (const ucontext_t *ucp) );
|
||||
|
||||
#endif /* _UCONTEXT_H */
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
94
kernel/system/do_mcontext.c
Normal file
94
kernel/system/do_mcontext.c
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* The kernel calls that are implemented in this file:
|
||||
* m_type: SYS_SETMCONTEXT
|
||||
* m_type: SYS_GETMCONTEXT
|
||||
*
|
||||
* The parameters for these kernel calls are:
|
||||
* m1_i1: PR_ENDPT # proc endpoint doing call
|
||||
* m1_p1: PR_MEM_PTR # pointer to mcontext structure
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../system.h"
|
||||
#include <string.h>
|
||||
#include <machine/mcontext.h>
|
||||
|
||||
#if USE_MCONTEXT
|
||||
/*===========================================================================*
|
||||
* do_getmcontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC int do_getmcontext(struct proc * caller, message * m_ptr)
|
||||
{
|
||||
/* Retrieve machine context of a process */
|
||||
|
||||
register struct proc *rp;
|
||||
int proc_nr, r;
|
||||
mcontext_t mc;
|
||||
|
||||
if (! isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
|
||||
if (iskerneln(proc_nr)) return(EPERM);
|
||||
rp = proc_addr(proc_nr);
|
||||
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
if (!(rp->p_misc_flags & MF_FPU_INITIALIZED))
|
||||
return(OK); /* No state to copy */
|
||||
#endif
|
||||
|
||||
/* Get the mcontext structure into our address space. */
|
||||
if ((r = data_copy(m_ptr->PR_ENDPT, (vir_bytes) m_ptr->PR_CTX_PTR, KERNEL,
|
||||
(vir_bytes) &mc, (phys_bytes) sizeof(struct __mcontext))) != OK)
|
||||
return(r);
|
||||
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
/* Copy FPU state */
|
||||
mc.mc_fpu_flags = 0;
|
||||
if (rp->p_misc_flags & MF_FPU_INITIALIZED) {
|
||||
mc.mc_fpu_flags = 0 | rp->p_misc_flags & MF_FPU_INITIALIZED;
|
||||
memcpy(&(mc.mc_fpu_state), rp->p_fpu_state.fpu_save_area_p,
|
||||
FPU_XFP_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Copy the mcontext structure to the user's address space. */
|
||||
if ((r = data_copy(KERNEL, (vir_bytes) &mc, m_ptr->PR_ENDPT,
|
||||
(vir_bytes) m_ptr->PR_CTX_PTR,
|
||||
(phys_bytes) sizeof(struct __mcontext))) != OK)
|
||||
return(r);
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* do_setmcontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC int do_setmcontext(struct proc * caller, message * m_ptr)
|
||||
{
|
||||
/* Set machine context of a process */
|
||||
|
||||
register struct proc *rp;
|
||||
int proc_nr, r;
|
||||
mcontext_t mc;
|
||||
|
||||
if (!isokendpt(m_ptr->PR_ENDPT, &proc_nr)) return(EINVAL);
|
||||
rp = proc_addr(proc_nr);
|
||||
|
||||
/* Get the mcontext structure into our address space. */
|
||||
if ((r = data_copy(m_ptr->PR_ENDPT, (vir_bytes) m_ptr->PR_CTX_PTR, KERNEL,
|
||||
(vir_bytes) &mc, (phys_bytes) sizeof(struct __mcontext))) != OK)
|
||||
return(r);
|
||||
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
/* Copy FPU state */
|
||||
if (mc.mc_fpu_flags & MF_FPU_INITIALIZED) {
|
||||
rp->p_misc_flags |= MF_FPU_INITIALIZED;
|
||||
memcpy(rp->p_fpu_state.fpu_save_area_p, &(mc.mc_fpu_state),
|
||||
FPU_XFP_SIZE);
|
||||
} else
|
||||
rp->p_misc_flags &= ~MF_FPU_INITIALIZED;
|
||||
#endif
|
||||
|
||||
return(OK);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -20,4 +20,6 @@ SRCS+= \
|
|||
io_outsl.S \
|
||||
io_outsw.S \
|
||||
io_outw.S \
|
||||
oneC_sum.S
|
||||
oneC_sum.S \
|
||||
ucontext.S
|
||||
|
||||
|
|
222
lib/libc/arch/i386/misc/ucontext.S
Normal file
222
lib/libc/arch/i386/misc/ucontext.S
Normal file
|
@ -0,0 +1,222 @@
|
|||
|
||||
#ifdef __ACK__
|
||||
.text
|
||||
begtext:
|
||||
#ifdef __ACK__
|
||||
.rom
|
||||
#else
|
||||
.data
|
||||
#endif
|
||||
begrom:
|
||||
.data
|
||||
begdata:
|
||||
.bss
|
||||
begbss:
|
||||
#endif
|
||||
|
||||
.extern _getuctx
|
||||
.extern _setuctx
|
||||
.extern _resumecontext
|
||||
|
||||
/* Offsets into ucontext_t structure. Keep in sync with <sys/ucontext.h>! */
|
||||
#define UC_FLAGS 0
|
||||
#define UC_LINK UC_FLAGS + 4
|
||||
#define MCTX UC_LINK + 4
|
||||
#define MAGIC MCTX
|
||||
#define GS MAGIC+4
|
||||
#define FS GS+2
|
||||
#define ES FS+2
|
||||
#define DS ES+2
|
||||
#define DI DS+2
|
||||
#define SI DI+4
|
||||
#define BP SI+4
|
||||
#define ST BP+4 /* Hole for another SP */
|
||||
#define BX ST+4
|
||||
#define DX BX+4
|
||||
#define CX DX+4
|
||||
#define AX CX+4
|
||||
#define RETADR AX+4
|
||||
#define PC RETADR+4
|
||||
#define CS PC+4
|
||||
#define PSW CS+4
|
||||
#define SP PSW+4
|
||||
#define SS SP+4
|
||||
|
||||
|
||||
/* MCF_MAGIC value from <mcontext.h> */
|
||||
#define MCF_MAGIC 0xc0ffee
|
||||
|
||||
/* Values from <sys/ucontext.h> */
|
||||
#define UCF_IGNFPU 0x002
|
||||
#define UCF_IGNSIGM 0x004
|
||||
|
||||
|
||||
/* EINVAL from errno.h */
|
||||
#define EFAULT 14
|
||||
#define EINVAL 22
|
||||
|
||||
/* int getcontext(ucontext_t *ucp)
|
||||
* Initialise the structure pointed to by ucp to the current user context
|
||||
* of the calling thread. */
|
||||
|
||||
|
||||
.text
|
||||
|
||||
.globl _getcontext
|
||||
.balign 16
|
||||
_getcontext:
|
||||
/* In case a process does not use the FPU and is neither interested in
|
||||
* saving its signal mask, then we can skip the context switch to
|
||||
* PM and kernel altogether and only save general-purpose registers. */
|
||||
|
||||
mov (%esp), %ecx /* Save return address:
|
||||
* When setcontext or swapcontext is called,
|
||||
* we jump to this address and continue
|
||||
* running. */
|
||||
|
||||
mov 4(%esp), %edx /* edx = ucp */
|
||||
/* Check null pointer */
|
||||
cmp $0, %edx /* edx == NULL? */
|
||||
jne 3f /* Not null, continue */
|
||||
movl $EFAULT, (_errno)
|
||||
xor %eax, %eax
|
||||
dec %eax /* return -1 */
|
||||
ret
|
||||
|
||||
3: /* Check flags */
|
||||
push %ecx /* save ecx */
|
||||
push %ebx /* save ebx */
|
||||
lea UC_FLAGS(%edx), %ebx /* ebx = &(ucp->uc_flags) */
|
||||
mov (%ebx), %ecx /* ecx = ucp->uc_flags */
|
||||
mov $UCF_IGNFPU, %eax
|
||||
or $UCF_IGNSIGM, %eax
|
||||
cmp %eax, %ecx /* is UCF_IGNFPU or UCF_IGNSIGM set? */
|
||||
pop %ebx /* restore ebx */
|
||||
pop %ecx /* restore ecx */
|
||||
jz 1f /* Both are set, skip getuctx */
|
||||
|
||||
0:
|
||||
push %ecx /* Save ecx */
|
||||
push %edx
|
||||
call _getuctx /* getuctx(ucp) */
|
||||
pop %edx /* clean up stack and restore edx */
|
||||
pop %ecx /* Restore ecx */
|
||||
|
||||
1:
|
||||
/* Save the context */
|
||||
mov 4(%esp), %edx /* edx = ucp */
|
||||
pop %eax /* retaddr */
|
||||
mov %eax, PC(%edx) /* Save real RTA in mcp struct */
|
||||
mov %esp, SP(%edx) /* Save stack pointer (now pointing to ucp) */
|
||||
/* Save GP registers */
|
||||
mov %ebp, BP(%edx) /* Save EBP */
|
||||
mov %esi, SI(%edx) /* Save ESI */
|
||||
mov %edi, DI(%edx) /* Save EDI */
|
||||
mov %ebx, BX(%edx) /* Save EBX */
|
||||
mov %ecx, CX(%edx) /* Save ECX */
|
||||
movl $MCF_MAGIC, MAGIC(%edx) /* Set magic value */
|
||||
push %eax /* Restore retaddr */
|
||||
|
||||
xor %eax, %eax /* Return 0 */
|
||||
|
||||
2:
|
||||
add $4, %esp /* Remove stale (setcontext) RTA */
|
||||
jmp *%ecx /* Restore return address */
|
||||
|
||||
|
||||
/* int setcontext(const ucontext_t *ucp)
|
||||
* Restore the user context pointed to by ucp. A successful call to
|
||||
* setcontext does not return; program execution resumes at the point
|
||||
* specified by the ucp argument. If ucp was created with getcontext(),
|
||||
* program execution continues as if the corresponding call of getcontext()
|
||||
* had just returned. If ucp was created with makecontext(), program
|
||||
* execution continues with the function passed to makecontext(). */
|
||||
|
||||
.text
|
||||
|
||||
.globl _setcontext
|
||||
.balign 16
|
||||
_setcontext:
|
||||
/* In case a process does not use the FPU and is neither interested in
|
||||
* restoring its signal mask, then we can skip the context switch to
|
||||
* PM and kernel altogether and restore state here. */
|
||||
|
||||
mov 4(%esp), %edx /* edx = ucp */
|
||||
|
||||
/* Check null pointer */
|
||||
cmp $0, %edx /* edx == NULL? */
|
||||
jnz 3f /* Not null, continue */
|
||||
movl $EFAULT, (_errno)
|
||||
xor %eax, %eax
|
||||
dec %eax /* return -1 */
|
||||
ret
|
||||
|
||||
3: /* Check flags */
|
||||
push %ebx /* save ebx */
|
||||
lea MAGIC(%edx), %ebx /* ebx = &(ucp->mc_context.mc_magic) */
|
||||
mov (%ebx), %ecx /* ecx = ucp->mc_context.mc_magic */
|
||||
pop %ebx /* restore ebx */
|
||||
cmp $MCF_MAGIC, %ecx /* is the magic value set (is context valid)?*/
|
||||
jz 4f /* is set, proceed */
|
||||
movl $EINVAL, (_errno) /* not set, return error code */
|
||||
xor %eax, %eax
|
||||
dec %eax /* return -1 */
|
||||
ret
|
||||
|
||||
|
||||
4: push %ebx /* save ebx */
|
||||
lea UC_FLAGS(%edx), %ebx /* ebx = &(ucp->uc_flags) */
|
||||
mov (%ebx), %ecx /* ecx = ucp->uc_flags */
|
||||
pop %ebx /* restore ebx */
|
||||
mov $UCF_IGNFPU, %eax
|
||||
or $UCF_IGNSIGM, %eax
|
||||
cmp %eax, %ecx /* Are UCF_IGNFPU and UCF_IGNSIGM flags set? */
|
||||
jz 1f /* Both are set, so don't bother restoring FPU
|
||||
* state and signal mask */
|
||||
|
||||
0: push %ecx /* Save ecx */
|
||||
push %edx
|
||||
call _setuctx /* setuctx(ucp) */
|
||||
pop %edx /* Clean up stack and restore edx */
|
||||
pop %ecx /* Restore ecx */
|
||||
|
||||
1: /* Restore the registers */
|
||||
mov 4(%esp), %edx /* edx = ucp */
|
||||
mov CX(%edx), %ecx /* Restore ECX */
|
||||
mov BX(%edx), %ebx /* Restore EBX */
|
||||
mov DI(%edx), %edi /* Restore EDI */
|
||||
mov SI(%edx), %esi /* Restore ESI */
|
||||
mov BP(%edx), %ebp /* Restore EBP */
|
||||
mov SP(%edx), %esp /* Restore stack pointer */
|
||||
|
||||
2:
|
||||
jmp *PC(%edx) /* Push RTA onto stack so we can return to it */
|
||||
|
||||
|
||||
/* void ctx_start((void *func)(int arg1, ..., argn), arg1, ..., argn,
|
||||
* ucontext_t *ucp)
|
||||
* A wrapper to start function `func'. ESI register will contain a pointer
|
||||
* to ucp on the stack. By setting ESP to ESI, we effectively 'remove' all
|
||||
* arguments to `func' from the stack. Finally, a call to resumecontext
|
||||
* will start the next context in the linked list (or exit the program if
|
||||
* there is no context). */
|
||||
|
||||
.text
|
||||
|
||||
.globl _ctx_start
|
||||
.balign 16
|
||||
_ctx_start:
|
||||
/* 0(esp) -> func
|
||||
* 4(esp) -> arg1
|
||||
* ...
|
||||
* 4*n(esp) -> argn
|
||||
* 4*(n+1)(esp) -> ucp */
|
||||
|
||||
pop %eax /* eax = func */
|
||||
call *%eax /* func(arg1, ..., argn) */
|
||||
mov %esi, %esp /* Clean up stack */
|
||||
/* ucp is now at the top of the stack again */
|
||||
call _resumecontext /* resumecontext(ucp) */
|
||||
ret /* never reached */
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ SRCS+= \
|
|||
_getsysinfo.c \
|
||||
_lseek64.c \
|
||||
_mapdriver.c \
|
||||
_mcontext.c \
|
||||
_mount.c \
|
||||
_reboot.c \
|
||||
_sbrk.c \
|
||||
|
|
26
lib/libc/other/_mcontext.c
Normal file
26
lib/libc/other/_mcontext.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* mcontext.c
|
||||
*/
|
||||
#include <lib.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
PUBLIC int setmcontext(const mcontext_t *mcp)
|
||||
{
|
||||
message m;
|
||||
|
||||
m.m1_p1 = (char *) mcp;
|
||||
|
||||
return(_syscall(MM, SETMCONTEXT, &m));
|
||||
}
|
||||
|
||||
|
||||
PUBLIC int getmcontext(mcontext_t *mcp)
|
||||
{
|
||||
message m;
|
||||
|
||||
m.m1_p1 = (char *) mcp;
|
||||
|
||||
return(_syscall(MM, GETMCONTEXT, &m));
|
||||
}
|
||||
|
|
@ -91,6 +91,7 @@ SRCS+= \
|
|||
_time.c \
|
||||
_times.c \
|
||||
_truncate.c \
|
||||
_ucontext.c \
|
||||
_umask.c \
|
||||
_uname.c \
|
||||
_unlink.c \
|
||||
|
|
197
lib/libc/posix/_ucontext.c
Normal file
197
lib/libc/posix/_ucontext.c
Normal file
|
@ -0,0 +1,197 @@
|
|||
#include <lib.h>
|
||||
#include <machine/stackframe.h>
|
||||
#include <ucontext.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
_PROTOTYPE( void ctx_start, (void (*)(void), int, ...) );
|
||||
|
||||
/*===========================================================================*
|
||||
* setuctx *
|
||||
*===========================================================================*/
|
||||
PUBLIC int setuctx(const ucontext_t *ucp)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (ucp == NULL) {
|
||||
errno = EFAULT;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (!(ucp->uc_flags & UCF_IGNSIGM)) {
|
||||
/* Set signal mask */
|
||||
if ((r = sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL)) == -1)
|
||||
return(r);
|
||||
}
|
||||
|
||||
if (!(ucp->uc_flags & UCF_IGNFPU)) {
|
||||
if ((r = setmcontext(&(ucp->uc_mcontext))) == -1)
|
||||
return(r);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* getuctx *
|
||||
*===========================================================================*/
|
||||
PUBLIC int getuctx(ucontext_t *ucp)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (ucp == NULL) {
|
||||
errno = EFAULT;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (!(ucp->uc_flags & UCF_IGNSIGM)) {
|
||||
/* Get signal mask */
|
||||
if ((r = sigprocmask(0, NULL, &ucp->uc_sigmask)) == -1)
|
||||
return(r);
|
||||
}
|
||||
|
||||
if (!(ucp->uc_flags & UCF_IGNFPU)) {
|
||||
if ((r = getmcontext(&(ucp->uc_mcontext))) != 0)
|
||||
return(r);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* makecontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
|
||||
{
|
||||
va_list ap;
|
||||
unsigned int *stack_top, *stack_guard;
|
||||
|
||||
/* There are a number of situations that are erroneous, but we can't actually
|
||||
tell the caller something is wrong, because this is a void function.
|
||||
Instead, mcontext_t contains a magic field that has to be set
|
||||
properly before it can be used. */
|
||||
if (ucp == NULL) {
|
||||
return;
|
||||
} else if ((ucp->uc_stack.ss_sp == NULL) ||
|
||||
(ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
|
||||
ucp->uc_mcontext.mc_magic = 0;
|
||||
ucp->uc_mcontext.mc_p_reg.sp = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ucp->uc_mcontext.mc_magic == MCF_MAGIC) {
|
||||
#if (_MINIX_CHIP == _CHIP_INTEL)
|
||||
/* The caller provides a pointer to a stack that we can use to run our
|
||||
context on. When the context starts, control is given to a wrapped
|
||||
start routine, which calls a function and cleans up the stack
|
||||
afterwards. The wrapper needs the address of that function on the
|
||||
stack.
|
||||
The stack will be prepared as follows:
|
||||
func() - start routine
|
||||
arg1 - first argument
|
||||
...
|
||||
argn - last argument
|
||||
ucp - context, esp points here when `func' returns
|
||||
_ctx_start pops the address of `func' from the stack and calls it.
|
||||
The stack will then be setup with all arguments for `func'. When
|
||||
`func' returns, _ctx_start cleans up the stack such that ucp is at
|
||||
the top of the stack, ready to be used by resumecontext.
|
||||
Resumecontext, in turn, checks whether another context is ready to
|
||||
be executed (i.e., uc_link != NULL) or exit(2)s the process. */
|
||||
|
||||
/* Find the top of the stack from which we grow downwards. */
|
||||
stack_top = (unsigned int *) ((uintptr_t ) ucp->uc_stack.ss_sp +
|
||||
ucp->uc_stack.ss_size);
|
||||
|
||||
/* Align the arguments to 16 bytes (we might lose a few bytes of stack
|
||||
space here).*/
|
||||
stack_top = (unsigned int *) ((uintptr_t) stack_top & ~0xf);
|
||||
|
||||
/* Make room for 'func', the `func' routine arguments, and ucp. */
|
||||
stack_top -= (1 + argc + 1);
|
||||
|
||||
/* Adjust the machine context to point to the top of this stack and the
|
||||
program counter to the context start wrapper. */
|
||||
ucp->uc_mcontext.mc_p_reg.fp = 0; /* Clear frame pointer */
|
||||
ucp->uc_mcontext.mc_p_reg.sp = (reg_t) stack_top;
|
||||
ucp->uc_mcontext.mc_p_reg.pc = (reg_t) ctx_start;
|
||||
|
||||
*stack_top++ = (uintptr_t) func;
|
||||
|
||||
/* Copy arguments to the stack. */
|
||||
va_start(ap, argc);
|
||||
while (argc-- > 0) {
|
||||
*stack_top++ = va_arg(ap, uintptr_t);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
/* Store ucp on the stack */
|
||||
*stack_top = (uintptr_t) ucp;
|
||||
|
||||
/* Set ESI to point to the base of the stack where ucp is stored, so
|
||||
that the wrapper function knows how to clean up the stack after
|
||||
calling `func' (i.e., how to adjust ESP). */
|
||||
ucp->uc_mcontext.mc_p_reg.si = (reg_t) stack_top;
|
||||
|
||||
|
||||
/* If we ran out of stack space, invalidate stack pointer. Eventually,
|
||||
swapcontext will choke on this and return ENOMEM. */
|
||||
if (stack_top == ucp->uc_stack.ss_sp)
|
||||
ucp->uc_mcontext.mc_p_reg.sp = 0;
|
||||
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* swapcontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
|
||||
{
|
||||
int r;
|
||||
unsigned int *stack_guard;
|
||||
|
||||
if ((oucp == NULL) || (ucp == NULL)) {
|
||||
errno = EFAULT;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (ucp->uc_mcontext.mc_p_reg.sp == 0) {
|
||||
/* No stack space. Bail out. */
|
||||
errno = ENOMEM;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
oucp->uc_flags &= ~UCF_SWAPPED;
|
||||
r = getcontext(oucp);
|
||||
if ((r == 0) && !(oucp->uc_flags & UCF_SWAPPED)) {
|
||||
oucp->uc_flags |= UCF_SWAPPED;
|
||||
r = setcontext(ucp);
|
||||
}
|
||||
|
||||
return(r);
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* resumecontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC void resumecontext(ucontext_t *ucp)
|
||||
{
|
||||
if (ucp->uc_link == NULL) exit(0);
|
||||
|
||||
/* Error handling? Where should the error go to? */
|
||||
(void) setcontext((const ucontext_t *) ucp->uc_link);
|
||||
|
||||
exit(1); /* Never reached */
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ SRCS= \
|
|||
sef_ping.c \
|
||||
sef_init.c \
|
||||
sys_abort.c \
|
||||
sys_mcontext.c \
|
||||
sys_cprof.c \
|
||||
sys_endsig.c \
|
||||
sys_eniop.c \
|
||||
|
|
32
lib/libsys/sys_mcontext.c
Normal file
32
lib/libsys/sys_mcontext.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include "syslib.h"
|
||||
|
||||
PUBLIC int sys_getmcontext(proc, mcp)
|
||||
endpoint_t proc; /* process retrieving context */
|
||||
mcontext_t *mcp; /* where to store context */
|
||||
{
|
||||
/* A process wants to store its context in mcp. */
|
||||
|
||||
message m;
|
||||
int r;
|
||||
|
||||
m.PR_ENDPT = proc;
|
||||
m.PR_CTX_PTR = (char *) mcp;
|
||||
r = _kernel_call(SYS_GETMCONTEXT, &m);
|
||||
return r;
|
||||
}
|
||||
|
||||
PUBLIC int sys_setmcontext(proc, mcp)
|
||||
endpoint_t proc; /* process setting context */
|
||||
mcontext_t *mcp; /* where to get context from */
|
||||
{
|
||||
/* A process wants to restore context stored in ucp. */
|
||||
|
||||
message m;
|
||||
int r;
|
||||
|
||||
m.PR_ENDPT = proc;
|
||||
m.PR_CTX_PTR = (char *) mcp;
|
||||
r = _kernel_call(SYS_SETMCONTEXT, &m);
|
||||
return r;
|
||||
}
|
||||
|
96
man/man3/getcontext.3
Normal file
96
man/man3/getcontext.3
Normal file
|
@ -0,0 +1,96 @@
|
|||
.TH GETCONTEXT 3 "Mar 2, 2010"
|
||||
.SH NAME
|
||||
getcontext, setcontext \- get and set current user context
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.ft B
|
||||
#include <ucontext.h>
|
||||
|
||||
int getcontext(ucontext\_t *\fIucp\fP)
|
||||
int setcontext(const ucontext\_t *\fIoucp\fP)
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.BR makecontext (3)
|
||||
,
|
||||
.BR swapcontext (3)
|
||||
,
|
||||
.BR getcontext (3)
|
||||
, and
|
||||
.BR setcontext (3)
|
||||
together form a set of functions that allow user-level context switching between multiple threads of control within a process.
|
||||
.PP
|
||||
The \fIucontext_t\fP type is a structure that has at least the following members:
|
||||
.in +4
|
||||
.nf
|
||||
|
||||
typedef struct __ucontext {
|
||||
ucontext_t *uc_link;
|
||||
sigset_t uc_sigmask;
|
||||
stack_t uc_stack;
|
||||
mcontext_t uc_mcontext;
|
||||
...
|
||||
} ucontext_t;
|
||||
|
||||
.fi
|
||||
.in
|
||||
with \fIsigset_t\fP and \fIstack_t\fP defined in
|
||||
.IR <signal.h> .
|
||||
Here \fIuc_link\fP points to the context that will be resumed when the current context returns (if \fIuc_link\fP is NULL, the process exits), \fIsigset_t\fP is the set of signals blocks in this context, \fIuc_stack\fP is the stack used by this context (when the context was modified by
|
||||
.BR makecontext (3)),
|
||||
and \fIuc_mcontext\fP is the machine-specific representation of the saved context. The \fImcontext_t\fP type is machine-dependent and opaque.
|
||||
.PP
|
||||
MINIX 3 has an additional \fIuc_flags\fP member that supports the following flags:
|
||||
.PP
|
||||
.in +2
|
||||
.nf
|
||||
UCF_IGNSIGM /* Current signal mask is not stored or restored */
|
||||
UCF_IGNFPU /* FPU state is not stored or restored for this context */
|
||||
.fi
|
||||
.in
|
||||
.PP
|
||||
Not storing and restoring the signal mask and/or FPU state speeds up context switching considerably.
|
||||
.PP
|
||||
|
||||
The
|
||||
.BR getcontext ()
|
||||
function initializes the structure pointed to by \fIucp\fP to the current user context of the calling thread.
|
||||
.PP
|
||||
The
|
||||
.BR setcontext ()
|
||||
function restores the user context pointed to by \fIucp\fP. A succesful call does not return; program execution resumes at the point specified by the \fIucp\fP argument passed to
|
||||
.BR setcontext ().
|
||||
The \fIucp\fP argument should be created either by a prior call to
|
||||
.BR getcontext ()
|
||||
or
|
||||
.BR makecontext ().
|
||||
If the \fIucp\fP argument was created with
|
||||
.BR getcontext (),
|
||||
program execution continues as if the corresponding call of
|
||||
.BR getcontext ()
|
||||
had just returned. If the \fIucp\fP argument was created with
|
||||
.BR makecontext (),
|
||||
program execution continues with the function passed to
|
||||
.BR makecontext ().
|
||||
|
||||
.SH "RETURN VALUE"
|
||||
When successful,
|
||||
.BR getcontext ()
|
||||
returns 0 and
|
||||
.BR setcontext ()
|
||||
does not return. Otherwise, both return -1 and
|
||||
.I errno
|
||||
is set to indicate the error.
|
||||
|
||||
.SH "ERRORS"
|
||||
.TP 15
|
||||
[EINVAL]
|
||||
The context is not properly initialized.
|
||||
.TP 15
|
||||
[EFAULT]
|
||||
\fIucp\fP is a NULL pointer.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.BR makecontext (3).
|
||||
|
||||
.SH "AUTHORS"
|
||||
Thomas Veerman
|
75
man/man3/makecontext.3
Normal file
75
man/man3/makecontext.3
Normal file
|
@ -0,0 +1,75 @@
|
|||
.TH MAKECONTEXT 3 "Mar 2, 2010"
|
||||
.SH NAME
|
||||
makecontext, swapcontext \- manipulate user contexts
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.ft B
|
||||
#include <ucontext.h>
|
||||
|
||||
void makecontext(ucontext\_t *\fIucp\fP, void \fI(*func)(void)\fP, int \fIargc\fP, ...)
|
||||
int swapcontext(ucontext\_t *\fIoucp\fP, const ucontext\_t *\fIucp\fP)
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.BR makecontext (3)
|
||||
,
|
||||
.BR swapcontext (3)
|
||||
,
|
||||
.BR getcontext (3)
|
||||
, and
|
||||
.BR setcontext (3)
|
||||
together form a set of functions that allow user-level context switching between multiple threads of control within a process.
|
||||
.PP
|
||||
The
|
||||
.BR makecontext ()
|
||||
function modifies the user thread pointed to by
|
||||
.I ucp
|
||||
to continue execution by invoking function
|
||||
.I func
|
||||
and passing that function a number of
|
||||
.I argc
|
||||
integer arguments. The value of
|
||||
.I argc
|
||||
must match the number of integer arguments passed to
|
||||
.I func
|
||||
, otherwise the behavior is undefined. Context
|
||||
.I ucp
|
||||
must have been initialized by a call to
|
||||
.BR getcontext (3)
|
||||
and have a stack allocated for it. The address of the stack must be assigned to \fIucp\->uc_stack.ss_size\fP and the size of the stack to \fIucp\->uc_stack.ss_size\fP. The \fIucp\->uc_link\fP member is used to determine which successor context is run after the context modified by
|
||||
.BR makecontext ()
|
||||
returns. If left NULL, the process exits.
|
||||
.PP
|
||||
The
|
||||
.BR swapcontext ()
|
||||
function saves the current context in the context structure pointed to by
|
||||
.I oucp
|
||||
and sets the context to the context structure pointed to by \fIucp\fP.
|
||||
|
||||
.SH "RETURN VALUE"
|
||||
When successful,
|
||||
.BR swapcontext ()
|
||||
returns 0. Otherwise, -1 is returned and
|
||||
.I errno
|
||||
is set to indicate the error. Note that a succesful call to
|
||||
.BR swapcontext ()
|
||||
actually does not return. Only after returning to the context that called
|
||||
.BR swapcontext ()
|
||||
, it appears as if
|
||||
.BR swapcontext ()
|
||||
returned 0.
|
||||
|
||||
.SH "ERRORS"
|
||||
.TP 15
|
||||
[EFAULT]
|
||||
Either the \fIucp\fP or \fIoucp\fP is a NULL pointer.
|
||||
.TP 15
|
||||
[EINVAL]
|
||||
The context is not properly initialized.
|
||||
.TP 15
|
||||
[ENOMEM]
|
||||
The \fIucp\fP argument does not have enough stack left to complete the operation.
|
||||
.SH "SEE ALSO"
|
||||
.BR getcontext (3).
|
||||
|
||||
.SH "AUTHORS"
|
||||
Thomas Veerman
|
|
@ -18,7 +18,7 @@ LDFLAGS = -i
|
|||
|
||||
OBJ = main.o forkexit.o break.o exec.o time.o timers.o alarm.o \
|
||||
signal.o utility.o table.o trace.o getset.o misc.o \
|
||||
profile.o dma.o
|
||||
profile.o dma.o mcontext.o
|
||||
|
||||
# build local binary
|
||||
all build: $(SERVER)
|
||||
|
|
26
servers/pm/mcontext.c
Normal file
26
servers/pm/mcontext.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "pm.h"
|
||||
#include <minix/callnr.h>
|
||||
#include <minix/endpoint.h>
|
||||
#include <minix/com.h>
|
||||
#include <minix/vm.h>
|
||||
#include "mproc.h"
|
||||
#include "param.h"
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* do_setmcontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC int do_setmcontext()
|
||||
{
|
||||
return sys_setmcontext(who_e, (mcontext_t *) m_in.m1_p1);
|
||||
}
|
||||
|
||||
|
||||
/*===========================================================================*
|
||||
* do_getmcontext *
|
||||
*===========================================================================*/
|
||||
PUBLIC int do_getmcontext()
|
||||
{
|
||||
return sys_getmcontext(who_e, (mcontext_t *) m_in.m1_p1);
|
||||
}
|
||||
|
|
@ -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) );
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -4,19 +4,21 @@ CC = exec cc
|
|||
GCC = /usr/gnu/bin/gcc
|
||||
CFLAGS= -O -D_MINIX -D_POSIX_SOURCE
|
||||
CFLAGS-GCC= $(CFLAGS) -Wall
|
||||
CFLAGS-GCCFPU= $(CFLAGS-GCC) -W -mhard-float
|
||||
|
||||
OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
|
||||
test10 test12 test13 test14 test15 test16 test17 test18 test19 \
|
||||
test21 test22 test23 test25 test26 test27 test28 test29 \
|
||||
test30 test31 test32 test34 test35 test36 test37 test38 \
|
||||
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
|
||||
test42 test44 test45 test47 test48 test49 test50
|
||||
test42 test44 test45 test47 test48 test49 test50 test51
|
||||
|
||||
BIGOBJ= test20 test24
|
||||
ROOTOBJ= test11 test33 test43 test46
|
||||
GCCOBJ= test45-gcc test49-gcc
|
||||
GCCFPUOBJ= test51-gcc
|
||||
|
||||
all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(ROOTOBJ)
|
||||
all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(GCCFPUOBJ) $(ROOTOBJ)
|
||||
chmod 755 *.sh run
|
||||
|
||||
$(OBJ):
|
||||
|
@ -28,6 +30,9 @@ $(BIGOBJ):
|
|||
$(GCCOBJ):
|
||||
[ ! -x $(GCC) ] || $(GCC) $(CFLAGS-GCC) -o $@ ${@:S/-gcc//}.c
|
||||
|
||||
$(GCCFPUOBJ):
|
||||
[ ! -x $(GCC) ] || $(GCC) $(CFLAGS-GCCFPU) -o $@ ${@:S/-gcc//}.c
|
||||
|
||||
$(ROOTOBJ):
|
||||
$(CC) $(CFLAGS) $@.c
|
||||
@install -c -o root -m 4755 a.out $@
|
||||
|
@ -99,4 +104,6 @@ test48: test48.c
|
|||
test49: test49.c
|
||||
test49-gcc: test49.c
|
||||
test50: test50.c
|
||||
test51: test51.c
|
||||
test51-gcc: test51.c
|
||||
|
||||
|
|
4
test/run
4
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
|
||||
|
|
321
test/test51.c
Normal file
321
test/test51.c
Normal file
|
@ -0,0 +1,321 @@
|
|||
/* Test51.c
|
||||
*
|
||||
* Test getcontext, setcontext, makecontext, and swapcontext system calls.
|
||||
*
|
||||
* Part of this test is somewhat based on the GNU GCC ucontext test set.
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ucontext.h>
|
||||
#include <math.h>
|
||||
#include <fenv.h>
|
||||
|
||||
_PROTOTYPE( void do_calcs, (void) );
|
||||
_PROTOTYPE( void do_child, (void) );
|
||||
_PROTOTYPE( void do_parent, (void) );
|
||||
_PROTOTYPE( void err, (int subtest, int error_no) );
|
||||
_PROTOTYPE( void func1, (int a, int b, int c, int d, int e, int f, int g,
|
||||
int h, int i, int j, int k, int l, int m, int n,
|
||||
int o, int p, int q, int r, int s, int t, int u,
|
||||
int v, int w, int x, int y, int z, int aa, int bb));
|
||||
_PROTOTYPE( void func2, (void) );
|
||||
_PROTOTYPE( void just_exit, (void) );
|
||||
_PROTOTYPE( void test_brk, (void) );
|
||||
_PROTOTYPE( void verify_main_reenter, (void) );
|
||||
|
||||
#define ERROR_MAX 5
|
||||
#define SSIZE 32768
|
||||
#define ROUNDS 10
|
||||
#define SWAPS 10
|
||||
|
||||
int errct = 0;
|
||||
ucontext_t ctx[3];
|
||||
int entered_func1, entered_func2, reentered_main, entered_overflow;
|
||||
|
||||
static char st_stack[SSIZE];
|
||||
static volatile int shift, global;
|
||||
|
||||
void do_calcs(void)
|
||||
{
|
||||
float a, b, c, d, e;
|
||||
float foo, bar;
|
||||
int i;
|
||||
|
||||
a = 1.1;
|
||||
b = 2.2;
|
||||
c = 3.3;
|
||||
d = 4.4;
|
||||
e = 5.5;
|
||||
|
||||
foo = a * b; /* 2.42 */
|
||||
bar = c * d; /* 14.52 */
|
||||
|
||||
i = 0;
|
||||
while(i < ROUNDS) {
|
||||
foo += c; /* 5.72 */
|
||||
foo *= d; /* 25.168 */
|
||||
foo /= e; /* 4.5760 */
|
||||
bar -= a; /* 13.42 */
|
||||
bar *= b; /* 29.524 */
|
||||
bar /= e; /* 5.3680 */
|
||||
|
||||
/* Undo */
|
||||
foo *= e;
|
||||
foo /= d;
|
||||
foo -= c;
|
||||
|
||||
bar *= e;
|
||||
bar /= b;
|
||||
bar += a;
|
||||
|
||||
i++;
|
||||
}
|
||||
if(fabs(foo - (a * b)) > 0.0001) err(8, 1);
|
||||
if(fabs(bar - (c * d)) > 0.0001) err(8, 2);
|
||||
}
|
||||
|
||||
void do_child(void)
|
||||
{
|
||||
int s;
|
||||
s = 1;
|
||||
|
||||
/* Initialize FPU context and verify it's set to round to nearest. */
|
||||
if (fegetround() != FE_TONEAREST) err(9, 1);
|
||||
|
||||
/* Now we change the rounding to something else, and this should be preserved
|
||||
between context swaps. */
|
||||
if (fesetround(FE_DOWNWARD) != 0) err(9, 2);
|
||||
|
||||
while(s < SWAPS) {
|
||||
s++;
|
||||
if (swapcontext(&ctx[2], &ctx[1]) == -1) err(9, 2);
|
||||
do_calcs();
|
||||
if (fegetround() != FE_DOWNWARD) err(9, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void do_parent(void)
|
||||
{
|
||||
int s;
|
||||
s = 1;
|
||||
|
||||
/* Initialize FPU context and verify it's set to round to nearest. */
|
||||
if (fegetround() != FE_TONEAREST) err(10, 1);
|
||||
|
||||
/* Now we change the rounding to something else, and this should be preserved
|
||||
between context swaps. */
|
||||
if (fesetround(FE_UPWARD) != 0) err(10, 2);
|
||||
|
||||
while(s < SWAPS) {
|
||||
do_calcs();
|
||||
if (fegetround() != FE_UPWARD) err(10, 3);
|
||||
s++;
|
||||
if (swapcontext(&ctx[1], &ctx[2]) == -1) err(10, 4);
|
||||
}
|
||||
/* Returning to main thread through uc_link */
|
||||
}
|
||||
|
||||
void fail(void)
|
||||
{
|
||||
/* Shouldn't get here */
|
||||
err(5, 1);
|
||||
}
|
||||
|
||||
void func1(int a, int b, int c, int d, int e, int f, int g,
|
||||
int h, int i, int j, int k, int l, int m, int n,
|
||||
int o, int p, int q, int r, int s, int t, int u,
|
||||
int v, int w, int x, int y, int z, int aa, int bb)
|
||||
{
|
||||
if ( a != (0x0000001 << shift) || b != (0x0000004 << shift) ||
|
||||
c != (0x0000010 << shift) || d != (0x0000040 << shift) ||
|
||||
e != (0x0000100 << shift) || f != (0x0000400 << shift) ||
|
||||
g != (0x0001000 << shift) || h != (0x0004000 << shift) ||
|
||||
i != (0x0010000 << shift) || j != (0x0040000 << shift) ||
|
||||
k != (0x0100000 << shift) || l != (0x0400000 << shift) ||
|
||||
m != (0x1000000 << shift) || n != (0x4000000 << shift) ||
|
||||
o != (0x0000002 << shift) || p != (0x0000008 << shift) ||
|
||||
q != (0x0000020 << shift) || r != (0x0000080 << shift) ||
|
||||
s != (0x0000200 << shift) || t != (0x0000800 << shift) ||
|
||||
u != (0x0002000 << shift) || v != (0x0008000 << shift) ||
|
||||
w != (0x0020000 << shift) || x != (0x0080000 << shift) ||
|
||||
y != (0x0200000 << shift) || z != (0x0800000 << shift) ||
|
||||
aa != (0x2000000 << shift) || bb != (0x8000000 << shift) ) {
|
||||
err(2, 1);
|
||||
}
|
||||
|
||||
if (shift && swapcontext (&ctx[1], &ctx[2]) != 0) err(2, 2);
|
||||
shift++;
|
||||
entered_func1++;
|
||||
}
|
||||
|
||||
void func2(void)
|
||||
{
|
||||
if (swapcontext(&ctx[2], &ctx[1]) != 0) err(3, 1);
|
||||
entered_func2++;
|
||||
}
|
||||
|
||||
void just_exit(void)
|
||||
{
|
||||
if (errct == 0) printf("ok\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
void test_brk(void)
|
||||
{
|
||||
char *big_stack;
|
||||
|
||||
big_stack = malloc(16 * 1024 * 1024); /* 16 MB */
|
||||
/* If this fails, it is likely brk system call failed due stack/data segments
|
||||
collision detection. Unless the system really is low on memory, this is an
|
||||
error. */
|
||||
if (big_stack == NULL) err(7, 1);
|
||||
}
|
||||
|
||||
void verify_main_reenter(void)
|
||||
{
|
||||
if (reentered_main == 0) err(4, 1);
|
||||
if (errct == 0) printf("ok\n");
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
printf("Test 51 (GCC) ");
|
||||
#else
|
||||
printf("Test 51 (ACK) ");
|
||||
#endif
|
||||
fflush(stdout);
|
||||
|
||||
atexit(verify_main_reenter);
|
||||
|
||||
/* Save current context in ctx[0] */
|
||||
if (getcontext(&ctx[0]) != 0) {
|
||||
/* Don't verify reentering main, not going to happen */
|
||||
atexit(just_exit);
|
||||
err(1, 1);
|
||||
}
|
||||
|
||||
ctx[1] = ctx[0];
|
||||
ctx[1].uc_stack.ss_sp = st_stack;
|
||||
ctx[1].uc_stack.ss_size = SSIZE;
|
||||
ctx[1].uc_link = &ctx[0]; /* When done running, return here */
|
||||
|
||||
/* ctx[1] is going to run func1 and then return here (uc_link). */
|
||||
/* We'll see later on whether makecontext worked. */
|
||||
makecontext(&ctx[1], (void (*) (void)) func1, 28,
|
||||
(0x0000001 << shift), (0x0000004 << shift),
|
||||
(0x0000010 << shift), (0x0000040 << shift),
|
||||
(0x0000100 << shift), (0x0000400 << shift),
|
||||
(0x0001000 << shift), (0x0004000 << shift),
|
||||
(0x0010000 << shift), (0x0040000 << shift),
|
||||
(0x0100000 << shift), (0x0400000 << shift),
|
||||
(0x1000000 << shift), (0x4000000 << shift),
|
||||
(0x0000002 << shift), (0x0000008 << shift),
|
||||
(0x0000020 << shift), (0x0000080 << shift),
|
||||
(0x0000200 << shift), (0x0000800 << shift),
|
||||
(0x0002000 << shift), (0x0008000 << shift),
|
||||
(0x0020000 << shift), (0x0080000 << shift),
|
||||
(0x0200000 << shift), (0x0800000 << shift),
|
||||
(0x2000000 << shift), (0x8000000 << shift));
|
||||
|
||||
if (++global == 1) {
|
||||
/* First time we're here. Let's run ctx[1] and return to ctx[0] when
|
||||
* we're done. Note that we return to above the 'makecontext' call. */
|
||||
if (setcontext(&ctx[1]) != 0) err(1, 2);
|
||||
}
|
||||
if (global != 2) {
|
||||
/* When ++global was 1 we let ctx[1] run and returned to ctx[0], so
|
||||
above ++global is executed again and should've become 2. */
|
||||
err(1, 3);
|
||||
}
|
||||
|
||||
/* Setup ctx[2] to run func2 */
|
||||
if (getcontext(&ctx[2]) != 0) err(1, 4);
|
||||
ctx[2].uc_stack.ss_sp = malloc(SSIZE);
|
||||
ctx[2].uc_stack.ss_size = SSIZE;
|
||||
ctx[2].uc_link = &ctx[1];
|
||||
makecontext(&ctx[2], (void (*) (void)) func2, 0);
|
||||
|
||||
/* Now things become tricky. ctx[2] is set up such that when it finishes
|
||||
running, and starts ctx[1] again. However, func1 swaps back to func2. Then,
|
||||
when func2 has finished running, we continue with ctx[1] and, finally, we
|
||||
return to ctx[0]. */
|
||||
if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 5); /* makecontext failed? */
|
||||
reentered_main = 1;
|
||||
|
||||
/* The call graph is as follows:
|
||||
*
|
||||
* ########
|
||||
* /--------># main #
|
||||
* 7 /----########----\
|
||||
* | | ^ |
|
||||
* | 1 2 3
|
||||
* | V | V
|
||||
* #########----/ #########
|
||||
* # func1 #<-------4-------# func2 #
|
||||
* #########--------5------>#########
|
||||
* ^ |
|
||||
* | |
|
||||
* \---------6--------/
|
||||
*
|
||||
* Main calls func1, func1 increases entered_func1, and returns to main. Main
|
||||
* calls func2, swaps to func1, swaps to func2, which increases entered_func2,
|
||||
* continues with func1, which increases entered_func1 again, continues to
|
||||
* main, where reentered_main is set to 1. In effect, entered_func1 == 2,
|
||||
* entered_func2 == 1, reentered_main == 1. Verify that. */
|
||||
|
||||
if (entered_func1 != 2) err(1, 6);
|
||||
if (entered_func2 != 1) err(1, 7);
|
||||
/* reentered_main == 1 is verified upon exit */
|
||||
|
||||
/* Try to allocate too small a stack */
|
||||
free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
|
||||
if (getcontext(&ctx[2]) != 0) err(1, 8);
|
||||
ctx[2].uc_stack.ss_sp = malloc(MINSIGSTKSZ-1);
|
||||
ctx[2].uc_stack.ss_size = MINSIGSTKSZ-1;
|
||||
ctx[2].uc_link = &ctx[0];
|
||||
makecontext(&ctx[2], (void (*) (void)) fail, 0);
|
||||
/* Because makecontext is void, we can only detect an error by trying to use
|
||||
the invalid context */
|
||||
if (swapcontext(&ctx[0], &ctx[2]) == 0) err(1, 9);
|
||||
|
||||
/* Try to allocate a huge stack to force the usage of brk/sbrk system call
|
||||
to enlarge the data segment. Because we are fiddling with the stack
|
||||
pointer, the OS might think the stack segment and data segment have
|
||||
collided and kill us. This is wrong and therefore the following should
|
||||
work. */
|
||||
free(ctx[2].uc_stack.ss_sp); /* Deallocate stack space first */
|
||||
if (getcontext(&ctx[2]) != 0) err(1, 14);
|
||||
ctx[2].uc_stack.ss_sp = malloc(8 * 1024 * 1024); /* 8 MB */
|
||||
ctx[2].uc_stack.ss_size = 8 * 1024 * 1024;
|
||||
ctx[2].uc_link = &ctx[0];
|
||||
makecontext(&ctx[2], (void (*) (void)) test_brk, 0);
|
||||
if (swapcontext(&ctx[0], &ctx[2]) != 0) err(1, 15);
|
||||
|
||||
ctx[1].uc_link = &ctx[0];
|
||||
ctx[2].uc_link = NULL;
|
||||
makecontext(&ctx[1], (void (*) (void)) do_parent, 0);
|
||||
makecontext(&ctx[2], (void (*) (void)) do_child, 0);
|
||||
if (swapcontext(&ctx[0], &ctx[2]) == -1) err(1, 16);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* We can't use a global subtest variable reliably, because threads might
|
||||
change the value when we reenter a thread (i.e., report wrong subtest
|
||||
number). */
|
||||
void err(int subtest, int error_no)
|
||||
{
|
||||
printf("Subtest %d, error %d\n", subtest, error_no);
|
||||
|
||||
if (errct++ > ERROR_MAX) {
|
||||
printf("Too many errors, test aborted\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue