262 lines
7.9 KiB
C
262 lines
7.9 KiB
C
#include <sys/cdefs.h>
|
|
#include <namespace.h>
|
|
#include <lib.h>
|
|
#include <machine/stackframe.h>
|
|
#include <sys/cdefs.h>
|
|
#include <ucontext.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
void ctx_start(void (*)(void), int, ...);
|
|
|
|
/*===========================================================================*
|
|
* setuctx *
|
|
*===========================================================================*/
|
|
int setuctx(const ucontext_t *ucp)
|
|
{
|
|
int r;
|
|
|
|
if (ucp == NULL) {
|
|
errno = EFAULT;
|
|
return(-1);
|
|
}
|
|
|
|
if (!(ucp->uc_flags & _UC_IGNSIGM)) {
|
|
/* Set signal mask */
|
|
if ((r = sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL)) == -1)
|
|
return(r);
|
|
}
|
|
|
|
if (!(ucp->uc_flags & _UC_IGNFPU)) {
|
|
if ((r = setmcontext(&(ucp->uc_mcontext))) == -1)
|
|
return(r);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* getuctx *
|
|
*===========================================================================*/
|
|
int getuctx(ucontext_t *ucp)
|
|
{
|
|
int r;
|
|
|
|
if (ucp == NULL) {
|
|
errno = EFAULT;
|
|
return(-1);
|
|
}
|
|
|
|
if (!(ucp->uc_flags & _UC_IGNSIGM)) {
|
|
/* Get signal mask */
|
|
if ((r = sigprocmask(0, NULL, &ucp->uc_sigmask)) == -1)
|
|
return(r);
|
|
}
|
|
|
|
if (!(ucp->uc_flags & _UC_IGNFPU)) {
|
|
if ((r = getmcontext(&(ucp->uc_mcontext))) != 0)
|
|
return(r);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* makecontext *
|
|
*===========================================================================*/
|
|
void makecontext(ucontext_t *ucp, void (*func)(void), int argc, ...)
|
|
{
|
|
va_list ap;
|
|
unsigned int *stack_top;
|
|
|
|
/* 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;
|
|
_UC_MACHINE_SET_STACK(ucp, 0);
|
|
return;
|
|
}
|
|
|
|
if (ucp->uc_mcontext.mc_magic == MCF_MAGIC) {
|
|
#if defined(__i386__)
|
|
/* 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. */
|
|
_UC_MACHINE_SET_EBP(ucp, 0); /* Clear frame pointer */
|
|
_UC_MACHINE_SET_STACK(ucp, (reg_t) stack_top);
|
|
_UC_MACHINE_SET_PC(ucp, (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). */
|
|
_UC_MACHINE_SET_ESI(ucp, (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) {
|
|
_UC_MACHINE_SET_STACK(ucp, 0);
|
|
}
|
|
#elif defined(__arm__)
|
|
/* 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 the
|
|
requested function. When the function finishes, it returns to the
|
|
_ctx_start wrapper that calls resumecontext (after setting up
|
|
resumecontext's parameter).
|
|
|
|
The first four arguments for the function will be passed in
|
|
regs r0-r3 as specified by the ABI, and the rest will go on
|
|
the stack. The ucp is saved in r4 so that we can
|
|
eventually pass it to resumecontext. The r4 register is
|
|
callee-preserved, so the ucp will remain valid in r4 when
|
|
_ctx_start runs. _ctx_start will move the ucp from r4 into
|
|
r0, so that the ucp is the first paramater for resumecontext.
|
|
Then, _ctx_start will call 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' routine arguments that don't fit in r0-r3 */
|
|
if (argc > 4)
|
|
stack_top -= argc - 4;
|
|
|
|
/* Adjust the machine context to point to the top of this stack and the
|
|
program counter to the 'func' entry point. Set lr to ctx_start, so
|
|
ctx_start runs after 'func'. Save ucp in r4 */
|
|
_UC_MACHINE_SET_FP(ucp, 0); /* Clear frame pointer */
|
|
_UC_MACHINE_SET_STACK(ucp, (reg_t) stack_top);
|
|
_UC_MACHINE_SET_PC(ucp, (reg_t) func);
|
|
_UC_MACHINE_SET_LR(ucp, (reg_t) ctx_start);
|
|
_UC_MACHINE_SET_R4(ucp, (reg_t) ucp);
|
|
|
|
/* Copy arguments to r0-r3 and stack. */
|
|
va_start(ap, argc);
|
|
/* Pass up to four arguments in registers. */
|
|
if (argc-- > 0)
|
|
_UC_MACHINE_SET_R0(ucp, va_arg(ap, uintptr_t));
|
|
if (argc-- > 0)
|
|
_UC_MACHINE_SET_R1(ucp, va_arg(ap, uintptr_t));
|
|
if (argc-- > 0)
|
|
_UC_MACHINE_SET_R2(ucp, va_arg(ap, uintptr_t));
|
|
if (argc-- > 0)
|
|
_UC_MACHINE_SET_R3(ucp, va_arg(ap, uintptr_t));
|
|
/* Pass the rest on the stack. */
|
|
while (argc-- > 0) {
|
|
*stack_top++ = va_arg(ap, uintptr_t);
|
|
}
|
|
va_end(ap);
|
|
|
|
/* 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) {
|
|
_UC_MACHINE_SET_STACK(ucp, 0);
|
|
}
|
|
#else
|
|
# error "Unsupported platform"
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* swapcontext *
|
|
*===========================================================================*/
|
|
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
|
|
{
|
|
int r;
|
|
|
|
if ((oucp == NULL) || (ucp == NULL)) {
|
|
errno = EFAULT;
|
|
return(-1);
|
|
}
|
|
|
|
if (_UC_MACHINE_STACK(ucp) == 0) {
|
|
/* No stack space. Bail out. */
|
|
errno = ENOMEM;
|
|
return(-1);
|
|
}
|
|
|
|
oucp->uc_flags &= ~_UC_SWAPPED;
|
|
r = getcontext(oucp);
|
|
if ((r == 0) && !(oucp->uc_flags & _UC_SWAPPED)) {
|
|
oucp->uc_flags |= _UC_SWAPPED;
|
|
r = setcontext(ucp);
|
|
}
|
|
|
|
return(r);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* resumecontext *
|
|
*===========================================================================*/
|
|
__dead
|
|
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 */
|
|
}
|
|
|