reactos/ntoskrnl/kdbg/kdb_cli.c
Pierre Schweitzer d6dc1fd231
[NTOSKRNL] Add a raw implementation of !irpfind in kdbg
This is far from perfect, and totally doesn't match the
WinDBG way of doing it. Instead of browsing pool to find
matching 'IRP' tags, we just browse all the processes
to find the queued IRP. This requires the IRPs to be queued,
obviously, and will make us miss the leaked IRPs, for instance.

Proper way to do it would be to implement !poolfind and then
rely on its implementation to find our IRPs.

Perhaps later ;-)
2019-01-01 22:00:56 +01:00

3860 lines
115 KiB
C

/*
* ReactOS kernel
* Copyright (C) 2005 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/kdbg/kdb_cli.c
* PURPOSE: Kernel debugger command line interface
* PROGRAMMER: Gregor Anich (blight@blight.eu.org)
* Hervé Poussineau
* UPDATE HISTORY:
* Created 16/01/2005
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* DEFINES *******************************************************************/
#define KEY_BS 8
#define KEY_ESC 27
#define KEY_DEL 127
#define KEY_SCAN_UP 72
#define KEY_SCAN_DOWN 80
/* Scan codes of keyboard keys: */
#define KEYSC_END 0x004f
#define KEYSC_PAGEUP 0x0049
#define KEYSC_PAGEDOWN 0x0051
#define KEYSC_HOME 0x0047
#define KEYSC_ARROWUP 0x0048
#define KDB_ENTER_CONDITION_TO_STRING(cond) \
((cond) == KdbDoNotEnter ? "never" : \
((cond) == KdbEnterAlways ? "always" : \
((cond) == KdbEnterFromKmode ? "kmode" : "umode")))
#define KDB_ACCESS_TYPE_TO_STRING(type) \
((type) == KdbAccessRead ? "read" : \
((type) == KdbAccessWrite ? "write" : \
((type) == KdbAccessReadWrite ? "rdwr" : "exec")))
#define NPX_STATE_TO_STRING(state) \
((state) == NPX_STATE_LOADED ? "Loaded" : \
((state) == NPX_STATE_NOT_LOADED ? "Not loaded" : "Unknown"))
/* PROTOTYPES ****************************************************************/
static BOOLEAN KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdDisassembleX(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdRegs(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdContinue(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdStep(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdBreakPointList(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdEnableDisableClearBreakPoint(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdThread(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdProc(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdMod(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdPcr(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdTss(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdReboot(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdFilter(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdDmesg(ULONG Argc, PCHAR Argv[]);
BOOLEAN ExpKdbgExtPool(ULONG Argc, PCHAR Argv[]);
BOOLEAN ExpKdbgExtPoolUsed(ULONG Argc, PCHAR Argv[]);
BOOLEAN ExpKdbgExtFileCache(ULONG Argc, PCHAR Argv[]);
BOOLEAN ExpKdbgExtDefWrites(ULONG Argc, PCHAR Argv[]);
BOOLEAN PspKdbgIrpFind(ULONG Argc, PCHAR Argv[]);
#ifdef __ROS_DWARF__
static BOOLEAN KdbpCmdPrintStruct(ULONG Argc, PCHAR Argv[]);
#endif
/* GLOBALS *******************************************************************/
static PKDBG_CLI_ROUTINE KdbCliCallbacks[10];
static BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */
static BOOLEAN KdbBreakOnModuleLoad = FALSE; /* Set to TRUE to break into KDB when a module is loaded */
static CHAR KdbCommandHistoryBuffer[2048]; /* Command history string ringbuffer */
static PCHAR KdbCommandHistory[sizeof(KdbCommandHistoryBuffer) / 8] = { NULL }; /* Command history ringbuffer */
static LONG KdbCommandHistoryBufferIndex = 0;
static LONG KdbCommandHistoryIndex = 0;
static ULONG KdbNumberOfRowsPrinted = 0;
static ULONG KdbNumberOfColsPrinted = 0;
static BOOLEAN KdbOutputAborted = FALSE;
static BOOLEAN KdbRepeatLastCommand = FALSE;
static LONG KdbNumberOfRowsTerminal = -1;
static LONG KdbNumberOfColsTerminal = -1;
PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */
BOOLEAN KdbpBugCheckRequested = FALSE;
/* Vars for dmesg */
/* defined in ../kd/kdio.c, declare here: */
extern volatile BOOLEAN KdbpIsInDmesgMode;
extern const ULONG KdpDmesgBufferSize;
extern PCHAR KdpDmesgBuffer;
extern volatile ULONG KdpDmesgCurrentPosition;
extern volatile ULONG KdpDmesgFreeBytes;
extern volatile ULONG KdbDmesgTotalWritten;
static const struct
{
PCHAR Name;
PCHAR Syntax;
PCHAR Help;
BOOLEAN (*Fn)(ULONG Argc, PCHAR Argv[]);
} KdbDebuggerCommands[] = {
/* Data */
{ NULL, NULL, "Data", NULL },
{ "?", "? expression", "Evaluate expression.", KdbpCmdEvalExpression },
{ "disasm", "disasm [address] [L count]", "Disassemble count instructions at address.", KdbpCmdDisassembleX },
{ "x", "x [address] [L count]", "Display count dwords, starting at addr.", KdbpCmdDisassembleX },
{ "regs", "regs", "Display general purpose registers.", KdbpCmdRegs },
{ "cregs", "cregs", "Display control registers.", KdbpCmdRegs },
{ "sregs", "sregs", "Display status registers.", KdbpCmdRegs },
{ "dregs", "dregs", "Display debug registers.", KdbpCmdRegs },
{ "bt", "bt [*frameaddr|thread id]", "Prints current backtrace or from given frame addr", KdbpCmdBackTrace },
#ifdef __ROS_DWARF__
{ "dt", "dt [mod] [type] [addr]", "Print a struct. Addr is optional.", KdbpCmdPrintStruct },
#endif
/* Flow control */
{ NULL, NULL, "Flow control", NULL },
{ "cont", "cont", "Continue execution (leave debugger)", KdbpCmdContinue },
{ "step", "step [count]", "Execute single instructions, stepping into interrupts.", KdbpCmdStep },
{ "next", "next [count]", "Execute single instructions, skipping calls and reps.", KdbpCmdStep },
{ "bl", "bl", "List breakpoints.", KdbpCmdBreakPointList },
{ "be", "be [breakpoint]", "Enable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
{ "bd", "bd [breakpoint]", "Disable breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
{ "bc", "bc [breakpoint]", "Clear breakpoint.", KdbpCmdEnableDisableClearBreakPoint },
{ "bpx", "bpx [address] [IF condition]", "Set software execution breakpoint at address.", KdbpCmdBreakPoint },
{ "bpm", "bpm [r|w|rw|x] [byte|word|dword] [address] [IF condition]", "Set memory breakpoint at address.", KdbpCmdBreakPoint },
/* Process/Thread */
{ NULL, NULL, "Process/Thread", NULL },
{ "thread", "thread [list[ pid]|[attach ]tid]", "List threads in current or specified process, display thread with given id or attach to thread.", KdbpCmdThread },
{ "proc", "proc [list|[attach ]pid]", "List processes, display process with given id or attach to process.", KdbpCmdProc },
/* System information */
{ NULL, NULL, "System info", NULL },
{ "mod", "mod [address]", "List all modules or the one containing address.", KdbpCmdMod },
{ "gdt", "gdt", "Display global descriptor table.", KdbpCmdGdtLdtIdt },
{ "ldt", "ldt", "Display local descriptor table.", KdbpCmdGdtLdtIdt },
{ "idt", "idt", "Display interrupt descriptor table.", KdbpCmdGdtLdtIdt },
{ "pcr", "pcr", "Display processor control region.", KdbpCmdPcr },
{ "tss", "tss", "Display task state segment.", KdbpCmdTss },
/* Others */
{ NULL, NULL, "Others", NULL },
{ "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck },
{ "reboot", "reboot", "Reboots the system.", KdbpCmdReboot},
{ "filter", "filter [error|warning|trace|info|level]+|-[componentname|default]", "Enable/disable debug channels", KdbpCmdFilter },
{ "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet },
{ "dmesg", "dmesg", "Display debug messages on screen, with navigation on pages.", KdbpCmdDmesg },
{ "kmsg", "kmsg", "Kernel dmesg. Alias for dmesg.", KdbpCmdDmesg },
{ "help", "help", "Display help screen.", KdbpCmdHelp },
{ "!pool", "!pool [Address [Flags]]", "Display information about pool allocations.", ExpKdbgExtPool },
{ "!poolused", "!poolused [Flags [Tag]]", "Display pool usage.", ExpKdbgExtPoolUsed },
{ "!filecache", "!filecache", "Display cache usage.", ExpKdbgExtFileCache },
{ "!defwrites", "!defwrites", "Display cache write values.", ExpKdbgExtDefWrites },
{ "!irpfind", "!irpfind [criteria data]", "Lists IRPs potentially matching criteria", PspKdbgIrpFind },
};
/* FUNCTIONS *****************************************************************/
/*!\brief Transform a component name to an integer
*
* \param ComponentName The name of the component.
* \param ComponentId Receives the component id on success.
*
* \retval TRUE Success.
* \retval FALSE Failure.
*/
static BOOLEAN
KdbpGetComponentId(
IN PCCH ComponentName,
OUT PULONG ComponentId)
{
ULONG i;
static struct
{
PCCH Name;
ULONG Id;
}
ComponentTable[] =
{
{ "DEFAULT", MAXULONG },
{ "SYSTEM", DPFLTR_SYSTEM_ID },
{ "SMSS", DPFLTR_SMSS_ID },
{ "SETUP", DPFLTR_SETUP_ID },
{ "NTFS", DPFLTR_NTFS_ID },
{ "FSTUB", DPFLTR_FSTUB_ID },
{ "CRASHDUMP", DPFLTR_CRASHDUMP_ID },
{ "CDAUDIO", DPFLTR_CDAUDIO_ID },
{ "CDROM", DPFLTR_CDROM_ID },
{ "CLASSPNP", DPFLTR_CLASSPNP_ID },
{ "DISK", DPFLTR_DISK_ID },
{ "REDBOOK", DPFLTR_REDBOOK_ID },
{ "STORPROP", DPFLTR_STORPROP_ID },
{ "SCSIPORT", DPFLTR_SCSIPORT_ID },
{ "SCSIMINIPORT", DPFLTR_SCSIMINIPORT_ID },
{ "CONFIG", DPFLTR_CONFIG_ID },
{ "I8042PRT", DPFLTR_I8042PRT_ID },
{ "SERMOUSE", DPFLTR_SERMOUSE_ID },
{ "LSERMOUS", DPFLTR_LSERMOUS_ID },
{ "KBDHID", DPFLTR_KBDHID_ID },
{ "MOUHID", DPFLTR_MOUHID_ID },
{ "KBDCLASS", DPFLTR_KBDCLASS_ID },
{ "MOUCLASS", DPFLTR_MOUCLASS_ID },
{ "TWOTRACK", DPFLTR_TWOTRACK_ID },
{ "WMILIB", DPFLTR_WMILIB_ID },
{ "ACPI", DPFLTR_ACPI_ID },
{ "AMLI", DPFLTR_AMLI_ID },
{ "HALIA64", DPFLTR_HALIA64_ID },
{ "VIDEO", DPFLTR_VIDEO_ID },
{ "SVCHOST", DPFLTR_SVCHOST_ID },
{ "VIDEOPRT", DPFLTR_VIDEOPRT_ID },
{ "TCPIP", DPFLTR_TCPIP_ID },
{ "DMSYNTH", DPFLTR_DMSYNTH_ID },
{ "NTOSPNP", DPFLTR_NTOSPNP_ID },
{ "FASTFAT", DPFLTR_FASTFAT_ID },
{ "SAMSS", DPFLTR_SAMSS_ID },
{ "PNPMGR", DPFLTR_PNPMGR_ID },
{ "NETAPI", DPFLTR_NETAPI_ID },
{ "SCSERVER", DPFLTR_SCSERVER_ID },
{ "SCCLIENT", DPFLTR_SCCLIENT_ID },
{ "SERIAL", DPFLTR_SERIAL_ID },
{ "SERENUM", DPFLTR_SERENUM_ID },
{ "UHCD", DPFLTR_UHCD_ID },
{ "RPCPROXY", DPFLTR_RPCPROXY_ID },
{ "AUTOCHK", DPFLTR_AUTOCHK_ID },
{ "DCOMSS", DPFLTR_DCOMSS_ID },
{ "UNIMODEM", DPFLTR_UNIMODEM_ID },
{ "SIS", DPFLTR_SIS_ID },
{ "FLTMGR", DPFLTR_FLTMGR_ID },
{ "WMICORE", DPFLTR_WMICORE_ID },
{ "BURNENG", DPFLTR_BURNENG_ID },
{ "IMAPI", DPFLTR_IMAPI_ID },
{ "SXS", DPFLTR_SXS_ID },
{ "FUSION", DPFLTR_FUSION_ID },
{ "IDLETASK", DPFLTR_IDLETASK_ID },
{ "SOFTPCI", DPFLTR_SOFTPCI_ID },
{ "TAPE", DPFLTR_TAPE_ID },
{ "MCHGR", DPFLTR_MCHGR_ID },
{ "IDEP", DPFLTR_IDEP_ID },
{ "PCIIDE", DPFLTR_PCIIDE_ID },
{ "FLOPPY", DPFLTR_FLOPPY_ID },
{ "FDC", DPFLTR_FDC_ID },
{ "TERMSRV", DPFLTR_TERMSRV_ID },
{ "W32TIME", DPFLTR_W32TIME_ID },
{ "PREFETCHER", DPFLTR_PREFETCHER_ID },
{ "RSFILTER", DPFLTR_RSFILTER_ID },
{ "FCPORT", DPFLTR_FCPORT_ID },
{ "PCI", DPFLTR_PCI_ID },
{ "DMIO", DPFLTR_DMIO_ID },
{ "DMCONFIG", DPFLTR_DMCONFIG_ID },
{ "DMADMIN", DPFLTR_DMADMIN_ID },
{ "WSOCKTRANSPORT", DPFLTR_WSOCKTRANSPORT_ID },
{ "VSS", DPFLTR_VSS_ID },
{ "PNPMEM", DPFLTR_PNPMEM_ID },
{ "PROCESSOR", DPFLTR_PROCESSOR_ID },
{ "DMSERVER", DPFLTR_DMSERVER_ID },
{ "SR", DPFLTR_SR_ID },
{ "INFINIBAND", DPFLTR_INFINIBAND_ID },
{ "IHVDRIVER", DPFLTR_IHVDRIVER_ID },
{ "IHVVIDEO", DPFLTR_IHVVIDEO_ID },
{ "IHVAUDIO", DPFLTR_IHVAUDIO_ID },
{ "IHVNETWORK", DPFLTR_IHVNETWORK_ID },
{ "IHVSTREAMING", DPFLTR_IHVSTREAMING_ID },
{ "IHVBUS", DPFLTR_IHVBUS_ID },
{ "HPS", DPFLTR_HPS_ID },
{ "RTLTHREADPOOL", DPFLTR_RTLTHREADPOOL_ID },
{ "LDR", DPFLTR_LDR_ID },
{ "TCPIP6", DPFLTR_TCPIP6_ID },
{ "ISAPNP", DPFLTR_ISAPNP_ID },
{ "SHPC", DPFLTR_SHPC_ID },
{ "STORPORT", DPFLTR_STORPORT_ID },
{ "STORMINIPORT", DPFLTR_STORMINIPORT_ID },
{ "PRINTSPOOLER", DPFLTR_PRINTSPOOLER_ID },
{ "VSSDYNDISK", DPFLTR_VSSDYNDISK_ID },
{ "VERIFIER", DPFLTR_VERIFIER_ID },
{ "VDS", DPFLTR_VDS_ID },
{ "VDSBAS", DPFLTR_VDSBAS_ID },
{ "VDSDYN", DPFLTR_VDSDYN_ID },
{ "VDSDYNDR", DPFLTR_VDSDYNDR_ID },
{ "VDSLDR", DPFLTR_VDSLDR_ID },
{ "VDSUTIL", DPFLTR_VDSUTIL_ID },
{ "DFRGIFC", DPFLTR_DFRGIFC_ID },
{ "MM", DPFLTR_MM_ID },
{ "DFSC", DPFLTR_DFSC_ID },
{ "WOW64", DPFLTR_WOW64_ID },
{ "ALPC", DPFLTR_ALPC_ID },
{ "WDI", DPFLTR_WDI_ID },
{ "PERFLIB", DPFLTR_PERFLIB_ID },
{ "KTM", DPFLTR_KTM_ID },
{ "IOSTRESS", DPFLTR_IOSTRESS_ID },
{ "HEAP", DPFLTR_HEAP_ID },
{ "WHEA", DPFLTR_WHEA_ID },
{ "USERGDI", DPFLTR_USERGDI_ID },
{ "MMCSS", DPFLTR_MMCSS_ID },
{ "TPM", DPFLTR_TPM_ID },
{ "THREADORDER", DPFLTR_THREADORDER_ID },
{ "ENVIRON", DPFLTR_ENVIRON_ID },
{ "EMS", DPFLTR_EMS_ID },
{ "WDT", DPFLTR_WDT_ID },
{ "FVEVOL", DPFLTR_FVEVOL_ID },
{ "NDIS", DPFLTR_NDIS_ID },
{ "NVCTRACE", DPFLTR_NVCTRACE_ID },
{ "LUAFV", DPFLTR_LUAFV_ID },
{ "APPCOMPAT", DPFLTR_APPCOMPAT_ID },
{ "USBSTOR", DPFLTR_USBSTOR_ID },
{ "SBP2PORT", DPFLTR_SBP2PORT_ID },
{ "COVERAGE", DPFLTR_COVERAGE_ID },
{ "CACHEMGR", DPFLTR_CACHEMGR_ID },
{ "MOUNTMGR", DPFLTR_MOUNTMGR_ID },
{ "CFR", DPFLTR_CFR_ID },
{ "TXF", DPFLTR_TXF_ID },
{ "KSECDD", DPFLTR_KSECDD_ID },
{ "FLTREGRESS", DPFLTR_FLTREGRESS_ID },
{ "MPIO", DPFLTR_MPIO_ID },
{ "MSDSM", DPFLTR_MSDSM_ID },
{ "UDFS", DPFLTR_UDFS_ID },
{ "PSHED", DPFLTR_PSHED_ID },
{ "STORVSP", DPFLTR_STORVSP_ID },
{ "LSASS", DPFLTR_LSASS_ID },
{ "SSPICLI", DPFLTR_SSPICLI_ID },
{ "CNG", DPFLTR_CNG_ID },
{ "EXFAT", DPFLTR_EXFAT_ID },
{ "FILETRACE", DPFLTR_FILETRACE_ID },
{ "XSAVE", DPFLTR_XSAVE_ID },
{ "SE", DPFLTR_SE_ID },
{ "DRIVEEXTENDER", DPFLTR_DRIVEEXTENDER_ID },
};
for (i = 0; i < sizeof(ComponentTable) / sizeof(ComponentTable[0]); i++)
{
if (_stricmp(ComponentName, ComponentTable[i].Name) == 0)
{
*ComponentId = ComponentTable[i].Id;
return TRUE;
}
}
return FALSE;
}
/*!\brief Evaluates an expression...
*
* Much like KdbpRpnEvaluateExpression, but prints the error message (if any)
* at the given offset.
*
* \param Expression Expression to evaluate.
* \param ErrOffset Offset (in characters) to print the error message at.
* \param Result Receives the result on success.
*
* \retval TRUE Success.
* \retval FALSE Failure.
*/
static BOOLEAN
KdbpEvaluateExpression(
IN PCHAR Expression,
IN LONG ErrOffset,
OUT PULONGLONG Result)
{
static CHAR ErrMsgBuffer[130] = "^ ";
LONG ExpressionErrOffset = -1;
PCHAR ErrMsg = ErrMsgBuffer;
BOOLEAN Ok;
Ok = KdbpRpnEvaluateExpression(Expression, KdbCurrentTrapFrame, Result,
&ExpressionErrOffset, ErrMsgBuffer + 2);
if (!Ok)
{
if (ExpressionErrOffset >= 0)
ExpressionErrOffset += ErrOffset;
else
ErrMsg += 2;
KdbpPrint("%*s%s\n", ExpressionErrOffset, "", ErrMsg);
}
return Ok;
}
BOOLEAN
NTAPI
KdbpGetHexNumber(
IN PCHAR pszNum,
OUT ULONG_PTR *pulValue)
{
char *endptr;
/* Skip optional '0x' prefix */
if ((pszNum[0] == '0') && ((pszNum[1] == 'x') || (pszNum[1] == 'X')))
pszNum += 2;
/* Make a number from the string (hex) */
*pulValue = strtoul(pszNum, &endptr, 16);
return (*endptr == '\0');
}
/*!\brief Evaluates an expression and displays the result.
*/
static BOOLEAN
KdbpCmdEvalExpression(
ULONG Argc,
PCHAR Argv[])
{
ULONG i, len;
ULONGLONG Result = 0;
ULONG ul;
LONG l = 0;
BOOLEAN Ok;
if (Argc < 2)
{
KdbpPrint("?: Argument required\n");
return TRUE;
}
/* Put the arguments back together */
Argc--;
for (i = 1; i < Argc; i++)
{
len = strlen(Argv[i]);
Argv[i][len] = ' ';
}
/* Evaluate the expression */
Ok = KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result);
if (Ok)
{
if (Result > 0x00000000ffffffffLL)
{
if (Result & 0x8000000000000000LL)
KdbpPrint("0x%016I64x %20I64u %20I64d\n", Result, Result, Result);
else
KdbpPrint("0x%016I64x %20I64u\n", Result, Result);
}
else
{
ul = (ULONG)Result;
if (ul <= 0xff && ul >= 0x80)
l = (LONG)((CHAR)ul);
else if (ul <= 0xffff && ul >= 0x8000)
l = (LONG)((SHORT)ul);
else
l = (LONG)ul;
if (l < 0)
KdbpPrint("0x%08lx %10lu %10ld\n", ul, ul, l);
else
KdbpPrint("0x%08lx %10lu\n", ul, ul);
}
}
return TRUE;
}
#ifdef __ROS_DWARF__
/*!\brief Print a struct
*/
static VOID
KdbpPrintStructInternal
(PROSSYM_INFO Info,
PCHAR Indent,
BOOLEAN DoRead,
PVOID BaseAddress,
PROSSYM_AGGREGATE Aggregate)
{
ULONG i;
ULONGLONG Result;
PROSSYM_AGGREGATE_MEMBER Member;
ULONG IndentLen = strlen(Indent);
ROSSYM_AGGREGATE MemberAggregate = {0 };
for (i = 0; i < Aggregate->NumElements; i++) {
Member = &Aggregate->Elements[i];
KdbpPrint("%s%p+%x: %s", Indent, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size, Member->Name ? Member->Name : "<anoymous>");
if (DoRead) {
if (!strcmp(Member->Type, "_UNICODE_STRING")) {
KdbpPrint("\"%wZ\"\n", ((PCHAR)BaseAddress) + Member->BaseOffset);
continue;
} else if (!strcmp(Member->Type, "PUNICODE_STRING")) {
KdbpPrint("\"%wZ\"\n", *(((PUNICODE_STRING*)((PCHAR)BaseAddress) + Member->BaseOffset)));
continue;
}
switch (Member->Size) {
case 1:
case 2:
case 4:
case 8: {
Result = 0;
if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
if (Member->Bits) {
Result >>= Member->FirstBit;
Result &= ((1 << Member->Bits) - 1);
}
KdbpPrint(" %lx\n", Result);
}
else goto readfail;
break;
}
default: {
if (Member->Size < 8) {
if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
ULONG j;
for (j = 0; j < Member->Size; j++) {
KdbpPrint(" %02x", (int)(Result & 0xff));
Result >>= 8;
}
} else goto readfail;
} else {
KdbpPrint(" %s @ %p {\n", Member->Type, ((PCHAR)BaseAddress) + Member->BaseOffset);
Indent[IndentLen] = ' ';
if (RosSymAggregate(Info, Member->Type, &MemberAggregate)) {
KdbpPrintStructInternal(Info, Indent, DoRead, ((PCHAR)BaseAddress) + Member->BaseOffset, &MemberAggregate);
RosSymFreeAggregate(&MemberAggregate);
}
Indent[IndentLen] = 0;
KdbpPrint("%s}\n", Indent);
} break;
}
}
} else {
readfail:
if (Member->Size <= 8) {
KdbpPrint(" ??\n");
} else {
KdbpPrint(" %s @ %x {\n", Member->Type, Member->BaseOffset);
Indent[IndentLen] = ' ';
if (RosSymAggregate(Info, Member->Type, &MemberAggregate)) {
KdbpPrintStructInternal(Info, Indent, DoRead, BaseAddress, &MemberAggregate);
RosSymFreeAggregate(&MemberAggregate);
}
Indent[IndentLen] = 0;
KdbpPrint("%s}\n", Indent);
}
}
}
}
PROSSYM_INFO KdbpSymFindCachedFile(PUNICODE_STRING ModName);
static BOOLEAN
KdbpCmdPrintStruct(
ULONG Argc,
PCHAR Argv[])
{
ULONG i;
ULONGLONG Result = 0;
PVOID BaseAddress = 0;
ROSSYM_AGGREGATE Aggregate = {0};
UNICODE_STRING ModName = {0};
ANSI_STRING AnsiName = {0};
CHAR Indent[100] = {0};
PROSSYM_INFO Info;
if (Argc < 3) goto end;
AnsiName.Length = AnsiName.MaximumLength = strlen(Argv[1]);
AnsiName.Buffer = Argv[1];
RtlAnsiStringToUnicodeString(&ModName, &AnsiName, TRUE);
Info = KdbpSymFindCachedFile(&ModName);
if (!Info || !RosSymAggregate(Info, Argv[2], &Aggregate)) {
DPRINT1("Could not get aggregate\n");
goto end;
}
// Get an argument for location if it was given
if (Argc > 3) {
ULONG len;
PCHAR ArgStart = Argv[3];
DPRINT1("Trying to get expression\n");
for (i = 3; i < Argc - 1; i++)
{
len = strlen(Argv[i]);
Argv[i][len] = ' ';
}
/* Evaluate the expression */
DPRINT1("Arg: %s\n", ArgStart);
if (KdbpEvaluateExpression(ArgStart, strlen(ArgStart), &Result)) {
BaseAddress = (PVOID)(ULONG_PTR)Result;
DPRINT1("BaseAddress: %p\n", BaseAddress);
}
}
DPRINT1("BaseAddress %p\n", BaseAddress);
KdbpPrintStructInternal(Info, Indent, !!BaseAddress, BaseAddress, &Aggregate);
end:
RosSymFreeAggregate(&Aggregate);
RtlFreeUnicodeString(&ModName);
return TRUE;
}
#endif
/*!\brief Display list of active debug channels
*/
static BOOLEAN
KdbpCmdFilter(
ULONG Argc,
PCHAR Argv[])
{
ULONG i, j, ComponentId, Level;
ULONG set = DPFLTR_MASK, clear = DPFLTR_MASK;
PCHAR pend;
LPCSTR opt, p;
static struct
{
LPCSTR Name;
ULONG Level;
}
debug_classes[] =
{
{ "error", 1 << DPFLTR_ERROR_LEVEL },
{ "warning", 1 << DPFLTR_WARNING_LEVEL },
{ "trace", 1 << DPFLTR_TRACE_LEVEL },
{ "info", 1 << DPFLTR_INFO_LEVEL },
};
for (i = 1; i < Argc; i++)
{
opt = Argv[i];
p = opt + strcspn(opt, "+-");
if (!p[0]) p = opt; /* assume it's a debug channel name */
if (p > opt)
{
for (j = 0; j < sizeof(debug_classes) / sizeof(debug_classes[0]); j++)
{
SIZE_T len = strlen(debug_classes[j].Name);
if (len != (p - opt))
continue;
if (_strnicmp(opt, debug_classes[j].Name, len) == 0) /* found it */
{
if (*p == '+')
set |= debug_classes[j].Level;
else
clear |= debug_classes[j].Level;
break;
}
}
if (j == sizeof(debug_classes) / sizeof(debug_classes[0]))
{
Level = strtoul(opt, &pend, 0);
if (pend != p)
{
KdbpPrint("filter: bad class name '%.*s'\n", p - opt, opt);
continue;
}
if (*p == '+')
set |= Level;
else
clear |= Level;
}
}
else
{
if (*p == '-')
clear = MAXULONG;
else
set = MAXULONG;
}
if (*p == '+' || *p == '-')
p++;
if (!KdbpGetComponentId(p, &ComponentId))
{
KdbpPrint("filter: '%s' is not a valid component name!\n", p);
return TRUE;
}
/* Get current mask value */
NtSetDebugFilterState(ComponentId, set, TRUE);
NtSetDebugFilterState(ComponentId, clear, FALSE);
}
return TRUE;
}
/*!\brief Disassembles 10 instructions at eip or given address or
* displays 16 dwords from memory at given address.
*/
static BOOLEAN
KdbpCmdDisassembleX(
ULONG Argc,
PCHAR Argv[])
{
ULONG Count;
ULONG ul;
INT i;
ULONGLONG Result = 0;
ULONG_PTR Address = KdbCurrentTrapFrame->Tf.Eip;
LONG InstLen;
if (Argv[0][0] == 'x') /* display memory */
Count = 16;
else /* disassemble */
Count = 10;
if (Argc >= 2)
{
/* Check for [L count] part */
ul = 0;
if (strcmp(Argv[Argc-2], "L") == 0)
{
ul = strtoul(Argv[Argc-1], NULL, 0);
if (ul > 0)
{
Count = ul;
Argc -= 2;
}
}
else if (Argv[Argc-1][0] == 'L')
{
ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
if (ul > 0)
{
Count = ul;
Argc--;
}
}
/* Put the remaining arguments back together */
Argc--;
for (ul = 1; ul < Argc; ul++)
{
Argv[ul][strlen(Argv[ul])] = ' ';
}
Argc++;
}
/* Evaluate the expression */
if (Argc > 1)
{
if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
return TRUE;
if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
KdbpPrint("Warning: Address %I64x is beeing truncated\n",Result);
Address = (ULONG_PTR)Result;
}
else if (Argv[0][0] == 'x')
{
KdbpPrint("x: Address argument required.\n");
return TRUE;
}
if (Argv[0][0] == 'x')
{
/* Display dwords */
ul = 0;
while (Count > 0)
{
if (!KdbSymPrintAddress((PVOID)Address, NULL))
KdbpPrint("<%x>:", Address);
else
KdbpPrint(":");
i = min(4, Count);
Count -= i;
while (--i >= 0)
{
if (!NT_SUCCESS(KdbpSafeReadMemory(&ul, (PVOID)Address, sizeof(ul))))
KdbpPrint(" ????????");
else
KdbpPrint(" %08x", ul);
Address += sizeof(ul);
}
KdbpPrint("\n");
}
}
else
{
/* Disassemble */
while (Count-- > 0)
{
if (!KdbSymPrintAddress((PVOID)Address, NULL))
KdbpPrint("<%08x>: ", Address);
else
KdbpPrint(": ");
InstLen = KdbpDisassemble(Address, KdbUseIntelSyntax);
if (InstLen < 0)
{
KdbpPrint("<INVALID>\n");
return TRUE;
}
KdbpPrint("\n");
Address += InstLen;
}
}
return TRUE;
}
/*!\brief Displays CPU registers.
*/
static BOOLEAN
KdbpCmdRegs(
ULONG Argc,
PCHAR Argv[])
{
PKTRAP_FRAME Tf = &KdbCurrentTrapFrame->Tf;
INT i;
static const PCHAR EflagsBits[32] = { " CF", NULL, " PF", " BIT3", " AF", " BIT5",
" ZF", " SF", " TF", " IF", " DF", " OF",
NULL, NULL, " NT", " BIT15", " RF", " VF",
" AC", " VIF", " VIP", " ID", " BIT22",
" BIT23", " BIT24", " BIT25", " BIT26",
" BIT27", " BIT28", " BIT29", " BIT30",
" BIT31" };
if (Argv[0][0] == 'r') /* regs */
{
KdbpPrint("CS:EIP 0x%04x:0x%08x\n"
"SS:ESP 0x%04x:0x%08x\n"
" EAX 0x%08x EBX 0x%08x\n"
" ECX 0x%08x EDX 0x%08x\n"
" ESI 0x%08x EDI 0x%08x\n"
" EBP 0x%08x\n",
Tf->SegCs & 0xFFFF, Tf->Eip,
Tf->HardwareSegSs, Tf->HardwareEsp,
Tf->Eax, Tf->Ebx,
Tf->Ecx, Tf->Edx,
Tf->Esi, Tf->Edi,
Tf->Ebp);
KdbpPrint("EFLAGS 0x%08x ", Tf->EFlags);
for (i = 0; i < 32; i++)
{
if (i == 1)
{
if ((Tf->EFlags & (1 << 1)) == 0)
KdbpPrint(" !BIT1");
}
else if (i == 12)
{
KdbpPrint(" IOPL%d", (Tf->EFlags >> 12) & 3);
}
else if (i == 13)
{
}
else if ((Tf->EFlags & (1 << i)) != 0)
{
KdbpPrint(EflagsBits[i]);
}
}
KdbpPrint("\n");
}
else if (Argv[0][0] == 'c') /* cregs */
{
ULONG Cr0, Cr2, Cr3, Cr4;
KDESCRIPTOR Gdtr = {0, 0, 0}, Idtr = {0, 0, 0};
USHORT Ldtr;
static const PCHAR Cr0Bits[32] = { " PE", " MP", " EM", " TS", " ET", " NE", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
" WP", NULL, " AM", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, " NW", " CD", " PG" };
static const PCHAR Cr4Bits[32] = { " VME", " PVI", " TSD", " DE", " PSE", " PAE", " MCE", " PGE",
" PCE", " OSFXSR", " OSXMMEXCPT", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
Cr0 = KdbCurrentTrapFrame->Cr0;
Cr2 = KdbCurrentTrapFrame->Cr2;
Cr3 = KdbCurrentTrapFrame->Cr3;
Cr4 = KdbCurrentTrapFrame->Cr4;
/* Get descriptor table regs */
Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
Ldtr = Ke386GetLocalDescriptorTable();
__sidt(&Idtr.Limit);
/* Display the control registers */
KdbpPrint("CR0 0x%08x ", Cr0);
for (i = 0; i < 32; i++)
{
if (!Cr0Bits[i])
continue;
if ((Cr0 & (1 << i)) != 0)
KdbpPrint(Cr0Bits[i]);
}
KdbpPrint("\nCR2 0x%08x\n", Cr2);
KdbpPrint("CR3 0x%08x Pagedir-Base 0x%08x %s%s\n", Cr3, (Cr3 & 0xfffff000),
(Cr3 & (1 << 3)) ? " PWT" : "", (Cr3 & (1 << 4)) ? " PCD" : "" );
KdbpPrint("CR4 0x%08x ", Cr4);
for (i = 0; i < 32; i++)
{
if (!Cr4Bits[i])
continue;
if ((Cr4 & (1 << i)) != 0)
KdbpPrint(Cr4Bits[i]);
}
/* Display the descriptor table regs */
KdbpPrint("\nGDTR Base 0x%08x Size 0x%04x\n", Gdtr.Base, Gdtr.Limit);
KdbpPrint("LDTR 0x%04x\n", Ldtr);
KdbpPrint("IDTR Base 0x%08x Size 0x%04x\n", Idtr.Base, Idtr.Limit);
}
else if (Argv[0][0] == 's') /* sregs */
{
KdbpPrint("CS 0x%04x Index 0x%04x %cDT RPL%d\n",
Tf->SegCs & 0xffff, (Tf->SegCs & 0xffff) >> 3,
(Tf->SegCs & (1 << 2)) ? 'L' : 'G', Tf->SegCs & 3);
KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n",
Tf->SegDs, Tf->SegDs >> 3, (Tf->SegDs & (1 << 2)) ? 'L' : 'G', Tf->SegDs & 3);
KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n",
Tf->SegEs, Tf->SegEs >> 3, (Tf->SegEs & (1 << 2)) ? 'L' : 'G', Tf->SegEs & 3);
KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n",
Tf->SegFs, Tf->SegFs >> 3, (Tf->SegFs & (1 << 2)) ? 'L' : 'G', Tf->SegFs & 3);
KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n",
Tf->SegGs, Tf->SegGs >> 3, (Tf->SegGs & (1 << 2)) ? 'L' : 'G', Tf->SegGs & 3);
KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n",
Tf->HardwareSegSs, Tf->HardwareSegSs >> 3, (Tf->HardwareSegSs & (1 << 2)) ? 'L' : 'G', Tf->HardwareSegSs & 3);
}
else /* dregs */
{
ASSERT(Argv[0][0] == 'd');
KdbpPrint("DR0 0x%08x\n"
"DR1 0x%08x\n"
"DR2 0x%08x\n"
"DR3 0x%08x\n"
"DR6 0x%08x\n"
"DR7 0x%08x\n",
Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3,
Tf->Dr6, Tf->Dr7);
}
return TRUE;
}
static BOOLEAN
KdbpTrapFrameFromPrevTss(
PKTRAP_FRAME TrapFrame)
{
ULONG_PTR Eip, Ebp;
KDESCRIPTOR Gdtr;
KGDTENTRY Desc;
USHORT Sel;
PKTSS Tss;
Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
Sel = Ke386GetTr();
if ((Sel & (sizeof(KGDTENTRY) - 1)) ||
(Sel < sizeof(KGDTENTRY)) ||
(Sel + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
return FALSE;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
(PVOID)(Gdtr.Base + Sel),
sizeof(KGDTENTRY))))
return FALSE;
if (Desc.HighWord.Bits.Type != 0xB)
return FALSE;
Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
Desc.HighWord.Bytes.BaseMid << 16 |
Desc.HighWord.Bytes.BaseHi << 24);
if (!NT_SUCCESS(KdbpSafeReadMemory(&Sel,
(PVOID)&Tss->Backlink,
sizeof(USHORT))))
return FALSE;
if ((Sel & (sizeof(KGDTENTRY) - 1)) ||
(Sel < sizeof(KGDTENTRY)) ||
(Sel + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
return FALSE;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
(PVOID)(Gdtr.Base + Sel),
sizeof(KGDTENTRY))))
return FALSE;
if (Desc.HighWord.Bits.Type != 0xB)
return FALSE;
Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
Desc.HighWord.Bytes.BaseMid << 16 |
Desc.HighWord.Bytes.BaseHi << 24);
if (!NT_SUCCESS(KdbpSafeReadMemory(&Eip,
(PVOID)&Tss->Eip,
sizeof(ULONG_PTR))))
return FALSE;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Ebp,
(PVOID)&Tss->Ebp,
sizeof(ULONG_PTR))))
return FALSE;
TrapFrame->Eip = Eip;
TrapFrame->Ebp = Ebp;
return TRUE;
}
VOID __cdecl KiTrap02(VOID);
VOID FASTCALL KiTrap03Handler(IN PKTRAP_FRAME);
VOID __cdecl KiTrap08(VOID);
VOID __cdecl KiTrap09(VOID);
static BOOLEAN
KdbpInNmiOrDoubleFaultHandler(
ULONG_PTR Address)
{
return (Address > (ULONG_PTR)KiTrap02 && Address < (ULONG_PTR)KiTrap03Handler) ||
(Address > (ULONG_PTR)KiTrap08 && Address < (ULONG_PTR)KiTrap09);
}
/*!\brief Displays a backtrace.
*/
static BOOLEAN
KdbpCmdBackTrace(
ULONG Argc,
PCHAR Argv[])
{
ULONG ul;
ULONGLONG Result = 0;
ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp;
ULONG_PTR Address;
KTRAP_FRAME TrapFrame;
if (Argc >= 2)
{
/* Check for [L count] part */
ul = 0;
if (strcmp(Argv[Argc-2], "L") == 0)
{
ul = strtoul(Argv[Argc-1], NULL, 0);
if (ul > 0)
{
Argc -= 2;
}
}
else if (Argv[Argc-1][0] == 'L')
{
ul = strtoul(Argv[Argc-1] + 1, NULL, 0);
if (ul > 0)
{
Argc--;
}
}
/* Put the remaining arguments back together */
Argc--;
for (ul = 1; ul < Argc; ul++)
{
Argv[ul][strlen(Argv[ul])] = ' ';
}
Argc++;
}
/* Check if frame addr or thread id is given. */
if (Argc > 1)
{
if (Argv[1][0] == '*')
{
Argv[1]++;
/* Evaluate the expression */
if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
return TRUE;
if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
KdbpPrint("Warning: Address %I64x is beeing truncated\n",Result);
Frame = (ULONG_PTR)Result;
}
else
{
KdbpPrint("Thread backtrace not supported yet!\n");
return TRUE;
}
}
else
{
KdbpPrint("Eip:\n");
/* Try printing the function at EIP */
if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip, &KdbCurrentTrapFrame->Tf))
KdbpPrint("<%08x>\n", KdbCurrentTrapFrame->Tf.Eip);
else
KdbpPrint("\n");
}
TrapFrame = KdbCurrentTrapFrame->Tf;
KdbpPrint("Frames:\n");
for (;;)
{
BOOLEAN GotNextFrame;
if (Frame == 0)
break;
if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR))))
{
KdbpPrint("Couldn't access memory at 0x%p!\n", Frame + sizeof(ULONG_PTR));
break;
}
if ((GotNextFrame = NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR)))))
TrapFrame.Ebp = Frame;
/* Print the location of the call instruction */
if (!KdbSymPrintAddress((PVOID)(Address - 5), &TrapFrame))
KdbpPrint("<%08x>\n", Address);
else
KdbpPrint("\n");
if (KdbOutputAborted) break;
if (Address == 0)
break;
if (KdbpInNmiOrDoubleFaultHandler(Address))
{
if ((GotNextFrame = KdbpTrapFrameFromPrevTss(&TrapFrame)))
{
Address = TrapFrame.Eip;
Frame = TrapFrame.Ebp;
if (!KdbSymPrintAddress((PVOID)Address, &TrapFrame))
KdbpPrint("<%08x>\n", Address);
else
KdbpPrint("\n");
}
}
if (!GotNextFrame)
{
KdbpPrint("Couldn't access memory at 0x%p!\n", Frame);
break;
}
}
return TRUE;
}
/*!\brief Continues execution of the system/leaves KDB.
*/
static BOOLEAN
KdbpCmdContinue(
ULONG Argc,
PCHAR Argv[])
{
/* Exit the main loop */
return FALSE;
}
/*!\brief Continues execution of the system/leaves KDB.
*/
static BOOLEAN
KdbpCmdStep(
ULONG Argc,
PCHAR Argv[])
{
ULONG Count = 1;
if (Argc > 1)
{
Count = strtoul(Argv[1], NULL, 0);
if (Count == 0)
{
KdbpPrint("%s: Integer argument required\n", Argv[0]);
return TRUE;
}
}
if (Argv[0][0] == 'n')
KdbSingleStepOver = TRUE;
else
KdbSingleStepOver = FALSE;
/* Set the number of single steps and return to the interrupted code. */
KdbNumSingleSteps = Count;
return FALSE;
}
/*!\brief Lists breakpoints.
*/
static BOOLEAN
KdbpCmdBreakPointList(
ULONG Argc,
PCHAR Argv[])
{
LONG l;
ULONG_PTR Address = 0;
KDB_BREAKPOINT_TYPE Type = 0;
KDB_ACCESS_TYPE AccessType = 0;
UCHAR Size = 0;
UCHAR DebugReg = 0;
BOOLEAN Enabled = FALSE;
BOOLEAN Global = FALSE;
PEPROCESS Process = NULL;
PCHAR str1, str2, ConditionExpr, GlobalOrLocal;
CHAR Buffer[20];
l = KdbpGetNextBreakPointNr(0);
if (l < 0)
{
KdbpPrint("No breakpoints.\n");
return TRUE;
}
KdbpPrint("Breakpoints:\n");
do
{
if (!KdbpGetBreakPointInfo(l, &Address, &Type, &Size, &AccessType, &DebugReg,
&Enabled, &Global, &Process, &ConditionExpr))
{
continue;
}
if (l == KdbLastBreakPointNr)
{
str1 = "\x1b[1m*";
str2 = "\x1b[0m";
}
else
{
str1 = " ";
str2 = "";
}
if (Global)
{
GlobalOrLocal = " global";
}
else
{
GlobalOrLocal = Buffer;
sprintf(Buffer, " PID 0x%08lx",
(ULONG)(Process ? Process->UniqueProcessId : INVALID_HANDLE_VALUE));
}
if (Type == KdbBreakPointSoftware || Type == KdbBreakPointTemporary)
{
KdbpPrint(" %s%03d BPX 0x%08x%s%s%s%s%s\n",
str1, l, Address,
Enabled ? "" : " disabled",
GlobalOrLocal,
ConditionExpr ? " IF " : "",
ConditionExpr ? ConditionExpr : "",
str2);
}
else if (Type == KdbBreakPointHardware)
{
if (!Enabled)
{
KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s disabled%s%s%s%s\n", str1, l, Address,
KDB_ACCESS_TYPE_TO_STRING(AccessType),
Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
GlobalOrLocal,
ConditionExpr ? " IF " : "",
ConditionExpr ? ConditionExpr : "",
str2);
}
else
{
KdbpPrint(" %s%03d BPM 0x%08x %-5s %-5s DR%d%s%s%s%s\n", str1, l, Address,
KDB_ACCESS_TYPE_TO_STRING(AccessType),
Size == 1 ? "byte" : (Size == 2 ? "word" : "dword"),
DebugReg,
GlobalOrLocal,
ConditionExpr ? " IF " : "",
ConditionExpr ? ConditionExpr : "",
str2);
}
}
}
while ((l = KdbpGetNextBreakPointNr(l+1)) >= 0);
return TRUE;
}
/*!\brief Enables, disables or clears a breakpoint.
*/
static BOOLEAN
KdbpCmdEnableDisableClearBreakPoint(
ULONG Argc,
PCHAR Argv[])
{
PCHAR pend;
ULONG BreakPointNr;
if (Argc < 2)
{
KdbpPrint("%s: argument required\n", Argv[0]);
return TRUE;
}
pend = Argv[1];
BreakPointNr = strtoul(Argv[1], &pend, 0);
if (pend == Argv[1] || *pend != '\0')
{
KdbpPrint("%s: integer argument required\n", Argv[0]);
return TRUE;
}
if (Argv[0][1] == 'e') /* enable */
{
KdbpEnableBreakPoint(BreakPointNr, NULL);
}
else if (Argv [0][1] == 'd') /* disable */
{
KdbpDisableBreakPoint(BreakPointNr, NULL);
}
else /* clear */
{
ASSERT(Argv[0][1] == 'c');
KdbpDeleteBreakPoint(BreakPointNr, NULL);
}
return TRUE;
}
/*!\brief Sets a software or hardware (memory) breakpoint at the given address.
*/
static BOOLEAN
KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[])
{
ULONGLONG Result = 0;
ULONG_PTR Address;
KDB_BREAKPOINT_TYPE Type;
UCHAR Size = 0;
KDB_ACCESS_TYPE AccessType = 0;
ULONG AddressArgIndex, i;
LONG ConditionArgIndex;
BOOLEAN Global = TRUE;
if (Argv[0][2] == 'x') /* software breakpoint */
{
if (Argc < 2)
{
KdbpPrint("bpx: Address argument required.\n");
return TRUE;
}
AddressArgIndex = 1;
Type = KdbBreakPointSoftware;
}
else /* memory breakpoint */
{
ASSERT(Argv[0][2] == 'm');
if (Argc < 2)
{
KdbpPrint("bpm: Access type argument required (one of r, w, rw, x)\n");
return TRUE;
}
if (_stricmp(Argv[1], "x") == 0)
AccessType = KdbAccessExec;
else if (_stricmp(Argv[1], "r") == 0)
AccessType = KdbAccessRead;
else if (_stricmp(Argv[1], "w") == 0)
AccessType = KdbAccessWrite;
else if (_stricmp(Argv[1], "rw") == 0)
AccessType = KdbAccessReadWrite;
else
{
KdbpPrint("bpm: Unknown access type '%s'\n", Argv[1]);
return TRUE;
}
if (Argc < 3)
{
KdbpPrint("bpm: %s argument required.\n", AccessType == KdbAccessExec ? "Address" : "Memory size");
return TRUE;
}
AddressArgIndex = 3;
if (_stricmp(Argv[2], "byte") == 0)
Size = 1;
else if (_stricmp(Argv[2], "word") == 0)
Size = 2;
else if (_stricmp(Argv[2], "dword") == 0)
Size = 4;
else if (AccessType == KdbAccessExec)
{
Size = 1;
AddressArgIndex--;
}
else
{
KdbpPrint("bpm: Unknown memory size '%s'\n", Argv[2]);
return TRUE;
}
if (Argc <= AddressArgIndex)
{
KdbpPrint("bpm: Address argument required.\n");
return TRUE;
}
Type = KdbBreakPointHardware;
}
/* Put the arguments back together */
ConditionArgIndex = -1;
for (i = AddressArgIndex; i < (Argc-1); i++)
{
if (strcmp(Argv[i+1], "IF") == 0) /* IF found */
{
ConditionArgIndex = i + 2;
if ((ULONG)ConditionArgIndex >= Argc)
{
KdbpPrint("%s: IF requires condition expression.\n", Argv[0]);
return TRUE;
}
for (i = ConditionArgIndex; i < (Argc-1); i++)
Argv[i][strlen(Argv[i])] = ' ';
break;
}
Argv[i][strlen(Argv[i])] = ' ';
}
/* Evaluate the address expression */
if (!KdbpEvaluateExpression(Argv[AddressArgIndex],
sizeof("kdb:> ")-1 + (Argv[AddressArgIndex]-Argv[0]),
&Result))
{
return TRUE;
}
if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0],Result);
Address = (ULONG_PTR)Result;
KdbpInsertBreakPoint(Address, Type, Size, AccessType,
(ConditionArgIndex < 0) ? NULL : Argv[ConditionArgIndex],
Global, NULL);
return TRUE;
}
/*!\brief Lists threads or switches to another thread context.
*/
static BOOLEAN
KdbpCmdThread(
ULONG Argc,
PCHAR Argv[])
{
PLIST_ENTRY Entry;
PETHREAD Thread = NULL;
PEPROCESS Process = NULL;
BOOLEAN ReferencedThread = FALSE, ReferencedProcess = FALSE;
PULONG Esp;
PULONG Ebp;
ULONG Eip;
ULONG ul = 0;
PCHAR State, pend, str1, str2;
static const PCHAR ThreadStateToString[DeferredReady+1] =
{
"Initialized", "Ready", "Running",
"Standby", "Terminated", "Waiting",
"Transition", "DeferredReady"
};
ASSERT(KdbCurrentProcess);
if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
{
Process = KdbCurrentProcess;
if (Argc >= 3)
{
ul = strtoul(Argv[2], &pend, 0);
if (Argv[2] == pend)
{
KdbpPrint("thread: '%s' is not a valid process id!\n", Argv[2]);
return TRUE;
}
if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
{
KdbpPrint("thread: Invalid process id!\n");
return TRUE;
}
/* Remember our reference */
ReferencedProcess = TRUE;
}
Entry = Process->ThreadListHead.Flink;
if (Entry == &Process->ThreadListHead)
{
if (Argc >= 3)
KdbpPrint("No threads in process 0x%08x!\n", ul);
else
KdbpPrint("No threads in current process!\n");
if (ReferencedProcess)
ObDereferenceObject(Process);
return TRUE;
}
KdbpPrint(" TID State Prior. Affinity EBP EIP\n");
do
{
Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
if (Thread == KdbCurrentThread)
{
str1 = "\x1b[1m*";
str2 = "\x1b[0m";
}
else
{
str1 = " ";
str2 = "";
}
if (!Thread->Tcb.InitialStack)
{
/* Thread has no kernel stack (probably terminated) */
Esp = Ebp = NULL;
Eip = 0;
}
else if (Thread->Tcb.TrapFrame)
{
if (Thread->Tcb.TrapFrame->PreviousPreviousMode == KernelMode)
Esp = (PULONG)Thread->Tcb.TrapFrame->TempEsp;
else
Esp = (PULONG)Thread->Tcb.TrapFrame->HardwareEsp;
Ebp = (PULONG)Thread->Tcb.TrapFrame->Ebp;
Eip = Thread->Tcb.TrapFrame->Eip;
}
else
{
Esp = (PULONG)Thread->Tcb.KernelStack;
Ebp = (PULONG)Esp[4];
Eip = 0;
if (Ebp) /* FIXME: Should we attach to the process to read Ebp[1]? */
KdbpSafeReadMemory(&Eip, Ebp + 1, sizeof (Eip));
}
if (Thread->Tcb.State < (DeferredReady + 1))
State = ThreadStateToString[Thread->Tcb.State];
else
State = "Unknown";
KdbpPrint(" %s0x%08x %-11s %3d 0x%08x 0x%08x 0x%08x%s\n",
str1,
Thread->Cid.UniqueThread,
State,
Thread->Tcb.Priority,
Thread->Tcb.Affinity,
Ebp,
Eip,
str2);
Entry = Entry->Flink;
}
while (Entry != &Process->ThreadListHead);
/* Release our reference, if any */
if (ReferencedProcess)
ObDereferenceObject(Process);
}
else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
{
if (Argc < 3)
{
KdbpPrint("thread attach: thread id argument required!\n");
return TRUE;
}
ul = strtoul(Argv[2], &pend, 0);
if (Argv[2] == pend)
{
KdbpPrint("thread attach: '%s' is not a valid thread id!\n", Argv[2]);
return TRUE;
}
if (!KdbpAttachToThread((PVOID)ul))
{
return TRUE;
}
KdbpPrint("Attached to thread 0x%08x.\n", ul);
}
else
{
Thread = KdbCurrentThread;
if (Argc >= 2)
{
ul = strtoul(Argv[1], &pend, 0);
if (Argv[1] == pend)
{
KdbpPrint("thread: '%s' is not a valid thread id!\n", Argv[1]);
return TRUE;
}
if (!NT_SUCCESS(PsLookupThreadByThreadId((PVOID)ul, &Thread)))
{
KdbpPrint("thread: Invalid thread id!\n");
return TRUE;
}
/* Remember our reference */
ReferencedThread = TRUE;
}
if (Thread->Tcb.State < (DeferredReady + 1))
State = ThreadStateToString[Thread->Tcb.State];
else
State = "Unknown";
KdbpPrint("%s"
" TID: 0x%08x\n"
" State: %s (0x%x)\n"
" Priority: %d\n"
" Affinity: 0x%08x\n"
" Initial Stack: 0x%08x\n"
" Stack Limit: 0x%08x\n"
" Stack Base: 0x%08x\n"
" Kernel Stack: 0x%08x\n"
" Trap Frame: 0x%08x\n"
" NPX State: %s (0x%x)\n",
(Argc < 2) ? "Current Thread:\n" : "",
Thread->Cid.UniqueThread,
State, Thread->Tcb.State,
Thread->Tcb.Priority,
Thread->Tcb.Affinity,
Thread->Tcb.InitialStack,
Thread->Tcb.StackLimit,
Thread->Tcb.StackBase,
Thread->Tcb.KernelStack,
Thread->Tcb.TrapFrame,
NPX_STATE_TO_STRING(Thread->Tcb.NpxState), Thread->Tcb.NpxState);
/* Release our reference if we had one */
if (ReferencedThread)
ObDereferenceObject(Thread);
}
return TRUE;
}
/*!\brief Lists processes or switches to another process context.
*/
static BOOLEAN
KdbpCmdProc(
ULONG Argc,
PCHAR Argv[])
{
PLIST_ENTRY Entry;
PEPROCESS Process;
BOOLEAN ReferencedProcess = FALSE;
PCHAR State, pend, str1, str2;
ULONG ul;
extern LIST_ENTRY PsActiveProcessHead;
if (Argc >= 2 && _stricmp(Argv[1], "list") == 0)
{
Entry = PsActiveProcessHead.Flink;
if (!Entry || Entry == &PsActiveProcessHead)
{
KdbpPrint("No processes in the system!\n");
return TRUE;
}
KdbpPrint(" PID State Filename\n");
do
{
Process = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
if (Process == KdbCurrentProcess)
{
str1 = "\x1b[1m*";
str2 = "\x1b[0m";
}
else
{
str1 = " ";
str2 = "";
}
State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
KdbpPrint(" %s0x%08x %-10s %s%s\n",
str1,
Process->UniqueProcessId,
State,
Process->ImageFileName,
str2);
Entry = Entry->Flink;
}
while(Entry != &PsActiveProcessHead);
}
else if (Argc >= 2 && _stricmp(Argv[1], "attach") == 0)
{
if (Argc < 3)
{
KdbpPrint("process attach: process id argument required!\n");
return TRUE;
}
ul = strtoul(Argv[2], &pend, 0);
if (Argv[2] == pend)
{
KdbpPrint("process attach: '%s' is not a valid process id!\n", Argv[2]);
return TRUE;
}
if (!KdbpAttachToProcess((PVOID)ul))
{
return TRUE;
}
KdbpPrint("Attached to process 0x%08x, thread 0x%08x.\n", (ULONG)ul,
(ULONG)KdbCurrentThread->Cid.UniqueThread);
}
else
{
Process = KdbCurrentProcess;
if (Argc >= 2)
{
ul = strtoul(Argv[1], &pend, 0);
if (Argv[1] == pend)
{
KdbpPrint("proc: '%s' is not a valid process id!\n", Argv[1]);
return TRUE;
}
if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)ul, &Process)))
{
KdbpPrint("proc: Invalid process id!\n");
return TRUE;
}
/* Remember our reference */
ReferencedProcess = TRUE;
}
State = ((Process->Pcb.State == ProcessInMemory) ? "In Memory" :
((Process->Pcb.State == ProcessOutOfMemory) ? "Out of Memory" : "In Transition"));
KdbpPrint("%s"
" PID: 0x%08x\n"
" State: %s (0x%x)\n"
" Image Filename: %s\n",
(Argc < 2) ? "Current process:\n" : "",
Process->UniqueProcessId,
State, Process->Pcb.State,
Process->ImageFileName);
/* Release our reference, if any */
if (ReferencedProcess)
ObDereferenceObject(Process);
}
return TRUE;
}
/*!\brief Lists loaded modules or the one containing the specified address.
*/
static BOOLEAN
KdbpCmdMod(
ULONG Argc,
PCHAR Argv[])
{
ULONGLONG Result = 0;
ULONG_PTR Address;
PLDR_DATA_TABLE_ENTRY LdrEntry;
BOOLEAN DisplayOnlyOneModule = FALSE;
INT i = 0;
if (Argc >= 2)
{
/* Put the arguments back together */
Argc--;
while (--Argc >= 1)
Argv[Argc][strlen(Argv[Argc])] = ' ';
/* Evaluate the expression */
if (!KdbpEvaluateExpression(Argv[1], sizeof("kdb:> ")-1 + (Argv[1]-Argv[0]), &Result))
{
return TRUE;
}
if (Result > (ULONGLONG)(~((ULONG_PTR)0)))
KdbpPrint("%s: Warning: Address %I64x is beeing truncated\n", Argv[0],Result);
Address = (ULONG_PTR)Result;
if (!KdbpSymFindModule((PVOID)Address, NULL, -1, &LdrEntry))
{
KdbpPrint("No module containing address 0x%p found!\n", Address);
return TRUE;
}
DisplayOnlyOneModule = TRUE;
}
else
{
if (!KdbpSymFindModule(NULL, NULL, 0, &LdrEntry))
{
ULONG_PTR ntoskrnlBase = ((ULONG_PTR)KdbpCmdMod) & 0xfff00000;
KdbpPrint(" Base Size Name\n");
KdbpPrint(" %08x %08x %s\n", ntoskrnlBase, 0, "ntoskrnl.exe");
return TRUE;
}
i = 1;
}
KdbpPrint(" Base Size Name\n");
for (;;)
{
KdbpPrint(" %08x %08x %wZ\n", LdrEntry->DllBase, LdrEntry->SizeOfImage, &LdrEntry->BaseDllName);
if(DisplayOnlyOneModule || !KdbpSymFindModule(NULL, NULL, i++, &LdrEntry))
break;
}
return TRUE;
}
/*!\brief Displays GDT, LDT or IDTd.
*/
static BOOLEAN
KdbpCmdGdtLdtIdt(
ULONG Argc,
PCHAR Argv[])
{
KDESCRIPTOR Reg;
ULONG SegDesc[2];
ULONG SegBase;
ULONG SegLimit;
PCHAR SegType;
USHORT SegSel;
UCHAR Type, Dpl;
INT i;
ULONG ul;
if (Argv[0][0] == 'i')
{
/* Read IDTR */
__sidt(&Reg.Limit);
if (Reg.Limit < 7)
{
KdbpPrint("Interrupt descriptor table is empty.\n");
return TRUE;
}
KdbpPrint("IDT Base: 0x%08x Limit: 0x%04x\n", Reg.Base, Reg.Limit);
KdbpPrint(" Idx Type Seg. Sel. Offset DPL\n");
for (i = 0; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
{
if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)(Reg.Base + i), sizeof(SegDesc))))
{
KdbpPrint("Couldn't access memory at 0x%08x!\n", Reg.Base + i);
return TRUE;
}
Dpl = ((SegDesc[1] >> 13) & 3);
if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
SegType = "TASKGATE";
else if ((SegDesc[1] & 0x1fe0) == 0x0e00) /* 32 bit Interrupt gate */
SegType = "INTGATE32";
else if ((SegDesc[1] & 0x1fe0) == 0x0600) /* 16 bit Interrupt gate */
SegType = "INTGATE16";
else if ((SegDesc[1] & 0x1fe0) == 0x0f00) /* 32 bit Trap gate */
SegType = "TRAPGATE32";
else if ((SegDesc[1] & 0x1fe0) == 0x0700) /* 16 bit Trap gate */
SegType = "TRAPGATE16";
else
SegType = "UNKNOWN";
if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
{
KdbpPrint(" %03d %-10s [NP] [NP] %02d\n",
i / 8, SegType, Dpl);
}
else if ((SegDesc[1] & 0x1f00) == 0x0500) /* Task gate */
{
SegSel = SegDesc[0] >> 16;
KdbpPrint(" %03d %-10s 0x%04x %02d\n",
i / 8, SegType, SegSel, Dpl);
}
else
{
SegSel = SegDesc[0] >> 16;
SegBase = (SegDesc[1] & 0xffff0000) | (SegDesc[0] & 0x0000ffff);
KdbpPrint(" %03d %-10s 0x%04x 0x%08x %02d\n",
i / 8, SegType, SegSel, SegBase, Dpl);
}
}
}
else
{
ul = 0;
if (Argv[0][0] == 'g')
{
/* Read GDTR */
Ke386GetGlobalDescriptorTable(&Reg.Limit);
i = 8;
}
else
{
ASSERT(Argv[0][0] == 'l');
/* Read LDTR */
Reg.Limit = Ke386GetLocalDescriptorTable();
Reg.Base = 0;
i = 0;
ul = 1 << 2;
}
if (Reg.Limit < 7)
{
KdbpPrint("%s descriptor table is empty.\n",
Argv[0][0] == 'g' ? "Global" : "Local");
return TRUE;
}
KdbpPrint("%cDT Base: 0x%08x Limit: 0x%04x\n",
Argv[0][0] == 'g' ? 'G' : 'L', Reg.Base, Reg.Limit);
KdbpPrint(" Idx Sel. Type Base Limit DPL Attribs\n");
for (; (i + sizeof(SegDesc) - 1) <= Reg.Limit; i += 8)
{
if (!NT_SUCCESS(KdbpSafeReadMemory(SegDesc, (PVOID)(Reg.Base + i), sizeof(SegDesc))))
{
KdbpPrint("Couldn't access memory at 0x%08x!\n", Reg.Base + i);
return TRUE;
}
Dpl = ((SegDesc[1] >> 13) & 3);
Type = ((SegDesc[1] >> 8) & 0xf);
SegBase = SegDesc[0] >> 16;
SegBase |= (SegDesc[1] & 0xff) << 16;
SegBase |= SegDesc[1] & 0xff000000;
SegLimit = SegDesc[0] & 0x0000ffff;
SegLimit |= (SegDesc[1] >> 16) & 0xf;
if ((SegDesc[1] & (1 << 23)) != 0)
{
SegLimit *= 4096;
SegLimit += 4095;
}
else
{
SegLimit++;
}
if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
{
switch (Type)
{
case 1: SegType = "TSS16(Avl)"; break;
case 2: SegType = "LDT"; break;
case 3: SegType = "TSS16(Busy)"; break;
case 4: SegType = "CALLGATE16"; break;
case 5: SegType = "TASKGATE"; break;
case 6: SegType = "INTGATE16"; break;
case 7: SegType = "TRAPGATE16"; break;
case 9: SegType = "TSS32(Avl)"; break;
case 11: SegType = "TSS32(Busy)"; break;
case 12: SegType = "CALLGATE32"; break;
case 14: SegType = "INTGATE32"; break;
case 15: SegType = "INTGATE32"; break;
default: SegType = "UNKNOWN"; break;
}
if (!(Type >= 1 && Type <= 3) &&
Type != 9 && Type != 11)
{
SegBase = 0;
SegLimit = 0;
}
}
else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
{
if ((SegDesc[1] & (1 << 22)) != 0)
SegType = "DATA32";
else
SegType = "DATA16";
}
else /* Code segment */
{
if ((SegDesc[1] & (1 << 22)) != 0)
SegType = "CODE32";
else
SegType = "CODE16";
}
if ((SegDesc[1] & (1 << 15)) == 0) /* not present */
{
KdbpPrint(" %03d 0x%04x %-11s [NP] [NP] %02d NP\n",
i / 8, i | Dpl | ul, SegType, Dpl);
}
else
{
KdbpPrint(" %03d 0x%04x %-11s 0x%08x 0x%08x %02d ",
i / 8, i | Dpl | ul, SegType, SegBase, SegLimit, Dpl);
if ((SegDesc[1] & (1 << 12)) == 0) /* System segment */
{
/* FIXME: Display system segment */
}
else if ((SegDesc[1] & (1 << 11)) == 0) /* Data segment */
{
if ((SegDesc[1] & (1 << 10)) != 0) /* Expand-down */
KdbpPrint(" E");
KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/W" : " R");
if ((SegDesc[1] & (1 << 8)) != 0)
KdbpPrint(" A");
}
else /* Code segment */
{
if ((SegDesc[1] & (1 << 10)) != 0) /* Conforming */
KdbpPrint(" C");
KdbpPrint((SegDesc[1] & (1 << 9)) ? " R/X" : " X");
if ((SegDesc[1] & (1 << 8)) != 0)
KdbpPrint(" A");
}
if ((SegDesc[1] & (1 << 20)) != 0)
KdbpPrint(" AVL");
KdbpPrint("\n");
}
}
}
return TRUE;
}
/*!\brief Displays the KPCR
*/
static BOOLEAN
KdbpCmdPcr(
ULONG Argc,
PCHAR Argv[])
{
PKIPCR Pcr = (PKIPCR)KeGetPcr();
KdbpPrint("Current PCR is at 0x%08x.\n", (INT)Pcr);
KdbpPrint(" Tib.ExceptionList: 0x%08x\n"
" Tib.StackBase: 0x%08x\n"
" Tib.StackLimit: 0x%08x\n"
" Tib.SubSystemTib: 0x%08x\n"
" Tib.FiberData/Version: 0x%08x\n"
" Tib.ArbitraryUserPointer: 0x%08x\n"
" Tib.Self: 0x%08x\n"
" SelfPcr: 0x%08x\n"
" PCRCB: 0x%08x\n"
" Irql: 0x%02x\n"
" IRR: 0x%08x\n"
" IrrActive: 0x%08x\n"
" IDR: 0x%08x\n"
" KdVersionBlock: 0x%08x\n"
" IDT: 0x%08x\n"
" GDT: 0x%08x\n"
" TSS: 0x%08x\n"
" MajorVersion: 0x%04x\n"
" MinorVersion: 0x%04x\n"
" SetMember: 0x%08x\n"
" StallScaleFactor: 0x%08x\n"
" Number: 0x%02x\n"
" L2CacheAssociativity: 0x%02x\n"
" VdmAlert: 0x%08x\n"
" L2CacheSize: 0x%08x\n"
" InterruptMode: 0x%08x\n",
Pcr->NtTib.ExceptionList, Pcr->NtTib.StackBase, Pcr->NtTib.StackLimit,
Pcr->NtTib.SubSystemTib, Pcr->NtTib.FiberData, Pcr->NtTib.ArbitraryUserPointer,
Pcr->NtTib.Self, Pcr->SelfPcr, Pcr->Prcb, Pcr->Irql, Pcr->IRR, Pcr->IrrActive,
Pcr->IDR, Pcr->KdVersionBlock, Pcr->IDT, Pcr->GDT, Pcr->TSS,
Pcr->MajorVersion, Pcr->MinorVersion, Pcr->SetMember, Pcr->StallScaleFactor,
Pcr->Number, Pcr->SecondLevelCacheAssociativity,
Pcr->VdmAlert, Pcr->SecondLevelCacheSize, Pcr->InterruptMode);
return TRUE;
}
/*!\brief Displays the TSS
*/
static BOOLEAN
KdbpCmdTss(
ULONG Argc,
PCHAR Argv[])
{
KTSS *Tss = KeGetPcr()->TSS;
KdbpPrint("Current TSS is at 0x%08x.\n", (INT)Tss);
KdbpPrint(" Eip: 0x%08x\n"
" Es: 0x%04x\n"
" Cs: 0x%04x\n"
" Ss: 0x%04x\n"
" Ds: 0x%04x\n"
" Fs: 0x%04x\n"
" Gs: 0x%04x\n"
" IoMapBase: 0x%04x\n",
Tss->Eip, Tss->Es, Tss->Cs, Tss->Ds, Tss->Fs, Tss->Gs, Tss->IoMapBase);
return TRUE;
}
/*!\brief Bugchecks the system.
*/
static BOOLEAN
KdbpCmdBugCheck(
ULONG Argc,
PCHAR Argv[])
{
/* Set the flag and quit looping */
KdbpBugCheckRequested = TRUE;
return FALSE;
}
static BOOLEAN
KdbpCmdReboot(
ULONG Argc,
PCHAR Argv[])
{
/* Reboot immediately (we do not return) */
HalReturnToFirmware(HalRebootRoutine);
return FALSE;
}
VOID
KdbpPager(
IN PCHAR Buffer,
IN ULONG BufLength);
/*!\brief Display debug messages on screen, with paging.
*
* Keys for per-page view: Home, End, PageUp, Arrow Up, PageDown,
* all others are as PageDown.
*/
static BOOLEAN
KdbpCmdDmesg(
ULONG Argc,
PCHAR Argv[])
{
ULONG beg, end;
KdbpIsInDmesgMode = TRUE; /* Toggle logging flag */
if (!KdpDmesgBuffer)
{
KdbpPrint("Dmesg: error, buffer is not allocated! /DEBUGPORT=SCREEN kernel param required for dmesg.\n");
return TRUE;
}
KdbpPrint("*** Dmesg *** TotalWritten=%lu, BufferSize=%lu, CurrentPosition=%lu\n",
KdbDmesgTotalWritten, KdpDmesgBufferSize, KdpDmesgCurrentPosition);
// Pass data to the pager:
end = KdpDmesgCurrentPosition;
beg = (end + KdpDmesgFreeBytes) % KdpDmesgBufferSize;
// no roll-overs, and overwritten=lost bytes
if (KdbDmesgTotalWritten <= KdpDmesgBufferSize)
{
// show buffer (KdpDmesgBuffer + beg, num)
KdbpPager(KdpDmesgBuffer, KdpDmesgCurrentPosition);
}
else
{
// show 2 buffers: (KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg)
// and: (KdpDmesgBuffer, end)
KdbpPager(KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg);
KdbpPrint("*** Dmesg: buffer rollup ***\n");
KdbpPager(KdpDmesgBuffer, end);
}
KdbpPrint("*** Dmesg: end of output ***\n");
KdbpIsInDmesgMode = FALSE; /* Toggle logging flag */
return TRUE;
}
/*!\brief Sets or displays a config variables value.
*/
static BOOLEAN
KdbpCmdSet(
ULONG Argc,
PCHAR Argv[])
{
LONG l;
BOOLEAN First;
PCHAR pend = 0;
KDB_ENTER_CONDITION ConditionFirst = KdbDoNotEnter;
KDB_ENTER_CONDITION ConditionLast = KdbDoNotEnter;
static const PCHAR ExceptionNames[21] =
{
"ZERODEVIDE", "DEBUGTRAP", "NMI", "INT3", "OVERFLOW", "BOUND", "INVALIDOP",
"NOMATHCOP", "DOUBLEFAULT", "RESERVED(9)", "INVALIDTSS", "SEGMENTNOTPRESENT",
"STACKFAULT", "GPF", "PAGEFAULT", "RESERVED(15)", "MATHFAULT", "ALIGNMENTCHECK",
"MACHINECHECK", "SIMDFAULT", "OTHERS"
};
if (Argc == 1)
{
KdbpPrint("Available settings:\n");
KdbpPrint(" syntax [intel|at&t]\n");
KdbpPrint(" condition [exception|*] [first|last] [never|always|kmode|umode]\n");
KdbpPrint(" break_on_module_load [true|false]\n");
}
else if (strcmp(Argv[1], "syntax") == 0)
{
if (Argc == 2)
{
KdbpPrint("syntax = %s\n", KdbUseIntelSyntax ? "intel" : "at&t");
}
else if (Argc >= 3)
{
if (_stricmp(Argv[2], "intel") == 0)
KdbUseIntelSyntax = TRUE;
else if (_stricmp(Argv[2], "at&t") == 0)
KdbUseIntelSyntax = FALSE;
else
KdbpPrint("Unknown syntax '%s'.\n", Argv[2]);
}
}
else if (strcmp(Argv[1], "condition") == 0)
{
if (Argc == 2)
{
KdbpPrint("Conditions: (First) (Last)\n");
for (l = 0; l < RTL_NUMBER_OF(ExceptionNames) - 1; l++)
{
if (!ExceptionNames[l])
continue;
if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
ASSERT(0);
if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
ASSERT(0);
KdbpPrint(" #%02d %-20s %-8s %-8s\n", l, ExceptionNames[l],
KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
}
ASSERT(l == (RTL_NUMBER_OF(ExceptionNames) - 1));
KdbpPrint(" %-20s %-8s %-8s\n", ExceptionNames[l],
KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
}
else
{
if (Argc >= 5 && strcmp(Argv[2], "*") == 0) /* Allow * only when setting condition */
{
l = -1;
}
else
{
l = strtoul(Argv[2], &pend, 0);
if (Argv[2] == pend)
{
for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++)
{
if (!ExceptionNames[l])
continue;
if (_stricmp(ExceptionNames[l], Argv[2]) == 0)
break;
}
}
if (l >= RTL_NUMBER_OF(ExceptionNames))
{
KdbpPrint("Unknown exception '%s'.\n", Argv[2]);
return TRUE;
}
}
if (Argc > 4)
{
if (_stricmp(Argv[3], "first") == 0)
First = TRUE;
else if (_stricmp(Argv[3], "last") == 0)
First = FALSE;
else
{
KdbpPrint("set condition: second argument must be 'first' or 'last'\n");
return TRUE;
}
if (_stricmp(Argv[4], "never") == 0)
ConditionFirst = KdbDoNotEnter;
else if (_stricmp(Argv[4], "always") == 0)
ConditionFirst = KdbEnterAlways;
else if (_stricmp(Argv[4], "umode") == 0)
ConditionFirst = KdbEnterFromUmode;
else if (_stricmp(Argv[4], "kmode") == 0)
ConditionFirst = KdbEnterFromKmode;
else
{
KdbpPrint("set condition: third argument must be 'never', 'always', 'umode' or 'kmode'\n");
return TRUE;
}
if (!KdbpSetEnterCondition(l, First, ConditionFirst))
{
if (l >= 0)
KdbpPrint("Couldn't change condition for exception #%02d\n", l);
else
KdbpPrint("Couldn't change condition for all exceptions\n", l);
}
}
else /* Argc >= 3 */
{
if (!KdbpGetEnterCondition(l, TRUE, &ConditionFirst))
ASSERT(0);
if (!KdbpGetEnterCondition(l, FALSE, &ConditionLast))
ASSERT(0);
if (l < (RTL_NUMBER_OF(ExceptionNames) - 1))
{
KdbpPrint("Condition for exception #%02d (%s): FirstChance %s LastChance %s\n",
l, ExceptionNames[l],
KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
}
else
{
KdbpPrint("Condition for all other exceptions: FirstChance %s LastChance %s\n",
KDB_ENTER_CONDITION_TO_STRING(ConditionFirst),
KDB_ENTER_CONDITION_TO_STRING(ConditionLast));
}
}
}
}
else if (strcmp(Argv[1], "break_on_module_load") == 0)
{
if (Argc == 2)
KdbpPrint("break_on_module_load = %s\n", KdbBreakOnModuleLoad ? "enabled" : "disabled");
else if (Argc >= 3)
{
if (_stricmp(Argv[2], "enable") == 0 || _stricmp(Argv[2], "enabled") == 0 || _stricmp(Argv[2], "true") == 0)
KdbBreakOnModuleLoad = TRUE;
else if (_stricmp(Argv[2], "disable") == 0 || _stricmp(Argv[2], "disabled") == 0 || _stricmp(Argv[2], "false") == 0)
KdbBreakOnModuleLoad = FALSE;
else
KdbpPrint("Unknown setting '%s'.\n", Argv[2]);
}
}
else
{
KdbpPrint("Unknown setting '%s'.\n", Argv[1]);
}
return TRUE;
}
/*!\brief Displays help screen.
*/
static BOOLEAN
KdbpCmdHelp(
ULONG Argc,
PCHAR Argv[])
{
ULONG i;
KdbpPrint("Kernel debugger commands:\n");
for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
{
if (!KdbDebuggerCommands[i].Syntax) /* Command group */
{
if (i > 0)
KdbpPrint("\n");
KdbpPrint("\x1b[7m* %s:\x1b[0m\n", KdbDebuggerCommands[i].Help);
continue;
}
KdbpPrint(" %-20s - %s\n",
KdbDebuggerCommands[i].Syntax,
KdbDebuggerCommands[i].Help);
}
return TRUE;
}
/*!\brief Prints the given string with printf-like formatting.
*
* \param Format Format of the string/arguments.
* \param ... Variable number of arguments matching the format specified in \a Format.
*
* \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
* number of lines required to print a single line from the Buffer in the terminal.
* Prints maximum 4096 chars, because of its buffer size.
*/
VOID
KdbpPrint(
IN PCHAR Format,
IN ... OPTIONAL)
{
static CHAR Buffer[4096];
static BOOLEAN TerminalInitialized = FALSE;
static BOOLEAN TerminalConnected = FALSE;
static BOOLEAN TerminalReportsSize = TRUE;
CHAR c = '\0';
PCHAR p, p2;
ULONG Length;
ULONG i, j;
LONG RowsPrintedByTerminal;
ULONG ScanCode;
va_list ap;
/* Check if the user has aborted output of the current command */
if (KdbOutputAborted)
return;
/* Initialize the terminal */
if (!TerminalInitialized)
{
DbgPrint("\x1b[7h"); /* Enable linewrap */
/* Query terminal type */
/*DbgPrint("\x1b[Z");*/
DbgPrint("\x05");
TerminalInitialized = TRUE;
Length = 0;
KeStallExecutionProcessor(100000);
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
Buffer[Length++] = c;
if (Length >= (sizeof (Buffer) - 1))
break;
}
Buffer[Length] = '\0';
if (Length > 0)
TerminalConnected = TRUE;
}
/* Get number of rows and columns in terminal */
if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
(KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
{
if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
{
/* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
TerminalReportsSize = FALSE;
KeStallExecutionProcessor(100000);
DbgPrint("\x1b[18t");
c = KdbpTryGetCharSerial(5000);
if (c == KEY_ESC)
{
c = KdbpTryGetCharSerial(5000);
if (c == '[')
{
Length = 0;
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
Buffer[Length++] = c;
if (isalpha(c) || Length >= (sizeof (Buffer) - 1))
break;
}
Buffer[Length] = '\0';
if (Buffer[0] == '8' && Buffer[1] == ';')
{
for (i = 2; (i < Length) && (Buffer[i] != ';'); i++);
if (Buffer[i] == ';')
{
Buffer[i++] = '\0';
/* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
KdbNumberOfRowsTerminal = strtoul(Buffer + 2, NULL, 0);
KdbNumberOfColsTerminal = strtoul(Buffer + i, NULL, 0);
TerminalReportsSize = TRUE;
}
}
}
/* Clear further characters */
while ((c = KdbpTryGetCharSerial(5000)) != -1);
}
}
if (KdbNumberOfRowsTerminal <= 0)
{
/* Set number of rows to the default. */
KdbNumberOfRowsTerminal = 23; //24; //Mna.: 23 for SCREEN debugport
}
else if (KdbNumberOfColsTerminal <= 0)
{
/* Set number of cols to the default. */
KdbNumberOfColsTerminal = 75; //80; //Mna.: 75 for SCREEN debugport
}
}
/* Get the string */
va_start(ap, Format);
Length = _vsnprintf(Buffer, sizeof (Buffer) - 1, Format, ap);
Buffer[Length] = '\0';
va_end(ap);
p = Buffer;
while (p[0] != '\0')
{
i = strcspn(p, "\n");
/* Calculate the number of lines which will be printed in the terminal
* when outputting the current line
*/
if (i > 0)
RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
else
RowsPrintedByTerminal = 0;
if (p[i] == '\n')
RowsPrintedByTerminal++;
/*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
/* Display a prompt if we printed one screen full of text */
if (KdbNumberOfRowsTerminal > 0 &&
(LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
{
KdbRepeatLastCommand = FALSE;
if (KdbNumberOfColsPrinted > 0)
DbgPrint("\n");
DbgPrint("--- Press q to abort, any other key to continue ---");
RowsPrintedByTerminal++; /* added by Mna. */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpGetCharSerial();
else
c = KdbpGetCharKeyboard(&ScanCode);
if (c == '\r')
{
/* Try to read '\n' which might follow '\r' - if \n is not received here
* it will be interpreted as "return" when the next command should be read.
*/
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpTryGetCharSerial(5);
else
c = KdbpTryGetCharKeyboard(&ScanCode, 5);
}
DbgPrint("\n");
if (c == 'q')
{
KdbOutputAborted = TRUE;
return;
}
KdbNumberOfRowsPrinted = 0;
KdbNumberOfColsPrinted = 0;
}
/* Insert a NUL after the line and print only the current line. */
if (p[i] == '\n' && p[i + 1] != '\0')
{
c = p[i + 1];
p[i + 1] = '\0';
}
else
{
c = '\0';
}
/* Remove escape sequences from the line if there's no terminal connected */
if (!TerminalConnected)
{
while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
{
if (p2[1] == '[')
{
j = 2;
while (!isalpha(p2[j++]));
strcpy(p2, p2 + j);
}
else
{
strcpy(p2, p2 + 1);
}
}
}
DbgPrint("%s", p);
if (c != '\0')
p[i + 1] = c;
/* Set p to the start of the next line and
* remember the number of rows/cols printed
*/
p += i;
if (p[0] == '\n')
{
p++;
KdbNumberOfColsPrinted = 0;
}
else
{
ASSERT(p[0] == '\0');
KdbNumberOfColsPrinted += i;
}
KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
}
}
/** memrchr(), explicitly defined, since was absent in MinGW of RosBE. */
/*
* Reverse memchr()
* Find the last occurrence of 'c' in the buffer 's' of size 'n'.
*/
void *
memrchr(const void *s, int c, size_t n)
{
const unsigned char *cp;
if (n != 0)
{
cp = (unsigned char *)s + n;
do
{
if (*(--cp) == (unsigned char)c)
return (void *)cp;
} while (--n != 0);
}
return NULL;
}
/*!\brief Calculate pointer position for N lines upper of current position.
*
* \param Buffer Characters buffer to operate on.
* \param BufLength Buffer size.
*
* \note Calculate pointer position for N lines upper of current displaying
* position within the given buffer.
*
* Used by KdbpPager().
* Now N lines count is hardcoded to KdbNumberOfRowsTerminal.
*/
PCHAR
CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
{
PCHAR p;
// p0 is initial guess of Page Start
ULONG p0len = KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal;
PCHAR p0 = pCurPos - p0len;
PCHAR prev_p = p0, p1;
ULONG j;
if (pCurPos < Buffer)
pCurPos = Buffer;
ASSERT(pCurPos <= Buffer + BufLength);
p = memrchr(p0, '\n', p0len);
if (NULL == p)
p = p0;
for (j = KdbNumberOfRowsTerminal; j--; )
{
int linesCnt;
p1 = memrchr(p0, '\n', p-p0);
prev_p = p;
p = p1;
if (NULL == p)
{
p = prev_p;
if (NULL == p)
p = p0;
break;
}
linesCnt = (KdbNumberOfColsTerminal+prev_p-p-2) / KdbNumberOfColsTerminal;
if (linesCnt > 1)
j -= linesCnt-1;
}
ASSERT(p != 0);
++p;
return p;
}
/*!\brief Prints the given string with, page by page.
*
* \param Buffer Characters buffer to print.
* \param BufferLen Buffer size.
*
* \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
* number of lines required to print a single line from the Buffer in the terminal.
* Maximum length of buffer is limited only by memory size.
*
* Note: BufLength should be greater then (KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal).
*
*/
VOID
KdbpPager(
IN PCHAR Buffer,
IN ULONG BufLength)
{
static CHAR InBuffer[4096];
static BOOLEAN TerminalInitialized = FALSE;
static BOOLEAN TerminalConnected = FALSE;
static BOOLEAN TerminalReportsSize = TRUE;
CHAR c = '\0';
PCHAR p, p2;
ULONG Length;
ULONG i, j;
LONG RowsPrintedByTerminal;
ULONG ScanCode;
if( BufLength == 0)
return;
/* Check if the user has aborted output of the current command */
if (KdbOutputAborted)
return;
/* Initialize the terminal */
if (!TerminalInitialized)
{
DbgPrint("\x1b[7h"); /* Enable linewrap */
/* Query terminal type */
/*DbgPrint("\x1b[Z");*/
DbgPrint("\x05");
TerminalInitialized = TRUE;
Length = 0;
KeStallExecutionProcessor(100000);
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
InBuffer[Length++] = c;
if (Length >= (sizeof (InBuffer) - 1))
break;
}
InBuffer[Length] = '\0';
if (Length > 0)
TerminalConnected = TRUE;
}
/* Get number of rows and columns in terminal */
if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
(KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
{
if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
{
/* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
TerminalReportsSize = FALSE;
KeStallExecutionProcessor(100000);
DbgPrint("\x1b[18t");
c = KdbpTryGetCharSerial(5000);
if (c == KEY_ESC)
{
c = KdbpTryGetCharSerial(5000);
if (c == '[')
{
Length = 0;
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
InBuffer[Length++] = c;
if (isalpha(c) || Length >= (sizeof (InBuffer) - 1))
break;
}
InBuffer[Length] = '\0';
if (InBuffer[0] == '8' && InBuffer[1] == ';')
{
for (i = 2; (i < Length) && (InBuffer[i] != ';'); i++);
if (Buffer[i] == ';')
{
Buffer[i++] = '\0';
/* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
KdbNumberOfRowsTerminal = strtoul(InBuffer + 2, NULL, 0);
KdbNumberOfColsTerminal = strtoul(InBuffer + i, NULL, 0);
TerminalReportsSize = TRUE;
}
}
}
/* Clear further characters */
while ((c = KdbpTryGetCharSerial(5000)) != -1);
}
}
if (KdbNumberOfRowsTerminal <= 0)
{
/* Set number of rows to the default. */
KdbNumberOfRowsTerminal = 24;
}
else if (KdbNumberOfColsTerminal <= 0)
{
/* Set number of cols to the default. */
KdbNumberOfColsTerminal = 80;
}
}
/* Get the string */
p = Buffer;
while (p[0] != '\0')
{
if ( p > Buffer+BufLength)
{
DbgPrint("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer+BufLength));
return;
}
i = strcspn(p, "\n");
// Are we out of buffer?
if (p + i > Buffer + BufLength)
// Leaving pager function:
break;
/* Calculate the number of lines which will be printed in the terminal
* when outputting the current line
*/
if (i > 0)
RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
else
RowsPrintedByTerminal = 0;
if (p[i] == '\n')
RowsPrintedByTerminal++;
/*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
/* Display a prompt if we printed one screen full of text */
if (KdbNumberOfRowsTerminal > 0 &&
(LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
{
KdbRepeatLastCommand = FALSE;
if (KdbNumberOfColsPrinted > 0)
DbgPrint("\n");
DbgPrint("--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---");
RowsPrintedByTerminal++;
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpGetCharSerial();
else
c = KdbpGetCharKeyboard(&ScanCode);
if (c == '\r')
{
/* Try to read '\n' which might follow '\r' - if \n is not received here
* it will be interpreted as "return" when the next command should be read.
*/
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpTryGetCharSerial(5);
else
c = KdbpTryGetCharKeyboard(&ScanCode, 5);
}
//DbgPrint("\n"); //Consize version: don't show pressed key
DbgPrint(" '%c'/scan=%04x\n", c, ScanCode); // Shows pressed key
if (c == 'q')
{
KdbOutputAborted = TRUE;
return;
}
if ( ScanCode == KEYSC_END || c=='e')
{
PCHAR pBufEnd = Buffer + BufLength;
p = CountOnePageUp(Buffer, BufLength, pBufEnd);
i = strcspn(p, "\n");
}
else if (ScanCode == KEYSC_PAGEUP || c=='u')
{
p = CountOnePageUp(Buffer, BufLength, p);
i = strcspn(p, "\n");
}
else if (ScanCode == KEYSC_HOME || c=='h')
{
p = Buffer;
i = strcspn(p, "\n");
}
else if (ScanCode == KEYSC_ARROWUP)
{
p = CountOnePageUp(Buffer, BufLength, p);
i = strcspn(p, "\n");
}
KdbNumberOfRowsPrinted = 0;
KdbNumberOfColsPrinted = 0;
}
/* Insert a NUL after the line and print only the current line. */
if (p[i] == '\n' && p[i + 1] != '\0')
{
c = p[i + 1];
p[i + 1] = '\0';
}
else
{
c = '\0';
}
/* Remove escape sequences from the line if there's no terminal connected */
if (!TerminalConnected)
{
while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
{
if (p2[1] == '[')
{
j = 2;
while (!isalpha(p2[j++]));
strcpy(p2, p2 + j);
}
else
{
strcpy(p2, p2 + 1);
}
}
}
// The main printing of the current line:
DbgPrint(p);
// restore not null char with saved:
if (c != '\0')
p[i + 1] = c;
/* Set p to the start of the next line and
* remember the number of rows/cols printed
*/
p += i;
if (p[0] == '\n')
{
p++;
KdbNumberOfColsPrinted = 0;
}
else
{
ASSERT(p[0] == '\0');
KdbNumberOfColsPrinted += i;
}
KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
}
}
/*!\brief Appends a command to the command history
*
* \param Command Pointer to the command to append to the history.
*/
static VOID
KdbpCommandHistoryAppend(
IN PCHAR Command)
{
ULONG Length1 = strlen(Command) + 1;
ULONG Length2 = 0;
INT i;
PCHAR Buffer;
ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer));
if (Length1 <= 1 ||
(KdbCommandHistory[KdbCommandHistoryIndex] &&
strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0))
{
return;
}
/* Calculate Length1 and Length2 */
Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex;
KdbCommandHistoryBufferIndex += Length1;
if (KdbCommandHistoryBufferIndex >= (LONG)RTL_NUMBER_OF(KdbCommandHistoryBuffer))
{
KdbCommandHistoryBufferIndex -= RTL_NUMBER_OF(KdbCommandHistoryBuffer);
Length2 = KdbCommandHistoryBufferIndex;
Length1 -= Length2;
}
/* Remove previous commands until there is enough space to append the new command */
for (i = KdbCommandHistoryIndex; KdbCommandHistory[i];)
{
if ((Length2 > 0 &&
(KdbCommandHistory[i] >= Buffer ||
KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))) ||
(Length2 <= 0 &&
(KdbCommandHistory[i] >= Buffer &&
KdbCommandHistory[i] < (KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex))))
{
KdbCommandHistory[i] = NULL;
}
i--;
if (i < 0)
i = RTL_NUMBER_OF(KdbCommandHistory) - 1;
if (i == KdbCommandHistoryIndex)
break;
}
/* Make sure the new command history entry is free */
KdbCommandHistoryIndex++;
KdbCommandHistoryIndex %= RTL_NUMBER_OF(KdbCommandHistory);
if (KdbCommandHistory[KdbCommandHistoryIndex])
{
KdbCommandHistory[KdbCommandHistoryIndex] = NULL;
}
/* Append command */
KdbCommandHistory[KdbCommandHistoryIndex] = Buffer;
ASSERT((KdbCommandHistory[KdbCommandHistoryIndex] + Length1) <= KdbCommandHistoryBuffer + RTL_NUMBER_OF(KdbCommandHistoryBuffer));
memcpy(KdbCommandHistory[KdbCommandHistoryIndex], Command, Length1);
if (Length2 > 0)
{
memcpy(KdbCommandHistoryBuffer, Command + Length1, Length2);
}
}
/*!\brief Reads a line of user-input.
*
* \param Buffer Buffer to store the input into. Trailing newlines are removed.
* \param Size Size of \a Buffer.
*
* \note Accepts only \n newlines, \r is ignored.
*/
static VOID
KdbpReadCommand(
OUT PCHAR Buffer,
IN ULONG Size)
{
CHAR Key;
PCHAR Orig = Buffer;
ULONG ScanCode = 0;
BOOLEAN EchoOn;
static CHAR LastCommand[1024];
static CHAR NextKey = '\0';
INT CmdHistIndex = -1;
INT i;
EchoOn = !((KdbDebugState & KD_DEBUG_KDNOECHO) != 0);
for (;;)
{
if (KdbDebugState & KD_DEBUG_KDSERIAL)
{
Key = (NextKey == '\0') ? KdbpGetCharSerial() : NextKey;
NextKey = '\0';
ScanCode = 0;
if (Key == KEY_ESC) /* ESC */
{
Key = KdbpGetCharSerial();
if (Key == '[')
{
Key = KdbpGetCharSerial();
switch (Key)
{
case 'A':
ScanCode = KEY_SCAN_UP;
break;
case 'B':
ScanCode = KEY_SCAN_DOWN;
break;
case 'C':
break;
case 'D':
break;
}
}
}
}
else
{
ScanCode = 0;
Key = (NextKey == '\0') ? KdbpGetCharKeyboard(&ScanCode) : NextKey;
NextKey = '\0';
}
if ((ULONG)(Buffer - Orig) >= (Size - 1))
{
/* Buffer is full, accept only newlines */
if (Key != '\n')
continue;
}
if (Key == '\r')
{
/* Read the next char - this is to throw away a \n which most clients should
* send after \r.
*/
KeStallExecutionProcessor(100000);
if (KdbDebugState & KD_DEBUG_KDSERIAL)
NextKey = KdbpTryGetCharSerial(5);
else
NextKey = KdbpTryGetCharKeyboard(&ScanCode, 5);
if (NextKey == '\n' || NextKey == -1) /* \n or no response at all */
NextKey = '\0';
KdbpPrint("\n");
/*
* Repeat the last command if the user presses enter. Reduces the
* risk of RSI when single-stepping.
*/
if (Buffer != Orig)
{
KdbRepeatLastCommand = TRUE;
*Buffer = '\0';
RtlStringCbCopyA(LastCommand, sizeof(LastCommand), Orig);
}
else if (KdbRepeatLastCommand)
RtlStringCbCopyA(Buffer, Size, LastCommand);
else
*Buffer = '\0';
return;
}
else if (Key == KEY_BS || Key == KEY_DEL)
{
if (Buffer > Orig)
{
Buffer--;
*Buffer = 0;
if (EchoOn)
KdbpPrint("%c %c", KEY_BS, KEY_BS);
else
KdbpPrint(" %c", KEY_BS);
}
}
else if (ScanCode == KEY_SCAN_UP)
{
BOOLEAN Print = TRUE;
if (CmdHistIndex < 0)
{
CmdHistIndex = KdbCommandHistoryIndex;
}
else
{
i = CmdHistIndex - 1;
if (i < 0)
CmdHistIndex = RTL_NUMBER_OF(KdbCommandHistory) - 1;
if (KdbCommandHistory[i] && i != KdbCommandHistoryIndex)
CmdHistIndex = i;
else
Print = FALSE;
}
if (Print && KdbCommandHistory[CmdHistIndex])
{
while (Buffer > Orig)
{
Buffer--;
*Buffer = 0;
if (EchoOn)
KdbpPrint("%c %c", KEY_BS, KEY_BS);
else
KdbpPrint(" %c", KEY_BS);
}
i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
Orig[i] = '\0';
Buffer = Orig + i;
KdbpPrint("%s", Orig);
}
}
else if (ScanCode == KEY_SCAN_DOWN)
{
if (CmdHistIndex > 0 && CmdHistIndex != KdbCommandHistoryIndex)
{
i = CmdHistIndex + 1;
if (i >= (INT)RTL_NUMBER_OF(KdbCommandHistory))
i = 0;
if (KdbCommandHistory[i])
{
CmdHistIndex = i;
while (Buffer > Orig)
{
Buffer--;
*Buffer = 0;
if (EchoOn)
KdbpPrint("%c %c", KEY_BS, KEY_BS);
else
KdbpPrint(" %c", KEY_BS);
}
i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1);
memcpy(Orig, KdbCommandHistory[CmdHistIndex], i);
Orig[i] = '\0';
Buffer = Orig + i;
KdbpPrint("%s", Orig);
}
}
}
else
{
if (EchoOn)
KdbpPrint("%c", Key);
*Buffer = Key;
Buffer++;
}
}
}
BOOLEAN
NTAPI
KdbRegisterCliCallback(
PVOID Callback,
BOOLEAN Deregister)
{
ULONG i;
/* Loop all entries */
for (i = 0; i < _countof(KdbCliCallbacks); i++)
{
/* Check if deregistering was requested */
if (Deregister)
{
/* Check if this entry is the one that was registered */
if (KdbCliCallbacks[i] == Callback)
{
/* Delete it and report success */
KdbCliCallbacks[i] = NULL;
return TRUE;
}
}
else
{
/* Check if this entry is free */
if (KdbCliCallbacks[i] == NULL)
{
/* Set it and and report success */
KdbCliCallbacks[i] = Callback;
return TRUE;
}
}
}
/* Unsuccessful */
return FALSE;
}
/*! \brief Invokes registered CLI callbacks until one of them handled the
* Command.
*
* \param Command - Command line to parse and execute if possible.
* \param Argc - Number of arguments in Argv
* \param Argv - Array of strings, each of them containing one argument.
*
* \return TRUE, if the command was handled, FALSE if it was not handled.
*/
static
BOOLEAN
KdbpInvokeCliCallbacks(
IN PCHAR Command,
IN ULONG Argc,
IN PCH Argv[])
{
ULONG i;
/* Loop all entries */
for (i = 0; i < _countof(KdbCliCallbacks); i++)
{
/* Check if this entry is registered */
if (KdbCliCallbacks[i])
{
/* Invoke the callback and check if it handled the command */
if (KdbCliCallbacks[i](Command, Argc, Argv))
{
return TRUE;
}
}
}
/* None of the callbacks handled the command */
return FALSE;
}
/*!\brief Parses command line and executes command if found
*
* \param Command Command line to parse and execute if possible.
*
* \retval TRUE Don't continue execution.
* \retval FALSE Continue execution (leave KDB)
*/
static BOOLEAN
KdbpDoCommand(
IN PCHAR Command)
{
ULONG i;
PCHAR p;
ULONG Argc;
// FIXME: for what do we need a 1024 characters command line and 256 tokens?
static PCH Argv[256];
static CHAR OrigCommand[1024];
RtlStringCbCopyA(OrigCommand, sizeof(OrigCommand), Command);
Argc = 0;
p = Command;
for (;;)
{
while (*p == '\t' || *p == ' ')
p++;
if (*p == '\0')
break;
i = strcspn(p, "\t ");
Argv[Argc++] = p;
p += i;
if (*p == '\0')
break;
*p = '\0';
p++;
}
if (Argc < 1)
return TRUE;
for (i = 0; i < RTL_NUMBER_OF(KdbDebuggerCommands); i++)
{
if (!KdbDebuggerCommands[i].Name)
continue;
if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0)
{
return KdbDebuggerCommands[i].Fn(Argc, Argv);
}
}
/* Now invoke the registered callbacks */
if (KdbpInvokeCliCallbacks(Command, Argc, Argv))
{
return TRUE;
}
KdbpPrint("Command '%s' is unknown.\n", OrigCommand);
return TRUE;
}
/*!\brief KDB Main Loop.
*
* \param EnteredOnSingleStep TRUE if KDB was entered on single step.
*/
VOID
KdbpCliMainLoop(
IN BOOLEAN EnteredOnSingleStep)
{
static CHAR Command[1024];
BOOLEAN Continue;
if (EnteredOnSingleStep)
{
if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip, &KdbCurrentTrapFrame->Tf))
{
KdbpPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip);
}
KdbpPrint(": ");
if (KdbpDisassemble(KdbCurrentTrapFrame->Tf.Eip, KdbUseIntelSyntax) < 0)
{
KdbpPrint("<INVALID>");
}
KdbpPrint("\n");
}
/* Flush the input buffer */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
{
while (KdbpTryGetCharSerial(1) != -1);
}
else
{
ULONG ScanCode;
while (KdbpTryGetCharKeyboard(&ScanCode, 1) != -1);
}
/* Main loop */
do
{
/* Reset the number of rows/cols printed */
KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
/* Print the prompt */
KdbpPrint("kdb:> ");
/* Read a command and remember it */
KdbpReadCommand(Command, sizeof (Command));
KdbpCommandHistoryAppend(Command);
/* Reset the number of rows/cols printed and output aborted state */
KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
KdbOutputAborted = FALSE;
/* Call the command */
Continue = KdbpDoCommand(Command);
KdbOutputAborted = FALSE;
}
while (Continue);
}
/*!\brief Called when a module is loaded.
*
* \param Name Filename of the module which was loaded.
*/
VOID
KdbpCliModuleLoaded(
IN PUNICODE_STRING Name)
{
if (!KdbBreakOnModuleLoad)
return;
KdbpPrint("Module %wZ loaded.\n", Name);
DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
}
/*!\brief This function is called by KdbEnterDebuggerException...
*
* Used to interpret the init file in a context with a trapframe setup
* (KdbpCliInit call KdbEnter which will call KdbEnterDebuggerException which will
* call this function if KdbInitFileBuffer is not NULL.
*/
VOID
KdbpCliInterpretInitFile(VOID)
{
PCHAR p1, p2;
INT i;
CHAR c;
/* Execute the commands in the init file */
DPRINT("KDB: Executing KDBinit file...\n");
p1 = KdbInitFileBuffer;
while (p1[0] != '\0')
{
i = strcspn(p1, "\r\n");
if (i > 0)
{
c = p1[i];
p1[i] = '\0';
/* Look for "break" command and comments */
p2 = p1;
while (isspace(p2[0]))
p2++;
if (strncmp(p2, "break", sizeof("break")-1) == 0 &&
(p2[sizeof("break")-1] == '\0' || isspace(p2[sizeof("break")-1])))
{
/* break into the debugger */
KdbpCliMainLoop(FALSE);
}
else if (p2[0] != '#' && p2[0] != '\0') /* Ignore empty lines and comments */
{
KdbpDoCommand(p1);
}
p1[i] = c;
}
p1 += i;
while (p1[0] == '\r' || p1[0] == '\n')
p1++;
}
DPRINT("KDB: KDBinit executed\n");
}
/*!\brief Called when KDB is initialized
*
* Reads the KDBinit file from the SystemRoot\System32\drivers\etc directory and executes it.
*/
VOID
KdbpCliInit(VOID)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING FileName;
IO_STATUS_BLOCK Iosb;
FILE_STANDARD_INFORMATION FileStdInfo;
HANDLE hFile = NULL;
INT FileSize;
PCHAR FileBuffer;
ULONG OldEflags;
/* Initialize the object attributes */
RtlInitUnicodeString(&FileName, L"\\SystemRoot\\System32\\drivers\\etc\\KDBinit");
InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL);
/* Open the file */
Status = ZwOpenFile(&hFile, FILE_READ_DATA | SYNCHRONIZE,
&ObjectAttributes, &Iosb, 0,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
FILE_NO_INTERMEDIATE_BUFFERING);
if (!NT_SUCCESS(Status))
{
DPRINT("Could not open \\SystemRoot\\System32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
return;
}
/* Get the size of the file */
Status = ZwQueryInformationFile(hFile, &Iosb, &FileStdInfo, sizeof (FileStdInfo),
FileStandardInformation);
if (!NT_SUCCESS(Status))
{
ZwClose(hFile);
DPRINT("Could not query size of \\SystemRoot\\System32\\drivers\\etc\\KDBinit (Status 0x%x)", Status);
return;
}
FileSize = FileStdInfo.EndOfFile.u.LowPart;
/* Allocate memory for the file */
FileBuffer = ExAllocatePool(PagedPool, FileSize + 1); /* add 1 byte for terminating '\0' */
if (!FileBuffer)
{
ZwClose(hFile);
DPRINT("Could not allocate %d bytes for KDBinit file\n", FileSize);
return;
}
/* Load file into memory */
Status = ZwReadFile(hFile, NULL, NULL, NULL, &Iosb, FileBuffer, FileSize, NULL, NULL);
ZwClose(hFile);
if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
{
ExFreePool(FileBuffer);
DPRINT("Could not read KDBinit file into memory (Status 0x%lx)\n", Status);
return;
}
FileSize = min(FileSize, (INT)Iosb.Information);
FileBuffer[FileSize] = '\0';
/* Enter critical section */
OldEflags = __readeflags();
_disable();
/* Interpret the init file... */
KdbInitFileBuffer = FileBuffer;
KdbEnter();
KdbInitFileBuffer = NULL;
/* Leave critical section */
__writeeflags(OldEflags);
ExFreePool(FileBuffer);
}
VOID
NTAPI
KdpSerialDebugPrint(
LPSTR Message,
ULONG Length
);
STRING KdpPromptString = RTL_CONSTANT_STRING("kdb:> ");
extern KSPIN_LOCK KdpSerialSpinLock;
ULONG
NTAPI
KdpPrompt(
_In_reads_bytes_(InStringLength) PCHAR UnsafeInString,
_In_ USHORT InStringLength,
_Out_writes_bytes_(OutStringLength) PCHAR UnsafeOutString,
_In_ USHORT OutStringLength,
_In_ KPROCESSOR_MODE PreviousMode)
{
USHORT i;
CHAR Response;
ULONG DummyScanCode;
KIRQL OldIrql;
PCHAR InString;
PCHAR OutString;
CHAR InStringBuffer[512];
CHAR OutStringBuffer[512];
/* Normalize the lengths */
InStringLength = min(InStringLength,
sizeof(InStringBuffer));
OutStringLength = min(OutStringLength,
sizeof(OutStringBuffer));
/* Check if we need to verify the string */
if (PreviousMode != KernelMode)
{
/* Handle user-mode buffers safely */
_SEH2_TRY
{
/* Probe the prompt */
ProbeForRead(UnsafeInString,
InStringLength,
1);
/* Capture prompt */
InString = InStringBuffer;
RtlCopyMemory(InString,
UnsafeInString,
InStringLength);
/* Probe and make room for response */
ProbeForWrite(UnsafeOutString,
OutStringLength,
1);
OutString = OutStringBuffer;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Bad string pointer, bail out */
_SEH2_YIELD(return 0);
}
_SEH2_END;
}
else
{
InString = UnsafeInString;
OutString = UnsafeOutString;
}
/* Acquire the printing spinlock without waiting at raised IRQL */
while (TRUE)
{
/* Wait when the spinlock becomes available */
while (!KeTestSpinLock(&KdpSerialSpinLock));
/* Spinlock was free, raise IRQL */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Try to get the spinlock */
if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock))
break;
/* Someone else got the spinlock, lower IRQL back */
KeLowerIrql(OldIrql);
}
/* Loop the string to send */
for (i = 0; i < InStringLength; i++)
{
/* Print it to serial */
KdPortPutByteEx(&SerialPortInfo, *(PCHAR)(InString + i));
}
/* Print a new line for log neatness */
KdPortPutByteEx(&SerialPortInfo, '\r');
KdPortPutByteEx(&SerialPortInfo, '\n');
/* Print the kdb prompt */
for (i = 0; i < KdpPromptString.Length; i++)
{
/* Print it to serial */
KdPortPutByteEx(&SerialPortInfo,
*(KdpPromptString.Buffer + i));
}
if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
KbdDisableMouse();
/* Loop the whole string */
for (i = 0; i < OutStringLength; i++)
{
/* Check if this is serial debugging mode */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
{
/* Get the character from serial */
do
{
Response = KdbpTryGetCharSerial(MAXULONG);
} while (Response == -1);
}
else
{
/* Get the response from the keyboard */
do
{
Response = KdbpTryGetCharKeyboard(&DummyScanCode, MAXULONG);
} while (Response == -1);
}
/* Check for return */
if (Response == '\r')
{
/*
* We might need to discard the next '\n'.
* Wait a bit to make sure we receive it.
*/
KeStallExecutionProcessor(100000);
/* Check the mode */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
{
/* Read and discard the next character, if any */
KdbpTryGetCharSerial(5);
}
else
{
/* Read and discard the next character, if any */
KdbpTryGetCharKeyboard(&DummyScanCode, 5);
}
/*
* Null terminate the output string -- documentation states that
* DbgPrompt does not null terminate, but it does
*/
*(PCHAR)(OutString + i) = 0;
break;
}
/* Write it back and print it to the log */
*(PCHAR)(OutString + i) = Response;
KdPortPutByteEx(&SerialPortInfo, Response);
}
if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
KbdEnableMouse();
/* Print a new line */
KdPortPutByteEx(&SerialPortInfo, '\r');
KdPortPutByteEx(&SerialPortInfo, '\n');
/* Release spinlock */
KiReleaseSpinLock(&KdpSerialSpinLock);
/* Lower IRQL back */
KeLowerIrql(OldIrql);
/* Copy back response if required */
if (PreviousMode != KernelMode)
{
_SEH2_TRY
{
/* Safely copy back response to user mode */
RtlCopyMemory(UnsafeOutString,
OutString,
i);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* String became invalid after we exited, fail */
_SEH2_YIELD(return 0);
}
_SEH2_END;
}
/* Return the length */
return i;
}