xref: /illumos-gate/usr/src/lib/libc/i386/gen/makectxt.c (revision ed093b41a93e8563e6e1e5dae0768dda2a7bcc27)
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