reactos/ntoskrnl/ke/i386/ldt.c

273 lines
7.3 KiB
C
Raw Permalink Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/ke/i386/ldt.c
* PURPOSE: LDT management
*
* PROGRAMMERS: David Welch (welch@cwcom.net)
* Stefan Ginsberg (stefan.ginsberg@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
static KSPIN_LOCK LdtLock;
static KSPIN_LOCK GdtLock;
/* FUNCTIONS *****************************************************************/
NTSTATUS
NTAPI
Ke386GetGdtEntryThread(IN PKTHREAD Thread,
IN ULONG Offset,
IN PKGDTENTRY Descriptor)
{
/* Make sure the offset isn't outside the allowed range */
if (Offset >= (KGDT_NUMBER * sizeof(KGDTENTRY)))
{
/* It is, fail */
return STATUS_ACCESS_VIOLATION;
}
/* Check if this is the LDT selector */
if (Offset == KGDT_LDT)
{
/* Get it from the thread's process */
RtlCopyMemory(Descriptor,
&Thread->Process->LdtDescriptor,
sizeof(KGDTENTRY));
}
else
{
/* Get the descriptor entry from the GDT */
RtlCopyMemory(Descriptor,
(PVOID)(((ULONG_PTR)KeGetPcr()->GDT) + Offset),
sizeof(KGDTENTRY));
/* Check if this is the TEB selector */
if (Offset == KGDT_R3_TEB)
{
/*
* Make sure we set the correct base for this thread. This is per
* process and is set in the GDT on context switch, so it might not
* be correct for the thread specified.
*/
Descriptor->BaseLow =
(USHORT)((ULONG_PTR)(Thread->Teb) & 0xFFFF);
Descriptor->HighWord.Bytes.BaseMid =
(UCHAR)((ULONG_PTR)(Thread->Teb) >> 16);
Descriptor->HighWord.Bytes.BaseHi =
(UCHAR)((ULONG_PTR)(Thread->Teb) >> 24);
}
}
/* Success */
return STATUS_SUCCESS;
}
VOID
KeSetBaseGdtSelector(ULONG Entry,
PVOID Base)
{
KIRQL oldIrql;
PUSHORT Gdt;
DPRINT("KeSetBaseGdtSelector(Entry %x, Base %p)\n", Entry, Base);
KeAcquireSpinLock(&GdtLock, &oldIrql);
Gdt = (PUSHORT)KeGetPcr()->GDT;
Entry = (Entry & (~0x3)) / 2;
Gdt[Entry + 1] = (USHORT)(((ULONG)Base) & 0xffff);
Gdt[Entry + 2] = Gdt[Entry + 2] & ~(0xff);
Gdt[Entry + 2] = (USHORT)(Gdt[Entry + 2] |
((((ULONG)Base) & 0xff0000) >> 16));
Gdt[Entry + 3] = Gdt[Entry + 3] & ~(0xff00);
Gdt[Entry + 3] = (USHORT)(Gdt[Entry + 3] |
((((ULONG)Base) & 0xff000000) >> 16));
DPRINT("%x %x %x %x\n",
Gdt[Entry + 0],
Gdt[Entry + 1],
Gdt[Entry + 2],
Gdt[Entry + 3]);
KeReleaseSpinLock(&GdtLock, oldIrql);
}
VOID
KeSetGdtSelector(ULONG Entry,
ULONG Value1,
ULONG Value2)
{
KIRQL oldIrql;
PULONG Gdt;
DPRINT("KeSetGdtSelector(Entry %x, Value1 %x, Value2 %x)\n",
Entry, Value1, Value2);
KeAcquireSpinLock(&GdtLock, &oldIrql);
Gdt = (PULONG) KeGetPcr()->GDT;
Entry = (Entry & (~0x3)) / 4;
Gdt[Entry] = Value1;
Gdt[Entry + 1] = Value2;
DPRINT("%x %x\n",
Gdt[Entry + 0],
Gdt[Entry + 1]);
KeReleaseSpinLock(&GdtLock, oldIrql);
}
BOOLEAN PspIsDescriptorValid(PLDT_ENTRY ldt_entry)
{
ULONG Base, SegLimit;
/*
Allow invalid descriptors.
*/
if(!ldt_entry->HighWord.Bits.Type &&
!ldt_entry->HighWord.Bits.Dpl)
return TRUE;
/* eliminate system descriptors and code segments other than
execute and execute/read and DPL<3 descriptors */
if(!(ldt_entry->HighWord.Bits.Type & 0x10) ||
(ldt_entry->HighWord.Bits.Type & 0x8 &&
ldt_entry->HighWord.Bits.Type & 0x4) ||
ldt_entry->HighWord.Bits.Dpl != 3 ||
ldt_entry->HighWord.Bits.Reserved_0) return FALSE;
if(!ldt_entry->HighWord.Bits.Pres) return TRUE;
Base=ldt_entry->BaseLow | (ldt_entry->HighWord.Bytes.BaseMid << 16) |
(ldt_entry->HighWord.Bytes.BaseHi << 24);
SegLimit=ldt_entry->LimitLow |
(ldt_entry->HighWord.Bits.LimitHi << 16);
if(ldt_entry->HighWord.Bits.Type & 0x4)
{
SegLimit=(ldt_entry->HighWord.Bits.Default_Big) ? -1 : (USHORT)-1;
} else if(ldt_entry->HighWord.Bits.Granularity)
{
SegLimit=(SegLimit << 12) | 0xfff;
}
if ((Base + SegLimit > (ULONG_PTR) MmHighestUserAddress) ||
(Base > Base+SegLimit))
{
DPRINT1("WARNING: Windows would mark this descriptor invalid!\n");
}
/*
Certain "DOS32" programs expect to be able to create DPMI selectors
that wrap the address space. Windows NT does not allow user-created
selectors to reach into kernel memory. However, there is no security
risk in allowing it; the page table will prevent access anyway.
*/
return (/*(Base + SegLimit > (ULONG_PTR) MmHighestUserAddress) ||
(Base > Base+SegLimit) ? FALSE : TRUE*/ TRUE);
}
NTSTATUS NTAPI
NtSetLdtEntries (ULONG Selector1,
LDT_ENTRY LdtEntry1,
ULONG Selector2,
LDT_ENTRY LdtEntry2)
{
KIRQL oldIrql;
ULONG NewLdtSize = sizeof(LDT_ENTRY);
PUSHORT LdtDescriptor;
ULONG LdtBase;
ULONG LdtLimit;
if((Selector1 & ~0xffff) || (Selector2 & ~0xffff)) return STATUS_INVALID_LDT_DESCRIPTOR;
Selector1 &= ~0x7;
Selector2 &= ~0x7;
if((Selector1 && !PspIsDescriptorValid(&LdtEntry1)) ||
(Selector2 && !PspIsDescriptorValid(&LdtEntry2))) return STATUS_INVALID_LDT_DESCRIPTOR;
if(!(Selector1 || Selector2)) return STATUS_SUCCESS;
NewLdtSize += (Selector1 >= Selector2) ? Selector1 : Selector2;
KeAcquireSpinLock(&LdtLock, &oldIrql);
LdtDescriptor = (PUSHORT) &PsGetCurrentProcess()->Pcb.LdtDescriptor;
LdtBase = LdtDescriptor[1] |
((LdtDescriptor[2] & 0xff) << 16) |
((LdtDescriptor[3] & ~0xff) << 16);
LdtLimit = LdtDescriptor[0] |
((LdtDescriptor[3] & 0xf) << 16);
if(LdtLimit < (NewLdtSize - 1))
{
/* allocate new ldt, copy old one there, set gdt ldt entry to new
values and load ldtr register and free old ldt */
ULONG NewLdtBase = (ULONG) ExAllocatePool(NonPagedPool,
NewLdtSize);
if(!NewLdtBase)
{
KeReleaseSpinLock(&LdtLock, oldIrql);
return STATUS_INSUFFICIENT_RESOURCES;
}
if(LdtBase)
{
memcpy((PVOID) NewLdtBase, (PVOID) LdtBase, LdtLimit+1);
}
LdtDescriptor[0] = (USHORT)((--NewLdtSize) & 0xffff);
LdtDescriptor[1] = (USHORT)(NewLdtBase & 0xffff);
LdtDescriptor[2] = (USHORT)(((NewLdtBase & 0xff0000) >> 16) | 0x8200);
LdtDescriptor[3] = (USHORT)(((NewLdtSize & 0xf0000) >> 16) |
((NewLdtBase & 0xff000000) >> 16));
KeSetGdtSelector(KGDT_LDT,
((PULONG) LdtDescriptor)[0],
((PULONG) LdtDescriptor)[1]);
Ke386SetLocalDescriptorTable(KGDT_LDT);
if(LdtBase)
{
ExFreePool((PVOID) LdtBase);
}
LdtBase = NewLdtBase;
}
if(Selector1)
{
memcpy((char*)LdtBase + Selector1,
&LdtEntry1,
sizeof(LDT_ENTRY));
}
if(Selector2)
{
memcpy((char*)LdtBase + Selector2,
&LdtEntry2,
sizeof(LDT_ENTRY));
}
KeReleaseSpinLock(&LdtLock, oldIrql);
return STATUS_SUCCESS;
}