[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / ke / i386 / ldt.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/ldt.c
5 * PURPOSE: LDT managment
6 *
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
8 * Stefan Ginsberg (stefan.ginsberg@reactos.org)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 static KSPIN_LOCK LdtLock;
20 static KSPIN_LOCK GdtLock;
21
22 /* FUNCTIONS *****************************************************************/
23
24 NTSTATUS
25 NTAPI
26 Ke386GetGdtEntryThread(IN PKTHREAD Thread,
27 IN ULONG Offset,
28 IN PKGDTENTRY Descriptor)
29 {
30 /* Make sure the offset isn't outside the allowed range */
31 if (Offset >= (KGDT_NUMBER * sizeof(KGDTENTRY)))
32 {
33 /* It is, fail */
34 return STATUS_ACCESS_VIOLATION;
35 }
36
37 /* Check if this is the LDT selector */
38 if (Offset == KGDT_LDT)
39 {
40 /* Get it from the thread's process */
41 RtlCopyMemory(Descriptor,
42 &Thread->Process->LdtDescriptor,
43 sizeof(KGDTENTRY));
44 }
45 else
46 {
47 /* Get the descriptor entry from the GDT */
48 RtlCopyMemory(Descriptor,
49 (PVOID)(((ULONG_PTR)KeGetPcr()->GDT) + Offset),
50 sizeof(KGDTENTRY));
51
52 /* Check if this is the TEB selector */
53 if (Offset == KGDT_R3_TEB)
54 {
55 /*
56 * Make sure we set the correct base for this thread. This is per
57 * process and is set in the GDT on context switch, so it might not
58 * be correct for the thread specified.
59 */
60 Descriptor->BaseLow =
61 (USHORT)((ULONG_PTR)(Thread->Teb) & 0xFFFF);
62 Descriptor->HighWord.Bytes.BaseMid =
63 (UCHAR)((ULONG_PTR)(Thread->Teb) >> 16);
64 Descriptor->HighWord.Bytes.BaseHi =
65 (UCHAR)((ULONG_PTR)(Thread->Teb) >> 24);
66 }
67 }
68
69 /* Success */
70 return STATUS_SUCCESS;
71 }
72
73 VOID
74 KeSetBaseGdtSelector(ULONG Entry,
75 PVOID Base)
76 {
77 KIRQL oldIrql;
78 PUSHORT Gdt;
79
80 DPRINT("KeSetBaseGdtSelector(Entry %x, Base %p)\n", Entry, Base);
81
82 KeAcquireSpinLock(&GdtLock, &oldIrql);
83
84 Gdt = (PUSHORT)KeGetPcr()->GDT;
85 Entry = (Entry & (~0x3)) / 2;
86
87 Gdt[Entry + 1] = (USHORT)(((ULONG)Base) & 0xffff);
88
89 Gdt[Entry + 2] = Gdt[Entry + 2] & ~(0xff);
90 Gdt[Entry + 2] = (USHORT)(Gdt[Entry + 2] |
91 ((((ULONG)Base) & 0xff0000) >> 16));
92
93 Gdt[Entry + 3] = Gdt[Entry + 3] & ~(0xff00);
94 Gdt[Entry + 3] = (USHORT)(Gdt[Entry + 3] |
95 ((((ULONG)Base) & 0xff000000) >> 16));
96
97 DPRINT("%x %x %x %x\n",
98 Gdt[Entry + 0],
99 Gdt[Entry + 1],
100 Gdt[Entry + 2],
101 Gdt[Entry + 3]);
102
103 KeReleaseSpinLock(&GdtLock, oldIrql);
104 }
105
106 VOID
107 KeSetGdtSelector(ULONG Entry,
108 ULONG Value1,
109 ULONG Value2)
110 {
111 KIRQL oldIrql;
112 PULONG Gdt;
113
114 DPRINT("KeSetGdtSelector(Entry %x, Value1 %x, Value2 %x)\n",
115 Entry, Value1, Value2);
116
117 KeAcquireSpinLock(&GdtLock, &oldIrql);
118
119 Gdt = (PULONG) KeGetPcr()->GDT;
120 Entry = (Entry & (~0x3)) / 4;
121
122 Gdt[Entry] = Value1;
123 Gdt[Entry + 1] = Value2;
124
125 DPRINT("%x %x\n",
126 Gdt[Entry + 0],
127 Gdt[Entry + 1]);
128
129 KeReleaseSpinLock(&GdtLock, oldIrql);
130 }
131
132 BOOLEAN PspIsDescriptorValid(PLDT_ENTRY ldt_entry)
133 {
134 ULONG Base, SegLimit;
135 /*
136 Allow invalid descriptors.
137 */
138 if(!ldt_entry->HighWord.Bits.Type &&
139 !ldt_entry->HighWord.Bits.Dpl)
140 return TRUE;
141
142 /* eliminate system descriptors and code segments other than
143 execute and execute/read and DPL<3 descriptors */
144 if(!(ldt_entry->HighWord.Bits.Type & 0x10) ||
145 (ldt_entry->HighWord.Bits.Type & 0x8 &&
146 ldt_entry->HighWord.Bits.Type & 0x4) ||
147 ldt_entry->HighWord.Bits.Dpl != 3 ||
148 ldt_entry->HighWord.Bits.Reserved_0) return FALSE;
149
150 if(!ldt_entry->HighWord.Bits.Pres) return TRUE;
151
152 Base=ldt_entry->BaseLow | (ldt_entry->HighWord.Bytes.BaseMid << 16) |
153 (ldt_entry->HighWord.Bytes.BaseHi << 24);
154
155 SegLimit=ldt_entry->LimitLow |
156 (ldt_entry->HighWord.Bits.LimitHi << 16);
157
158 if(ldt_entry->HighWord.Bits.Type & 0x4)
159 {
160 SegLimit=(ldt_entry->HighWord.Bits.Default_Big) ? -1 : (USHORT)-1;
161
162 } else if(ldt_entry->HighWord.Bits.Granularity)
163 {
164 SegLimit=(SegLimit << 12) | 0xfff;
165 }
166
167 if ((Base + SegLimit > (ULONG_PTR) MmHighestUserAddress) ||
168 (Base > Base+SegLimit))
169 {
170 DPRINT1("WARNING: Windows would mark this descriptor invalid!");
171 }
172
173 /*
174 Certain "DOS32" programs expect to be able to create DPMI selectors
175 that wrap the address space. Windows NT does not allow user-created
176 selectors to reach into kernel memory. However, there is no security
177 risk in allowing it; the page table will prevent access anyway.
178 */
179 return (/*(Base + SegLimit > (ULONG_PTR) MmHighestUserAddress) ||
180 (Base > Base+SegLimit) ? FALSE : TRUE*/ TRUE);
181 }
182
183 NTSTATUS NTAPI
184 NtSetLdtEntries (ULONG Selector1,
185 LDT_ENTRY LdtEntry1,
186 ULONG Selector2,
187 LDT_ENTRY LdtEntry2)
188 {
189 KIRQL oldIrql;
190 ULONG NewLdtSize = sizeof(LDT_ENTRY);
191 PUSHORT LdtDescriptor;
192 ULONG LdtBase;
193 ULONG LdtLimit;
194
195 if((Selector1 & ~0xffff) || (Selector2 & ~0xffff)) return STATUS_INVALID_LDT_DESCRIPTOR;
196
197 Selector1 &= ~0x7;
198 Selector2 &= ~0x7;
199
200 if((Selector1 && !PspIsDescriptorValid(&LdtEntry1)) ||
201 (Selector2 && !PspIsDescriptorValid(&LdtEntry2))) return STATUS_INVALID_LDT_DESCRIPTOR;
202 if(!(Selector1 || Selector2)) return STATUS_SUCCESS;
203
204 NewLdtSize += (Selector1 >= Selector2) ? Selector1 : Selector2;
205
206 KeAcquireSpinLock(&LdtLock, &oldIrql);
207
208 LdtDescriptor = (PUSHORT) &PsGetCurrentProcess()->Pcb.LdtDescriptor;
209 LdtBase = LdtDescriptor[1] |
210 ((LdtDescriptor[2] & 0xff) << 16) |
211 ((LdtDescriptor[3] & ~0xff) << 16);
212 LdtLimit = LdtDescriptor[0] |
213 ((LdtDescriptor[3] & 0xf) << 16);
214
215 if(LdtLimit < (NewLdtSize - 1))
216 {
217 /* allocate new ldt, copy old one there, set gdt ldt entry to new
218 values and load ldtr register and free old ldt */
219
220 ULONG NewLdtBase = (ULONG) ExAllocatePool(NonPagedPool,
221 NewLdtSize);
222
223 if(!NewLdtBase)
224 {
225 KeReleaseSpinLock(&LdtLock, oldIrql);
226 return STATUS_INSUFFICIENT_RESOURCES;
227 }
228
229 if(LdtBase)
230 {
231 memcpy((PVOID) NewLdtBase, (PVOID) LdtBase, LdtLimit+1);
232 }
233
234 LdtDescriptor[0] = (USHORT)((--NewLdtSize) & 0xffff);
235 LdtDescriptor[1] = (USHORT)(NewLdtBase & 0xffff);
236 LdtDescriptor[2] = (USHORT)(((NewLdtBase & 0xff0000) >> 16) | 0x8200);
237 LdtDescriptor[3] = (USHORT)(((NewLdtSize & 0xf0000) >> 16) |
238 ((NewLdtBase & 0xff000000) >> 16));
239
240 KeSetGdtSelector(KGDT_LDT,
241 ((PULONG) LdtDescriptor)[0],
242 ((PULONG) LdtDescriptor)[1]);
243
244 Ke386SetLocalDescriptorTable(KGDT_LDT);
245
246 if(LdtBase)
247 {
248 ExFreePool((PVOID) LdtBase);
249 }
250
251 LdtBase = NewLdtBase;
252 }
253
254 if(Selector1)
255 {
256 memcpy((char*)LdtBase + Selector1,
257 &LdtEntry1,
258 sizeof(LDT_ENTRY));
259 }
260
261 if(Selector2)
262 {
263 memcpy((char*)LdtBase + Selector2,
264 &LdtEntry2,
265 sizeof(LDT_ENTRY));
266 }
267
268 KeReleaseSpinLock(&LdtLock, oldIrql);
269 return STATUS_SUCCESS;
270 }
271
272