- 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:
@@ -20,4 +20,6 @@ SRCS+= \
|
||||
io_outsl.S \
|
||||
io_outsw.S \
|
||||
io_outw.S \
|
||||
oneC_sum.S
|
||||
oneC_sum.S \
|
||||
ucontext.S
|
||||
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
|
||||
#ifdef __ACK__
|
||||
.text
|
||||
begtext:
|
||||
#ifdef __ACK__
|
||||
.rom
|
||||
#else
|
||||
.data
|
||||
#endif
|
||||
begrom:
|
||||
.data
|
||||
begdata:
|
||||
.bss
|
||||
begbss:
|
||||
#endif
|
||||
|
||||
.extern _getuctx
|
||||
.extern _setuctx
|
||||
.extern _resumecontext
|
||||
|
||||
/* Offsets into ucontext_t structure. Keep in sync with <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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user