1132400Sgrehan/*
2161802Smarcel * Copyright 2004 by Peter Grehan.
3161802Smarcel * Copyright 2006 Marcel Moolenaar
4161802Smarcel * All rights reserved.
5132400Sgrehan *
6132400Sgrehan * Redistribution and use in source and binary forms, with or without
7132400Sgrehan * modification, are permitted provided that the following conditions
8132400Sgrehan * are met:
9132400Sgrehan * 1. Redistributions of source code must retain the above copyright
10132400Sgrehan *    notice, this list of conditions and the following disclaimer.
11132400Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
12132400Sgrehan *    notice, this list of conditions and the following disclaimer in the
13132400Sgrehan *    documentation and/or other materials provided with the distribution.
14132400Sgrehan * 3. The name of the author may not be used to endorse or promote products
15132400Sgrehan *    derived from this software without specific prior written permission.
16132400Sgrehan *
17132400Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18132400Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19132400Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20132400Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21132400Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22132400Sgrehan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23132400Sgrehan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24132400Sgrehan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25132400Sgrehan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26132400Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27132400Sgrehan * SUCH DAMAGE.
28132400Sgrehan *
29132400Sgrehan * $FreeBSD: releng/10.2/lib/libkse/arch/powerpc/include/pthread_md.h 174127 2007-12-01 14:23:29Z rwatson $
30132400Sgrehan */
31132400Sgrehan
32132400Sgrehan/*
33132400Sgrehan * Machine-dependent thread prototypes/definitions for the thread kernel.
34132400Sgrehan */
35132400Sgrehan#ifndef _PTHREAD_MD_H_
36132400Sgrehan#define	_PTHREAD_MD_H_
37132400Sgrehan
38132400Sgrehan#include <sys/kse.h>
39132400Sgrehan#include <stddef.h>
40132400Sgrehan#include <ucontext.h>
41132400Sgrehan
42132400Sgrehanextern void _ppc32_enter_uts(struct kse_mailbox *, kse_func_t, void *, size_t);
43132400Sgrehanextern int  _ppc32_setcontext(mcontext_t *, intptr_t, intptr_t *);
44132400Sgrehanextern int  _ppc32_getcontext(mcontext_t *);
45132400Sgrehan
46132400Sgrehan#define	KSE_STACKSIZE		16384
47161802Smarcel#define	DTV_OFFSET		offsetof(struct tcb, tcb_tp.tp_dtv)
48132400Sgrehan
49132400Sgrehan#define	THR_GETCONTEXT(ucp)	_ppc32_getcontext(&(ucp)->uc_mcontext)
50132400Sgrehan#define	THR_SETCONTEXT(ucp)	_ppc32_setcontext(&(ucp)->uc_mcontext, 0, NULL)
51132400Sgrehan
52132400Sgrehan#define	PER_THREAD
53132400Sgrehan
54132400Sgrehanstruct kcb;
55132400Sgrehanstruct kse;
56132400Sgrehanstruct pthread;
57132400Sgrehanstruct tcb;
58132400Sgrehan
59132400Sgrehan/*
60161828Smarcel * %r2 points to the following.
61132400Sgrehan */
62132400Sgrehanstruct ppc32_tp {
63161802Smarcel	void		*tp_dtv;	/* dynamic thread vector */
64132400Sgrehan	uint32_t	_reserved_;
65161802Smarcel	double		tp_tls[0];	/* static TLS */
66132400Sgrehan};
67132400Sgrehan
68132400Sgrehanstruct tcb {
69132400Sgrehan	struct kse_thr_mailbox	tcb_tmbx;
70132400Sgrehan	struct pthread		*tcb_thread;
71132400Sgrehan	struct kcb		*tcb_curkcb;
72132400Sgrehan	long			tcb_isfake;
73161830Smarcel	long			tcb_spare[3];
74132400Sgrehan	struct ppc32_tp		tcb_tp;
75132400Sgrehan};
76132400Sgrehan
77132400Sgrehanstruct kcb {
78132400Sgrehan	struct kse_mailbox	kcb_kmbx;
79161802Smarcel	struct kse		*kcb_kse;
80161802Smarcel	struct tcb		*kcb_curtcb;
81132400Sgrehan	struct tcb		kcb_faketcb;
82132400Sgrehan};
83132400Sgrehan
84132400Sgrehan/*
85132400Sgrehan * From the PowerPC32 TLS spec:
86132400Sgrehan *
87132400Sgrehan * "r2 is the thread pointer, and points 0x7000 past the end of the
88132400Sgrehan * thread control block." Or, 0x7008 past the start of the 8-byte tcb
89132400Sgrehan */
90132400Sgrehan#define TP_OFFSET	0x7008
91132400Sgrehan
92161828Smarcelstatic __inline char *
93174127Srwatsonppc_get_tp(void)
94161828Smarcel{
95161828Smarcel	register char *r2 __asm__("%r2");
96132400Sgrehan
97161828Smarcel	return (r2 - TP_OFFSET);
98161828Smarcel}
99161828Smarcel
100161828Smarcelstatic __inline void
101161828Smarcelppc_set_tp(char *tp)
102161828Smarcel{
103161828Smarcel	register char *r2 __asm__("%r2");
104161828Smarcel	__asm __volatile("mr %0,%1" : "=r"(r2) : "r"(tp + TP_OFFSET));
105161828Smarcel}
106161828Smarcel
107161828Smarcelstatic __inline struct tcb *
108174127Srwatsonppc_get_tcb(void)
109161828Smarcel{
110161828Smarcel	return ((struct tcb *)(ppc_get_tp() - offsetof(struct tcb, tcb_tp)));
111161828Smarcel}
112161828Smarcel
113161828Smarcelstatic __inline void
114161828Smarcelppc_set_tcb(struct tcb *tcb)
115161828Smarcel{
116161828Smarcel	ppc_set_tp((char*)&tcb->tcb_tp);
117161828Smarcel}
118161828Smarcel
119132400Sgrehan/*
120132400Sgrehan * The kcb and tcb constructors.
121132400Sgrehan */
122133806Sgrehanstruct tcb	*_tcb_ctor(struct pthread *, int);
123132400Sgrehanvoid		_tcb_dtor(struct tcb *);
124132400Sgrehanstruct kcb	*_kcb_ctor(struct kse *kse);
125132400Sgrehanvoid		_kcb_dtor(struct kcb *);
126132400Sgrehan
127132400Sgrehan/* Called from the KSE to set its private data. */
128132400Sgrehanstatic __inline void
129132400Sgrehan_kcb_set(struct kcb *kcb)
130132400Sgrehan{
131132400Sgrehan	/* There is no thread yet; use the fake tcb. */
132161828Smarcel	ppc_set_tcb(&kcb->kcb_faketcb);
133132400Sgrehan}
134132400Sgrehan
135132400Sgrehan/*
136132400Sgrehan * Get the current kcb.
137132400Sgrehan *
138132400Sgrehan * This can only be called while in a critical region; don't
139132400Sgrehan * worry about having the kcb changed out from under us.
140132400Sgrehan */
141132400Sgrehanstatic __inline struct kcb *
142132400Sgrehan_kcb_get(void)
143132400Sgrehan{
144161828Smarcel	return (ppc_get_tcb()->tcb_curkcb);
145132400Sgrehan}
146132400Sgrehan
147132400Sgrehan/*
148132400Sgrehan * Enter a critical region.
149132400Sgrehan *
150132400Sgrehan * Read and clear km_curthread in the kse mailbox.
151132400Sgrehan */
152132400Sgrehanstatic __inline struct kse_thr_mailbox *
153132400Sgrehan_kcb_critical_enter(void)
154132400Sgrehan{
155132400Sgrehan	struct kse_thr_mailbox *crit;
156161828Smarcel	struct tcb *tcb;
157132400Sgrehan	uint32_t flags;
158132400Sgrehan
159161828Smarcel	tcb = ppc_get_tcb();
160161828Smarcel	if (tcb->tcb_isfake != 0) {
161132400Sgrehan		/*
162132400Sgrehan		 * We already are in a critical region since
163132400Sgrehan		 * there is no current thread.
164132400Sgrehan		 */
165132400Sgrehan		crit = NULL;
166132400Sgrehan	} else {
167161828Smarcel		flags = tcb->tcb_tmbx.tm_flags;
168161828Smarcel		tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
169161828Smarcel		crit = tcb->tcb_curkcb->kcb_kmbx.km_curthread;
170161828Smarcel		tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL;
171161828Smarcel		tcb->tcb_tmbx.tm_flags = flags;
172132400Sgrehan	}
173132400Sgrehan	return (crit);
174132400Sgrehan}
175132400Sgrehan
176132400Sgrehanstatic __inline void
177132400Sgrehan_kcb_critical_leave(struct kse_thr_mailbox *crit)
178132400Sgrehan{
179161828Smarcel	struct tcb *tcb;
180161828Smarcel
181161828Smarcel	tcb = ppc_get_tcb();
182161828Smarcel
183161828Smarcel	/* No need to do anything if this is a fake tcb. */
184161828Smarcel	if (tcb->tcb_isfake == 0)
185161828Smarcel		tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit;
186132400Sgrehan}
187132400Sgrehan
188132400Sgrehanstatic __inline int
189132400Sgrehan_kcb_in_critical(void)
190132400Sgrehan{
191161828Smarcel	struct tcb *tcb;
192132400Sgrehan	uint32_t flags;
193132400Sgrehan	int ret;
194132400Sgrehan
195161828Smarcel	tcb = ppc_get_tcb();
196161828Smarcel	if (tcb->tcb_isfake != 0) {
197132400Sgrehan		/*
198132400Sgrehan		 * We are in a critical region since there is no
199132400Sgrehan		 * current thread.
200132400Sgrehan		 */
201132400Sgrehan		ret = 1;
202132400Sgrehan	} else {
203161828Smarcel		flags = tcb->tcb_tmbx.tm_flags;
204161828Smarcel		tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
205161828Smarcel		ret = (tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL);
206161828Smarcel		tcb->tcb_tmbx.tm_flags = flags;
207132400Sgrehan	}
208132400Sgrehan	return (ret);
209132400Sgrehan}
210132400Sgrehan
211132400Sgrehanstatic __inline void
212132400Sgrehan_tcb_set(struct kcb *kcb, struct tcb *tcb)
213132400Sgrehan{
214132400Sgrehan        if (tcb == NULL)
215132400Sgrehan                tcb = &kcb->kcb_faketcb;
216132400Sgrehan        kcb->kcb_curtcb = tcb;
217132400Sgrehan        tcb->tcb_curkcb = kcb;
218161828Smarcel	ppc_set_tcb(tcb);
219132400Sgrehan}
220132400Sgrehan
221132400Sgrehanstatic __inline struct tcb *
222132400Sgrehan_tcb_get(void)
223132400Sgrehan{
224161828Smarcel	return (ppc_get_tcb());
225132400Sgrehan}
226132400Sgrehan
227132400Sgrehanstatic __inline struct pthread *
228132400Sgrehan_get_curthread(void)
229132400Sgrehan{
230161828Smarcel	return (ppc_get_tcb()->tcb_thread);
231132400Sgrehan}
232132400Sgrehan
233132400Sgrehan/*
234132400Sgrehan * Get the current kse.
235132400Sgrehan *
236132400Sgrehan * Like _kcb_get(), this can only be called while in a critical region.
237132400Sgrehan */
238132400Sgrehanstatic __inline struct kse *
239132400Sgrehan_get_curkse(void)
240132400Sgrehan{
241161828Smarcel	return (ppc_get_tcb()->tcb_curkcb->kcb_kse);
242132400Sgrehan}
243132400Sgrehan
244132400Sgrehanstatic __inline int
245132400Sgrehan_thread_enter_uts(struct tcb *tcb, struct kcb *kcb)
246132400Sgrehan{
247132400Sgrehan	if (_ppc32_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) {
248132400Sgrehan		/* Make the fake tcb the current thread. */
249132400Sgrehan		kcb->kcb_curtcb = &kcb->kcb_faketcb;
250161828Smarcel		ppc_set_tcb(&kcb->kcb_faketcb);
251132400Sgrehan		_ppc32_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func,
252132400Sgrehan		    kcb->kcb_kmbx.km_stack.ss_sp,
253132400Sgrehan		    kcb->kcb_kmbx.km_stack.ss_size - 32);
254132400Sgrehan		/* We should not reach here. */
255132400Sgrehan		return (-1);
256132400Sgrehan	}
257132400Sgrehan	return (0);
258132400Sgrehan}
259132400Sgrehan
260132400Sgrehanstatic __inline int
261132400Sgrehan_thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox)
262132400Sgrehan{
263132400Sgrehan	mcontext_t *mc;
264132400Sgrehan	extern int _libkse_debug;
265132400Sgrehan
266132400Sgrehan	_tcb_set(kcb, tcb);
267132400Sgrehan	mc = &tcb->tcb_tmbx.tm_context.uc_mcontext;
268132400Sgrehan
269132400Sgrehan	/*
270132400Sgrehan	 * A full context needs a system call to restore, so use
271132400Sgrehan	 * kse_switchin. Otherwise, the partial context can be
272132400Sgrehan	 * restored with _ppc32_setcontext
273132400Sgrehan	 */
274132400Sgrehan	if (mc->mc_vers != _MC_VERSION_KSE && _libkse_debug != 0) {
275132400Sgrehan		if (setmbox)
276132400Sgrehan			kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX);
277161828Smarcel		else
278161828Smarcel			kse_switchin(&tcb->tcb_tmbx, 0);
279132400Sgrehan	} else {
280132400Sgrehan		tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp;
281132400Sgrehan		if (setmbox)
282132400Sgrehan			_ppc32_setcontext(mc, (intptr_t)&tcb->tcb_tmbx,
283174112Sdeischen			    (intptr_t *)(void *)&kcb->kcb_kmbx.km_curthread);
284132400Sgrehan		else
285132400Sgrehan			_ppc32_setcontext(mc, 0, NULL);
286132400Sgrehan	}
287132400Sgrehan
288132400Sgrehan	/* We should not reach here. */
289132400Sgrehan	return (-1);
290132400Sgrehan}
291132400Sgrehan
292132400Sgrehan#endif /* _PTHREAD_MD_H_ */
293