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