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, we need to allocate and add space for the 130 * UC_XSAVE member if we have the appropriate hardware support. The i386 / 131 * amd64 versions could be consolidated in a single x86 impl, but we don't have 132 * that yet. 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 * The AT_SUN_FPTYPE value is used as an approximation for the size of 148 * the uc_xsave structure that we need additional space for. Ideally we 149 * should ask the kernel how much space we actually need and only 150 * allocate that much. Because the uc_xsave member does not need to 151 * include the 512-byte XMM structure or the full xsave header, this 152 * will work in the interim. 153 * 154 * Currently the system doesn't support dynamically enabling FPU 155 * features with the Intel xfd (extended feature disable) MSR. When we 156 * have support for that we'll need to redo this and ask the kernel for 157 * the right size. We will probably want to cache the size for rtld as 158 * well. For more information see uts/intel/os/fpu.c's big theory 159 * statement. 160 */ 161 switch (___getauxval(AT_SUN_FPTYPE)) { 162 case AT_386_FPINFO_XSAVE: 163 case AT_386_FPINFO_XSAVE_AMD: 164 do_xsave = B_TRUE; 165 to_alloc += ___getauxval(AT_SUN_FPSIZE); 166 break; 167 default: 168 break; 169 } 170 171 ucp = calloc(1, to_alloc); 172 if (ucp == NULL) { 173 return (NULL); 174 } 175 176 if (do_xsave) { 177 uintptr_t addr = (uintptr_t)ucp; 178 ucp->uc_xsave = addr + sizeof (ucontext_t); 179 } 180 181 return (ucp); 182 } 183 184 void 185 ucontext_free(ucontext_t *ucp) 186 { 187 free(ucp); 188 } 189