pthread_md.h revision 161828
1/*
2 * Copyright 2004 by Peter Grehan.
3 * Copyright 2006 Marcel Moolenaar
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/lib/libkse/arch/powerpc/include/pthread_md.h 161828 2006-09-01 17:52:13Z marcel $
30 */
31
32/*
33 * Machine-dependent thread prototypes/definitions for the thread kernel.
34 */
35#ifndef _PTHREAD_MD_H_
36#define	_PTHREAD_MD_H_
37
38#include <sys/kse.h>
39#include <stddef.h>
40#include <ucontext.h>
41
42extern void _ppc32_enter_uts(struct kse_mailbox *, kse_func_t, void *, size_t);
43extern int  _ppc32_setcontext(mcontext_t *, intptr_t, intptr_t *);
44extern int  _ppc32_getcontext(mcontext_t *);
45
46#define	KSE_STACKSIZE		16384
47#define	DTV_OFFSET		offsetof(struct tcb, tcb_tp.tp_dtv)
48
49#define	THR_GETCONTEXT(ucp)	_ppc32_getcontext(&(ucp)->uc_mcontext)
50#define	THR_SETCONTEXT(ucp)	_ppc32_setcontext(&(ucp)->uc_mcontext, 0, NULL)
51
52#define	PER_THREAD
53
54struct kcb;
55struct kse;
56struct pthread;
57struct tcb;
58
59/*
60 * %r2 points to the following.
61 */
62struct ppc32_tp {
63	void		*tp_dtv;	/* dynamic thread vector */
64	uint32_t	_reserved_;
65	double		tp_tls[0];	/* static TLS */
66};
67
68struct tcb {
69	struct kse_thr_mailbox	tcb_tmbx;
70	struct pthread		*tcb_thread;
71	struct kcb		*tcb_curkcb;
72	long			tcb_isfake;
73	struct ppc32_tp		tcb_tp;
74};
75
76struct kcb {
77	struct kse_mailbox	kcb_kmbx;
78	struct kse		*kcb_kse;
79	struct tcb		*kcb_curtcb;
80	struct tcb		kcb_faketcb;
81};
82
83/*
84 * From the PowerPC32 TLS spec:
85 *
86 * "r2 is the thread pointer, and points 0x7000 past the end of the
87 * thread control block." Or, 0x7008 past the start of the 8-byte tcb
88 */
89#define TP_OFFSET	0x7008
90
91static __inline char *
92ppc_get_tp()
93{
94	register char *r2 __asm__("%r2");
95
96	return (r2 - TP_OFFSET);
97}
98
99static __inline void
100ppc_set_tp(char *tp)
101{
102	register char *r2 __asm__("%r2");
103	__asm __volatile("mr %0,%1" : "=r"(r2) : "r"(tp + TP_OFFSET));
104}
105
106static __inline struct tcb *
107ppc_get_tcb()
108{
109	return ((struct tcb *)(ppc_get_tp() - offsetof(struct tcb, tcb_tp)));
110}
111
112static __inline void
113ppc_set_tcb(struct tcb *tcb)
114{
115	ppc_set_tp((char*)&tcb->tcb_tp);
116}
117
118/*
119 * The kcb and tcb constructors.
120 */
121struct tcb	*_tcb_ctor(struct pthread *, int);
122void		_tcb_dtor(struct tcb *);
123struct kcb	*_kcb_ctor(struct kse *kse);
124void		_kcb_dtor(struct kcb *);
125
126/* Called from the KSE to set its private data. */
127static __inline void
128_kcb_set(struct kcb *kcb)
129{
130	/* There is no thread yet; use the fake tcb. */
131	ppc_set_tcb(&kcb->kcb_faketcb);
132}
133
134/*
135 * Get the current kcb.
136 *
137 * This can only be called while in a critical region; don't
138 * worry about having the kcb changed out from under us.
139 */
140static __inline struct kcb *
141_kcb_get(void)
142{
143	return (ppc_get_tcb()->tcb_curkcb);
144}
145
146/*
147 * Enter a critical region.
148 *
149 * Read and clear km_curthread in the kse mailbox.
150 */
151static __inline struct kse_thr_mailbox *
152_kcb_critical_enter(void)
153{
154	struct kse_thr_mailbox *crit;
155	struct tcb *tcb;
156	uint32_t flags;
157
158	tcb = ppc_get_tcb();
159	if (tcb->tcb_isfake != 0) {
160		/*
161		 * We already are in a critical region since
162		 * there is no current thread.
163		 */
164		crit = NULL;
165	} else {
166		flags = tcb->tcb_tmbx.tm_flags;
167		tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
168		crit = tcb->tcb_curkcb->kcb_kmbx.km_curthread;
169		tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL;
170		tcb->tcb_tmbx.tm_flags = flags;
171	}
172	return (crit);
173}
174
175static __inline void
176_kcb_critical_leave(struct kse_thr_mailbox *crit)
177{
178	struct tcb *tcb;
179
180	tcb = ppc_get_tcb();
181
182	/* No need to do anything if this is a fake tcb. */
183	if (tcb->tcb_isfake == 0)
184		tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit;
185}
186
187static __inline int
188_kcb_in_critical(void)
189{
190	struct tcb *tcb;
191	uint32_t flags;
192	int ret;
193
194	tcb = ppc_get_tcb();
195	if (tcb->tcb_isfake != 0) {
196		/*
197		 * We are in a critical region since there is no
198		 * current thread.
199		 */
200		ret = 1;
201	} else {
202		flags = tcb->tcb_tmbx.tm_flags;
203		tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
204		ret = (tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL);
205		tcb->tcb_tmbx.tm_flags = flags;
206	}
207	return (ret);
208}
209
210static __inline void
211_tcb_set(struct kcb *kcb, struct tcb *tcb)
212{
213        if (tcb == NULL)
214                tcb = &kcb->kcb_faketcb;
215        kcb->kcb_curtcb = tcb;
216        tcb->tcb_curkcb = kcb;
217	ppc_set_tcb(tcb);
218}
219
220static __inline struct tcb *
221_tcb_get(void)
222{
223	return (ppc_get_tcb());
224}
225
226static __inline struct pthread *
227_get_curthread(void)
228{
229	return (ppc_get_tcb()->tcb_thread);
230}
231
232/*
233 * Get the current kse.
234 *
235 * Like _kcb_get(), this can only be called while in a critical region.
236 */
237static __inline struct kse *
238_get_curkse(void)
239{
240	return (ppc_get_tcb()->tcb_curkcb->kcb_kse);
241}
242
243static __inline int
244_thread_enter_uts(struct tcb *tcb, struct kcb *kcb)
245{
246	if (_ppc32_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) {
247		/* Make the fake tcb the current thread. */
248		kcb->kcb_curtcb = &kcb->kcb_faketcb;
249		ppc_set_tcb(&kcb->kcb_faketcb);
250		_ppc32_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func,
251		    kcb->kcb_kmbx.km_stack.ss_sp,
252		    kcb->kcb_kmbx.km_stack.ss_size - 32);
253		/* We should not reach here. */
254		return (-1);
255	}
256	return (0);
257}
258
259static __inline int
260_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox)
261{
262	mcontext_t *mc;
263	extern int _libkse_debug;
264
265	_tcb_set(kcb, tcb);
266	mc = &tcb->tcb_tmbx.tm_context.uc_mcontext;
267
268	/*
269	 * A full context needs a system call to restore, so use
270	 * kse_switchin. Otherwise, the partial context can be
271	 * restored with _ppc32_setcontext
272	 */
273	if (mc->mc_vers != _MC_VERSION_KSE && _libkse_debug != 0) {
274		if (setmbox)
275			kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX);
276		else
277			kse_switchin(&tcb->tcb_tmbx, 0);
278	} else {
279		tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp;
280		if (setmbox)
281			_ppc32_setcontext(mc, (intptr_t)&tcb->tcb_tmbx,
282			    (intptr_t *)&kcb->kcb_kmbx.km_curthread);
283		else
284			_ppc32_setcontext(mc, 0, NULL);
285	}
286
287	/* We should not reach here. */
288	return (-1);
289}
290
291#endif /* _PTHREAD_MD_H_ */
292