windows-nt/Source/XPSP1/NT/base/ntos/ke/i386/ldtsup.c

393 lines
7.8 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
ldtsup.c
Abstract:
This module implements interfaces that support manipulation of i386 Ldts.
These entry points only exist on i386 machines.
Author:
Bryan M. Willman (bryanwi) 14-May-1991
Environment:
Kernel mode only.
Revision History:
--*/
#include "ki.h"
//
// Low level assembler support procedures
//
VOID
KiLoadLdtr(
VOID
);
VOID
KiFlushDescriptors(
VOID
);
//
// Local service procedures
//
VOID
Ki386LoadTargetLdtr (
IN PKIPI_CONTEXT SignalDone,
IN PVOID Parameter1,
IN PVOID Parameter2,
IN PVOID Parameter3
);
VOID
Ki386FlushTargetDescriptors (
IN PKIPI_CONTEXT SignalDone,
IN PVOID Parameter1,
IN PVOID Parameter2,
IN PVOID Parameter3
);
VOID
Ke386SetLdtProcess (
IN PKPROCESS Process,
IN PLDT_ENTRY Ldt,
IN ULONG Limit
)
/*++
Routine Description:
The specified LDT (which may be null) will be made the active Ldt of
the specified process, for all threads thereof, on whichever
processors they are running. The change will take effect before the
call returns.
An Ldt address of NULL or a Limit of 0 will cause the process to
receive the NULL Ldt.
This function only exists on i386 and i386 compatible processors.
No checking is done on the validity of Ldt entries.
N.B.
While a single Ldt structure can be shared amoung processes, any
edits to the Ldt of one of those processes will only be synchronized
for that process. Thus, processes other than the one the change is
applied to may not see the change correctly.
Arguments:
Process - Pointer to KPROCESS object describing the process for
which the Ldt is to be set.
Ldt - Pointer to an array of LDT_ENTRYs (that is, a pointer to an
Ldt.)
Limit - Ldt limit (must be 0 mod 8)
Return Value:
None.
--*/
{
KGDTENTRY LdtDescriptor;
BOOLEAN LocalProcessor;
KIRQL OldIrql;
PKPRCB Prcb;
KAFFINITY TargetProcessors;
//
// Compute the contents of the Ldt descriptor
//
if ((Ldt == NULL) || (Limit == 0)) {
//
// Set up an empty descriptor
//
LdtDescriptor.LimitLow = 0;
LdtDescriptor.BaseLow = 0;
LdtDescriptor.HighWord.Bytes.BaseMid = 0;
LdtDescriptor.HighWord.Bytes.Flags1 = 0;
LdtDescriptor.HighWord.Bytes.Flags2 = 0;
LdtDescriptor.HighWord.Bytes.BaseHi = 0;
} else {
//
// Insure that the unfilled fields of the selector are zero
// N.B. If this is not done, random values appear in the high
// portion of the Ldt limit.
//
LdtDescriptor.HighWord.Bytes.Flags1 = 0;
LdtDescriptor.HighWord.Bytes.Flags2 = 0;
//
// Set the limit and base
//
LdtDescriptor.LimitLow = (USHORT) ((ULONG) Limit - 1);
LdtDescriptor.BaseLow = (USHORT) ((ULONG) Ldt & 0xffff);
LdtDescriptor.HighWord.Bytes.BaseMid = (UCHAR) (((ULONG)Ldt & 0xff0000) >> 16);
LdtDescriptor.HighWord.Bytes.BaseHi = (UCHAR) (((ULONG)Ldt & 0xff000000) >> 24);
//
// Type is LDT, DPL = 0
//
LdtDescriptor.HighWord.Bits.Type = TYPE_LDT;
LdtDescriptor.HighWord.Bits.Dpl = DPL_SYSTEM;
//
// Make it present
//
LdtDescriptor.HighWord.Bits.Pres = 1;
}
//
// Acquire the context swap lock so a context switch cannot occur.
//
KiLockContextSwap(&OldIrql);
//
// Set the Ldt fields in the process object.
//
Process->LdtDescriptor = LdtDescriptor;
//
// Tell all processors active for this process to reload their LDTs
//
#ifdef NT_UP
KiLoadLdtr();
#else
Prcb = KeGetCurrentPrcb();
TargetProcessors = Process->ActiveProcessors & ~Prcb->SetMember;
if (TargetProcessors != 0) {
KiIpiSendPacket(TargetProcessors,
Ki386LoadTargetLdtr,
NULL,
NULL,
NULL);
}
KiLoadLdtr();
if (TargetProcessors != 0) {
//
// Stall until target processor(s) release us
//
KiIpiStallOnPacketTargets(TargetProcessors);
}
#endif
//
// Restore IRQL and release the context swap lock.
//
KiUnlockContextSwap(OldIrql);
return;
}
VOID
Ki386LoadTargetLdtr (
IN PKIPI_CONTEXT SignalDone,
IN PVOID Parameter1,
IN PVOID Parameter2,
IN PVOID Parameter3
)
/*++
Routine Description:
Reload local Ldt register and clear signal bit in TargetProcessor mask
Arguments:
Argument - pointer to a ipi packet structure.
ReadyFlag - Pointer to flag to be set once LDTR has been reloaded
Return Value:
none.
--*/
{
//
// Reload the LDTR register from currently active process object
//
KiLoadLdtr();
KiIpiSignalPacketDone(SignalDone);
return;
}
VOID
Ke386SetDescriptorProcess (
IN PKPROCESS Process,
IN ULONG Offset,
IN LDT_ENTRY LdtEntry
)
/*++
Routine Description:
The specified LdtEntry (which could be 0, not present, etc) will be
edited into the specified Offset in the Ldt of the specified Process.
This will be synchronzied accross all the processors executing the
process. The edit will take affect on all processors before the call
returns.
N.B.
Editing an Ldt descriptor requires stalling all processors active
for the process, to prevent accidental loading of descriptors in
an inconsistent state.
Arguments:
Process - Pointer to KPROCESS object describing the process for
which the descriptor edit is to be performed.
Offset - Byte offset into the Ldt of the descriptor to edit.
Must be 0 mod 8.
LdtEntry - Value to edit into the descriptor in hardware format.
No checking is done on the validity of this item.
Return Value:
none.
--*/
{
PLDT_ENTRY Ldt;
KIRQL OldIrql;
PKPRCB Prcb;
KAFFINITY TargetProcessors;
//
// Compute address of descriptor to edit.
//
Ldt =
(PLDT_ENTRY)
((Process->LdtDescriptor.HighWord.Bytes.BaseHi << 24) |
((Process->LdtDescriptor.HighWord.Bytes.BaseMid << 16) & 0xff0000) |
(Process->LdtDescriptor.BaseLow & 0xffff));
Offset = Offset / 8;
KiLockContextSwap(&OldIrql);
#ifdef NT_UP
//
// Edit the Ldt.
//
Ldt[Offset] = LdtEntry;
#else
Prcb = KeGetCurrentPrcb();
TargetProcessors = Process->ActiveProcessors & ~Prcb->SetMember;
if (TargetProcessors != 0) {
KiIpiSendSynchronousPacket(
Prcb,
TargetProcessors,
Ki386FlushTargetDescriptors,
(PVOID)&Prcb->ReverseStall,
NULL,
NULL);
KiIpiStallOnPacketTargets(TargetProcessors);
}
//
// All target processors have flushed the segment descriptors and
// are waiting to proceed. Edit the ldt on the current processor,
// then continue the execution of target processors.
//
Ldt[Offset] = LdtEntry;
if (TargetProcessors != 0) {
Prcb->ReverseStall += 1;
}
#endif
//
// Restore IRQL and release the context swap lock.
//
KiUnlockContextSwap(OldIrql);
return;
}
VOID
Ki386FlushTargetDescriptors (
IN PKIPI_CONTEXT SignalDone,
IN PVOID Proceed,
IN PVOID Parameter2,
IN PVOID Parameter3
)
/*++
Routine Description:
This function flushes the segment descriptors on the current processor.
Arguments:
Argument - pointer to a _KIPI_FLUSH_DESCRIPTOR structure.
ReadyFlag - pointer to flag to syncroize with
Return Value:
none.
--*/
{
//
// Flush the segment descriptors on the current processor and signal that
// the descriptors have been flushed.
//
KiFlushDescriptors();
KiIpiSignalPacketDoneAndStall (SignalDone, Proceed);
return;
}