https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4ce819ca5a7de11a1b77e…
commit 4ce819ca5a7de11a1b77e05124d2d2f0d35871c4
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Mon Jan 9 19:35:18 2023 +0100
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Sat Mar 11 01:22:19 2023 +0100
[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/bl…
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.
---
ntoskrnl/include/internal/kd.h | 9 +-
ntoskrnl/io/iomgr/iomgr.c | 8 --
ntoskrnl/kd/kdio.c | 121 +++++++++--------
ntoskrnl/kd/kdmain.c | 291 ++++++++++++++++++++++++++++++++++++++---
ntoskrnl/kdbg/kdb.h | 4 +-
ntoskrnl/kdbg/kdb_cli.c | 51 ++++++--
6 files changed, 384 insertions(+), 100 deletions(-)
diff --git a/ntoskrnl/include/internal/kd.h b/ntoskrnl/include/internal/kd.h
index 325e0b0ea3c..424ae89fe1f 100644
--- a/ntoskrnl/include/internal/kd.h
+++ b/ntoskrnl/include/internal/kd.h
@@ -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 */
diff --git a/ntoskrnl/io/iomgr/iomgr.c b/ntoskrnl/io/iomgr/iomgr.c
index 380a8518661..b982414e270 100644
--- a/ntoskrnl/io/iomgr/iomgr.c
+++ b/ntoskrnl/io/iomgr/iomgr.c
@@ -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;
diff --git a/ntoskrnl/kd/kdio.c b/ntoskrnl/kd/kdio.c
index 129db8b9b5e..4575b9a2e8b 100644
--- a/ntoskrnl/kd/kdio.c
+++ b/ntoskrnl/kd/kdio.c
@@ -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;
}
diff --git a/ntoskrnl/kd/kdmain.c b/ntoskrnl/kd/kdmain.c
index be749a705a9..abd358da0fc 100644
--- a/ntoskrnl/kd/kdmain.c
+++ b/ntoskrnl/kd/kdmain.c
@@ -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(a)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(a)reactos.org>
+ * Copyright 2020 Hervé Poussineau <hpoussin(a)reactos.org>
+ * Copyright 2023 Hermès Bélusca-Maïto
<hermes.belusca-maito(a)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++)
+ {
+ 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)
{
- InitRoutines[i](&DispatchTable[i], 0);
+ /* 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 */
diff --git a/ntoskrnl/kdbg/kdb.h b/ntoskrnl/kdbg/kdb.h
index 11bc4cb452f..4424dd4837c 100644
--- a/ntoskrnl/kdbg/kdb.h
+++ b/ntoskrnl/kdbg/kdb.h
@@ -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
diff --git a/ntoskrnl/kdbg/kdb_cli.c b/ntoskrnl/kdbg/kdb_cli.c
index fdd5d694924..aaebf573cf8 100644
--- a/ntoskrnl/kdbg/kdb_cli.c
+++ b/ntoskrnl/kdbg/kdb_cli.c
@@ -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 */