17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
57257d1b4Sraf * Common Development and Distribution License (the "License").
67257d1b4Sraf * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217257d1b4Sraf
227c478bd9Sstevel@tonic-gate /*
237257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
287c478bd9Sstevel@tonic-gate /* All Rights Reserved */
297c478bd9Sstevel@tonic-gate
30*ed093b41SRobert Mustacchi /*
31*ed093b41SRobert Mustacchi * Copyright 2023 Oxide Computer Company
32*ed093b41SRobert Mustacchi */
33*ed093b41SRobert Mustacchi
347257d1b4Sraf #pragma weak _makecontext = makecontext
357c478bd9Sstevel@tonic-gate
367257d1b4Sraf #include "lint.h"
377c478bd9Sstevel@tonic-gate #include <stdarg.h>
387c478bd9Sstevel@tonic-gate #include <ucontext.h>
397c478bd9Sstevel@tonic-gate #include <sys/stack.h>
40*ed093b41SRobert Mustacchi #include <sys/auxv.h>
41*ed093b41SRobert Mustacchi #include <errno.h>
42*ed093b41SRobert Mustacchi #include "libc.h"
437c478bd9Sstevel@tonic-gate
447c478bd9Sstevel@tonic-gate /*
457c478bd9Sstevel@tonic-gate * The ucontext_t that the user passes in must have been primed with a
467c478bd9Sstevel@tonic-gate * call to getcontext(2), have the uc_stack member set to reflect the
477c478bd9Sstevel@tonic-gate * stack which this context will use, and have the uc_link member set
487c478bd9Sstevel@tonic-gate * to the context which should be resumed when this context returns.
497c478bd9Sstevel@tonic-gate * When makecontext() returns, the ucontext_t will be set to run the
507c478bd9Sstevel@tonic-gate * given function with the given parameters on the stack specified by
517c478bd9Sstevel@tonic-gate * uc_stack, and which will return to the ucontext_t specified by uc_link.
527c478bd9Sstevel@tonic-gate */
537c478bd9Sstevel@tonic-gate
54ceef08daSJosef 'Jeff' Sipek /*
55ceef08daSJosef 'Jeff' Sipek * The original i386 ABI said that the stack pointer need be only 4-byte
56ceef08daSJosef 'Jeff' Sipek * aligned before a function call (STACK_ALIGN == 4). The ABI supplement
57ceef08daSJosef 'Jeff' Sipek * version 1.0 changed the required alignment to 16-byte for the benefit of
58ceef08daSJosef 'Jeff' Sipek * floating point code compiled using sse2. The compiler assumes this
59ceef08daSJosef 'Jeff' Sipek * alignment and maintains it for calls it generates. If the stack is
60ceef08daSJosef 'Jeff' Sipek * initially properly aligned, it will continue to be so aligned. If it is
61ceef08daSJosef 'Jeff' Sipek * not initially so aligned, it will never become so aligned.
62ceef08daSJosef 'Jeff' Sipek *
63ceef08daSJosef 'Jeff' Sipek * One slightly confusing detail to keep in mind is that the 16-byte
64ceef08daSJosef 'Jeff' Sipek * alignment (%esp & 0xf == 0) is true just *before* the call instruction.
65ceef08daSJosef 'Jeff' Sipek * The call instruction will then push a return value, decrementing %esp by
66ceef08daSJosef 'Jeff' Sipek * 4. Therefore, if one dumps %esp at the at the very first instruction in
67ceef08daSJosef 'Jeff' Sipek * a function, it will end with a 0xc. The compiler expects this and
68ceef08daSJosef 'Jeff' Sipek * compensates for it properly.
69ceef08daSJosef 'Jeff' Sipek *
70ceef08daSJosef 'Jeff' Sipek * Note: If you change this value, you need to change it in the following
71ceef08daSJosef 'Jeff' Sipek * files as well:
72ceef08daSJosef 'Jeff' Sipek *
73ceef08daSJosef 'Jeff' Sipek * - lib/libc/i386/threads/machdep.c
743e76f9d6SRichard Lowe * - lib/crt/i386/crti.s
753e76f9d6SRichard Lowe * - lib/crt/i386/crt1.s
76ceef08daSJosef 'Jeff' Sipek */
77ceef08daSJosef 'Jeff' Sipek #undef STACK_ALIGN
78ceef08daSJosef 'Jeff' Sipek #define STACK_ALIGN 16
79ceef08daSJosef 'Jeff' Sipek
807c478bd9Sstevel@tonic-gate static void resumecontext(void);
817c478bd9Sstevel@tonic-gate
827c478bd9Sstevel@tonic-gate void
makecontext(ucontext_t * ucp,void (* func)(),int argc,...)837c478bd9Sstevel@tonic-gate makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
847c478bd9Sstevel@tonic-gate {
857c478bd9Sstevel@tonic-gate long *sp;
867c478bd9Sstevel@tonic-gate long *tsp;
877c478bd9Sstevel@tonic-gate va_list ap;
887c478bd9Sstevel@tonic-gate size_t size;
897c478bd9Sstevel@tonic-gate
907c478bd9Sstevel@tonic-gate ucp->uc_mcontext.gregs[EIP] = (greg_t)func;
917c478bd9Sstevel@tonic-gate
927c478bd9Sstevel@tonic-gate size = sizeof (long) * (argc + 1);
937c478bd9Sstevel@tonic-gate
94ceef08daSJosef 'Jeff' Sipek tsp = (long *)(((uintptr_t)ucp->uc_stack.ss_sp +
957c478bd9Sstevel@tonic-gate ucp->uc_stack.ss_size - size) & ~(STACK_ALIGN - 1));
967c478bd9Sstevel@tonic-gate
97ceef08daSJosef 'Jeff' Sipek /*
98ceef08daSJosef 'Jeff' Sipek * Since we're emulating the call instruction, we must push the
99ceef08daSJosef 'Jeff' Sipek * return address (which involves adjusting the stack pointer to
100ceef08daSJosef 'Jeff' Sipek * have the proper 4-byte bias).
101ceef08daSJosef 'Jeff' Sipek */
102ceef08daSJosef 'Jeff' Sipek sp = tsp - 1;
1037c478bd9Sstevel@tonic-gate
1047c478bd9Sstevel@tonic-gate *sp = (long)resumecontext; /* return address */
1057c478bd9Sstevel@tonic-gate
1067c478bd9Sstevel@tonic-gate ucp->uc_mcontext.gregs[UESP] = (greg_t)sp;
107ceef08daSJosef 'Jeff' Sipek
108ceef08daSJosef 'Jeff' Sipek /*
109ceef08daSJosef 'Jeff' Sipek * "push" all the arguments
110ceef08daSJosef 'Jeff' Sipek */
111ceef08daSJosef 'Jeff' Sipek va_start(ap, argc);
112ceef08daSJosef 'Jeff' Sipek while (argc-- > 0)
113ceef08daSJosef 'Jeff' Sipek *tsp++ = va_arg(ap, long);
114ceef08daSJosef 'Jeff' Sipek va_end(ap);
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate
1177c478bd9Sstevel@tonic-gate
1187c478bd9Sstevel@tonic-gate static void
resumecontext(void)1197c478bd9Sstevel@tonic-gate resumecontext(void)
1207c478bd9Sstevel@tonic-gate {
1217c478bd9Sstevel@tonic-gate ucontext_t uc;
1227c478bd9Sstevel@tonic-gate
1237c478bd9Sstevel@tonic-gate (void) getcontext(&uc);
1247c478bd9Sstevel@tonic-gate (void) setcontext(uc.uc_link);
1257c478bd9Sstevel@tonic-gate }
126*ed093b41SRobert Mustacchi
127*ed093b41SRobert Mustacchi /*
128*ed093b41SRobert Mustacchi * This is the ISA-specific allocation logic for allocating and setting up an
129*ed093b41SRobert Mustacchi * extended ucontext_t. In particular, we need to allocate and add space for the
130*ed093b41SRobert Mustacchi * UC_XSAVE member if we have the appropriate hardware support. The i386 /
131*ed093b41SRobert Mustacchi * amd64 versions could be consolidated in a single x86 impl, but we don't have
132*ed093b41SRobert Mustacchi * that yet.
133*ed093b41SRobert Mustacchi */
134*ed093b41SRobert Mustacchi ucontext_t *
ucontext_alloc(uint32_t flags)135*ed093b41SRobert Mustacchi ucontext_alloc(uint32_t flags)
136*ed093b41SRobert Mustacchi {
137*ed093b41SRobert Mustacchi boolean_t do_xsave = B_FALSE;
138*ed093b41SRobert Mustacchi size_t to_alloc = sizeof (ucontext_t);
139*ed093b41SRobert Mustacchi ucontext_t *ucp;
140*ed093b41SRobert Mustacchi
141*ed093b41SRobert Mustacchi if (flags != 0) {
142*ed093b41SRobert Mustacchi errno = EINVAL;
143*ed093b41SRobert Mustacchi return (NULL);
144*ed093b41SRobert Mustacchi }
145*ed093b41SRobert Mustacchi
146*ed093b41SRobert Mustacchi /*
147*ed093b41SRobert Mustacchi * The AT_SUN_FPTYPE value is used as an approximation for the size of
148*ed093b41SRobert Mustacchi * the uc_xsave structure that we need additional space for. Ideally we
149*ed093b41SRobert Mustacchi * should ask the kernel how much space we actually need and only
150*ed093b41SRobert Mustacchi * allocate that much. Because the uc_xsave member does not need to
151*ed093b41SRobert Mustacchi * include the 512-byte XMM structure or the full xsave header, this
152*ed093b41SRobert Mustacchi * will work in the interim.
153*ed093b41SRobert Mustacchi *
154*ed093b41SRobert Mustacchi * Currently the system doesn't support dynamically enabling FPU
155*ed093b41SRobert Mustacchi * features with the Intel xfd (extended feature disable) MSR. When we
156*ed093b41SRobert Mustacchi * have support for that we'll need to redo this and ask the kernel for
157*ed093b41SRobert Mustacchi * the right size. We will probably want to cache the size for rtld as
158*ed093b41SRobert Mustacchi * well. For more information see uts/intel/os/fpu.c's big theory
159*ed093b41SRobert Mustacchi * statement.
160*ed093b41SRobert Mustacchi */
161*ed093b41SRobert Mustacchi switch (___getauxval(AT_SUN_FPTYPE)) {
162*ed093b41SRobert Mustacchi case AT_386_FPINFO_XSAVE:
163*ed093b41SRobert Mustacchi case AT_386_FPINFO_XSAVE_AMD:
164*ed093b41SRobert Mustacchi do_xsave = B_TRUE;
165*ed093b41SRobert Mustacchi to_alloc += ___getauxval(AT_SUN_FPSIZE);
166*ed093b41SRobert Mustacchi break;
167*ed093b41SRobert Mustacchi default:
168*ed093b41SRobert Mustacchi break;
169*ed093b41SRobert Mustacchi }
170*ed093b41SRobert Mustacchi
171*ed093b41SRobert Mustacchi ucp = calloc(1, to_alloc);
172*ed093b41SRobert Mustacchi if (ucp == NULL) {
173*ed093b41SRobert Mustacchi return (NULL);
174*ed093b41SRobert Mustacchi }
175*ed093b41SRobert Mustacchi
176*ed093b41SRobert Mustacchi if (do_xsave) {
177*ed093b41SRobert Mustacchi uintptr_t addr = (uintptr_t)ucp;
178*ed093b41SRobert Mustacchi ucp->uc_xsave = addr + sizeof (ucontext_t);
179*ed093b41SRobert Mustacchi }
180*ed093b41SRobert Mustacchi
181*ed093b41SRobert Mustacchi return (ucp);
182*ed093b41SRobert Mustacchi }
183*ed093b41SRobert Mustacchi
184*ed093b41SRobert Mustacchi void
ucontext_free(ucontext_t * ucp)185*ed093b41SRobert Mustacchi ucontext_free(ucontext_t *ucp)
186*ed093b41SRobert Mustacchi {
187*ed093b41SRobert Mustacchi free(ucp);
188*ed093b41SRobert Mustacchi }
189