/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    psldt.c

Abstract:

    This module contains code for the process and thread ldt support.

Notes:

    The nonpaged pool consumed by the LDT is returned to the system at process
    deletion time.  The process deletion handler calls PspDeleteLdt.  We
    do not keep a reference to the process once the ldt is created.

    Note that the LDT must be kept in nonpaged memory because the EXIT_ALL
    macros that return from traps and interrupts pop ds (which may be an LDT
    selector) and then other registers.  With interrupts disabled.

--*/

#include "psp.h"

//
// Internal constants
//

#define DESCRIPTOR_GRAN     0x00800000
#define DESCRIPTOR_NP       0x00008000
#define DESCRIPTOR_SYSTEM   0x00001000
#define DESCRIPTOR_CONFORM  0x00001C00
#define DESCRIPTOR_DPL      0x00006000
#define DESCRIPTOR_TYPEDPL  0x00007F00


KMUTEX LdtMutex;

//
// Internal subroutines
//

PLDT_ENTRY
PspCreateLdt (
    IN PLDT_ENTRY Ldt,
    IN ULONG Offset,
    IN ULONG Size,
    IN ULONG AllocationSize
    );

LOGICAL
PspIsDescriptorValid (
    IN PLDT_ENTRY Descriptor
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, PspLdtInitialize)
#pragma alloc_text(PAGE, PsSetLdtEntries)
#pragma alloc_text(PAGE, NtSetLdtEntries)
#pragma alloc_text(PAGE, PspDeleteLdt)
#pragma alloc_text(PAGE, PspQueryLdtInformation)
#pragma alloc_text(PAGE, PspSetLdtSize)
#pragma alloc_text(PAGE, PspSetLdtInformation)
#pragma alloc_text(PAGE, PspCreateLdt)
#pragma alloc_text(PAGE, PspIsDescriptorValid)
#pragma alloc_text(PAGE, PspQueryDescriptorThread)
#pragma alloc_text(PAGE, PsSetProcessLdtInfo)
#endif

NTSTATUS
PspLdtInitialize (
    VOID
    )

/*++

Routine Description:

    This routine initializes the LDT support for the x86

Arguments:

    None

Return Value:

    NTSTATUS.

--*/
{
    KeInitializeMutex  (&LdtMutex, 0);
    return STATUS_SUCCESS;
}


NTSTATUS
PspQueryLdtInformation (
    IN PEPROCESS Process,
    OUT PPROCESS_LDT_INFORMATION LdtInformation,
    IN ULONG LdtInformationLength,
    OUT PULONG ReturnLength
    )
/*++

Routine Description:

    This function performs the work for the LDT portion of the query
    process information function.  It copies the contents of the LDT
    for the specified process into the user's buffer, up to the length
    of the buffer.

Arguments:

    Process -- Supplies a pointer to the process to return LDT info for
    LdtInformation -- Supplies a pointer to the buffer
    ReturnLength -- Returns the number of bytes put into the buffer

Return Value:

    NTSTATUS.

--*/
{
    ULONG CopyLength, CopyEnd;
    NTSTATUS Status;
    ULONG HeaderLength;
    ULONG Length=0, Start=0;
    LONG MutexStatus;
    PLDTINFORMATION ProcessLdtInfo;

    PAGED_CODE();

    //
    // Verify the parameters
    //

    if (LdtInformationLength < sizeof (PROCESS_LDT_INFORMATION)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // This portion of the parameters may be in user space
    //
    try {
        //
        // Capture parameters
        //
        Length = LdtInformation->Length;
        Start = LdtInformation->Start;

    } except (EXCEPTION_EXECUTE_HANDLER) {
        return GetExceptionCode ();
    }

    //
    // The buffer containing the LDT entries must be in the information
    // structure.  We subtract one LDT entry, because the structure is
    // declared to contain one.
    //
    if (LdtInformationLength - sizeof(PROCESS_LDT_INFORMATION) + sizeof(LDT_ENTRY) < Length) {

        return STATUS_INFO_LENGTH_MISMATCH;
    }

    // An LDT entry is a processor structure, and must be 8 bytes long
    ASSERT((sizeof(LDT_ENTRY) == 8));

    //
    // The length of the structure must be an even number of LDT entries
    //
    if (Length % sizeof (LDT_ENTRY)) {
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // The information to get from the LDT must start on an LDT entry
    // boundary.
    //
    if (Start % sizeof (LDT_ENTRY)) {
        return STATUS_INVALID_LDT_OFFSET;
    }

    //
    // Acquire the LDT mutex
    //

    Status = KeWaitForSingleObject (&LdtMutex,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);

    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    ProcessLdtInfo = Process->LdtInformation;

    //
    // If the process has an LDT
    //

    if ((ProcessLdtInfo) && (ProcessLdtInfo->Size)) {

        ASSERT ((ProcessLdtInfo->Ldt));

        //
        // Set the end of the copy to be the smaller of:
        //  the end of the information the user requested or
        //  the end of the information that is actually there
        //

        if (ProcessLdtInfo->Size < Start) {
           CopyEnd = Start;
        } else if (ProcessLdtInfo->Size - Start  > Length) {
            CopyEnd = Length + Start;
        } else {
            CopyEnd = ProcessLdtInfo->Size;
        }

        CopyLength = CopyEnd - Start;

        try {

            //
            // Set the length field to the actual length of the LDT
            //

            LdtInformation->Length = ProcessLdtInfo->Size;

            //
            // Copy the contents of the LDT into the user's buffer
            //

            if (CopyLength) {

                RtlCopyMemory (&(LdtInformation->LdtEntries),
                               (PCHAR)ProcessLdtInfo->Ldt + Start,
                               CopyLength);
            }

        } except(EXCEPTION_EXECUTE_HANDLER) {
            MutexStatus = KeReleaseMutex (&LdtMutex, FALSE);
            ASSERT ((MutexStatus == 0));
            return GetExceptionCode ();
        }

    } else {

        //
        // There is no LDT
        //

        CopyLength = 0;
        try {
            LdtInformation->Length = 0;
        } except (EXCEPTION_EXECUTE_HANDLER) {
            MutexStatus = KeReleaseMutex (&LdtMutex, FALSE);
            ASSERT ((MutexStatus == 0));
            return GetExceptionCode ();
        }
    }

    //
    // Set the length of the information returned
    //

    if (ARGUMENT_PRESENT (ReturnLength)) {

        try {
            HeaderLength = (PCHAR)(&(LdtInformation->LdtEntries)) -
                (PCHAR)(&(LdtInformation->Start));
            *ReturnLength = CopyLength + HeaderLength;
        } except (EXCEPTION_EXECUTE_HANDLER){
            MutexStatus = KeReleaseMutex (&LdtMutex, FALSE);
            ASSERT ((MutexStatus == 0));
            return GetExceptionCode ();
        }
    }

    MutexStatus = KeReleaseMutex (&LdtMutex, FALSE);
    ASSERT ((MutexStatus == 0));
    return STATUS_SUCCESS;
}


NTSTATUS
PspSetLdtSize (
    IN PEPROCESS Process,
    IN PPROCESS_LDT_SIZE LdtSize,
    IN ULONG LdtSizeLength
    )

/*++

Routine Description:

    This routine changes the LDT size.  It will shrink the LDT, but not
    grow it.  If the LDT shrinks by 1 or more pages from its current allocation,
    the LDT will be reallocated for the new smaller size.  If the allocated
    size of the LDT changes, the quota charge for the LDT will be reduced.

Arguments:

    Process -- Supplies a pointer to the process whose LDT is to be sized
    LdtSize -- Supplies a pointer to the size information

Return Value:

    NTSTATUS.

--*/
{
    ULONG OldSize = 0, NewSize;
    LONG MutexState;
    ULONG Length=0;
    PLDT_ENTRY OldLdt = NULL;
    NTSTATUS Status;
    PLDTINFORMATION ProcessLdtInfo;
    PLDT_ENTRY Ldt;

    PAGED_CODE();

    //
    // Verify the parameters
    //
    if (LdtSizeLength != sizeof (PROCESS_LDT_SIZE)){
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // The following parameters may be in user space
    //
    try {
        //
        // Capture the new LDT length
        //
        Length = LdtSize->Length;

    } except(EXCEPTION_EXECUTE_HANDLER){
        return GetExceptionCode ();
    }


    ASSERT((sizeof(LDT_ENTRY) == 8));

    //
    // The LDT must always be an integral number of LDT_ENTRIES
    //
    if (Length % sizeof(LDT_ENTRY)) {
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // Acquire the LDT Mutex
    //

    Status = KeWaitForSingleObject (&LdtMutex,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);

    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    //
    // If there isn't an LDT we can't set the size of the LDT
    //
    ProcessLdtInfo = Process->LdtInformation;
    if ((ProcessLdtInfo == NULL) || (ProcessLdtInfo->Size == 0)) {
        MutexState = KeReleaseMutex( &LdtMutex, FALSE );
        ASSERT((MutexState == 0));
        return STATUS_NO_LDT;
    }

    //
    // This function cannot be used to grow the LDT
    //
    if (Length > ProcessLdtInfo->Size) {
        MutexState = KeReleaseMutex( &LdtMutex, FALSE );
        ASSERT((MutexState == 0));
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // Later, we will set ProcessLdtInfo->LDT = LDT.  We may set the value
    // of LDT in the if statement below, but there is one case where we
    // don't
    //
    Ldt = ProcessLdtInfo->Ldt;

    //
    // Adjust the size of the LDT
    //

    ProcessLdtInfo->Size = Length;

    //
    // Free some of the LDT memory if conditions allow
    //

    if ( Length == 0 ) {

        OldSize = ProcessLdtInfo->AllocatedSize;
        OldLdt = ProcessLdtInfo->Ldt;

        ProcessLdtInfo->AllocatedSize = 0;
        Ldt = NULL;

    } else if ((ProcessLdtInfo->AllocatedSize - ProcessLdtInfo->Size) >= PAGE_SIZE) {

        OldSize = ProcessLdtInfo->AllocatedSize;
        OldLdt = ProcessLdtInfo->Ldt;

        //
        // Calculate new LDT size (lowest integer number of pages
        // large enough)
        //

        ProcessLdtInfo->AllocatedSize = ROUND_TO_PAGES (ProcessLdtInfo->Size);

        //
        // Reallocate and copy the LDT
        //

        Ldt = PspCreateLdt (ProcessLdtInfo->Ldt,
                            0,
                            ProcessLdtInfo->Size,
                            ProcessLdtInfo->AllocatedSize);

        if ( Ldt == NULL ) {

            //
            // We cannot reduce the allocation, but we can reduce the
            // LDT selector limit (done using Ke386SetLdtProcess)
            //

            Ldt = OldLdt;
            ProcessLdtInfo->AllocatedSize = OldSize;
            OldLdt = NULL;
        }
    }

    ProcessLdtInfo->Ldt = Ldt;

    //
    // Change the limit on the Process LDT
    //

    Ke386SetLdtProcess (&(Process->Pcb),
                        ProcessLdtInfo->Ldt,
                        ProcessLdtInfo->Size);

    NewSize = ProcessLdtInfo->AllocatedSize;

    MutexState = KeReleaseMutex (&LdtMutex, FALSE);

    ASSERT((MutexState == 0));

    //
    // If we resized the LDT, free the old one and reduce the quota charge
    //

    if (OldLdt) {
        ExFreePool (OldLdt);

        PsReturnProcessNonPagedPoolQuota (Process,
                                          OldSize - NewSize);
    }

    return STATUS_SUCCESS;
}


NTSTATUS
PspSetLdtInformation(
    IN PEPROCESS Process,
    IN PPROCESS_LDT_INFORMATION LdtInformation,
    IN ULONG LdtInformationLength
    )

/*++

Routine Description:

    This function alters the ldt for a specified process.  It can alter
    portions of the LDT, or the whole LDT.  If an LDT is created or
    grown, the specified process will be charged the quota for the LDT.
    Each descriptor that is set will be verified.

Arguments:

    Process -- Supplies a pointer to the process whose LDT is to be modified
    LdtInformation -- Supplies a pointer to the information about the LDT
        modifications
    LdtInformationLength -- Supplies the length of the LdtInformation
        structure.

--*/
{
    NTSTATUS Status;
    PLDT_ENTRY OldLdt = NULL;
    ULONG OldSize = 0;
    ULONG AllocatedSize;
    ULONG Size;
    ULONG MutexState;
    ULONG LdtOffset;
    PLDT_ENTRY CurrentDescriptor;
    PPROCESS_LDT_INFORMATION LdtInfo=NULL;
    PLDTINFORMATION ProcessLdtInfo;
    PLDT_ENTRY Ldt;

    PAGED_CODE();

    if (LdtInformationLength < sizeof (PROCESS_LDT_INFORMATION)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    Status = STATUS_SUCCESS;
    LdtInfo = ExAllocatePoolWithQuotaTag (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
                                          LdtInformationLength,
                                          'dLsP');

    if (LdtInfo == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // allocate a local buffer to capture the ldt information to
    //
    try {
        //
        // Copy the information the user is supplying
        //
        RtlCopyMemory (LdtInfo,
                       LdtInformation,
                       LdtInformationLength);

    } except (EXCEPTION_EXECUTE_HANDLER) {
        ExFreePool (LdtInfo);
        Status = GetExceptionCode ();
        return Status;
    }

    //
    // Verify that the Start and Length are plausible
    //
    if (LdtInfo->Start & 0xFFFF0000) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_OFFSET;
    }

    if (LdtInfo->Length & 0xFFFF0000) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // Ensure that the buffer it large enough to contain the specified number
    // of selectors.
    //
    if (LdtInformationLength - sizeof (PROCESS_LDT_INFORMATION) + sizeof (LDT_ENTRY) < LdtInfo->Length) {
        ExFreePool (LdtInfo);
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // The info to set must be an integral number of selectors
    //
    if (LdtInfo->Length % sizeof (LDT_ENTRY)) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // The beginning of the info must be on a selector boundary
    //
    if (LdtInfo->Start % sizeof (LDT_ENTRY)) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_OFFSET;
    }

    //
    // Verify all of the descriptors.
    //

    for (CurrentDescriptor = LdtInfo->LdtEntries;
         (PCHAR)CurrentDescriptor < (PCHAR)LdtInfo->LdtEntries + LdtInfo->Length;
          CurrentDescriptor++) {
        if (!PspIsDescriptorValid (CurrentDescriptor)) {
            ExFreePool (LdtInfo);
            return STATUS_INVALID_LDT_DESCRIPTOR;
        }
    }

    //
    // Acquire the LDT Mutex
    //

    Status = KeWaitForSingleObject (&LdtMutex,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);
    if (!NT_SUCCESS (Status)) {
        ExFreePool (LdtInfo);
        return Status;
    }

    ProcessLdtInfo = Process->LdtInformation;

    //
    // If the process doen't have an LDT information structure, allocate
    //  one and attach it to the process
    //
    if (ProcessLdtInfo == NULL) {
        ProcessLdtInfo = ExAllocatePoolWithTag (NonPagedPool,
                                                sizeof(LDTINFORMATION),
                                                'dLsP');
        if (ProcessLdtInfo == NULL) {
            goto SetInfoCleanup;
        }
        Process->LdtInformation = ProcessLdtInfo;
        RtlZeroMemory (ProcessLdtInfo, sizeof (LDTINFORMATION));
    }

    //
    // If we are supposed to remove the LDT
    //
    if (LdtInfo->Length == 0)  {

        //
        // Remove the process' LDT
        //

        if (ProcessLdtInfo->Ldt) {
            OldSize = ProcessLdtInfo->AllocatedSize;
            OldLdt = ProcessLdtInfo->Ldt;

            ProcessLdtInfo->AllocatedSize = 0;
            ProcessLdtInfo->Size = 0;
            ProcessLdtInfo->Ldt = NULL;

            Ke386SetLdtProcess (&Process->Pcb,
                                NULL,
                                0);

            PsReturnProcessNonPagedPoolQuota (Process, OldSize);
        }


    } else if (ProcessLdtInfo->Ldt == NULL) {

        //
        // Create a new LDT for the process
        //

        //
        // Allocate an integral number of pages for the LDT.
        //

        ASSERT(((PAGE_SIZE % 2) == 0));

        AllocatedSize = ROUND_TO_PAGES (LdtInfo->Start + LdtInfo->Length);

        Size = LdtInfo->Start + LdtInfo->Length;

        Ldt = PspCreateLdt (LdtInfo->LdtEntries,
                            LdtInfo->Start,
                            Size,
                            AllocatedSize);

        if (Ldt == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto SetInfoCleanup;
        }

        Status = PsChargeProcessNonPagedPoolQuota (Process,
                                                   AllocatedSize);

        if (!NT_SUCCESS (Status)) {
            ExFreePool (Ldt);
            Ldt = NULL;
            goto SetInfoCleanup;
        }

        ProcessLdtInfo->Ldt = Ldt;
        ProcessLdtInfo->Size = Size;
        ProcessLdtInfo->AllocatedSize = AllocatedSize;
        Ke386SetLdtProcess (&Process->Pcb,
                            ProcessLdtInfo->Ldt,
                            ProcessLdtInfo->Size);


    } else if (LdtInfo->Length + LdtInfo->Start > ProcessLdtInfo->Size) {

        //
        // Grow the process' LDT
        //

        if (LdtInfo->Length + LdtInfo->Start > ProcessLdtInfo->AllocatedSize) {

            //
            // Current LDT allocation is not large enough, so create a
            // new larger LDT
            //

            OldSize = ProcessLdtInfo->AllocatedSize;

            Size = LdtInfo->Start + LdtInfo->Length;
            AllocatedSize = ROUND_TO_PAGES (Size);

            Ldt = PspCreateLdt (ProcessLdtInfo->Ldt,
                                0,
                                OldSize,
                                AllocatedSize);

            if (Ldt == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto SetInfoCleanup;
            }


            Status = PsChargeProcessNonPagedPoolQuota (Process,
                                                       AllocatedSize);

            if (!NT_SUCCESS (Status)) {
                ExFreePool (Ldt);
                Ldt = NULL;
                goto SetInfoCleanup;
            }
            PsReturnProcessNonPagedPoolQuota (Process,
                                              OldSize);

            //
            // Swap LDT information
            //
            OldLdt = ProcessLdtInfo->Ldt;
            ProcessLdtInfo->Ldt = Ldt;
            ProcessLdtInfo->Size = Size;
            ProcessLdtInfo->AllocatedSize = AllocatedSize;

            //
            // Put new selectors into the new ldt
            //
            RtlCopyMemory ((PCHAR)(ProcessLdtInfo->Ldt) + LdtInfo->Start,
                           LdtInfo->LdtEntries,
                           LdtInfo->Length);

            Ke386SetLdtProcess (&Process->Pcb,
                                ProcessLdtInfo->Ldt,
                                ProcessLdtInfo->Size);


        } else {

            //
            // Current LDT allocation is large enough
            //

            ProcessLdtInfo->Size = LdtInfo->Length + LdtInfo->Start;

            Ke386SetLdtProcess (&Process->Pcb,
                                ProcessLdtInfo->Ldt,
                                ProcessLdtInfo->Size);

            //
            // Change the selectors in the table
            //
            for (LdtOffset = LdtInfo->Start, CurrentDescriptor = LdtInfo->LdtEntries;
                 LdtOffset < LdtInfo->Start + LdtInfo->Length;
                 LdtOffset += sizeof(LDT_ENTRY), CurrentDescriptor++) {

                Ke386SetDescriptorProcess (&Process->Pcb,
                                           LdtOffset,
                                           *CurrentDescriptor);
            }
        }
    } else {

        //
        // Simply changing some selectors
        //

        for (LdtOffset = LdtInfo->Start, CurrentDescriptor = LdtInfo->LdtEntries;
             LdtOffset < LdtInfo->Start +  LdtInfo->Length;
             LdtOffset += sizeof(LDT_ENTRY), CurrentDescriptor++) {

            Ke386SetDescriptorProcess (&Process->Pcb,
                                       LdtOffset,
                                       *CurrentDescriptor);
        }
        Status = STATUS_SUCCESS;
    }


SetInfoCleanup:

    MutexState = KeReleaseMutex (&LdtMutex, FALSE);
    ASSERT ((MutexState == 0));

    if (OldLdt != NULL) {
        ExFreePool (OldLdt);
    }

    if (LdtInfo != NULL) {
        ExFreePool (LdtInfo);
    }

    return Status;
}

PLDT_ENTRY
PspCreateLdt (
    IN PLDT_ENTRY Ldt,
    IN ULONG Offset,
    IN ULONG Size,
    IN ULONG AllocationSize
    )

/*++

Routine Description:

    This routine allocates space in nonpaged pool for an LDT, and copies the
    specified selectors into it.  IT DOES NOT VALIDATE THE SELECTORS.
    Selector validation must be done before calling this routine.  IT
    DOES NOT CHARGE THE QUOTA FOR THE LDT.

Arguments:

    Ldt -- Supplies a pointer to the descriptors to be put into the LDT.
    Offset -- Supplies the offset in the LDT to copy the descriptors to.
    Size -- Supplies the actualsize of the new LDT
    AllocationSize -- Supplies the size to allocate

Return Value:

    Pointer to the new LDT
--*/
{
    PLDT_ENTRY NewLdt;

    PAGED_CODE();

    ASSERT ((AllocationSize >= Size));
    ASSERT (((Size % sizeof(LDT_ENTRY)) == 0));

    NewLdt = ExAllocatePoolWithTag (NonPagedPool, AllocationSize, 'dLsP');

    if (NewLdt != NULL) {
        RtlZeroMemory (NewLdt, AllocationSize);
        RtlCopyMemory ((PCHAR)NewLdt + Offset, Ldt, Size - Offset);
    }

    return NewLdt;
}



LOGICAL
PspIsDescriptorValid (
    IN PLDT_ENTRY Descriptor
    )

/*++

Routine Description:

    This function determines if the supplied descriptor is valid to put
    into a process LDT.  For the descriptor to be valid it must have the
    following characteristics:

    Base < MM_HIGHEST_USER_ADDRESS
    Base + Limit < MM_HIGHEST_USER_ADDRESS
    Type must be
        ReadWrite, ReadOnly, ExecuteRead, ExecuteOnly, or Invalid
        big or small
        normal or grow down
        Not a system descriptor (system bit is 1 == application)
            This rules out all gates, etc
        Not conforming
    DPL must be 3

Arguments:

    Descriptor -- Supplies a pointer to the descriptor to check

Return Value:

    True if the descriptor is valid (note: valid to put into an LDT.  This
        includes Invalid descriptors)
    False if not
--*/

{
    ULONG Base;
    ULONG Limit;

    PAGED_CODE();

    //
    // if descriptor is an invalid descriptor
    //

    if ((Descriptor->HighWord.Bits.Type == 0) &&
        (Descriptor->HighWord.Bits.Dpl == 0)) {

        return TRUE;
    }

    Base = Descriptor->BaseLow | (Descriptor->HighWord.Bytes.BaseMid << 16) |
           (Descriptor->HighWord.Bytes.BaseHi << 24);

    Limit = Descriptor->LimitLow | (Descriptor->HighWord.Bits.LimitHi << 16);

    //
    // Only have to check for present selectors
    //
    if (Descriptor->HighWord.Bits.Pres) {
        ULONG ActualLimit;

        if ((Descriptor->HighWord.Bits.Type&0x14) == 0x14) {
            if (Descriptor->HighWord.Bits.Default_Big == 1) {
                ActualLimit = 0xFFFFFFFF;
            } else {
                ActualLimit = 0xFFFF;
            }
        } else if (Descriptor->HighWord.Bits.Granularity == 0) {
            ActualLimit = Limit;
        } else {
            ActualLimit = (Limit<<12) + 0xFFF;
        }

        //
        // See if the segment extends into the kernel address space.
        //
        if (Base > Base + ActualLimit ||
            ((PVOID)(Base + ActualLimit) > MM_HIGHEST_USER_ADDRESS)) {
            return FALSE;
        }

        //
        // Don't let the reserved field be set.
        //
        if (Descriptor->HighWord.Bits.Reserved_0 != 0) {
            return FALSE;
        }
    }


    //
    // if Dpl is not 3
    //

    if (Descriptor->HighWord.Bits.Dpl != 3) {
        return FALSE;
    }

    //
    // if descriptor is a system descriptor (which includes gates)
    // if bit 4 of the Type field is 0, then it's a system descriptor,
    // and we don't like it.
    //

    if (!(Descriptor->HighWord.Bits.Type & 0x10)) {
        return FALSE;
    }

    //
    // if descriptor is conforming code
    //

    if (((Descriptor->HighWord.Bits.Type & 0x18) == 0x18) &&
        (Descriptor->HighWord.Bits.Type & 0x4)) {

        return FALSE;
    }

    return TRUE;
}

NTSTATUS
PspQueryDescriptorThread (
    PETHREAD Thread,
    PVOID ThreadInformation,
    ULONG ThreadInformationLength,
    PULONG ReturnLength
    )
/*++

Routine Description:

    This function retrieves a descriptor table entry for the specified thread.
    This entry may be in either the Gdt or the LDT, as specfied by the
    supplied selector

Arguments:

    Thread -- Supplies a pointer to the thread.
    ThreadInformation -- Supplies information on the descriptor.
    ThreadInformationLength -- Supplies the length of the information.
    ReturnLength -- Returns the number of bytes returned.

--*/
{
    DESCRIPTOR_TABLE_ENTRY DescriptorEntry={0};
    PEPROCESS Process;
    LONG MutexState;
    NTSTATUS Status;

    PAGED_CODE();

    ASSERT( sizeof(KGDTENTRY) == sizeof(LDT_ENTRY) );

    //
    // Verify parameters
    //

    if ( ThreadInformationLength != sizeof(DESCRIPTOR_TABLE_ENTRY) ) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    try {
        DescriptorEntry = *(PDESCRIPTOR_TABLE_ENTRY)ThreadInformation;
    } except(EXCEPTION_EXECUTE_HANDLER){
        return GetExceptionCode ();
    }


    Status = STATUS_SUCCESS;

    //
    // If its a Gdt entry, let the kernel find it for us
    //

    if ( !(DescriptorEntry.Selector & SELECTOR_TABLE_INDEX) ) {

        if ( (DescriptorEntry.Selector & 0xFFFFFFF8) >= KGDT_NUMBER * sizeof(KGDTENTRY) ) {

            return STATUS_ACCESS_VIOLATION;
        }

        try {
            Ke386GetGdtEntryThread (&Thread->Tcb,
                                    DescriptorEntry.Selector & 0xFFFFFFF8,
                                    (PKGDTENTRY) &(((PDESCRIPTOR_TABLE_ENTRY)ThreadInformation)->Descriptor));
            if (ARGUMENT_PRESENT(ReturnLength) ) {
                *ReturnLength = sizeof(LDT_ENTRY);
            }
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return GetExceptionCode ();
        }
    } else {

        //
        // it's an LDT entry, so copy it from the LDT
        //

        Process = THREAD_TO_PROCESS (Thread);

        //
        // Acquire the LDT Mutex
        //

        Status = KeWaitForSingleObject (&LdtMutex,
                                        Executive,
                                        KernelMode,
                                        FALSE,
                                        NULL);
        if (!NT_SUCCESS (Status)) {
            return Status;
        }

        if ( Process->LdtInformation == NULL ) {

            // If there is no LDT
            Status = STATUS_NO_LDT;

        } else if ( (DescriptorEntry.Selector & 0xFFFFFFF8) >=
            ((PLDTINFORMATION)(Process->LdtInformation))->Size ) {

            // Else If the selector is outside the table
            Status = STATUS_ACCESS_VIOLATION;

        } else try {

            // Else return the contents of the descriptor
            RtlCopyMemory (&(((PDESCRIPTOR_TABLE_ENTRY)ThreadInformation)->Descriptor),
                           (PCHAR)(((PLDTINFORMATION)(Process->LdtInformation))->Ldt) +
                               (DescriptorEntry.Selector & 0xFFFFFFF8),
                           sizeof(LDT_ENTRY));

            if (ARGUMENT_PRESENT(ReturnLength)) {
                *ReturnLength = sizeof(LDT_ENTRY);
            }

        } except(EXCEPTION_EXECUTE_HANDLER) {
            Status = GetExceptionCode ();
        }

        MutexState = KeReleaseMutex (&LdtMutex, FALSE);
        ASSERT ((MutexState == 0));
    }

    return Status;
}

VOID
PspDeleteLdt(
    IN PEPROCESS Process
    )
/*++

Routine Description:

    This routine frees the nonpaged pool associated with a process' LDT, if
    it has one.

Arguments:

    Process -- Supplies a pointer to the process

Return Value:

    None
--*/
{
    PLDTINFORMATION LdtInformation;

    PAGED_CODE();

    LdtInformation = Process->LdtInformation;
    if (LdtInformation != NULL) {
        if (LdtInformation->Ldt != NULL) {
            PsReturnProcessNonPagedPoolQuota (Process, LdtInformation->AllocatedSize);
            ExFreePool (LdtInformation->Ldt);
        }
        ExFreePool( LdtInformation );
    }
}

NTSTATUS
PsSetLdtEntries (
    IN ULONG Selector0,
    IN ULONG Entry0Low,
    IN ULONG Entry0Hi,
    IN ULONG Selector1,
    IN ULONG Entry1Low,
    IN ULONG Entry1Hi
    )
/*++

Routine Description:

    This routine sets up to two selectors in the current process's LDT.
    The LDT will be grown as necessary.  A selector value of 0 indicates
    that the specified selector was not passed (allowing the setting of
    a single selector).

Arguments:

    Selector0 -- Supplies the number of the first descriptor to set
    Entry0Low -- Supplies the low 32 bits of the descriptor
    Entry0Hi -- Supplies the high 32 bits of the descriptor
    Selector1 -- Supplies the number of the first descriptor to set
    Entry1Low -- Supplies the low 32 bits of the descriptor
    Entry1Hi -- Supplies the high 32 bits of the descriptor

Return Value:

    NTSTATUS.

--*/

{
    ULONG LdtSize, AllocatedSize;
    NTSTATUS Status;
    PEPROCESS Process;
    LDT_ENTRY Descriptor[2];
    PLDT_ENTRY Ldt, OldLdt;
    PLDTINFORMATION ProcessLdtInformation;
    LONG MutexState;
    ULONG Selector1Index;

    PAGED_CODE();

    //
    // Verify the selectors.  We do not allow selectors that point into
    // Kernel space, system selectors, or conforming code selectors
    //

    //
    // Verify the selectors
    //
    if ((Selector0 & 0xFFFF0000) || (Selector1 & 0xFFFF0000)) {
        return STATUS_INVALID_LDT_DESCRIPTOR;
    }

    // Change the selector values to indexes into the LDT

    Selector0 = Selector0 & ~(RPL_MASK | SELECTOR_TABLE_INDEX);
    Selector1 = Selector1 & ~(RPL_MASK | SELECTOR_TABLE_INDEX);


    //
    // Verify descriptor 0
    //

    Selector1Index = 0;
    if (Selector0) {

        Selector1Index = 1;

        *((PULONG)(&Descriptor[0]))       = Entry0Low;
        *(((PULONG)(&Descriptor[0])) + 1) = Entry0Hi;

        //
        // Validate the descriptor
        //
        if (!PspIsDescriptorValid (&Descriptor[0])) {
            return STATUS_INVALID_LDT_DESCRIPTOR;
        }
    }

    //
    // Verify descriptor 1
    //

    if (Selector1) {
        *((PULONG)(&Descriptor[Selector1Index]))       = Entry1Low;
        *(((PULONG)(&Descriptor[Selector1Index])) + 1) = Entry1Hi;

        //
        // Validate the descriptor
        //
        if (!PspIsDescriptorValid (&Descriptor[Selector1Index])) {
            return STATUS_INVALID_LDT_DESCRIPTOR;
        }
    }

    //
    // Figure out how large the LDT needs to be
    //

    if (Selector0 > Selector1) {
        LdtSize = Selector0 + sizeof(LDT_ENTRY);
    } else {
        LdtSize = Selector1 + sizeof(LDT_ENTRY);
    }

    Process = PsGetCurrentProcess();

    //
    // Acquire the LDT mutex.
    //

    Status = KeWaitForSingleObject (&LdtMutex,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);

    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    ProcessLdtInformation = Process->LdtInformation;

    //
    // Most of the time, the process will already have an LDT, and it
    // will be large enough.  for this, we just set the descriptors and
    // return
    //

    if (ProcessLdtInformation) {

        //
        // If the LDT descriptor does not have to be modified.
        //

        if (ProcessLdtInformation->Size >= LdtSize) {

            if (Selector0) {

                Ke386SetDescriptorProcess (&(Process->Pcb),
                                           Selector0,
                                           Descriptor[0]);
            }

            if (Selector1) {

                Ke386SetDescriptorProcess (&(Process->Pcb),
                                           Selector1,
                                           Descriptor[Selector1Index]);
            }

            MutexState = KeReleaseMutex (&LdtMutex, FALSE);
            ASSERT (MutexState == 0);
            return STATUS_SUCCESS;
        }

        //
        // Else if the LDT will fit in the memory currently allocated.
        //

        if (ProcessLdtInformation->AllocatedSize >= LdtSize) {

            //
            // First remove the LDT.  This will allow us to edit the memory.
            // We will then put the LDT back.  Since we have to change the
            // limit anyway, it would take two calls to the kernel ldt
            // management minimum to set the descriptors.  Each of those calls
            // would stall all of the processors in an MP system.  If we
            // didn't remove the ldt first, and we were setting two descriptors,
            // we would have to call the LDT management 3 times (once per
            // descriptor, and once to change the limit of the LDT).
            //

            Ke386SetLdtProcess (&(Process->Pcb), NULL, 0L);

            //
            // Set the Descriptors in the LDT.
            //

            if (Selector0) {
                *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)]) = Descriptor[0];
            }

            if (Selector1) {
                *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)]) = Descriptor[Selector1Index];
            }

            //
            // Set the LDT for the process
            //

            ProcessLdtInformation->Size = LdtSize;

            Ke386SetLdtProcess (&(Process->Pcb),
                                ProcessLdtInformation->Ldt,
                                ProcessLdtInformation->Size);

            MutexState = KeReleaseMutex (&LdtMutex, FALSE);
            ASSERT (MutexState == 0);
            return STATUS_SUCCESS;
        }

        //
        // Otherwise we have to grow the LDT allocation.
        //
    }

    //
    // If the process does not yet have an LDT information structure,
    // allocate and attach one.
    //

    OldLdt = NULL;

    if (!Process->LdtInformation) {
        ProcessLdtInformation = ExAllocatePoolWithTag (NonPagedPool,
                                                       sizeof(LDTINFORMATION),
                                                       'dLsP');
        if (ProcessLdtInformation == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto SetLdtEntriesCleanup;
        }
        Process->LdtInformation = ProcessLdtInformation;
        ProcessLdtInformation->Size = 0L;
        ProcessLdtInformation->AllocatedSize = 0L;
        ProcessLdtInformation->Ldt = NULL;
    }

    //
    // Now, we either need to create or grow an LDT, so allocate some
    // memory, and copy as necessary
    //

    AllocatedSize = ROUND_TO_PAGES (LdtSize);

    Ldt = ExAllocatePoolWithTag (NonPagedPool, AllocatedSize, 'dLsP');

    if (Ldt == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto SetLdtEntriesCleanup;
    }

    Status = PsChargeProcessNonPagedPoolQuota (Process, AllocatedSize);

    if (!NT_SUCCESS (Status)) {
        ExFreePool (Ldt);
        Ldt = NULL;
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto SetLdtEntriesCleanup;
    }

    RtlZeroMemory (Ldt, AllocatedSize);

    OldLdt = ProcessLdtInformation->Ldt;

    if (OldLdt != NULL) {

        //
        // copy the contents of the old LDT
        //

        RtlCopyMemory (Ldt, OldLdt, ProcessLdtInformation->Size);

        PsReturnProcessNonPagedPoolQuota (Process,
                                          ProcessLdtInformation->AllocatedSize);
    }

    ProcessLdtInformation->Size = LdtSize;
    ProcessLdtInformation->AllocatedSize = AllocatedSize;
    ProcessLdtInformation->Ldt = Ldt;

    //
    // Set the descriptors in the LDT
    //

    if (Selector0) {
        *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)]) = Descriptor[0];
    }

    if (Selector1) {
        *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)]) = Descriptor[Selector1Index];
    }

    //
    // Set the LDT for the process
    //

    Ke386SetLdtProcess (&Process->Pcb,
                        ProcessLdtInformation->Ldt,
                        ProcessLdtInformation->Size);

    //
    // Cleanup and exit
    //

    Status = STATUS_SUCCESS;

SetLdtEntriesCleanup:

    MutexState = KeReleaseMutex (&LdtMutex, FALSE);
    ASSERT (MutexState == 0);

    if (OldLdt != NULL) {
        ExFreePool (OldLdt);
    }

    return Status;
}

NTSTATUS
NtSetLdtEntries(
    __in ULONG Selector0,
    __in ULONG Entry0Low,
    __in ULONG Entry0Hi,
    __in ULONG Selector1,
    __in ULONG Entry1Low,
    __in ULONG Entry1Hi
    )
/*++

Routine Description:

    This routine sets up to two selectors in the current process's LDT.
    The LDT will be grown as necessary.  A selector value of 0 indicates
    that the specified selector was not passed (allowing the setting of
    a single selector).

Arguments:

    Selector0 -- Supplies the number of the first descriptor to set
    Entry0Low -- Supplies the low 32 bits of the descriptor
    Entry0Hi -- Supplies the high 32 bits of the descriptor
    Selector1 -- Supplies the number of the first descriptor to set
    Entry1Low -- Supplies the low 32 bits of the descriptor
    Entry1Hi -- Supplies the high 32 bits of the descriptor

Return Value:

    NTSTATUS.

--*/

{
    return PsSetLdtEntries (Selector0,
                            Entry0Low,
                            Entry0Hi,
                            Selector1,
                            Entry1Low,
                            Entry1Hi
                            );
}

NTSTATUS
PsSetProcessLdtInfo (
    IN PPROCESS_LDT_INFORMATION LdtInformation,
    IN ULONG LdtInformationLength
    )

/*++

Routine Description:

    This function alters the ldt for a specified process.  It can alter
    portions of the LDT, or the whole LDT.  If an Ldt is created or
    grown, the specified process will be charged the quota for the LDT.
    Each descriptor that is set will be verified.

Arguments:

    LdtInformation - Supplies a pointer to a record that contains the
        information to set.  This pointer has already been probed, but since
        it is a usermode pointer, accesses must be guarded by try-except.

    LdtInformationLength - Supplies the length of the record that contains
        the information to set.

Return Value:

    NTSTATUS.

--*/

{
    PEPROCESS Process = PsGetCurrentProcess();
    NTSTATUS Status;
    PLDT_ENTRY OldLdt = NULL;
    ULONG OldSize = 0;
    ULONG AllocatedSize;
    ULONG Size;
    ULONG MutexState;
    ULONG LdtOffset;
    PLDT_ENTRY CurrentDescriptor;
    PPROCESS_LDT_INFORMATION LdtInfo;
    PLDTINFORMATION ProcessLdtInfo;
    PLDT_ENTRY Ldt;

    PAGED_CODE();

    if (LdtInformationLength < (ULONG)sizeof( PROCESS_LDT_INFORMATION)) {
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // Allocate a local buffer to capture the ldt information to
    //

    LdtInfo = ExAllocatePoolWithQuotaTag (NonPagedPool|POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
                                          LdtInformationLength,
                                          'ldmV');
    if (LdtInfo == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = STATUS_SUCCESS;

    try {

        //
        // Copy the information the user is supplying
        //

        RtlCopyMemory (LdtInfo,
                       LdtInformation,
                       LdtInformationLength);

    } except (EXCEPTION_EXECUTE_HANDLER) {
        Status = GetExceptionCode ();
        ExFreePool (LdtInfo);
    }

    //
    // If the capture didn't succeed
    //

    if (!NT_SUCCESS (Status)) {

        if (Status == STATUS_ACCESS_VIOLATION) {
            return STATUS_SUCCESS;
        }

        return Status;
    }

    //
    // Verify that the Start and Length are plausible
    //
    if (LdtInfo->Start & 0xFFFF0000) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_OFFSET;
    }

    if (LdtInfo->Length & 0xFFFF0000) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // Ensure that the buffer is large enough to contain the specified number
    // of selectors.
    //
    if (LdtInformationLength - sizeof (PROCESS_LDT_INFORMATION) + sizeof (LDT_ENTRY) < LdtInfo->Length) {
        ExFreePool (LdtInfo);
        return STATUS_INFO_LENGTH_MISMATCH;
    }

    //
    // The info to set must be an integral number of selectors
    //
    if (LdtInfo->Length % sizeof (LDT_ENTRY)) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_SIZE;
    }

    //
    // The beginning of the info must be on a selector boundary
    //
    if (LdtInfo->Start % sizeof (LDT_ENTRY)) {
        ExFreePool (LdtInfo);
        return STATUS_INVALID_LDT_OFFSET;
    }

    //
    // Verify all of the descriptors.
    //

    for (CurrentDescriptor = LdtInfo->LdtEntries;
         (PCHAR)CurrentDescriptor < (PCHAR)LdtInfo->LdtEntries + LdtInfo->Length;
          CurrentDescriptor += 1) {

        if (!PspIsDescriptorValid (CurrentDescriptor)) {
            ExFreePool (LdtInfo);
            return STATUS_INVALID_LDT_DESCRIPTOR;
        }
    }

    //
    // Acquire the Ldt Mutex
    //

    Status = KeWaitForSingleObject (&LdtMutex,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);
    if (!NT_SUCCESS (Status)) {
        ExFreePool (LdtInfo);
        return Status;
    }

    ProcessLdtInfo = Process->LdtInformation;

    //
    // If the process doesn't have an Ldt information structure, allocate
    // one and attach it to the process
    //

    if (ProcessLdtInfo == NULL) {
        ProcessLdtInfo = ExAllocatePoolWithTag (NonPagedPool,
                                                sizeof(LDTINFORMATION),
                                                'dLsP');
        if (ProcessLdtInfo == NULL) {
            goto SetInfoCleanup;
        }
        RtlZeroMemory (ProcessLdtInfo, sizeof (LDTINFORMATION));
        Process->LdtInformation = ProcessLdtInfo;
    }

    //
    // If we are supposed to remove the LDT
    //
    if (LdtInfo->Length == 0)  {

        //
        // Remove the process' Ldt
        //

        if (ProcessLdtInfo->Ldt) {
            OldSize = ProcessLdtInfo->AllocatedSize;
            OldLdt = ProcessLdtInfo->Ldt;

            ProcessLdtInfo->AllocatedSize = 0;
            ProcessLdtInfo->Size = 0;
            ProcessLdtInfo->Ldt = NULL;

            Ke386SetLdtProcess (&Process->Pcb,
                                NULL,
                                0);

            PsReturnProcessNonPagedPoolQuota (Process, OldSize);
        }

    } else if (ProcessLdtInfo->Ldt == NULL) {

        //
        // Create a new Ldt for the process
        //
        // Allocate an integral number of pages for the LDT.
        //

        ASSERT(((PAGE_SIZE % 2) == 0));

        AllocatedSize = ROUND_TO_PAGES (LdtInfo->Start + LdtInfo->Length);

        Size = LdtInfo->Start + LdtInfo->Length;

        Ldt = PspCreateLdt (LdtInfo->LdtEntries,
                            LdtInfo->Start,
                            Size,
                            AllocatedSize);

        if (Ldt == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto SetInfoCleanup;
        }

        Status = PsChargeProcessNonPagedPoolQuota (Process,
                                                   AllocatedSize);

        if (!NT_SUCCESS (Status)) {
            ExFreePool (Ldt);
            Ldt = NULL;
            goto SetInfoCleanup;
        }

        ProcessLdtInfo->Ldt = Ldt;
        ProcessLdtInfo->Size = Size;
        ProcessLdtInfo->AllocatedSize = AllocatedSize;
        Ke386SetLdtProcess (&Process->Pcb,
                            ProcessLdtInfo->Ldt,
                            ProcessLdtInfo->Size);


    } else if (LdtInfo->Length + LdtInfo->Start > ProcessLdtInfo->Size) {

        //
        // Grow the process' Ldt
        //

        if (LdtInfo->Length + LdtInfo->Start > ProcessLdtInfo->AllocatedSize) {

            //
            // Current Ldt allocation is not large enough, so create a
            // new larger Ldt
            //

            OldSize = ProcessLdtInfo->AllocatedSize;

            Size = LdtInfo->Start + LdtInfo->Length;
            AllocatedSize = ROUND_TO_PAGES (Size);

            Ldt = PspCreateLdt (ProcessLdtInfo->Ldt,
                                0,
                                OldSize,
                                AllocatedSize);

            if (Ldt == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto SetInfoCleanup;
            }

            Status = PsChargeProcessNonPagedPoolQuota (Process,
                                                       AllocatedSize);

            if (!NT_SUCCESS (Status)) {
                ExFreePool (Ldt);
                Ldt = NULL;
                goto SetInfoCleanup;
            }
            PsReturnProcessNonPagedPoolQuota (Process,
                                              OldSize);

            //
            // Swap Ldt information
            //
            OldLdt = ProcessLdtInfo->Ldt;
            ProcessLdtInfo->Ldt = Ldt;
            ProcessLdtInfo->Size = Size;
            ProcessLdtInfo->AllocatedSize = AllocatedSize;

            //
            // Put new selectors into the new ldt
            //
            RtlCopyMemory ((PCHAR)(ProcessLdtInfo->Ldt) + LdtInfo->Start,
                           LdtInfo->LdtEntries,
                           LdtInfo->Length);

            Ke386SetLdtProcess (&Process->Pcb,
                                ProcessLdtInfo->Ldt,
                                ProcessLdtInfo->Size);


        } else {

            //
            // Current Ldt allocation is large enough
            //

            ProcessLdtInfo->Size = LdtInfo->Length + LdtInfo->Start;

            Ke386SetLdtProcess (&Process->Pcb,
                                ProcessLdtInfo->Ldt,
                                ProcessLdtInfo->Size);

            //
            // Change the selectors in the table
            //
            for (LdtOffset = LdtInfo->Start, CurrentDescriptor = LdtInfo->LdtEntries;
                 LdtOffset < LdtInfo->Start + LdtInfo->Length;
                 LdtOffset += sizeof(LDT_ENTRY), CurrentDescriptor++) {

                Ke386SetDescriptorProcess (&Process->Pcb,
                                           LdtOffset,
                                           *CurrentDescriptor);
            }
        }
    } else {

        //
        // Simply changing some selectors
        //

        for (LdtOffset = LdtInfo->Start, CurrentDescriptor = LdtInfo->LdtEntries;
             LdtOffset < LdtInfo->Start +  LdtInfo->Length;
             LdtOffset += sizeof(LDT_ENTRY), CurrentDescriptor++) {

            Ke386SetDescriptorProcess (&Process->Pcb,
                                       LdtOffset,
                                       *CurrentDescriptor);
        }
        Status = STATUS_SUCCESS;
    }


SetInfoCleanup:

    MutexState = KeReleaseMutex (&LdtMutex, FALSE);
    ASSERT ((MutexState == 0));

    if (OldLdt != NULL) {
        ExFreePool (OldLdt);
    }

    if (LdtInfo != NULL) {
        ExFreePool (LdtInfo);
    }

    return Status;
}

