[CLASS2]: Fix status code on too small as exposed by kernel32:DeviceIoControl
[reactos.git] / ntoskrnl / kd / kdmain.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/kd/kdmain.c
5 * PURPOSE: Kernel Debugger Initialization
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <debug.h>
13
14 /* VARIABLES ***************************************************************/
15
16 BOOLEAN KdDebuggerEnabled = FALSE;
17 BOOLEAN KdEnteredDebugger = FALSE;
18 BOOLEAN KdDebuggerNotPresent = TRUE;
19 BOOLEAN KdBreakAfterSymbolLoad = FALSE;
20 BOOLEAN KdpBreakPending = FALSE;
21 BOOLEAN KdPitchDebugger = TRUE;
22 BOOLEAN KdIgnoreUmExceptions = FALSE;
23 KD_CONTEXT KdpContext;
24 ULONG Kd_WIN2000_Mask;
25 VOID NTAPI PspDumpThreads(BOOLEAN SystemThreads);
26
27 typedef struct
28 {
29 ULONG ComponentId;
30 ULONG Level;
31 } KD_COMPONENT_DATA;
32 #define MAX_KD_COMPONENT_TABLE_ENTRIES 128
33 KD_COMPONENT_DATA KdComponentTable[MAX_KD_COMPONENT_TABLE_ENTRIES];
34 ULONG KdComponentTableEntries = 0;
35
36 ULONG Kd_DEFAULT_MASK = 1 << DPFLTR_ERROR_LEVEL;
37
38 /* PRIVATE FUNCTIONS *********************************************************/
39
40 ULONG
41 NTAPI
42 KdpServiceDispatcher(ULONG Service,
43 PVOID Buffer1,
44 ULONG Buffer1Length)
45 {
46 ULONG Result = 0;
47
48 switch (Service)
49 {
50 case BREAKPOINT_PRINT: /* DbgPrint */
51 Result = KdpPrintString(Buffer1, Buffer1Length);
52 break;
53
54 #if DBG
55 case ' soR': /* ROS-INTERNAL */
56 {
57 switch ((ULONG_PTR)Buffer1)
58 {
59 case DumpAllThreads:
60 PspDumpThreads(TRUE);
61 break;
62
63 case DumpUserThreads:
64 PspDumpThreads(FALSE);
65 break;
66
67 case KdSpare3:
68 MmDumpArmPfnDatabase(FALSE);
69 break;
70
71 default:
72 break;
73 }
74 break;
75 }
76
77 #if defined(_M_IX86) && !defined(_WINKD_) // See ke/i386/traphdlr.c
78 /* Register a debug callback */
79 case 'CsoR':
80 {
81 switch (Buffer1Length)
82 {
83 case ID_Win32PreServiceHook:
84 KeWin32PreServiceHook = Buffer1;
85 break;
86
87 case ID_Win32PostServiceHook:
88 KeWin32PostServiceHook = Buffer1;
89 break;
90
91 }
92 break;
93 }
94 #endif
95
96 /* Special case for stack frame dumps */
97 case 'DsoR':
98 {
99 KeRosDumpStackFrames((PULONG)Buffer1, Buffer1Length);
100 break;
101 }
102
103 #if defined(KDBG)
104 /* Register KDBG CLI callback */
105 case 'RbdK':
106 {
107 Result = KdbRegisterCliCallback(Buffer1, Buffer1Length);
108 break;
109 }
110 #endif /* KDBG */
111 #endif /* DBG */
112 default:
113 DPRINT1("Invalid debug service call!\n");
114 HalDisplayString("Invalid debug service call!\r\n");
115 break;
116 }
117
118 return Result;
119 }
120
121 BOOLEAN
122 NTAPI
123 KdpEnterDebuggerException(IN PKTRAP_FRAME TrapFrame,
124 IN PKEXCEPTION_FRAME ExceptionFrame,
125 IN PEXCEPTION_RECORD ExceptionRecord,
126 IN PCONTEXT Context,
127 IN KPROCESSOR_MODE PreviousMode,
128 IN BOOLEAN SecondChance)
129 {
130 KD_CONTINUE_TYPE Return = kdHandleException;
131 ULONG ExceptionCommand = ExceptionRecord->ExceptionInformation[0];
132
133 /* Check if this was a breakpoint due to DbgPrint or Load/UnloadSymbols */
134 if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
135 (ExceptionRecord->NumberParameters > 0) &&
136 ((ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS) ||
137 (ExceptionCommand == BREAKPOINT_UNLOAD_SYMBOLS) ||
138 (ExceptionCommand == BREAKPOINT_COMMAND_STRING) ||
139 (ExceptionCommand == BREAKPOINT_PRINT) ||
140 (ExceptionCommand == BREAKPOINT_PROMPT)))
141 {
142 /* Check if this is a debug print */
143 if (ExceptionCommand == BREAKPOINT_PRINT)
144 {
145 /* Print the string */
146 KdpServiceDispatcher(BREAKPOINT_PRINT,
147 (PVOID)ExceptionRecord->ExceptionInformation[1],
148 ExceptionRecord->ExceptionInformation[2]);
149
150 /* Return success */
151 KeSetContextReturnRegister(Context, STATUS_SUCCESS);
152 }
153 #ifdef KDBG
154 else if (ExceptionCommand == BREAKPOINT_LOAD_SYMBOLS)
155 {
156 PLDR_DATA_TABLE_ENTRY LdrEntry;
157
158 /* Load symbols. Currently implemented only for KDBG! */
159 if(KdbpSymFindModule(((PKD_SYMBOLS_INFO)ExceptionRecord->ExceptionInformation[2])->BaseOfDll, NULL, -1, &LdrEntry))
160 KdbSymProcessSymbols(LdrEntry);
161 }
162 else if (ExceptionCommand == BREAKPOINT_PROMPT)
163 {
164 ULONG ReturnValue;
165 LPSTR OutString;
166 USHORT OutStringLength;
167
168 /* Get the response string and length */
169 OutString = (LPSTR)Context->Ebx;
170 OutStringLength = (USHORT)Context->Edi;
171
172 /* Call KDBG */
173 ReturnValue = KdpPrompt((LPSTR)ExceptionRecord->
174 ExceptionInformation[1],
175 (USHORT)ExceptionRecord->
176 ExceptionInformation[2],
177 OutString,
178 OutStringLength);
179
180 /* Return the number of characters that we received */
181 Context->Eax = ReturnValue;
182 }
183 #endif
184
185 /* This we can handle: simply bump the Program Counter */
186 KeSetContextPc(Context, KeGetContextPc(Context) + KD_BREAKPOINT_SIZE);
187 return TRUE;
188 }
189
190 #ifdef KDBG
191 /* Check if this is an assertion failure */
192 if (ExceptionRecord->ExceptionCode == STATUS_ASSERTION_FAILURE)
193 {
194 /* Bump EIP to the instruction following the int 2C */
195 Context->Eip += 2;
196 }
197 #endif
198
199 /* Get out of here if the Debugger isn't connected */
200 if (KdDebuggerNotPresent) return FALSE;
201
202 #ifdef KDBG
203 /* Call KDBG if available */
204 Return = KdbEnterDebuggerException(ExceptionRecord,
205 PreviousMode,
206 Context,
207 TrapFrame,
208 !SecondChance);
209 #else /* not KDBG */
210 if (WrapperInitRoutine)
211 {
212 /* Call GDB */
213 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
214 Context,
215 TrapFrame);
216 }
217 #endif /* not KDBG */
218
219 /* Debugger didn't handle it, please handle! */
220 if (Return == kdHandleException) return FALSE;
221
222 /* Debugger handled it */
223 return TRUE;
224 }
225
226 BOOLEAN
227 NTAPI
228 KdpCallGdb(IN PKTRAP_FRAME TrapFrame,
229 IN PEXCEPTION_RECORD ExceptionRecord,
230 IN PCONTEXT Context)
231 {
232 KD_CONTINUE_TYPE Return = kdDoNotHandleException;
233
234 /* Get out of here if the Debugger isn't connected */
235 if (KdDebuggerNotPresent) return FALSE;
236
237 /* FIXME:
238 * Right now, the GDB wrapper seems to handle exceptions differntly
239 * from KDGB and both are called at different times, while the GDB
240 * one is only called once and that's it. I don't really have the knowledge
241 * to fix the GDB stub, so until then, we'll be using this hack
242 */
243 if (WrapperInitRoutine)
244 {
245 Return = WrapperTable.KdpExceptionRoutine(ExceptionRecord,
246 Context,
247 TrapFrame);
248 }
249
250 /* Debugger didn't handle it, please handle! */
251 if (Return == kdHandleException) return FALSE;
252
253 /* Debugger handled it */
254 return TRUE;
255 }
256
257 BOOLEAN
258 NTAPI
259 KdIsThisAKdTrap(IN PEXCEPTION_RECORD ExceptionRecord,
260 IN PCONTEXT Context,
261 IN KPROCESSOR_MODE PreviousMode)
262 {
263 /* KDBG has its own mechanism for ignoring user mode exceptions */
264 return FALSE;
265 }
266
267 /* PUBLIC FUNCTIONS *********************************************************/
268
269 /*
270 * @implemented
271 */
272 BOOLEAN
273 NTAPI
274 KdRefreshDebuggerNotPresent(VOID)
275 {
276 UNIMPLEMENTED;
277
278 /* Just return whatever was set previously -- FIXME! */
279 return KdDebuggerNotPresent;
280 }
281
282 /*
283 * @implemented
284 */
285 NTSTATUS
286 NTAPI
287 KdDisableDebugger(VOID)
288 {
289 KIRQL OldIrql;
290
291 /* Raise IRQL */
292 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
293
294 /* TODO: Disable any breakpoints */
295
296 /* Disable the Debugger */
297 KdDebuggerEnabled = FALSE;
298 SharedUserData->KdDebuggerEnabled = FALSE;
299
300 /* Lower the IRQL */
301 KeLowerIrql(OldIrql);
302
303 /* Return success */
304 return STATUS_SUCCESS;
305 }
306
307 /*
308 * @implemented
309 */
310 NTSTATUS
311 NTAPI
312 KdEnableDebugger(VOID)
313 {
314 KIRQL OldIrql;
315
316 /* Raise IRQL */
317 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
318
319 /* TODO: Re-enable any breakpoints */
320
321 /* Enable the Debugger */
322 KdDebuggerEnabled = TRUE;
323 SharedUserData->KdDebuggerEnabled = TRUE;
324
325 /* Lower the IRQL */
326 KeLowerIrql(OldIrql);
327
328 /* Return success */
329 return STATUS_SUCCESS;
330 }
331
332 /*
333 * @implemented
334 */
335 BOOLEAN
336 NTAPI
337 KdPollBreakIn(VOID)
338 {
339 return KdpBreakPending;
340 }
341
342 /*
343 * @unimplemented
344 */
345 NTSTATUS
346 NTAPI
347 KdPowerTransition(ULONG PowerState)
348 {
349 UNIMPLEMENTED;
350 return STATUS_NOT_IMPLEMENTED;
351 }
352
353 /*
354 * @unimplemented
355 */
356 NTSTATUS
357 NTAPI
358 KdChangeOption(IN KD_OPTION Option,
359 IN ULONG InBufferLength OPTIONAL,
360 IN PVOID InBuffer,
361 IN ULONG OutBufferLength OPTIONAL,
362 OUT PVOID OutBuffer,
363 OUT PULONG OutBufferRequiredLength OPTIONAL)
364 {
365 UNIMPLEMENTED;
366 return STATUS_NOT_IMPLEMENTED;
367 }
368
369
370 NTSTATUS
371 NTAPI
372 NtQueryDebugFilterState(IN ULONG ComponentId,
373 IN ULONG Level)
374 {
375 ULONG i;
376
377 /* Convert Level to mask if it isn't already one */
378 if (Level < 32)
379 Level = 1 << Level;
380
381 /* Check if it is not the default component */
382 if (ComponentId != MAXULONG)
383 {
384 /* No, search for an existing entry in the table */
385 for (i = 0; i < KdComponentTableEntries; i++)
386 {
387 /* Check if it is the right component */
388 if (ComponentId == KdComponentTable[i].ComponentId)
389 {
390 /* Check if mask are matching */
391 return (Level & KdComponentTable[i].Level) ? TRUE : FALSE;
392 }
393 }
394 }
395
396 /* Entry not found in the table, use default mask */
397 return (Level & Kd_DEFAULT_MASK) ? TRUE : FALSE;
398 }
399
400 NTSTATUS
401 NTAPI
402 NtSetDebugFilterState(IN ULONG ComponentId,
403 IN ULONG Level,
404 IN BOOLEAN State)
405 {
406 ULONG i;
407
408 /* Convert Level to mask if it isn't already one */
409 if (Level < 32)
410 Level = 1 << Level;
411 Level &= ~DPFLTR_MASK;
412
413 /* Check if it is the default component */
414 if (ComponentId == MAXULONG)
415 {
416 /* Yes, modify the default mask */
417 if (State)
418 Kd_DEFAULT_MASK |= Level;
419 else
420 Kd_DEFAULT_MASK &= ~Level;
421
422 return STATUS_SUCCESS;
423 }
424
425 /* Search for an existing entry */
426 for (i = 0; i < KdComponentTableEntries; i++ )
427 {
428 if (ComponentId == KdComponentTable[i].ComponentId)
429 break;
430 }
431
432 /* Check if we have found an existing entry */
433 if (i == KdComponentTableEntries)
434 {
435 /* Check if we have enough space in the table */
436 if (i == MAX_KD_COMPONENT_TABLE_ENTRIES)
437 return STATUS_INVALID_PARAMETER_1;
438
439 /* Add a new entry */
440 ++KdComponentTableEntries;
441 KdComponentTable[i].ComponentId = ComponentId;
442 KdComponentTable[i].Level = Kd_DEFAULT_MASK;
443 }
444
445 /* Update entry table */
446 if (State)
447 KdComponentTable[i].Level |= Level;
448 else
449 KdComponentTable[i].Level &= ~Level;
450
451 return STATUS_SUCCESS;
452 }
453
454 /*
455 * @unimplemented
456 */
457 NTSTATUS
458 NTAPI
459 KdSystemDebugControl(IN SYSDBG_COMMAND Command,
460 IN PVOID InputBuffer,
461 IN ULONG InputBufferLength,
462 OUT PVOID OutputBuffer,
463 IN ULONG OutputBufferLength,
464 IN OUT PULONG ReturnLength,
465 IN KPROCESSOR_MODE PreviousMode)
466 {
467 /* HACK */
468 return KdpServiceDispatcher(Command, InputBuffer, InputBufferLength);
469 }
470
471 PKDEBUG_ROUTINE KiDebugRoutine = KdpEnterDebuggerException;
472
473 /* EOF */