pthread_md.h revision 161802
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 161802 2006-09-01 06:17:16Z 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 a struct kcb.
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
90register uint8_t *_tp __asm__("%r2");
91#define	PPC_SET_TP(x)	\
92	__asm __volatile("mr %0,%1" : "=r"(_tp) : "r"((char*)(x) + TP_OFFSET))
93
94#define _tcb  ((struct tcb *)(_tp - TP_OFFSET - offsetof(struct tcb, tcb_tp)))
95
96/*
97 * The kcb and tcb constructors.
98 */
99struct tcb	*_tcb_ctor(struct pthread *, int);
100void		_tcb_dtor(struct tcb *);
101struct kcb	*_kcb_ctor(struct kse *kse);
102void		_kcb_dtor(struct kcb *);
103
104/* Called from the KSE to set its private data. */
105static __inline void
106_kcb_set(struct kcb *kcb)
107{
108	/* There is no thread yet; use the fake tcb. */
109	PPC_SET_TP(&kcb->kcb_faketcb.tcb_tp);
110}
111
112/*
113 * Get the current kcb.
114 *
115 * This can only be called while in a critical region; don't
116 * worry about having the kcb changed out from under us.
117 */
118static __inline struct kcb *
119_kcb_get(void)
120{
121	return (_tcb->tcb_curkcb);
122}
123
124/*
125 * Enter a critical region.
126 *
127 * Read and clear km_curthread in the kse mailbox.
128 */
129static __inline struct kse_thr_mailbox *
130_kcb_critical_enter(void)
131{
132	struct kse_thr_mailbox *crit;
133	uint32_t flags;
134
135	if (_tcb->tcb_isfake != 0) {
136		/*
137		 * We already are in a critical region since
138		 * there is no current thread.
139		 */
140		crit = NULL;
141	} else {
142		flags = _tcb->tcb_tmbx.tm_flags;
143		_tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
144		crit = _tcb->tcb_curkcb->kcb_kmbx.km_curthread;
145		_tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL;
146		_tcb->tcb_tmbx.tm_flags = flags;
147	}
148	return (crit);
149}
150
151static __inline void
152_kcb_critical_leave(struct kse_thr_mailbox *crit)
153{
154        /* No need to do anything if this is a fake tcb. */
155        if (_tcb->tcb_isfake == 0)
156                _tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit;
157}
158
159static __inline int
160_kcb_in_critical(void)
161{
162	uint32_t flags;
163	int ret;
164
165	if (_tcb->tcb_isfake != 0) {
166		/*
167		 * We are in a critical region since there is no
168		 * current thread.
169		 */
170		ret = 1;
171	} else {
172		flags = _tcb->tcb_tmbx.tm_flags;
173		_tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
174		ret = (_tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL);
175		_tcb->tcb_tmbx.tm_flags = flags;
176	}
177	return (ret);
178}
179
180static __inline void
181_tcb_set(struct kcb *kcb, struct tcb *tcb)
182{
183        if (tcb == NULL)
184                tcb = &kcb->kcb_faketcb;
185        kcb->kcb_curtcb = tcb;
186        tcb->tcb_curkcb = kcb;
187	PPC_SET_TP(&tcb->tcb_tp);
188}
189
190static __inline struct tcb *
191_tcb_get(void)
192{
193	return (_tcb);
194}
195
196static __inline struct pthread *
197_get_curthread(void)
198{
199	return (_tcb->tcb_thread);
200}
201
202/*
203 * Get the current kse.
204 *
205 * Like _kcb_get(), this can only be called while in a critical region.
206 */
207static __inline struct kse *
208_get_curkse(void)
209{
210	return (_tcb->tcb_curkcb->kcb_kse);
211}
212
213static __inline int
214_thread_enter_uts(struct tcb *tcb, struct kcb *kcb)
215{
216	if (_ppc32_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) {
217		/* Make the fake tcb the current thread. */
218		kcb->kcb_curtcb = &kcb->kcb_faketcb;
219		PPC_SET_TP(&kcb->kcb_faketcb.tcb_tp);
220		_ppc32_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func,
221		    kcb->kcb_kmbx.km_stack.ss_sp,
222		    kcb->kcb_kmbx.km_stack.ss_size - 32);
223		/* We should not reach here. */
224		return (-1);
225	}
226	return (0);
227}
228
229static __inline int
230_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox)
231{
232	mcontext_t *mc;
233	extern int _libkse_debug;
234
235	_tcb_set(kcb, tcb);
236	mc = &tcb->tcb_tmbx.tm_context.uc_mcontext;
237
238	/*
239	 * A full context needs a system call to restore, so use
240	 * kse_switchin. Otherwise, the partial context can be
241	 * restored with _ppc32_setcontext
242	 */
243	if (mc->mc_vers != _MC_VERSION_KSE && _libkse_debug != 0) {
244		if (setmbox)
245			kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX);
246                else
247                        kse_switchin(&tcb->tcb_tmbx, 0);
248	} else {
249		tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp;
250		if (setmbox)
251			_ppc32_setcontext(mc, (intptr_t)&tcb->tcb_tmbx,
252			    (intptr_t *)&kcb->kcb_kmbx.km_curthread);
253		else
254			_ppc32_setcontext(mc, 0, NULL);
255	}
256
257	/* We should not reach here. */
258	return (-1);
259}
260
261#endif /* _PTHREAD_MD_H_ */
262