1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*      Copyright (c) 1988 AT&T     */
  28 /*        All Rights Reserved   */
  29 
  30 /*
  31  * Copyright 2023 Oxide Computer Company
  32  */
  33 
  34 #pragma weak _makecontext = makecontext
  35 
  36 #include "lint.h"
  37 #include <stdarg.h>
  38 #include <ucontext.h>
  39 #include <sys/stack.h>
  40 #include <sys/auxv.h>
  41 #include <errno.h>
  42 #include "libc.h"
  43 
  44 /*
  45  * The ucontext_t that the user passes in must have been primed with a
  46  * call to getcontext(2), have the uc_stack member set to reflect the
  47  * stack which this context will use, and have the uc_link member set
  48  * to the context which should be resumed when this context returns.
  49  * When makecontext() returns, the ucontext_t will be set to run the
  50  * given function with the given parameters on the stack specified by
  51  * uc_stack, and which will return to the ucontext_t specified by uc_link.
  52  */
  53 
  54 /*
  55  * The original i386 ABI said that the stack pointer need be only 4-byte
  56  * aligned before a function call (STACK_ALIGN == 4).  The ABI supplement
  57  * version 1.0 changed the required alignment to 16-byte for the benefit of
  58  * floating point code compiled using sse2.  The compiler assumes this
  59  * alignment and maintains it for calls it generates.  If the stack is
  60  * initially properly aligned, it will continue to be so aligned.  If it is
  61  * not initially so aligned, it will never become so aligned.
  62  *
  63  * One slightly confusing detail to keep in mind is that the 16-byte
  64  * alignment (%esp & 0xf == 0) is true just *before* the call instruction.
  65  * The call instruction will then push a return value, decrementing %esp by
  66  * 4.  Therefore, if one dumps %esp at the at the very first instruction in
  67  * a function, it will end with a 0xc.  The compiler expects this and
  68  * compensates for it properly.
  69  *
  70  * Note: If you change this value, you need to change it in the following
  71  * files as well:
  72  *
  73  *  - lib/libc/i386/threads/machdep.c
  74  *  - lib/crt/i386/crti.s
  75  *  - lib/crt/i386/crt1.s
  76  */
  77 #undef  STACK_ALIGN
  78 #define STACK_ALIGN     16
  79 
  80 static void resumecontext(void);
  81 
  82 void
  83 makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
  84 {
  85         long *sp;
  86         long *tsp;
  87         va_list ap;
  88         size_t size;
  89 
  90         ucp->uc_mcontext.gregs[EIP] = (greg_t)func;
  91 
  92         size = sizeof (long) * (argc + 1);
  93 
  94         tsp = (long *)(((uintptr_t)ucp->uc_stack.ss_sp +
  95             ucp->uc_stack.ss_size - size) & ~(STACK_ALIGN - 1));
  96 
  97         /*
  98          * Since we're emulating the call instruction, we must push the
  99          * return address (which involves adjusting the stack pointer to
 100          * have the proper 4-byte bias).
 101          */
 102         sp = tsp - 1;
 103 
 104         *sp = (long)resumecontext;              /* return address */
 105 
 106         ucp->uc_mcontext.gregs[UESP] = (greg_t)sp;
 107 
 108         /*
 109          * "push" all the arguments
 110          */
 111         va_start(ap, argc);
 112         while (argc-- > 0)
 113                 *tsp++ = va_arg(ap, long);
 114         va_end(ap);
 115 }
 116 
 117 
 118 static void
 119 resumecontext(void)
 120 {
 121         ucontext_t uc;
 122 
 123         (void) getcontext(&uc);
 124         (void) setcontext(uc.uc_link);
 125 }
 126 
 127 /*
 128  * This is the ISA-specific allocation logic for allocating and setting up an
 129  * extended ucontext_t. In particular, right now we need to allocate and add
 130  * space for the UC_XSAVE member if we have the appropriate hardware support.
 131  * The i386 / amd64 versions could be consolidated in a single x86 impl, but we
 132  * don't have that right now.
 133  */
 134 ucontext_t *
 135 ucontext_alloc(uint32_t flags)
 136 {
 137         boolean_t do_xsave = B_FALSE;
 138         size_t to_alloc = sizeof (ucontext_t);
 139         ucontext_t *ucp;
 140 
 141         if (flags != 0) {
 142                 errno = EINVAL;
 143                 return (NULL);
 144         }
 145 
 146         /*
 147          * This value isn't really 100% accurate. The xsave size is basically
 148          * the worst case that we can have. The XMM / xsave structures aren't
 149          * included in here, but are going to be enough to cover this. We can
 150          * probably try to do a little better and should consider asking the
 151          * kernel for something more accurate. In particular, the problem with
 152          * tis is that it doesn't account for the right size of future-looking
 153          * dynamic things, but then again neither does rtld. We'll deal with
 154          * this when we have support for the xfd MSR and actually use it. For
 155          * more information see uts/intel/os/fpu.c's big theory statement.
 156          */
 157         switch (___getauxval(AT_SUN_FPTYPE)) {
 158         case AT_386_FPINFO_XSAVE:
 159         case AT_386_FPINFO_XSAVE_AMD:
 160                 do_xsave = B_TRUE;
 161                 to_alloc += ___getauxval(AT_SUN_FPSIZE);
 162                 break;
 163         default:
 164                 break;
 165         }
 166 
 167         ucp = calloc(1, to_alloc);
 168         if (ucp == NULL) {
 169                 return (NULL);
 170         }
 171 
 172         if (do_xsave) {
 173                 uintptr_t addr = (uintptr_t)ucp;
 174                 ucp->uc_xsave = addr + sizeof (ucontext_t);
 175         }
 176 
 177         return (ucp);
 178 }
 179 
 180 void
 181 ucontext_free(ucontext_t *ucp)
 182 {
 183         free(ucp);
 184 }