1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2001 Daniel M. Eischen <deischen@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Neither the name of the author nor the names of its contributors
13 *    may be used to endorse or promote products derived from this software
14 *    without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <sys/signal.h>
34#include <sys/ucontext.h>
35
36#include <errno.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <unistd.h>
40
41/* Prototypes */
42extern void _ctx_start(ucontext_t *, int argc, ...);
43
44
45__weak_reference(__makecontext, makecontext);
46
47void
48_ctx_done (ucontext_t *ucp)
49{
50	if (ucp->uc_link == NULL)
51		exit(0);
52	else {
53		/*
54		 * Since this context has finished, don't allow it
55		 * to be restarted without being reinitialized (via
56		 * setcontext or swapcontext).
57		 */
58		ucp->uc_mcontext.mc_len = 0;
59
60		/* Set context to next one in link */
61		/* XXX - what to do for error, abort? */
62		setcontext((const ucontext_t *)ucp->uc_link);
63		abort();	/* should never get here */
64	}
65}
66
67void
68__makecontext(ucontext_t *ucp, void (*start)(void), int argc, ...)
69{
70	va_list		ap;
71	char		*stack_top;
72	intptr_t	*argp;
73	int		i;
74
75	if (ucp == NULL)
76		return;
77	else if ((ucp->uc_stack.ss_sp == NULL) ||
78	    (ucp->uc_stack.ss_size < MINSIGSTKSZ)) {
79		/*
80		 * This should really return -1 with errno set to ENOMEM
81		 * or something, but the spec says that makecontext is
82		 * a void function.   At least make sure that the context
83		 * isn't valid so it can't be used without an error.
84		 */
85		ucp->uc_mcontext.mc_len = 0;
86	}
87	/* XXX - Do we want to sanity check argc? */
88	else if (argc < 0) {
89		ucp->uc_mcontext.mc_len = 0;
90	}
91	/* Make sure the context is valid. */
92	else if (ucp->uc_mcontext.mc_len == sizeof(mcontext_t)) {
93		/*
94		 * Arrange the stack as follows:
95		 *
96		 *	_ctx_start()	- context start wrapper
97		 *	start()		- user start routine
98		 * 	arg1            - first argument, aligned(16)
99		 *	...
100		 *	argn
101		 *	ucp		- this context, %ebp points here
102		 *
103		 * When the context is started, control will return to
104		 * the context start wrapper which will pop the user
105		 * start routine from the top of the stack.  After that,
106		 * the top of the stack will be setup with all arguments
107		 * necessary for calling the start routine.  When the
108		 * start routine returns, the context wrapper then sets
109		 * the stack pointer to %ebp which was setup to point to
110		 * the base of the stack (and where ucp is stored).  It
111		 * will then call _ctx_done() to swap in the next context
112		 * (uc_link != 0) or exit the program (uc_link == 0).
113		 */
114		stack_top = (char *)(ucp->uc_stack.ss_sp +
115		    ucp->uc_stack.ss_size - sizeof(intptr_t));
116
117		/*
118		 * Adjust top of stack to allow for 3 pointers (return
119		 * address, _ctx_start, and ucp) and argc arguments.
120		 * We allow the arguments to be pointers also.  The first
121		 * argument to the user function must be properly aligned.
122		 */
123		stack_top = stack_top - (sizeof(intptr_t) * (1 + argc));
124		stack_top = (char *)((unsigned)stack_top & ~15);
125		stack_top = stack_top - (2 * sizeof(intptr_t));
126		argp = (intptr_t *)stack_top;
127
128		/*
129		 * Setup the top of the stack with the user start routine
130		 * followed by all of its aguments and the pointer to the
131		 * ucontext.  We need to leave a spare spot at the top of
132		 * the stack because setcontext will move eip to the top
133		 * of the stack before returning.
134		 */
135		*argp = (intptr_t)_ctx_start;  /* overwritten with same value */
136		argp++;
137		*argp = (intptr_t)start;
138		argp++;
139
140		/* Add all the arguments: */
141		va_start(ap, argc);
142		for (i = 0; i < argc; i++) {
143			*argp = va_arg(ap, intptr_t);
144			argp++;
145		}
146		va_end(ap);
147
148		/* The ucontext is placed at the bottom of the stack. */
149		*argp = (intptr_t)ucp;
150
151		/*
152		 * Set the machine context to point to the top of the
153		 * stack and the program counter to the context start
154		 * wrapper.  Note that setcontext() pushes the return
155		 * address onto the top of the stack, so allow for this
156		 * by adjusting the stack downward 1 slot.  Also set
157		 * %esi to point to the base of the stack where ucp
158		 * is stored.
159		 */
160		ucp->uc_mcontext.mc_esi = (int)argp;
161		ucp->uc_mcontext.mc_ebp = 0;
162		ucp->uc_mcontext.mc_esp = (int)stack_top + sizeof(caddr_t);
163		ucp->uc_mcontext.mc_eip = (int)_ctx_start;
164	}
165}
166