Support cancellation in librt.
[kopensolaris-gnu/glibc.git] / linuxthreads / sysdeps / i386 / useldt.h
1 /* Special definitions for ix86 machine using segment register based
2    thread descriptor.
3    Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5    Contributed by Ulrich Drepper <drepper@cygnus.com>.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of the
10    License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21
22 #ifndef __ASSEMBLER__
23 #include <stddef.h>     /* For offsetof.  */
24 #include <stdlib.h>     /* For abort().  */
25 #include <sysdep.h>
26
27
28 /* We don't want to include the kernel header.  So duplicate the
29    information.  */
30
31 /* Structure passed on `modify_ldt' call.  */
32 struct modify_ldt_ldt_s
33 {
34   unsigned int entry_number;
35   unsigned long int base_addr;
36   unsigned int limit;
37   unsigned int seg_32bit:1;
38   unsigned int contents:2;
39   unsigned int read_exec_only:1;
40   unsigned int limit_in_pages:1;
41   unsigned int seg_not_present:1;
42   unsigned int useable:1;
43   unsigned int empty:25;
44 };
45
46 /* System call to set LDT entry.  */
47 extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t);
48
49
50 /* Return the thread descriptor for the current thread.
51
52    The contained asm must *not* be marked volatile since otherwise
53    assignments like
54         pthread_descr self = thread_self();
55    do not get optimized away.  */
56 #define THREAD_SELF \
57 ({                                                                            \
58   register pthread_descr __self;                                              \
59   __asm__ ("movl %%gs:%c1,%0" : "=r" (__self)                                 \
60            : "i" (offsetof (struct _pthread_descr_struct,                     \
61                             p_header.data.self)));                            \
62   __self;                                                                     \
63 })
64
65
66 /* Initialize the thread-unique value.  Two possible ways to do it.  */
67
68 #define DO_MODIFY_LDT(descr, nr)                                              \
69 ({                                                                            \
70   struct modify_ldt_ldt_s ldt_entry =                                         \
71     { nr, (unsigned long int) (descr), 0xfffff /* 4GB in pages */,            \
72       1, 0, 0, 1, 0, 1, 0 };                                                  \
73   if (__modify_ldt (1, &ldt_entry, sizeof (ldt_entry)) != 0)                  \
74     abort ();                                                                 \
75   asm ("movw %w0, %%gs" : : "q" (nr * 8 + 7));                                \
76 })
77
78 #ifdef __PIC__
79 # define USETLS_EBX_ARG "r"
80 # define USETLS_LOAD_EBX "xchgl %1, %%ebx\n\t"
81 #else
82 # define USETLS_EBX_ARG "b"
83 # define USETLS_LOAD_EBX
84 #endif
85
86 /* When using the new set_thread_area call, we don't need to change %gs
87    because we inherited the value set up in the main thread by TLS setup.
88    We need to extract that value and set up the same segment in this
89    thread.  */
90 #if USE_TLS
91 # define DO_SET_THREAD_AREA_REUSE(nr)   1
92 #else
93 /* Without TLS, we do the initialization of the main thread, where NR == 0.  */
94 # define DO_SET_THREAD_AREA_REUSE(nr)   (!__builtin_constant_p (nr) || (nr))
95 #endif
96 #define DO_SET_THREAD_AREA(descr, nr) \
97 ({                                                                            \
98   int __gs;                                                                   \
99   if (DO_SET_THREAD_AREA_REUSE (nr))                                          \
100     {                                                                         \
101       asm ("movw %%gs, %w0" : "=q" (__gs));                                   \
102       struct modify_ldt_ldt_s ldt_entry =                                     \
103         { (__gs & 0xffff) >> 3,                                               \
104           (unsigned long int) (descr), 0xfffff /* 4GB in pages */,            \
105           1, 0, 0, 1, 0, 1, 0 };                                              \
106                                                                               \
107       int __result;                                                           \
108       __asm (USETLS_LOAD_EBX                                                  \
109              "movl %2, %%eax\n\t"                                             \
110              "int $0x80\n\t"                                                  \
111              USETLS_LOAD_EBX                                                  \
112              : "=&a" (__result)                                               \
113              : USETLS_EBX_ARG (&ldt_entry), "i" (__NR_set_thread_area),       \
114                "m" (ldt_entry)                                                \
115              : "memory");                                                     \
116       if (__result == 0)                                                      \
117         asm ("movw %w0, %%gs" :: "q" (__gs));                                 \
118       else                                                                    \
119         __gs = -1;                                                            \
120     }                                                                         \
121   else                                                                        \
122     {                                                                         \
123       struct modify_ldt_ldt_s ldt_entry =                                     \
124         { -1,                                                                 \
125           (unsigned long int) (descr), 0xfffff /* 4GB in pages */,            \
126           1, 0, 0, 1, 0, 1, 0 };                                              \
127       int __result;                                                           \
128       __asm (USETLS_LOAD_EBX                                                  \
129              "movl %2, %%eax\n\t"                                             \
130              "int $0x80\n\t"                                                  \
131              USETLS_LOAD_EBX                                                  \
132              : "=&a" (__result)                                               \
133              : USETLS_EBX_ARG (&ldt_entry), "i" (__NR_set_thread_area),       \
134                "m" (ldt_entry)                                                \
135              : "memory");                                                     \
136       if (__result == 0)                                                      \
137         {                                                                     \
138           __gs = (ldt_entry.entry_number << 3) + 3;                           \
139           asm ("movw %w0, %%gs" : : "q" (__gs));                              \
140         }                                                                     \
141       else                                                                    \
142         __gs = -1;                                                            \
143     }                                                                         \
144   __gs;                                                                       \
145 })
146
147 #if defined __ASSUME_SET_THREAD_AREA_SYSCALL
148 # define INIT_THREAD_SELF(descr, nr)    DO_SET_THREAD_AREA (descr, nr)
149 #elif defined __NR_set_thread_area
150 # define INIT_THREAD_SELF(descr, nr)                                          \
151 ({                                                                            \
152   if (__builtin_expect (__have_no_set_thread_area, 0)                         \
153       || (DO_SET_THREAD_AREA (descr, DO_SET_THREAD_AREA_REUSE (nr)) == -1     \
154           && (__have_no_set_thread_area = 1)))                                \
155     DO_MODIFY_LDT (descr, nr);                                                \
156 })
157 /* Defined in pspinlock.c.  */
158 extern int __have_no_set_thread_area;
159 #else
160 # define INIT_THREAD_SELF(descr, nr)    DO_MODIFY_LDT (descr, nr)
161 #endif
162
163 /* Free resources associated with thread descriptor.  */
164 #ifdef __ASSUME_SET_THREAD_AREA_SYSCALL
165 #define FREE_THREAD(descr, nr) do { } while (0)
166 #elif defined __NR_set_thread_area
167 #define FREE_THREAD(descr, nr) \
168 {                                                                             \
169   int __gs;                                                                   \
170   __asm__ __volatile__ ("movw %%gs, %w0" : "=q" (__gs));                      \
171   if (__builtin_expect (__gs & 4, 0))                                         \
172     {                                                                         \
173       struct modify_ldt_ldt_s ldt_entry =                                     \
174         { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 };                                    \
175       __modify_ldt (1, &ldt_entry, sizeof (ldt_entry));                       \
176     }                                                                         \
177 }
178 #else
179 #define FREE_THREAD(descr, nr) \
180 {                                                                             \
181   struct modify_ldt_ldt_s ldt_entry =                                         \
182     { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 };                                        \
183   __modify_ldt (1, &ldt_entry, sizeof (ldt_entry));                           \
184 }
185 #endif
186
187 /* Read member of the thread descriptor directly.  */
188 #define THREAD_GETMEM(descr, member) \
189 ({                                                                            \
190   __typeof__ (descr->member) __value;                                         \
191   if (sizeof (__value) == 1)                                                  \
192     __asm__ __volatile__ ("movb %%gs:%P2,%b0"                                 \
193                           : "=q" (__value)                                    \
194                           : "0" (0),                                          \
195                             "i" (offsetof (struct _pthread_descr_struct,      \
196                                            member)));                         \
197   else if (sizeof (__value) == 4)                                             \
198     __asm__ __volatile__ ("movl %%gs:%P1,%0"                                  \
199                           : "=r" (__value)                                    \
200                           : "i" (offsetof (struct _pthread_descr_struct,      \
201                                            member)));                         \
202   else                                                                        \
203     {                                                                         \
204       if (sizeof (__value) != 8)                                              \
205         /* There should not be any value with a size other than 1, 4 or 8.  */\
206         abort ();                                                             \
207                                                                               \
208       __asm__ __volatile__ ("movl %%gs:%P1,%%eax\n\t"                         \
209                             "movl %%gs:%P2,%%edx"                             \
210                             : "=A" (__value)                                  \
211                             : "i" (offsetof (struct _pthread_descr_struct,    \
212                                              member)),                        \
213                               "i" (offsetof (struct _pthread_descr_struct,    \
214                                              member) + 4));                   \
215     }                                                                         \
216   __value;                                                                    \
217 })
218
219 /* Same as THREAD_GETMEM, but the member offset can be non-constant.  */
220 #define THREAD_GETMEM_NC(descr, member) \
221 ({                                                                            \
222   __typeof__ (descr->member) __value;                                         \
223   if (sizeof (__value) == 1)                                                  \
224     __asm__ __volatile__ ("movb %%gs:(%2),%b0"                                \
225                           : "=q" (__value)                                    \
226                           : "0" (0),                                          \
227                             "r" (offsetof (struct _pthread_descr_struct,      \
228                                            member)));                         \
229   else if (sizeof (__value) == 4)                                             \
230     __asm__ __volatile__ ("movl %%gs:(%1),%0"                                 \
231                           : "=r" (__value)                                    \
232                           : "r" (offsetof (struct _pthread_descr_struct,      \
233                                            member)));                         \
234   else                                                                        \
235     {                                                                         \
236       if (sizeof (__value) != 8)                                              \
237         /* There should not be any value with a size other than 1, 4 or 8.  */\
238         abort ();                                                             \
239                                                                               \
240       __asm__ __volatile__ ("movl %%gs:(%1),%%eax\n\t"                        \
241                             "movl %%gs:4(%1),%%edx"                           \
242                             : "=&A" (__value)                                 \
243                             : "r" (offsetof (struct _pthread_descr_struct,    \
244                                              member)));                       \
245     }                                                                         \
246   __value;                                                                    \
247 })
248
249 /* Same as THREAD_SETMEM, but the member offset can be non-constant.  */
250 #define THREAD_SETMEM(descr, member, value) \
251 ({                                                                            \
252   __typeof__ (descr->member) __value = (value);                               \
253   if (sizeof (__value) == 1)                                                  \
254     __asm__ __volatile__ ("movb %0,%%gs:%P1" :                                \
255                           : "q" (__value),                                    \
256                             "i" (offsetof (struct _pthread_descr_struct,      \
257                                            member)));                         \
258   else if (sizeof (__value) == 4)                                             \
259     __asm__ __volatile__ ("movl %0,%%gs:%P1" :                                \
260                           : "r" (__value),                                    \
261                             "i" (offsetof (struct _pthread_descr_struct,      \
262                                            member)));                         \
263   else                                                                        \
264     {                                                                         \
265       if (sizeof (__value) != 8)                                              \
266         /* There should not be any value with a size other than 1, 4 or 8.  */\
267         abort ();                                                             \
268                                                                               \
269       __asm__ __volatile__ ("movl %%eax,%%gs:%P1\n\n"                         \
270                             "movl %%edx,%%gs:%P2" :                           \
271                             : "A" (__value),                                  \
272                               "i" (offsetof (struct _pthread_descr_struct,    \
273                                              member)),                        \
274                               "i" (offsetof (struct _pthread_descr_struct,    \
275                                              member) + 4));                   \
276     }                                                                         \
277 })
278
279 /* Set member of the thread descriptor directly.  */
280 #define THREAD_SETMEM_NC(descr, member, value) \
281 ({                                                                            \
282   __typeof__ (descr->member) __value = (value);                               \
283   if (sizeof (__value) == 1)                                                  \
284     __asm__ __volatile__ ("movb %0,%%gs:(%1)" :                               \
285                           : "q" (__value),                                    \
286                             "r" (offsetof (struct _pthread_descr_struct,      \
287                                            member)));                         \
288   else if (sizeof (__value) == 4)                                             \
289     __asm__ __volatile__ ("movl %0,%%gs:(%1)" :                               \
290                           : "r" (__value),                                    \
291                             "r" (offsetof (struct _pthread_descr_struct,      \
292                                            member)));                         \
293   else                                                                        \
294     {                                                                         \
295       if (sizeof (__value) != 8)                                              \
296         /* There should not be any value with a size other than 1, 4 or 8.  */\
297         abort ();                                                             \
298                                                                               \
299       __asm__ __volatile__ ("movl %%eax,%%gs:(%1)\n\t"                        \
300                             "movl %%edx,%%gs:4(%1)" :                         \
301                             : "A" (__value),                                  \
302                               "r" (offsetof (struct _pthread_descr_struct,    \
303                                              member)));                       \
304     }                                                                         \
305 })
306 #endif
307
308 #if __ASSUME_LDT_WORKS > 0
309 /* We want the OS to assign stack addresses.  */
310 #define FLOATING_STACKS 1
311
312 /* Maximum size of the stack if the rlimit is unlimited.  */
313 #define ARCH_STACK_MAX_SIZE     8*1024*1024
314 #endif