BOOLEAN KdpTrap ( IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN PEXCEPTION_RECORD ExceptionRecord, IN PCONTEXT ContextRecord, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN SecondChance ) /*++ Routine Description: This routine is called whenever a exception is dispatched and the kernel debugger is active. Arguments: TrapFrame - Supplies a pointer to a trap frame that describes the trap. ExceptionFrame - Supplies a pointer to a exception frame that describes the trap. ExceptionRecord - Supplies a pointer to an exception record that describes the exception. ContextRecord - Supplies the context at the time of the exception. PreviousMode - Supplies the previous processor mode. SecondChance - Supplies a boolean value that determines whether this is the second chance (TRUE) that the exception has been raised. Return Value: A value of TRUE is returned if the exception is handled. Otherwise a value of FALSE is returned. --*/ { BOOLEAN Completion; BOOLEAN Enable; BOOLEAN UnloadSymbols = FALSE; ULONG OldIar; STRING Input; STRING Output; PKPRCB Prcb; // // Synchronize processor execution, save processor state, enter debugger, // and flush the current TB. // re_enter_debugger: Enable = KdEnterDebugger(TrapFrame, ExceptionFrame); Prcb = KeGetCurrentPrcb(); KiSaveProcessorState(TrapFrame, ExceptionFrame); KeFlushCurrentTb(); // // If this is a breakpoint instruction, then check to determine if is // an internal command. // if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) && ((ExceptionRecord->ExceptionInformation[0] & BREAKPOINT_CODE_MASK) >= DEBUG_PRINT_BREAKPOINT)) { // // Switch on the breakpoint code. // switch (ExceptionRecord->ExceptionInformation[0] & BREAKPOINT_CODE_MASK) { // // Print a debug string. // // Arguments: // // a0 - Supplies a pointer to an output string buffer. // a1 - Supplies the length of the output string buffer. // case DEBUG_PRINT_BREAKPOINT: ContextRecord->Iar += 4; Output.Buffer = (PCHAR)ContextRecord->Gpr3; Output.Length = (USHORT)ContextRecord->Gpr4; if (KdDebuggerNotPresent == FALSE) { if (KdpPrintString(&Output)) { ContextRecord->Gpr3 = (ULONG)STATUS_BREAKPOINT; } else { ContextRecord->Gpr3 = (ULONG)STATUS_SUCCESS; } } else { ContextRecord->Gpr3 = (ULONG)STATUS_DEVICE_NOT_CONNECTED; } KiRestoreProcessorState(TrapFrame, ExceptionFrame); KdExitDebugger(Enable); return TRUE; // // Print a debug prompt string, then input a string. // // r.3 - Supplies a pointer to an output string buffer. // r.4 - Supplies the length of the output string buffer.. // r.5 - supplies a pointer to an input string buffer. // r.6 - Supplies the length of the input string bufffer. // case DEBUG_PROMPT_BREAKPOINT: ContextRecord->Iar += 4; Output.Buffer = (PCHAR)ContextRecord->Gpr3; Output.Length = (USHORT)ContextRecord->Gpr4; Input.Buffer = (PCHAR)ContextRecord->Gpr5; Input.MaximumLength = (USHORT)ContextRecord->Gpr6; KdpPromptString(&Output, &Input); ContextRecord->Gpr3 = Input.Length; KiRestoreProcessorState(TrapFrame, ExceptionFrame); KdExitDebugger(Enable); return TRUE; // // Load the symbolic information for an image. // // Arguments: // // r.3 - Supplies a pointer to an output string descriptor. // r.4 - Supplies a the base address of the image. // case DEBUG_UNLOAD_SYMBOLS_BREAKPOINT: UnloadSymbols = TRUE; // // Fall through // case DEBUG_LOAD_SYMBOLS_BREAKPOINT: OldIar = ContextRecord->Iar; if (KdDebuggerNotPresent == FALSE) { KdpReportLoadSymbolsStateChange((PSTRING)ContextRecord->Gpr3, (PKD_SYMBOLS_INFO) ContextRecord->Gpr4, UnloadSymbols, &Prcb->ProcessorState.ContextFrame); } RtlCopyMemory(ContextRecord, &Prcb->ProcessorState.ContextFrame, sizeof(CONTEXT)); KiRestoreProcessorState(TrapFrame, ExceptionFrame); KdExitDebugger(Enable); // // If the kernel debugger did not update the IAR, then increment // past the breakpoint instruction. // if (ContextRecord->Iar == OldIar) { ContextRecord->Iar += 4; } return TRUE; // // Unknown internal command. // default: break; } } // // Report state change to kernel debugger on host machine. // Completion = KdpReportExceptionStateChange( ExceptionRecord, &Prcb->ProcessorState.ContextFrame, SecondChance); RtlCopyMemory(ContextRecord, &Prcb->ProcessorState.ContextFrame, sizeof(CONTEXT)); KiRestoreProcessorState(TrapFrame, ExceptionFrame); KdExitDebugger(Enable); // // check to see if the user of the remote debugger // requested memory to be paged in // if (KdpPageInAddress) { if (KeGetCurrentIrql() <= APC_LEVEL) { // // if the IQRL is below DPC level then cause // the page fault to occur and then re-enter // the debugger. this whole process is transparent // to the user. // KdpPageInData( (PUCHAR)KdpPageInAddress ); KdpPageInAddress = 0; KdpControlCPending = FALSE; goto re_enter_debugger; } else { // // we cannot take a page fault // here so a worker item is queued to take the // page fault. after the worker item takes the // page fault it sets the contol-c flag so that // the user re-enters the debugger just as if // control-c was pressed. // if (KdpControlCPressed) { ExInitializeWorkItem( &KdpPageInWorkItem, (PWORKER_THREAD_ROUTINE) KdpPageInData, (PVOID) KdpPageInAddress ); ExQueueWorkItem( &KdpPageInWorkItem, DelayedWorkQueue ); KdpPageInAddress = 0; } } } KdpControlCPressed = FALSE; return Completion; }
DECLSPEC_NORETURN VOID __cdecl KiTrap02(VOID) { PKTSS Tss, NmiTss; PKTHREAD Thread; PKPROCESS Process; PKGDTENTRY TssGdt; KTRAP_FRAME TrapFrame; KIRQL OldIrql; // // In some sort of strange recursion case, we might end up here with the IF // flag incorrectly on the interrupt frame -- during a normal NMI this would // normally already be set. // // For sanity's sake, make sure interrupts are disabled for sure. // NMIs will already be since the CPU does it for us. // _disable(); // // Get the current TSS, thread, and process // Tss = PCR->TSS; Thread = ((PKIPCR)PCR)->PrcbData.CurrentThread; Process = Thread->ApcState.Process; // // Save data usually not in the TSS // Tss->CR3 = Process->DirectoryTableBase[0]; Tss->IoMapBase = Process->IopmOffset; Tss->LDT = Process->LdtDescriptor.LimitLow ? KGDT_LDT : 0; // // Now get the base address of the NMI TSS // TssGdt = &((PKIPCR)KeGetPcr())->GDT[KGDT_NMI_TSS / sizeof(KGDTENTRY)]; NmiTss = (PKTSS)(ULONG_PTR)(TssGdt->BaseLow | TssGdt->HighWord.Bytes.BaseMid << 16 | TssGdt->HighWord.Bytes.BaseHi << 24); // // Switch to it and activate it, masking off the nested flag // // Note that in reality, we are already on the NMI tss -- we just need to // update the PCR to reflect this // PCR->TSS = NmiTss; __writeeflags(__readeflags() &~ EFLAGS_NESTED_TASK); TssGdt->HighWord.Bits.Dpl = 0; TssGdt->HighWord.Bits.Pres = 1; TssGdt->HighWord.Bits.Type = I386_TSS; // // Now build the trap frame based on the original TSS // // The CPU does a hardware "Context switch" / task switch of sorts and so it // takes care of saving our context in the normal TSS. // // We just have to go get the values... // RtlZeroMemory(&TrapFrame, sizeof(KTRAP_FRAME)); TrapFrame.HardwareSegSs = Tss->Ss0; TrapFrame.HardwareEsp = Tss->Esp0; TrapFrame.EFlags = Tss->EFlags; TrapFrame.SegCs = Tss->Cs; TrapFrame.Eip = Tss->Eip; TrapFrame.Ebp = Tss->Ebp; TrapFrame.Ebx = Tss->Ebx; TrapFrame.Esi = Tss->Esi; TrapFrame.Edi = Tss->Edi; TrapFrame.SegFs = Tss->Fs; TrapFrame.ExceptionList = PCR->NtTib.ExceptionList; TrapFrame.PreviousPreviousMode = -1; TrapFrame.Eax = Tss->Eax; TrapFrame.Ecx = Tss->Ecx; TrapFrame.Edx = Tss->Edx; TrapFrame.SegDs = Tss->Ds; TrapFrame.SegEs = Tss->Es; TrapFrame.SegGs = Tss->Gs; TrapFrame.DbgEip = Tss->Eip; TrapFrame.DbgEbp = Tss->Ebp; // // Store the trap frame in the KPRCB // KiSaveProcessorState(&TrapFrame, NULL); // // Call any registered NMI handlers and see if they handled it or not // if (!KiHandleNmi()) { // // They did not, so call the platform HAL routine to bugcheck the system // // Make sure the HAL believes it's running at HIGH IRQL... we can't use // the normal APIs here as playing with the IRQL could change the system // state // OldIrql = PCR->Irql; PCR->Irql = HIGH_LEVEL; HalHandleNMI(NULL); PCR->Irql = OldIrql; } // // Although the CPU disabled NMIs, we just did a BIOS Call, which could've // totally changed things. // // We have to make sure we're still in our original NMI -- a nested NMI // will point back to the NMI TSS, and in that case we're hosed. // if (PCR->TSS->Backlink != KGDT_NMI_TSS) { // // Restore original TSS // PCR->TSS = Tss; // // Set it back to busy // TssGdt->HighWord.Bits.Dpl = 0; TssGdt->HighWord.Bits.Pres = 1; TssGdt->HighWord.Bits.Type = I386_ACTIVE_TSS; // // Restore nested flag // __writeeflags(__readeflags() | EFLAGS_NESTED_TASK); // // Handled, return from interrupt // KiIret(); } // // Unhandled: crash the system // KiSystemFatalException(EXCEPTION_NMI, NULL); }
VOID KiFreezeTargetExecution ( IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame ) /*++ Routine Description: This function freezes the execution of the current running processor. If a trapframe is supplied to current state is saved into the prcb for the debugger. Arguments: TrapFrame - Supplies a pointer to the trap frame that describes the trap. ExceptionFrame - Supplies a pointer to the exception frame that describes the trap. Return Value: None. --*/ { #if !defined(NT_UP) KIRQL OldIrql; PKPRCB Prcb; BOOLEAN Enable; KCONTINUE_STATUS Status; EXCEPTION_RECORD ExceptionRecord; Enable = KiDisableInterrupts(); KeRaiseIrql(HIGH_LEVEL, &OldIrql); Prcb = KeGetCurrentPrcb(); Prcb->IpiFrozen = TARGET_FROZEN; Prcb->SkipTick = TRUE; if (TrapFrame != NULL) { KiSaveProcessorState(TrapFrame, ExceptionFrame); } // // Sweep the data cache in case this is a system crash and the bug // check code is attempting to write a crash dump file. // KeSweepCurrentDcache(); // // Wait for person requesting us to freeze to // clear our frozen flag // while (FrozenState(Prcb->IpiFrozen) == TARGET_FROZEN) { if (Prcb->IpiFrozen & FREEZE_ACTIVE) { // // This processor has been made the active processor // if (TrapFrame) { RtlZeroMemory (&ExceptionRecord, sizeof ExceptionRecord); ExceptionRecord.ExceptionCode = STATUS_WAKE_SYSTEM_DEBUGGER; ExceptionRecord.ExceptionRecord = &ExceptionRecord; ExceptionRecord.ExceptionAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER (&Prcb->ProcessorState.ContextFrame); Status = (KiDebugSwitchRoutine) ( &ExceptionRecord, &Prcb->ProcessorState.ContextFrame, FALSE ); } else { Status = ContinueError; } // // If status is anything other then, continue with next // processor then reselect master // if (Status != ContinueNextProcessor) { Prcb->IpiFrozen &= ~FREEZE_ACTIVE; KiFreezeOwner->IpiFrozen |= FREEZE_ACTIVE; } } KeYieldProcessor(); } if (TrapFrame != NULL) { KiRestoreProcessorState(TrapFrame, ExceptionFrame); } Prcb->IpiFrozen = RUNNING; KeFlushCurrentTb(); KeSweepCurrentIcache(); KeLowerIrql(OldIrql); KiRestoreInterrupts(Enable); #endif // !define(NT_UP) return; }