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

swapcontext, and makecontext).
- Fix VM to not erroneously think the stack segment and data segment have
  collided when a user-space thread invokes brk().
- Add test51 to test ucontext functionality.
- Add man pages for ucontext system calls.
This commit is contained in:
Thomas Veerman
2010-03-12 15:58:41 +00:00
parent 3efbb8f133
commit bef0e3eb63
33 changed files with 1232 additions and 19 deletions
+3 -1
View File
@@ -20,4 +20,6 @@ SRCS+= \
io_outsl.S \
io_outsw.S \
io_outw.S \
oneC_sum.S
oneC_sum.S \
ucontext.S
+222
View File
@@ -0,0 +1,222 @@
#ifdef __ACK__
.text
begtext:
#ifdef __ACK__
.rom
#else
.data
#endif
begrom:
.data
begdata:
.bss
begbss:
#endif
.extern _getuctx
.extern _setuctx
.extern _resumecontext
/* Offsets into ucontext_t structure. Keep in sync with <sys/ucontext.h>! */
#define UC_FLAGS 0
#define UC_LINK UC_FLAGS + 4
#define MCTX UC_LINK + 4
#define MAGIC MCTX
#define GS MAGIC+4
#define FS GS+2
#define ES FS+2
#define DS ES+2
#define DI DS+2
#define SI DI+4
#define BP SI+4
#define ST BP+4 /* Hole for another SP */
#define BX ST+4
#define DX BX+4
#define CX DX+4
#define AX CX+4
#define RETADR AX+4
#define PC RETADR+4
#define CS PC+4
#define PSW CS+4
#define SP PSW+4
#define SS SP+4
/* MCF_MAGIC value from <mcontext.h> */
#define MCF_MAGIC 0xc0ffee
/* Values from <sys/ucontext.h> */
#define UCF_IGNFPU 0x002
#define UCF_IGNSIGM 0x004
/* EINVAL from errno.h */
#define EFAULT 14
#define EINVAL 22
/* int getcontext(ucontext_t *ucp)
* Initialise the structure pointed to by ucp to the current user context
* of the calling thread. */
.text
.globl _getcontext
.balign 16
_getcontext:
/* In case a process does not use the FPU and is neither interested in
* saving its signal mask, then we can skip the context switch to
* PM and kernel altogether and only save general-purpose registers. */
mov (%esp), %ecx /* Save return address:
* When setcontext or swapcontext is called,
* we jump to this address and continue
* running. */
mov 4(%esp), %edx /* edx = ucp */
/* Check null pointer */
cmp $0, %edx /* edx == NULL? */
jne 3f /* Not null, continue */
movl $EFAULT, (_errno)
xor %eax, %eax
dec %eax /* return -1 */
ret
3: /* Check flags */
push %ecx /* save ecx */
push %ebx /* save ebx */
lea UC_FLAGS(%edx), %ebx /* ebx = &(ucp->uc_flags) */
mov (%ebx), %ecx /* ecx = ucp->uc_flags */
mov $UCF_IGNFPU, %eax
or $UCF_IGNSIGM, %eax
cmp %eax, %ecx /* is UCF_IGNFPU or UCF_IGNSIGM set? */
pop %ebx /* restore ebx */
pop %ecx /* restore ecx */
jz 1f /* Both are set, skip getuctx */
0:
push %ecx /* Save ecx */
push %edx
call _getuctx /* getuctx(ucp) */
pop %edx /* clean up stack and restore edx */
pop %ecx /* Restore ecx */
1:
/* Save the context */
mov 4(%esp), %edx /* edx = ucp */
pop %eax /* retaddr */
mov %eax, PC(%edx) /* Save real RTA in mcp struct */
mov %esp, SP(%edx) /* Save stack pointer (now pointing to ucp) */
/* Save GP registers */
mov %ebp, BP(%edx) /* Save EBP */
mov %esi, SI(%edx) /* Save ESI */
mov %edi, DI(%edx) /* Save EDI */
mov %ebx, BX(%edx) /* Save EBX */
mov %ecx, CX(%edx) /* Save ECX */
movl $MCF_MAGIC, MAGIC(%edx) /* Set magic value */
push %eax /* Restore retaddr */
xor %eax, %eax /* Return 0 */
2:
add $4, %esp /* Remove stale (setcontext) RTA */
jmp *%ecx /* Restore return address */
/* int setcontext(const ucontext_t *ucp)
* Restore the user context pointed to by ucp. A successful call to
* setcontext does not return; program execution resumes at the point
* specified by the ucp argument. If ucp was created with getcontext(),
* program execution continues as if the corresponding call of getcontext()
* had just returned. If ucp was created with makecontext(), program
* execution continues with the function passed to makecontext(). */
.text
.globl _setcontext
.balign 16
_setcontext:
/* In case a process does not use the FPU and is neither interested in
* restoring its signal mask, then we can skip the context switch to
* PM and kernel altogether and restore state here. */
mov 4(%esp), %edx /* edx = ucp */
/* Check null pointer */
cmp $0, %edx /* edx == NULL? */
jnz 3f /* Not null, continue */
movl $EFAULT, (_errno)
xor %eax, %eax
dec %eax /* return -1 */
ret
3: /* Check flags */
push %ebx /* save ebx */
lea MAGIC(%edx), %ebx /* ebx = &(ucp->mc_context.mc_magic) */
mov (%ebx), %ecx /* ecx = ucp->mc_context.mc_magic */
pop %ebx /* restore ebx */
cmp $MCF_MAGIC, %ecx /* is the magic value set (is context valid)?*/
jz 4f /* is set, proceed */
movl $EINVAL, (_errno) /* not set, return error code */
xor %eax, %eax
dec %eax /* return -1 */
ret
4: push %ebx /* save ebx */
lea UC_FLAGS(%edx), %ebx /* ebx = &(ucp->uc_flags) */
mov (%ebx), %ecx /* ecx = ucp->uc_flags */
pop %ebx /* restore ebx */
mov $UCF_IGNFPU, %eax
or $UCF_IGNSIGM, %eax
cmp %eax, %ecx /* Are UCF_IGNFPU and UCF_IGNSIGM flags set? */
jz 1f /* Both are set, so don't bother restoring FPU
* state and signal mask */
0: push %ecx /* Save ecx */
push %edx
call _setuctx /* setuctx(ucp) */
pop %edx /* Clean up stack and restore edx */
pop %ecx /* Restore ecx */
1: /* Restore the registers */
mov 4(%esp), %edx /* edx = ucp */
mov CX(%edx), %ecx /* Restore ECX */
mov BX(%edx), %ebx /* Restore EBX */
mov DI(%edx), %edi /* Restore EDI */
mov SI(%edx), %esi /* Restore ESI */
mov BP(%edx), %ebp /* Restore EBP */
mov SP(%edx), %esp /* Restore stack pointer */
2:
jmp *PC(%edx) /* Push RTA onto stack so we can return to it */
/* void ctx_start((void *func)(int arg1, ..., argn), arg1, ..., argn,
* ucontext_t *ucp)
* A wrapper to start function `func'. ESI register will contain a pointer
* to ucp on the stack. By setting ESP to ESI, we effectively 'remove' all
* arguments to `func' from the stack. Finally, a call to resumecontext
* will start the next context in the linked list (or exit the program if
* there is no context). */
.text
.globl _ctx_start
.balign 16
_ctx_start:
/* 0(esp) -> func
* 4(esp) -> arg1
* ...
* 4*n(esp) -> argn
* 4*(n+1)(esp) -> ucp */
pop %eax /* eax = func */
call *%eax /* func(arg1, ..., argn) */
mov %esi, %esp /* Clean up stack */
/* ucp is now at the top of the stack again */
call _resumecontext /* resumecontext(ucp) */
ret /* never reached */
+1
View File
@@ -22,6 +22,7 @@ SRCS+= \
_getsysinfo.c \
_lseek64.c \
_mapdriver.c \
_mcontext.c \
_mount.c \
_reboot.c \
_sbrk.c \
+26
View File
@@ -0,0 +1,26 @@
/*
* mcontext.c
*/
#include <lib.h>
#include <ucontext.h>
#include <unistd.h>
PUBLIC int setmcontext(const mcontext_t *mcp)
{
message m;
m.m1_p1 = (char *) mcp;
return(_syscall(MM, SETMCONTEXT, &m));
}
PUBLIC int getmcontext(mcontext_t *mcp)
{
message m;
m.m1_p1 = (char *) mcp;
return(_syscall(MM, GETMCONTEXT, &m));
}
+1
View File
@@ -91,6 +91,7 @@ SRCS+= \
_time.c \
_times.c \
_truncate.c \
_ucontext.c \
_umask.c \
_uname.c \
_unlink.c \
+197
View File
@@ -0,0 +1,197 @@
#include <lib.h>
#include <machine/stackframe.h>
#include <ucontext.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
_PROTOTYPE( void ctx_start, (void (*)(void), int, ...) );
/*===========================================================================*
* setuctx *
*===========================================================================*/
PUBLIC int setuctx(const ucontext_t *ucp)
{
int r;
if (ucp == NULL) {
errno = EFAULT;
return(-1);
}
if (!(ucp->uc_flags & UCF_IGNSIGM)) {
/* Set signal mask */
if ((r = sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL)) == -1)
return(r);
}
if (!(ucp->uc_flags & UCF_IGNFPU)) {
if ((r = setmcontext(&(ucp->uc_mcontext))) == -1)
return(r);
}
return(0);
}
/*===========================================================================*
* getuctx *
*===========================================================================*/
PUBLIC int getuctx(ucontext_t *ucp)
{
int r;
if (ucp == NULL) {
errno = EFAULT;
return(-1);
}
if (!(ucp->uc_flags & UCF_IGNSIGM)) {
/* Get signal mask */
if ((r = sigprocmask(0, NULL, &ucp->uc_sigmask)) == -1)
return(r);
}
if (!(ucp->uc_flags & UCF_IGNFPU)) {
if ((r = getmcontext(&(ucp->uc_mcontext))) != 0)
return(r);
}
return(0);
}
/*===========================================================================*
* makecontext *
*===========================================================================*/
PUBLIC void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
{
va_list ap;
unsigned int *stack_top, *stack_guard;
/* There are a number of situations that are erroneous, but we can't actually
tell the caller something is wrong, because this is a void function.
Instead, mcontext_t contains a magic field that has to be set
properly before it can be used. */
if (ucp == NULL) {
return;
} else if ((ucp->uc_stack.ss_sp == NULL) ||
(ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
ucp->uc_mcontext.mc_magic = 0;
ucp->uc_mcontext.mc_p_reg.sp = 0;
return;
}
if (ucp->uc_mcontext.mc_magic == MCF_MAGIC) {
#if (_MINIX_CHIP == _CHIP_INTEL)
/* The caller provides a pointer to a stack that we can use to run our
context on. When the context starts, control is given to a wrapped
start routine, which calls a function and cleans up the stack
afterwards. The wrapper needs the address of that function on the
stack.
The stack will be prepared as follows:
func() - start routine
arg1 - first argument
...
argn - last argument
ucp - context, esp points here when `func' returns
_ctx_start pops the address of `func' from the stack and calls it.
The stack will then be setup with all arguments for `func'. When
`func' returns, _ctx_start cleans up the stack such that ucp is at
the top of the stack, ready to be used by resumecontext.
Resumecontext, in turn, checks whether another context is ready to
be executed (i.e., uc_link != NULL) or exit(2)s the process. */
/* Find the top of the stack from which we grow downwards. */
stack_top = (unsigned int *) ((uintptr_t ) ucp->uc_stack.ss_sp +
ucp->uc_stack.ss_size);
/* Align the arguments to 16 bytes (we might lose a few bytes of stack
space here).*/
stack_top = (unsigned int *) ((uintptr_t) stack_top & ~0xf);
/* Make room for 'func', the `func' routine arguments, and ucp. */
stack_top -= (1 + argc + 1);
/* Adjust the machine context to point to the top of this stack and the
program counter to the context start wrapper. */
ucp->uc_mcontext.mc_p_reg.fp = 0; /* Clear frame pointer */
ucp->uc_mcontext.mc_p_reg.sp = (reg_t) stack_top;
ucp->uc_mcontext.mc_p_reg.pc = (reg_t) ctx_start;
*stack_top++ = (uintptr_t) func;
/* Copy arguments to the stack. */
va_start(ap, argc);
while (argc-- > 0) {
*stack_top++ = va_arg(ap, uintptr_t);
}
va_end(ap);
/* Store ucp on the stack */
*stack_top = (uintptr_t) ucp;
/* Set ESI to point to the base of the stack where ucp is stored, so
that the wrapper function knows how to clean up the stack after
calling `func' (i.e., how to adjust ESP). */
ucp->uc_mcontext.mc_p_reg.si = (reg_t) stack_top;
/* If we ran out of stack space, invalidate stack pointer. Eventually,
swapcontext will choke on this and return ENOMEM. */
if (stack_top == ucp->uc_stack.ss_sp)
ucp->uc_mcontext.mc_p_reg.sp = 0;
#else
# error "Unsupported platform"
#endif
}
}
/*===========================================================================*
* swapcontext *
*===========================================================================*/
PUBLIC int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
{
int r;
unsigned int *stack_guard;
if ((oucp == NULL) || (ucp == NULL)) {
errno = EFAULT;
return(-1);
}
if (ucp->uc_mcontext.mc_p_reg.sp == 0) {
/* No stack space. Bail out. */
errno = ENOMEM;
return(-1);
}
oucp->uc_flags &= ~UCF_SWAPPED;
r = getcontext(oucp);
if ((r == 0) && !(oucp->uc_flags & UCF_SWAPPED)) {
oucp->uc_flags |= UCF_SWAPPED;
r = setcontext(ucp);
}
return(r);
}
/*===========================================================================*
* resumecontext *
*===========================================================================*/
PUBLIC void resumecontext(ucontext_t *ucp)
{
if (ucp->uc_link == NULL) exit(0);
/* Error handling? Where should the error go to? */
(void) setcontext((const ucontext_t *) ucp->uc_link);
exit(1); /* Never reached */
}
+1
View File
@@ -33,6 +33,7 @@ SRCS= \
sef_ping.c \
sef_init.c \
sys_abort.c \
sys_mcontext.c \
sys_cprof.c \
sys_endsig.c \
sys_eniop.c \
+32
View File
@@ -0,0 +1,32 @@
#include "syslib.h"
PUBLIC int sys_getmcontext(proc, mcp)
endpoint_t proc; /* process retrieving context */
mcontext_t *mcp; /* where to store context */
{
/* A process wants to store its context in mcp. */
message m;
int r;
m.PR_ENDPT = proc;
m.PR_CTX_PTR = (char *) mcp;
r = _kernel_call(SYS_GETMCONTEXT, &m);
return r;
}
PUBLIC int sys_setmcontext(proc, mcp)
endpoint_t proc; /* process setting context */
mcontext_t *mcp; /* where to get context from */
{
/* A process wants to restore context stored in ucp. */
message m;
int r;
m.PR_ENDPT = proc;
m.PR_CTX_PTR = (char *) mcp;
r = _kernel_call(SYS_SETMCONTEXT, &m);
return r;
}