1 /* 2 * Copyright (c) 2015 Matthew Dillon, All rights reserved. 3 * 4 * 1. Redistributions of source code must retain the above copyright 5 * notice, this list of conditions and the following disclaimer. 6 * 2. Redistributions in binary form must reproduce the above copyright 7 * notice, this list of conditions and the following disclaimer in 8 * the documentation and/or other materials provided with the 9 * distribution. 10 * 11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 12 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 14 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 15 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 16 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 17 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 19 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 20 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 21 * SUCH DAMAGE. 22 */ 23 24 #include <sys/cdefs.h> 25 #include <sys/param.h> 26 #include <sys/signal.h> 27 #include <sys/ucontext.h> 28 29 #include <machine/frame.h> 30 #include <machine/tss.h> 31 #include <machine/segments.h> 32 33 #include <signal.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <stdarg.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 40 /* Prototypes */ 41 42 static void makectx_quick_wrapper(ucontext_t *ucp, uint64_t *stack_top); 43 44 /* 45 * makecontext_quick() associates a stack with a user thread context 46 * setup to execute a cofunc sequence. The caller only initializes the 47 * uc_stack.* fields, uc_cofunc, and uc_arg. This function will zero or 48 * initialize all other fields. Upon return the caller can optionally 49 * also initialize uc_link. 50 * 51 * These 'quick' calls do not mess with the signal mask and do not require 52 * kernel intervention. Scratch registers (including FP regs, which are also 53 * scratch registers) are not saved or restored. Cofunction loops also 54 * optimize cofunc call loops by not saving the register state when 55 * switching away to double performance. Of course, swapcontext_quick() 56 * still saves the register state. There is no getcontext_quick() call 57 * on purpose. 58 */ 59 void 60 _makecontext_quick(ucontext_t *ucp) 61 { 62 uint64_t *stack_top; 63 64 if (ucp == NULL) 65 return; 66 bzero(&ucp->uc_sigmask, sizeof(ucp->uc_sigmask)); 67 bzero(&ucp->uc_mcontext, sizeof(ucp->uc_mcontext)); 68 ucp->uc_link = NULL; 69 ucp->uc_mcontext.mc_len = sizeof(mcontext_t); 70 71 stack_top = (uint64_t *)(ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size); 72 stack_top = (uint64_t *)((uint64_t)(stack_top) & ~63UL); 73 stack_top -= 1; 74 75 /* 76 * Set the machine context to point to the top of the 77 * stack and the program counter to the context start 78 * wrapper. Note that setcontext() pushes the return 79 * address onto the top of the stack, so allow for this 80 * by adjusting the stack downward 1 slot. Also set 81 * %rbp to point to the base of the stack where ucp 82 * is stored. 83 */ 84 ucp->uc_mcontext.mc_rdi = (register_t)ucp; 85 ucp->uc_mcontext.mc_rsi = (register_t)stack_top; 86 ucp->uc_mcontext.mc_rsp = (register_t)stack_top; 87 ucp->uc_mcontext.mc_rip = (register_t)makectx_quick_wrapper; 88 ucp->uc_mcontext.mc_ownedfp = _MC_FPOWNED_NONE; 89 ucp->uc_mcontext.mc_fpformat = _MC_FPFMT_NODEV; 90 ucp->uc_mcontext.mc_cs = GSEL(GUCODE_SEL, SEL_UPL); 91 ucp->uc_mcontext.mc_ss = GSEL(GUDATA_SEL, SEL_UPL); 92 } 93 94 __weak_reference(_makecontext_quick, makecontext_quick); 95 96 /* 97 * If the cofunc call returns set the context up to re-execute the 98 * wrapper if the linkages eventually link back to this ucp. The 99 * cofunc can also change uc_cofunc and uc_arg as it desires, allowing 100 * cofunctions to be optimally linked together. 101 */ 102 static void 103 makectx_quick_wrapper(ucontext_t *ucp, uint64_t *stack_top) 104 { 105 for (;;) { 106 ucp->uc_cofunc(ucp, ucp->uc_arg); 107 if (ucp->uc_link == ucp) 108 continue; 109 ucp->uc_mcontext.mc_rdi = (register_t)ucp; 110 ucp->uc_mcontext.mc_rsi = (register_t)stack_top; 111 ucp->uc_mcontext.mc_rsp = (register_t)stack_top; 112 ucp->uc_mcontext.mc_rip = (register_t)makectx_quick_wrapper; 113 if (ucp->uc_link) 114 setcontext_quick(ucp->uc_link); 115 exit(0); 116 } 117 } 118