- Clarify a check and don't cast KPCR->GDT to PKIPCR (spotted by Timo).
[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 %x)\n",
81 Entry, Base);
82
83 KeAcquireSpinLock(&GdtLock, &oldIrql);
84
85 Gdt = (PUSHORT)KeGetPcr()->GDT;
86 Entry = (Entry & (~0x3)) / 2;
87
88 Gdt[Entry + 1] = (USHORT)(((ULONG)Base) & 0xffff);
89
90 Gdt[Entry + 2] = Gdt[Entry + 2] & ~(0xff);
91 Gdt[Entry + 2] = (USHORT)(Gdt[Entry + 2] |
92 ((((ULONG)Base) & 0xff0000) >> 16));
93
94 Gdt[Entry + 3] = Gdt[Entry + 3] & ~(0xff00);
95 Gdt[Entry + 3] = (USHORT)(Gdt[Entry + 3] |
96 ((((ULONG)Base) & 0xff000000) >> 16));
97
98 DPRINT("%x %x %x %x\n",
99 Gdt[Entry + 0],
100 Gdt[Entry + 1],
101 Gdt[Entry + 2],
102 Gdt[Entry + 3]);
103
104 KeReleaseSpinLock(&GdtLock, oldIrql);
105 }
106
107 VOID
108 KeSetGdtSelector(ULONG Entry,
109 ULONG Value1,
110 ULONG Value2)
111 {
112 KIRQL oldIrql;
113 PULONG Gdt;
114
115 DPRINT("KeSetGdtSelector(Entry %x, Value1 %x, Value2 %x)\n",
116 Entry, Value1, Value2);
117
118 KeAcquireSpinLock(&GdtLock, &oldIrql);
119
120 Gdt = (PULONG) KeGetPcr()->GDT;
121 Entry = (Entry & (~0x3)) / 4;
122
123 Gdt[Entry] = Value1;
124 Gdt[Entry + 1] = Value2;
125
126 DPRINT("%x %x\n",
127 Gdt[Entry + 0],
128 Gdt[Entry + 1]);
129
130 KeReleaseSpinLock(&GdtLock, oldIrql);
131 }
132
133 BOOLEAN PspIsDescriptorValid(PLDT_ENTRY ldt_entry)
134 {
135 ULONG Base, SegLimit;
136 /*
137 Allow invalid descriptors.
138 */
139 if(!ldt_entry->HighWord.Bits.Type &&
140 !ldt_entry->HighWord.Bits.Dpl)
141 return TRUE;
142
143 /* eliminate system descriptors and code segments other than
144 execute and execute/read and DPL<3 descriptors */
145 if(!(ldt_entry->HighWord.Bits.Type & 0x10) ||
146 (ldt_entry->HighWord.Bits.Type & 0x8 &&
147 ldt_entry->HighWord.Bits.Type & 0x4) ||
148 ldt_entry->HighWord.Bits.Dpl != 3 ||
149 ldt_entry->HighWord.Bits.Reserved_0) return FALSE;
150
151 if(!ldt_entry->HighWord.Bits.Pres) return TRUE;
152
153 Base=ldt_entry->BaseLow | (ldt_entry->HighWord.Bytes.BaseMid << 16) |
154 (ldt_entry->HighWord.Bytes.BaseHi << 24);
155
156 SegLimit=ldt_entry->LimitLow |
157 (ldt_entry->HighWord.Bits.LimitHi << 16);
158
159 if(ldt_entry->HighWord.Bits.Type & 0x4)
160 {
161 SegLimit=(ldt_entry->HighWord.Bits.Default_Big) ? -1 : (USHORT)-1;
162
163 } else if(ldt_entry->HighWord.Bits.Granularity)
164 {
165 SegLimit=(SegLimit << 12) | 0xfff;
166 }
167
168 if ((Base + SegLimit > (ULONG_PTR) MmHighestUserAddress) ||
169 (Base > Base+SegLimit))
170 {
171 DPRINT1("WARNING: Windows would mark this descriptor invalid!");
172 }
173
174 /*
175 Certain "DOS32" programs expect to be able to create DPMI selectors
176 that wrap the address space. Windows NT does not allow user-created
177 selectors to reach into kernel memory. However, there is no security
178 risk in allowing it; the page table will prevent access anyway.
179 */
180 return (/*(Base + SegLimit > (ULONG_PTR) MmHighestUserAddress) ||
181 (Base > Base+SegLimit) ? FALSE : TRUE*/ TRUE);
182 }
183
184 NTSTATUS NTAPI
185 NtSetLdtEntries (ULONG Selector1,
186 LDT_ENTRY LdtEntry1,
187 ULONG Selector2,
188 LDT_ENTRY LdtEntry2)
189 {
190 KIRQL oldIrql;
191 ULONG NewLdtSize = sizeof(LDT_ENTRY);
192 PUSHORT LdtDescriptor;
193 ULONG LdtBase;
194 ULONG LdtLimit;
195
196 if((Selector1 & ~0xffff) || (Selector2 & ~0xffff)) return STATUS_INVALID_LDT_DESCRIPTOR;
197
198 Selector1 &= ~0x7;
199 Selector2 &= ~0x7;
200
201 if((Selector1 && !PspIsDescriptorValid(&LdtEntry1)) ||
202 (Selector2 && !PspIsDescriptorValid(&LdtEntry2))) return STATUS_INVALID_LDT_DESCRIPTOR;
203 if(!(Selector1 || Selector2)) return STATUS_SUCCESS;
204
205 NewLdtSize += (Selector1 >= Selector2) ? Selector1 : Selector2;
206
207 KeAcquireSpinLock(&LdtLock, &oldIrql);
208
209 LdtDescriptor = (PUSHORT) &PsGetCurrentProcess()->Pcb.LdtDescriptor;
210 LdtBase = LdtDescriptor[1] |
211 ((LdtDescriptor[2] & 0xff) << 16) |
212 ((LdtDescriptor[3] & ~0xff) << 16);
213 LdtLimit = LdtDescriptor[0] |
214 ((LdtDescriptor[3] & 0xf) << 16);
215
216 if(LdtLimit < (NewLdtSize - 1))
217 {
218 /* allocate new ldt, copy old one there, set gdt ldt entry to new
219 values and load ldtr register and free old ldt */
220
221 ULONG NewLdtBase = (ULONG) ExAllocatePool(NonPagedPool,
222 NewLdtSize);
223
224 if(!NewLdtBase)
225 {
226 KeReleaseSpinLock(&LdtLock, oldIrql);
227 return STATUS_INSUFFICIENT_RESOURCES;
228 }
229
230 if(LdtBase)
231 {
232 memcpy((PVOID) NewLdtBase, (PVOID) LdtBase, LdtLimit+1);
233 }
234
235 LdtDescriptor[0] = (USHORT)((--NewLdtSize) & 0xffff);
236 LdtDescriptor[1] = (USHORT)(NewLdtBase & 0xffff);
237 LdtDescriptor[2] = (USHORT)(((NewLdtBase & 0xff0000) >> 16) | 0x8200);
238 LdtDescriptor[3] = (USHORT)(((NewLdtSize & 0xf0000) >> 16) |
239 ((NewLdtBase & 0xff000000) >> 16));
240
241 KeSetGdtSelector(KGDT_LDT,
242 ((PULONG) LdtDescriptor)[0],
243 ((PULONG) LdtDescriptor)[1]);
244
245 Ke386SetLocalDescriptorTable(KGDT_LDT);
246
247 if(LdtBase)
248 {
249 ExFreePool((PVOID) LdtBase);
250 }
251
252 LdtBase = NewLdtBase;
253 }
254
255 if(Selector1)
256 {
257 memcpy((char*)LdtBase + Selector1,
258 &LdtEntry1,
259 sizeof(LDT_ENTRY));
260 }
261
262 if(Selector2)
263 {
264 memcpy((char*)LdtBase + Selector2,
265 &LdtEntry2,
266 sizeof(LDT_ENTRY));
267 }
268
269 KeReleaseSpinLock(&LdtLock, oldIrql);
270 return STATUS_SUCCESS;
271 }
272
273