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 static void resumecontext(void); 55 56 void 57 makecontext(ucontext_t *ucp, void (*func)(), int argc, ...) 58 { 59 long *sp; 60 long *tsp; 61 va_list ap; 62 size_t size; 63 int pusharg = (argc > 6 ? argc - 6 : 0); 64 greg_t tmp; 65 int i; 66 67 ucp->uc_mcontext.gregs[REG_PC] = (greg_t)func; 68 69 size = sizeof (long) * (pusharg + 1); 70 71 /* 72 * Calculate new value for %rsp. On entry to a function, 73 * %rsp must be STACK_ENTRY_ALIGNed but not STACK_ALIGNed. 74 * This is because the pushq %rbp will correct the alignment. 75 */ 76 77 sp = (long *)(((uintptr_t)ucp->uc_stack.ss_sp + 78 ucp->uc_stack.ss_size - size) & ~(STACK_ENTRY_ALIGN - 1)); 79 80 if (((uintptr_t)sp & (STACK_ALIGN - 1ul)) == 0) 81 sp -= STACK_ENTRY_ALIGN / sizeof (*sp); 82 83 tsp = sp + 1; 84 85 va_start(ap, argc); 86 87 for (i = 0; i < argc; i++) { 88 tmp = va_arg(ap, long); 89 switch (i) { 90 case 0: 91 ucp->uc_mcontext.gregs[REG_RDI] = tmp; 92 break; 93 case 1: 94 ucp->uc_mcontext.gregs[REG_RSI] = tmp; 95 break; 96 case 2: 97 ucp->uc_mcontext.gregs[REG_RDX] = tmp; 98 break; 99 case 3: 100 ucp->uc_mcontext.gregs[REG_RCX] = tmp; 101 break; 102 case 4: 103 ucp->uc_mcontext.gregs[REG_R8] = tmp; 104 break; 105 case 5: 106 ucp->uc_mcontext.gregs[REG_R9] = tmp; 107 break; 108 default: 109 *tsp++ = tmp; 110 break; 111 } 112 } 113 114 va_end(ap); 115 116 *sp = (long)resumecontext; /* return address */ 117 118 ucp->uc_mcontext.gregs[REG_SP] = (greg_t)sp; 119 } 120 121 122 static void 123 resumecontext(void) 124 { 125 ucontext_t uc; 126 127 (void) getcontext(&uc); 128 (void) setcontext(uc.uc_link); 129 } 130 131 /* 132 * This is the ISA-specific allocation logic for allocating and setting up an 133 * extended ucontext_t. In particular, we need to allocate and add space for the 134 * UC_XSAVE member if we have the appropriate hardware support. The i386 / 135 * amd64 versions could be consolidated in a single x86 impl, but we don't have 136 * that yet. 137 */ 138 ucontext_t * 139 ucontext_alloc(uint32_t flags) 140 { 141 boolean_t do_xsave = B_FALSE; 142 size_t to_alloc = sizeof (ucontext_t); 143 ucontext_t *ucp; 144 145 if (flags != 0) { 146 errno = EINVAL; 147 return (NULL); 148 } 149 150 /* 151 * The AT_SUN_FPTYPE value is used as an approximation for the size of 152 * the uc_xsave structure that we need additional space for. Ideally we 153 * should ask the kernel how much space we actually need and only 154 * allocate that much. Because the uc_xsave member does not need to 155 * include the 512-byte XMM structure or the full xsave header, this 156 * will work in the interim. 157 * 158 * Currently the system doesn't support dynamically enabling FPU 159 * features with the Intel xfd (extended feature disable) MSR. When we 160 * have support for that we'll need to redo this and ask the kernel for 161 * the right size. We will probably want to cache the size for rtld as 162 * well. For more information see uts/intel/os/fpu.c's big theory 163 * statement. 164 */ 165 switch (___getauxval(AT_SUN_FPTYPE)) { 166 case AT_386_FPINFO_XSAVE: 167 case AT_386_FPINFO_XSAVE_AMD: 168 do_xsave = B_TRUE; 169 to_alloc += ___getauxval(AT_SUN_FPSIZE); 170 break; 171 default: 172 break; 173 } 174 175 ucp = calloc(1, to_alloc); 176 if (ucp == NULL) { 177 return (NULL); 178 } 179 180 if (do_xsave) { 181 /* 182 * Right now we're not really concerned with alignment of the 183 * uc_xsave member. This structure it points to is no defined 184 * for application access and the kernel doesn't care. This will 185 * give us something that is fairly reasonable though. 186 */ 187 uintptr_t addr = (uintptr_t)ucp; 188 ucp->uc_xsave = addr + sizeof (ucontext_t); 189 } 190 191 return (ucp); 192 } 193 194 void 195 ucontext_free(ucontext_t *ucp) 196 { 197 free(ucp); 198 } 199