makecontext.c revision 89177
1106716Smarcel/*
2106716Smarcel * Copyright (c) 2001 Daniel M. Eischen <deischen@freebsd.org>
3106716Smarcel * All rights reserved.
4106716Smarcel *
5106716Smarcel * Redistribution and use in source and binary forms, with or without
6106716Smarcel * modification, are permitted provided that the following conditions
7106716Smarcel * are met:
8106716Smarcel * 1. Redistributions of source code must retain the above copyright
9106716Smarcel *    notice, this list of conditions and the following disclaimer.
10106716Smarcel * 2. Neither the name of the author nor the names of its contributors
11106716Smarcel *    may be used to endorse or promote products derived from this software
12106716Smarcel *    without specific prior written permission.
13106716Smarcel *
14106716Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15106716Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16106716Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17106716Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18106716Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19106716Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20106716Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21106716Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22106716Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23106716Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24106716Smarcel * SUCH DAMAGE.
25106716Smarcel */
26106716Smarcel
27106716Smarcel#include <sys/cdefs.h>
28106716Smarcel__FBSDID("$FreeBSD: head/lib/libc/i386/gen/makecontext.c 89177 2002-01-10 02:40:59Z deischen $");
29106716Smarcel
30106716Smarcel#include <sys/param.h>
31106716Smarcel#include <sys/signal.h>
32106716Smarcel
33106716Smarcel#include <errno.h>
34106716Smarcel#include <stdarg.h>
35106716Smarcel#include <ucontext.h>
36106716Smarcel#include <unistd.h>
37106716Smarcel
38111176Sru/* Prototypes */
39106716Smarcelextern void _ctx_start(ucontext_t *, int argc, ...);
40106716Smarcel
41106716Smarcel
42106716Smarcel__weak_reference(__makecontext, makecontext);
43106716Smarcel
44106716Smarcelvoid
45106716Smarcel_ctx_done (ucontext_t *ucp)
46168569Sdelphij{
47106716Smarcel	if (ucp->uc_link == NULL)
48106716Smarcel		exit(0);
49106716Smarcel	else {
50106716Smarcel		/*
51106716Smarcel		 * Since this context has finished, don't allow it
52106716Smarcel		 * to be restarted without being reinitialized (via
53106716Smarcel		 * setcontext or swapcontext).
54106716Smarcel		 */
55106716Smarcel		ucp->uc_mcontext.mc_flags = 0;
56106716Smarcel
57106716Smarcel		/* Set context to next one in link */
58106716Smarcel		/* XXX - what to do for error, abort? */
59106716Smarcel		setcontext((const ucontext_t *)ucp->uc_link);
60106716Smarcel		abort();	/* should never get here */
61106716Smarcel	}
62106716Smarcel}
63106716Smarcel
64106716Smarcelvoid
65106716Smarcel__makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...)
66106716Smarcel{
67106716Smarcel	va_list		ap;
68106716Smarcel	char		*stack_top;
69106716Smarcel	intptr_t	*argp;
70106716Smarcel	int		i;
71106716Smarcel
72106716Smarcel	if (ucp == NULL)
73106716Smarcel		return;
74106716Smarcel	else if ((ucp->uc_stack.ss_sp == NULL) ||
75106716Smarcel	    (ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
76106716Smarcel		/*
77106716Smarcel		 * This should really return -1 with errno set to ENOMEM
78106716Smarcel		 * or something, but the spec says that makecontext is
79106716Smarcel		 * a void function.   At least make sure that the context
80106716Smarcel		 * isn't valid so it can't be used without an error.
81106716Smarcel		 */
82106716Smarcel		ucp->uc_mcontext.mc_flags = 0;
83106716Smarcel	}
84106716Smarcel	/* XXX - Do we want to sanity check argc? */
85106716Smarcel	else if ((argc < 0) || (argc > NCARGS)) {
86106716Smarcel		ucp->uc_mcontext.mc_flags = 0;
87106716Smarcel	}
88106716Smarcel	/* Make sure the context is valid. */
89106716Smarcel	else if ((ucp->uc_mcontext.mc_flags & __UC_MC_VALID) != 0) {
90106716Smarcel		/*
91106716Smarcel		 * Arrange the stack as follows:
92106716Smarcel		 *
93106716Smarcel		 *	_ctx_start()	- context start wrapper
94106716Smarcel		 *	start()		- user start routine
95106716Smarcel		 * 	arg1
96106716Smarcel		 *	...
97106716Smarcel		 *	argn
98106716Smarcel		 *	ucp		- this context, %ebp points here
99106716Smarcel		 *
100106716Smarcel		 * When the context is started, control will return to
101106716Smarcel		 * the context start wrapper which will pop the user
102106716Smarcel		 * start routine from the top of the stack.  After that,
103106716Smarcel		 * the top of the stack will be setup with all arguments
104106716Smarcel		 * necessary for calling the start routine.  When the
105106716Smarcel		 * start routine returns, the context wrapper then sets
106106716Smarcel		 * the stack pointer to %ebp which was setup to point to
107106716Smarcel		 * the base of the stack (and where ucp is stored).  It
108106716Smarcel		 * will then call _ctx_done() to swap in the next context
109106716Smarcel		 * (uc_link != 0) or exit the program (uc_link == 0).
110106716Smarcel		 */
111106716Smarcel		stack_top = (char *)(ucp->uc_stack.ss_sp +
112106716Smarcel		    ucp->uc_stack.ss_size - sizeof(double));
113106716Smarcel		stack_top = (char *)ALIGN(stack_top);
114106716Smarcel
115106716Smarcel		/*
116118367Smarcel		 * Adjust top of stack to allow for 3 pointers (return
117106716Smarcel		 * address, _ctx_start, and ucp) and argc arguments.
118106716Smarcel		 * We allow the arguments to be pointers also.
119168569Sdelphij		 */
120106716Smarcel		stack_top = stack_top - (sizeof(intptr_t) * (3 + argc));
121106716Smarcel		argp = (intptr_t *)stack_top;
122168569Sdelphij
123106716Smarcel		/*
124106716Smarcel		 * Setup the top of the stack with the user start routine
125106716Smarcel		 * followed by all of its aguments and the pointer to the
126118367Smarcel		 * ucontext.  We need to leave a spare spot at the top of
127106716Smarcel		 * the stack because setcontext will move eip to the top
128106716Smarcel		 * of the stack before returning.
129106716Smarcel		 */
130106716Smarcel		*argp = (intptr_t)_ctx_start;  /* overwritten with same value */
131106716Smarcel		argp++;
132106716Smarcel		*argp = (intptr_t)start;
133115084Smarcel		argp++;
134118367Smarcel
135118367Smarcel		/* Add all the arguments: */
136106716Smarcel		va_start(ap, argc);
137106716Smarcel		for (i = 0; i < argc; i++) {
138106716Smarcel			*argp = va_arg(ap, intptr_t);
139106716Smarcel			argp++;
140106716Smarcel		}
141106716Smarcel		va_end(ap);
142106716Smarcel
143106716Smarcel		/* The ucontext is placed at the bottom of the stack. */
144106716Smarcel		*argp = (intptr_t)ucp;
145106716Smarcel
146106716Smarcel		/*
147106716Smarcel		 * Set the machine context to point to the top of the
148106716Smarcel		 * stack and the program counter to the context start
149168569Sdelphij		 * wrapper.  Note that setcontext() pushes the return
150106716Smarcel		 * address onto the top of the stack, so allow for this
151106716Smarcel		 * by adjusting the stack downward 1 slot.  Also set
152106716Smarcel		 * %ebp to point to the base of the stack where ucp
153106716Smarcel		 * is stored.
154106716Smarcel		 */
155106716Smarcel		ucp->uc_mcontext.mc_ebp = (int)argp;
156118367Smarcel		ucp->uc_mcontext.mc_esp = (int)stack_top + sizeof(caddr_t);
157106716Smarcel		ucp->uc_mcontext.mc_eip = (int)_ctx_start;
158106716Smarcel	}
159106716Smarcel}
160106716Smarcel