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