]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libkse/arch/powerpc/include/pthread_md.h
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libkse / arch / powerpc / include / pthread_md.h
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$
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
42 extern void _ppc32_enter_uts(struct kse_mailbox *, kse_func_t, void *, size_t);
43 extern int  _ppc32_setcontext(mcontext_t *, intptr_t, intptr_t *);
44 extern 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
54 struct kcb;
55 struct kse;
56 struct pthread;
57 struct tcb;
58
59 /*
60  * %r2 points to the following.
61  */
62 struct ppc32_tp {
63         void            *tp_dtv;        /* dynamic thread vector */
64         uint32_t        _reserved_;
65         double          tp_tls[0];      /* static TLS */
66 };
67
68 struct tcb {
69         struct kse_thr_mailbox  tcb_tmbx;
70         struct pthread          *tcb_thread;
71         struct kcb              *tcb_curkcb;
72         long                    tcb_isfake;
73         long                    tcb_spare[3];
74         struct ppc32_tp         tcb_tp;
75 };
76
77 struct kcb {
78         struct kse_mailbox      kcb_kmbx;
79         struct kse              *kcb_kse;
80         struct tcb              *kcb_curtcb;
81         struct tcb              kcb_faketcb;
82 };
83
84 /*
85  * From the PowerPC32 TLS spec:
86  *
87  * "r2 is the thread pointer, and points 0x7000 past the end of the
88  * thread control block." Or, 0x7008 past the start of the 8-byte tcb
89  */
90 #define TP_OFFSET       0x7008
91
92 static __inline char *
93 ppc_get_tp(void)
94 {
95         register char *r2 __asm__("%r2");
96
97         return (r2 - TP_OFFSET);
98 }
99
100 static __inline void
101 ppc_set_tp(char *tp)
102 {
103         register char *r2 __asm__("%r2");
104         __asm __volatile("mr %0,%1" : "=r"(r2) : "r"(tp + TP_OFFSET));
105 }
106
107 static __inline struct tcb *
108 ppc_get_tcb(void)
109 {
110         return ((struct tcb *)(ppc_get_tp() - offsetof(struct tcb, tcb_tp)));
111 }
112
113 static __inline void
114 ppc_set_tcb(struct tcb *tcb)
115 {
116         ppc_set_tp((char*)&tcb->tcb_tp);
117 }
118
119 /*
120  * The kcb and tcb constructors.
121  */
122 struct tcb      *_tcb_ctor(struct pthread *, int);
123 void            _tcb_dtor(struct tcb *);
124 struct kcb      *_kcb_ctor(struct kse *kse);
125 void            _kcb_dtor(struct kcb *);
126
127 /* Called from the KSE to set its private data. */
128 static __inline void
129 _kcb_set(struct kcb *kcb)
130 {
131         /* There is no thread yet; use the fake tcb. */
132         ppc_set_tcb(&kcb->kcb_faketcb);
133 }
134
135 /*
136  * Get the current kcb.
137  *
138  * This can only be called while in a critical region; don't
139  * worry about having the kcb changed out from under us.
140  */
141 static __inline struct kcb *
142 _kcb_get(void)
143 {
144         return (ppc_get_tcb()->tcb_curkcb);
145 }
146
147 /*
148  * Enter a critical region.
149  *
150  * Read and clear km_curthread in the kse mailbox.
151  */
152 static __inline struct kse_thr_mailbox *
153 _kcb_critical_enter(void)
154 {
155         struct kse_thr_mailbox *crit;
156         struct tcb *tcb;
157         uint32_t flags;
158
159         tcb = ppc_get_tcb();
160         if (tcb->tcb_isfake != 0) {
161                 /*
162                  * We already are in a critical region since
163                  * there is no current thread.
164                  */
165                 crit = NULL;
166         } else {
167                 flags = tcb->tcb_tmbx.tm_flags;
168                 tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
169                 crit = tcb->tcb_curkcb->kcb_kmbx.km_curthread;
170                 tcb->tcb_curkcb->kcb_kmbx.km_curthread = NULL;
171                 tcb->tcb_tmbx.tm_flags = flags;
172         }
173         return (crit);
174 }
175
176 static __inline void
177 _kcb_critical_leave(struct kse_thr_mailbox *crit)
178 {
179         struct tcb *tcb;
180
181         tcb = ppc_get_tcb();
182
183         /* No need to do anything if this is a fake tcb. */
184         if (tcb->tcb_isfake == 0)
185                 tcb->tcb_curkcb->kcb_kmbx.km_curthread = crit;
186 }
187
188 static __inline int
189 _kcb_in_critical(void)
190 {
191         struct tcb *tcb;
192         uint32_t flags;
193         int ret;
194
195         tcb = ppc_get_tcb();
196         if (tcb->tcb_isfake != 0) {
197                 /*
198                  * We are in a critical region since there is no
199                  * current thread.
200                  */
201                 ret = 1;
202         } else {
203                 flags = tcb->tcb_tmbx.tm_flags;
204                 tcb->tcb_tmbx.tm_flags |= TMF_NOUPCALL;
205                 ret = (tcb->tcb_curkcb->kcb_kmbx.km_curthread == NULL);
206                 tcb->tcb_tmbx.tm_flags = flags;
207         }
208         return (ret);
209 }
210
211 static __inline void
212 _tcb_set(struct kcb *kcb, struct tcb *tcb)
213 {
214         if (tcb == NULL)
215                 tcb = &kcb->kcb_faketcb;
216         kcb->kcb_curtcb = tcb;
217         tcb->tcb_curkcb = kcb;
218         ppc_set_tcb(tcb);
219 }
220
221 static __inline struct tcb *
222 _tcb_get(void)
223 {
224         return (ppc_get_tcb());
225 }
226
227 static __inline struct pthread *
228 _get_curthread(void)
229 {
230         return (ppc_get_tcb()->tcb_thread);
231 }
232
233 /*
234  * Get the current kse.
235  *
236  * Like _kcb_get(), this can only be called while in a critical region.
237  */
238 static __inline struct kse *
239 _get_curkse(void)
240 {
241         return (ppc_get_tcb()->tcb_curkcb->kcb_kse);
242 }
243
244 static __inline int
245 _thread_enter_uts(struct tcb *tcb, struct kcb *kcb)
246 {
247         if (_ppc32_getcontext(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) {
248                 /* Make the fake tcb the current thread. */
249                 kcb->kcb_curtcb = &kcb->kcb_faketcb;
250                 ppc_set_tcb(&kcb->kcb_faketcb);
251                 _ppc32_enter_uts(&kcb->kcb_kmbx, kcb->kcb_kmbx.km_func,
252                     kcb->kcb_kmbx.km_stack.ss_sp,
253                     kcb->kcb_kmbx.km_stack.ss_size - 32);
254                 /* We should not reach here. */
255                 return (-1);
256         }
257         return (0);
258 }
259
260 static __inline int
261 _thread_switch(struct kcb *kcb, struct tcb *tcb, int setmbox)
262 {
263         mcontext_t *mc;
264         extern int _libkse_debug;
265
266         _tcb_set(kcb, tcb);
267         mc = &tcb->tcb_tmbx.tm_context.uc_mcontext;
268
269         /*
270          * A full context needs a system call to restore, so use
271          * kse_switchin. Otherwise, the partial context can be
272          * restored with _ppc32_setcontext
273          */
274         if (mc->mc_vers != _MC_VERSION_KSE && _libkse_debug != 0) {
275                 if (setmbox)
276                         kse_switchin(&tcb->tcb_tmbx, KSE_SWITCHIN_SETTMBX);
277                 else
278                         kse_switchin(&tcb->tcb_tmbx, 0);
279         } else {
280                 tcb->tcb_tmbx.tm_lwp = kcb->kcb_kmbx.km_lwp;
281                 if (setmbox)
282                         _ppc32_setcontext(mc, (intptr_t)&tcb->tcb_tmbx,
283                             (intptr_t *)(void *)&kcb->kcb_kmbx.km_curthread);
284                 else
285                         _ppc32_setcontext(mc, 0, NULL);
286         }
287
288         /* We should not reach here. */
289         return (-1);
290 }
291
292 #endif /* _PTHREAD_MD_H_ */