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 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 long tcb_spare[3]; 74 struct ppc32_tp tcb_tp; 75}; 76 77struct 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 92static __inline char * 93ppc_get_tp(void) 94{ 95 register char *r2 __asm__("%r2"); 96 97 return (r2 - TP_OFFSET); 98} 99 100static __inline void 101ppc_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 107static __inline struct tcb * 108ppc_get_tcb(void) 109{ 110 return ((struct tcb *)(ppc_get_tp() - offsetof(struct tcb, tcb_tp))); 111} 112 113static __inline void 114ppc_set_tcb(struct tcb *tcb) 115{ 116 ppc_set_tp((char*)&tcb->tcb_tp); 117} 118 119/* 120 * The kcb and tcb constructors. 121 */ 122struct tcb *_tcb_ctor(struct pthread *, int); 123void _tcb_dtor(struct tcb *); 124struct kcb *_kcb_ctor(struct kse *kse); 125void _kcb_dtor(struct kcb *); 126 127/* Called from the KSE to set its private data. */ 128static __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 */ 141static __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 */ 152static __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 176static __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 188static __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 211static __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 221static __inline struct tcb * 222_tcb_get(void) 223{ 224 return (ppc_get_tcb()); 225} 226 227static __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 */ 238static __inline struct kse * 239_get_curkse(void) 240{ 241 return (ppc_get_tcb()->tcb_curkcb->kcb_kse); 242} 243 244static __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 260static __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_ */ 293