devenum: COM cleanup for ICreateDevEnum.
[wine] / libs / wine / ldt.c
1 /*
2  * LDT manipulation functions
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This 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 this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wine/library.h"
35
36 #if defined(__i386__) && !defined(__MINGW32__) && !defined(_MSC_VER)
37
38 #ifdef __linux__
39
40 #ifdef HAVE_SYS_SYSCALL_H
41 # include <sys/syscall.h>
42 #endif
43
44 struct modify_ldt_s
45 {
46     unsigned int  entry_number;
47     unsigned long base_addr;
48     unsigned int  limit;
49     unsigned int  seg_32bit : 1;
50     unsigned int  contents : 2;
51     unsigned int  read_exec_only : 1;
52     unsigned int  limit_in_pages : 1;
53     unsigned int  seg_not_present : 1;
54     unsigned int  usable : 1;
55     unsigned int  garbage : 25;
56 };
57
58 static inline void fill_modify_ldt_struct( struct modify_ldt_s *ptr, const LDT_ENTRY *entry )
59 {
60     ptr->base_addr       = (unsigned long)wine_ldt_get_base(entry);
61     ptr->limit           = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
62     ptr->seg_32bit       = entry->HighWord.Bits.Default_Big;
63     ptr->contents        = (entry->HighWord.Bits.Type >> 2) & 3;
64     ptr->read_exec_only  = !(entry->HighWord.Bits.Type & 2);
65     ptr->limit_in_pages  = entry->HighWord.Bits.Granularity;
66     ptr->seg_not_present = !entry->HighWord.Bits.Pres;
67     ptr->usable          = entry->HighWord.Bits.Sys;
68     ptr->garbage         = 0;
69 }
70
71 static inline int modify_ldt( int func, struct modify_ldt_s *ptr, unsigned long count )
72 {
73     return syscall( SYS_modify_ldt, func, ptr, count );
74 }
75
76 static inline int set_thread_area( struct modify_ldt_s *ptr )
77 {
78     return syscall( 243 /* SYS_set_thread_area */, ptr );
79 }
80
81 #endif  /* linux */
82
83 #if defined(__svr4__) || defined(_SCO_DS)
84 #include <sys/sysi86.h>
85 #ifndef __sun__
86 #include <sys/seg.h>
87 #endif
88 #endif
89
90 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
91 #include <machine/segments.h>
92 #include <machine/sysarch.h>
93 #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
94
95 #ifdef __APPLE__
96 #include <i386/user_ldt.h>
97 #endif
98
99 /* local copy of the LDT */
100 #ifdef __APPLE__
101 struct __wine_ldt_copy wine_ldt_copy = { { 0, 0, 0 } };
102 #else
103 struct __wine_ldt_copy wine_ldt_copy;
104 #endif
105
106 static const LDT_ENTRY null_entry;  /* all-zeros, used to clear LDT entries */
107
108 #define LDT_FIRST_ENTRY 512
109 #define LDT_SIZE 8192
110
111 /* empty function for default locks */
112 static void nop(void) { }
113
114 static void (*lock_ldt)(void) = nop;
115 static void (*unlock_ldt)(void) = nop;
116
117
118 static inline int is_gdt_sel( unsigned short sel ) { return !(sel & 4); }
119
120 /***********************************************************************
121  *           wine_ldt_init_locking
122  *
123  * Set the LDT locking/unlocking functions.
124  */
125 void wine_ldt_init_locking( void (*lock_func)(void), void (*unlock_func)(void) )
126 {
127     lock_ldt = lock_func;
128     unlock_ldt = unlock_func;
129 }
130
131
132 /***********************************************************************
133  *           wine_ldt_get_entry
134  *
135  * Retrieve an LDT entry. Return a null entry if selector is not allocated.
136  */
137 void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry )
138 {
139     int index = sel >> 3;
140
141     if (is_gdt_sel(sel))
142     {
143         *entry = null_entry;
144         return;
145     }
146     lock_ldt();
147     if (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED)
148     {
149         wine_ldt_set_base(  entry, wine_ldt_copy.base[index] );
150         wine_ldt_set_limit( entry, wine_ldt_copy.limit[index] );
151         wine_ldt_set_flags( entry, wine_ldt_copy.flags[index] );
152     }
153     else *entry = null_entry;
154     unlock_ldt();
155 }
156
157
158 /***********************************************************************
159  *           internal_set_entry
160  *
161  * Set an LDT entry, without locking. For internal use only.
162  */
163 static int internal_set_entry( unsigned short sel, const LDT_ENTRY *entry )
164 {
165     int ret = 0, index = sel >> 3;
166
167     if (index < LDT_FIRST_ENTRY) return 0;  /* cannot modify reserved entries */
168
169 #ifdef linux
170     {
171         struct modify_ldt_s ldt_info;
172
173         ldt_info.entry_number = index;
174         fill_modify_ldt_struct( &ldt_info, entry );
175         if ((ret = modify_ldt(0x11, &ldt_info, sizeof(ldt_info))) < 0)
176             perror( "modify_ldt" );
177     }
178 #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
179     {
180         LDT_ENTRY entry_copy = *entry;
181         /* The kernel will only let us set LDTs with user priority level */
182         if (entry_copy.HighWord.Bits.Pres
183             && entry_copy.HighWord.Bits.Dpl != 3)
184                 entry_copy.HighWord.Bits.Dpl = 3;
185         ret = i386_set_ldt(index, (union descriptor *)&entry_copy, 1);
186         if (ret < 0)
187         {
188             perror("i386_set_ldt");
189             fprintf( stderr, "Did you reconfigure the kernel with \"options USER_LDT\"?\n" );
190             exit(1);
191         }
192     }
193 #elif defined(__svr4__) || defined(_SCO_DS)
194     {
195         struct ssd ldt_mod;
196         ldt_mod.sel  = sel;
197         ldt_mod.bo   = (unsigned long)wine_ldt_get_base(entry);
198         ldt_mod.ls   = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
199         ldt_mod.acc1 = entry->HighWord.Bytes.Flags1;
200         ldt_mod.acc2 = entry->HighWord.Bytes.Flags2 >> 4;
201         if ((ret = sysi86(SI86DSCR, &ldt_mod)) == -1) perror("sysi86");
202     }
203 #elif defined(__APPLE__)
204     if ((ret = i386_set_ldt(index, (union ldt_entry *)entry, 1)) < 0)
205         perror("i386_set_ldt");
206 #else
207     fprintf( stderr, "No LDT support on this platform\n" );
208     exit(1);
209 #endif
210
211     if (ret >= 0)
212     {
213         wine_ldt_copy.base[index]  = wine_ldt_get_base(entry);
214         wine_ldt_copy.limit[index] = wine_ldt_get_limit(entry);
215         wine_ldt_copy.flags[index] = (entry->HighWord.Bits.Type |
216                                  (entry->HighWord.Bits.Default_Big ? WINE_LDT_FLAGS_32BIT : 0) |
217                                  (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED));
218     }
219     return ret;
220 }
221
222
223 /***********************************************************************
224  *           wine_ldt_set_entry
225  *
226  * Set an LDT entry.
227  */
228 int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry )
229 {
230     int ret;
231
232     lock_ldt();
233     ret = internal_set_entry( sel, entry );
234     unlock_ldt();
235     return ret;
236 }
237
238
239 /***********************************************************************
240  *           wine_ldt_is_system
241  *
242  * Check if the selector is a system selector (i.e. not managed by Wine).
243  */
244 int wine_ldt_is_system( unsigned short sel )
245 {
246     return is_gdt_sel(sel) || ((sel >> 3) < LDT_FIRST_ENTRY);
247 }
248
249
250 /***********************************************************************
251  *           wine_ldt_get_ptr
252  *
253  * Convert a segment:offset pair to a linear pointer.
254  * Note: we don't lock the LDT since this has to be fast.
255  */
256 void *wine_ldt_get_ptr( unsigned short sel, unsigned long offset )
257 {
258     int index;
259
260     if (is_gdt_sel(sel))  /* GDT selector */
261         return (void *)offset;
262     if ((index = (sel >> 3)) < LDT_FIRST_ENTRY)  /* system selector */
263         return (void *)offset;
264     if (!(wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_32BIT)) offset &= 0xffff;
265     return (char *)wine_ldt_copy.base[index] + offset;
266 }
267
268
269 /***********************************************************************
270  *           wine_ldt_alloc_entries
271  *
272  * Allocate a number of consecutive ldt entries, without setting the LDT contents.
273  * Return a selector for the first entry.
274  */
275 unsigned short wine_ldt_alloc_entries( int count )
276 {
277     int i, index, size = 0;
278
279     if (count <= 0) return 0;
280     lock_ldt();
281     for (i = LDT_FIRST_ENTRY; i < LDT_SIZE; i++)
282     {
283         if (wine_ldt_copy.flags[i] & WINE_LDT_FLAGS_ALLOCATED) size = 0;
284         else if (++size >= count)  /* found a large enough block */
285         {
286             index = i - size + 1;
287
288             /* mark selectors as allocated */
289             for (i = 0; i < count; i++) wine_ldt_copy.flags[index + i] |= WINE_LDT_FLAGS_ALLOCATED;
290             unlock_ldt();
291             return (index << 3) | 7;
292         }
293     }
294     unlock_ldt();
295     return 0;
296 }
297
298
299 /***********************************************************************
300  *           wine_ldt_realloc_entries
301  *
302  * Reallocate a number of consecutive ldt entries, without changing the LDT contents.
303  * Return a selector for the first entry.
304  */
305 unsigned short wine_ldt_realloc_entries( unsigned short sel, int oldcount, int newcount )
306 {
307     int i;
308
309     if (oldcount < newcount)  /* we need to add selectors */
310     {
311         int index = sel >> 3;
312
313         lock_ldt();
314         /* check if the next selectors are free */
315         if (index + newcount > LDT_SIZE) i = oldcount;
316         else
317             for (i = oldcount; i < newcount; i++)
318                 if (wine_ldt_copy.flags[index+i] & WINE_LDT_FLAGS_ALLOCATED) break;
319
320         if (i < newcount)  /* they are not free */
321         {
322             wine_ldt_free_entries( sel, oldcount );
323             sel = wine_ldt_alloc_entries( newcount );
324         }
325         else  /* mark the selectors as allocated */
326         {
327             for (i = oldcount; i < newcount; i++)
328                 wine_ldt_copy.flags[index+i] |= WINE_LDT_FLAGS_ALLOCATED;
329         }
330         unlock_ldt();
331     }
332     else if (oldcount > newcount) /* we need to remove selectors */
333     {
334         wine_ldt_free_entries( sel + (newcount << 3), newcount - oldcount );
335     }
336     return sel;
337 }
338
339
340 /***********************************************************************
341  *           wine_ldt_free_entries
342  *
343  * Free a number of consecutive ldt entries and clear their contents.
344  */
345 void wine_ldt_free_entries( unsigned short sel, int count )
346 {
347     int index;
348
349     lock_ldt();
350     for (index = sel >> 3; count > 0; count--, index++)
351     {
352         internal_set_entry( sel, &null_entry );
353         wine_ldt_copy.flags[index] = 0;
354     }
355     unlock_ldt();
356 }
357
358
359 static int global_fs_sel = -1;  /* global selector for %fs shared among all threads */
360
361 /***********************************************************************
362  *           wine_ldt_alloc_fs
363  *
364  * Allocate an LDT entry for a %fs selector, reusing a global
365  * GDT selector if possible. Return the selector value.
366  */
367 unsigned short wine_ldt_alloc_fs(void)
368 {
369     if (global_fs_sel == -1)
370     {
371 #ifdef __linux__
372         struct modify_ldt_s ldt_info;
373         int ret;
374
375         /* the preloader may have allocated it already */
376         global_fs_sel = wine_get_fs();
377         if (global_fs_sel && is_gdt_sel(global_fs_sel)) return global_fs_sel;
378
379         ldt_info.entry_number = -1;
380         fill_modify_ldt_struct( &ldt_info, &null_entry );
381         if ((ret = set_thread_area( &ldt_info ) < 0))
382         {
383             global_fs_sel = 0;  /* don't try it again */
384             if (errno != ENOSYS) perror( "set_thread_area" );
385         }
386         else global_fs_sel = (ldt_info.entry_number << 3) | 3;
387 #elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
388         global_fs_sel = GSEL( GUFS_SEL, SEL_UPL );
389 #endif
390     }
391     if (global_fs_sel > 0) return global_fs_sel;
392     return wine_ldt_alloc_entries( 1 );
393 }
394
395
396 /***********************************************************************
397  *           wine_ldt_init_fs
398  *
399  * Initialize the entry for the %fs selector of the current thread, and
400  * set the thread %fs register.
401  *
402  * Note: this runs in the context of the new thread, so cannot acquire locks.
403  */
404 void wine_ldt_init_fs( unsigned short sel, const LDT_ENTRY *entry )
405 {
406     if ((sel & ~3) == (global_fs_sel & ~3))
407     {
408 #ifdef __linux__
409         struct modify_ldt_s ldt_info;
410         int ret;
411
412         ldt_info.entry_number = sel >> 3;
413         fill_modify_ldt_struct( &ldt_info, entry );
414         if ((ret = set_thread_area( &ldt_info ) < 0)) perror( "set_thread_area" );
415 #elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
416         i386_set_fsbase( wine_ldt_get_base( entry ));
417 #endif
418     }
419     else  /* LDT selector */
420     {
421         internal_set_entry( sel, entry );
422     }
423     wine_set_fs( sel );
424 }
425
426
427 /***********************************************************************
428  *           wine_ldt_free_fs
429  *
430  * Free a %fs selector returned by wine_ldt_alloc_fs.
431  */
432 void wine_ldt_free_fs( unsigned short sel )
433 {
434     if (is_gdt_sel(sel)) return;  /* nothing to do */
435     if (!((wine_get_fs() ^ sel) & ~3))
436     {
437         /* FIXME: if freeing current %fs we cannot acquire locks */
438         wine_set_fs( 0 );
439         internal_set_entry( sel, &null_entry );
440         wine_ldt_copy.flags[sel >> 3] = 0;
441     }
442     else wine_ldt_free_entries( sel, 1 );
443 }
444
445
446 /***********************************************************************
447  *           selector access functions
448  */
449 __ASM_GLOBAL_FUNC( wine_get_cs, "movw %cs,%ax\n\tret" )
450 __ASM_GLOBAL_FUNC( wine_get_ds, "movw %ds,%ax\n\tret" )
451 __ASM_GLOBAL_FUNC( wine_get_es, "movw %es,%ax\n\tret" )
452 __ASM_GLOBAL_FUNC( wine_get_fs, "movw %fs,%ax\n\tret" )
453 __ASM_GLOBAL_FUNC( wine_get_gs, "movw %gs,%ax\n\tret" )
454 __ASM_GLOBAL_FUNC( wine_get_ss, "movw %ss,%ax\n\tret" )
455 __ASM_GLOBAL_FUNC( wine_set_fs, "movl 4(%esp),%eax\n\tmovw %ax,%fs\n\tret" )
456 __ASM_GLOBAL_FUNC( wine_set_gs, "movl 4(%esp),%eax\n\tmovw %ax,%gs\n\tret" )
457
458 #endif /* __i386__ */