Changed the GDI driver interface to pass an opaque PHYSDEV pointer
[wine] / library / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include "winbase.h"
30 #include "wine/library.h"
31
32 #ifdef __i386__
33
34 #ifdef linux
35
36 #ifdef HAVE_SYS_SYSCALL_H
37 # include <sys/syscall.h>
38 #endif
39
40 struct modify_ldt_s
41 {
42     unsigned int  entry_number;
43     unsigned long base_addr;
44     unsigned int  limit;
45     unsigned int  seg_32bit : 1;
46     unsigned int  contents : 2;
47     unsigned int  read_exec_only : 1;
48     unsigned int  limit_in_pages : 1;
49     unsigned int  seg_not_present : 1;
50 };
51
52 static inline int modify_ldt( int func, struct modify_ldt_s *ptr,
53                                   unsigned long count )
54 {
55     int res;
56 #ifdef __PIC__
57     __asm__ __volatile__( "pushl %%ebx\n\t"
58                           "movl %2,%%ebx\n\t"
59                           "int $0x80\n\t"
60                           "popl %%ebx"
61                           : "=a" (res)
62                           : "0" (SYS_modify_ldt),
63                             "r" (func),
64                             "c" (ptr),
65                             "d" (count) );
66 #else
67     __asm__ __volatile__("int $0x80"
68                          : "=a" (res)
69                          : "0" (SYS_modify_ldt),
70                            "b" (func),
71                            "c" (ptr),
72                            "d" (count) );
73 #endif  /* __PIC__ */
74     if (res >= 0) return res;
75     errno = -res;
76     return -1;
77 }
78
79 #endif  /* linux */
80
81 #if defined(__svr4__) || defined(_SCO_DS)
82 #include <sys/sysi86.h>
83 extern int sysi86(int,void*);
84 #ifndef __sun__
85 #include <sys/seg.h>
86 #endif
87 #endif
88
89 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
90 #include <machine/segments.h>
91
92 extern int i386_get_ldt(int, union descriptor *, int);
93 extern int i386_set_ldt(int, union descriptor *, int);
94 #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
95
96 #endif  /* __i386__ */
97
98 /* local copy of the LDT */
99 struct __wine_ldt_copy wine_ldt_copy;
100
101
102 /***********************************************************************
103  *           ldt_get_entry
104  *
105  * Retrieve an LDT entry.
106  */
107 void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry )
108 {
109     int index = sel >> 3;
110     wine_ldt_set_base(  entry, wine_ldt_copy.base[index] );
111     wine_ldt_set_limit( entry, wine_ldt_copy.limit[index] );
112     wine_ldt_set_flags( entry, wine_ldt_copy.flags[index] );
113 }
114
115
116 /***********************************************************************
117  *           ldt_set_entry
118  *
119  * Set an LDT entry.
120  */
121 int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry )
122 {
123     int ret = 0, index = sel >> 3;
124
125     /* Entry 0 must not be modified; its base and limit are always 0 */
126     if (!index) return 0;
127
128 #ifdef __i386__
129
130 #ifdef linux
131     {
132         struct modify_ldt_s ldt_info;
133
134         ldt_info.entry_number    = index;
135         ldt_info.base_addr       = (unsigned long)wine_ldt_get_base(entry);
136         ldt_info.limit           = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
137         ldt_info.seg_32bit       = entry->HighWord.Bits.Default_Big;
138         ldt_info.contents        = (entry->HighWord.Bits.Type >> 2) & 3;
139         ldt_info.read_exec_only  = !(entry->HighWord.Bits.Type & 2);
140         ldt_info.limit_in_pages  = entry->HighWord.Bits.Granularity;
141         ldt_info.seg_not_present = !entry->HighWord.Bits.Pres;
142
143         if ((ret = modify_ldt(1, &ldt_info, sizeof(ldt_info))) < 0)
144             perror( "modify_ldt" );
145     }
146 #endif  /* linux */
147
148 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
149     {
150         LDT_ENTRY entry_copy = *entry;
151         /* The kernel will only let us set LDTs with user priority level */
152         if (entry_copy.HighWord.Bits.Pres
153             && entry_copy.HighWord.Bits.Dpl != 3)
154                 entry_copy.HighWord.Bits.Dpl = 3;
155         ret = i386_set_ldt(index, (union descriptor *)&entry_copy, 1);
156         if (ret < 0)
157         {
158             perror("i386_set_ldt");
159             fprintf( stderr, "Did you reconfigure the kernel with \"options USER_LDT\"?\n" );
160             exit(1);
161         }
162     }
163 #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */
164
165 #if defined(__svr4__) || defined(_SCO_DS)
166     {
167         struct ssd ldt_mod;
168         ldt_mod.sel  = sel;
169         ldt_mod.bo   = (unsigned long)wine_ldt_get_base(entry);
170         ldt_mod.ls   = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
171         ldt_mod.acc1 = entry->HighWord.Bytes.Flags1;
172         ldt_mod.acc2 = entry->HighWord.Bytes.Flags2 >> 4;
173         if ((ret = sysi86(SI86DSCR, &ldt_mod)) == -1) perror("sysi86");
174     }
175 #endif
176
177 #endif  /* __i386__ */
178
179     if (ret >= 0)
180     {
181         wine_ldt_copy.base[index]  = wine_ldt_get_base(entry);
182         wine_ldt_copy.limit[index] = wine_ldt_get_limit(entry);
183         wine_ldt_copy.flags[index] = (entry->HighWord.Bits.Type |
184                                  (entry->HighWord.Bits.Default_Big ? WINE_LDT_FLAGS_32BIT : 0) |
185                                  (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED));
186     }
187     return ret;
188 }