xref: /reactos/ntoskrnl/ex/dbgctrl.c (revision 5100859e)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ex/dbgctrl.c
5  * PURPOSE:         System debug control
6  * PROGRAMMERS:     Alex Ionescu
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* DATA **********************************************************************/
16 
17 // #ifdef _WINKD_
18 /*
19  * WinDBG Debugger Worker State Machine data
20  */
21 WORK_QUEUE_ITEM ExpDebuggerWorkItem;
22 /*
23  * The following global variables must be visible through all the kernel
24  * because WinDBG explicitely search for them inside our symbols.
25  */
26 WINKD_WORKER_STATE ExpDebuggerWork;
27 PEPROCESS ExpDebuggerProcessAttach;
28 PEPROCESS ExpDebuggerProcessKill;
29 ULONG_PTR ExpDebuggerPageIn;
30 // #endif /* _WINKD_ */
31 
32 /* FUNCTIONS *****************************************************************/
33 
34 // #ifdef _WINKD_
35 /*
36  * WinDBG Debugger Worker State Machine
37  *
38  * This functionality is used whenever WinDBG wants to attach or kill a user-mode
39  * process from within live kernel-mode session, and/or page-in an address region.
40  * It is implemented as a state machine: when it is in "Ready" state, WinDBG can
41  * initialize the data for the state machine, then switch its state to "Start".
42  * The worker thread balance manager detects this, switches the state to "Initialized"
43  * and queues a worker thread. As long as the state is not "Ready" again, WinDBG
44  * prevents from requeuing a new thread. When the thread is started, it captures
45  * all the data, then resets the machine state to "Ready", thus allowing WinDBG
46  * to requeue another worker thread.
47  *
48  * WinDBG commands:
49  *     .process /i <addr> (where <addr> is the address of the EPROCESS block for this process)
50  *     .kill <addr>       (       "                "                "                "       )
51  *     .pagein <addr>     (where <addr> is the address to page in)
52  */
53 VOID
54 NTAPI
55 ExpDebuggerWorker(IN PVOID Context)
56 {
57     PEPROCESS ProcessToAttach, ProcessToKill;
58     ULONG_PTR PageInAddress;
59     PEPROCESS Process;
60     KAPC_STATE ApcState;
61 
62     UNREFERENCED_PARAMETER(Context);
63 
64     /* Be sure we were started in an initialized state */
65     ASSERTMSG("ExpDebuggerWorker being entered in non-initialized state!\n",
66               ExpDebuggerWork == WinKdWorkerInitialized);
67     if (ExpDebuggerWork != WinKdWorkerInitialized)
68     {
69         /* An error happened, so get a chance to restart proper */
70         ExpDebuggerWork = WinKdWorkerReady;
71         return;
72     }
73 
74     /* Get the processes to be attached or killed, and the address to page in */
75     ProcessToAttach = ExpDebuggerProcessAttach;
76     ProcessToKill   = ExpDebuggerProcessKill;
77     PageInAddress   = ExpDebuggerPageIn;
78 
79     /* Reset the state machine to its ready state */
80     ExpDebuggerProcessAttach = NULL;
81     ExpDebuggerProcessKill   = NULL;
82     ExpDebuggerPageIn = (ULONG_PTR)NULL;
83     ExpDebuggerWork = WinKdWorkerReady;
84 
85     /* Default to the current process if we don't find the process to be attached or killed */
86     Process = NULL;
87 
88     /* Check if we need to attach or kill some process */
89     if (ProcessToAttach != NULL || ProcessToKill != NULL)
90     {
91         /* Find the process in the list */
92         Process = PsGetNextProcess(Process);
93         while (Process)
94         {
95             /* Is this the process we want to attach to? */
96             if (Process == ProcessToAttach)
97             {
98                 /* Yes, attach ourselves to it */
99                 KeStackAttachProcess(&Process->Pcb, &ApcState);
100                 break;
101             }
102             /* Or is this the process we want to kill? */
103             else if (Process == ProcessToKill)
104             {
105                 /* Yes, kill and dereference it, then return */
106                 PsTerminateProcess(Process, DBG_TERMINATE_PROCESS);
107                 ObDereferenceObject(Process);
108                 return;
109             }
110 
111             /* Get the next process */
112             Process = PsGetNextProcess(Process);
113         }
114 
115         /* We either have found a process, or we default to the current process */
116     }
117 
118     /* If we have an address to page in... */
119     if (PageInAddress)
120     {
121         /* ... try to do it by attempting to read at this address */
122         _SEH2_TRY
123         {
124             ProbeForReadUchar(PageInAddress);
125         }
126         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
127         {
128             DPRINT1("Failed to page in address 0x%p, Status 0x%08lx\n", PageInAddress, _SEH2_GetExceptionCode());
129         }
130         _SEH2_END;
131     }
132 
133     /* Break into the process (or the current one if Process == NULL) */
134     DbgBreakPointWithStatus(DBG_STATUS_WORKER);
135 
136     /* If we are attached to a process, not the current one... */
137     if (Process)
138     {
139         /* ... we can detach from the process */
140         KeUnstackDetachProcess(&ApcState);
141         /* Dereference the process which was referenced for us by PsGetNextProcess */
142         ObDereferenceObject(Process);
143     }
144 }
145 // #endif /* _WINKD_ */
146 
147 /*++
148  * @name NtSystemDebugControl
149  * @implemented
150  *
151  * Perform various queries to debugger.
152  * This API is subject to test-case creation to further evaluate its
153  * abilities (if needed to at all)
154  *
155  * See: http://www.osronline.com/showthread.cfm?link=93915
156  *      http://void.ru/files/Ntexapi.h
157  *      http://www.codeguru.com/code/legacy/system/ntexapi.zip
158  *      http://www.securityfocus.com/bid/9694
159  *
160  * @param ControlCode
161  *        Description of the parameter. Wrapped to more lines on ~70th
162  *        column.
163  *
164  * @param InputBuffer
165  *        FILLME
166  *
167  * @param InputBufferLength
168  *        FILLME
169  *
170  * @param OutputBuffer
171  *        FILLME
172  *
173  * @param OutputBufferLength
174  *        FILLME
175  *
176   * @param ReturnLength
177  *        FILLME
178  *
179  * @return STATUS_SUCCESS in case of success, proper error code otherwise
180  *
181  * @remarks None
182  *
183  *--*/
184 NTSTATUS
185 NTAPI
186 NtSystemDebugControl(SYSDBG_COMMAND ControlCode,
187                      PVOID InputBuffer,
188                      ULONG InputBufferLength,
189                      PVOID OutputBuffer,
190                      ULONG OutputBufferLength,
191                      PULONG ReturnLength)
192 {
193     switch (ControlCode)
194     {
195         case SysDbgQueryModuleInformation:
196         case SysDbgQueryTraceInformation:
197         case SysDbgSetTracepoint:
198         case SysDbgSetSpecialCall:
199         case SysDbgClearSpecialCalls:
200         case SysDbgQuerySpecialCalls:
201         case SysDbgQueryVersion:
202         case SysDbgReadVirtual:
203         case SysDbgWriteVirtual:
204         case SysDbgReadPhysical:
205         case SysDbgWritePhysical:
206         case SysDbgReadControlSpace:
207         case SysDbgWriteControlSpace:
208         case SysDbgReadIoSpace:
209         case SysDbgWriteIoSpace:
210         case SysDbgReadMsr:
211         case SysDbgWriteMsr:
212         case SysDbgReadBusData:
213         case SysDbgWriteBusData:
214         case SysDbgCheckLowMemory:
215         case SysDbgGetTriageDump:
216             return STATUS_NOT_IMPLEMENTED;
217         case SysDbgBreakPoint:
218         case SysDbgEnableKernelDebugger:
219         case SysDbgDisableKernelDebugger:
220         case SysDbgGetAutoKdEnable:
221         case SysDbgSetAutoKdEnable:
222         case SysDbgGetPrintBufferSize:
223         case SysDbgSetPrintBufferSize:
224         case SysDbgGetKdUmExceptionEnable:
225         case SysDbgSetKdUmExceptionEnable:
226         case SysDbgGetKdBlockEnable:
227         case SysDbgSetKdBlockEnable:
228             return KdSystemDebugControl(
229                 ControlCode,
230                 InputBuffer, InputBufferLength,
231                 OutputBuffer, OutputBufferLength,
232                 ReturnLength, KeGetPreviousMode());
233         default:
234             return STATUS_INVALID_INFO_CLASS;
235     }
236 }
237