xref: /reactos/ntoskrnl/ke/i386/ldt.c (revision 40462c92)
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!\n");
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 
273