summaryrefslogtreecommitdiff
path: root/sysdeps/mach/hurd/i386/tls.h
blob: 00b611e9037310dd7231e055376b0523642ae92c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/* Definitions for thread-local data handling.  Hurd/i386 version.
   Copyright (C) 2003-2014 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _I386_TLS_H
#define _I386_TLS_H


/* Some things really need not be machine-dependent.  */
#include <sysdeps/mach/hurd/tls.h>


#ifndef __ASSEMBLER__
/* Type for the dtv.  */
typedef union dtv
{
  size_t counter;
  struct
  {
    void *val;
    bool is_static;
  } pointer;
} dtv_t;


/* Type of the TCB.  */
typedef struct
{
  void *tcb;			/* Points to this structure.  */
  dtv_t *dtv;			/* Vector of pointers to TLS data.  */
  thread_t self;		/* This thread's control port.  */
  int multiple_threads;
  uintptr_t sysinfo;
  uintptr_t stack_guard;
  uintptr_t pointer_guard;
  int gscope_flag;
  int private_futex;
  /* Reservation of some values for the TM ABI.  */
  void *__private_tm[4];
  /* GCC split stack support.  */
  void *__private_ss;

  /* Keep this field last */
  mach_port_t reply_port;	/* This thread's reply port.  */
  struct hurd_sigstate *_hurd_sigstate;
} tcbhead_t;
#endif


/* The TCB can have any size and the memory following the address the
   thread pointer points to is unspecified.  Allocate the TCB there.  */
#define TLS_TCB_AT_TP	1

#ifndef __ASSEMBLER__

/* Use i386-specific RPCs to arrange that %gs segment register prefix
   addresses the TCB in each thread.  */
# include <mach/i386/mach_i386.h>

# ifndef HAVE_I386_SET_GDT
#  define __i386_set_gdt(thr, sel, desc) ((void) (thr), (void) (sel), (void) (desc), MIG_BAD_ID)
# endif

#define __i386_selector_is_ldt(sel) (!!((sel) & 4))

# include <errno.h>
# include <assert.h>

# define HURD_TLS_DESC_DECL(desc, tcb)					      \
  struct descriptor desc =						      \
    {				/* low word: */				      \
      0xffff			/* limit 0..15 */			      \
      | (((unsigned int) (tcb)) << 16) /* base 0..15 */			      \
      ,				/* high word: */			      \
      ((((unsigned int) (tcb)) >> 16) & 0xff) /* base 16..23 */		      \
      | ((0x12 | 0x60 | 0x80) << 8) /* access = ACC_DATA_W|ACC_PL_U|ACC_P */  \
      | (0xf << 16)		/* limit 16..19 */			      \
      | ((4 | 8) << 20)		/* granularity = SZ_32|SZ_G */		      \
      | (((unsigned int) (tcb)) & 0xff000000) /* base 24..31 */		      \
    }

# define HURD_DESC_TLS(desc)						      \
  ({									      \
   (tcbhead_t *) (   (desc->low_word >> 16)				      \
                  | ((desc->high_word & 0xff) << 16)			      \
                  |  (desc->high_word & 0xff000000)			      \
     );})

#define __LIBC_NO_TLS()							      \
  ({ unsigned short ds, gs;						      \
     asm ("movw %%ds,%w0; movw %%gs,%w1" : "=q" (ds), "=q" (gs));	      \
     ds == gs; })

static inline const char * __attribute__ ((unused))
_hurd_tls_init (tcbhead_t *tcb, int secondcall)
{
  HURD_TLS_DESC_DECL (desc, tcb);
  thread_t self = __mach_thread_self ();
  const char *msg = NULL;

  if (!secondcall)
    {
      /* This field is used by TLS accesses to get our "thread pointer"
	 from the TLS point of view.  */
      tcb->tcb = tcb;

      /* Get the first available selector.  */
      int sel = -1;
      kern_return_t err = __i386_set_gdt (self, &sel, desc);
      if (err == MIG_BAD_ID)
	{
	  /* Old kernel, use a per-thread LDT.  */
	  sel = 0x27;
	  err = __i386_set_ldt (self, sel, &desc, 1);
	  assert_perror (err);
	  if (err)
	    {
	      msg = "i386_set_ldt failed";
	      goto out;
	    }
	}
      else if (err)
	{
	  assert_perror (err); /* Separate from above with different line #. */
	  msg = "i386_set_gdt failed";
	  goto out;
	}

      /* Now install the new selector.  */
      asm volatile ("mov %w0, %%gs" :: "q" (sel));
    }
  else
    {
      /* Fetch the selector set by the first call.  */
      int sel;
      asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0));
      if (__glibc_unlikely (__i386_selector_is_ldt(sel)))
	{
	  kern_return_t err = __i386_set_ldt (self, sel, &desc, 1);
	  assert_perror (err);
	  if (err)
	    {
	      msg = "i386_set_ldt failed";
	      goto out;
	    }
	}
      else
	{
	  kern_return_t err = __i386_set_gdt (self, &sel, desc);
	  assert_perror (err);
	  if (err)
	    {
	      msg = "i386_set_gdt failed";
	      goto out;
	    }
	}
    }

out:
  __mach_port_deallocate (__mach_task_self (), self);
  return msg;
}

/* Code to initially initialize the thread pointer.  This might need
   special attention since 'errno' is not yet available and if the
   operation can cause a failure 'errno' must not be touched.  */
# define TLS_INIT_TP(descr, secondcall) \
    _hurd_tls_init ((tcbhead_t *) (descr), (secondcall))

/* Return the TCB address of the current thread.  */
# define THREAD_SELF							      \
  ({ tcbhead_t *__tcb;							      \
     __asm__ ("movl %%gs:%c1,%0" : "=r" (__tcb)				      \
	      : "i" (offsetof (tcbhead_t, tcb)));			      \
     __tcb;})

/* Return the TCB address of a thread given its state.  */
# define THREAD_TCB(thread, thread_state)				      \
  ({ int __sel = (thread_state)->basic.gs;				      \
     struct descriptor __desc, *___desc = &__desc;			      \
     unsigned int __count = 1;						      \
     kern_return_t __err;						      \
     if (__builtin_expect (__sel, 0x48) & 4) /* LDT selector */		      \
       __err = __i386_get_ldt ((thread), __sel, 1, &___desc, &__count);	      \
     else								      \
       __err = __i386_get_gdt ((thread), __sel, &__desc);		      \
     assert_perror (__err);						      \
     assert (__count == 1);						      \
     HURD_DESC_TLS(___desc);})

/* Install new dtv for current thread.  */
# define INSTALL_NEW_DTV(dtvp)						      \
  ({ asm volatile ("movl %0,%%gs:%P1"					      \
		   : : "ir" (dtvp), "i" (offsetof (tcbhead_t, dtv))); })

/* Return the address of the dtv for the current thread.  */
# define THREAD_DTV()							      \
  ({ dtv_t *_dtv;							      \
     asm ("movl %%gs:%P1,%0" : "=q" (_dtv) : "i" (offsetof (tcbhead_t, dtv)));\
     _dtv; })

# include <mach/machine/thread_status.h>

/* Set up TLS in the new thread of a fork child, copying from the original.  */
static inline kern_return_t __attribute__ ((unused))
_hurd_tls_fork (thread_t child, thread_t orig, struct i386_thread_state *state)
{
  /* Fetch the selector set by _hurd_tls_init.  */
  int sel;
  asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0));
  if (sel == state->ds)		/* _hurd_tls_init was never called.  */
    return 0;

  struct descriptor desc, *_desc = &desc;
  kern_return_t err;
  unsigned int count = 1;

  if (__glibc_unlikely (__i386_selector_is_ldt(sel)))
    err = __i386_get_ldt (orig, sel, 1, &_desc, &count);
  else
    err = __i386_get_gdt (orig, sel, &desc);

  assert_perror (err);
  if (err)
    return err;

  if (__glibc_unlikely (__i386_selector_is_ldt(sel)))
    err = __i386_set_ldt (child, sel, &desc, 1);
  else
    err = __i386_set_gdt (child, &sel, desc);

  state->gs = sel;
  return err;
}

static inline kern_return_t __attribute__ ((unused))
_hurd_tls_new (thread_t child, struct i386_thread_state *state, tcbhead_t *tcb)
{
  /* Fetch the selector set by _hurd_tls_init.  */
  int sel;
  asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0));
  if (sel == state->ds)		/* _hurd_tls_init was never called.  */
    return 0;

  HURD_TLS_DESC_DECL (desc, tcb);
  kern_return_t err;

  tcb->tcb = tcb;
  tcb->self = child;

  if (__glibc_unlikely (__i386_selector_is_ldt(sel)))
    err = __i386_set_ldt (child, sel, &desc, 1);
  else
    err = __i386_set_gdt (child, &sel, desc);

  state->gs = sel;
  return err;
}

#endif	/* !__ASSEMBLER__ */

#endif	/* i386/tls.h */