Back to index

glibc  2.9
tls.h
Go to the documentation of this file.
00001 /* Definitions for thread-local data handling.  Hurd/i386 version.
00002    Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #ifndef _I386_TLS_H
00021 #define _I386_TLS_H
00022 
00023 #if defined HAVE_TLS_SUPPORT
00024 
00025 /* Some things really need not be machine-dependent.  */
00026 # include <sysdeps/mach/hurd/tls.h>
00027 
00028 /* The TCB can have any size and the memory following the address the
00029    thread pointer points to is unspecified.  Allocate the TCB there.  */
00030 # define TLS_TCB_AT_TP      1
00031 
00032 # ifndef __ASSEMBLER__
00033 
00034 /* Use i386-specific RPCs to arrange that %gs segment register prefix
00035    addresses the TCB in each thread.  */
00036 # include <mach/i386/mach_i386.h>
00037 
00038 # ifndef HAVE_I386_SET_GDT
00039 #  define __i386_set_gdt(thr, sel, desc) ((void) (thr), (void) (sel), (void) (desc), MIG_BAD_ID)
00040 # endif
00041 
00042 # include <errno.h>
00043 # include <assert.h>
00044 
00045 #define HURD_TLS_DESC_DECL(desc, tcb)                                       \
00046   struct descriptor desc =                                           \
00047     {                       /* low word: */                                 \
00048       0xffff                /* limit 0..15 */                        \
00049       | (((unsigned int) (tcb)) << 16) /* base 0..15 */                     \
00050       ,                            /* high word: */                         \
00051       ((((unsigned int) (tcb)) >> 16) & 0xff) /* base 16..23 */                    \
00052       | ((0x12 | 0x60 | 0x80) << 8) /* access = ACC_DATA_W|ACC_PL_U|ACC_P */  \
00053       | (0xf << 16)         /* limit 16..19 */                       \
00054       | ((4 | 8) << 20)            /* granularity = SZ_32|SZ_G */                  \
00055       | (((unsigned int) (tcb)) & 0xff000000) /* base 24..31 */                    \
00056     }
00057 
00058 
00059 static inline const char * __attribute__ ((unused))
00060 _hurd_tls_init (tcbhead_t *tcb, int secondcall)
00061 {
00062   HURD_TLS_DESC_DECL (desc, tcb);
00063 
00064   if (!secondcall)
00065     {
00066       /* This field is used by TLS accesses to get our "thread pointer"
00067         from the TLS point of view.  */
00068       tcb->tcb = tcb;
00069 
00070       /* Cache our thread port.  */
00071       tcb->self = __mach_thread_self ();
00072 
00073       /* Get the first available selector.  */
00074       int sel = -1;
00075       error_t err = __i386_set_gdt (tcb->self, &sel, desc);
00076       if (err == MIG_BAD_ID)
00077        {
00078          /* Old kernel, use a per-thread LDT.  */
00079          sel = 0x27;
00080          err = __i386_set_ldt (tcb->self, sel, &desc, 1);
00081          assert_perror (err);
00082          if (err)
00083            return "i386_set_ldt failed";
00084        }
00085       else if (err)
00086        {
00087          assert_perror (err); /* Separate from above with different line #. */
00088          return "i386_set_gdt failed";
00089        }
00090 
00091       /* Now install the new selector.  */
00092       asm volatile ("mov %w0, %%gs" :: "q" (sel));
00093     }
00094   else
00095     {
00096       /* Fetch the selector set by the first call.  */
00097       int sel;
00098       asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0));
00099       if (__builtin_expect (sel, 0x50) & 4) /* LDT selector */
00100        {
00101          error_t err = __i386_set_ldt (tcb->self, sel, &desc, 1);
00102          assert_perror (err);
00103          if (err)
00104            return "i386_set_ldt failed";
00105        }
00106       else
00107        {
00108          error_t err = __i386_set_gdt (tcb->self, &sel, desc);
00109          assert_perror (err);
00110          if (err)
00111            return "i386_set_gdt failed";
00112        }
00113     }
00114 
00115   return 0;
00116 }
00117 
00118 /* Code to initially initialize the thread pointer.  This might need
00119    special attention since 'errno' is not yet available and if the
00120    operation can cause a failure 'errno' must not be touched.  */
00121 # define TLS_INIT_TP(descr, secondcall) \
00122     _hurd_tls_init ((tcbhead_t *) (descr), (secondcall))
00123 # define TLS_INIT_TP_EXPENSIVE 1
00124 
00125 /* Return the TCB address of the current thread.  */
00126 # define THREAD_SELF                                                        \
00127   ({ tcbhead_t *__tcb;                                                      \
00128      __asm__ ("movl %%gs:%c1,%0" : "=r" (__tcb)                             \
00129              : "i" (offsetof (tcbhead_t, tcb)));                     \
00130      __tcb;})
00131 
00132 /* Install new dtv for current thread.  */
00133 # define INSTALL_NEW_DTV(dtvp)                                              \
00134   ({ asm volatile ("movl %0,%%gs:%P1"                                       \
00135                  : : "ir" (dtvp), "i" (offsetof (tcbhead_t, dtv))); })
00136 
00137 /* Return the address of the dtv for the current thread.  */
00138 # define THREAD_DTV()                                                       \
00139   ({ dtv_t *_dtv;                                                    \
00140      asm ("movl %%gs:%P1,%0" : "=q" (_dtv) : "i" (offsetof (tcbhead_t, dtv)));\
00141      _dtv; })
00142 
00143 #include <mach/machine/thread_status.h>
00144 
00145 /* Set up TLS in the new thread of a fork child, copying from our own.  */
00146 static inline error_t __attribute__ ((unused))
00147 _hurd_tls_fork (thread_t child, struct i386_thread_state *state)
00148 {
00149   /* Fetch the selector set by _hurd_tls_init.  */
00150   int sel;
00151   asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0));
00152   if (sel == state->ds)            /* _hurd_tls_init was never called.  */
00153     return 0;
00154 
00155   tcbhead_t *const tcb = THREAD_SELF;
00156   HURD_TLS_DESC_DECL (desc, tcb);
00157   error_t err;
00158 
00159   if (__builtin_expect (sel, 0x50) & 4) /* LDT selector */
00160     err = __i386_set_ldt (child, sel, &desc, 1);
00161   else
00162     err = __i386_set_gdt (child, &sel, desc);
00163 
00164   state->gs = sel;
00165   return err;
00166 }
00167 
00168 # endif       /* !__ASSEMBLER__ */
00169 #endif /* HAVE_TLS_SUPPORT */
00170 
00171 #endif /* i386/tls.h */