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