Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

632 lines
17 KiB

/*++
Copyright (c) 1990-2001 Microsoft Corporation
Module Name:
kdinit.c
Abstract:
This module implements the initialization for the portable kernel debgger.
Author:
David N. Cutler 27-July-1990
Revision History:
--*/
#include "kdp.h"
BOOLEAN
KdRegisterDebuggerDataBlock(
IN ULONG Tag,
IN PDBGKD_DEBUG_DATA_HEADER64 DataHeader,
IN ULONG Size
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEKD, KdInitSystem)
#pragma alloc_text(PAGEKD, KdUpdateDataBlock)
#pragma alloc_text(PAGEKD, KdRegisterDebuggerDataBlock)
#endif
BOOLEAN KdBreakAfterSymbolLoad;
VOID
KdUpdateDataBlock(
VOID
)
/*++
Routine Description:
We have to update this variable seperately since it is initialized at a
later time by PS. PS will call us to update the data block.
--*/
{
KdDebuggerDataBlock.KeUserCallbackDispatcher = (ULONG_PTR) KeUserCallbackDispatcher;
}
ULONG_PTR
KdGetDataBlock(
VOID
)
/*++
Routine Description:
Called by crashdump to get the address of this data block
This routine can not be paged.
--*/
{
return (ULONG_PTR)(&KdDebuggerDataBlock);
}
BOOLEAN
KdInitSystem(
IN ULONG Phase,
IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL
)
/*++
Routine Description:
This routine initializes the portable kernel debugger.
Arguments:
Phase - Initialization phase
LoaderBlock - Supplies a pointer to the LOADER_PARAMETER_BLOCK passed
in from the OS Loader.
Return Value:
None.
--*/
{
ULONG Index;
BOOLEAN Initialize;
PLIST_ENTRY NextEntry;
PCHAR Options;
if (Phase == 0) {
//
// If kernel debugger is already initialized, then return.
//
if (KdDebuggerEnabled != FALSE) {
return TRUE;
}
KiDebugRoutine = KdpStub;
KdBreakAfterSymbolLoad = FALSE;
//
// Determine whether or not the debugger should be enabled.
//
// Note that if LoaderBlock == NULL, then KdInitSystem was called
// from BugCheck code. For this case the debugger is always enabled
// to report the bugcheck if possible.
//
if (!KdpDebuggerDataListHead.Flink)
{
#ifdef _IA64_
KdDebuggerDataBlock.MmVirtualTranslationBase =
KeGetPcr()->PteUbase;
#endif
InitializeListHead(&KdpDebuggerDataListHead);
KdRegisterDebuggerDataBlock(KDBG_TAG,
&KdDebuggerDataBlock.Header,
sizeof(KdDebuggerDataBlock));
KdVersionBlock.MinorVersion = (short)NtBuildNumber;
KdVersionBlock.MajorVersion = (short)((NtBuildNumber >> 28) & 0xFFFFFFF);
KdVersionBlock.MaxStateChange =
(UCHAR)(DbgKdMaximumStateChange - DbgKdMinimumStateChange);
KdVersionBlock.MaxManipulate =
(UCHAR)(DbgKdMaximumManipulate - DbgKdMinimumManipulate);
KdVersionBlock.PsLoadedModuleList =
(ULONG64)(LONG64)(LONG_PTR)&PsLoadedModuleList;
KdVersionBlock.DebuggerDataList =
(ULONG64)(LONG64)(LONG_PTR)&KdpDebuggerDataListHead;
#if !defined(NT_UP)
KdVersionBlock.Flags |= DBGKD_VERS_FLAG_MP;
#endif
#if defined(_AMD64_) || defined(_X86_)
//
// Enable this for all platforms when VersionBlock is added
// to all the KPCR definitions.
//
KeGetPcr()->KdVersionBlock = &KdVersionBlock;
#endif
}
if (LoaderBlock != NULL) {
// If the debugger is being initialized during boot, PsNtosImageBase
// and PsLoadedModuleList are not yet valid. KdInitSystem got
// the image base from the loader block.
// On the other hand, if the debugger was initialized by a bugcheck,
// it didn't get a loader block to look at, but the system was
// running so the other variables are valid.
//
KdVersionBlock.KernBase = (ULONG64)(LONG64)(LONG_PTR)
CONTAINING_RECORD(
(LoaderBlock->LoadOrderListHead.Flink),
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks)->DllBase;
//
// Fill in and register the debugger's debugger data blocks.
// Most fields are already initialized, some fields will not be
// filled in until later.
//
if (LoaderBlock->LoadOptions != NULL) {
Options = LoaderBlock->LoadOptions;
_strupr(Options);
//
// If any of the port option, baud option, or debug is
// specified, then enable the debugger unless it is explictly
// disabled.
//
Initialize = TRUE;
if (strstr(Options, "DEBUG") == NULL) {
Initialize = FALSE;
}
//
// If the debugger is explicitly disabled, then set to NODEBUG.
//
if (strstr(Options, "NODEBUG")) {
Initialize = FALSE;
KdPitchDebugger = TRUE;
}
if (strstr(Options, "CRASHDEBUG")) {
Initialize = FALSE;
KdPitchDebugger = FALSE;
}
} else {
//
// If the load options are not specified, then set to NODEBUG.
//
KdPitchDebugger = TRUE;
Initialize = FALSE;
}
} else {
KdVersionBlock.KernBase = (ULONG64)(LONG64)(LONG_PTR)PsNtosImageBase;
Initialize = TRUE;
}
KdDebuggerDataBlock.KernBase = (ULONG_PTR) KdVersionBlock.KernBase;
if (Initialize == FALSE) {
return(TRUE);
}
if (!NT_SUCCESS(KdDebuggerInitialize0(LoaderBlock))) {
return TRUE;
}
//
// Set address of kernel debugger trap routine.
//
KiDebugRoutine = KdpTrap;
if (!KdpDebuggerStructuresInitialized) {
KdpContext.KdpControlCPending = FALSE;
// Retries are set to this after boot
KdpContext.KdpDefaultRetries = MAXIMUM_RETRIES;
KiDebugSwitchRoutine = KdpSwitchProcessor;
#if !defined(_TRUSTED_WINDOWS_)
//
// Initialize TimeSlip
//
KeInitializeDpc(&KdpTimeSlipDpc, KdpTimeSlipDpcRoutine, NULL);
KeInitializeTimer(&KdpTimeSlipTimer);
ExInitializeWorkItem(&KdpTimeSlipWorkItem, KdpTimeSlipWork, NULL);
#endif
KdpDebuggerStructuresInitialized = TRUE ;
}
KdTimerStart.HighPart = 0L;
KdTimerStart.LowPart = 0L;
//
// Mark debugger enabled.
//
KdPitchDebugger = FALSE;
KdDebuggerEnabled = TRUE;
SharedUserData->KdDebuggerEnabled = 0x00000001;
//
// If the loader block address is specified, then scan the loaded
// module list and load the image symbols via the kernel debugger
// for the system and the HAL. If the host debugger has been started
// with the -d option a break into the kernel debugger will occur at
// this point.
//
if (LoaderBlock != NULL) {
Index = 0;
NextEntry = LoaderBlock->LoadOrderListHead.Flink;
while ((NextEntry != &LoaderBlock->LoadOrderListHead) &&
(Index < 2)) {
CHAR Buffer[256];
ULONG Count;
PKLDR_DATA_TABLE_ENTRY DataTableEntry;
WCHAR *Filename;
ULONG Length;
STRING NameString;
//
// Get the address of the data table entry for the next component.
//
DataTableEntry = CONTAINING_RECORD(NextEntry,
KLDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
//
// Load the symbols for the next component.
//
Filename = DataTableEntry->FullDllName.Buffer;
Length = DataTableEntry->FullDllName.Length / sizeof(WCHAR);
Count = 0;
do {
Buffer[Count++] = (CHAR)*Filename++;
} while (Count < Length);
Buffer[Count] = 0;
RtlInitString(&NameString, Buffer);
DbgLoadImageSymbols(&NameString,
DataTableEntry->DllBase,
(ULONG)-1);
NextEntry = NextEntry->Flink;
Index += 1;
}
}
//
// If -b was specified when the host debugger was started, then set up
// to break after symbols are loaded for the kernel, hal, and drivers
// that were loaded by the loader.
//
if (LoaderBlock != NULL) {
KdBreakAfterSymbolLoad = KdPollBreakIn();
}
} else {
//
// Initialize timer facility - HACKHACK
//
KeQueryPerformanceCounter(&KdPerformanceCounterRate);
}
return TRUE;
}
BOOLEAN
KdRegisterDebuggerDataBlock(
IN ULONG Tag,
IN PDBGKD_DEBUG_DATA_HEADER64 DataHeader,
IN ULONG Size
)
/*++
Routine Description:
This routine is called by a component or driver to register a
debugger data block. The data block is made accessible to the
kernel debugger, thus providing a reliable method of exposing
random data to debugger extensions.
Arguments:
Tag - Supplies a unique 4 byte tag which is used to identify the
data block.
DataHeader - Supplies the address of the debugger data block header.
The OwnerTag field must contain a unique value, and the Size
field must contain the size of the data block, including the
header. If this block is already present, or there is
already a block with the same value for OwnerTag, this one
will not be inserted. If Size is incorrect, this code will
not notice, but the usermode side of the debugger might not
function correctly.
Size - Supplies the size of the data block, including the header.
Return Value:
TRUE if the block was added to the list, FALSE if not.
--*/
{
KIRQL OldIrql;
PLIST_ENTRY List;
PDBGKD_DEBUG_DATA_HEADER64 Header;
KeAcquireSpinLock(&KdpDataSpinLock, &OldIrql);
//
// Look for a record with the same tag or address
//
List = KdpDebuggerDataListHead.Flink;
while (List != &KdpDebuggerDataListHead) {
Header = CONTAINING_RECORD(List, DBGKD_DEBUG_DATA_HEADER64, List);
List = List->Flink;
if ((Header == DataHeader) || (Header->OwnerTag == Tag)) {
KeReleaseSpinLock(&KdpDataSpinLock, OldIrql);
return FALSE;
}
}
//
// It wasn't already there, so add it.
//
DataHeader->OwnerTag = Tag;
DataHeader->Size = Size;
InsertTailList(&KdpDebuggerDataListHead, (PLIST_ENTRY)(&DataHeader->List));
KeReleaseSpinLock(&KdpDataSpinLock, OldIrql);
return TRUE;
}
VOID
KdDeregisterDebuggerDataBlock(
IN PDBGKD_DEBUG_DATA_HEADER64 DataHeader
)
/*++
Routine Description:
This routine is called to deregister a data block previously
registered with KdRegisterDebuggerDataBlock. If the block is
found in the list, it is removed.
Arguments:
DataHeader - Supplies the address of the data block which is
to be removed from the list.
Return Value:
None
--*/
{
KIRQL OldIrql;
PLIST_ENTRY List;
PDBGKD_DEBUG_DATA_HEADER64 Header;
KeAcquireSpinLock(&KdpDataSpinLock, &OldIrql);
//
// Make sure the data block is on our list before removing it.
//
List = KdpDebuggerDataListHead.Flink;
while (List != &KdpDebuggerDataListHead) {
Header = CONTAINING_RECORD(List, DBGKD_DEBUG_DATA_HEADER64, List);
List = List->Flink;
if (DataHeader == Header) {
RemoveEntryList((PLIST_ENTRY)(&DataHeader->List));
break;
}
}
KeReleaseSpinLock(&KdpDataSpinLock, OldIrql);
}
VOID
KdLogDbgPrint(
IN PSTRING String
)
{
KIRQL OldIrql;
ULONG Length;
ULONG LengthCopied;
for (; ;) {
if (KeTestSpinLock (&KdpPrintSpinLock)) {
KeRaiseIrql (HIGH_LEVEL, &OldIrql);
if (KeTryToAcquireSpinLockAtDpcLevel(&KdpPrintSpinLock)) {
break; // got the lock
}
KeLowerIrql(OldIrql);
}
}
if (KdPrintCircularBuffer) {
Length = String->Length;
//
// truncate ridiculous strings
//
if (Length > KdPrintBufferSize) {
Length = KdPrintBufferSize;
}
if (KdPrintWritePointer + Length <= KdPrintCircularBuffer + KdPrintBufferSize) {
KdpCopyFromPtr(KdPrintWritePointer, String->Buffer, Length, &LengthCopied);
KdPrintWritePointer += LengthCopied;
if (KdPrintWritePointer >= KdPrintCircularBuffer + KdPrintBufferSize) {
KdPrintWritePointer = KdPrintCircularBuffer;
KdPrintRolloverCount++;
}
} else {
ULONG First = (ULONG)(KdPrintCircularBuffer + KdPrintBufferSize - KdPrintWritePointer);
KdpCopyFromPtr(KdPrintWritePointer,
String->Buffer,
First,
&LengthCopied);
if (LengthCopied == First) {
KdpCopyFromPtr(KdPrintCircularBuffer,
String->Buffer + First,
Length - First,
&LengthCopied);
LengthCopied += First;
}
if (LengthCopied > First) {
KdPrintWritePointer = KdPrintCircularBuffer + LengthCopied - First;
KdPrintRolloverCount++;
} else {
KdPrintWritePointer += LengthCopied;
if (KdPrintWritePointer >= KdPrintCircularBuffer + KdPrintBufferSize) {
KdPrintWritePointer = KdPrintCircularBuffer;
KdPrintRolloverCount++;
}
}
}
}
KiReleaseSpinLock(&KdpPrintSpinLock);
KeLowerIrql(OldIrql);
}
NTSTATUS
KdSetDbgPrintBufferSize(
IN ULONG Size
)
{
KIRQL OldIrql;
PUCHAR OldBuffer;
PUCHAR NewBuffer;
// If kd isn't active just fail.
if (KdPitchDebugger) {
return STATUS_ACCESS_DENIED;
}
//
// First attempt to allocate the new buffer. If the given
// buffer size fits in the default buffer, use it.
//
// We don't attempt to detect if we can reuse an existing
// allocated buffer to avoid having to take the lock to
// keep such a pointer valid while checking. This operation
// should be infrequent so such an optimization is unnecessary.
//
// Disallow excessively large requests. 16MB is the current limit.
if (Size >= 0x1000000) {
return STATUS_INVALID_PARAMETER_1;
}
if (Size > KDPRINTDEFAULTBUFFERSIZE) {
NewBuffer = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, Size, 'bPdK');
if (!NewBuffer) {
return STATUS_NO_MEMORY;
}
} else {
// Size == 0 means just set the default.
if (!Size) {
Size = KDPRINTDEFAULTBUFFERSIZE;
}
NewBuffer = KdPrintDefaultCircularBuffer;
}
//
// Now take the lock and swap in the new buffer.
//
for (; ;) {
if (KeTestSpinLock (&KdpPrintSpinLock)) {
KeRaiseIrql (HIGH_LEVEL, &OldIrql);
if (KeTryToAcquireSpinLockAtDpcLevel(&KdpPrintSpinLock)) {
break; // got the lock
}
KeLowerIrql(OldIrql);
}
}
// Buffer must be zeroed as the write pointer is
// being reset. This has to be done inside the lock
// to avoid races when the new buffer is the same
// as the old.
RtlZeroMemory(NewBuffer, Size);
OldBuffer = KdPrintCircularBuffer;
KdPrintCircularBuffer = NewBuffer;
KdPrintBufferSize = Size;
KdPrintWritePointer = NewBuffer;
KdPrintRolloverCount = 0;
KdPrintBufferChanges++;
KiReleaseSpinLock(&KdpPrintSpinLock);
KeLowerIrql(OldIrql);
//
// Free any old buffer that was replaced.
//
if (OldBuffer && OldBuffer != KdPrintDefaultCircularBuffer) {
ExFreePool(OldBuffer);
}
return STATUS_SUCCESS;
}