[PIC] (USETLS_LOAD_EBX): Use correct input register number.
[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
26
27 /* We don't want to include the kernel header.  So duplicate the
28    information.  */
29
30 /* Structure passed on `modify_ldt' call.  */
31 struct modify_ldt_ldt_s
32 {
33   unsigned int entry_number;
34   unsigned long int base_addr;
35   unsigned int limit;
36   unsigned int seg_32bit:1;
37   unsigned int contents:2;
38   unsigned int read_exec_only:1;
39   unsigned int limit_in_pages:1;
40   unsigned int seg_not_present:1;
41   unsigned int useable:1;
42   unsigned int empty:25;
43 };
44
45 /* System call to set LDT entry.  */
46 extern int __modify_ldt (int, struct modify_ldt_ldt_s *, size_t);
47
48
49 /* Return the thread descriptor for the current thread.
50
51    The contained asm must *not* be marked volatile since otherwise
52    assignments like
53         pthread_descr self = thread_self();
54    do not get optimized away.  */
55 #define THREAD_SELF \
56 ({                                                                            \
57   register pthread_descr __self;                                              \
58   __asm__ ("movl %%gs:%c1,%0" : "=r" (__self)                                 \
59            : "i" (offsetof (struct _pthread_descr_struct,                     \
60                             p_header.data.self)));                            \
61   __self;                                                                     \
62 })
63
64
65 /* Initialize the thread-unique value.  Two possible ways to do it.  */
66
67 #define DO_MODIFY_LDT(descr, nr)                                              \
68 ({                                                                            \
69   struct modify_ldt_ldt_s ldt_entry =                                         \
70     { nr, (unsigned long int) (descr), 0xfffff /* 4GB in pages */,            \
71       1, 0, 0, 1, 0, 1, 0 };                                                  \
72   if (__modify_ldt (1, &ldt_entry, sizeof (ldt_entry)) != 0)                  \
73     abort ();                                                                 \
74   asm ("movw %w0, %%gs" : : "q" (nr * 8 + 7));                                \
75 })
76
77 #ifdef __PIC__
78 # define USETLS_EBX_ARG "r"
79 # define USETLS_LOAD_EBX "xchgl %1, %%ebx\n\t"
80 #else
81 # define USETLS_EBX_ARG "b"
82 # define USETLS_LOAD_EBX
83 #endif
84
85 /* When using the new set_thread_area call, we don't need to change %gs
86    because we inherited the value set up in the main thread by TLS setup.
87    We need to extract that value and set up the same segment in this
88    thread.  */
89 #if USE_TLS
90 # define DO_SET_THREAD_AREA_REUSE(nr)   1
91 #else
92 /* Without TLS, we do the initialization of the main thread, where NR == 0.  */
93 # define DO_SET_THREAD_AREA_REUSE(nr)   (!__builtin_constant_p (nr) || (nr))
94 #endif
95 #define DO_SET_THREAD_AREA(descr, nr) \
96 ({                                                                            \
97   int __gs;                                                                   \
98   if (DO_SET_THREAD_AREA_REUSE (nr))                                          \
99     {                                                                         \
100       asm ("movw %%gs, %w0" : "=q" (__gs));                                   \
101       struct modify_ldt_ldt_s ldt_entry =                                     \
102         { (__gs & 0xffff) >> 3,                                               \
103           (unsigned long int) (descr), 0xfffff /* 4GB in pages */,            \
104           1, 0, 0, 1, 0, 1, 0 };                                              \
105                                                                               \
106       int __result;                                                           \
107       __asm (USETLS_LOAD_EBX                                                  \
108              "movl %2, %%eax\n\t"                                             \
109              "int $0x80\n\t"                                                  \
110              USETLS_LOAD_EBX                                                  \
111              : "=&a" (__result)                                               \
112              : USETLS_EBX_ARG (&ldt_entry), "i" (__NR_set_thread_area));      \
113       if (__result == 0)                                                      \
114         asm ("movw %w0, %%gs" :: "q" (__gs));                                 \
115       else                                                                    \
116         __gs = -1;                                                            \
117     }                                                                         \
118   else                                                                        \
119     {                                                                         \
120       struct modify_ldt_ldt_s ldt_entry =                                     \
121         { -1,                                                                 \
122           (unsigned long int) (descr), 0xfffff /* 4GB in pages */,            \
123           1, 0, 0, 1, 0, 1, 0 };                                              \
124       int __result;                                                           \
125       __asm (USETLS_LOAD_EBX                                                  \
126              "movl %2, %%eax\n\t"                                             \
127              "int $0x80\n\t"                                                  \
128              USETLS_LOAD_EBX                                                  \
129              : "=&a" (__result)                                               \
130              : USETLS_EBX_ARG (&ldt_entry), "i" (__NR_set_thread_area));      \
131       if (__result == 0)                                                      \
132         {                                                                     \
133           __gs = (ldt_entry.entry_number << 3) + 3;                           \
134           asm ("movw %w0, %%gs" : : "q" (__gs));                              \
135         }                                                                     \
136       else                                                                    \
137         __gs = -1;                                                            \
138     }                                                                         \
139   __gs;                                                                       \
140 })
141
142 #if defined __ASSUME_SET_THREAD_AREA_SYSCALL
143 # define INIT_THREAD_SELF(descr, nr)    DO_SET_THREAD_AREA (descr, nr)
144 #elif defined __NR_set_thread_area
145 # define INIT_THREAD_SELF(descr, nr)                                          \
146 ({                                                                            \
147   if (__builtin_expect (__have_no_set_thread_area, 0)                         \
148       || (DO_SET_THREAD_AREA (descr, DO_SET_THREAD_AREA_REUSE (nr)) == -1     \
149           && (__have_no_set_thread_area = 1)))                                \
150     DO_MODIFY_LDT (descr, nr);                                                \
151 })
152 /* Defined in pspinlock.c.  */
153 extern int __have_no_set_thread_area;
154 #else
155 # define INIT_THREAD_SELF(descr, nr)    DO_MODIFY_LDT (descr, nr)
156 #endif
157
158 /* Free resources associated with thread descriptor.  */
159 #ifdef __ASSUME_SET_THREAD_AREA_SYSCALL
160 #define FREE_THREAD(descr, nr) do { } while (0)
161 #elif defined __NR_set_thread_area
162 #define FREE_THREAD(descr, nr) \
163 {                                                                             \
164   int __gs;                                                                   \
165   __asm__ __volatile__ ("movw %%gs, %w0" : "=q" (__gs));                      \
166   if (__builtin_expect (__gs & 4, 0))                                         \
167     {                                                                         \
168       struct modify_ldt_ldt_s ldt_entry =                                     \
169         { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 };                                    \
170       __modify_ldt (1, &ldt_entry, sizeof (ldt_entry));                       \
171     }                                                                         \
172 }
173 #else
174 #define FREE_THREAD(descr, nr) \
175 {                                                                             \
176   struct modify_ldt_ldt_s ldt_entry =                                         \
177     { nr, 0, 0, 0, 0, 1, 0, 1, 0, 0 };                                        \
178   __modify_ldt (1, &ldt_entry, sizeof (ldt_entry));                           \
179 }
180 #endif
181
182 /* Read member of the thread descriptor directly.  */
183 #define THREAD_GETMEM(descr, member) \
184 ({                                                                            \
185   __typeof__ (descr->member) __value;                                         \
186   if (sizeof (__value) == 1)                                                  \
187     __asm__ __volatile__ ("movb %%gs:%P2,%b0"                                 \
188                           : "=q" (__value)                                    \
189                           : "0" (0),                                          \
190                             "i" (offsetof (struct _pthread_descr_struct,      \
191                                            member)));                         \
192   else if (sizeof (__value) == 4)                                             \
193     __asm__ __volatile__ ("movl %%gs:%P1,%0"                                  \
194                           : "=r" (__value)                                    \
195                           : "i" (offsetof (struct _pthread_descr_struct,      \
196                                            member)));                         \
197   else                                                                        \
198     {                                                                         \
199       if (sizeof (__value) != 8)                                              \
200         /* There should not be any value with a size other than 1, 4 or 8.  */\
201         abort ();                                                             \
202                                                                               \
203       __asm__ __volatile__ ("movl %%gs:%P1,%%eax\n\t"                         \
204                             "movl %%gs:%P2,%%edx"                             \
205                             : "=A" (__value)                                  \
206                             : "i" (offsetof (struct _pthread_descr_struct,    \
207                                              member)),                        \
208                               "i" (offsetof (struct _pthread_descr_struct,    \
209                                              member) + 4));                   \
210     }                                                                         \
211   __value;                                                                    \
212 })
213
214 /* Same as THREAD_GETMEM, but the member offset can be non-constant.  */
215 #define THREAD_GETMEM_NC(descr, member) \
216 ({                                                                            \
217   __typeof__ (descr->member) __value;                                         \
218   if (sizeof (__value) == 1)                                                  \
219     __asm__ __volatile__ ("movb %%gs:(%2),%b0"                                \
220                           : "=q" (__value)                                    \
221                           : "0" (0),                                          \
222                             "r" (offsetof (struct _pthread_descr_struct,      \
223                                            member)));                         \
224   else if (sizeof (__value) == 4)                                             \
225     __asm__ __volatile__ ("movl %%gs:(%1),%0"                                 \
226                           : "=r" (__value)                                    \
227                           : "r" (offsetof (struct _pthread_descr_struct,      \
228                                            member)));                         \
229   else                                                                        \
230     {                                                                         \
231       if (sizeof (__value) != 8)                                              \
232         /* There should not be any value with a size other than 1, 4 or 8.  */\
233         abort ();                                                             \
234                                                                               \
235       __asm__ __volatile__ ("movl %%gs:(%1),%%eax\n\t"                        \
236                             "movl %%gs:4(%1),%%edx"                           \
237                             : "=&A" (__value)                                 \
238                             : "r" (offsetof (struct _pthread_descr_struct,    \
239                                              member)));                       \
240     }                                                                         \
241   __value;                                                                    \
242 })
243
244 /* Same as THREAD_SETMEM, but the member offset can be non-constant.  */
245 #define THREAD_SETMEM(descr, member, value) \
246 ({                                                                            \
247   __typeof__ (descr->member) __value = (value);                               \
248   if (sizeof (__value) == 1)                                                  \
249     __asm__ __volatile__ ("movb %0,%%gs:%P1" :                                \
250                           : "q" (__value),                                    \
251                             "i" (offsetof (struct _pthread_descr_struct,      \
252                                            member)));                         \
253   else if (sizeof (__value) == 4)                                             \
254     __asm__ __volatile__ ("movl %0,%%gs:%P1" :                                \
255                           : "r" (__value),                                    \
256                             "i" (offsetof (struct _pthread_descr_struct,      \
257                                            member)));                         \
258   else                                                                        \
259     {                                                                         \
260       if (sizeof (__value) != 8)                                              \
261         /* There should not be any value with a size other than 1, 4 or 8.  */\
262         abort ();                                                             \
263                                                                               \
264       __asm__ __volatile__ ("movl %%eax,%%gs:%P1\n\n"                         \
265                             "movl %%edx,%%gs:%P2" :                           \
266                             : "A" (__value),                                  \
267                               "i" (offsetof (struct _pthread_descr_struct,    \
268                                              member)),                        \
269                               "i" (offsetof (struct _pthread_descr_struct,    \
270                                              member) + 4));                   \
271     }                                                                         \
272 })
273
274 /* Set member of the thread descriptor directly.  */
275 #define THREAD_SETMEM_NC(descr, member, value) \
276 ({                                                                            \
277   __typeof__ (descr->member) __value = (value);                               \
278   if (sizeof (__value) == 1)                                                  \
279     __asm__ __volatile__ ("movb %0,%%gs:(%1)" :                               \
280                           : "q" (__value),                                    \
281                             "r" (offsetof (struct _pthread_descr_struct,      \
282                                            member)));                         \
283   else if (sizeof (__value) == 4)                                             \
284     __asm__ __volatile__ ("movl %0,%%gs:(%1)" :                               \
285                           : "r" (__value),                                    \
286                             "r" (offsetof (struct _pthread_descr_struct,      \
287                                            member)));                         \
288   else                                                                        \
289     {                                                                         \
290       if (sizeof (__value) != 8)                                              \
291         /* There should not be any value with a size other than 1, 4 or 8.  */\
292         abort ();                                                             \
293                                                                               \
294       __asm__ __volatile__ ("movl %%eax,%%gs:(%1)\n\t"                        \
295                             "movl %%edx,%%gs:4(%1)" :                         \
296                             : "A" (__value),                                  \
297                               "r" (offsetof (struct _pthread_descr_struct,    \
298                                              member)));                       \
299     }                                                                         \
300 })
301 #endif
302
303 /* We want the OS to assign stack addresses.  */
304 #define FLOATING_STACKS 1
305
306 /* Maximum size of the stack if the rlimit is unlimited.  */
307 #define ARCH_STACK_MAX_SIZE     8*1024*1024