189177Sdeischen/*
289177Sdeischen * Copyright (c) 2001 Daniel M. Eischen <deischen@freebsd.org>
389177Sdeischen * All rights reserved.
489177Sdeischen *
589177Sdeischen * Redistribution and use in source and binary forms, with or without
689177Sdeischen * modification, are permitted provided that the following conditions
789177Sdeischen * are met:
889177Sdeischen * 1. Redistributions of source code must retain the above copyright
989177Sdeischen *    notice, this list of conditions and the following disclaimer.
1089177Sdeischen * 2. Neither the name of the author nor the names of its contributors
1189177Sdeischen *    may be used to endorse or promote products derived from this software
1289177Sdeischen *    without specific prior written permission.
1389177Sdeischen *
1489177Sdeischen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1589177Sdeischen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1689177Sdeischen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1789177Sdeischen * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1889177Sdeischen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1989177Sdeischen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2089177Sdeischen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2189177Sdeischen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2289177Sdeischen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2389177Sdeischen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2489177Sdeischen * SUCH DAMAGE.
2589177Sdeischen */
2689177Sdeischen
2789177Sdeischen#include <sys/cdefs.h>
2889177Sdeischen__FBSDID("$FreeBSD$");
2989177Sdeischen
3089177Sdeischen#include <sys/param.h>
3189177Sdeischen#include <sys/signal.h>
32103406Smini#include <sys/ucontext.h>
3389177Sdeischen
3489177Sdeischen#include <errno.h>
3589177Sdeischen#include <stdarg.h>
36101915Srobert#include <stdlib.h>
3789177Sdeischen#include <unistd.h>
3889177Sdeischen
3989177Sdeischen/* Prototypes */
4089177Sdeischenextern void _ctx_start(ucontext_t *, int argc, ...);
4189177Sdeischen
4289177Sdeischen
4389177Sdeischen__weak_reference(__makecontext, makecontext);
4489177Sdeischen
4589177Sdeischenvoid
4689177Sdeischen_ctx_done (ucontext_t *ucp)
4789177Sdeischen{
4889177Sdeischen	if (ucp->uc_link == NULL)
4989177Sdeischen		exit(0);
5089177Sdeischen	else {
5189177Sdeischen		/*
5289177Sdeischen		 * Since this context has finished, don't allow it
5389177Sdeischen		 * to be restarted without being reinitialized (via
5489177Sdeischen		 * setcontext or swapcontext).
5589177Sdeischen		 */
56103406Smini		ucp->uc_mcontext.mc_len = 0;
5789177Sdeischen
5889177Sdeischen		/* Set context to next one in link */
5989177Sdeischen		/* XXX - what to do for error, abort? */
6089177Sdeischen		setcontext((const ucontext_t *)ucp->uc_link);
6189177Sdeischen		abort();	/* should never get here */
6289177Sdeischen	}
6389177Sdeischen}
6489177Sdeischen
6589177Sdeischenvoid
6689177Sdeischen__makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...)
6789177Sdeischen{
6889177Sdeischen	va_list		ap;
6989177Sdeischen	char		*stack_top;
7089177Sdeischen	intptr_t	*argp;
7189177Sdeischen	int		i;
7289177Sdeischen
7389177Sdeischen	if (ucp == NULL)
7489177Sdeischen		return;
7589177Sdeischen	else if ((ucp->uc_stack.ss_sp == NULL) ||
7689177Sdeischen	    (ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
7789177Sdeischen		/*
7889177Sdeischen		 * This should really return -1 with errno set to ENOMEM
7989177Sdeischen		 * or something, but the spec says that makecontext is
8089177Sdeischen		 * a void function.   At least make sure that the context
8189177Sdeischen		 * isn't valid so it can't be used without an error.
8289177Sdeischen		 */
83103406Smini		ucp->uc_mcontext.mc_len = 0;
8489177Sdeischen	}
8589177Sdeischen	/* XXX - Do we want to sanity check argc? */
8689177Sdeischen	else if ((argc < 0) || (argc > NCARGS)) {
87103406Smini		ucp->uc_mcontext.mc_len = 0;
8889177Sdeischen	}
8989177Sdeischen	/* Make sure the context is valid. */
90103406Smini	else if (ucp->uc_mcontext.mc_len == sizeof(mcontext_t)) {
9189177Sdeischen		/*
9289177Sdeischen		 * Arrange the stack as follows:
9389177Sdeischen		 *
9489177Sdeischen		 *	_ctx_start()	- context start wrapper
9589177Sdeischen		 *	start()		- user start routine
96138403Sdeischen		 * 	arg1            - first argument, aligned(16)
9789177Sdeischen		 *	...
9889177Sdeischen		 *	argn
9989177Sdeischen		 *	ucp		- this context, %ebp points here
10089177Sdeischen		 *
10189177Sdeischen		 * When the context is started, control will return to
10289177Sdeischen		 * the context start wrapper which will pop the user
10389177Sdeischen		 * start routine from the top of the stack.  After that,
10489177Sdeischen		 * the top of the stack will be setup with all arguments
10589177Sdeischen		 * necessary for calling the start routine.  When the
10689177Sdeischen		 * start routine returns, the context wrapper then sets
10789177Sdeischen		 * the stack pointer to %ebp which was setup to point to
10889177Sdeischen		 * the base of the stack (and where ucp is stored).  It
10989177Sdeischen		 * will then call _ctx_done() to swap in the next context
11089177Sdeischen		 * (uc_link != 0) or exit the program (uc_link == 0).
11189177Sdeischen		 */
11289177Sdeischen		stack_top = (char *)(ucp->uc_stack.ss_sp +
113138403Sdeischen		    ucp->uc_stack.ss_size - sizeof(intptr_t));
11489177Sdeischen
11589177Sdeischen		/*
11689177Sdeischen		 * Adjust top of stack to allow for 3 pointers (return
11789177Sdeischen		 * address, _ctx_start, and ucp) and argc arguments.
118138403Sdeischen		 * We allow the arguments to be pointers also.  The first
119138403Sdeischen		 * argument to the user function must be properly aligned.
12089177Sdeischen		 */
121138403Sdeischen		stack_top = stack_top - (sizeof(intptr_t) * (1 + argc));
122138403Sdeischen		stack_top = (char *)((unsigned)stack_top & ~15);
123138403Sdeischen		stack_top = stack_top - (2 * sizeof(intptr_t));
12489177Sdeischen		argp = (intptr_t *)stack_top;
12589177Sdeischen
12689177Sdeischen		/*
12789177Sdeischen		 * Setup the top of the stack with the user start routine
12889177Sdeischen		 * followed by all of its aguments and the pointer to the
12989177Sdeischen		 * ucontext.  We need to leave a spare spot at the top of
13089177Sdeischen		 * the stack because setcontext will move eip to the top
13189177Sdeischen		 * of the stack before returning.
13289177Sdeischen		 */
13389177Sdeischen		*argp = (intptr_t)_ctx_start;  /* overwritten with same value */
13489177Sdeischen		argp++;
13589177Sdeischen		*argp = (intptr_t)start;
13689177Sdeischen		argp++;
13789177Sdeischen
13889177Sdeischen		/* Add all the arguments: */
13989177Sdeischen		va_start(ap, argc);
14089177Sdeischen		for (i = 0; i < argc; i++) {
14189177Sdeischen			*argp = va_arg(ap, intptr_t);
14289177Sdeischen			argp++;
14389177Sdeischen		}
14489177Sdeischen		va_end(ap);
14589177Sdeischen
14689177Sdeischen		/* The ucontext is placed at the bottom of the stack. */
14789177Sdeischen		*argp = (intptr_t)ucp;
14889177Sdeischen
14989177Sdeischen		/*
15089177Sdeischen		 * Set the machine context to point to the top of the
15189177Sdeischen		 * stack and the program counter to the context start
15289177Sdeischen		 * wrapper.  Note that setcontext() pushes the return
15389177Sdeischen		 * address onto the top of the stack, so allow for this
15489177Sdeischen		 * by adjusting the stack downward 1 slot.  Also set
155131460Sdavidxu		 * %esi to point to the base of the stack where ucp
15689177Sdeischen		 * is stored.
15789177Sdeischen		 */
158131460Sdavidxu		ucp->uc_mcontext.mc_esi = (int)argp;
159131460Sdavidxu		ucp->uc_mcontext.mc_ebp = 0;
16089177Sdeischen		ucp->uc_mcontext.mc_esp = (int)stack_top + sizeof(caddr_t);
16189177Sdeischen		ucp->uc_mcontext.mc_eip = (int)_ctx_start;
16289177Sdeischen	}
16389177Sdeischen}
164