2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/kd64/kdapi.c
5 * PURPOSE: KD64 Public Routines and Internal Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* PRIVATE FUNCTIONS *********************************************************/
19 KdpSetCommonState(IN ULONG NewState
,
21 IN PDBGKD_WAIT_STATE_CHANGE64 WaitStateChange
)
23 USHORT InstructionCount
;
24 BOOLEAN HadBreakpoints
;
26 /* Setup common stuff available for all CPU architectures */
27 WaitStateChange
->NewState
= NewState
;
28 WaitStateChange
->ProcessorLevel
= KeProcessorLevel
;
29 WaitStateChange
->Processor
= (USHORT
)KeGetCurrentPrcb()->Number
;
30 WaitStateChange
->NumberProcessors
= (ULONG
)KeNumberProcessors
;
31 WaitStateChange
->Thread
= (ULONG
)KeGetCurrentThread();
32 WaitStateChange
->ProgramCounter
= (ULONG64
)Context
->Eip
;
34 /* Zero out the Control Report */
35 RtlZeroMemory(&WaitStateChange
->ControlReport
,
36 sizeof(DBGKD_CONTROL_REPORT
));
38 /* Now copy the instruction stream and set the count */
39 RtlCopyMemory(&WaitStateChange
->ControlReport
.InstructionStream
[0],
40 (PVOID
)(ULONG_PTR
)WaitStateChange
->ProgramCounter
,
42 InstructionCount
= DBGKD_MAXSTREAM
;
43 WaitStateChange
->ControlReport
.InstructionCount
= InstructionCount
;
45 /* Clear all the breakpoints in this region */
46 HadBreakpoints
= FALSE
;
48 KdpDeleteBreakpointRange((PVOID
)WaitStateChange
->ProgramCounter
,
49 (PVOID
)(WaitStateChange
->ProgramCounter
+
50 WaitStateChange
->ControlReport
.
51 InstructionCount
- 1));
55 /* Copy the instruction stream again, this time without breakpoints */
56 RtlCopyMemory(&WaitStateChange
->ControlReport
.InstructionStream
[0],
57 (PVOID
)(ULONG_PTR
)WaitStateChange
->ProgramCounter
,
58 WaitStateChange
->ControlReport
.InstructionCount
);
64 KdpSetContextState(IN PDBGKD_WAIT_STATE_CHANGE64 WaitStateChange
,
67 PKPRCB Prcb
= KeGetCurrentPrcb();
69 /* Copy i386 specific debug registers */
70 WaitStateChange
->ControlReport
.Dr6
= Prcb
->ProcessorState
.SpecialRegisters
.
72 WaitStateChange
->ControlReport
.Dr7
= Prcb
->ProcessorState
.SpecialRegisters
.
75 /* Copy i386 specific segments */
76 WaitStateChange
->ControlReport
.SegCs
= (USHORT
)Context
->SegCs
;
77 WaitStateChange
->ControlReport
.SegDs
= (USHORT
)Context
->SegDs
;
78 WaitStateChange
->ControlReport
.SegEs
= (USHORT
)Context
->SegEs
;
79 WaitStateChange
->ControlReport
.SegFs
= (USHORT
)Context
->SegFs
;
82 WaitStateChange
->ControlReport
.EFlags
= Context
->EFlags
;
84 /* Set Report Flags */
85 WaitStateChange
->ControlReport
.ReportFlags
= REPORT_INCLUDES_SEGS
;
86 if (WaitStateChange
->ControlReport
.SegCs
== KGDT_R0_CODE
)
88 WaitStateChange
->ControlReport
.ReportFlags
= REPORT_INCLUDES_CS
;
94 KdpSysGetVersion(IN PDBGKD_GET_VERSION64 Version
)
96 /* Copy the version block */
97 RtlCopyMemory(Version
, &KdVersionBlock
, sizeof(DBGKD_GET_VERSION64
));
102 KdpGetVersion(IN PDBGKD_MANIPULATE_STATE64 State
)
106 /* Fill out the header */
107 Header
.Length
= sizeof(DBGKD_MANIPULATE_STATE64
);
108 Header
.Buffer
= (PCHAR
)State
;
110 /* Get the version block */
111 KdpSysGetVersion(&State
->u
.GetVersion64
);
113 /* Fill out the state */
114 State
->ApiNumber
= DbgKdGetVersionApi
;
115 State
->ReturnStatus
= STATUS_SUCCESS
;
117 /* Send the packet */
118 KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
125 BOOLEAN VirtCalled
= FALSE
;
129 KdpReadVirtualMemory(IN PDBGKD_MANIPULATE_STATE64 State
,
134 ULONG Length
= State
->u
.ReadMemory
.TransferCount
;
135 NTSTATUS Status
= STATUS_SUCCESS
;
137 /* Validate length */
138 if (Length
> (PACKET_MAX_SIZE
- sizeof(DBGKD_MANIPULATE_STATE64
)))
140 /* Overflow, set it to maximum possible */
141 Length
= PACKET_MAX_SIZE
- sizeof(DBGKD_MANIPULATE_STATE64
);
145 if (!MmIsAddressValid((PVOID
)(ULONG_PTR
)State
->u
.ReadMemory
.TargetBaseAddress
))
147 Ke386SetCr2(State
->u
.ReadMemory
.TargetBaseAddress
);
152 if ((ULONG_PTR
)State
->u
.ReadMemory
.TargetBaseAddress
< KSEG0_BASE
)
155 Status
= STATUS_UNSUCCESSFUL
;
157 else if ((ULONG_PTR
)State
->u
.ReadMemory
.TargetBaseAddress
>= (ULONG_PTR
)SharedUserData
)
160 Status
= STATUS_UNSUCCESSFUL
;
164 RtlCopyMemory(Data
->Buffer
,
165 (PVOID
)(ULONG_PTR
)State
->u
.ReadMemory
.TargetBaseAddress
,
169 /* Fill out the header */
170 Data
->Length
= Length
;
171 Header
.Length
= sizeof(DBGKD_MANIPULATE_STATE64
);
172 Header
.Buffer
= (PCHAR
)State
;
174 /* Fill out the state */
175 State
->ReturnStatus
= Status
;
176 State
->u
.ReadMemory
.ActualBytesRead
= Length
;
178 /* Send the packet */
179 KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
187 KdpReadControlSpace(IN PDBGKD_MANIPULATE_STATE64 State
,
191 PDBGKD_READ_MEMORY64 ReadMemory
= &State
->u
.ReadMemory
;
193 ULONG Length
, RealLength
;
196 /* Setup the header */
197 Header
.Length
= sizeof(DBGKD_MANIPULATE_STATE64
);
198 Header
.Buffer
= (PCHAR
)State
;
199 ASSERT(Data
->Length
== 0);
201 /* Check the length requested */
202 Length
= ReadMemory
->TransferCount
;
203 if (Length
> (PACKET_MAX_SIZE
- sizeof(DBGKD_MANIPULATE_STATE64
)))
205 /* Use maximum allowed */
206 Length
= PACKET_MAX_SIZE
- sizeof(DBGKD_MANIPULATE_STATE64
);
209 /* Make sure that this is a valid request */
210 if (((ULONG
)ReadMemory
->TargetBaseAddress
< sizeof(KPROCESSOR_STATE
)) &&
211 (State
->Processor
< KeNumberProcessors
))
213 /* Get the actual length */
214 RealLength
= sizeof(KPROCESSOR_STATE
) -
215 (ULONG_PTR
)ReadMemory
->TargetBaseAddress
;
216 if (RealLength
< Length
) Length
= RealLength
;
218 /* Set the proper address */
219 ControlStart
= (PVOID
)((ULONG_PTR
)ReadMemory
->TargetBaseAddress
+
220 (ULONG_PTR
)&KiProcessorBlock
[State
->Processor
]->
223 /* Copy the memory */
224 RtlCopyMemory(Data
->Buffer
, ControlStart
, Length
);
225 Data
->Length
= Length
;
228 State
->ReturnStatus
= STATUS_SUCCESS
;
229 ReadMemory
->ActualBytesRead
= Data
->Length
;
233 /* Invalid request */
235 State
->ReturnStatus
= STATUS_UNSUCCESSFUL
;
236 ReadMemory
->ActualBytesRead
= 0;
240 KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
248 KdpRestoreBreakpoint(IN PDBGKD_MANIPULATE_STATE64 State
,
252 PDBGKD_RESTORE_BREAKPOINT RestoreBp
= &State
->u
.RestoreBreakPoint
;
255 /* Fill out the header */
256 Header
.Length
= sizeof(DBGKD_MANIPULATE_STATE64
);
257 Header
.Buffer
= (PCHAR
)State
;
258 ASSERT(Data
->Length
== 0);
260 /* Get the version block */
261 if (KdpDeleteBreakpoint(RestoreBp
->BreakPointHandle
))
264 State
->ReturnStatus
= STATUS_SUCCESS
;
269 State
->ReturnStatus
= STATUS_UNSUCCESSFUL
;
272 /* Send the packet */
273 KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
281 KdpGetContext(IN PDBGKD_MANIPULATE_STATE64 State
,
288 /* Setup the header */
289 Header
.Length
= sizeof(DBGKD_MANIPULATE_STATE64
);
290 Header
.Buffer
= (PCHAR
)State
;
291 ASSERT(Data
->Length
== 0);
293 /* Make sure that this is a valid request */
294 if (State
->Processor
< KeNumberProcessors
)
296 /* Check if the request is for this CPU */
297 if (State
->Processor
== KeGetCurrentPrcb()->Number
)
299 /* We're just copying our own context */
300 ControlStart
= Context
;
304 /* SMP not yet handled */
309 /* Copy the memory */
310 RtlCopyMemory(Data
->Buffer
, ControlStart
, sizeof(CONTEXT
));
311 Data
->Length
= sizeof(CONTEXT
);
314 State
->ReturnStatus
= STATUS_SUCCESS
;
318 /* Invalid request */
319 State
->ReturnStatus
= STATUS_UNSUCCESSFUL
;
323 KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
332 KdpSendWaitContinue(IN ULONG PacketType
,
333 IN PSTRING SendHeader
,
334 IN PSTRING SendData OPTIONAL
,
335 IN OUT PCONTEXT Context
)
338 DBGKD_MANIPULATE_STATE64 ManipulateState
;
342 /* Setup the Manipulate State structure */
343 Header
.MaximumLength
= sizeof(DBGKD_MANIPULATE_STATE64
);
344 Header
.Buffer
= (PCHAR
)&ManipulateState
;
345 Data
.MaximumLength
= sizeof(KdpMessageBuffer
);
346 Data
.Buffer
= KdpMessageBuffer
;
347 //KdpContextSent = FALSE;
350 /* Send the Packet */
351 KdSendPacket(PacketType
, SendHeader
, SendData
, &KdpContext
);
353 /* If the debugger isn't present anymore, just return success */
354 if (KdDebuggerNotPresent
) return ContinueSuccess
;
356 /* Main processing Loop */
362 /* Wait to get a reply to our packet */
363 RecvCode
= KdReceivePacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
369 /* If we got a resend request, do it */
370 if (RecvCode
== KdPacketNeedsResend
) goto SendPacket
;
371 } while (RecvCode
== KdPacketTimedOut
);
373 /* Now check what API we got */
374 switch (ManipulateState
.ApiNumber
)
376 case DbgKdReadVirtualMemoryApi
:
378 /* Read virtual memory */
379 KdpReadVirtualMemory(&ManipulateState
, &Data
, Context
);
383 case DbgKdWriteVirtualMemoryApi
:
386 Ke386SetCr2(DbgKdWriteVirtualMemoryApi
);
390 case DbgKdGetContextApi
:
393 KdpGetContext(&ManipulateState
, &Data
, Context
);
396 case DbgKdSetContextApi
:
399 Ke386SetCr2(DbgKdSetContextApi
);
403 case DbgKdWriteBreakPointApi
:
406 Ke386SetCr2(DbgKdWriteBreakPointApi
);
410 case DbgKdRestoreBreakPointApi
:
413 KdpRestoreBreakpoint(&ManipulateState
, &Data
, Context
);
416 case DbgKdContinueApi
:
419 Ke386SetCr2(DbgKdContinueApi
);
423 case DbgKdReadControlSpaceApi
:
425 /* Read control space */
426 KdpReadControlSpace(&ManipulateState
, &Data
, Context
);
429 case DbgKdWriteControlSpaceApi
:
432 Ke386SetCr2(DbgKdWriteControlSpaceApi
);
436 case DbgKdReadIoSpaceApi
:
439 Ke386SetCr2(DbgKdReadIoSpaceApi
);
443 case DbgKdWriteIoSpaceApi
:
446 Ke386SetCr2(DbgKdWriteIoSpaceApi
);
453 Ke386SetCr2(DbgKdRebootApi
);
457 case DbgKdContinueApi2
:
460 Ke386SetCr2(DbgKdContinueApi2
);
464 case DbgKdReadPhysicalMemoryApi
:
468 Ke386SetCr2(DbgKdReadPhysicalMemoryApi
);
472 case DbgKdWritePhysicalMemoryApi
:
475 Ke386SetCr2(DbgKdWritePhysicalMemoryApi
);
479 case DbgKdQuerySpecialCallsApi
:
482 Ke386SetCr2(DbgKdQuerySpecialCallsApi
);
486 case DbgKdSetSpecialCallApi
:
489 Ke386SetCr2(DbgKdSetSpecialCallApi
);
493 case DbgKdClearSpecialCallsApi
:
496 Ke386SetCr2(DbgKdClearSpecialCallsApi
);
500 case DbgKdSetInternalBreakPointApi
:
503 Ke386SetCr2(DbgKdSetInternalBreakPointApi
);
507 case DbgKdGetInternalBreakPointApi
:
510 Ke386SetCr2(DbgKdGetInternalBreakPointApi
);
514 case DbgKdReadIoSpaceExtendedApi
:
517 Ke386SetCr2(DbgKdReadIoSpaceExtendedApi
);
521 case DbgKdWriteIoSpaceExtendedApi
:
524 Ke386SetCr2(DbgKdWriteIoSpaceExtendedApi
);
528 case DbgKdGetVersionApi
:
530 /* Get version data */
531 KdpGetVersion(&ManipulateState
);
534 case DbgKdWriteBreakPointExApi
:
537 Ke386SetCr2(DbgKdWriteBreakPointExApi
);
541 case DbgKdRestoreBreakPointExApi
:
544 Ke386SetCr2(DbgKdRestoreBreakPointExApi
);
548 case DbgKdCauseBugCheckApi
:
551 Ke386SetCr2(DbgKdCauseBugCheckApi
);
555 case DbgKdSwitchProcessor
:
558 Ke386SetCr2(DbgKdSwitchProcessor
);
565 Ke386SetCr2(DbgKdPageInApi
);
569 case DbgKdReadMachineSpecificRegister
:
572 Ke386SetCr2(DbgKdReadMachineSpecificRegister
);
576 case DbgKdWriteMachineSpecificRegister
:
579 Ke386SetCr2(DbgKdWriteMachineSpecificRegister
);
586 Ke386SetCr2(OldVlm1
);
593 Ke386SetCr2(OldVlm2
);
597 case DbgKdSearchMemoryApi
:
600 Ke386SetCr2(DbgKdSearchMemoryApi
);
604 case DbgKdGetBusDataApi
:
607 Ke386SetCr2(DbgKdGetBusDataApi
);
611 case DbgKdSetBusDataApi
:
614 Ke386SetCr2(DbgKdSetBusDataApi
);
618 case DbgKdCheckLowMemoryApi
:
621 Ke386SetCr2(DbgKdCheckLowMemoryApi
);
625 case DbgKdClearAllInternalBreakpointsApi
:
627 /* Just clear the counter */
628 KdpNumInternalBreakpoints
= 0;
631 case DbgKdFillMemoryApi
:
634 Ke386SetCr2(DbgKdFillMemoryApi
);
638 case DbgKdQueryMemoryApi
:
641 Ke386SetCr2(DbgKdQueryMemoryApi
);
645 case DbgKdSwitchPartition
:
648 Ke386SetCr2(DbgKdSwitchPartition
);
652 /* Unsupported Message */
655 /* Setup an empty message, with failure */
659 ManipulateState
.ReturnStatus
= STATUS_UNSUCCESSFUL
;
662 KdSendPacket(PACKET_TYPE_KD_STATE_MANIPULATE
,
673 KdpReportLoadSymbolsStateChange(IN PSTRING PathName
,
674 IN PKD_SYMBOLS_INFO SymbolInfo
,
676 IN OUT PCONTEXT Context
)
680 DBGKD_WAIT_STATE_CHANGE64 WaitStateChange
;
681 KCONTINUE_STATUS Status
;
683 /* Start wait loop */
686 /* Build the architecture common parts of the message */
687 KdpSetCommonState(DbgKdLoadSymbolsStateChange
,
691 /* Now finish creating the structure */
692 KdpSetContextState(&WaitStateChange
, Context
);
694 /* Fill out load data */
695 WaitStateChange
.u
.LoadSymbols
.UnloadSymbols
= Unload
;
696 WaitStateChange
.u
.LoadSymbols
.BaseOfDll
= (ULONG
)SymbolInfo
->BaseOfDll
;
697 WaitStateChange
.u
.LoadSymbols
.ProcessId
= SymbolInfo
->ProcessId
;
698 WaitStateChange
.u
.LoadSymbols
.CheckSum
= SymbolInfo
->CheckSum
;
699 WaitStateChange
.u
.LoadSymbols
.SizeOfImage
= SymbolInfo
->SizeOfImage
;
701 /* Check if we have a symbol name */
704 /* Setup the information */
705 WaitStateChange
.u
.LoadSymbols
.PathNameLength
= PathName
->Length
;
706 Data
.Buffer
= KdpPathBuffer
;
707 Data
.Length
= WaitStateChange
.u
.LoadSymbols
.PathNameLength
;
713 WaitStateChange
.u
.LoadSymbols
.PathNameLength
= 0;
717 /* Setup the header */
718 Header
.Length
= sizeof(DBGKD_WAIT_STATE_CHANGE64
);
719 Header
.Buffer
= (PCHAR
)&WaitStateChange
;
721 /* Send the packet */
722 Status
= KdpSendWaitContinue(PACKET_TYPE_KD_STATE_CHANGE64
,
726 } while(Status
== ContinueProcessorReselected
);
735 KdpTimeSlipDpcRoutine(IN PKDPC Dpc
,
736 IN PVOID DeferredContext
,
737 IN PVOID SystemArgument1
,
738 IN PVOID SystemArgument2
)
740 LONG OldSlip
, NewSlip
, PendingSlip
;
742 /* Get the current pending slip */
743 PendingSlip
= KdpTimeSlipPending
;
746 /* Save the old value and either disable or enable it now. */
747 OldSlip
= PendingSlip
;
748 NewSlip
= OldSlip
> 1 ? 1 : 0;
750 /* Try to change the value */
751 } while (InterlockedCompareExchange(&KdpTimeSlipPending
,
753 OldSlip
) != OldSlip
);
755 /* If the New Slip value is 1, then do the Time Slipping */
756 if (NewSlip
) ExQueueWorkItem(&KdpTimeSlipWorkItem
, DelayedWorkQueue
);
761 KdpTimeSlipWork(IN PVOID Context
)
764 LARGE_INTEGER DueTime
;
766 /* Update the System time from the CMOS */
767 ExAcquireTimeRefreshLock(FALSE
);
768 ExUpdateSystemTimeFromCmos(FALSE
, 0);
769 ExReleaseTimeRefreshLock();
771 /* Check if we have a registered Time Slip Event and signal it */
772 KeAcquireSpinLock(&KdpTimeSlipEventLock
, &OldIrql
);
773 if (KdpTimeSlipEvent
) KeSetEvent(KdpTimeSlipEvent
, 0, FALSE
);
774 KeReleaseSpinLock(&KdpTimeSlipEventLock
, OldIrql
);
776 /* Delay the DPC until it runs next time */
777 DueTime
.QuadPart
= -1800000000;
778 KeSetTimer(&KdpTimeSlipTimer
, DueTime
, &KdpTimeSlipDpc
);
783 KdpSwitchProcessor(IN PEXCEPTION_RECORD ExceptionRecord
,
784 IN OUT PCONTEXT ContextRecord
,
785 IN BOOLEAN SecondChanceException
)
789 /* Save the port data */
792 /* Report a state change */
794 Status
= KdpReportExceptionStateChange(ExceptionRecord
,
796 SecondChanceException
);
801 /* Restore the port data and return */
808 KdpQueryPerformanceCounter(IN PKTRAP_FRAME TrapFrame
)
810 LARGE_INTEGER Null
= {{0}};
812 /* Check if interrupts were disabled */
813 if (!(TrapFrame
->EFlags
& EFLAGS_INTERRUPT_MASK
))
815 /* Nothing to return */
819 /* Otherwise, do the call */
820 return KeQueryPerformanceCounter(NULL
);
825 KdEnterDebugger(IN PKTRAP_FRAME TrapFrame
,
826 IN PKEXCEPTION_FRAME ExceptionFrame
)
830 /* Check if we have a trap frame */
833 /* Calculate the time difference for the enter */
834 KdTimerStop
= KdpQueryPerformanceCounter(TrapFrame
);
835 KdTimerDifference
.QuadPart
= KdTimerStop
.QuadPart
-
836 KdTimerStart
.QuadPart
;
840 /* No trap frame, so can't calculate */
841 KdTimerStop
.QuadPart
= 0;
844 /* Save the current IRQL */
845 KeGetCurrentPrcb()->DebuggerSavedIRQL
= KeGetCurrentIrql();
847 /* Freeze all CPUs */
848 Entered
= KeFreezeExecution(TrapFrame
, ExceptionFrame
);
850 /* Lock the port, save the state and set debugger entered */
851 KdpPortLocked
= KeTryToAcquireSpinLockAtDpcLevel(&KdpDebuggerLock
);
853 KdEnteredDebugger
= TRUE
;
855 /* Check freeze flag */
856 if (KiFreezeFlag
& 1)
858 /* Print out errror */
859 DbgPrint("FreezeLock was jammed! Backup SpinLock was used!\n");
862 /* Check processor state */
863 if (KiFreezeFlag
& 2)
865 /* Print out errror */
866 DbgPrint("Some processors not frozen in debugger!\n");
869 /* Make sure we acquired the port */
870 if (!KdpPortLocked
) DbgPrint("Port lock was not acquired!\n");
872 /* Return enter state */
878 KdExitDebugger(IN BOOLEAN Entered
)
882 /* Restore the state and unlock the port */
884 if (KdpPortLocked
) KdpPortUnlock();
886 /* Unfreeze the CPUs */
887 KeThawExecution(Entered
);
889 /* Compare time with the one from KdEnterDebugger */
890 if (!KdTimerStop
.QuadPart
)
892 /* We didn't get a trap frame earlier in so never got the time */
893 KdTimerStart
= KdTimerStop
;
897 /* Query the timer */
898 KdTimerStart
= KeQueryPerformanceCounter(NULL
);
901 /* Check if a Time Slip was on queue */
902 TimeSlip
= InterlockedIncrement(&KdpTimeSlipPending
);
905 /* Queue a DPC for the time slip */
906 InterlockedIncrement(&KdpTimeSlipPending
);
907 KeInsertQueueDpc(&KdpTimeSlipDpc
, NULL
, NULL
);
913 KdEnableDebuggerWithLock(BOOLEAN NeedLock
)
917 /* Check if we need to acquire the lock */
921 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
925 /* Check if we're not disabled */
928 /* Check if we had locked the port before */
932 KeLowerIrql(OldIrql
);
936 /* Fail: We're already enabled */
937 return STATUS_INVALID_PARAMETER
;
940 /* Decrease the disable count */
941 if (!(--KdDisableCount
))
943 /* We're now enabled again! Were we enabled before, too? */
944 if (KdPreviouslyEnabled
)
946 /* Reinitialize the Debugger */
947 KdInitSystem(0, NULL
) ;
948 KdpRestoreAllBreakpoints();
952 /* Check if we had locked the port before */
955 /* Yes, now unlock it */
956 KeLowerIrql(OldIrql
);
961 return STATUS_SUCCESS
;
964 /* PUBLIC FUNCTIONS **********************************************************/
971 KdEnableDebugger(VOID
)
973 /* Use the internal routine */
975 return KdEnableDebuggerWithLock(TRUE
);