mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892)
CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
This commit is contained in:
parent
bf734e5373
commit
4ce819ca5a
6 changed files with 384 additions and 100 deletions
|
@ -39,7 +39,7 @@ typedef enum _KD_CONTINUE_TYPE
|
|||
} KD_CONTINUE_TYPE;
|
||||
|
||||
typedef
|
||||
VOID
|
||||
NTSTATUS
|
||||
(NTAPI *PKDP_INIT_ROUTINE)(
|
||||
_In_ struct _KD_DISPATCH_TABLE *DispatchTable,
|
||||
_In_ ULONG BootPhase);
|
||||
|
@ -70,19 +70,19 @@ KdpScreenAcquire(VOID);
|
|||
VOID
|
||||
KdpScreenRelease(VOID);
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdpScreenInit(
|
||||
_In_ struct _KD_DISPATCH_TABLE *DispatchTable,
|
||||
_In_ ULONG BootPhase);
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdpSerialInit(
|
||||
_In_ struct _KD_DISPATCH_TABLE *DispatchTable,
|
||||
_In_ ULONG BootPhase);
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdpDebugLogInit(
|
||||
_In_ struct _KD_DISPATCH_TABLE *DispatchTable,
|
||||
|
@ -168,6 +168,7 @@ typedef struct _KD_DISPATCH_TABLE
|
|||
LIST_ENTRY KdProvidersList;
|
||||
PKDP_INIT_ROUTINE KdpInitRoutine;
|
||||
PKDP_PRINT_ROUTINE KdpPrintRoutine;
|
||||
NTSTATUS InitStatus;
|
||||
} KD_DISPATCH_TABLE, *PKD_DISPATCH_TABLE;
|
||||
|
||||
/* The current Debugging Mode */
|
||||
|
|
|
@ -588,14 +588,6 @@ IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
|
|||
* We can finally load other drivers from the boot volume. */
|
||||
PnPBootDriversInitialized = TRUE;
|
||||
|
||||
#if !defined(_WINKD_) && defined(KDBG)
|
||||
/* Read KDB Data */
|
||||
KdbpCliInit();
|
||||
|
||||
/* I/O is now setup for disk access, so phase 3 */
|
||||
KdInitSystem(3, LoaderBlock);
|
||||
#endif
|
||||
|
||||
/* Load system start drivers */
|
||||
IopInitializeSystemDrivers();
|
||||
PnpSystemInit = TRUE;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
#define KdpBufferSize (1024 * 512)
|
||||
static BOOLEAN KdpLoggingEnabled = FALSE;
|
||||
static BOOLEAN KdpLoggingStarting = FALSE;
|
||||
static PCHAR KdpDebugBuffer = NULL;
|
||||
static volatile ULONG KdpCurrentPosition = 0;
|
||||
static volatile ULONG KdpFreeBytes = 0;
|
||||
|
@ -39,14 +38,14 @@ static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
|
|||
|
||||
KDP_DEBUG_MODE KdpDebugMode;
|
||||
LIST_ENTRY KdProviders = {&KdProviders, &KdProviders};
|
||||
KD_DISPATCH_TABLE DispatchTable[KdMax];
|
||||
KD_DISPATCH_TABLE DispatchTable[KdMax] = {0};
|
||||
|
||||
PKDP_INIT_ROUTINE InitRoutines[KdMax] =
|
||||
{
|
||||
KdpScreenInit,
|
||||
KdpSerialInit,
|
||||
KdpDebugLogInit,
|
||||
#ifdef KDBG
|
||||
#ifdef KDBG // See kdb_cli.c
|
||||
KdpKdbgInit
|
||||
#endif
|
||||
};
|
||||
|
@ -158,7 +157,6 @@ KdpPrintToLogFile(PCHAR String,
|
|||
{
|
||||
KIRQL OldIrql;
|
||||
ULONG beg, end, num;
|
||||
BOOLEAN DoReinit = FALSE;
|
||||
|
||||
if (KdpDebugBuffer == NULL) return;
|
||||
|
||||
|
@ -185,45 +183,31 @@ KdpPrintToLogFile(PCHAR String,
|
|||
}
|
||||
|
||||
/* Release the spinlock */
|
||||
if (OldIrql == PASSIVE_LEVEL && !KdpLoggingStarting && !KdpLoggingEnabled && ExpInitializationPhase >= 2)
|
||||
{
|
||||
DoReinit = TRUE;
|
||||
}
|
||||
KdbpReleaseLock(&KdpDebugLogSpinLock, OldIrql);
|
||||
|
||||
if (DoReinit)
|
||||
{
|
||||
KdpLoggingStarting = TRUE;
|
||||
KdpDebugLogInit(NULL, 3);
|
||||
}
|
||||
|
||||
/* Signal the logger thread */
|
||||
if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
|
||||
KeSetEvent(&KdpLoggerThreadEvent, IO_NO_INCREMENT, FALSE);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdpDebugLogInit(
|
||||
_In_ PKD_DISPATCH_TABLE DispatchTable,
|
||||
_In_ ULONG BootPhase)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
UNICODE_STRING FileName;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
IO_STATUS_BLOCK Iosb;
|
||||
HANDLE ThreadHandle;
|
||||
KPRIORITY Priority;
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
if (!KdpDebugMode.File) return;
|
||||
if (!KdpDebugMode.File)
|
||||
return STATUS_PORT_DISCONNECTED;
|
||||
|
||||
if (BootPhase == 0)
|
||||
{
|
||||
/* Write out the functions that we support for now */
|
||||
DispatchTable->KdpInitRoutine = KdpDebugLogInit;
|
||||
DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
|
||||
|
||||
/* Register as a Provider */
|
||||
/* Register for BootPhase 1 initialization and as a Provider */
|
||||
DispatchTable->KdpInitRoutine = KdpDebugLogInit;
|
||||
InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
|
||||
}
|
||||
else if (BootPhase == 1)
|
||||
|
@ -235,20 +219,35 @@ KdpDebugLogInit(
|
|||
if (!KdpDebugBuffer)
|
||||
{
|
||||
KdpDebugMode.File = FALSE;
|
||||
return;
|
||||
RemoveEntryList(&DispatchTable->KdProvidersList);
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
KdpFreeBytes = KdpBufferSize;
|
||||
|
||||
/* Initialize spinlock */
|
||||
KeInitializeSpinLock(&KdpDebugLogSpinLock);
|
||||
|
||||
/* Register for later BootPhase 2 reinitialization */
|
||||
DispatchTable->KdpInitRoutine = KdpDebugLogInit;
|
||||
|
||||
HalDisplayString("\r\n File log debugging enabled\r\n\r\n");
|
||||
}
|
||||
else if (BootPhase == 3)
|
||||
else if (BootPhase >= 2)
|
||||
{
|
||||
UNICODE_STRING FileName;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
IO_STATUS_BLOCK Iosb;
|
||||
HANDLE ThreadHandle;
|
||||
KPRIORITY Priority;
|
||||
|
||||
/* If we have already successfully opened the log file, bail out */
|
||||
if (KdpLogFileHandle != NULL)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
/* Setup the log name */
|
||||
Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
|
||||
if (!NT_SUCCESS(Status)) return;
|
||||
if (!NT_SUCCESS(Status))
|
||||
goto Failure;
|
||||
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&FileName,
|
||||
|
@ -274,8 +273,16 @@ KdpDebugLogInit(
|
|||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to open log file: 0x%08x\n", Status);
|
||||
return;
|
||||
DPRINT1("Failed to open log file: 0x%08lx\n", Status);
|
||||
|
||||
/* Schedule an I/O reinitialization if needed */
|
||||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
|
||||
Status == STATUS_OBJECT_PATH_NOT_FOUND)
|
||||
{
|
||||
DispatchTable->KdpInitRoutine = KdpDebugLogInit;
|
||||
return Status;
|
||||
}
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
/** HACK for FILE_APPEND_DATA **
|
||||
|
@ -323,18 +330,29 @@ KdpDebugLogInit(
|
|||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to create log file thread: 0x%08lx\n", Status);
|
||||
ZwClose(KdpLogFileHandle);
|
||||
return;
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
Priority = 7;
|
||||
Priority = HIGH_PRIORITY;
|
||||
ZwSetInformationThread(ThreadHandle,
|
||||
ThreadPriority,
|
||||
&Priority,
|
||||
sizeof(Priority));
|
||||
|
||||
ZwClose(ThreadHandle);
|
||||
return Status;
|
||||
|
||||
Failure:
|
||||
KdpFreeBytes = 0;
|
||||
ExFreePoolWithTag(KdpDebugBuffer, TAG_KDBG);
|
||||
KdpDebugBuffer = NULL;
|
||||
KdpDebugMode.File = FALSE;
|
||||
RemoveEntryList(&DispatchTable->KdProvidersList);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* SERIAL FUNCTIONS **********************************************************/
|
||||
|
@ -365,38 +383,41 @@ KdpSerialPrint(PCHAR String,
|
|||
KdbpReleaseLock(&KdpSerialSpinLock, OldIrql);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdpSerialInit(
|
||||
_In_ PKD_DISPATCH_TABLE DispatchTable,
|
||||
_In_ ULONG BootPhase)
|
||||
{
|
||||
if (!KdpDebugMode.Serial) return;
|
||||
if (!KdpDebugMode.Serial)
|
||||
return STATUS_PORT_DISCONNECTED;
|
||||
|
||||
if (BootPhase == 0)
|
||||
{
|
||||
/* Write out the functions that we support for now */
|
||||
DispatchTable->KdpInitRoutine = KdpSerialInit;
|
||||
DispatchTable->KdpPrintRoutine = KdpSerialPrint;
|
||||
|
||||
/* Initialize the Port */
|
||||
if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber))
|
||||
{
|
||||
KdpDebugMode.Serial = FALSE;
|
||||
return;
|
||||
return STATUS_DEVICE_DOES_NOT_EXIST;
|
||||
}
|
||||
KdComPortInUse = SerialPortInfo.Address;
|
||||
|
||||
/* Initialize spinlock */
|
||||
KeInitializeSpinLock(&KdpSerialSpinLock);
|
||||
|
||||
/* Register as a Provider */
|
||||
/* Register for BootPhase 1 initialization and as a Provider */
|
||||
DispatchTable->KdpInitRoutine = KdpSerialInit;
|
||||
InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
|
||||
}
|
||||
else if (BootPhase == 1)
|
||||
{
|
||||
HalDisplayString("\r\n Serial debugging enabled\r\n\r\n");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* SCREEN FUNCTIONS **********************************************************/
|
||||
|
@ -483,21 +504,22 @@ KdpScreenPrint(PCHAR String,
|
|||
}
|
||||
}
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdpScreenInit(
|
||||
_In_ PKD_DISPATCH_TABLE DispatchTable,
|
||||
_In_ ULONG BootPhase)
|
||||
{
|
||||
if (!KdpDebugMode.Screen) return;
|
||||
if (!KdpDebugMode.Screen)
|
||||
return STATUS_PORT_DISCONNECTED;
|
||||
|
||||
if (BootPhase == 0)
|
||||
{
|
||||
/* Write out the functions that we support for now */
|
||||
DispatchTable->KdpInitRoutine = KdpScreenInit;
|
||||
DispatchTable->KdpPrintRoutine = KdpScreenPrint;
|
||||
|
||||
/* Register as a Provider */
|
||||
/* Register for BootPhase 1 initialization and as a Provider */
|
||||
DispatchTable->KdpInitRoutine = KdpScreenInit;
|
||||
InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
|
||||
}
|
||||
else if (BootPhase == 1)
|
||||
|
@ -507,16 +529,13 @@ KdpScreenInit(
|
|||
|
||||
HalDisplayString("\r\n Screen debugging enabled\r\n\r\n");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* GENERAL FUNCTIONS *********************************************************/
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
KdpPrintString(
|
||||
_In_ PSTRING Output);
|
||||
|
||||
extern STRING KdbPromptString;
|
||||
|
||||
VOID
|
||||
|
@ -551,20 +570,16 @@ KdSendPacket(
|
|||
if (!KdpDebugMode.Value)
|
||||
return;
|
||||
|
||||
/* Call the registered handlers */
|
||||
CurrentEntry = KdProviders.Flink;
|
||||
while (CurrentEntry != &KdProviders)
|
||||
/* Call the registered providers */
|
||||
for (CurrentEntry = KdProviders.Flink;
|
||||
CurrentEntry != &KdProviders;
|
||||
CurrentEntry = CurrentEntry->Flink)
|
||||
{
|
||||
/* Get the current table */
|
||||
CurrentTable = CONTAINING_RECORD(CurrentEntry,
|
||||
KD_DISPATCH_TABLE,
|
||||
KdProvidersList);
|
||||
|
||||
/* Call it */
|
||||
CurrentTable->KdpPrintRoutine(MessageData->Buffer, MessageData->Length);
|
||||
|
||||
/* Next Table */
|
||||
CurrentEntry = CurrentEntry->Flink;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/*
|
||||
* COPYRIGHT: See COPYING in the top level directory
|
||||
* PROJECT: ReactOS Kernel
|
||||
* FILE: ntoskrnl/kd/kdmain.c
|
||||
* PURPOSE: Kernel Debugger Initialization
|
||||
*
|
||||
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||
* PURPOSE: Kernel Debugger Initialization
|
||||
* COPYRIGHT: Copyright 2005 Alex Ionescu <alex.ionescu@reactos.org>
|
||||
* Copyright 2020 Hervé Poussineau <hpoussin@reactos.org>
|
||||
* Copyright 2023 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
|
||||
*/
|
||||
|
||||
#include <ntoskrnl.h>
|
||||
|
@ -13,8 +13,6 @@
|
|||
|
||||
/* VARIABLES ***************************************************************/
|
||||
|
||||
VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads);
|
||||
|
||||
extern ANSI_STRING KdpLogFileName;
|
||||
|
||||
/* PUBLIC FUNCTIONS *********************************************************/
|
||||
|
@ -86,8 +84,9 @@ NTAPI
|
|||
KdDebuggerInitialize0(
|
||||
_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)
|
||||
{
|
||||
ULONG i;
|
||||
PCHAR CommandLine, Port = NULL;
|
||||
ULONG i;
|
||||
BOOLEAN Success = FALSE;
|
||||
|
||||
if (LoaderBlock)
|
||||
{
|
||||
|
@ -127,15 +126,174 @@ KdDebuggerInitialize0(
|
|||
if (KdpDebugMode.Value == 0)
|
||||
KdpDebugMode.Serial = TRUE;
|
||||
|
||||
/* Call Providers at Phase 0 */
|
||||
for (i = 0; i < KdMax; i++)
|
||||
/* Call the providers at Phase 0 */
|
||||
for (i = 0; i < RTL_NUMBER_OF(DispatchTable); i++)
|
||||
{
|
||||
InitRoutines[i](&DispatchTable[i], 0);
|
||||
DispatchTable[i].InitStatus = InitRoutines[i](&DispatchTable[i], 0);
|
||||
Success = (Success || NT_SUCCESS(DispatchTable[i].InitStatus));
|
||||
}
|
||||
|
||||
/* Return success if at least one of the providers succeeded */
|
||||
return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reinitialization routine.
|
||||
* DRIVER_REINITIALIZE
|
||||
*
|
||||
* Calls each registered provider for reinitialization at Phase >= 2.
|
||||
* I/O is now set up for disk access, at different phases.
|
||||
**/
|
||||
static VOID
|
||||
NTAPI
|
||||
KdpDriverReinit(
|
||||
_In_ PDRIVER_OBJECT DriverObject,
|
||||
_In_opt_ PVOID Context,
|
||||
_In_ ULONG Count)
|
||||
{
|
||||
PLIST_ENTRY CurrentEntry;
|
||||
PKD_DISPATCH_TABLE CurrentTable;
|
||||
PKDP_INIT_ROUTINE KdpInitRoutine;
|
||||
ULONG BootPhase = (Count + 1); // Do BootPhase >= 2
|
||||
BOOLEAN ScheduleReinit = FALSE;
|
||||
|
||||
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
DPRINT("*** KD %sREINITIALIZATION - Phase %d ***\n",
|
||||
Context ? "" : "BOOT ", BootPhase);
|
||||
|
||||
/* Call the registered providers */
|
||||
for (CurrentEntry = KdProviders.Flink;
|
||||
CurrentEntry != &KdProviders; NOTHING)
|
||||
{
|
||||
/* Get the provider */
|
||||
CurrentTable = CONTAINING_RECORD(CurrentEntry,
|
||||
KD_DISPATCH_TABLE,
|
||||
KdProvidersList);
|
||||
/* Go to the next entry (the Init routine may unlink us) */
|
||||
CurrentEntry = CurrentEntry->Flink;
|
||||
|
||||
/* Call it if it requires a reinitialization */
|
||||
if (CurrentTable->KdpInitRoutine)
|
||||
{
|
||||
/* Get the initialization routine and reset it */
|
||||
KdpInitRoutine = CurrentTable->KdpInitRoutine;
|
||||
CurrentTable->KdpInitRoutine = NULL;
|
||||
CurrentTable->InitStatus = KdpInitRoutine(CurrentTable, BootPhase);
|
||||
DPRINT("KdpInitRoutine(%p) returned 0x%08lx\n",
|
||||
CurrentTable, CurrentTable->InitStatus);
|
||||
|
||||
/* Check whether it needs to be reinitialized again */
|
||||
ScheduleReinit = (ScheduleReinit || CurrentTable->KdpInitRoutine);
|
||||
}
|
||||
}
|
||||
|
||||
DPRINT("ScheduleReinit: %s\n", ScheduleReinit ? "TRUE" : "FALSE");
|
||||
if (ScheduleReinit)
|
||||
{
|
||||
/*
|
||||
* Determine when to reinitialize.
|
||||
* If Context == NULL, we are doing a boot-driver reinitialization.
|
||||
* It is initially done once (Count == 1), and is rescheduled once
|
||||
* after all other boot drivers get loaded (Count == 2).
|
||||
* If further reinitialization is needed, switch to system-driver
|
||||
* reinitialization and do it again, not more than twice.
|
||||
*/
|
||||
if (Count <= 1)
|
||||
{
|
||||
IoRegisterBootDriverReinitialization(DriverObject,
|
||||
KdpDriverReinit,
|
||||
(PVOID)FALSE);
|
||||
}
|
||||
else if (Count <= 3)
|
||||
{
|
||||
IoRegisterDriverReinitialization(DriverObject,
|
||||
KdpDriverReinit,
|
||||
(PVOID)TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Too late, no more reinitializations! */
|
||||
DPRINT("Cannot reinitialize anymore!\n");
|
||||
ScheduleReinit = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ScheduleReinit)
|
||||
{
|
||||
/* All the necessary reinitializations are done,
|
||||
* the driver object is not needed anymore. */
|
||||
ObMakeTemporaryObject(DriverObject);
|
||||
IoDeleteDriver(DriverObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Entry point for the auxiliary driver.
|
||||
* DRIVER_INITIALIZE
|
||||
**/
|
||||
static NTSTATUS
|
||||
NTAPI
|
||||
KdpDriverEntry(
|
||||
_In_ PDRIVER_OBJECT DriverObject,
|
||||
_In_ PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
/* Register for reinitialization after the other drivers are loaded */
|
||||
IoRegisterBootDriverReinitialization(DriverObject,
|
||||
KdpDriverReinit,
|
||||
(PVOID)FALSE);
|
||||
|
||||
/* Set the driver as initialized */
|
||||
DriverObject->Flags |= DRVO_INITIALIZED;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Hooked HalInitPnpDriver() callback.
|
||||
* It is initially set by the HAL when HalInitSystem(0, ...)
|
||||
* is called earlier on.
|
||||
**/
|
||||
static pHalInitPnpDriver orgHalInitPnpDriver = NULL;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* HalInitPnpDriver() callback hook installed by KdDebuggerInitialize1().
|
||||
*
|
||||
* It is called during initialization of the I/O manager and is where
|
||||
* the auxiliary driver is created. This driver is needed for receiving
|
||||
* reinitialization callbacks in KdpDriverReinit() later.
|
||||
* This hook must *always* call the original HalInitPnpDriver() function
|
||||
* and return its returned value, or return STATUS_SUCCESS.
|
||||
**/
|
||||
static NTSTATUS
|
||||
NTAPI
|
||||
KdpInitDriver(VOID)
|
||||
{
|
||||
static BOOLEAN InitCalled = FALSE;
|
||||
NTSTATUS Status;
|
||||
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Driver\\KdDriver");
|
||||
|
||||
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
/* Ensure we are not called more than once */
|
||||
if (_InterlockedCompareExchange8((char*)&InitCalled, TRUE, FALSE) != FALSE)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
/* Create the driver */
|
||||
Status = IoCreateDriver(&DriverName, KdpDriverEntry);
|
||||
if (!NT_SUCCESS(Status))
|
||||
DPRINT1("IoCreateDriver failed: 0x%08lx\n", Status);
|
||||
/* Ignore any failure from IoCreateDriver(). If it fails, no I/O-related
|
||||
* initialization will happen (no file log debugging, etc.). */
|
||||
|
||||
/* Finally, restore and call the original HalInitPnpDriver() */
|
||||
InterlockedExchangePointer((PVOID*)&HalInitPnpDriver, orgHalInitPnpDriver);
|
||||
return (HalInitPnpDriver ? HalInitPnpDriver() : STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdDebuggerInitialize1(
|
||||
|
@ -143,26 +301,119 @@ KdDebuggerInitialize1(
|
|||
{
|
||||
PLIST_ENTRY CurrentEntry;
|
||||
PKD_DISPATCH_TABLE CurrentTable;
|
||||
PKDP_INIT_ROUTINE KdpInitRoutine;
|
||||
BOOLEAN Success = FALSE;
|
||||
BOOLEAN ReinitForPhase2 = FALSE;
|
||||
|
||||
/* Call the registered handlers */
|
||||
CurrentEntry = KdProviders.Flink;
|
||||
while (CurrentEntry != &KdProviders)
|
||||
/* Call the registered providers */
|
||||
for (CurrentEntry = KdProviders.Flink;
|
||||
CurrentEntry != &KdProviders; NOTHING)
|
||||
{
|
||||
/* Get the current table */
|
||||
/* Get the provider */
|
||||
CurrentTable = CONTAINING_RECORD(CurrentEntry,
|
||||
KD_DISPATCH_TABLE,
|
||||
KdProvidersList);
|
||||
/* Go to the next entry (the Init routine may unlink us) */
|
||||
CurrentEntry = CurrentEntry->Flink;
|
||||
|
||||
/* Get the initialization routine and reset it */
|
||||
ASSERT(CurrentTable->KdpInitRoutine);
|
||||
KdpInitRoutine = CurrentTable->KdpInitRoutine;
|
||||
CurrentTable->KdpInitRoutine = NULL;
|
||||
|
||||
/* Call it */
|
||||
CurrentTable->KdpInitRoutine(CurrentTable, 1);
|
||||
CurrentTable->InitStatus = KdpInitRoutine(CurrentTable, 1);
|
||||
|
||||
/* Next Table */
|
||||
CurrentEntry = CurrentEntry->Flink;
|
||||
/* Check whether it needs to be reinitialized for Phase 2 */
|
||||
Success = (Success || NT_SUCCESS(CurrentTable->InitStatus));
|
||||
ReinitForPhase2 = (ReinitForPhase2 || CurrentTable->KdpInitRoutine);
|
||||
}
|
||||
|
||||
NtGlobalFlag |= FLG_STOP_ON_EXCEPTION;
|
||||
|
||||
/* If we don't need to reinitialize providers for Phase 2, we are done */
|
||||
if (!ReinitForPhase2)
|
||||
{
|
||||
/* Return success if at least one of them succeeded */
|
||||
return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to be able to perform I/O-related initialization (starting a
|
||||
* logger thread for file log debugging, loading KDBinit file for KDBG,
|
||||
* etc.). A good place for this would be as early as possible, once the
|
||||
* I/O Manager has started the storage and the boot filesystem drivers.
|
||||
*
|
||||
* Here is an overview of the initialization steps of the NT Kernel and
|
||||
* Executive:
|
||||
* ----
|
||||
* KiSystemStartup(KeLoaderBlock)
|
||||
* if (Cpu == 0) KdInitSystem(0, KeLoaderBlock);
|
||||
* KiSwitchToBootStack() -> KiSystemStartupBootStack()
|
||||
* -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock)
|
||||
*
|
||||
* (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); )
|
||||
* KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock);
|
||||
*
|
||||
* ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0;
|
||||
* HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback.
|
||||
* ...
|
||||
* PsInitSystem(LoaderBlock)
|
||||
* PsCreateSystemThread(Phase1Initialization)
|
||||
*
|
||||
* Phase1Initialization(Discard): ExpInitializationPhase = 1;
|
||||
* HalInitSystem(1, KeLoaderBlock);
|
||||
* ...
|
||||
* Early initialization of Ob, Ex, Ke.
|
||||
* KdInitSystem(1, KeLoaderBlock);
|
||||
* ...
|
||||
* KdDebuggerInitialize1(LoaderBlock);
|
||||
* ...
|
||||
* IoInitSystem(LoaderBlock);
|
||||
* ...
|
||||
* ----
|
||||
* As we can see, KdDebuggerInitialize1() is the last KD initialization
|
||||
* routine the kernel calls, and is called *before* the I/O Manager starts.
|
||||
* Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also,
|
||||
* we want to do the I/O initialization as soon as possible. There does
|
||||
* not seem to be any exported way to be notified about the I/O manager
|
||||
* initialization steps... that is, unless we somehow become a driver and
|
||||
* insert ourselves in the flow!
|
||||
*
|
||||
* Since we are not a regular driver, we need to invoke IoCreateDriver()
|
||||
* to create one. However, remember that we are currently running *before*
|
||||
* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this,
|
||||
* calling IoCreateDriver(), much like any other IO functions, would lead
|
||||
* to a crash, because it calls
|
||||
* ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType
|
||||
* is non-initialized yet (it's NULL).
|
||||
*
|
||||
* The chosen solution is to hook a "known" exported callback: namely, the
|
||||
* HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver").
|
||||
* It is set very early on by the HAL via the HalInitSystem(0, ...) call,
|
||||
* and is called early on by IoInitSystem() before any driver is loaded,
|
||||
* but after the I/O Manager has been minimally set up so that new drivers
|
||||
* can be created.
|
||||
* When the hook: KdpInitDriver() is called, we create our driver with
|
||||
* IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then
|
||||
* restore and call the original HalInitPnpDriver() callback.
|
||||
*
|
||||
* Another possible unexplored alternative, could be to insert ourselves
|
||||
* in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the
|
||||
* KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while
|
||||
* we may be able to do this, because boot-drivers are resident in memory,
|
||||
* much like we are, we cannot insert ourselves in the system-driver list
|
||||
* however, since those drivers are expected to come from PE image files.)
|
||||
*
|
||||
* Once the KdpDriverEntry() driver entrypoint is called, we register
|
||||
* KdpDriverReinit() for re-initialization with the I/O Manager, in order
|
||||
* to provide more initialization points. KdpDriverReinit() calls the KD
|
||||
* providers at BootPhase >= 2, and schedules further reinitializations
|
||||
* (at most 3 more) if any of the providers request so.
|
||||
**/
|
||||
orgHalInitPnpDriver =
|
||||
InterlockedExchangePointer((PVOID*)&HalInitPnpDriver, KdpInitDriver);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
/* EOF */
|
||||
|
|
|
@ -82,7 +82,7 @@ KdbpStackSwitchAndCall(
|
|||
|
||||
extern PCHAR KdbInitFileBuffer;
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdbInitialize(
|
||||
_In_ PKD_DISPATCH_TABLE DispatchTable,
|
||||
|
@ -94,7 +94,7 @@ KdbRegisterCliCallback(
|
|||
PVOID Callback,
|
||||
BOOLEAN Deregister);
|
||||
|
||||
VOID
|
||||
NTSTATUS
|
||||
KdbpCliInit(VOID);
|
||||
|
||||
VOID
|
||||
|
|
|
@ -3795,11 +3795,13 @@ KdbpCliInterpretInitFile(VOID)
|
|||
DPRINT("KDB: KDBinit executed\n");
|
||||
}
|
||||
|
||||
/*!\brief Called when KDB is initialized
|
||||
/**
|
||||
* @brief Called when KDB is initialized.
|
||||
*
|
||||
* Reads the KDBinit file from the SystemRoot\System32\drivers\etc directory and executes it.
|
||||
*/
|
||||
VOID
|
||||
* Reads the KDBinit file from the SystemRoot\System32\drivers\etc directory
|
||||
* and executes it.
|
||||
**/
|
||||
NTSTATUS
|
||||
KdbpCliInit(VOID)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
|
@ -3812,6 +3814,10 @@ KdbpCliInit(VOID)
|
|||
PCHAR FileBuffer;
|
||||
ULONG OldEflags;
|
||||
|
||||
/* Don't load the KDBinit file if its buffer is already lying around */
|
||||
if (KdbInitFileBuffer)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
/* Initialize the object attributes */
|
||||
RtlInitUnicodeString(&FileName, L"\\SystemRoot\\System32\\drivers\\etc\\KDBinit");
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
|
@ -3828,7 +3834,7 @@ KdbpCliInit(VOID)
|
|||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT("Could not open \\SystemRoot\\System32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
|
||||
return;
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Get the size of the file */
|
||||
|
@ -3839,7 +3845,7 @@ KdbpCliInit(VOID)
|
|||
{
|
||||
ZwClose(hFile);
|
||||
DPRINT("Could not query size of \\SystemRoot\\System32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
|
||||
return;
|
||||
return Status;
|
||||
}
|
||||
FileSize = FileStdInfo.EndOfFile.u.LowPart;
|
||||
|
||||
|
@ -3849,7 +3855,7 @@ KdbpCliInit(VOID)
|
|||
{
|
||||
ZwClose(hFile);
|
||||
DPRINT("Could not allocate %d bytes for KDBinit file\n", FileSize);
|
||||
return;
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Load file into memory */
|
||||
|
@ -3860,7 +3866,7 @@ KdbpCliInit(VOID)
|
|||
{
|
||||
ExFreePool(FileBuffer);
|
||||
DPRINT("Could not read KDBinit file into memory (Status 0x%lx)\n", Status);
|
||||
return;
|
||||
return Status;
|
||||
}
|
||||
|
||||
FileSize = min(FileSize, (INT)Iosb.Information);
|
||||
|
@ -3872,13 +3878,15 @@ KdbpCliInit(VOID)
|
|||
|
||||
/* Interpret the init file... */
|
||||
KdbInitFileBuffer = FileBuffer;
|
||||
//KdbEnter(); // FIXME
|
||||
//KdbEnter(); // FIXME, see commit baa47fa5e
|
||||
KdbInitFileBuffer = NULL;
|
||||
|
||||
/* Leave critical section */
|
||||
__writeeflags(OldEflags);
|
||||
|
||||
ExFreePool(FileBuffer);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3947,10 +3955,10 @@ KdbDebugPrint(
|
|||
* @param[in] BootPhase
|
||||
* Phase of initialization.
|
||||
*
|
||||
* @return None.
|
||||
* @return A status value.
|
||||
* @note Also known as "KdpKdbgInit".
|
||||
**/
|
||||
VOID
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
KdbInitialize(
|
||||
_In_ PKD_DISPATCH_TABLE DispatchTable,
|
||||
|
@ -3959,14 +3967,17 @@ KdbInitialize(
|
|||
if (BootPhase == 0)
|
||||
{
|
||||
/* Write out the functions that we support for now */
|
||||
DispatchTable->KdpInitRoutine = KdbInitialize;
|
||||
DispatchTable->KdpPrintRoutine = KdbDebugPrint;
|
||||
|
||||
/* Register as a Provider */
|
||||
/* Register for BootPhase 1 initialization and as a Provider */
|
||||
DispatchTable->KdpInitRoutine = KdbInitialize;
|
||||
InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
|
||||
}
|
||||
else if (BootPhase == 1)
|
||||
{
|
||||
/* Register for later BootPhase 2 reinitialization */
|
||||
DispatchTable->KdpInitRoutine = KdbInitialize;
|
||||
|
||||
/* Initialize Dmesg support */
|
||||
|
||||
/* Allocate a buffer for Dmesg log buffer. +1 for terminating null,
|
||||
|
@ -3987,6 +3998,20 @@ KdbInitialize(
|
|||
/* Initialize symbols support */
|
||||
KdbSymInit(BootPhase);
|
||||
}
|
||||
else if (BootPhase >= 2)
|
||||
{
|
||||
/* I/O is now set up for disk access: Read KDB Data */
|
||||
NTSTATUS Status = KdbpCliInit();
|
||||
|
||||
/* Schedule an I/O reinitialization if needed */
|
||||
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
|
||||
Status == STATUS_OBJECT_PATH_NOT_FOUND)
|
||||
{
|
||||
DispatchTable->KdpInitRoutine = KdbInitialize;
|
||||
}
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
Loading…
Reference in a new issue