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