diff --git a/reactos/Makefile b/reactos/Makefile index a349d6476b9..cb4902670cb 100644 --- a/reactos/Makefile +++ b/reactos/Makefile @@ -216,6 +216,8 @@ bootcd_install_before: $(CP) media/nls/l_intl.nls $(BOOTCD_DIR)/reactos/l_intl.nls $(HALFVERBOSEECHO) [COPY] media/drivers/etc/services to $(BOOTCD_DIR)/reactos/services $(CP) media/drivers/etc/services $(BOOTCD_DIR)/reactos/services + $(HALFVERBOSEECHO) [COPY] media/drivers/etc/KDB.init to $(BOOTCD_DIR)/reactos/KDB.init + $(CP) media/drivers/etc/KDB.init $(BOOTCD_DIR)/reactos/KDB.init bootcd_basic: bootcd_directory_layout bootcd_bootstrap_files bootcd_install_before @@ -1043,6 +1045,8 @@ install_before: $(CP) media/nls/l_intl.nls $(INSTALL_DIR)/system32/casemap.nls $(HALFVERBOSEECHO) [INSTALL] media/drivers/etc/services to $(INSTALL_DIR)/system32/drivers/etc/services $(CP) media/drivers/etc/services $(INSTALL_DIR)/system32/drivers/etc/services + $(HALFVERBOSEECHO) [INSTALL] media/drivers/etc/KDB.init to $(INSTALL_DIR)/system32/drivers/etc/KDB.init + $(CP) media/drivers/etc/KDB.init $(INSTALL_DIR)/system32/drivers/etc/KDB.init .PHONY: install_clean install_dirs install_before diff --git a/reactos/drivers/input/keyboard/keyboard.c b/reactos/drivers/input/keyboard/keyboard.c index d87d14201c5..e30c58b83fc 100644 --- a/reactos/drivers/input/keyboard/keyboard.c +++ b/reactos/drivers/input/keyboard/keyboard.c @@ -38,6 +38,7 @@ static BYTE capsDown,numDown,scrollDown; static DWORD ctrlKeyState; static PKINTERRUPT KbdInterrupt; static KDPC KbdDpc; +static PIO_WORKITEM KbdWorkItem = NULL; static BOOLEAN AlreadyOpened = FALSE; /* @@ -407,6 +408,24 @@ static WORD ScanToVirtual(BYTE scanCode) } +/* + * Debug request handler + */ + +static VOID STDCALL +KbdWorkItemRoutine(IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context) +{ + LONG Debug; + + Debug = InterlockedExchange(&DoSystemDebug, -1); + if (Debug != -1) + { + KdSystemDebugControl(Debug); + } +} + + /* * Keyboard IRQ handler */ @@ -419,14 +438,21 @@ KbdDpcRoutine(PKDPC Dpc, { PIRP Irp = (PIRP)SystemArgument2; PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)SystemArgument1; - + if (SystemArgument1 == NULL && DoSystemDebug != -1) { - KdSystemDebugControl(DoSystemDebug); - DoSystemDebug = -1; + if (KbdWorkItem != NULL) + { + IoQueueWorkItem(KbdWorkItem, (PIO_WORKITEM_ROUTINE)KbdWorkItemRoutine, DelayedWorkQueue, NULL); + } + else + { + KdSystemDebugControl(DoSystemDebug); + DoSystemDebug = -1; + } return; } - + CHECKPOINT; DPRINT("KbdDpcRoutine(DeviceObject %x, Irp %x)\n", DeviceObject,Irp); @@ -436,6 +462,7 @@ KbdDpcRoutine(PKDPC Dpc, IoStartNextPacket(DeviceObject,FALSE); } + static BOOLEAN STDCALL KeyboardHandler(PKINTERRUPT Interrupt, PVOID Context) @@ -538,7 +565,7 @@ KeyboardHandler(PKINTERRUPT Interrupt, else if (InSysRq == TRUE && ScanToVirtual(thisKey) >= VK_A && ScanToVirtual(thisKey) <= VK_Z && isDown) { - DoSystemDebug = ScanToVirtual(thisKey) - VK_A; + InterlockedExchange(&DoSystemDebug, ScanToVirtual(thisKey) - VK_A); KeInsertQueueDpc(&KbdDpc, NULL, NULL); return(TRUE); } @@ -659,6 +686,11 @@ static int InitializeKeyboard(PDEVICE_OBJECT DeviceObject) KbdClearInput(); KeyboardConnectInterrupt(DeviceObject); KeInitializeDpc(&KbdDpc,KbdDpcRoutine,NULL); + KbdWorkItem = IoAllocateWorkItem(DeviceObject); + if (KbdWorkItem == NULL) + { + DPRINT("Warning: Couldn't allocate work item!\n"); + } return 0; } diff --git a/reactos/media/drivers/etc/KDB.init b/reactos/media/drivers/etc/KDB.init new file mode 100644 index 00000000000..53a68e1e23a --- /dev/null +++ b/reactos/media/drivers/etc/KDB.init @@ -0,0 +1,15 @@ +# Example KDB.init file +# +# The disassembly flavor is set to "intel" (default is "at&t") and the +# + +# Set the disassembly flavor to "intel" (default is "at&t") +set syntax intel + +# Change the condition to enter KDB on INT3 to "always" (default is "kmode") +set condition INT3 first always + +# This is a special command available only in the KDB.init file - it breaks into +# KDB when it is interpreting the init file at startup. +#break + diff --git a/reactos/ntoskrnl/Makefile b/reactos/ntoskrnl/Makefile index 9dc41bf8ef9..f9987f39972 100644 --- a/reactos/ntoskrnl/Makefile +++ b/reactos/ntoskrnl/Makefile @@ -25,14 +25,10 @@ LINKER_SCRIPT := ntoskrnl.lnk STRIP_FLAGS := -Wl,-s ifeq ($(KDBG), 1) -OBJECTS_KDBG := dbg/kdb.o dbg/kdb_serial.o dbg/kdb_keyboard.o dbg/rdebug.o \ - dbg/i386/kdb_help.o \ - ../dk/w32/lib/libkjs.a dbg/i386/i386-dis.o -CFLAGS_KDBG := -I../lib/kjs/include +OBJECTS_KDBG := dbg/kdb.o dbg/kdb_cli.o dbg/kdb_expr.o dbg/kdb_keyboard.o \ + dbg/kdb_serial.o dbg/kdb_string.o dbg/rdebug.o dbg/i386/kdb_help.o \ + dbg/i386/i386-dis.o dbg/i386/longjmp.o dbg/i386/setjmp.o preall: all - -../dk/w32/lib/libkjs.a: - $(MAKE) -C ../lib/kjs else OBJECTS_KDBG := endif diff --git a/reactos/ntoskrnl/dbg/i386/i386-dis.c b/reactos/ntoskrnl/dbg/i386/i386-dis.c index 66bb380e896..95c5f4c619a 100644 --- a/reactos/ntoskrnl/dbg/i386/i386-dis.c +++ b/reactos/ntoskrnl/dbg/i386/i386-dis.c @@ -1,4 +1,4 @@ -/* $Id:$ +/* $Id$ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -52,10 +52,10 @@ extern long MmSafeCopyFromUser(void *Dest, void *Src, unsigned long NumberOfByte int -print_insn_i386_att (bfd_vma pc, struct disassemble_info *info); +print_insn_i386 (bfd_vma pc, struct disassemble_info *info); int -KdbPrintDisasm(void* Ignored, const char* fmt, ...) +KdbpPrintDisasm(void* Ignored, const char* fmt, ...) { va_list ap; static char buffer[256]; @@ -69,46 +69,46 @@ KdbPrintDisasm(void* Ignored, const char* fmt, ...) } int -KdbNopPrintDisasm(void* Ignored, const char* fmt, ...) +KdbpNopPrintDisasm(void* Ignored, const char* fmt, ...) { return(0); } int static -KdbReadMemory(unsigned int Addr, unsigned char* Data, unsigned int Length, - struct disassemble_info * Ignored) +KdbpReadMemory(unsigned int Addr, unsigned char* Data, unsigned int Length, + struct disassemble_info * Ignored) { return KdbpSafeReadMemory(Data, (void *)Addr, Length); /* 0 means no error */ } void static -KdbMemoryError(int Status, unsigned int Addr, - struct disassemble_info * Ignored) +KdbpMemoryError(int Status, unsigned int Addr, + struct disassemble_info * Ignored) { } void static -KdbPrintAddressInCode(unsigned int Addr, struct disassemble_info * Ignored) +KdbpPrintAddressInCode(unsigned int Addr, struct disassemble_info * Ignored) { if (!KdbSymPrintAddress((void*)Addr)) { - DbgPrint("<0x%X>", Addr); + DbgPrint("<%08x>", Addr); } } void static -KdbNopPrintAddress(unsigned int Addr, struct disassemble_info * Ignored) +KdbpNopPrintAddress(unsigned int Addr, struct disassemble_info * Ignored) { } #include "dis-asm.h" long -KdbGetInstLength(unsigned int Address) +KdbpGetInstLength(unsigned int Address) { disassemble_info info; - info.fprintf_func = KdbNopPrintDisasm; + info.fprintf_func = KdbpNopPrintDisasm; info.stream = NULL; info.application_data = NULL; info.flavour = bfd_target_unknown_flavour; @@ -116,9 +116,9 @@ KdbGetInstLength(unsigned int Address) info.mach = bfd_mach_i386_i386; info.insn_sets = 0; info.flags = 0; - info.read_memory_func = KdbReadMemory; - info.memory_error_func = KdbMemoryError; - info.print_address_func = KdbNopPrintAddress; + info.read_memory_func = KdbpReadMemory; + info.memory_error_func = KdbpMemoryError; + info.print_address_func = KdbpNopPrintAddress; info.symbol_at_address_func = NULL; info.buffer = NULL; info.buffer_vma = info.buffer_length = 0; @@ -126,25 +126,25 @@ KdbGetInstLength(unsigned int Address) info.display_endian = BIG_ENDIAN_LITTLE; info.disassembler_options = NULL; - return(print_insn_i386_att(Address, &info)); + return(print_insn_i386(Address, &info)); } long -KdbDisassemble(unsigned int Address) +KdbpDisassemble(unsigned int Address, unsigned long IntelSyntax) { disassemble_info info; - info.fprintf_func = KdbPrintDisasm; + info.fprintf_func = KdbpPrintDisasm; info.stream = NULL; info.application_data = NULL; info.flavour = bfd_target_unknown_flavour; info.arch = bfd_arch_i386; - info.mach = bfd_mach_i386_i386; + info.mach = IntelSyntax ? bfd_mach_i386_i386_intel_syntax : bfd_mach_i386_i386; info.insn_sets = 0; info.flags = 0; - info.read_memory_func = KdbReadMemory; - info.memory_error_func = KdbMemoryError; - info.print_address_func = KdbPrintAddressInCode; + info.read_memory_func = KdbpReadMemory; + info.memory_error_func = KdbpMemoryError; + info.print_address_func = KdbpPrintAddressInCode; info.symbol_at_address_func = NULL; info.buffer = NULL; info.buffer_vma = info.buffer_length = 0; @@ -152,7 +152,7 @@ KdbDisassemble(unsigned int Address) info.display_endian = BIG_ENDIAN_LITTLE; info.disassembler_options = NULL; - return(print_insn_i386_att(Address, &info)); + return(print_insn_i386(Address, &info)); } /* Print i386 instructions for GDB, the GNU debugger. @@ -2113,7 +2113,7 @@ print_insn (pc, info) #else mode_64bit = 0; priv.orig_sizeflag = AFLAG | DFLAG; - intel_syntax = 0; + /*intel_syntax = 0;*/ #endif if (intel_syntax) diff --git a/reactos/ntoskrnl/dbg/i386/kdb_help.S b/reactos/ntoskrnl/dbg/i386/kdb_help.S index 2da6570fb36..cda895a42d9 100644 --- a/reactos/ntoskrnl/dbg/i386/kdb_help.S +++ b/reactos/ntoskrnl/dbg/i386/kdb_help.S @@ -1,29 +1,19 @@ #include #include - .data -_KdbEipTemp: - .int 0 +.text - .text -.globl _KdbEnter +.globl _KdbEnter _KdbEnter: - /* - * Record when we are inside the debugger. - */ - incl _KdbEntryCount - - /* - * Save the callers eip. - */ - popl _KdbEipTemp - /* * Set up a trap frame */ + /* Ss - space already reserved by return EIP */ + pushl %esp /* Esp */ pushfl /* Eflags */ pushl %cs /* Cs */ - pushl _KdbEipTemp /* Eip */ + pushl 12(%esp) /* Eip */ + movl %ss, 16(%esp) /* Save Ss */ pushl $0 /* ErrorCode */ pushl %ebp /* Ebp */ pushl %ebx /* Ebx */ @@ -56,34 +46,35 @@ _KdbEnter: pushl $0 /* TempEip */ pushl $0 /* TempCs */ pushl $0 /* DebugPointer */ - pushl $0 /* DebugArgMark */ - pushl _KdbEipTemp /* DebugEip */ + pushl $3 /* DebugArgMark (Exception number) */ + pushl 0x60(%esp) /* DebugEip */ pushl %ebp /* DebugEbp */ - /* - * Push a pointer to the trap frame - */ - pushl %esp - /* * Call KDB */ - call _KdbInternalEnter + movl %esp, %eax + pushl $1 /* FirstChance */ + pushl %eax /* Push a pointer to the trap frame */ + pushl $0 /* Context */ + pushl $0 /* PreviousMode (KernelMode) */ + pushl $0 /* ExceptionRecord */ + call _KdbEnterDebuggerException /* - * Pop the argument + * Pop the arguments and unused portions of the trap frame: + * DebugEbp + * DebugEip + * DebugArgMark + * DebugPointer + * TempCs + * TempEip */ - popl %eax + addl $(11*4), %esp /* - * Ignore unused portions of the trap frame. + * Restore/update debugging registers. */ - popl %eax /* DebugEbp */ - popl %eax /* DebugEip */ - popl %eax /* DebugArgMark */ - popl %eax /* DebugPointer */ - popl %eax /* TempCs */ - popl %eax /* TempEip */ popl %eax /* Dr0 */ movl %eax, %dr0 popl %eax /* Dr1 */ @@ -113,13 +104,17 @@ _KdbEnter: popl %edi /* Edi */ popl %esi /* Esi */ popl %ebx /* Ebx */ - popl %ebp /* Ebp */ - addl $4, %esp /* ErrorCode */ - /* - * Record when we are in the debugger. - */ - decl _KdbEntryCount + /* Remove SS:ESP from the stack */ + movl 16(%esp), %ebp + movl %ebp, 24(%esp) + movl 12(%esp), %ebp + movl %ebp, 20(%esp) + movl 8(%esp), %ebp + movl %ebp, 16(%esp) + + popl %ebp /* Ebp */ + addl $12, %esp /* ErrorCode and SS:ESP */ /* * Return to the caller. @@ -127,5 +122,26 @@ _KdbEnter: iret +.globl _KdbpStackSwitchAndCall@8 +_KdbpStackSwitchAndCall@8: + pushl %ebp + movl %esp, %ebp + + movl 0x8(%esp), %eax /* New stack */ + movl 0xC(%esp), %ecx /* Function to call */ + movl %esp, %edx /* Old stack */ + + /* Switch stack */ + movl %eax, %esp + pushl %edx + + /* Call function */ + call *%ecx + + /* Switch back to old stack */ + popl %esp + + /* Return */ + popl %ebp + ret $8 - diff --git a/reactos/ntoskrnl/dbg/i386/longjmp.S b/reactos/ntoskrnl/dbg/i386/longjmp.S new file mode 100644 index 00000000000..1edbaaf2ebb --- /dev/null +++ b/reactos/ntoskrnl/dbg/i386/longjmp.S @@ -0,0 +1,70 @@ + .file "longjmp.S" +/* + * Copyright (C) 1998, 1999, Jonathan S. Shapiro. + * + * This file is part of the EROS Operating System. + * + * 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, + * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + /* + * typedef struct { + * unsigned long ebx, esi, edi; + * unsigned long ebp; + * unsigned long sp; + * unsigned long pc; + * } jmp_buf[1]; + */ + + /* + * On entry, the stack to longjmp looks like: + * + * value + * ptr to jmp_buf + * return PC + */ + +.globl _longjmp +_longjmp: + pushl %ebp + movl %esp,%ebp + + movl 8(%ebp),%ecx /* address of jmp_buf to ecx */ + movl 12(%ebp),%eax /* return value to %eax */ + testl %eax,%eax + jne 1f + incl %eax /* return 1 if handed 0 */ + +1: + movl (%ecx),%ebx /* restore %ebx */ + movl 4(%ecx),%esi /* restore %esi */ + movl 8(%ecx),%edi /* restore %edi */ + + /* + * From this instant on we are not running in a valid frame + */ + + movl 12(%ecx),%ebp /* restore %ebp */ + movl 16(%ecx),%esp /* restore %esp */ + /* movl 20(%ecx),%eax return PC */ + + /* + * Since we are abandoning the stack in any case, + * there isn't much point in doing the usual return + * discipline. + */ + + jmpl *20(%ecx) + diff --git a/reactos/ntoskrnl/dbg/i386/setjmp.S b/reactos/ntoskrnl/dbg/i386/setjmp.S new file mode 100644 index 00000000000..d53e0592f38 --- /dev/null +++ b/reactos/ntoskrnl/dbg/i386/setjmp.S @@ -0,0 +1,59 @@ + .file "setjmp.S" +/* + * Copyright (C) 1998, 1999, Jonathan S. Shapiro. + * + * This file is part of the EROS Operating System. + * + * 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, + * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* #include */ + + + /* + * typedef struct { + * unsigned long ebx, esi, edi; + * unsigned long ebp; + * unsigned long sp; + * unsigned long pc; + * } jmp_buf[1]; + */ + + /* + * On entry, the stack to setjmp looks like: + * + * ptr to jmp_buf + * return PC + */ +.globl _setjmp +_setjmp: + pushl %ebp + movl %esp,%ebp + + movl 0x8(%ebp),%eax /* address of jmp_buf to eax */ + movl %ebx,(%eax) /* save %ebx */ + movl %esi,4(%eax) /* save %esi */ + movl %edi,8(%eax) /* save %edi */ + leal 8(%ebp),%edx /* calling proc's esp, not ours! */ + movl %edx,16(%eax) + movl 4(%ebp), %edx /* save return PC */ + movl %edx,20(%eax) + movl 0(%ebp),%edx /* calling proc's ebp, not ours! */ + movl %edx,12(%eax) + + xorl %eax,%eax /* return 0 the first time */ + leave + ret $4 + diff --git a/reactos/ntoskrnl/dbg/kdb.c b/reactos/ntoskrnl/dbg/kdb.c index fa97e8879e9..910905cb12d 100644 --- a/reactos/ntoskrnl/dbg/kdb.c +++ b/reactos/ntoskrnl/dbg/kdb.c @@ -12,1786 +12,1495 @@ #include #include "kdb.h" -#include "kjs.h" #define NDEBUG #include /* TYPES *********************************************************************/ +/* DEFINES *******************************************************************/ + +#define KDB_STACK_SIZE (4096*3) +#define KDB_MAXIMUM_BREAKPOINT_COUNT 256 +#define KDB_MAXIMUM_HW_BREAKPOINT_COUNT 4 +#define KDB_MAXIMUM_SW_BREAKPOINT_COUNT 256 + +#define __STRING(x) #x +#define _STRING(x) __STRING(x) + /* GLOBALS *******************************************************************/ -#define BS 8 -#define DEL 127 +STATIC LONG KdbEntryCount = 0; +STATIC CHAR KdbStack[KDB_STACK_SIZE]; -BOOL KbdEchoOn = TRUE; +STATIC ULONG KdbBreakPointCount = 0; /* Number of used breakpoints in the array */ +STATIC KDB_BREAKPOINT KdbBreakPoints[KDB_MAXIMUM_BREAKPOINT_COUNT] = {{0}}; /* Breakpoint array */ +STATIC ULONG KdbSwBreakPointCount = 0; /* Number of enabled software breakpoints */ +STATIC ULONG KdbHwBreakPointCount = 0; /* Number of enabled hardware breakpoints */ +STATIC PKDB_BREAKPOINT KdbSwBreakPoints[KDB_MAXIMUM_SW_BREAKPOINT_COUNT]; /* Enabled software breakpoints, orderless */ +STATIC PKDB_BREAKPOINT KdbHwBreakPoints[KDB_MAXIMUM_HW_BREAKPOINT_COUNT]; /* Enabled hardware breakpoints, orderless */ +STATIC PKDB_BREAKPOINT KdbBreakPointToReenable = NULL; /* Set to a breakpoint struct when single stepping after + a software breakpoint was hit, to reenable it */ +LONG KdbLastBreakPointNr = -1; /* Index of the breakpoint which cause KDB to be entered */ +ULONG KdbNumSingleSteps = 0; /* How many single steps to do */ +BOOLEAN KdbSingleStepOver = FALSE; /* Whether to step over calls/reps. */ -typedef struct +STATIC BOOLEAN KdbEnteredOnSingleStep = FALSE; /* Set to true when KDB was entered because of single step */ +PEPROCESS KdbCurrentProcess = NULL; /* The current process context in which KDB runs */ +PEPROCESS KdbOriginalProcess = NULL; /* The process in whichs context KDB was intered */ +PETHREAD KdbCurrentThread = NULL; /* The current thread context in which KDB runs */ +PETHREAD KdbOriginalThread = NULL; /* The thread in whichs context KDB was entered */ +PKDB_KTRAP_FRAME KdbCurrentTrapFrame = NULL; /* Pointer to the current trapframe */ +STATIC KDB_KTRAP_FRAME KdbTrapFrame = { { 0 } }; /* The trapframe which was passed to KdbEnterDebuggerException */ +STATIC KDB_KTRAP_FRAME KdbThreadTrapFrame = { { 0 } }; /* The trapframe of the current thread (KdbCurrentThread) */ +STATIC KAPC_STATE KdbApcState; + +/* Array of conditions when to enter KDB */ +STATIC KDB_ENTER_CONDITION KdbEnterConditions[][2] = { - BOOLEAN Enabled; - BOOLEAN Temporary; - BOOLEAN Assigned; - ULONG Address; - UCHAR SavedInst; -} KDB_ACTIVE_BREAKPOINT; - -#define KDB_MAXIMUM_BREAKPOINT_COUNT (255) - -static ULONG KdbBreakPointCount = 0; -static KDB_ACTIVE_BREAKPOINT - KdbActiveBreakPoints[KDB_MAXIMUM_BREAKPOINT_COUNT]; - -static BOOLEAN KdbHandleUmode = FALSE; -static BOOLEAN KdbHandleHandled = FALSE; -static BOOLEAN KdbBreakOnModuleLoad = FALSE; - -static BOOLEAN KdbIgnoreNextSingleStep = FALSE; -static ULONG KdbLastSingleStepFrom = 0xFFFFFFFF; -static BOOLEAN KdbEnteredOnSingleStep = FALSE; - -ULONG KdbEntryCount = 0; - -int isalpha( int ); -VOID -PsDumpThreads(BOOLEAN System); -ULONG -DbgContCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgStopCondition(ULONG Aargc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgModuleLoadedAction(ULONG Aargc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgEchoToggle(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgCRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgBugCheckCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgBackTraceCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgAddrCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgXCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgScriptCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgThreadListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgProcessListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgProcessHelpCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgShowFilesCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgEnableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDisableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDisassemble(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgSetBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgDeleteBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgSetMemoryBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgStep(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgStepOver(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -ULONG -DbgFinish(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); - -struct -{ - PCH Name; - PCH Syntax; - PCH Help; - ULONG (*Fn)(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf); -} DebuggerCommands[] = { - {"cont", "cont", "Exit the debugger", DbgContCommand}, - {"echo", "echo", "Toggle serial echo", DbgEchoToggle}, - {"condition", "condition [all|umode|kmode]", "Kdbg enter condition", DbgStopCondition}, - {"module-loaded", "module-loaded [break|continue]", "Module-loaded action", DbgModuleLoadedAction}, - - {"regs", "regs", "Display general purpose registers", DbgRegsCommand}, - {"dregs", "dregs", "Display debug registers", DbgDRegsCommand}, - {"cregs", "cregs", "Display control registers", DbgCRegsCommand}, - {"bugcheck", "bugcheck", "Bugcheck the system", DbgBugCheckCommand}, - {"bt", "bt [*frame-address]|[thread-id]","Do a backtrace", DbgBackTraceCommand}, - {"addr", "addr
", "Displays symbol info", DbgAddrCommand}, - {"x", "x ", "Displays for ", DbgXCommand}, - {"plist", "plist", "Display processes in the system", DbgProcessListCommand}, - {"tlist", "tlist [sys]", "Display threads in the system", DbgThreadListCommand}, - {"sfiles", "sfiles", "Show files that print debug prints", DbgShowFilesCommand}, - {"efile", "efile ", "Enable debug prints from file", DbgEnableFileCommand}, - {"dfile", "dfile ", "Disable debug prints from file", DbgDisableFileCommand}, - {"js", "js", "Script mode", DbgScriptCommand}, - {"disasm", "disasm
", "Disables 10 instructions at
or " - "eip", DbgDisassemble}, - {"bp", "bp
", "Sets an int3 breakpoint at a given address", - DbgSetBreakPoint}, - {"bc", "bc ", "Deletes a breakpoint", - DbgDeleteBreakPoint}, - {"ba", "ba
", - "Sets a breakpoint using a debug register", DbgSetMemoryBreakPoint}, - {"t", "t", "Steps forward a single instructions", DbgStep}, - {"p", "p", "Steps forward a single instructions skipping calls", - DbgStepOver}, - {"finish", "finish", "Runs until the current function exits", DbgFinish}, - {"help", "help", "Display help screen", DbgProcessHelpCommand}, - {NULL, NULL, NULL} + /* First chance Last chance */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* Zero devide */ + { KdbEnterAlways, KdbDoNotEnter }, /* Debug trap */ + { KdbDoNotEnter, KdbEnterAlways }, /* NMI */ + { KdbEnterFromKmode, KdbDoNotEnter }, /* INT3 */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* Overflow */ + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, /* Invalid opcode */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* No math coprocessor fault */ + { KdbEnterAlways, KdbEnterAlways }, + { KdbEnterAlways, KdbEnterAlways }, + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, /* Stack fault */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* General protection fault */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* Page fault */ + { KdbEnterAlways, KdbEnterAlways }, /* Reserved (15) */ + { KdbDoNotEnter, KdbEnterFromKmode }, /* FPU fault */ + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, + { KdbDoNotEnter, KdbEnterFromKmode }, /* SIMD fault */ + { KdbDoNotEnter, KdbEnterFromKmode } /* Last entry: used for unknown exceptions */ }; -static const char *ExceptionTypeStrings[] = - { - "Divide Error", - "Debug Trap", - "NMI", - "Breakpoint", - "Overflow", - "BOUND range exceeded", - "Invalid Opcode", - "No Math Coprocessor", - "Double Fault", - "Unknown(9)", - "Invalid TSS", - "Segment Not Present", - "Stack Segment Fault", - "General Protection", - "Page Fault", - "Reserved(15)", - "Math Fault", - "Alignment Check", - "Machine Check", - "SIMD Fault" - }; - -volatile DWORD x_dr0 = 0, x_dr1 = 0, x_dr2 = 0, x_dr3 = 0, x_dr7 = 0; - -extern LONG KdbDisassemble(ULONG Address); -extern LONG KdbGetInstLength(ULONG Address); +/* Exception descriptions */ +STATIC CONST PCHAR ExceptionNrToString[] = +{ + "Divide Error", + "Debug Trap", + "NMI", + "Breakpoint", + "Overflow", + "BOUND range exceeded", + "Invalid Opcode", + "No Math Coprocessor", + "Double Fault", + "Unknown(9)", + "Invalid TSS", + "Segment Not Present", + "Stack Segment Fault", + "General Protection", + "Page Fault", + "Reserved(15)", + "Math Fault", + "Alignment Check", + "Machine Check", + "SIMD Fault" +}; /* FUNCTIONS *****************************************************************/ -/* - * Convert a string to an unsigned long integer. +/*!\brief Overwrites the instruction at \a Address with \a NewInst and stores + * the old instruction in *OldInst. * - * Ignores `locale' stuff. Assumes that the upper and lower case - * alphabets and digits are each contiguous. + * \param Process Process in which's context to overwrite the instruction. + * \param Address Address at which to overwrite the instruction. + * \param NewInst New instruction (written to \a Address) + * \param OldInst Old instruction (read from \a Address) + * + * \returns NTSTATUS */ -unsigned long -strtoul(const char *nptr, char **endptr, int base) +STATIC NTSTATUS +KdbpOverwriteInstruction( + IN PEPROCESS Process, + IN ULONG_PTR Address, + IN UCHAR NewInst, + OUT PUCHAR OldInst OPTIONAL) { - const char *s = nptr; - unsigned long acc; - int c; - unsigned long cutoff; - int neg = 0, any, cutlim; + NTSTATUS Status; + ULONG Protect; + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + KAPC_STATE ApcState; - /* - * See strtol for comments as to the logic used. - */ - do { - c = *s++; - } while (isspace(c)); - if (c == '-') - { - neg = 1; - c = *s++; - } - else if (c == '+') - c = *s++; - if ((base == 0 || base == 16) && - c == '0' && (*s == 'x' || *s == 'X')) - { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) - base = c == '0' ? 8 : 10; - cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; - cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; - for (acc = 0, any = 0;; c = *s++) - { - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; - if (c >= base) - break; - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= base; - acc += c; - } - } - if (any < 0) - { - acc = ULONG_MAX; - } - else if (neg) - acc = -acc; - if (endptr != 0) - *endptr = any ? (char *)s - 1 : (char *)nptr; - return acc; -} - - -char* -strpbrk(const char* s, const char* accept) -{ - int i; - for (; (*s) != 0; s++) - { - for (i = 0; accept[i] != 0; i++) - { - if (accept[i] == (*s)) - { - return((char *)s); - } - } - } - return(NULL); -} - - -#if 0 -NTSTATUS -KdbpSafeReadMemory(PVOID dst, PVOID src, INT size) -{ - INT page, page_end; - - /* check source */ - page_end = (((ULONG_PTR)src + size) / PAGE_SIZE); - for (page = ((ULONG_PTR)src / PAGE_SIZE); page <= page_end; page++) - { - if (!MmIsPagePresent(NULL, (PVOID)(page * PAGE_SIZE))) - return STATUS_UNSUCCESSFUL; - } - - /* copy memory */ - RtlCopyMemory(dst, src, size); - return STATUS_SUCCESS; -} - - -NTSTATUS -KdbpSafeWriteMemory(PVOID dst, PVOID src, INT size) -{ - return KdbpSafeWriteMemory(dst, src, size); - INT page, page_end; - - /* check destination */ - page_end = (((ULONG_PTR)dst + size) / PAGE_SIZE); - for (page = ((ULONG_PTR)dst / PAGE_SIZE); page <= page_end; page++) - { - if (!MmIsPagePresent(NULL, (PVOID)(page * PAGE_SIZE))) - return STATUS_UNSUCCESSFUL; - } - - /* copy memory */ - RtlCopyMemory(dst, src, size); - return STATUS_SUCCESS; -} -#endif /* unused */ - - -VOID -KdbGetCommand(PCH Buffer) -{ - CHAR Key; - PCH Orig = Buffer; - static CHAR LastCommand[256] = ""; - ULONG ScanCode = 0; - static CHAR LastKey = '\0'; - - KbdEchoOn = !((KdDebugState & KD_DEBUG_KDNOECHO) != 0); - - for (;;) - { - if (KdDebugState & KD_DEBUG_KDSERIAL) - while ((Key = KdbTryGetCharSerial()) == -1); - else - while ((Key = KdbTryGetCharKeyboard(&ScanCode)) == -1); - - if (Key == '\n' && LastKey == '\r') - { - /* Ignore this key... */ - } - else if (Key == '\r' || Key == '\n') - { - DbgPrint("\n"); - /* - Repeat the last command if the user presses enter. Reduces the - risk of RSI when single-stepping. - */ - if (Buffer == Orig) - { - strcpy(Buffer, LastCommand); - } - else - { - *Buffer = 0; - strcpy(LastCommand, Orig); - } - LastKey = Key; - return; - } - else if (Key == BS || Key == DEL) - { - if (Buffer > Orig) - { - Buffer--; - *Buffer = 0; - if (KbdEchoOn) - DbgPrint("%c %c", BS, BS); - else - DbgPrint(" %c", BS); - } - } - else if (ScanCode == 72) - { - ULONG i; - while (Buffer > Orig) - { - Buffer--; - *Buffer = 0; - if (KbdEchoOn) - DbgPrint("%c %c", BS, BS); - else - DbgPrint(" %c", BS); - } - for (i = 0; LastCommand[i] != 0; i++) - { - if (KbdEchoOn) - DbgPrint("%c", LastCommand[i]); - *Buffer = LastCommand[i]; - Buffer++; - } - } - else - { - if (KbdEchoOn) - DbgPrint("%c", Key); - - *Buffer = Key; - Buffer++; - } - LastKey = Key; - } -} - -BOOLEAN STATIC -KdbDecodeAddress(PCHAR Buffer, PULONG Address) -{ - while (isspace(*Buffer)) - { - Buffer++; - } - if (Buffer[0] == '<') - { - PCHAR ModuleName = Buffer + 1; - PCHAR AddressString = strpbrk(Buffer, ":"); - extern LIST_ENTRY ModuleTextListHead; - PLIST_ENTRY current_entry; - MODULE_TEXT_SECTION* current = NULL; - static WCHAR ModuleNameW[256]; - ULONG i; - - if (AddressString == NULL) - { - DbgPrint("Address %x is malformed.\n", Buffer); - return(FALSE); - } - *AddressString = 0; - AddressString++; - while (isspace(*AddressString)) - { - AddressString++; - } - - for (i = 0; ModuleName[i] != 0 && !isspace(ModuleName[i]); i++) - { - ModuleNameW[i] = (WCHAR)ModuleName[i]; - } - ModuleNameW[i] = 0; - - /* Find the module. */ - current_entry = ModuleTextListHead.Flink; + /* Get the protection for the address. */ + Protect = MmGetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address)); - while (current_entry != &ModuleTextListHead && - current_entry != NULL) - { - current = - CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry); - if (wcscmp(ModuleNameW, current->Name) == 0) - { - break; - } - current_entry = current_entry->Flink; - } - if (current_entry == NULL || current_entry == &ModuleTextListHead) - { - DbgPrint("Couldn't find module %s.\n", ModuleName); - return(FALSE); - } - *Address = current->Base; - *Address += strtoul(AddressString, NULL, 16); - return(TRUE); - } - else - { - *Address = strtoul(Buffer, NULL, 0); - return(TRUE); - } -} + /* Return if that page isn't present. */ + if (Protect & PAGE_NOACCESS) + { + return STATUS_MEMORY_NOT_ALLOCATED; + } + + /* Attach to the process */ + if (CurrentProcess != Process) + { + KeStackAttachProcess(EPROCESS_TO_KPROCESS(Process), &ApcState); + } -NTSTATUS STATIC -KdbOverwriteInst(ULONG Address, PUCHAR PreviousInst, UCHAR NewInst) -{ - NTSTATUS Status; - ULONG Protect; - /* Get the protection for the address. */ - Protect = MmGetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address)); - /* Return if that page isn't present. */ - if (Protect & PAGE_NOACCESS) - { - return(STATUS_MEMORY_NOT_ALLOCATED); - } - if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) - { - MmSetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address), + /* Make the page writeable if it is read only. */ + if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) + { + MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), (Protect & ~(PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) | PAGE_READWRITE); - } - /* Copy the old instruction back to the caller. */ - if (PreviousInst != NULL) - { - Status = KdbpSafeReadMemory(PreviousInst, (PUCHAR)Address, 1); + } + + /* Copy the old instruction back to the caller. */ + if (OldInst != NULL) + { + Status = KdbpSafeReadMemory(OldInst, (PUCHAR)Address, 1); if (!NT_SUCCESS(Status)) - { - if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) - { - MmSetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address), Protect); - } - return(Status); - } - } - /* Copy the new instruction in its place. */ - Status = KdbpSafeWriteMemory((PUCHAR)Address, &NewInst, 1); - if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) - { - MmSetPageProtect(PsGetCurrentProcess(), (PVOID)PAGE_ROUND_DOWN(Address), Protect); - } - return Status; + { + if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) + { + MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect); + } + /* Detach from process */ + if (CurrentProcess != Process) + { + KeDetachProcess(); + } + return Status; + } + } + + /* Copy the new instruction in its place. */ + Status = KdbpSafeWriteMemory((PUCHAR)Address, &NewInst, 1); + + /* Restore the page protection. */ + if (Protect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ)) + { + MmSetPageProtect(Process, (PVOID)PAGE_ROUND_DOWN(Address), Protect); + } + + /* Detach from process */ + if (CurrentProcess != Process) + { + KeUnstackDetachProcess(&ApcState); + } + + return Status; } - -VOID STATIC -KdbRenableBreakPoints(VOID) +/*!\brief Checks whether the given instruction can be single stepped or has to be + * stepped over using a temporary breakpoint. + * + * \retval TRUE Instruction is a call. + * \retval FALSE Instruction is not a call. + */ +BOOLEAN +KdbpShouldStepOverInstruction(ULONG_PTR Eip) { - ULONG i; - for (i = 0; i < KDB_MAXIMUM_BREAKPOINT_COUNT; i++) - { - if (KdbActiveBreakPoints[i].Assigned && - !KdbActiveBreakPoints[i].Enabled) - { - KdbActiveBreakPoints[i].Enabled = TRUE; - (VOID)KdbOverwriteInst(KdbActiveBreakPoints[i].Address, - &KdbActiveBreakPoints[i].SavedInst, - 0xCC); - } - } -} + UCHAR Mem[3]; + INT i = 0; -LONG STATIC -KdbIsBreakPointOurs(PKTRAP_FRAME Tf) -{ - ULONG i; - for (i = 0; i < KDB_MAXIMUM_BREAKPOINT_COUNT; i++) - { - if (KdbActiveBreakPoints[i].Assigned && - KdbActiveBreakPoints[i].Address == (Tf->Eip - 1)) - { - return(i); - } - } - return(-1); -} + if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem)))) + { + KdbpPrint("Couldn't access memory at 0x%x\n", (UINT)Eip); + return FALSE; + } -VOID STATIC -KdbDeleteBreakPoint(ULONG BreakPointNr) -{ - KdbBreakPointCount--; - KdbActiveBreakPoints[BreakPointNr].Assigned = FALSE; -} - -NTSTATUS STATIC -KdbInsertBreakPoint(ULONG Address, BOOLEAN Temporary) -{ - NTSTATUS Status; - UCHAR SavedInst; - ULONG i; - if (KdbBreakPointCount == KDB_MAXIMUM_BREAKPOINT_COUNT) - { - return(STATUS_UNSUCCESSFUL); - } - for (i = 0; i < KDB_MAXIMUM_BREAKPOINT_COUNT; i++) - { - if (!KdbActiveBreakPoints[i].Assigned) - { - break; - } - } - Status = KdbOverwriteInst(Address, &SavedInst, 0xCC); - if (!NT_SUCCESS(Status)) - { - return(Status); - } - KdbActiveBreakPoints[i].Assigned = TRUE; - KdbActiveBreakPoints[i].Enabled = TRUE; - KdbActiveBreakPoints[i].Address = Address; - KdbActiveBreakPoints[i].Temporary = Temporary; - KdbActiveBreakPoints[i].SavedInst = SavedInst; - return(STATUS_SUCCESS); -} - -ULONG -DbgSetBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG Addr; - NTSTATUS Status; - ULONG i; - if (Argc < 2) - { - DbgPrint("Need an address to set the breakpoint at.\n"); - return(1); - } - /* Stitch the remaining arguments back into a single string. */ - for (i = 2; i < Argc; i++) - { - Argv[i][-1] = ' '; - } - if (!KdbDecodeAddress(Argv[1], &Addr)) - { - return(1); - } - DbgPrint("Setting breakpoint at 0x%X\n", Addr); - if (!NT_SUCCESS(Status = KdbInsertBreakPoint(Addr, FALSE))) - { - DbgPrint("Failed to set breakpoint (Status %X)\n", Status); - } - return(1); -} - -ULONG -DbgDeleteBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG BreakPointNr; - if (Argc != 2) - { - DbgPrint("Need a breakpoint number to delete.\n"); - return(1); - } - BreakPointNr = strtoul(Argv[1], NULL, 10); - DbgPrint("Deleting breakpoint %d.\n", BreakPointNr); - KdbDeleteBreakPoint(BreakPointNr); - return(1); -} - -ULONG -DbgSetMemoryBreakPoint(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG DebugRegNr; - UCHAR BreakType; - ULONG Length, Address; - ULONG Rw = 0; - ULONG i; - if (Argc != 2 && Argc < 5) - { - DbgPrint("ba <0-3> <1|2|4>
\n"); - return(1); - } - DebugRegNr = strtoul(Argv[1], NULL, 10); - if (DebugRegNr >= 4) - { - DbgPrint("Debug register number should be between 0 and 3.\n"); - return(1); - } - if (Argc == 2) - { - /* Clear the breakpoint. */ - Tf->Dr7 &= ~(0x3 << (DebugRegNr * 2)); - if ((Tf->Dr7 & 0xFF) == 0) - { - /* - If no breakpoints are enabled then - clear the exact match flags. - */ - Tf->Dr7 &= 0xFFFFFCFF; - } - return(1); - } - BreakType = Argv[2][0]; - if (BreakType != 'r' && BreakType != 'w' && BreakType != 'e') - { - DbgPrint("Access type to break on should be either 'r', 'w' or 'e'.\n"); - return(1); - } - Length = strtoul(Argv[3], NULL, 10); - if (Length != 1 && Length != 2 && Length != 4) - { - DbgPrint("Length of the breakpoint should be one, two or four.\n"); - return(1); - } - if (Length != 1 && BreakType == 'e') - { - DbgPrint("The length of an execution breakpoint should be one.\n"); - return(1); - } - /* Stitch the remaining arguments back into a single string. */ - for (i = 4; i < Argc; i++) - { - Argv[i][-1] = ' '; - } - if (!KdbDecodeAddress(Argv[4], &Address)) - { - return(1); - } - if ((Address & (Length - 1)) != 0) - { - DbgPrint("The breakpoint address should be aligned to a multiple of " - "the breakpoint length.\n"); - return(1); - } - - /* Set the breakpoint address. */ - switch (DebugRegNr) - { - case 0: Tf->Dr0 = Address; break; - case 1: Tf->Dr1 = Address; break; - case 2: Tf->Dr2 = Address; break; - case 3: Tf->Dr3 = Address; break; - } - /* Enable the breakpoint. */ - Tf->Dr7 |= (0x3 << (DebugRegNr * 2)); - /* Enable the exact match bits. */ - Tf->Dr7 |= 0x00000300; - /* Clear existing state. */ - Tf->Dr7 &= ~(0xF << (16 + (DebugRegNr * 4))); - /* Set the breakpoint type. */ - switch (BreakType) - { - case 'r': Rw = 3; break; - case 'w': Rw = 1; break; - case 'e': Rw = 0; break; - } - Tf->Dr7 |= (Rw << (16 + (DebugRegNr * 4))); - /* Set the breakpoint length. */ - Tf->Dr7 |= ((Length - 1) << (18 + (DebugRegNr * 4))); - - return(1); -} - -ULONG -DbgStep(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - /* Set the single step flag and return to the interrupted code. */ - Tf->Eflags |= (1 << 8); - KdbIgnoreNextSingleStep = FALSE; - KdbLastSingleStepFrom = Tf->Eip; - return(0); -} - -ULONG -DbgStepOver(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - PUCHAR Eip; - UCHAR Mem[3]; - - if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Tf->Eip, sizeof (Mem)))) - { - DbgPrint("Couldn't access memory at 0x%x\n", (UINT)Tf->Eip); - return(1); - } - Eip = Mem; - - /* Check if the current instruction is a call. */ - while (Eip[0] == 0x66 || Eip[0] == 0x67) - { - Eip++; - } - if (Eip[0] == 0xE8 || Eip[0] == 0x9A || Eip[0] == 0xF2 || Eip[0] == 0xF3 || - (Eip[0] == 0xFF && (Eip[1] & 0x38) == 0x10)) - { - ULONG NextInst = Tf->Eip + KdbGetInstLength(Tf->Eip); - KdbLastSingleStepFrom = Tf->Eip; - KdbInsertBreakPoint(NextInst, TRUE); - return(0); - } - else - { - return(DbgStep(Argc, Argv, Tf)); - } -} - -ULONG -DbgFinish(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - PULONG Ebp = (PULONG)Tf->Ebp; - ULONG ReturnAddress; - NTSTATUS Status; - PKTHREAD CurrentThread; - - /* Check that ebp points onto the stack. */ - CurrentThread = KeGetCurrentThread(); - if (CurrentThread == NULL || - !(Ebp >= (PULONG)CurrentThread->StackLimit && - Ebp <= (PULONG)CurrentThread->StackBase)) - { - DbgPrint("This function doesn't appear to have a valid stack frame.\n"); - return(1); - } - - /* Get the address of the caller. */ - Status = KdbpSafeReadMemory(&ReturnAddress, Ebp + 1, sizeof(ULONG)); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Memory access error (%X) while getting return address.\n", - Status); - return(1); - } - - /* Set a temporary breakpoint at that location. */ - Status = KdbInsertBreakPoint(ReturnAddress, TRUE); - if (!NT_SUCCESS(Status)) - { - DbgPrint("Couldn't set a temporary breakpoint at %X (Status %X)\n", - ReturnAddress, Status); - return(1); - } - - /* - Otherwise start running again and with any luck we will break back into - the debugger when the current function returns. - */ - return(0); -} - -ULONG -DbgDisassemble(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG Address, i; - LONG InstLen; - if (Argc >= 2) - { - /* Stitch the remaining arguments back into a single string. */ - for (i = 2; i < Argc; i++) - { - Argv[i][-1] = ' '; - } - if (!KdbDecodeAddress(Argv[1], &Address)) - { - return(1); - } - } - else - { - Address = Tf->Eip; - } - for (i = 0; i < 10; i++) - { - if (!KdbSymPrintAddress((PVOID)Address)) - { - DbgPrint("<%x>", Address); - } - DbgPrint(": "); - InstLen = KdbDisassemble(Address); - if (InstLen < 0) - { - DbgPrint("\n"); - return(1); - } - DbgPrint("\n"); - Address += InstLen; - } - - return(1); -} - -ULONG -DbgProcessHelpCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG i, j, len; - - DbgPrint("Kernel debugger commands:\n"); - for (i = 0; DebuggerCommands[i].Name != NULL; i++) - { - DbgPrint(" %s", DebuggerCommands[i].Syntax); - len = strlen(DebuggerCommands[i].Syntax); - if (len < 35) - { - for (j = 0; j < 35 - len; j++) - { - DbgPrint(" "); - } - } - DbgPrint(" - %s\n", DebuggerCommands[i].Help); - } - return(1); -} - -ULONG -DbgThreadListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - BOOL System = FALSE; - if (Argc == 2 && (!strcmp(Argv[1], "sys") || !strcmp(Argv[1], "SYS"))) - System = TRUE; - - PsDumpThreads(System); - return(1); -} - -ULONG -DbgProcessListCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - extern LIST_ENTRY PsActiveProcessHead; - PLIST_ENTRY current_entry; - PEPROCESS current; - ULONG i = 1; - - if (PsActiveProcessHead.Flink == NULL) - { - DbgPrint("No processes.\n"); - return(1); - } - - DbgPrint("Process list: "); - current_entry = PsActiveProcessHead.Flink; - while (current_entry != &PsActiveProcessHead) - { - current = CONTAINING_RECORD(current_entry, EPROCESS, ProcessListEntry); - DbgPrint("%d %.8s", current->UniqueProcessId, - current->ImageFileName); + /* Check if the current instruction is a call. */ + while ((i < sizeof (Mem)) && (Mem[i] == 0x66 || Mem[i] == 0x67)) i++; - if ((i % 4) == 0) - { - DbgPrint("\n"); - } - current_entry = current_entry->Flink; - } - return(1); + if (i == sizeof (Mem)) + return FALSE; + if (Mem[i] == 0xE8 || Mem[i] == 0x9A || Mem[i] == 0xF2 || Mem[i] == 0xF3 || + (((i + 1) < sizeof (Mem)) && Mem[i] == 0xFF && (Mem[i+1] & 0x38) == 0x10)) + { + return TRUE; + } + return FALSE; } -VOID -DbgPrintBackTrace(PULONG Frame, ULONG_PTR StackBase, ULONG_PTR StackLimit) +/*!\brief Steps over an instruction + * + * If the given instruction should be stepped over, this function inserts a + * temporary breakpoint after the instruction and returns TRUE, otherwise it + * returns FALSE. + * + * \retval TRUE Temporary breakpoint set after instruction. + * \retval FALSE No breakpoint was set. + */ +BOOLEAN +KdbpStepOverInstruction(ULONG_PTR Eip) { - PVOID Address; + LONG InstLen; - DbgPrint("Frames:\n"); - while (Frame != NULL) - { - if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, Frame + 1, sizeof (Address)))) - { - DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT)(Frame + 1)); - break; - } - KdbSymPrintAddress(Address); - DbgPrint("\n"); - if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, Frame, sizeof (Frame)))) - { - DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT)Frame); - break; - } - } + if (!KdbpShouldStepOverInstruction(Eip)) + return FALSE; + + InstLen = KdbpGetInstLength(Eip); + if (InstLen < 1) + return FALSE; + + if (!NT_SUCCESS(KdbpInsertBreakPoint(Eip + InstLen, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL))) + return FALSE; + + return TRUE; } -ULONG -DbgAddrCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME tf) +/*!\brief Steps into an instruction (interrupts) + * + * If the given instruction should be stepped into, this function inserts a + * temporary breakpoint at the target instruction and returns TRUE, otherwise it + * returns FALSE. + * + * \retval TRUE Temporary breakpoint set at target instruction. + * \retval FALSE No breakpoint was set. + */ +BOOLEAN +KdbpStepIntoInstruction(ULONG_PTR Eip) { - PVOID Addr; + struct __attribute__((packed)) { + USHORT Limit; + ULONG Base; + } Idtr; + UCHAR Mem[2]; + INT IntVect; + ULONG IntDesc[2]; + ULONG_PTR TargetEip; - if (Argc == 2) - { - Addr = (PVOID)strtoul(Argv[1], NULL, 0); - KdbSymPrintAddress(Addr); - } + /* Read memory */ + if (!NT_SUCCESS(KdbpSafeReadMemory(Mem, (PVOID)Eip, sizeof (Mem)))) + { + /*KdbpPrint("Couldn't access memory at 0x%x\n", (UINT)Eip);*/ + return FALSE; + } - return(1); + /* Check for INT instruction */ + /* FIXME: Check for iret */ + if (Mem[0] == 0xcc) + IntVect = 3; + else if (Mem[0] == 0xcd) + IntVect = Mem[1]; + else if (Mem[0] == 0xce && KdbCurrentTrapFrame->Tf.Eflags & (1<<11)) /* 1 << 11 is the overflow flag */ + IntVect = 4; + else + return FALSE; + + if (IntVect < 32) /* We should be informed about interrupts < 32 by the kernel, no need to breakpoint them */ + { + return FALSE; + } + + /* Read the interrupt descriptor table register */ + asm volatile("sidt %0" : : "m"(Idtr)); + if (IntVect >= (Idtr.Limit + 1) / 8) + { + /*KdbpPrint("IDT does not contain interrupt vector %d\n.", IntVect);*/ + return TRUE; + } + + /* Get the interrupt descriptor */ + if (!NT_SUCCESS(KdbpSafeReadMemory(IntDesc, (PVOID)(Idtr.Base + (IntVect * 8)), sizeof (IntDesc)))) + { + /*KdbpPrint("Couldn't access memory at 0x%x\n", (UINT)Idtr.Base + (IntVect * 8));*/ + return FALSE; + } + + /* Check descriptor and get target eip (16 bit interrupt/trap gates not supported) */ + if ((IntDesc[1] & (1 << 15)) == 0) /* not present */ + { + return FALSE; + } + if ((IntDesc[1] & 0x1f00) == 0x0500) /* Task gate */ + { + /* FIXME: Task gates not supported */ + return FALSE; + } + else if (((IntDesc[1] & 0x1fe0) == 0x0e00) || /* 32 bit Interrupt gate */ + ((IntDesc[1] & 0x1fe0) == 0x0f00)) /* 32 bit Trap gate */ + { + /* FIXME: Should the segment selector of the interrupt gate be checked? */ + TargetEip = (IntDesc[1] & 0xffff0000) | (IntDesc[0] & 0x0000ffff); + } + else + { + return FALSE; + } + + /* Insert breakpoint */ + if (!NT_SUCCESS(KdbpInsertBreakPoint(TargetEip, KdbBreakPointTemporary, 0, 0, NULL, FALSE, NULL))) + return FALSE; + + return TRUE; } -ULONG -DbgXCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME tf) +/*!\brief Gets the number of the next breakpoint >= Start. + * + * \param Start Breakpoint number to start searching at. -1 if no more breakpoints are found. + * + * \returns Breakpoint number (-1 if no more breakpoints are found) + */ +LONG +KdbpGetNextBreakPointNr( + IN ULONG Start OPTIONAL) { - PDWORD Addr = NULL; - DWORD Items = 1; - DWORD i = 0; - DWORD Item; - - if (Argc >= 2) - Addr = (PDWORD)strtoul(Argv[1], NULL, 0); - if (Argc >= 3) - Items = (DWORD)strtoul(Argv[2], NULL, 0); - - if (Addr == NULL) - return(1); - - for (i = 0; i < Items; i++) - { - if( (i % 4) == 0 ) - { - if (i != 0) - DbgPrint("\n"); - DbgPrint("%08x:", (int)(&Addr[i])); - } - if (!NT_SUCCESS(KdbpSafeReadMemory(&Item, Addr + i, sizeof (Item)))) - { - DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT)(Addr + i)); - break; - } - DbgPrint("%08x ", Item); - } - - return(1); + for (; Start < RTL_NUMBER_OF(KdbBreakPoints); Start++) + { + if (KdbBreakPoints[Start].Type != KdbBreakPointNone) + return Start; + } + return -1; } -static int KjsReadRegValue( void *context, - JSNode *result, - JSNode *args ) { - PCHAR cp; - PVOID *context_list = context; - PKJS kjs = (PKJS)context_list[0]; - JSVirtualMachine *vm = kjs->vm; - NTSTATUS Status; - RTL_QUERY_REGISTRY_TABLE QueryTable[2] = { { 0 } }; - UNICODE_STRING NameString; - UNICODE_STRING PathString; - UNICODE_STRING DefaultString; - UNICODE_STRING ValueResult; - ANSI_STRING AnsiResult; - - if (args->u.vinteger != 2 || - args[1].type != JS_STRING || args[2].type != JS_STRING) { - return JS_PROPERTY_FOUND; - } - - RtlInitUnicodeString(&PathString,NULL); - RtlInitUnicodeString(&NameString,NULL); - - cp = js_string_to_c_string (vm, &args[1]); - RtlCreateUnicodeStringFromAsciiz(&PathString,cp); - js_free(cp); - cp = js_string_to_c_string (vm, &args[2]); - RtlCreateUnicodeStringFromAsciiz(&NameString,cp); - js_free(cp); - - RtlInitUnicodeString(&ValueResult,NULL); - RtlInitUnicodeString(&DefaultString,L""); - RtlInitAnsiString(&AnsiResult,NULL); - - QueryTable->EntryContext = 0; - QueryTable->Flags = - RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT; - QueryTable->Name = NameString.Buffer; - QueryTable->DefaultType = REG_SZ; - QueryTable->DefaultData = &DefaultString; - QueryTable->EntryContext = &ValueResult; - Status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, - PathString.Buffer, - QueryTable, - NULL, - NULL ); - - RtlFreeUnicodeString(&NameString); - RtlFreeUnicodeString(&PathString); - - if (NT_SUCCESS(Status)) { - RtlInitAnsiString(&AnsiResult,NULL); - RtlUnicodeStringToAnsiString(&AnsiResult, - &ValueResult, - TRUE); - js_vm_make_string (vm, result, AnsiResult.Buffer, - strlen(AnsiResult.Buffer)); - RtlFreeAnsiString(&AnsiResult); - } else { - result->type = JS_INTEGER; - result->u.vinteger = Status; - } - - return JS_PROPERTY_FOUND; -} - -static int KjsGetRegister( void *context, - JSNode *result, - JSNode *args ) { - PVOID *context_list = context; - if( args->u.vinteger == 1 && args->type == JS_INTEGER ) { - DWORD Result = ((DWORD *)context_list[1])[args[1].u.vinteger]; - result->type = JS_INTEGER; - result->u.vinteger = Result; - } - - return JS_PROPERTY_FOUND; -} - -static int KjsGetNthModule( void *context, - JSNode *result, - JSNode *args ) { - PVOID *context_list = context; - PKJS kjs = (PKJS)context_list[0]; - JSVirtualMachine *vm = kjs->vm; - PLIST_ENTRY current_entry; - MODULE_TEXT_SECTION *current = NULL; - extern LIST_ENTRY ModuleTextListHead; - int n = 0; - - if (args->u.vinteger != 1 || args[1].type != JS_INTEGER) { - return JS_PROPERTY_FOUND; - } - - current_entry = ModuleTextListHead.Flink; - - while (current_entry != &ModuleTextListHead && - current_entry != NULL && - n <= args[1].u.vinteger) { - current = CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, - ListEntry); - current_entry = current_entry->Flink; - n++; - } - - if (current_entry && current) { - ANSI_STRING NameStringNarrow; - UNICODE_STRING NameUnicodeString; - - RtlInitUnicodeString( &NameUnicodeString, current->Name ); - RtlUnicodeStringToAnsiString( &NameStringNarrow, - &NameUnicodeString, - TRUE ); - - js_vm_make_array (vm, result, 2); - - js_vm_make_string (vm, - &result->u.varray->data[0], - NameStringNarrow.Buffer, - NameStringNarrow.Length); - - RtlFreeAnsiString(&NameStringNarrow); - - result->u.varray->data[1].type = JS_INTEGER; - result->u.varray->data[1].u.vinteger = (DWORD)current->Base; - result->type = JS_ARRAY; - return JS_PROPERTY_FOUND; - } - result->type = JS_UNDEFINED; - return JS_PROPERTY_FOUND; -} - -static BOOL FindJSEndMark( PCHAR Buffer ) { - int i; - - for( i = 0; Buffer[i] && Buffer[i+1]; i++ ) { - if( Buffer[i] == ';' && Buffer[i+1] == ';' ) return TRUE; - } - return FALSE; -} - -ULONG -DbgScriptCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME tf) +/*!\brief Returns information of the specified breakpoint. + * + * \param BreakPointNr Number of the breakpoint to return information of. + * \param Address Receives the address of the breakpoint. + * \param Type Receives the type of the breakpoint (hardware or software) + * \param Size Size - for memory breakpoints. + * \param AccessType Access type - for hardware breakpoints. + * \param DebugReg Debug register - for enabled hardware breakpoints. + * \param Enabled Whether the breakpoint is enabled or not. + * \param Process The owning process of the breakpoint. + * \param ConditionExpression The expression which was given as condition for the bp. + * + * \returns NULL on failure, pointer to a KDB_BREAKPOINT struct on success. + */ +BOOLEAN +KdbpGetBreakPointInfo( + IN ULONG BreakPointNr, + OUT ULONG_PTR *Address OPTIONAL, + OUT KDB_BREAKPOINT_TYPE *Type OPTIONAL, + OUT UCHAR *Size OPTIONAL, + OUT KDB_ACCESS_TYPE *AccessType OPTIONAL, + OUT UCHAR *DebugReg OPTIONAL, + OUT BOOLEAN *Enabled OPTIONAL, + OUT BOOLEAN *Global OPTIONAL, + OUT PEPROCESS *Process OPTIONAL, + OUT PCHAR *ConditionExpression OPTIONAL) { - PCHAR Buffer; - PCHAR BufferStart; - static void *interp = 0; - void *script_cmd_context[2]; + PKDB_BREAKPOINT bp; - if( !interp ) interp = kjs_create_interp(NULL); - if( !interp ) return 1; + if (BreakPointNr >= RTL_NUMBER_OF(KdbBreakPoints) || + KdbBreakPoints[BreakPointNr].Type == KdbBreakPointNone) + { + return FALSE; + } + + bp = KdbBreakPoints + BreakPointNr; + if (Address != NULL) + *Address = bp->Address; + if (Type != NULL) + *Type = bp->Type; + if (bp->Type == KdbBreakPointHardware) + { + if (Size != NULL) + *Size = bp->Data.Hw.Size; + if (AccessType != NULL) + *AccessType = bp->Data.Hw.AccessType; + if (DebugReg != NULL && bp->Enabled) + *DebugReg = bp->Data.Hw.DebugReg; + } + if (Enabled != NULL) + *Enabled = bp->Enabled; + if (Global != NULL) + *Global = bp->Global; + if (Process != NULL) + *Process = bp->Process; + if (ConditionExpression != NULL) + *ConditionExpression = bp->ConditionExpression; - BufferStart = Buffer = ExAllocatePool( NonPagedPool, 4096 ); - if( !Buffer ) return 1; - - script_cmd_context[0] = interp; - script_cmd_context[1] = &tf; - - kjs_system_register( interp, "regs", script_cmd_context, - KjsGetRegister ); - kjs_system_register( interp, "regread", script_cmd_context, - KjsReadRegValue ); - kjs_system_register( interp, "getmodule", script_cmd_context, - KjsGetNthModule ); - - kjs_eval( interp, - "eval(" - "System.regread(" - "'\\\\Registry\\\\Machine\\\\System\\\\" - "CurrentControlSet\\\\Control\\\\Kdb'," - "'kjsinit'));" ); - - DbgPrint("\nKernel Debugger Script Interface (JavaScript :-)\n"); - DbgPrint("Terminate input with ;; and end scripting with .\n"); - do - { - if( Buffer != BufferStart ) - DbgPrint("..... "); - else - DbgPrint("kjs:> "); - KdbGetCommand( BufferStart ); - if( BufferStart[0] == '.' ) { - if( BufferStart != Buffer ) { - DbgPrint("Input Aborted.\n"); - BufferStart = Buffer; - } else { - /* Single dot input -> exit */ - break; - } - } else { - if( FindJSEndMark( Buffer ) ) { - kjs_eval( interp, Buffer ); - BufferStart = Buffer; - DbgPrint("\n"); - } else { - BufferStart = BufferStart + strlen(BufferStart); - } - } - } while (TRUE); - - ExFreePool( Buffer ); - - kjs_system_unregister( interp, script_cmd_context, KjsGetRegister ); - kjs_system_unregister( interp, script_cmd_context, KjsReadRegValue ); - kjs_system_unregister( interp, script_cmd_context, KjsGetNthModule ); - - return(1); + return TRUE; } -ULONG -DbgBackTraceCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Inserts a breakpoint into the breakpoint array. + * + * The \a Process of the breakpoint is set to \a KdbCurrentProcess + * + * \param Address Address at which to set the breakpoint. + * \param Type Type of breakpoint (hardware or software) + * \param Size Size of breakpoint (for hardware/memory breakpoints) + * \param AccessType Access type (for hardware breakpoins) + * \param ConditionExpression Expression which must evaluate to true for conditional breakpoints. + * \param Global Wether the breakpoint is global or local to a process. + * \param BreakPointNumber Receives the breakpoint number on success + * + * \returns NTSTATUS + */ +NTSTATUS +KdbpInsertBreakPoint( + IN ULONG_PTR Address, + IN KDB_BREAKPOINT_TYPE Type, + IN UCHAR Size OPTIONAL, + IN KDB_ACCESS_TYPE AccessType OPTIONAL, + IN PCHAR ConditionExpression OPTIONAL, + IN BOOLEAN Global, + OUT PULONG BreakPointNumber OPTIONAL) { - ULONG_PTR StackBase, StackLimit; - extern unsigned int init_stack, init_stack_top; + LONG i; + PVOID Condition; + PCHAR ConditionExpressionDup; + LONG ErrOffset; + CHAR ErrMsg[128]; - /* Without an argument we print the current stack. */ - if (Argc == 1) - { - if (PsGetCurrentThread() != NULL) - { - StackBase = (ULONG_PTR)PsGetCurrentThread()->Tcb.StackBase; - StackLimit = PsGetCurrentThread()->Tcb.StackLimit; - } - else - { - StackBase = (ULONG_PTR)init_stack_top; - StackLimit = (ULONG_PTR)init_stack; - } - DbgPrintBackTrace((PULONG)&Tf->DebugEbp, StackBase, StackLimit); - } - /* - * If there are two arguments and the second begins with a asterik treat it - * as the address of a frame to start printing the back trace from. - */ - else if (Argc == 2 && Argv[1][0] == '*') - { - PULONG Frame; - Frame = (PULONG)strtoul(&Argv[1][1], NULL, 0); - DbgPrintBackTrace(Frame, ULONG_MAX, 0); - } - /* - * Otherwise treat the argument as the id of a thread whose back trace is to - * be printed. - */ - else - { - } - return(1); -} + ASSERT(Type != KdbBreakPointNone); -VOID -DbgPrintCr0(ULONG Cr0) -{ - ULONG i; - - DbgPrint("CR0:"); - if (Cr0 & (1 << 0)) - { - DbgPrint(" PE"); - } - if (Cr0 & (1 << 1)) - { - DbgPrint(" MP"); - } - if (Cr0 & (1 << 2)) - { - DbgPrint(" EM"); - } - if (Cr0 & (1 << 3)) - { - DbgPrint(" TS"); - } - if (!(Cr0 & (1 << 4))) - { - DbgPrint(" !BIT5"); - } - if (Cr0 & (1 << 5)) - { - DbgPrint(" NE"); - } - for (i = 6; i < 16; i++) - { - if (Cr0 & (1 << i)) - { - DbgPrint(" BIT%d", i); - } - } - if (Cr0 & (1 << 16)) - { - DbgPrint(" WP"); - } - if (Cr0 & (1 << 17)) - { - DbgPrint(" BIT17"); - } - if (Cr0 & (1 << 18)) - { - DbgPrint(" AM"); - } - for (i = 19; i < 29; i++) - { - if (Cr0 & (1 << i)) - { - DbgPrint(" BIT%d", i); - } - } - if (Cr0 & (1 << 29)) - { - DbgPrint(" NW"); - } - if (Cr0 & (1 << 30)) - { - DbgPrint(" CD"); - } - if (Cr0 & (1 << 31)) - { - DbgPrint(" PG"); - } - DbgPrint("\n"); -} - -ULONG -DbgCRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - ULONG Cr0, Cr1, Cr2, Cr3, Cr4; - ULONG Ldtr; - USHORT Tr; - - __asm__ __volatile__ ("movl %%cr0, %0\n\t" : "=d" (Cr0)); - /* __asm__ __volatile__ ("movl %%cr1, %0\n\t" : "=d" (Cr1)); */ - Cr1 = 0; - __asm__ __volatile__ ("movl %%cr2, %0\n\t" : "=d" (Cr2)); - __asm__ __volatile__ ("movl %%cr3, %0\n\t" : "=d" (Cr3)); - __asm__ __volatile__ ("movl %%cr4, %0\n\t" : "=d" (Cr4)); - __asm__ __volatile__ ("str %0\n\t" : "=d" (Tr)); - __asm__ __volatile__ ("sldt %0\n\t" : "=d" (Ldtr)); - DbgPrintCr0(Cr0); - DbgPrint("CR1 %.8x CR2 %.8x CR3 %.8x CR4 %.8x TR %.8x LDTR %.8x\n", - Cr1, Cr2, Cr3, Cr4, (ULONG)Tf, Ldtr); - return(1); -} - -ULONG -DbgDRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - DbgPrint("Trap : DR0 %.8x DR1 %.8x DR2 %.8x DR3 %.8x DR6 %.8x DR7 %.8x\n", - Tf->Dr0, Tf->Dr1, Tf->Dr2, Tf->Dr3, Tf->Dr6, Tf->Dr7); - return(1); -} - -ULONG -DbgContCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - /* Not too difficult. */ - return(0); -} - -ULONG -DbgStopCondition(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - if( Argc == 1 ) { - if( KdbHandleHandled ) DbgPrint("all\n"); - else if( KdbHandleUmode ) DbgPrint("umode\n"); - else DbgPrint("kmode\n"); - } - else if( !strcmp(Argv[1],"all") ) - { KdbHandleHandled = TRUE; KdbHandleUmode = TRUE; } - else if( !strcmp(Argv[1],"umode") ) - { KdbHandleHandled = FALSE; KdbHandleUmode = TRUE; } - else if( !strcmp(Argv[1],"kmode") ) - { KdbHandleHandled = FALSE; KdbHandleUmode = FALSE; } - - return(TRUE); -} - -ULONG -DbgModuleLoadedAction(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - if (Argc == 1) + if (Type == KdbBreakPointHardware) + { + if ((Address % Size) != 0) { - if (KdbBreakOnModuleLoad) - DbgPrint("Current setting: break\n"); - else - DbgPrint("Current setting: continue\n"); + KdbpPrint("Address (0x%x) must be aligned to a multiple of the size (%d)\n", Address, Size); + return STATUS_UNSUCCESSFUL; } - else if (!strcmp(Argv[1], "break")) + if (AccessType == KdbAccessExec && Size != 1) { - KdbBreakOnModuleLoad = TRUE; + KdbpPrint("Size must be 1 for execution breakpoints.\n"); + return STATUS_UNSUCCESSFUL; } - else if (!strcmp(Argv[1], "continue")) + } + + if (KdbBreakPointCount == KDB_MAXIMUM_BREAKPOINT_COUNT) + { + return STATUS_UNSUCCESSFUL; + } + + /* Parse conditon expression string and duplicate it */ + if (ConditionExpression != NULL) + { + Condition = KdbpRpnParseExpression(ConditionExpression, &ErrOffset, ErrMsg); + if (Condition == NULL) { - KdbBreakOnModuleLoad = FALSE; + if (ErrOffset >= 0) + KdbpPrint("Couldn't parse expression: %s at character %d\n", ErrMsg, ErrOffset); + else + KdbpPrint("Couldn't parse expression: %s", ErrMsg); + return STATUS_UNSUCCESSFUL; } - else + + i = strlen(ConditionExpression) + 1; + ConditionExpressionDup = ExAllocatePoolWithTag(NonPagedPool, i, TAG_KDBG); + RtlCopyMemory(ConditionExpressionDup, ConditionExpression, i); + + } + else + { + Condition = NULL; + ConditionExpressionDup = NULL; + } + + /* Find unused breakpoint */ + if (Type == KdbBreakPointTemporary) + { + for (i = RTL_NUMBER_OF(KdbBreakPoints) - 1; i >= 0; i--) { - DbgPrint("Unknown setting: %s\n", Argv[1]); + if (KdbBreakPoints[i].Type == KdbBreakPointNone) + break; + } + } + else + { + for (i = 0; i < RTL_NUMBER_OF(KdbBreakPoints); i++) + { + if (KdbBreakPoints[i].Type == KdbBreakPointNone) + break; + } + } + ASSERT(i < RTL_NUMBER_OF(KdbBreakPoints)); + + /* Set the breakpoint */ + ASSERT(KdbCurrentProcess != NULL); + KdbBreakPoints[i].Type = Type; + KdbBreakPoints[i].Address = Address; + KdbBreakPoints[i].Enabled = FALSE; + KdbBreakPoints[i].Global = Global; + KdbBreakPoints[i].Process = KdbCurrentProcess; + KdbBreakPoints[i].ConditionExpression = ConditionExpressionDup; + KdbBreakPoints[i].Condition = Condition; + if (Type == KdbBreakPointHardware) + { + + KdbBreakPoints[i].Data.Hw.Size = Size; + KdbBreakPoints[i].Data.Hw.AccessType = AccessType; + } + KdbBreakPointCount++; + + if (Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d inserted.\n", i); + + /* Try to enable the breakpoint */ + KdbpEnableBreakPoint(i, NULL); + + /* Return the breakpoint number */ + if (BreakPointNumber != NULL) + *BreakPointNumber = i; + + return STATUS_SUCCESS; +} + +/*!\brief Deletes a breakpoint + * + * \param BreakPointNr Number of the breakpoint to delete. Can be -1 + * \param BreakPoint Breakpoint to delete. Can be NULL. + * + * \retval TRUE Success. + * \retval FALSE Failure (invalid breakpoint number) + */ +BOOLEAN +KdbpDeleteBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL) +{ + if (BreakPointNr < 0) + { + ASSERT(BreakPoint != NULL); + BreakPointNr = BreakPoint - KdbBreakPoints; + } + if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + if (BreakPoint == NULL) + { + BreakPoint = KdbBreakPoints + BreakPointNr; + } + if (BreakPoint->Type == KdbBreakPointNone) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + + if (BreakPoint->Enabled && + !KdbpDisableBreakPoint(-1, BreakPoint)) + return FALSE; + + if (BreakPoint->Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d deleted.\n", BreakPointNr); + BreakPoint->Type = KdbBreakPointNone; + KdbBreakPointCount--; + + return TRUE; +} + +/*!\brief Checks if the breakpoint was set by the debugger + * + * Tries to find a breakpoint in the breakpoint array which caused + * the debug exception to happen. + * + * \param ExpNr Exception Number (1 or 3) + * \param TrapFrame Exception trapframe + * + * \returns Breakpoint number, -1 on error. + */ +STATIC LONG +KdbpIsBreakPointOurs( + IN ULONG ExpNr, + IN PKTRAP_FRAME TrapFrame) +{ + INT i; + ASSERT(ExpNr == 1 || ExpNr == 3); + + if (ExpNr == 3) /* Software interrupt */ + { + ULONG_PTR BpEip = (ULONG_PTR)TrapFrame->Eip - 1; /* Get EIP of INT3 instruction */ + for (i = 0; i < KdbSwBreakPointCount; i++) + { + ASSERT((KdbSwBreakPoints[i]->Type == KdbBreakPointSoftware || + KdbSwBreakPoints[i]->Type == KdbBreakPointTemporary)); + ASSERT(KdbSwBreakPoints[i]->Enabled); + if (KdbSwBreakPoints[i]->Address == BpEip) + { + return KdbSwBreakPoints[i] - KdbBreakPoints; + } + } + } + else if (ExpNr == 1) /* Hardware interrupt */ + { + UCHAR DebugReg; + for (i = 0; i < KdbHwBreakPointCount; i++) + { + ASSERT(KdbHwBreakPoints[i]->Type == KdbBreakPointHardware && + KdbHwBreakPoints[i]->Enabled); + DebugReg = KdbHwBreakPoints[i]->Data.Hw.DebugReg; + if ((TrapFrame->Dr6 & (1 << DebugReg)) != 0) + { + return KdbHwBreakPoints[i] - KdbBreakPoints; + } + } + } + + return -1; +} + +/*!\brief Enables a breakpoint. + * + * \param BreakPointNr Number of the breakpoint to enable Can be -1. + * \param BreakPoint Breakpoint to enable. Can be NULL. + * + * \retval TRUE Success. + * \retval FALSE Failure. + * + * \sa KdbpDisableBreakPoint + */ +BOOLEAN +KdbpEnableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL) +{ + NTSTATUS Status; + INT i; + ULONG ul; + + if (BreakPointNr < 0) + { + ASSERT(BreakPoint != NULL); + BreakPointNr = BreakPoint - KdbBreakPoints; + } + if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + if (BreakPoint == NULL) + { + BreakPoint = KdbBreakPoints + BreakPointNr; + } + if (BreakPoint->Type == KdbBreakPointNone) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + + if (BreakPoint->Enabled == TRUE) + { + KdbpPrint("Breakpoint %d is already enabled.\n", BreakPointNr); + return TRUE; + } + + if (BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary) + { + if (KdbSwBreakPointCount >= KDB_MAXIMUM_SW_BREAKPOINT_COUNT) + { + KdbpPrint("Maximum number of SW breakpoints (%d) used. " + "Disable another breakpoint in order to enable this one.\n", + KDB_MAXIMUM_SW_BREAKPOINT_COUNT); + return FALSE; + } + Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address, + 0xCC, &BreakPoint->Data.SavedInstruction); + if (!NT_SUCCESS(Status)) + { + KdbpPrint("Couldn't access memory at 0x%x\n", BreakPoint->Address); + return FALSE; + } + KdbSwBreakPoints[KdbSwBreakPointCount++] = BreakPoint; + } + else + { + if (BreakPoint->Data.Hw.AccessType == KdbAccessExec) + ASSERT(BreakPoint->Data.Hw.Size == 1); + ASSERT((BreakPoint->Address % BreakPoint->Data.Hw.Size) == 0); + if (KdbHwBreakPointCount >= KDB_MAXIMUM_HW_BREAKPOINT_COUNT) + { + KdbpPrint("Maximum number of HW breakpoints (%d) already used. " + "Disable another breakpoint in order to enable this one.\n", + KDB_MAXIMUM_HW_BREAKPOINT_COUNT); + return FALSE; } - return(TRUE); + /* Find unused hw breakpoint */ + ASSERT(KDB_MAXIMUM_HW_BREAKPOINT_COUNT == 4); + for (i = 0; i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT; i++) + { + if ((KdbTrapFrame.Tf.Dr7 & (0x3 << (i * 2))) == 0) + break; + } + ASSERT(i < KDB_MAXIMUM_HW_BREAKPOINT_COUNT); + + /* Set the breakpoint address. */ + switch (i) + { + case 0: + KdbTrapFrame.Tf.Dr0 = BreakPoint->Address; + break; + case 1: + KdbTrapFrame.Tf.Dr1 = BreakPoint->Address; + break; + case 2: + KdbTrapFrame.Tf.Dr2 = BreakPoint->Address; + break; + case 3: + KdbTrapFrame.Tf.Dr3 = BreakPoint->Address; + break; + } + + /* Enable the global breakpoint */ + KdbTrapFrame.Tf.Dr7 |= (0x2 << (i * 2)); + + /* Enable the exact match bits. */ + KdbTrapFrame.Tf.Dr7 |= 0x00000300; + + /* Clear existing state. */ + KdbTrapFrame.Tf.Dr7 &= ~(0xF << (16 + (i * 4))); + + /* Set the breakpoint type. */ + switch (BreakPoint->Data.Hw.AccessType) + { + case KdbAccessExec: + ul = 0; + break; + case KdbAccessWrite: + ul = 1; + break; + case KdbAccessRead: + case KdbAccessReadWrite: + ul = 3; + break; + default: + ASSERT(0); + return TRUE; + break; + } + KdbTrapFrame.Tf.Dr7 |= (ul << (16 + (i * 4))); + + /* Set the breakpoint length. */ + KdbTrapFrame.Tf.Dr7 |= ((BreakPoint->Data.Hw.Size - 1) << (18 + (i * 4))); + + /* Update KdbCurrentTrapFrame - values are taken from there by the CLI */ + if (&KdbTrapFrame != KdbCurrentTrapFrame) + { + KdbCurrentTrapFrame->Tf.Dr0 = KdbTrapFrame.Tf.Dr0; + KdbCurrentTrapFrame->Tf.Dr1 = KdbTrapFrame.Tf.Dr1; + KdbCurrentTrapFrame->Tf.Dr2 = KdbTrapFrame.Tf.Dr2; + KdbCurrentTrapFrame->Tf.Dr3 = KdbTrapFrame.Tf.Dr3; + KdbCurrentTrapFrame->Tf.Dr6 = KdbTrapFrame.Tf.Dr6; + KdbCurrentTrapFrame->Tf.Dr7 = KdbTrapFrame.Tf.Dr7; + } + + BreakPoint->Data.Hw.DebugReg = i; + KdbHwBreakPoints[KdbHwBreakPointCount++] = BreakPoint; + } + + BreakPoint->Enabled = TRUE; + if (BreakPoint->Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d enabled.\n", BreakPointNr); + return TRUE; } -ULONG -DbgEchoToggle(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Disables a breakpoint. + * + * \param BreakPointNr Number of the breakpoint to disable. Can be -1 + * \param BreakPoint Breakpoint to disable. Can be NULL. + * + * \retval TRUE Success. + * \retval FALSE Failure. + * + * \sa KdbpEnableBreakPoint + */ +BOOLEAN +KdbpDisableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL) { - KbdEchoOn = !KbdEchoOn; - return(TRUE); + INT i; + NTSTATUS Status; + + if (BreakPointNr < 0) + { + ASSERT(BreakPoint != NULL); + BreakPointNr = BreakPoint - KdbBreakPoints; + } + if (BreakPointNr < 0 || BreakPointNr >= KDB_MAXIMUM_BREAKPOINT_COUNT) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + if (BreakPoint == NULL) + { + BreakPoint = KdbBreakPoints + BreakPointNr; + } + if (BreakPoint->Type == KdbBreakPointNone) + { + KdbpPrint("Invalid breakpoint: %d\n", BreakPointNr); + return FALSE; + } + + if (BreakPoint->Enabled == FALSE) + { + KdbpPrint("Breakpoint %d is not enabled.\n", BreakPointNr); + return TRUE; + } + + if (BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary) + { + ASSERT(KdbSwBreakPointCount > 0); + Status = KdbpOverwriteInstruction(BreakPoint->Process, BreakPoint->Address, + BreakPoint->Data.SavedInstruction, NULL); + if (!NT_SUCCESS(Status)) + { + KdbpPrint("Couldn't restore original instruction.\n"); + return FALSE; + } + + for (i = 0; i < KdbSwBreakPointCount; i++) + { + if (KdbSwBreakPoints[i] == BreakPoint) + { + KdbSwBreakPoints[i] = KdbSwBreakPoints[--KdbSwBreakPointCount]; + i = -1; /* if the last breakpoint is disabled dont break with i >= KdbSwBreakPointCount */ + break; + } + } + if (i != -1) /* not found */ + ASSERT(0); + } + else + { + ASSERT(BreakPoint->Type == KdbBreakPointHardware); + + /* Clear the breakpoint. */ + KdbTrapFrame.Tf.Dr7 &= ~(0x3 << (BreakPoint->Data.Hw.DebugReg * 2)); + if ((KdbTrapFrame.Tf.Dr7 & 0xFF) == 0) + { + /* + * If no breakpoints are enabled then clear the exact match flags. + */ + KdbTrapFrame.Tf.Dr7 &= 0xFFFFFCFF; + } + + for (i = 0; i < KdbHwBreakPointCount; i++) + { + if (KdbHwBreakPoints[i] == BreakPoint) + { + KdbHwBreakPoints[i] = KdbHwBreakPoints[--KdbHwBreakPointCount]; + i = -1; /* if the last breakpoint is disabled dont break with i >= KdbHwBreakPointCount */ + break; + } + } + if (i != -1) /* not found */ + ASSERT(0); + } + + BreakPoint->Enabled = FALSE; + if (BreakPoint->Type != KdbBreakPointTemporary) + KdbpPrint("Breakpoint %d disabled.\n", BreakPointNr); + return TRUE; } -VOID -DbgPrintEflags(ULONG Eflags) +/*!\brief Gets the first or last chance enter-condition for exception nr. \a ExceptionNr + * + * \param ExceptionNr Number of the exception to get condition of. + * \param FirstChance Whether to get first or last chance condition. + * \param Condition Receives the condition setting. + * + * \retval TRUE Success. + * \retval FALSE Failure (invalid exception nr) + */ +BOOLEAN +KdbpGetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + OUT KDB_ENTER_CONDITION *Condition) { - DbgPrint("EFLAGS:"); - if (Eflags & (1 << 0)) - { - DbgPrint(" CF"); - } - if (!(Eflags & (1 << 1))) - { - DbgPrint(" !BIT1"); - } - if (Eflags & (1 << 2)) - { - DbgPrint(" PF"); - } - if (Eflags & (1 << 3)) - { - DbgPrint(" BIT3"); - } - if (Eflags & (1 << 4)) - { - DbgPrint(" AF"); - } - if (Eflags & (1 << 5)) - { - DbgPrint(" BIT5"); - } - if (Eflags & (1 << 6)) - { - DbgPrint(" ZF"); - } - if (Eflags & (1 << 7)) - { - DbgPrint(" SF"); - } - if (Eflags & (1 << 8)) - { - DbgPrint(" TF"); - } - if (Eflags & (1 << 9)) - { - DbgPrint(" IF"); - } - if (Eflags & (1 << 10)) - { - DbgPrint(" DF"); - } - if (Eflags & (1 << 11)) - { - DbgPrint(" OF"); - } - if ((Eflags & ((1 << 12) | (1 << 13))) == 0) - { - DbgPrint(" IOPL0"); - } - else if ((Eflags & ((1 << 12) | (1 << 13))) == 1) - { - DbgPrint(" IOPL1"); - } - else if ((Eflags & ((1 << 12) | (1 << 13))) == 2) - { - DbgPrint(" IOPL2"); - } - else if ((Eflags & ((1 << 12) | (1 << 13))) == 3) - { - DbgPrint(" IOPL3"); - } - if (Eflags & (1 << 14)) - { - DbgPrint(" NT"); - } - if (Eflags & (1 << 15)) - { - DbgPrint(" BIT15"); - } - if (Eflags & (1 << 16)) - { - DbgPrint(" RF"); - } - if (Eflags & (1 << 17)) - { - DbgPrint(" VF"); - } - if (Eflags & (1 << 18)) - { - DbgPrint(" AC"); - } - if (Eflags & (1 << 19)) - { - DbgPrint(" VIF"); - } - if (Eflags & (1 << 20)) - { - DbgPrint(" VIP"); - } - if (Eflags & (1 << 21)) - { - DbgPrint(" ID"); - } - if (Eflags & (1 << 22)) - { - DbgPrint(" BIT22"); - } - if (Eflags & (1 << 23)) - { - DbgPrint(" BIT23"); - } - if (Eflags & (1 << 24)) - { - DbgPrint(" BIT24"); - } - if (Eflags & (1 << 25)) - { - DbgPrint(" BIT25"); - } - if (Eflags & (1 << 26)) - { - DbgPrint(" BIT26"); - } - if (Eflags & (1 << 27)) - { - DbgPrint(" BIT27"); - } - if (Eflags & (1 << 28)) - { - DbgPrint(" BIT28"); - } - if (Eflags & (1 << 29)) - { - DbgPrint(" BIT29"); - } - if (Eflags & (1 << 30)) - { - DbgPrint(" BIT30"); - } - if (Eflags & (1 << 31)) - { - DbgPrint(" BIT31"); - } - DbgPrint("\n"); + if (ExceptionNr >= RTL_NUMBER_OF(KdbEnterConditions)) + return FALSE; + + *Condition = KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1]; + return TRUE; } -ULONG -DbgRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Sets the first or last chance enter-condition for exception nr. \a ExceptionNr + * + * \param ExceptionNr Number of the exception to set condition of (-1 for all) + * \param FirstChance Whether to set first or last chance condition. + * \param Condition The new condition setting. + * + * \retval TRUE Success. + * \retval FALSE Failure (invalid exception nr) + */ +BOOLEAN +KdbpSetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + IN KDB_ENTER_CONDITION Condition) { - DbgPrint("CS:EIP %.4x:%.8x, EAX %.8x EBX %.8x ECX %.8x EDX %.8x\n", - Tf->Cs & 0xFFFF, Tf->Eip, Tf->Eax, Tf->Ebx, Tf->Ecx, Tf->Edx); - DbgPrint("ESI %.8x EDI %.8x EBP %.8x SS:ESP %.4x:%.8x\n", - Tf->Esi, Tf->Edi, Tf->Ebp, Tf->Ss & 0xFFFF, Tf->Esp); - DbgPrintEflags(Tf->Eflags); - return(1); + if (ExceptionNr < 0) + { + for (ExceptionNr = 0; ExceptionNr < RTL_NUMBER_OF(KdbEnterConditions); ExceptionNr++) + { + if (ExceptionNr == 1 || ExceptionNr == 8 || + ExceptionNr == 9 || ExceptionNr == 15) /* Reserved exceptions */ + { + continue; + } + KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition; + } + } + else + { + if (ExceptionNr >= RTL_NUMBER_OF(KdbEnterConditions) || + ExceptionNr == 1 || ExceptionNr == 8 || /* Do not allow changing of the debug */ + ExceptionNr == 9 || ExceptionNr == 15) /* trap or reserved exceptions */ + { + return FALSE; + } + KdbEnterConditions[ExceptionNr][FirstChance ? 0 : 1] = Condition; + } + return TRUE; } -ULONG -DbgBugCheckCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Switches to another thread context + * + * \param ThreadId Id of the thread to switch to. + * + * \retval TRUE Success. + * \retval FALSE Failure (i.e. invalid thread id) + */ +BOOLEAN +KdbpAttachToThread( + PVOID ThreadId) { - KEBUGCHECK(0xDEADDEAD); - return(1); + PETHREAD Thread = NULL; + PEPROCESS Process; + + /* Get a pointer to the thread */ + if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId, &Thread))) + { + KdbpPrint("Invalid thread id: 0x%08x\n", (UINT)ThreadId); + return FALSE; + } + Process = Thread->ThreadsProcess; + + if (KeIsExecutingDpc() && Process != KdbCurrentProcess) + { + KdbpPrint("Cannot attach to thread within another process while executing a DPC.\n"); + return FALSE; + } + + /* Save the current thread's context (if we previously attached to a thread) */ + if (KdbCurrentThread != KdbOriginalThread) + { + ASSERT(KdbCurrentTrapFrame == &KdbThreadTrapFrame); + RtlCopyMemory(KdbCurrentThread->Tcb.TrapFrame, &KdbCurrentTrapFrame->Tf, sizeof (KTRAP_FRAME)); + } + else + { + ASSERT(KdbCurrentTrapFrame == &KdbTrapFrame); + } + + /* Switch to the thread's context */ + if (Thread != KdbOriginalThread) + { + ASSERT(Thread->Tcb.TrapFrame != NULL); + RtlCopyMemory(&KdbThreadTrapFrame.Tf, Thread->Tcb.TrapFrame, sizeof (KTRAP_FRAME)); + asm volatile( + "movl %%cr0, %0" "\n\t" + "movl %%cr2, %1" "\n\t" + "movl %%cr3, %2" "\n\t" + "movl %%cr4, %3" "\n\t" + : "=r"(KdbTrapFrame.Cr0), "=r"(KdbTrapFrame.Cr2), + "=r"(KdbTrapFrame.Cr3), "=r"(KdbTrapFrame.Cr4)); + KdbCurrentTrapFrame = &KdbThreadTrapFrame; + } + else /* Switching back to original thread */ + { + KdbCurrentTrapFrame = &KdbTrapFrame; + } + KdbCurrentThread = Thread; + + /* Attach to the thread's process */ + ASSERT(KdbCurrentProcess == PsGetCurrentProcess()); + if (KdbCurrentProcess != Process) + { + if (KdbCurrentProcess != KdbOriginalProcess) /* detach from previously attached process */ + { + KeUnstackDetachProcess(&KdbApcState); + } + if (KdbOriginalProcess != Process) + { + KeStackAttachProcess(EPROCESS_TO_KPROCESS(Process), &KdbApcState); + } + KdbCurrentProcess = Process; + } + + return TRUE; } -ULONG -DbgShowFilesCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Switches to another process/thread context + * + * This function switches to the first thread in the specified process. + * + * \param ProcessId Id of the process to switch to. + * + * \retval TRUE Success. + * \retval FALSE Failure (i.e. invalid process id) + */ +BOOLEAN +KdbpAttachToProcess( + PVOID ProcessId) { - DbgShowFiles(); - return(1); + PEPROCESS Process = NULL; + PETHREAD Thread; + PLIST_ENTRY Entry; + + /* Get a pointer to the process */ + if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) + { + KdbpPrint("Invalid process id: 0x%08x\n", (UINT)ProcessId); + return FALSE; + } + + Entry = Process->ThreadListHead.Flink; + if (Entry == &KdbCurrentProcess->ThreadListHead) + { + KdbpPrint("No threads in process 0x%08x, cannot attach to process!\n", (UINT)ProcessId); + return FALSE; + } + + Thread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry); + + return KdbpAttachToThread(Thread->Cid.UniqueThread); } -ULONG -DbgEnableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) +/*!\brief Calls the main loop ... + */ +STATIC VOID +KdbpCallMainLoop() { - if (Argc == 2) - { - if (strlen(Argv[1]) > 0) - { - DbgEnableFile(Argv[1]); - } - } - return(1); + KdbpCliMainLoop(KdbEnteredOnSingleStep); } -ULONG -DbgDisableFileCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf) -{ - if (Argc == 2) - { - if (strlen(Argv[1]) > 0) - { - DbgDisableFile(Argv[1]); - } - } - return(1); -} - -VOID -KdbCreateThreadHook(PCONTEXT Context) -{ - Context->Dr0 = x_dr0; - Context->Dr1 = x_dr1; - Context->Dr2 = x_dr2; - Context->Dr3 = x_dr3; - Context->Dr7 = x_dr7; -} - -ULONG -KdbDoCommand(PCH CommandLine, PKTRAP_FRAME Tf) -{ - ULONG i; - PCH s1; - PCH s; - static PCH Argv[256]; - ULONG Argc; - static CHAR OrigCommand[256]; - - strcpy(OrigCommand, CommandLine); - - Argc = 0; - s = CommandLine; - while ((s1 = strpbrk(s, "\t ")) != NULL) - { - Argv[Argc] = s; - *s1 = 0; - s = s1 + 1; - Argc++; - } - Argv[Argc] = s; - Argc++; - - for (i = 0; DebuggerCommands[i].Name != NULL; i++) - { - if (strcmp(DebuggerCommands[i].Name, Argv[0]) == 0) - { - return(DebuggerCommands[i].Fn(Argc, Argv, Tf)); - } - } - DbgPrint("Command '%s' is unknown.", OrigCommand); - return(1); -} - -VOID -KdbMainLoop(PKTRAP_FRAME Tf) -{ - CHAR Command[256]; - ULONG s; - - if (!KdbEnteredOnSingleStep) - { - DbgPrint("\nEntered kernel debugger (type \"help\" for a list of commands)\n"); - } - else - { - if (!KdbSymPrintAddress((PVOID)Tf->Eip)) - { - DbgPrint("<%x>", Tf->Eip); - } - DbgPrint(": "); - if (KdbDisassemble(Tf->Eip) < 0) - { - DbgPrint(""); - } - KdbEnteredOnSingleStep = FALSE; - KdbLastSingleStepFrom = 0xFFFFFFFF; - } - - do - { - DbgPrint("\nkdb:> "); - - KdbGetCommand(Command); - s = KdbDoCommand(Command, Tf); - } while (s != 0); -} - -VOID -KdbInternalEnter(PKTRAP_FRAME Tf) +/*!\brief Internal function to enter KDB. + * + * Disables interrupts, releases display ownership, ... + */ +STATIC VOID +KdbpInternalEnter() { - __asm__ __volatile__ ("cli\n\t"); - KbdDisableMouse(); - if (KdDebugState & KD_DEBUG_SCREEN) - { + PETHREAD Thread; + PVOID SavedInitialStack, SavedStackBase, SavedKernelStack; + ULONG SavedStackLimit; + + KbdDisableMouse(); + if (KdDebugState & KD_DEBUG_SCREEN) + { HalReleaseDisplayOwnership(); - } - (VOID)KdbMainLoop(Tf); - KbdEnableMouse(); - __asm__ __volatile__("sti\n\t"); + } + + /* Call the interface's main loop on a different stack */ + Thread = PsGetCurrentThread(); + SavedInitialStack = Thread->Tcb.InitialStack; + SavedStackBase = Thread->Tcb.StackBase; + SavedStackLimit = Thread->Tcb.StackLimit; + SavedKernelStack = Thread->Tcb.KernelStack; + Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)KdbStack + KDB_STACK_SIZE; + Thread->Tcb.StackLimit = (ULONG)KdbStack; + Thread->Tcb.KernelStack = (char*)KdbStack + KDB_STACK_SIZE; + + /*KdbpPrint("Switching to KDB stack 0x%08x-0x%08x\n", Thread->Tcb.StackLimit, Thread->Tcb.StackBase);*/ + + KdbpStackSwitchAndCall(Thread->Tcb.KernelStack, KdbpCallMainLoop); + + Thread->Tcb.InitialStack = SavedInitialStack; + Thread->Tcb.StackBase = SavedStackBase; + Thread->Tcb.StackLimit = SavedStackLimit; + Thread->Tcb.KernelStack = SavedKernelStack; + KbdEnableMouse(); } +/*!\brief KDB Exception filter + * + * Called by the exception dispatcher. + * + * \param ExceptionRecord Unused. + * \param PreviousMode UserMode if the exception was raised from umode, otherwise KernelMode. + * \param Context Unused. + * \param TrapFrame Exception TrapFrame. + * \param FirstChance TRUE when called before exception frames were serached, + * FALSE for the second call. + * + * \returns KD_CONTINUE_TYPE + */ KD_CONTINUE_TYPE -KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, - KPROCESSOR_MODE PreviousMode, - PCONTEXT Context, - PKTRAP_FRAME TrapFrame, - BOOLEAN AlwaysHandle) +KdbEnterDebuggerException( + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN KPROCESSOR_MODE PreviousMode, + IN PCONTEXT Context OPTIONAL, + IN OUT PKTRAP_FRAME TrapFrame, + IN BOOLEAN FirstChance) { - LONG BreakPointNr; - ULONG ExpNr = (ULONG)TrapFrame->DebugArgMark; + ULONG ExpNr = (ULONG)TrapFrame->DebugArgMark; + KDB_ENTER_CONDITION EnterCondition; + KD_CONTINUE_TYPE ContinueType = kdHandleException; + PKDB_BREAKPOINT BreakPoint; + ULONG ul; + ULONGLONG ull; + BOOLEAN Resume = FALSE; + BOOLEAN EnterConditionMet = TRUE; + ULONG OldEflags; - /* Always handle beakpoints */ - if (ExpNr != 1 && ExpNr != 3) - { - DbgPrint(":KDBG:Entered:%s:%s\n", - PreviousMode==KernelMode ? "kmode" : "umode", - AlwaysHandle ? "always" : "if-unhandled"); + /* Exception inside the debugger? Game over. */ + if (InterlockedIncrement(&KdbEntryCount) > 1) + { + return kdHandleException; + } - /* If we aren't handling umode exceptions then return */ - if (PreviousMode == UserMode && !KdbHandleUmode && !AlwaysHandle) - { - return kdHandleException; - } + KdbCurrentProcess = PsGetCurrentProcess(); - /* If the exception would be unhandled (and we care) then handle it */ - if (PreviousMode == KernelMode && !KdbHandleHandled && !AlwaysHandle) - { - return kdHandleException; - } - } + /* Set continue type to kdContinue for single steps and breakpoints */ + if (ExpNr == 1 || ExpNr == 3) + ContinueType = kdContinue; - /* Exception inside the debugger? Game over. */ - if (KdbEntryCount > 0) - { - return(kdHandleException); - } - KdbEntryCount++; + /* Check if we should handle the exception. */ + ul = min(ExpNr, RTL_NUMBER_OF(KdbEnterConditions) - 1); + EnterCondition = KdbEnterConditions[ul][FirstChance ? 0 : 1]; + if (EnterCondition == KdbDoNotEnter || + (EnterCondition == KdbEnterFromUmode && PreviousMode != UserMode) || + (EnterCondition == KdbEnterFromKmode && PreviousMode != KernelMode)) + { + EnterConditionMet = FALSE; + } - /* Clear the single step flag. */ - TrapFrame->Eflags &= ~(1 << 8); - /* - Reenable any breakpoints we disabled so we could execute the breakpointed - instructions. - */ - KdbRenableBreakPoints(); - /* Silently ignore a debugger initiated single step. */ - if (ExpNr == 1 && KdbIgnoreNextSingleStep) - { - KdbIgnoreNextSingleStep = FALSE; - KdbEntryCount--; - return(kdContinue); - } - /* If we stopped on one of our breakpoints then let the user know. */ - if (ExpNr == 3 && (BreakPointNr = KdbIsBreakPointOurs(TrapFrame)) >= 0) - { - DbgPrint("Entered debugger on breakpoint %d.\n", BreakPointNr); + /* If we stopped on one of our breakpoints then let the user know. */ + KdbLastBreakPointNr = -1; + KdbEnteredOnSingleStep = FALSE; + + if (FirstChance && (ExpNr == 1 || ExpNr == 3) && + (KdbLastBreakPointNr = KdbpIsBreakPointOurs(ExpNr, TrapFrame)) >= 0) + { + BreakPoint = KdbBreakPoints + KdbLastBreakPointNr; + + if (ExpNr == 3) + { + /* + * The breakpoint will point to the next instruction by default so + * point it back to the start of original instruction. + */ + TrapFrame->Eip--; + + /* + * ... and restore the original instruction. + */ + if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address, + BreakPoint->Data.SavedInstruction, NULL))) + { + DbgPrint("Couldn't restore original instruction after INT3! Cannot continue execution.\n"); + KEBUGCHECK(0); + } + } + + if ((BreakPoint->Type == KdbBreakPointHardware) && + (BreakPoint->Data.Hw.AccessType == KdbAccessExec)) + { + Resume = TRUE; /* Set the resume flag when continuing execution */ + } + /* - The breakpoint will point to the next instruction by default so - point it back to the start of original instruction. - */ - TrapFrame->Eip--; + * When a temporary breakpoint is hit we have to make sure that we are + * in the same context in which it was set, otherwise it could happen + * that another process/thread hits it before and it gets deleted. + */ + else if (BreakPoint->Type == KdbBreakPointTemporary && + BreakPoint->Process == KdbCurrentProcess) + { + ASSERT((TrapFrame->Eflags & X86_EFLAGS_TF) == 0); + + /* + * Delete the temporary breakpoint which was used to step over or into the instruction. + */ + KdbpDeleteBreakPoint(-1, BreakPoint); + + if (--KdbNumSingleSteps > 0) + { + if ((KdbSingleStepOver && !KdbpStepOverInstruction(TrapFrame->Eip)) || + (!KdbSingleStepOver && !KdbpStepIntoInstruction(TrapFrame->Eip))) + { + TrapFrame->Eflags |= X86_EFLAGS_TF; + } + goto continue_execution; /* return */ + } + + KdbEnteredOnSingleStep = TRUE; + } + /* - ..and restore the original instruction. - */ - (VOID)KdbOverwriteInst(TrapFrame->Eip, NULL, - KdbActiveBreakPoints[BreakPointNr].SavedInst); + * If we hit a breakpoint set by the debugger we set the single step flag, + * ignore the next single step and reenable the breakpoint. + */ + else if (BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary) + { + ASSERT(ExpNr == 3); + TrapFrame->Eflags |= X86_EFLAGS_TF; + KdbBreakPointToReenable = BreakPoint; + } + /* - If this was a breakpoint set by the debugger then delete it otherwise - flag to enable it again after we step over this instruction. - */ - if (KdbActiveBreakPoints[BreakPointNr].Temporary) - { - KdbActiveBreakPoints[BreakPointNr].Assigned = FALSE; - KdbBreakPointCount--; - KdbEnteredOnSingleStep = TRUE; - } + * Make sure that the breakpoint should be triggered in this context + */ + if (!BreakPoint->Global && BreakPoint->Process != KdbCurrentProcess) + { + goto continue_execution; /* return */ + } + + /* + * Check if the condition for the breakpoint is met. + */ + if (BreakPoint->Condition != NULL) + { + /* Setup the KDB trap frame */ + RtlCopyMemory(&KdbTrapFrame.Tf, TrapFrame, sizeof (KTRAP_FRAME)); + asm volatile( + "movl %%cr0, %0" "\n\t" + "movl %%cr2, %1" "\n\t" + "movl %%cr3, %2" "\n\t" + "movl %%cr4, %3" "\n\t" + : "=r"(KdbTrapFrame.Cr0), "=r"(KdbTrapFrame.Cr2), + "=r"(KdbTrapFrame.Cr3), "=r"(KdbTrapFrame.Cr4)); + + ull = 0; + if (!KdbpRpnEvaluateParsedExpression(BreakPoint->Condition, &KdbTrapFrame, &ull, NULL, NULL)) + { + /* FIXME: Print warning? */ + } + else if (ull == 0) /* condition is not met */ + { + goto continue_execution; /* return */ + } + } + + if (BreakPoint->Type == KdbBreakPointSoftware) + { + DbgPrint("Entered debugger on breakpoint #%d: EXEC 0x%04x:0x%08x\n", + KdbLastBreakPointNr, TrapFrame->Cs & 0xffff, TrapFrame->Eip); + } + else if (BreakPoint->Type == KdbBreakPointHardware) + { + DbgPrint("Entered debugger on breakpoint #%d: %s 0x%08x\n", + KdbLastBreakPointNr, + (BreakPoint->Data.Hw.AccessType == KdbAccessRead) ? "READ" : + ((BreakPoint->Data.Hw.AccessType == KdbAccessWrite) ? "WRITE" : + ((BreakPoint->Data.Hw.AccessType == KdbAccessReadWrite) ? "RDWR" : "EXEC") + ), + BreakPoint->Address + ); + + } + } + else if (ExpNr == 1) + { + /* Silently ignore a debugger initiated single step. */ + if ((TrapFrame->Dr6 & 0xf) == 0 && KdbBreakPointToReenable != NULL) + { + /* FIXME: Make sure that the breakpoint was really hit (check bp->Address vs. tf->Eip) */ + BreakPoint = KdbBreakPointToReenable; + KdbBreakPointToReenable = NULL; + ASSERT(BreakPoint->Type == KdbBreakPointSoftware || + BreakPoint->Type == KdbBreakPointTemporary); + + /* + * Reenable the breakpoint we disabled to execute the breakpointed + * instruction. + */ + if (!NT_SUCCESS(KdbpOverwriteInstruction(KdbCurrentProcess, BreakPoint->Address, 0xCC, + &BreakPoint->Data.SavedInstruction))) + { + DbgPrint("Warning: Couldn't reenable breakpoint %d\n", + BreakPoint - KdbBreakPoints); + } + + /* Unset TF if we are no longer single stepping. */ + if (KdbNumSingleSteps == 0) + TrapFrame->Eflags &= ~X86_EFLAGS_TF; + goto continue_execution; /* return */ + } + + /* Check if we expect a single step */ + if ((TrapFrame->Dr6 & 0xf) == 0 && KdbNumSingleSteps > 0) + { + /*ASSERT((TrapFrame->Eflags & X86_EFLAGS_TF) != 0);*/ + if (--KdbNumSingleSteps > 0) + { + if ((KdbSingleStepOver && KdbpStepOverInstruction(TrapFrame->Eip)) || + (!KdbSingleStepOver && KdbpStepIntoInstruction(TrapFrame->Eip))) + { + TrapFrame->Eflags &= ~X86_EFLAGS_TF; + } + else + { + TrapFrame->Eflags |= X86_EFLAGS_TF; + } + goto continue_execution; /* return */ + } + + TrapFrame->Eflags &= ~X86_EFLAGS_TF; + KdbEnteredOnSingleStep = TRUE; + } else - { - KdbActiveBreakPoints[BreakPointNr].Enabled = FALSE; - TrapFrame->Eflags |= (1 << 8); - KdbIgnoreNextSingleStep = TRUE; - } - } - else if (ExpNr == 1) - { - if ((TrapFrame->Dr6 & 0xF) != 0) - { - DbgPrint("Entered debugger on memory breakpoint(s) %s%s%s%s.\n", - (TrapFrame->Dr6 & 0x1) ? "1" : "", - (TrapFrame->Dr6 & 0x2) ? "2" : "", - (TrapFrame->Dr6 & 0x4) ? "3" : "", - (TrapFrame->Dr6 & 0x8) ? "4" : ""); - } - else if (KdbLastSingleStepFrom != 0xFFFFFFFF) - { - KdbEnteredOnSingleStep = TRUE; - } + { + if (!EnterConditionMet) + { + InterlockedDecrement(&KdbEntryCount); + return ContinueType; + } + DbgPrint("Entered debugger on unexpected debug trap!\n"); + } + } + else if (ExpNr == 3) + { + if (KdbInitFileBuffer != NULL) + { + KdbpCliInterpretInitFile(); + EnterConditionMet = FALSE; + } + if (!EnterConditionMet) + { + InterlockedDecrement(&KdbEntryCount); + return ContinueType; + } + + DbgPrint("Entered debugger on embedded INT3 at 0x%04x:0x%08x.\n", + TrapFrame->Cs & 0xffff, TrapFrame->Eip - 1); + } + else + { + CONST PCHAR ExceptionString = (ExpNr < RTL_NUMBER_OF(ExceptionNrToString)) ? + (ExceptionNrToString[ExpNr]) : + ("Unknown/User defined exception"); + + if (!EnterConditionMet) + { + InterlockedDecrement(&KdbEntryCount); + return ContinueType; + } + + DbgPrint("Entered debugger on %s-chance exception number %d (%s)\n", + FirstChance ? "first" : "last", ExpNr, ExceptionString); + if (ExpNr == 14) + { + /* FIXME: Add noexec memory stuff */ + ULONG Cr2, Err; + asm volatile("movl %%cr2, %0" : "=r"(Cr2)); + Err = TrapFrame->ErrorCode; + DbgPrint("Memory at 0x%x could not be %s: ", Cr2, (Err & (1 << 1)) ? "written" : "read"); + if ((Err & (1 << 0)) == 0) + DbgPrint("Page not present.\n"); + else + { + if ((Err & (1 << 3)) != 0) + DbgPrint("Reserved bits in page directory set.\n"); + else + DbgPrint("Page protection violation.\n"); + } + } + } + + /* Once we enter the debugger we do not expect any more single steps to happen */ + KdbNumSingleSteps = 0; + + /* Update the current process pointer */ + KdbCurrentProcess = KdbOriginalProcess = PsGetCurrentProcess(); + KdbCurrentThread = KdbOriginalThread = PsGetCurrentThread(); + KdbCurrentTrapFrame = &KdbTrapFrame; + + /* Setup the KDB trap frame */ + RtlCopyMemory(&KdbTrapFrame.Tf, TrapFrame, sizeof(KTRAP_FRAME)); + asm volatile( + "movl %%cr0, %0" "\n\t" + "movl %%cr2, %1" "\n\t" + "movl %%cr3, %2" "\n\t" + "movl %%cr4, %3" "\n\t" + : "=r"(KdbTrapFrame.Cr0), "=r"(KdbTrapFrame.Cr2), + "=r"(KdbTrapFrame.Cr3), "=r"(KdbTrapFrame.Cr4)); + + /* Enter critical section */ + Ke386SaveFlags(OldEflags); + Ke386DisableInterrupts(); + + /* Call the main loop. */ + KdbpInternalEnter(); + + /* Check if we should single step */ + if (KdbNumSingleSteps > 0) + { + if ((KdbSingleStepOver && KdbpStepOverInstruction(KdbCurrentTrapFrame->Tf.Eip)) || + (!KdbSingleStepOver && KdbpStepIntoInstruction(KdbCurrentTrapFrame->Tf.Eip))) + { + ASSERT((KdbCurrentTrapFrame->Tf.Eflags & X86_EFLAGS_TF) == 0); + /*KdbCurrentTrapFrame->Tf.Eflags &= ~X86_EFLAGS_TF;*/ + } else - { - DbgPrint("Entered debugger on unexpected debug trap.\n"); - } - } - else - { - const char *ExceptionString = - (ExpNr < (sizeof (ExceptionTypeStrings) / sizeof (ExceptionTypeStrings[0]))) ? - (ExceptionTypeStrings[ExpNr]) : - ("Unknown/User defined exception"); - DbgPrint("Entered debugger on exception number %d (%s)\n", ExpNr, ExceptionString); - } - KdbInternalEnter(TrapFrame); - KdbEntryCount--; - if (ExpNr != 1 && ExpNr != 3) - { - return(kdHandleException); - } - else - { + { + KdbCurrentTrapFrame->Tf.Eflags |= X86_EFLAGS_TF; + } + } + + /* Save the current thread's trapframe */ + if (KdbCurrentTrapFrame == &KdbThreadTrapFrame) + { + RtlCopyMemory(KdbCurrentThread->Tcb.TrapFrame, KdbCurrentTrapFrame, sizeof (KTRAP_FRAME)); + } + + /* Detach from attached process */ + if (KdbCurrentProcess != KdbOriginalProcess) + { + KeUnstackDetachProcess(&KdbApcState); + } + + /* Update the exception TrapFrame */ + RtlCopyMemory(TrapFrame, &KdbTrapFrame.Tf, sizeof(KTRAP_FRAME)); +#if 0 + asm volatile( + "movl %0, %%cr0" "\n\t" + "movl %1, %%cr2" "\n\t" + "movl %2, %%cr3" "\n\t" + "movl %3, %%cr4" "\n\t" + : : "r"(KdbTrapFrame.Cr0), "r"(KdbTrapFrame.Cr2), + "r"(KdbTrapFrame.Cr3), "r"(KdbTrapFrame.Cr4)); +#endif + + /* Leave critical section */ + Ke386RestoreFlags(OldEflags); + +continue_execution: + /* Clear debug status */ + if (ExpNr == 1 || ExpNr == 3) /* FIXME: Why clear DR6 on INT3? */ + { + /* Set the RF flag so we don't trigger the same breakpoint again. */ + if (Resume) + { + TrapFrame->Eflags |= X86_EFLAGS_RF; + } + /* Clear dr6 status flags. */ - TrapFrame->Dr6 &= 0xFFFF1F00; - /* Set the RF flag to we don't trigger the same breakpoint again. */ - if (ExpNr == 1) - { - TrapFrame->Eflags |= (1 << 16); - } - return(kdContinue); - } + TrapFrame->Dr6 &= ~0x0000e00f; + + } + + InterlockedDecrement(&KdbEntryCount); + return ContinueType; +} + +VOID +KdbInit() +{ + KdbpCliInit(); +} + +VOID +KdbDeleteProcessHook(IN PEPROCESS Process) +{ + KdbSymFreeProcessSymbols(Process); + + /* FIXME: Delete breakpoints for process */ } VOID KdbModuleLoaded(IN PUNICODE_STRING Name) { - if (!KdbBreakOnModuleLoad) - return; - - DbgPrint("Module %wZ loaded.\n", Name); - DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C); + KdbpCliModuleLoaded(Name); } + diff --git a/reactos/ntoskrnl/dbg/kdb.h b/reactos/ntoskrnl/dbg/kdb.h index 46c08762505..8f08613742d 100644 --- a/reactos/ntoskrnl/dbg/kdb.h +++ b/reactos/ntoskrnl/dbg/kdb.h @@ -1,6 +1,82 @@ +#ifndef NTOSKRNL_KDB_H +#define NTOSKRNL_KDB_H + +/* INCLUDES ******************************************************************/ + #define NTOS_MODE_KERNEL #include +#include + +/* DEFINES *******************************************************************/ + +#define TAG_KDBG (('K' << 24) | ('D' << 16) | ('B' << 8) | 'G') + +#ifndef RTL_NUMBER_OF +# define RTL_NUMBER_OF(x) (sizeof(x) / sizeof((x)[0])) +#endif + + +/* TYPES *********************************************************************/ + +/* from kdb.c */ +typedef struct _KDB_KTRAP_FRAME +{ + KTRAP_FRAME Tf; + ULONG Cr0; + ULONG Cr1; /* reserved/unused */ + ULONG Cr2; + ULONG Cr3; + ULONG Cr4; +} KDB_KTRAP_FRAME, *PKDB_KTRAP_FRAME; + +typedef enum _KDB_BREAKPOINT_TYPE +{ + KdbBreakPointNone = 0, + KdbBreakPointSoftware, + KdbBreakPointHardware, + KdbBreakPointTemporary +} KDB_BREAKPOINT_TYPE; + +typedef enum _KDB_ACCESS_TYPE +{ + KdbAccessRead, + KdbAccessWrite, + KdbAccessReadWrite, + KdbAccessExec +} KDB_ACCESS_TYPE; + +typedef struct _KDB_BREAKPOINT +{ + KDB_BREAKPOINT_TYPE Type; /* Type of breakpoint */ + BOOLEAN Enabled; /* Whether the bp is enabled */ + ULONG_PTR Address; /* Address of the breakpoint */ + BOOLEAN Global; /* Whether the breakpoint is global or local to a process */ + PEPROCESS Process; /* Owning process */ + PCHAR ConditionExpression; + PVOID Condition; + union { + /* KdbBreakPointSoftware */ + UCHAR SavedInstruction; + /* KdbBreakPointHardware */ + struct { + UCHAR DebugReg : 2; + UCHAR Size : 3; + KDB_ACCESS_TYPE AccessType; + } Hw; + } Data; +} KDB_BREAKPOINT, *PKDB_BREAKPOINT; + +typedef enum _KDB_ENTER_CONDITION +{ + KdbDoNotEnter, + KdbEnterAlways, + KdbEnterFromKmode, + KdbEnterFromUmode +} KDB_ENTER_CONDITION; + + +/* from kdb_symbols.c */ typedef struct _KDB_MODULE_INFO { WCHAR Name[256]; @@ -9,6 +85,74 @@ typedef struct _KDB_MODULE_INFO PROSSYM_INFO RosSymInfo; } KDB_MODULE_INFO, *PKDB_MODULE_INFO; + +/* FUNCTIONS *****************************************************************/ + +/* from i386/i386-dis.c */ + +LONG +KdbpDisassemble( + IN ULONG Address, + IN ULONG IntelSyntax); + +LONG +KdbpGetInstLength( + IN ULONG Address); + +/* from i386/kdb_help.S */ + +STDCALL VOID +KdbpStackSwitchAndCall( + IN PVOID NewStack, + IN VOID (*Function)(VOID)); + +/* from kdb_cli.c */ + +extern PCHAR KdbInitFileBuffer; + +VOID +KdbpCliInit(); + +VOID +KdbpCliMainLoop( + IN BOOLEAN EnteredOnSingleStep); + +VOID +KdbpCliModuleLoaded( + IN PUNICODE_STRING Name); + +VOID +KdbpCliInterpretInitFile(); + +VOID +KdbpPrint( + IN PCHAR Format, + IN ... OPTIONAL); + +/* from kdb_expr.c */ + +BOOLEAN +KdbpRpnEvaluateExpression( + IN PCHAR Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL); + +PVOID +KdbpRpnParseExpression( + IN PCHAR Expression, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL); + +BOOLEAN +KdbpRpnEvaluateParsedExpression( + IN PVOID Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL); + /* from kdb_symbols.c */ BOOLEAN @@ -33,23 +177,86 @@ KdbSymGetAddressInformation(IN PROSSYM_INFO RosSymInfo, OUT PCH FileName OPTIONAL, OUT PCH FunctionName OPTIONAL); -/* other functions */ -#define KdbpSafeReadMemory(dst, src, size) MmSafeCopyFromUser(dst, src, size) -#define KdbpSafeWriteMemory(dst, src, size) MmSafeCopyToUser(dst, src, size) -CHAR -KdbTryGetCharKeyboard(PULONG ScanCode); -ULONG -KdbTryGetCharSerial(VOID); +/* from kdb.c */ + +extern PEPROCESS KdbCurrentProcess; +extern PETHREAD KdbCurrentThread; +extern LONG KdbLastBreakPointNr; +extern ULONG KdbNumSingleSteps; +extern BOOLEAN KdbSingleStepOver; +extern PKDB_KTRAP_FRAME KdbCurrentTrapFrame; + VOID -KdbEnter(VOID); +KdbInit(); + VOID -DbgRDebugInit(VOID); -VOID -DbgShowFiles(VOID); -VOID -DbgEnableFile(PCH Filename); -VOID -DbgDisableFile(PCH Filename); +KdbModuleLoaded( + IN PUNICODE_STRING Name); + +LONG +KdbpGetNextBreakPointNr( + IN ULONG Start OPTIONAL); + +BOOLEAN +KdbpGetBreakPointInfo( + IN ULONG BreakPointNr, + OUT ULONG_PTR *Address OPTIONAL, + OUT KDB_BREAKPOINT_TYPE *Type OPTIONAL, + OUT UCHAR *Size OPTIONAL, + OUT KDB_ACCESS_TYPE *AccessType OPTIONAL, + OUT UCHAR *DebugReg OPTIONAL, + OUT BOOLEAN *Enabled OPTIONAL, + OUT BOOLEAN *Global OPTIONAL, + OUT PEPROCESS *Process OPTIONAL, + OUT PCHAR *ConditionExpression OPTIONAL); + +NTSTATUS +KdbpInsertBreakPoint( + IN ULONG_PTR Address, + IN KDB_BREAKPOINT_TYPE Type, + IN UCHAR Size OPTIONAL, + IN KDB_ACCESS_TYPE AccessType OPTIONAL, + IN PCHAR ConditionExpression OPTIONAL, + IN BOOLEAN Global, + OUT PULONG BreakPointNumber OPTIONAL); + +BOOLEAN +KdbpDeleteBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL); + +BOOLEAN +KdbpEnableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL); + +BOOLEAN +KdbpDisableBreakPoint( + IN LONG BreakPointNr OPTIONAL, + IN OUT PKDB_BREAKPOINT BreakPoint OPTIONAL); + +BOOLEAN +KdbpGetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + OUT KDB_ENTER_CONDITION *Condition); + +BOOLEAN +KdbpSetEnterCondition( + IN LONG ExceptionNr, + IN BOOLEAN FirstChance, + IN KDB_ENTER_CONDITION Condition); + +BOOLEAN +KdbpAttachToThread( + PVOID ThreadId); + +BOOLEAN +KdbpAttachToProcess( + PVOID ProcessId); + +/* from profile.c */ + VOID KdbInitProfiling(); VOID @@ -61,13 +268,25 @@ KdbEnableProfiling(); VOID KdbProfileInterrupt(ULONG_PTR Eip); +/* other functions */ + +#define KdbpSafeReadMemory(dst, src, size) MmSafeCopyFromUser(dst, src, size) +#define KdbpSafeWriteMemory(dst, src, size) MmSafeCopyToUser(dst, src, size) +CHAR +KdbpTryGetCharKeyboard(PULONG ScanCode); +ULONG +KdbpTryGetCharSerial(VOID); VOID -KdbModuleLoaded(IN PUNICODE_STRING Name); +KdbEnter(VOID); +VOID +DbgRDebugInit(VOID); +VOID +DbgShowFiles(VOID); +VOID +DbgEnableFile(PCH Filename); +VOID +DbgDisableFile(PCH Filename); -struct KDB_BPINFO { - DWORD Addr; - DWORD Type; - DWORD Size; - DWORD Enabled; -}; +#endif /* NTOSKRNL_KDB_H */ + diff --git a/reactos/ntoskrnl/dbg/kdb_cli.c b/reactos/ntoskrnl/dbg/kdb_cli.c new file mode 100644 index 00000000000..318a4a35793 --- /dev/null +++ b/reactos/ntoskrnl/dbg/kdb_cli.c @@ -0,0 +1,2307 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/dbg/kdb_cli.c + * PURPOSE: Kernel debugger command line interface + * PROGRAMMER: Gregor Anich (blight@blight.eu.org) + * UPDATE HISTORY: + * Created 16/01/2005 + */ + +/* INCLUDES ******************************************************************/ + +#include +#include "kdb.h" +#define NDEBUG +#include + +/* DEFINES *******************************************************************/ + +#define KEY_BS 8 +#define KEY_ESC 27 +#define KEY_DEL 127 + +#define KEY_SCAN_UP 72 +#define KEY_SCAN_DOWN 80 + +#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_INVALID ? "Invalid" : \ + ((state) == NPX_STATE_VALID ? "Valid" : \ + ((state) == NPX_STATE_DIRTY ? "Dirty" : "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 KdbpCmdSet(ULONG Argc, PCHAR Argv[]); +STATIC BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]); + +/* GLOBALS *******************************************************************/ + +STATIC BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */ + +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 LONG KdbNumberOfRowsTerminal = -1; +STATIC LONG KdbNumberOfColsTerminal = -1; + +PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDB.init file is loaded into during initialization */ + +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 }, + + /* 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 }, + { "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet }, + { "help", "help", "Display help screen.", KdbpCmdHelp } +}; + +/* FUNCTIONS *****************************************************************/ + +/*!\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; +} + +/*!\brief Evaluates an expression and displays the result. + */ +STATIC BOOLEAN +KdbpCmdEvalExpression(ULONG Argc, PCHAR Argv[]) +{ + INT 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; +} + +/*!\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"); + 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)) + 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)) + KdbpPrint("<%08x>: ", Address); + else + KdbpPrint(": "); + InstLen = KdbpDisassemble(Address, KdbUseIntelSyntax); + if (InstLen < 0) + { + KdbpPrint("\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->Cs & 0xFFFF, Tf->Eip, + Tf->Ss, Tf->Esp, + 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; + struct __attribute__((packed)) { + USHORT Limit; + ULONG Base; + } Gdtr, Ldtr, Idtr; + ULONG Tr; + 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 */ + asm volatile("sgdt %0" : : "m"(Gdtr)); + asm volatile("sldt %0" : : "m"(Ldtr)); + asm volatile("sidt %0" : : "m"(Idtr)); + + /* Get the task register */ + asm volatile("str %0" : "=g"(Tr)); + + /* Display the control registers */ + KdbpPrint("CR0 0x%08x ", Cr0); + for (i = 0; i < 32; i++) + { + if (Cr0Bits[i] == NULL) + 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] == NULL) + 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 Base 0x%08x Size 0x%04x\n", Ldtr.Base, Ldtr.Limit); + 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->Cs & 0xffff, (Tf->Cs & 0xffff) >> 3, + (Tf->Cs & (1 << 2)) ? 'L' : 'G', Tf->Cs & 3); + KdbpPrint("DS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Ds, Tf->Ds >> 3, (Tf->Ds & (1 << 2)) ? 'L' : 'G', Tf->Ds & 3); + KdbpPrint("ES 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Es, Tf->Es >> 3, (Tf->Es & (1 << 2)) ? 'L' : 'G', Tf->Es & 3); + KdbpPrint("FS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Fs, Tf->Fs >> 3, (Tf->Fs & (1 << 2)) ? 'L' : 'G', Tf->Fs & 3); + KdbpPrint("GS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Gs, Tf->Gs >> 3, (Tf->Gs & (1 << 2)) ? 'L' : 'G', Tf->Gs & 3); + KdbpPrint("SS 0x%04x Index 0x%04x %cDT RPL%d\n", + Tf->Ss, Tf->Ss >> 3, (Tf->Ss & (1 << 2)) ? 'L' : 'G', Tf->Ss & 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; +} + +/*!\brief Displays a backtrace. + */ +STATIC BOOLEAN +KdbpCmdBackTrace(ULONG Argc, PCHAR Argv[]) +{ + ULONG Count; + ULONG ul; + ULONGLONG Result = 0; + ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp; + ULONG_PTR Address; + + 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++; + } + + /* 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"); + Frame = (ULONG_PTR)Result; + } + else + { + + KdbpPrint("Thread backtrace not supported yet!\n"); + return TRUE; + } + } + + KdbpPrint("Frames:\n"); + while (Frame != 0) + { + if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, (PVOID)(Frame + sizeof(ULONG_PTR)), sizeof (ULONG_PTR)))) + { + KdbpPrint("Couldn't access memory at 0x%x!\n", Frame + sizeof(ULONG_PTR)); + break; + } + if (!KdbSymPrintAddress((PVOID)Address)) + KdbpPrint("<%08x>\n", Address); + else + KdbpPrint("\n"); + if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR)))) + { + KdbpPrint("Couldn't access memory at 0x%x!\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; + INT AddressArgIndex, ConditionArgIndex, i; + 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 (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]); + 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; + PULONG Esp; + PULONG Ebp; + ULONG Eip, ul; + PCHAR State, pend, str1, str2; + STATIC CONST PCHAR ThreadStateToString[THREAD_STATE_MAX] = + { "Initialized", "Ready", "Running", + "Suspended", "Frozen", "Terminated1", + "Terminated2", "Blocked" }; + ASSERT(KdbCurrentProcess != NULL); + + 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; + } + } + + 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"); + 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.TrapFrame != NULL) + { + Esp = (PULONG)Thread->Tcb.TrapFrame->Esp; + 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 != NULL) /* FIXME: Should we attach to the process to read Ebp[1]? */ + KdbpSafeReadMemory(&Eip, Ebp + 1, sizeof (Eip));; + } + if (Thread->Tcb.State < THREAD_STATE_MAX) + 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); + } + 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; + } + } + + if (Thread->Tcb.State < THREAD_STATE_MAX) + 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); + + } + + return TRUE; +} + +/*!\brief Lists processes or switches to another process context. + */ +STATIC BOOLEAN +KdbpCmdProc(ULONG Argc, PCHAR Argv[]) +{ + PLIST_ENTRY Entry; + PEPROCESS Process; + PCHAR State, pend, str1, str2; + ULONG ul; + extern LIST_ENTRY PsActiveProcessHead; + + if (Argc >= 2 && _stricmp(Argv[1], "list") == 0) + { + Entry = PsActiveProcessHead.Flink; + if (Entry == &PsActiveProcessHead) + { + KdbpPrint("No processes in the system!\n"); + return TRUE; + } + + KdbpPrint(" PID State Filename\n"); + do + { + Process = CONTAINING_RECORD(Entry, EPROCESS, ProcessListEntry); + + if (Process == KdbCurrentProcess) + { + str1 = "\x1b[1m*"; + str2 = "\x1b[0m"; + } + else + { + str1 = " "; + str2 = ""; + } + + State = ((Process->Pcb.State == PROCESS_STATE_TERMINATED) ? "Terminated" : + ((Process->Pcb.State == PROCESS_STATE_ACTIVE) ? "Active" : "Unknown")); + + 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", (UINT)ul, + (UINT)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; + } + } + + State = ((Process->Pcb.State == PROCESS_STATE_TERMINATED) ? "Terminated" : + ((Process->Pcb.State == PROCESS_STATE_ACTIVE) ? "Active" : "Unknown")); + 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); + } + + 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; + KDB_MODULE_INFO Info; + 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]); + Address = (ULONG_PTR)Result; + + if (!KdbpSymFindModuleByAddress((PVOID)Address, &Info)) + { + KdbpPrint("No module containing address 0x%x found!\n", Address); + return TRUE; + } + DisplayOnlyOneModule = TRUE; + } + else + { + if (!KdbpSymFindModuleByIndex(0, &Info)) + { + KdbpPrint("No modules.\n"); + return TRUE; + } + i = 1; + } + + KdbpPrint(" Base Size Name\n"); + for (;;) + { + KdbpPrint(" %08x %08x %ws\n", Info.Base, Info.Size, Info.Name); + + if ((!DisplayOnlyOneModule && !KdbpSymFindModuleByIndex(i++, &Info)) || + DisplayOnlyOneModule) + { + break; + } + } + + return TRUE; +} + +/*!\brief Displays GDT, LDT or IDTd. + */ +STATIC BOOLEAN +KdbpCmdGdtLdtIdt(ULONG Argc, PCHAR Argv[]) +{ + struct __attribute__((packed)) { + USHORT Limit; + ULONG Base; + } 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 */ + asm volatile("sidt %0" : : "m"(Reg)); + + 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 + 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; + } + + 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"; + + 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 */ + asm volatile("sgdt %0" : : "m"(Reg)); + i = 8; + } + else + { + ASSERT(Argv[0][0] == 'l'); + /* Read LDTR */ + asm volatile("sldt %0" : : "m"(Reg)); + 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[]) +{ + PKPCR Pcr = KeGetCurrentKPCR(); + + 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" + " Self: 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" + " DebugActive: 0x%02x\n" + " ProcessorNumber: 0x%02x\n" + " L2CacheAssociativity: 0x%02x\n" + " VdmAlert: 0x%08x\n" + " L2CacheSize: 0x%08x\n" + " InterruptMode: 0x%08x\n", + Pcr->Tib.ExceptionList, Pcr->Tib.StackBase, Pcr->Tib.StackLimit, + Pcr->Tib.SubSystemTib, Pcr->Tib.FiberData, Pcr->Tib.ArbitraryUserPointer, + Pcr->Tib.Self, Pcr->Self, Pcr->PCRCB, 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->DebugActive, Pcr->ProcessorNumber, Pcr->L2CacheAssociativity, + Pcr->VdmAlert, Pcr->L2CacheSize, Pcr->InterruptMode); + + return TRUE; +} + +/*!\brief Displays the TSS + */ +STATIC BOOLEAN +KdbpCmdTss(ULONG Argc, PCHAR Argv[]) +{ + KTSS *Tss = KeGetCurrentKPCR()->TSS; + + KdbpPrint("Current TSS is at 0x%08x.\n", (INT)Tss); + KdbpPrint(" PreviousTask: 0x%08x\n" + " Ss0:Esp0: 0x%04x:0x%08x\n" + " Ss1:Esp1: 0x%04x:0x%08x\n" + " Ss2:Esp2: 0x%04x:0x%08x\n" + " Cr3: 0x%08x\n" + " Eip: 0x%08x\n" + " Eflags: 0x%08x\n" + " Eax: 0x%08x\n" + " Ecx: 0x%08x\n" + " Edx: 0x%08x\n" + " Ebx: 0x%08x\n" + " Esp: 0x%08x\n" + " Ebp: 0x%08x\n" + " Esi: 0x%08x\n" + " Edi: 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" + " Ldt: 0x%04x\n" + " Trap: 0x%04x\n" + " IoMapBase: 0x%04x\n", + Tss->PreviousTask, Tss->Ss0, Tss->Esp0, Tss->Ss1, Tss->Esp1, + Tss->Ss2, Tss->Esp2, Tss->Cr3, Tss->Eip, Tss->Eflags, Tss->Eax, + Tss->Ecx, Tss->Edx, Tss->Ebx, Tss->Esp, Tss->Ebp, Tss->Esi, + Tss->Edi, Tss->Es, Tss->Cs, Tss->Ss, Tss->Ds, Tss->Fs, Tss->Gs, + Tss->Ldt, Tss->Trap, Tss->IoMapBase); + return TRUE; +} + +/*!\brief Bugchecks the system. + */ +STATIC BOOLEAN +KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]) +{ + KEBUGCHECK(0xDEADDEAD); + 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"); + } + 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] == NULL) + 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 = (LONG)strtoul(Argv[2], &pend, 0); + if (Argv[2] == pend) + { + for (l = 0; l < RTL_NUMBER_OF(ExceptionNames); l++) + { + if (ExceptionNames[l] == NULL) + 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 + 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 == NULL) /* 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. + */ +VOID +KdbpPrint( + IN PCHAR Format, + IN ... OPTIONAL) +{ + STATIC CHAR Buffer[4096]; + STATIC BOOLEAN TerminalInitialized = FALSE; + STATIC BOOLEAN TerminalReportsSize = TRUE; + CHAR c; + PCHAR p; + INT Length; + INT i; + INT RowsPrintedByTerminal; + 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 */ + TerminalInitialized = 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 ((KdDebugState & KD_DEBUG_KDSERIAL) && TerminalReportsSize) + { + /* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */ + TerminalReportsSize = FALSE; + DbgPrint("\x1b[18t"); + i = 10; + while ((i-- > 0) && ((c = KdbpTryGetCharSerial()) == -1)); + if (c == KEY_ESC) + { + i = 5; + while ((i-- > 0) && ((c = KdbpTryGetCharSerial()) == -1)); + if (c == '[') + { + Length = 0; + for (;;) + { + i = 5; + while ((i-- > 0) && ((c = KdbpTryGetCharSerial()) == -1)); + 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; + } + } + } + } + } + + 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 */ + 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 ((KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal) + { + if (KdbNumberOfColsPrinted > 0) + DbgPrint("\n"); + DbgPrint("--- Press q to abort, any other key to continue ---"); + while ((c = KdbpTryGetCharSerial()) == -1); + if (c == '\r') + { + /* Ignore \r and wait for \n or another \r - if \n is not received here + * it will be interpreted as "return" when the next command should be read. + */ + while ((c = KdbpTryGetCharSerial()) == -1); + } + 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'; + } + + 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; + } +} + +/*!\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) +{ + LONG Length1 = strlen(Command) + 1; + LONG Length2 = 0; + INT i; + PCHAR Buffer; + + ASSERT(Length1 <= RTL_NUMBER_OF(KdbCommandHistoryBuffer)); + + if (Length1 <= 1 || + (KdbCommandHistory[KdbCommandHistoryIndex] != NULL && + strcmp(KdbCommandHistory[KdbCommandHistoryIndex], Command) == 0)) + { + return; + } + + /* Calculate Length1 and Length2 */ + Buffer = KdbCommandHistoryBuffer + KdbCommandHistoryBufferIndex; + KdbCommandHistoryBufferIndex += Length1; + if (KdbCommandHistoryBufferIndex >= 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] != NULL;) + { + 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] != NULL) + { + 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 LastKey = '\0'; + INT CmdHistIndex = -1; + INT i; + + EchoOn = !((KdDebugState & KD_DEBUG_KDNOECHO) != 0); + + for (;;) + { + if (KdDebugState & KD_DEBUG_KDSERIAL) + { + while ((Key = KdbpTryGetCharSerial()) == -1); + ScanCode = 0; + if (Key == KEY_ESC) /* ESC */ + { + while ((Key = KdbpTryGetCharSerial()) == -1); + if (Key == '[') + { + while ((Key = KdbpTryGetCharSerial()) == -1); + switch (Key) + { + case 'A': + ScanCode = KEY_SCAN_UP; + break; + case 'B': + ScanCode = KEY_SCAN_DOWN; + break; + case 'C': + break; + case 'D': + break; + } + } + } + } + else + while ((Key = KdbpTryGetCharKeyboard(&ScanCode)) == -1); + + if ((Buffer - Orig) >= (Size - 1)) + { + /* Buffer is full, accept only newlines */ + if (Key != '\n') + continue; + } + + if (Key == '\r') + { + /* Ignore this key... */ + } + else if (Key == '\n') + { + DbgPrint("\n"); + /* + * Repeat the last command if the user presses enter. Reduces the + * risk of RSI when single-stepping. + */ + if (Buffer == Orig) + { + strncpy(Buffer, LastCommand, Size); + Buffer[Size - 1] = '\0'; + } + else + { + *Buffer = '\0'; + strncpy(LastCommand, Orig, sizeof (LastCommand)); + LastCommand[sizeof (LastCommand) - 1] = '\0'; + } + LastKey = Key; + return; + } + else if (Key == KEY_BS || Key == KEY_DEL) + { + if (Buffer > Orig) + { + Buffer--; + *Buffer = 0; + if (EchoOn) + DbgPrint("%c %c", KEY_BS, KEY_BS); + else + DbgPrint(" %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] != NULL && i != KdbCommandHistoryIndex) + CmdHistIndex = i; + else + Print = FALSE; + } + if (Print && KdbCommandHistory[CmdHistIndex] != NULL) + { + while (Buffer > Orig) + { + Buffer--; + *Buffer = 0; + if (EchoOn) + DbgPrint("%c %c", KEY_BS, KEY_BS); + else + DbgPrint(" %c", KEY_BS); + } + i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1); + memcpy(Orig, KdbCommandHistory[CmdHistIndex], i); + Orig[i] = '\0'; + Buffer = Orig + i; + DbgPrint("%s", Orig); + } + } + else if (ScanCode == KEY_SCAN_DOWN) + { + if (CmdHistIndex > 0 && CmdHistIndex != KdbCommandHistoryIndex) + { + i = CmdHistIndex + 1; + if (i >= RTL_NUMBER_OF(KdbCommandHistory)) + i = 0; + if (KdbCommandHistory[i] != NULL) + { + CmdHistIndex = i; + while (Buffer > Orig) + { + Buffer--; + *Buffer = 0; + if (EchoOn) + DbgPrint("%c %c", KEY_BS, KEY_BS); + else + DbgPrint(" %c", KEY_BS); + } + i = min(strlen(KdbCommandHistory[CmdHistIndex]), Size - 1); + memcpy(Orig, KdbCommandHistory[CmdHistIndex], i); + Orig[i] = '\0'; + Buffer = Orig + i; + DbgPrint("%s", Orig); + } + } + } + else + { + if (EchoOn) + DbgPrint("%c", Key); + + *Buffer = Key; + Buffer++; + } + LastKey = Key; + } +} + +/*!\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 BOOL +KdbpDoCommand( + IN PCHAR Command) +{ + ULONG i; + PCHAR p; + ULONG Argc; + STATIC PCH Argv[256]; + STATIC CHAR OrigCommand[1024]; + + strncpy(OrigCommand, Command, sizeof(OrigCommand) - 1); + OrigCommand[sizeof(OrigCommand) - 1] = '\0'; + + 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 == NULL) + continue; + + if (strcmp(KdbDebuggerCommands[i].Name, Argv[0]) == 0) + { + return KdbDebuggerCommands[i].Fn(Argc, Argv); + } + } + + 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)) + { + DbgPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip); + } + DbgPrint(": "); + if (KdbpDisassemble(KdbCurrentTrapFrame->Tf.Eip, KdbUseIntelSyntax) < 0) + { + DbgPrint(""); + } + DbgPrint("\n"); + } + + do + { + /* Print the prompt */ + DbgPrint("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); + } while (Continue); +} + +/*!\brief Called when a module is loaded. + * + * \param Name Filename of the module which was loaded. + */ +VOID +KdbpCliModuleLoaded(IN PUNICODE_STRING Name) +{ + return; + + DbgPrint("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() +{ + PCHAR p1, p2; + INT i; + CHAR c; + + /* Execute the commands in the init file */ + DbgPrint("KDB: Executing KDB.init 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++; + } + DbgPrint("KDB: KDB.init executed\n"); +} + +/*!\brief Called when KDB is initialized + * + * Reads the KDB.init file from the SystemRoot\system32\drivers\etc directory and executes it. + */ +VOID +KdbpCliInit() +{ + 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\\KDB.init"); + InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL); + + /* Open the file */ + Status = ZwOpenFile(&hFile, FILE_READ_DATA, &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\\KDB.init (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\\KDB.init (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 == NULL) + { + ZwClose(hFile); + DPRINT("Could not allocate %d bytes for KDB.init file\n", FileSize); + return; + } + + /* Load file into memory */ + Status = ZwReadFile(hFile, 0, 0, 0, &Iosb, FileBuffer, FileSize, 0, 0); + ZwClose(hFile); + if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE) + { + ExFreePool(FileBuffer); + DPRINT("Could not read KDB.init file into memory (Status 0x%lx)\n", Status); + return; + } + FileSize = min(FileSize, Iosb.Information); + FileBuffer[FileSize] = '\0'; + + /* Enter critical section */ + Ke386SaveFlags(OldEflags); + Ke386DisableInterrupts(); + + /* Interpret the init file... */ + KdbInitFileBuffer = FileBuffer; + KdbEnter(); + KdbInitFileBuffer = NULL; + + /* Leave critical section */ + Ke386RestoreFlags(OldEflags); + + ExFreePool(FileBuffer); +} + diff --git a/reactos/ntoskrnl/dbg/kdb_expr.c b/reactos/ntoskrnl/dbg/kdb_expr.c new file mode 100644 index 00000000000..fb36fedf77c --- /dev/null +++ b/reactos/ntoskrnl/dbg/kdb_expr.c @@ -0,0 +1,1081 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/dbg/kdb_expr.c + * PURPOSE: Kernel debugger expression evaluation + * PROGRAMMER: Gregor Anich (blight@blight.eu.org) + * UPDATE HISTORY: + * Created 15/01/2005 + */ + +/* Note: + * + * The given expression is parsed and stored in reverse polish notation, + * then it is evaluated and the result is returned. + */ + +/* INCLUDES ******************************************************************/ + +#include +#include "kdb.h" +#define NDEBUG +#include + +/* TYPES *********************************************************************/ +typedef enum _RPN_OP_TYPE +{ + RpnOpNop, + RpnOpBinaryOperator, + RpnOpUnaryOperator, + RpnOpImmediate, + RpnOpRegister, + RpnOpDereference +} RPN_OP_TYPE; + +typedef ULONGLONG (*RPN_BINARY_OPERATOR)(ULONGLONG a, ULONGLONG b); + +typedef struct _RPN_OP +{ + RPN_OP_TYPE Type; + ULONG CharacterOffset; + union { + /* RpnOpBinaryOperator */ + RPN_BINARY_OPERATOR BinaryOperator; + /* RpnOpImmediate */ + ULONGLONG Immediate; + /* RpnOpRegister */ + UCHAR Register; + /* RpnOpDereference */ + UCHAR DerefMemorySize; + } Data; +} RPN_OP, *PRPN_OP; + +typedef struct _RPN_STACK +{ + ULONG Size; /* Number of RPN_OPs on Ops */ + ULONG Sp; /* Stack pointer */ + RPN_OP Ops[1]; /* Array of RPN_OPs */ +} RPN_STACK, *PRPN_STACK; + +/* DEFINES *******************************************************************/ +#define stricmp _stricmp + +#ifndef RTL_FIELD_SIZE +# define RTL_FIELD_SIZE(type, field) (sizeof(((type *)0)->field)) +#endif + +#define CONST_STRCPY(dst, src) \ + do { if ((dst) != NULL) { memcpy(dst, src, sizeof(src)); } } while (0); + +#define RPN_OP_STACK_SIZE 256 +#define RPN_VALUE_STACK_SIZE 256 + +/* GLOBALS *******************************************************************/ +STATIC struct { ULONG Size; ULONG Sp; RPN_OP Ops[RPN_OP_STACK_SIZE]; } RpnStack = { RPN_OP_STACK_SIZE, 0 }; + +STATIC CONST struct { PCHAR Name; UCHAR Offset; UCHAR Size; } RegisterToTrapFrame[] = +{ + {"eip", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Eip), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Eip)}, + {"eflags", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Eflags), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Eflags)}, + {"eax", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Eax), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Eax)}, + {"ebx", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ebx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ebx)}, + {"ecx", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ecx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ecx)}, + {"edx", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Edx), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Edx)}, + {"esi", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Esi), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Esi)}, + {"edi", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Edi), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Edi)}, + {"esp", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Esp), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Esp)}, + {"ebp", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ebp), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ebp)}, + {"cs", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Cs), 2 }, /* Use only the lower 2 bytes */ + {"ds", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ds), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ds)}, + {"es", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Es), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Es)}, + {"fs", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Fs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Fs)}, + {"gs", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Gs), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Gs)}, + {"ss", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Ss), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Ss)}, + {"dr0", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr0), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr0)}, + {"dr1", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr1), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr1)}, + {"dr2", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr2), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr2)}, + {"dr3", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr3), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr3)}, + {"dr6", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr6), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr6)}, + {"dr7", FIELD_OFFSET(KDB_KTRAP_FRAME, Tf.Dr7), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Tf.Dr7)}, + {"cr0", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr0), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr0)}, + {"cr2", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr2), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr2)}, + {"cr3", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr3), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr3)}, + {"cr4", FIELD_OFFSET(KDB_KTRAP_FRAME, Cr4), RTL_FIELD_SIZE(KDB_KTRAP_FRAME, Cr4)} +}; +STATIC CONST INT RegisterToTrapFrameCount = + sizeof (RegisterToTrapFrame) / sizeof (RegisterToTrapFrame[0]); + +/* FUNCTIONS *****************************************************************/ + +ULONGLONG +RpnBinaryOperatorAdd(ULONGLONG a, ULONGLONG b) +{ + return a + b; +} + +ULONGLONG +RpnBinaryOperatorSub(ULONGLONG a, ULONGLONG b) +{ + return a - b; +} + +ULONGLONG +RpnBinaryOperatorMul(ULONGLONG a, ULONGLONG b) +{ + return a * b; +} + +ULONGLONG +RpnBinaryOperatorDiv(ULONGLONG a, ULONGLONG b) +{ + + return a / b; +} + +ULONGLONG +RpnBinaryOperatorMod(ULONGLONG a, ULONGLONG b) +{ + return a % b; +} + +ULONGLONG +RpnBinaryOperatorEquals(ULONGLONG a, ULONGLONG b) +{ + return (a == b); +} + +ULONGLONG +RpnBinaryOperatorNotEquals(ULONGLONG a, ULONGLONG b) +{ + return (a != b); +} + +ULONGLONG +RpnBinaryOperatorLessThan(ULONGLONG a, ULONGLONG b) +{ + return (a < b); +} + +ULONGLONG +RpnBinaryOperatorLessThanOrEquals(ULONGLONG a, ULONGLONG b) +{ + return (a <= b); +} + +ULONGLONG +RpnBinaryOperatorGreaterThan(ULONGLONG a, ULONGLONG b) +{ + return (a > b); +} + +ULONGLONG +RpnBinaryOperatorGreaterThanOrEquals(ULONGLONG a, ULONGLONG b) +{ + return (a >= b); +} + +/*!\brief Dumps the given RPN stack content + * + * \param Stack Pointer to a RPN_STACK structure. + */ +VOID +RpnpDumpStack( + IN PRPN_STACK Stack) +{ + ULONG ul; + + ASSERT(Stack != NULL); + DbgPrint("\nStack size: %ld\n", Stack->Sp); + for (ul = 0; ul < Stack->Sp; ul++) + { + PRPN_OP Op = Stack->Ops + ul; + switch (Op->Type) + { + case RpnOpNop: + DbgPrint("NOP,"); + break; + + case RpnOpImmediate: + DbgPrint("0x%I64x,", Op->Data.Immediate); + break; + + case RpnOpBinaryOperator: + if (Op->Data.BinaryOperator == RpnBinaryOperatorAdd) + DbgPrint("+,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorSub) + DbgPrint("-,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorMul) + DbgPrint("*,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorDiv) + DbgPrint("/,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorMod) + DbgPrint("%%,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorEquals) + DbgPrint("==,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorNotEquals) + DbgPrint("!=,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorLessThan) + DbgPrint("<,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorLessThanOrEquals) + DbgPrint("<=,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorGreaterThan) + DbgPrint(">,"); + else if (Op->Data.BinaryOperator == RpnBinaryOperatorGreaterThanOrEquals) + DbgPrint(">=,"); + else + DbgPrint("UNKNOWN OP,"); + break; + + case RpnOpRegister: + DbgPrint("%s,", RegisterToTrapFrame[Op->Data.Register].Name); + break; + + case RpnOpDereference: + DbgPrint("[%s],", + (Op->Data.DerefMemorySize == 1) ? ("byte") : + ((Op->Data.DerefMemorySize == 2) ? ("word") : + ((Op->Data.DerefMemorySize == 4) ? ("dword") : ("qword")) + ) + ); + break; + + default: + DbgPrint("\nUnsupported Type: %d\n", Op->Type); + ul = Stack->Sp; + break; + } + } + DbgPrint("\n"); +} + +/*!\brief Clears the given RPN stack. + * + * \param Stack Pointer to a RPN_STACK structure. + */ +STATIC VOID +RpnpClearStack( + OUT PRPN_STACK Stack) +{ + ASSERT(Stack != NULL); + Stack->Sp = 0; +} + +/*!\brief Pushes an RPN_OP onto the stack. + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Op RPN_OP to be copied onto the stack. + */ +STATIC BOOLEAN +RpnpPushStack( + IN OUT PRPN_STACK Stack, + IN PRPN_OP Op) +{ + ASSERT(Stack != NULL); + ASSERT(Op != NULL); + + if (Stack->Sp >= Stack->Size) + return FALSE; + + memcpy(Stack->Ops + Stack->Sp, Op, sizeof (RPN_OP)); + Stack->Sp++; + return TRUE; +} + +/*!\brief Pops the top op from the stack. + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Op Pointer to an RPN_OP to store the popped op into (can be NULL). + * + * \retval TRUE Success. + * \retval FALSE Failure (stack empty) + */ +STATIC BOOLEAN +RpnpPopStack( + IN OUT PRPN_STACK Stack, + OUT PRPN_OP Op OPTIONAL) +{ + ASSERT(Stack != NULL); + + if (Stack->Sp == 0) + return FALSE; + + Stack->Sp--; + if (Op != NULL) + memcpy(Op, Stack->Ops + Stack->Sp, sizeof (RPN_OP)); + return TRUE; +} + +/*!\brief Gets the top op from the stack (not popping it) + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Op Pointer to an RPN_OP to copy the top op into. + * + * \retval TRUE Success. + * \retval FALSE Failure (stack empty) + */ +STATIC BOOLEAN +RpnpTopStack( + IN PRPN_STACK Stack, + OUT PRPN_OP Op) +{ + ASSERT(Stack != NULL); + ASSERT(Op != NULL); + + if (Stack->Sp == 0) + return FALSE; + + memcpy(Op, Stack->Ops + Stack->Sp - 1, sizeof (RPN_OP)); + return TRUE; +} + +/*!\brief Parses an expression. + * + * This functions parses the given expression until the end of string or a closing + * brace is found. As the function parses the string it pushes RPN_OPs onto the + * stack. + * + * Examples: 1+2*3 ; eax+10 ; (eax+16) * (ebx+4) ; dword[eax] + * + * \param Stack Pointer to a RPN_STACK structure. + * \param Expression String to parse. + * \param CharacterOffset Character offset of the subexpression from the beginning of the expression. + * \param End On success End is set to the character at which parsing stopped. + * \param ErrOffset On failure this is set to the character offset at which the error occoured. + * \param ErrMsg On failure a message describing the problem is copied into this buffer (128 bytes) + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +STATIC BOOLEAN +RpnpParseExpression( + IN PRPN_STACK Stack, + IN PCHAR Expression, + OUT PCHAR *End OPTIONAL, + IN ULONG CharacterOffset, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + PCHAR p = Expression; + PCHAR pend; + PCHAR Operator = NULL; + LONG OperatorOffset = -1; + RPN_OP RpnOp; + RPN_OP PoppedOperator; + BOOLEAN HavePoppedOperator = FALSE; + RPN_OP ComparativeOp; + BOOLEAN ComparativeOpFilled = FALSE; + BOOLEAN IsComparativeOp; + INT i, i2; + ULONG ul; + UCHAR MemorySize; + CHAR Buffer[16]; + BOOLEAN First; + + ASSERT(Stack != NULL); + ASSERT(Expression != NULL); + + First = TRUE; + for (;;) + { + /* Skip whitespace */ + while (isspace(*p)) + { + p++; + CharacterOffset++; + } + + /* Check for end of expression */ + if (p[0] == '\0' || p[0] == ')' || p[0] == ']') + break; + + if (!First) + { + /* Remember operator */ + Operator = p++; + OperatorOffset = CharacterOffset++;; + + /* Pop operator (to get the right operator precedence) */ + HavePoppedOperator = FALSE; + if (*Operator == '*' || *Operator == '/' || *Operator == '%') + { + if (RpnpTopStack(Stack, &PoppedOperator) && + PoppedOperator.Type == RpnOpBinaryOperator && + (PoppedOperator.Data.BinaryOperator == RpnBinaryOperatorAdd || + PoppedOperator.Data.BinaryOperator == RpnBinaryOperatorSub)) + { + RpnpPopStack(Stack, NULL); + HavePoppedOperator = TRUE; + } + else if (PoppedOperator.Type == RpnOpNop) + { + RpnpPopStack(Stack, NULL); + /* Discard the NOP - it was only pushed to indicate there was a + * closing brace, so the previous operator shouldn't be popped. + */ + } + } + else if ((Operator[0] == '=' && Operator[1] == '=') || + (Operator[0] == '!' && Operator[1] == '=') || + Operator[0] == '<' || Operator[0] == '>') + { + if (Operator[0] == '=' || Operator[0] == '!' || + (Operator[0] == '<' && Operator[1] == '=') || + (Operator[0] == '>' && Operator[1] == '=')) + { + p++; + CharacterOffset++; + } +#if 0 + /* Parse rest of expression */ + if (!RpnpParseExpression(Stack, p + 1, &pend, CharacterOffset + 1, + ErrOffset, ErrMsg)) + { + return FALSE; + } + else if (pend == p + 1) + { + CONST_STRCPY(ErrMsg, "Expression expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + 1; + return FALSE; + } + goto end_of_expression; /* return */ +#endif + } + else if (Operator[0] != '+' && Operator[0] != '-') + { + CONST_STRCPY(ErrMsg, "Operator expected"); + if (ErrOffset != NULL) + *ErrOffset = OperatorOffset; + return FALSE; + } + + /* Skip whitespace */ + while (isspace(*p)) + { + p++; + CharacterOffset++; + } + } + + /* Get operand */ + MemorySize = sizeof(ULONG_PTR); /* default to pointer size */ +get_operand: + i = strcspn(p, "+-*/%()[]<>!="); + if (i > 0) + { + i2 = i; + + /* Copy register name/memory size */ + while (isspace(p[--i2])); + i2 = min(i2 + 1, sizeof (Buffer) - 1); + strncpy(Buffer, p, i2); + Buffer[i2] = '\0'; + + /* Memory size prefix */ + if (p[i] == '[') + { + if (stricmp(Buffer, "byte") == 0) + MemorySize = 1; + else if (stricmp(Buffer, "word") == 0) + MemorySize = 2; + else if (stricmp(Buffer, "dword") == 0) + MemorySize = 4; + else if (stricmp(Buffer, "qword") == 0) + MemorySize = 8; + else + { + CONST_STRCPY(ErrMsg, "Invalid memory size prefix"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset; + return FALSE; + } + + p += i; + CharacterOffset += i; + goto get_operand; + } + + /* Try to find register */ + for (i = 0; i < RegisterToTrapFrameCount; i++) + { + if (stricmp(RegisterToTrapFrame[i].Name, Buffer) == 0) + break; + } + if (i < RegisterToTrapFrameCount) + { + RpnOp.Type = RpnOpRegister; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.Register = i; + i = strlen(RegisterToTrapFrame[i].Name); + CharacterOffset += i; + p += i; + } + else + { + /* Immediate value */ + /* FIXME: Need string to ULONGLONG function */ + ul = strtoul(p, &pend, 0); + if (p != pend) + { + RpnOp.Type = RpnOpImmediate; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.Immediate = (ULONGLONG)ul; + CharacterOffset += pend - p; + p = pend; + } + else + { + CONST_STRCPY(ErrMsg, "Operand expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset; + return FALSE; + } + } + + /* Push operand */ + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + else if (i == 0) + { + if (p[0] == '(' || p[0] == '[') /* subexpression */ + { + if (!RpnpParseExpression(Stack, p + 1, &pend, CharacterOffset + 1, + ErrOffset, ErrMsg)) + { + return FALSE; + } + else if (pend == p + 1) + { + CONST_STRCPY(ErrMsg, "Expression expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + 1; + return FALSE; + } + + if (p[0] == '[') /* dereference */ + { + ASSERT(MemorySize == 1 || MemorySize == 2 || + MemorySize == 4 || MemorySize == 8); + if (pend[0] != ']') + { + CONST_STRCPY(ErrMsg, "']' expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + (pend - p); + return FALSE; + } + RpnOp.Type = RpnOpDereference; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.DerefMemorySize = MemorySize; + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + else /* p[0] == '(' */ + { + if (pend[0] != ')') + { + CONST_STRCPY(ErrMsg, "')' expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset + (pend - p); + return FALSE; + } + } + + /* Push a "nop" to prevent popping of the + operator (which would + * result in (10+10)/2 beeing evaluated as 15) + */ + RpnOp.Type = RpnOpNop; + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* Skip closing brace/bracket */ + pend++; + + CharacterOffset += pend - p; + p = pend; + } + else if (First && p[0] == '-') /* Allow expressions like "- eax" */ + { + RpnOp.Type = RpnOpImmediate; + RpnOp.CharacterOffset = CharacterOffset; + RpnOp.Data.Immediate = 0; + if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + else + { + CONST_STRCPY(ErrMsg, "Operand expected"); + if (ErrOffset != NULL) + *ErrOffset = CharacterOffset; + return FALSE; + } + } + else + { + CONST_STRCPY(ErrMsg, "strcspn() failed"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + if (!First) + { + /* Push operator */ + RpnOp.CharacterOffset = OperatorOffset; + RpnOp.Type = RpnOpBinaryOperator; + IsComparativeOp = FALSE; + switch (*Operator) + { + case '+': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorAdd; + break; + + case '-': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorSub; + break; + + case '*': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorMul; + break; + + case '/': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorDiv; + break; + + case '%': + RpnOp.Data.BinaryOperator = RpnBinaryOperatorMod; + break; + + case '=': + ASSERT(Operator[1] == '='); + IsComparativeOp = TRUE; + RpnOp.Data.BinaryOperator = RpnBinaryOperatorEquals; + break; + + case '!': + ASSERT(Operator[1] == '='); + IsComparativeOp = TRUE; + RpnOp.Data.BinaryOperator = RpnBinaryOperatorNotEquals; + break; + + case '<': + IsComparativeOp = TRUE; + if (Operator[1] == '=') + RpnOp.Data.BinaryOperator = RpnBinaryOperatorLessThanOrEquals; + else + RpnOp.Data.BinaryOperator = RpnBinaryOperatorLessThan; + break; + + case '>': + IsComparativeOp = TRUE; + if (Operator[1] == '=') + RpnOp.Data.BinaryOperator = RpnBinaryOperatorGreaterThanOrEquals; + else + RpnOp.Data.BinaryOperator = RpnBinaryOperatorGreaterThan; + break; + + default: + ASSERT(0); + break; + } + if (IsComparativeOp) + { + if (ComparativeOpFilled && !RpnpPushStack(Stack, &ComparativeOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + memcpy(&ComparativeOp, &RpnOp, sizeof(RPN_OP)); + ComparativeOpFilled = TRUE; + } + else if (!RpnpPushStack(Stack, &RpnOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* Push popped operator */ + if (HavePoppedOperator) + { + if (!RpnpPushStack(Stack, &PoppedOperator)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + } + } + + First = FALSE; + } + +//end_of_expression: + + if (ComparativeOpFilled && !RpnpPushStack(Stack, &ComparativeOp)) + { + CONST_STRCPY(ErrMsg, "RPN op stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* Skip whitespace */ + while (isspace(*p)) + { + p++; + CharacterOffset++; + } + + if (End != NULL) + *End = p; + + return TRUE; +} + +/*!\brief Evaluates the RPN op stack and returns the result. + * + * \param Stack Pointer to a RPN_STACK structure. + * \param TrapFrame Register values. + * \param Result Pointer to an ULONG to store the result into. + * \param ErrOffset On failure this is set to the character offset at which the error occoured. + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +STATIC BOOLEAN +RpnpEvaluateStack( + IN PRPN_STACK Stack, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + ULONGLONG ValueStack[RPN_VALUE_STACK_SIZE]; + ULONG ValueStackPointer = 0; + ULONG index; + ULONGLONG ull; + ULONG ul; + USHORT us; + UCHAR uc; + PVOID p; + BOOLEAN Ok; +#ifdef DEBUG_RPN + ULONG ValueStackPointerMax = 0; +#endif + + ASSERT(Stack != NULL); + ASSERT(TrapFrame != NULL); + ASSERT(Result != NULL); + + for (index = 0; index < Stack->Sp; index++) + { + PRPN_OP Op = Stack->Ops + index; + +#ifdef DEBUG_RPN + ValueStackPointerMax = max(ValueStackPointerMax, ValueStackPointer); +#endif + + switch (Op->Type) + { + case RpnOpNop: + /* No operation */ + break; + + case RpnOpImmediate: + if (ValueStackPointer == RPN_VALUE_STACK_SIZE) + { + CONST_STRCPY(ErrMsg, "Value stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + ValueStack[ValueStackPointer++] = Op->Data.Immediate; + break; + + case RpnOpRegister: + if (ValueStackPointer == RPN_VALUE_STACK_SIZE) + { + CONST_STRCPY(ErrMsg, "Value stack overflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + ul = Op->Data.Register; + p = (PVOID)((ULONG_PTR)TrapFrame + RegisterToTrapFrame[ul].Offset); + switch (RegisterToTrapFrame[ul].Size) + { + case 1: ull = (ULONGLONG)(*(PUCHAR)p); break; + case 2: ull = (ULONGLONG)(*(PUSHORT)p); break; + case 4: ull = (ULONGLONG)(*(PULONG)p); break; + case 8: ull = (ULONGLONG)(*(PULONGLONG)p); break; + default: ASSERT(0); return FALSE; break; + } + ValueStack[ValueStackPointer++] = ull; + break; + + case RpnOpDereference: + if (ValueStackPointer < 1) + { + CONST_STRCPY(ErrMsg, "Value stack underflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + /* FIXME: Print a warning when address is out of range */ + p = (PVOID)(ULONG_PTR)ValueStack[ValueStackPointer - 1]; + Ok = FALSE; + switch (Op->Data.DerefMemorySize) + { + case 1: + if (NT_SUCCESS(KdbpSafeReadMemory(&uc, p, sizeof (uc)))) + { + Ok = TRUE; + ull = (ULONGLONG)uc; + } + break; + case 2: + if (NT_SUCCESS(KdbpSafeReadMemory(&us, p, sizeof (us)))) + { + Ok = TRUE; + ull = (ULONGLONG)us; + } + break; + case 4: + if (NT_SUCCESS(KdbpSafeReadMemory(&ul, p, sizeof (ul)))) + { + Ok = TRUE; + ull = (ULONGLONG)ul; + } + break; + case 8: + if (NT_SUCCESS(KdbpSafeReadMemory(&ull, p, sizeof (ull)))) + { + Ok = TRUE; + } + break; + default: + ASSERT(0); + return FALSE; + break; + } + if (!Ok) + { + _snprintf(ErrMsg, 128, "Couldn't access memory at 0x%lx", (ULONG)p); + if (ErrOffset != NULL) + *ErrOffset = Op->CharacterOffset; + return FALSE; + } + ValueStack[ValueStackPointer - 1] = ull; + break; + + case RpnOpBinaryOperator: + if (ValueStackPointer < 2) + { + CONST_STRCPY(ErrMsg, "Value stack underflow"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + ValueStackPointer--; + ull = ValueStack[ValueStackPointer]; + if (ull == 0 && (Op->Data.BinaryOperator == RpnBinaryOperatorDiv || + Op->Data.BinaryOperator == RpnBinaryOperatorDiv)) + { + CONST_STRCPY(ErrMsg, "Devision by zero"); + if (ErrOffset != NULL) + *ErrOffset = Op->CharacterOffset; + return FALSE; + } + ull = Op->Data.BinaryOperator(ValueStack[ValueStackPointer - 1], ull); + ValueStack[ValueStackPointer - 1] = ull; + break; + + default: + ASSERT(0); + return FALSE; + } + } +#ifdef DEBUG_RPN + DPRINT1("Max value stack pointer: %d\n", ValueStackPointerMax); +#endif + if (ValueStackPointer != 1) + { + CONST_STRCPY(ErrMsg, "Stack not empty after evaluation"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return FALSE; + } + + *Result = ValueStack[0]; + return TRUE; +} + +/*!\brief Evaluates the given expression + * + * \param Expression Expression to evaluate. + * \param TrapFrame Register values. + * \param Result Variable which receives the result on success. + * \param ErrOffset Variable which receives character offset on parse error (-1 on other errors) + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \retval TRUE Success. + * \retval FALSE Failure. + */ +BOOLEAN +KdbpRpnEvaluateExpression( + IN PCHAR Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + PRPN_STACK Stack = (PRPN_STACK)&RpnStack; + + ASSERT(Expression != NULL); + ASSERT(TrapFrame != NULL); + ASSERT(Result != NULL); + + /* Clear the stack and parse the expression */ + RpnpClearStack(Stack); + if (!RpnpParseExpression(Stack, Expression, NULL, 0, ErrOffset, ErrMsg)) + { + return FALSE; + } +#ifdef DEBUG_RPN + RpnpDumpStack(Stack); +#endif + + /* Evaluate the stack */ + if (!RpnpEvaluateStack(Stack, TrapFrame, Result, ErrOffset, ErrMsg)) + { + return FALSE; + } + + return TRUE; +} + +/*!\brief Parses the given expression and returns a "handle" to it. + * + * \param Expression Expression to evaluate. + * \param ErrOffset Variable which receives character offset on parse error (-1 on other errors) + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \returns "Handle" for the expression, NULL on failure. + * + * \sa KdbpRpnEvaluateExpression + */ +PVOID +KdbpRpnParseExpression( + IN PCHAR Expression, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + LONG Size; + PRPN_STACK Stack = (PRPN_STACK)&RpnStack; + PRPN_STACK NewStack; + + ASSERT(Expression != NULL); + + /* Clear the stack and parse the expression */ + RpnpClearStack(Stack); + if (!RpnpParseExpression(Stack, Expression, NULL, 0, ErrOffset, ErrMsg)) + { + return FALSE; + } +#ifdef DEBUG_RPN + RpnpDumpStack(Stack); +#endif + + /* Duplicate the stack and return a pointer/handle to it */ + ASSERT(Stack->Sp >= 1); + Size = sizeof (RPN_STACK) + (RTL_FIELD_SIZE(RPN_STACK, Ops[0]) * (Stack->Sp - 1)); + NewStack = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_KDBG); + if (NewStack == NULL) + { + CONST_STRCPY(ErrMsg, "Out of memory"); + if (ErrOffset != NULL) + *ErrOffset = -1; + return NULL; + } + memcpy(NewStack, Stack, Size); + NewStack->Size = NewStack->Sp; + + return NewStack; +} + +/*!\brief Evaluates the given expression and returns the result. + * + * \param Expression Expression "handle" returned by KdbpRpnParseExpression. + * \param TrapFrame Register values. + * \param Result Variable which receives the result on success. + * \param ErrOffset Variable which receives character offset on parse error (-1 on other errors) + * \param ErrMsg Buffer which receives an error message on failure (128 bytes) + * + * \returns "Handle" for the expression, NULL on failure. + * + * \sa KdbpRpnParseExpression + */ +BOOLEAN +KdbpRpnEvaluateParsedExpression( + IN PVOID Expression, + IN PKDB_KTRAP_FRAME TrapFrame, + OUT PULONGLONG Result, + OUT PLONG ErrOffset OPTIONAL, + OUT PCHAR ErrMsg OPTIONAL) +{ + PRPN_STACK Stack = (PRPN_STACK)Expression; + + ASSERT(Expression != NULL); + ASSERT(TrapFrame != NULL); + ASSERT(Result != NULL); + + /* Evaluate the stack */ + return RpnpEvaluateStack(Stack, TrapFrame, Result, ErrOffset, ErrMsg); +} + diff --git a/reactos/ntoskrnl/dbg/kdb_keyboard.c b/reactos/ntoskrnl/dbg/kdb_keyboard.c index a9804e39aa0..71376dd29e1 100644 --- a/reactos/ntoskrnl/dbg/kdb_keyboard.c +++ b/reactos/ntoskrnl/dbg/kdb_keyboard.c @@ -1,4 +1,4 @@ -/* $Id:$ +/* $Id$ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -62,7 +62,7 @@ VOID KbdDisableMouse() } CHAR -KdbTryGetCharKeyboard(PULONG ScanCode) +KdbpTryGetCharKeyboard(PULONG ScanCode) { static byte_t last_key = 0; static byte_t shift = 0; @@ -305,7 +305,7 @@ static char keymap[128][2] = { * Yes, this is horrible. */ ULONG -KdbTryGetCharKeyboard(VOID) +KdbpTryGetCharKeyboard(VOID) { static unsigned shift_state, ctrl_state, meta_state; unsigned scan_code, ch; diff --git a/reactos/ntoskrnl/dbg/kdb_serial.c b/reactos/ntoskrnl/dbg/kdb_serial.c index 3223a77f7c4..0d28c006df6 100644 --- a/reactos/ntoskrnl/dbg/kdb_serial.c +++ b/reactos/ntoskrnl/dbg/kdb_serial.c @@ -1,4 +1,4 @@ -/* $Id:$ +/* $Id$ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -20,7 +20,7 @@ extern KD_PORT_INFORMATION LogPortInfo; CHAR -KdbTryGetCharSerial() +KdbpTryGetCharSerial() { UCHAR Result; diff --git a/reactos/ntoskrnl/dbg/kdb_string.c b/reactos/ntoskrnl/dbg/kdb_string.c new file mode 100644 index 00000000000..773acce686d --- /dev/null +++ b/reactos/ntoskrnl/dbg/kdb_string.c @@ -0,0 +1,123 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/dbg/kdb_string.c + * PURPOSE: Kernel debugger string functions + * PROGRAMMER: Gregor Anich (blight@blight.eu.org) + * UPDATE HISTORY: + * Created 17/01/2005 + */ + +/* INCLUDES ******************************************************************/ +#include + +/* FUNCTIONS *****************************************************************/ + +#if 0 +int +_stricmp( + const char *s1, + const char *s2) +{ + char c1, c2; + for (;;) + { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + if (c1 == '\0') + break; + } + return 0; +} +#endif /* unused */ + +/* + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long +strtoul(const char *nptr, char **endptr, int base) +{ + const char *s = nptr; + unsigned long acc; + int c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') + { + neg = 1; + c = *s++; + } + else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) + { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; + cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) + { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) + { + acc = ULONG_MAX; + } + else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = any ? (char *)s - 1 : (char *)nptr; + return acc; +} + diff --git a/reactos/ntoskrnl/dbg/kdb_symbols.c b/reactos/ntoskrnl/dbg/kdb_symbols.c index 618711e6be0..c95259ef4bc 100644 --- a/reactos/ntoskrnl/dbg/kdb_symbols.c +++ b/reactos/ntoskrnl/dbg/kdb_symbols.c @@ -243,12 +243,12 @@ KdbSymPrintAddress(IN PVOID Address) FunctionName); if (NT_SUCCESS(Status)) { - DbgPrint("<%ws: %x (%s:%d (%s))>", + DbgPrint("<%ws:%x (%s:%d (%s))>", Info.Name, RelativeAddress, FileName, LineNumber, FunctionName); } else { - DbgPrint("<%ws: %x>", Info.Name, RelativeAddress); + DbgPrint("<%ws:%x>", Info.Name, RelativeAddress); } return TRUE; @@ -541,7 +541,7 @@ KdbSymFreeProcessSymbols(IN PEPROCESS Process) CurrentProcess = PsGetCurrentProcess(); if (CurrentProcess != Process) { - KeAttachProcess(&Process->Pcb); + KeAttachProcess(EPROCESS_TO_KPROCESS(Process)); } Peb = Process->Peb; ASSERT(Peb); diff --git a/reactos/ntoskrnl/include/internal/i386/ke.h b/reactos/ntoskrnl/include/internal/i386/ke.h index 06612b5c5a0..252ed8c04be 100644 --- a/reactos/ntoskrnl/include/internal/i386/ke.h +++ b/reactos/ntoskrnl/include/internal/i386/ke.h @@ -69,9 +69,11 @@ #define KTRAP_FRAME_RESERVED9 (0x8A) #define KTRAP_FRAME_SIZE (0x8C) +#define X86_EFLAGS_TF 0x00000100 /* Trap flag */ #define X86_EFLAGS_IF 0x00000200 /* Interrupt Enable flag */ #define X86_EFLAGS_IOPL 0x00003000 /* I/O Privilege Level bits */ #define X86_EFLAGS_NT 0x00004000 /* Nested Task flag */ +#define X86_EFLAGS_RF 0x00010000 /* Resume flag */ #define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */ #define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */ diff --git a/reactos/ntoskrnl/include/internal/kd.h b/reactos/ntoskrnl/include/internal/kd.h index 4594dd47ee4..f61149d052e 100644 --- a/reactos/ntoskrnl/include/internal/kd.h +++ b/reactos/ntoskrnl/include/internal/kd.h @@ -98,9 +98,6 @@ KdGdbDebugPrint (LPSTR Message); VOID KdDebugPrint (LPSTR Message); -VOID -KdbCreateThreadHook(PCONTEXT Context); - KD_CONTINUE_TYPE KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context, @@ -118,7 +115,7 @@ VOID KdPrintMda(PCH pch); # define KDB_CREATE_THREAD_HOOK(CONTEXT) do { } while (0) #else # define KDB_LOADUSERMODULE_HOOK(LDRMOD) KdbSymLoadUserModuleSymbols(LDRMOD) -# define KDB_DELETEPROCESS_HOOK(PROCESS) KdbSymFreeProcessSymbols(PROCESS) +# define KDB_DELETEPROCESS_HOOK(PROCESS) KdbDeleteProcessHook(PROCESS) # define KDB_LOADDRIVER_HOOK(FILENAME, MODULE) KdbSymLoadDriverSymbols(FILENAME, MODULE) # define KDB_UNLOADDRIVER_HOOK(MODULE) KdbSymUnloadDriverSymbols(MODULE) # define KDB_LOADERINIT_HOOK(NTOS, HAL) KdbSymInit(NTOS, HAL) @@ -126,6 +123,9 @@ VOID KdPrintMda(PCH pch); /*#define KDB_CREATE_THREAD_HOOK(CONTEXT) \ KdbCreateThreadHook(CONTEXT) */ +VOID +KdbDeleteProcessHook(IN PEPROCESS Process); + VOID KdbSymLoadUserModuleSymbols(IN PLDR_MODULE LdrModule); @@ -155,7 +155,7 @@ KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord, KPROCESSOR_MODE PreviousMode, PCONTEXT Context, PKTRAP_FRAME TrapFrame, - BOOLEAN HandleAlways); + BOOLEAN FirstChance); #endif /* KDBG || DBG */ diff --git a/reactos/ntoskrnl/ke/catch.c b/reactos/ntoskrnl/ke/catch.c index 1d4ca92a486..70deaaf2fbf 100644 --- a/reactos/ntoskrnl/ke/catch.c +++ b/reactos/ntoskrnl/ke/catch.c @@ -81,7 +81,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, #ifdef KDBG Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, FALSE); + Context, Tf, TRUE); if (Action == kdContinue) { return; @@ -131,7 +131,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, #ifdef KDBG Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, TRUE); + Context, Tf, FALSE); if (Action == kdContinue) { return; @@ -147,7 +147,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, /* PreviousMode == KernelMode */ #ifdef KDBG Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, FALSE); + Context, Tf, TRUE); if (Action == kdContinue) { return; @@ -168,7 +168,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, ExceptionRecord->ExceptionAddress ); #ifdef KDBG Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode, - Context, Tf, TRUE); + Context, Tf, FALSE); if (Action == kdContinue) { return; diff --git a/reactos/ntoskrnl/ke/i386/trap.s b/reactos/ntoskrnl/ke/i386/trap.s index 280720de5c8..5e24374e060 100644 --- a/reactos/ntoskrnl/ke/i386/trap.s +++ b/reactos/ntoskrnl/ke/i386/trap.s @@ -26,7 +26,9 @@ /* INCLUDES ******************************************************************/ +#include #include +#include #include #include #include @@ -74,6 +76,29 @@ _KiTrapRet: popl %edi popl %esi popl %ebx + +#ifdef DBG + /* + * Cleanup the stack which was used to setup a trapframe with SS:ESP when called + * from kmode. + */ + movw 0xC(%esp), %bp /* Get CS from trapframe */ + cmpw $KERNEL_CS, %bp + jne 0f + + /* Copy EBP, CS:EIP and EFLAGS from the trapframe back onto the top of our stack. */ + movl 0x00(%esp), %ebp /* EBP */ + movl %ebp, 0x24(%esp) + movl 0x08(%esp), %ebp /* EIP */ + movl %ebp, 0x2C(%esp) + movl 0x0C(%esp), %ebp /* CS */ + movl %ebp, 0x30(%esp) + movl 0x10(%esp), %ebp /* EFLAGS */ + movl %ebp, 0x34(%esp) + + addl $0x24, %esp +0: +#endif /* DBG */ popl %ebp addl $0x4, %esp /* Ignore error code */ @@ -81,10 +106,32 @@ _KiTrapRet: .globl _KiTrapProlog _KiTrapProlog: +#ifdef DBG + /* + * If we were called from kmode we start setting up a new trapframe (with SS:ESP at the end) + */ + movw 0x14(%esp), %bx /* Get old CS */ + cmpw $KERNEL_CS, %bx + + jne 0f + + leal 0x1C(%esp), %ebp + pushl %ss /* Old SS */ + pushl %ebp /* Old ESP */ + pushl 0x20(%esp) /* Old EFLAGS */ + pushl 0x20(%esp) /* Old CS */ + pushl 0x20(%esp) /* Old EIP */ + pushl 0x20(%esp) /* ErrorCode */ + pushl 0x20(%esp) /* Ebp */ + pushl 0x20(%esp) /* Ebx */ + pushl 0x20(%esp) /* Esi */ +0: +#endif /* DBG */ + pushl %edi pushl %fs - /* + /* * Check that the PCR exists, very early in the boot process it may * not */ diff --git a/reactos/ntoskrnl/ke/i386/tskswitch.S b/reactos/ntoskrnl/ke/i386/tskswitch.S index a97577fa5ee..4555f23746b 100644 --- a/reactos/ntoskrnl/ke/i386/tskswitch.S +++ b/reactos/ntoskrnl/ke/i386/tskswitch.S @@ -45,6 +45,10 @@ _Ki386ContextSwitch: * Thread = Thread to switch to * OldThread = Thread to switch from */ +#ifdef KDBG + jmp SaveTrapFrameForKDB +SaveTrapFrameForKDB_Return: +#endif pushl %ebp movl %esp, %ebp @@ -218,3 +222,128 @@ _Ki386ContextSwitch: ret .endfunc + + +#ifdef KDBG + +SaveTrapFrameForKDB: + /* + * Set up a trap frame. + */ + /* Ss - space already reserved by return EIP */ + pushl %esp /* Esp */ + pushfl /* Eflags */ + pushl %cs /* Cs */ + pushl 12(%esp) /* Eip */ + movl %ss, 16(%esp) /* Save Ss */ + pushl $0 /* ErrorCode */ + pushl %ebp /* Ebp */ + pushl %ebx /* Ebx */ + pushl %esi /* Esi */ + pushl %edi /* Edi */ + pushl %fs /* Fs */ + pushl $0 /* ExceptionList */ + pushl $0 /* PreviousMode */ + pushl %eax /* Eax */ + pushl %ecx /* Ecx */ + pushl %edx /* Edx */ + pushl %ds /* Ds */ + pushl %es /* Es */ + pushl %gs /* Gs */ + movl %dr7, %eax + pushl %eax /* Dr7 */ + /* Clear breakpoint enables in dr7. */ + andl $~0xffff, %eax + movl %eax, %dr7 + movl %dr6, %eax + pushl %eax /* Dr6 */ + movl %dr3, %eax + pushl %eax /* Dr3 */ + movl %dr2, %eax + pushl %eax /* Dr2 */ + movl %dr1, %eax + pushl %eax /* Dr1 */ + movl %dr0, %eax + pushl %eax /* Dr0 */ + pushl $0 /* TempEip */ + pushl $0 /* TempCs */ + pushl $0 /* DebugPointer */ + pushl $0xffffffff /* DebugArgMark (Exception number) */ + pushl 0x60(%esp) /* DebugEip */ + pushl %ebp /* DebugEbp */ + + movl %esp, %ebp /* Save pointer to new TrapFrame */ + + /* Save the old trapframe and set pointer to the new one */ + movl 0x80(%esp), %ebx /* Get pointer to OldThread */ + pushl KTHREAD_TRAP_FRAME(%ebx) + movl %ebp, KTHREAD_TRAP_FRAME(%ebx) + + /* Copy the arguments which were passed to Ki386ContextSwitch */ + pushl 0x80(%ebp) /* OldThread */ + pushl 0x7c(%ebp) /* NewThread */ + pushl $RestoreTrapFrameForKDB /* Return address */ + + /* Restore clobbered registers */ + movl KTRAP_FRAME_EBX(%ebp), %ebx + movl KTRAP_FRAME_EBP(%ebp), %ebp + + /* Return */ + jmp SaveTrapFrameForKDB_Return + + +RestoreTrapFrameForKDB: + addl $8, %esp /* Remove NewThread and OldThread arguments from the stack */ + movl 0x84(%esp), %ebx /* Get pointer to OldThread */ + + /* Restore the old trapframe */ + popl KTHREAD_TRAP_FRAME(%ebx) + + /* + * Pop unused portions of the trap frame: + * DebugEbp + * DebugEip + * DebugArgMark + * DebugPointer + * TempCs + * TempEip + * Dr0-3 + * Dr6-7 + */ + addl $(12*4), %esp + + /* + * Restore registers including any that might have been changed + * inside the debugger. + */ + popl %gs /* Gs */ + popl %es /* Es */ + popl %ds /* Ds */ + popl %edx /* Edx */ + popl %ecx /* Ecx */ + popl %eax /* Eax */ + addl $4, %esp /* PreviousMode */ + addl $4, %esp /* ExceptionList */ + popl %fs /* Fs */ + popl %edi /* Edi */ + popl %esi /* Esi */ + popl %ebx /* Ebx */ + + /* Remove SS:ESP from the stack */ + movl 16(%esp), %ebp + movl %ebp, 24(%esp) + movl 12(%esp), %ebp + movl %ebp, 20(%esp) + movl 8(%esp), %ebp + movl %ebp, 16(%esp) + + popl %ebp /* Ebp */ + addl $12, %esp /* ErrorCode and SS:ESP */ + + /* + * Return to the caller. + */ + iret + +#endif /* KDBG */ + diff --git a/reactos/ntoskrnl/ke/kthread.c b/reactos/ntoskrnl/ke/kthread.c index 40b352fb11a..c490cc53041 100644 --- a/reactos/ntoskrnl/ke/kthread.c +++ b/reactos/ntoskrnl/ke/kthread.c @@ -236,10 +236,7 @@ KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First) Thread->Alerted[0] = 0; Thread->Alerted[1] = 0; Thread->Iopl = 0; - /* - * FIXME: Think how this might work - */ - Thread->NpxState = 0; + Thread->NpxState = NPX_STATE_INVALID; Thread->Saturation = 0; Thread->Priority = Process->BasePriority; @@ -262,7 +259,7 @@ KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First) Thread->DecrementCount = 0; Thread->PriorityDecrement = 0; Thread->Quantum = Process->ThreadQuantum; - memset(Thread->WaitBlock, 0, sizeof(KWAIT_BLOCK)*4); + RtlZeroMemory(Thread->WaitBlock, sizeof(KWAIT_BLOCK)*4); Thread->LegoData = 0; Thread->UserAffinity = Process->Affinity; Thread->SystemAffinityActive = 0; @@ -271,7 +268,7 @@ KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First) Thread->ServiceTable = KeServiceDescriptorTable; Thread->Queue = NULL; KeInitializeSpinLock(&Thread->ApcQueueLock); - memset(&Thread->Timer, 0, sizeof(KTIMER)); + RtlZeroMemory(&Thread->Timer, sizeof(KTIMER)); KeInitializeTimer(&Thread->Timer); Thread->QueueListEntry.Flink = NULL; Thread->QueueListEntry.Blink = NULL; @@ -291,7 +288,7 @@ KeInitializeThread(PKPROCESS Process, PKTHREAD Thread, BOOLEAN First) Thread->PreviousMode = KernelMode; Thread->KernelTime = 0; Thread->UserTime = 0; - memset(&Thread->SavedApcState, 0, sizeof(KAPC_STATE)); + RtlZeroMemory(&Thread->SavedApcState, sizeof(KAPC_STATE)); Thread->ApcStateIndex = OriginalApcEnvironment; Thread->ApcQueueable = TRUE; diff --git a/reactos/ntoskrnl/ke/main.c b/reactos/ntoskrnl/ke/main.c index eef694b0bc7..572925d9a49 100644 --- a/reactos/ntoskrnl/ke/main.c +++ b/reactos/ntoskrnl/ke/main.c @@ -697,6 +697,7 @@ ExpInitializeExecutive(VOID) } #if defined(KDBG) || defined(DBG) + KdbInit(); KdbInitProfiling2(); #endif /* KDBG */ diff --git a/reactos/ntoskrnl/ps/w32call.c b/reactos/ntoskrnl/ps/w32call.c index 455fd633b7c..d6ee975793c 100644 --- a/reactos/ntoskrnl/ps/w32call.c +++ b/reactos/ntoskrnl/ps/w32call.c @@ -120,7 +120,7 @@ NtCallbackReturn (PVOID Result, else { *CallerResultLength = min(ResultLength, *CallerResultLength); - memcpy(*CallerResult, Result, *CallerResultLength); + RtlCopyMemory(*CallerResult, Result, *CallerResultLength); } } @@ -131,9 +131,9 @@ NtCallbackReturn (PVOID Result, if ((Thread->Tcb.NpxState & NPX_STATE_VALID) && ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread) { - memcpy((char*)InitialStack - sizeof(FX_SAVE_AREA), - (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA), - sizeof(FX_SAVE_AREA)); + RtlCopyMemory((char*)InitialStack - sizeof(FX_SAVE_AREA), + (char*)Thread->Tcb.InitialStack - sizeof(FX_SAVE_AREA), + sizeof(FX_SAVE_AREA)); } Thread->Tcb.InitialStack = InitialStack; Thread->Tcb.StackBase = StackBase; @@ -289,11 +289,11 @@ NtW32Call (IN ULONG RoutineIndex, AssignedStack = CONTAINING_RECORD(StackEntry, NTW32CALL_CALLBACK_STACK, ListEntry); NewStack = AssignedStack->BaseAddress; - memset(NewStack, 0, StackSize); + RtlZeroMemory(NewStack, StackSize); } /* FIXME: Need to check whether we were interrupted from v86 mode. */ - memcpy((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA), - Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD))); + RtlCopyMemory((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA), + Thread->Tcb.TrapFrame, sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD))); NewFrame = (PKTRAP_FRAME)((char*)NewStack + StackSize - sizeof(KTRAP_FRAME) - sizeof(FX_SAVE_AREA)); /* We need the stack pointer to remain 4-byte aligned */ NewFrame->Esp -= (((ArgumentLength + 3) & (~ 0x3)) + (4 * sizeof(ULONG))); @@ -303,7 +303,7 @@ NtW32Call (IN ULONG RoutineIndex, UserEsp[1] = RoutineIndex; UserEsp[2] = (ULONG)&UserEsp[4]; UserEsp[3] = ArgumentLength; - memcpy((PVOID)&UserEsp[4], Argument, ArgumentLength); + RtlCopyMemory((PVOID)&UserEsp[4], Argument, ArgumentLength); /* Switch to the new environment and return to user-mode. */ KeRaiseIrql(HIGH_LEVEL, &oldIrql); @@ -319,9 +319,9 @@ NtW32Call (IN ULONG RoutineIndex, if ((Thread->Tcb.NpxState & NPX_STATE_VALID) && ETHREAD_TO_KTHREAD(Thread) != KeGetCurrentKPCR()->PrcbData.NpxThread) { - memcpy((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA), - (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA), - sizeof(FX_SAVE_AREA)); + RtlCopyMemory((char*)NewStack + StackSize - sizeof(FX_SAVE_AREA), + (char*)SavedState.SavedInitialStack - sizeof(FX_SAVE_AREA), + sizeof(FX_SAVE_AREA)); } Thread->Tcb.InitialStack = Thread->Tcb.StackBase = (char*)NewStack + StackSize; Thread->Tcb.StackLimit = (ULONG)NewStack;