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#pragma weak _makecontext = makecontext
31#pragma weak ___makecontext_v2 = __makecontext_v2
32
33#include "lint.h"
34#include <stdarg.h>
35#include <strings.h>
36/* Can't just use <ucontext.h> due to redefine_extname stuff. */
37#include <sys/ucontext.h>
38#include <sys/regset.h>
39#include <sys/stack.h>
40#include <sys/frame.h>
41
42/*
43 * The ucontext_t that the user passes in must have been primed with a
44 * call to getcontext(2), have the uc_stack member set to reflect the
45 * stack which this context will use, and have the uc_link member set
46 * to the context which should be resumed when this context returns.
47 * When makecontext() returns, the ucontext_t will be set to run the
48 * given function with the given parameters on the stack specified by
49 * uc_stack, and which will return to the ucontext_t specified by uc_link.
50 */
51
52static void resumecontext(void);
53
54void
55makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
56{
57	greg_t *reg;
58	long *tsp;
59	char *sp;
60	int argno;
61	va_list ap;
62	size_t size;
63
64	reg = ucp->uc_mcontext.gregs;
65	reg[REG_PC] = (greg_t)func;
66	reg[REG_nPC] = reg[REG_PC] + 0x4;
67
68	/*
69	 * Reserve enough space for a frame and the arguments beyond the
70	 * sixth; round to stack alignment.
71	 */
72	size = sizeof (struct frame);
73	size += (argc > 6 ? argc - 6 : 0) * sizeof (long);
74
75	/*
76	 * The legacy implemenation of makecontext() on sparc has been to
77	 * interpret the uc_stack.ss_sp member incorrectly as the top of the
78	 * stack rather than the base. We preserve this behavior here, but
79	 * provide the correct semantics in __makecontext_v2().
80	 */
81	sp = (char *)(((uintptr_t)ucp->uc_stack.ss_sp - size) &
82	    ~(STACK_ALIGN - 1));
83
84	/*
85	 * Copy all args to the stack, and put the first 6 args into the
86	 * ucontext_t. Zero the other fields of the frame.
87	 */
88	/* LINTED pointer cast may result in improper alignment */
89	tsp = &((struct frame *)sp)->fr_argd[0];
90	bzero(sp, sizeof (struct frame));
91
92	va_start(ap, argc);
93
94	for (argno = 0; argno < argc; argno++) {
95		if (argno < 6)
96			*tsp++ = reg[REG_O0 + argno] = va_arg(ap, long);
97		else
98			*tsp++ = va_arg(ap, long);
99	}
100
101	va_end(ap);
102
103	reg[REG_SP] = (greg_t)sp - STACK_BIAS;		/* sp (when done) */
104	reg[REG_O7] = (greg_t)resumecontext - 8;	/* return pc */
105}
106
107void
108__makecontext_v2(ucontext_t *ucp, void (*func)(), int argc, ...)
109{
110	greg_t *reg;
111	long *tsp;
112	char *sp;
113	int argno;
114	va_list ap;
115	size_t size;
116
117	reg = ucp->uc_mcontext.gregs;
118	reg[REG_PC] = (greg_t)func;
119	reg[REG_nPC] = reg[REG_PC] + 0x4;
120
121	/*
122	 * Reserve enough space for a frame and the arguments beyond the
123	 * sixth; round to stack alignment.
124	 */
125	size = sizeof (struct frame);
126	size += (argc > 6 ? argc - 6 : 0) * sizeof (long);
127
128	sp = (char *)(((uintptr_t)ucp->uc_stack.ss_sp +
129	    ucp->uc_stack.ss_size - size) & ~(STACK_ALIGN - 1));
130
131	/*
132	 * Copy all args to the stack, and put the first 6 args into the
133	 * ucontext_t. Zero the other fields of the frame.
134	 */
135	/* LINTED pointer cast may result in improper alignment */
136	tsp = &((struct frame *)sp)->fr_argd[0];
137	bzero(sp, sizeof (struct frame));
138
139	va_start(ap, argc);
140
141	for (argno = 0; argno < argc; argno++) {
142		if (argno < 6)
143			*tsp++ = reg[REG_O0 + argno] = va_arg(ap, long);
144		else
145			*tsp++ = va_arg(ap, long);
146	}
147
148	va_end(ap);
149
150	reg[REG_SP] = (greg_t)sp - STACK_BIAS;		/* sp (when done) */
151	reg[REG_O7] = (greg_t)resumecontext - 8;	/* return pc */
152}
153
154static void
155resumecontext(void)
156{
157	/*
158	 * We can't include ucontext.h (where these functions are defined)
159	 * because it remaps the symbol makecontext.
160	 */
161	extern int getcontext(ucontext_t *);
162	extern int setcontext(const ucontext_t *);
163	ucontext_t uc;
164
165	(void) getcontext(&uc);
166	(void) setcontext(uc.uc_link);
167}
168