[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / kd / kdio.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/kd/kdio.c
5 * PURPOSE: NT Kernel Debugger Input/Output Functions
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #include <reactos/buildno.h>
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 #define KdpBufferSize (1024 * 512)
19 BOOLEAN KdpLoggingEnabled = FALSE;
20 PCHAR KdpDebugBuffer = NULL;
21 volatile ULONG KdpCurrentPosition = 0;
22 volatile ULONG KdpFreeBytes = 0;
23 KSPIN_LOCK KdpDebugLogSpinLock;
24 KEVENT KdpLoggerThreadEvent;
25 HANDLE KdpLogFileHandle;
26 ANSI_STRING KdpLogFileName = RTL_CONSTANT_STRING("\\SystemRoot\\debug.log");
27
28 KSPIN_LOCK KdpSerialSpinLock;
29 KD_PORT_INFORMATION SerialPortInfo = { DEFAULT_DEBUG_PORT, DEFAULT_DEBUG_BAUD_RATE, 0 };
30
31 /* Current Port in use. FIXME: Do we support more then one? */
32 ULONG KdpPort;
33
34 #define KdpScreenLineLenght 80
35 CHAR KdpScreenLineBuffer[KdpScreenLineLenght + 1] = "";
36 ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
37
38 const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024;
39 PCHAR KdpDmesgBuffer = NULL;
40 volatile ULONG KdpDmesgCurrentPosition = 0;
41 volatile ULONG KdpDmesgFreeBytes = 0;
42 volatile ULONG KdbDmesgTotalWritten = 0;
43 KSPIN_LOCK KdpDmesgLogSpinLock;
44 volatile BOOLEAN KdbpIsInDmesgMode = FALSE;
45
46 /* FILE DEBUG LOG FUNCTIONS **************************************************/
47
48 VOID
49 NTAPI
50 KdpLoggerThread(PVOID Context)
51 {
52 ULONG beg, end, num;
53 IO_STATUS_BLOCK Iosb;
54
55 KdpLoggingEnabled = TRUE;
56
57 while (TRUE)
58 {
59 KeWaitForSingleObject(&KdpLoggerThreadEvent, 0, KernelMode, FALSE, NULL);
60
61 /* Bug */
62 /* Keep KdpCurrentPosition and KdpFreeBytes values in local
63 * variables to avoid their possible change from Producer part,
64 * KdpPrintToLogFile function
65 */
66 end = KdpCurrentPosition;
67 num = KdpFreeBytes;
68
69 /* Now securely calculate values, based on local variables */
70 beg = (end + num) % KdpBufferSize;
71 num = KdpBufferSize - num;
72
73 /* Nothing to do? */
74 if (num == 0)
75 continue;
76
77 if (end > beg)
78 {
79 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
80 KdpDebugBuffer + beg, num, NULL, NULL);
81 }
82 else
83 {
84 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
85 KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL);
86
87 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb,
88 KdpDebugBuffer, end, NULL, NULL);
89 }
90
91 (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num);
92 }
93 }
94
95 VOID
96 NTAPI
97 KdpPrintToLogFile(PCH String,
98 ULONG StringLength)
99 {
100 ULONG beg, end, num;
101 KIRQL OldIrql;
102
103 if (KdpDebugBuffer == NULL) return;
104
105 /* Acquire the printing spinlock without waiting at raised IRQL */
106 while (TRUE)
107 {
108 /* Wait when the spinlock becomes available */
109 while (!KeTestSpinLock(&KdpDebugLogSpinLock));
110
111 /* Spinlock was free, raise IRQL */
112 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
113
114 /* Try to get the spinlock */
115 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDebugLogSpinLock))
116 break;
117
118 /* Someone else got the spinlock, lower IRQL back */
119 KeLowerIrql(OldIrql);
120 }
121
122 beg = KdpCurrentPosition;
123 num = KdpFreeBytes;
124 if (StringLength < num)
125 num = StringLength;
126
127 if (num != 0)
128 {
129 end = (beg + num) % KdpBufferSize;
130 KdpCurrentPosition = end;
131 KdpFreeBytes -= num;
132
133 if (end > beg)
134 {
135 RtlCopyMemory(KdpDebugBuffer + beg, String, num);
136 }
137 else
138 {
139 RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg);
140 RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end);
141 }
142 }
143
144 /* Release spinlock */
145 KiReleaseSpinLock(&KdpDebugLogSpinLock);
146
147 /* Lower IRQL */
148 KeLowerIrql(OldIrql);
149
150 /* Signal the logger thread */
151 if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled)
152 KeSetEvent(&KdpLoggerThreadEvent, 0, FALSE);
153 }
154
155 VOID
156 NTAPI
157 INIT_FUNCTION
158 KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
159 ULONG BootPhase)
160 {
161 NTSTATUS Status;
162 UNICODE_STRING FileName;
163 OBJECT_ATTRIBUTES ObjectAttributes;
164 IO_STATUS_BLOCK Iosb;
165 HANDLE ThreadHandle;
166 KPRIORITY Priority;
167 SIZE_T MemSizeMBs;
168
169 if (!KdpDebugMode.File) return;
170
171 if (BootPhase == 0)
172 {
173 KdComPortInUse = NULL;
174
175 /* Write out the functions that we support for now */
176 DispatchTable->KdpInitRoutine = KdpInitDebugLog;
177 DispatchTable->KdpPrintRoutine = KdpPrintToLogFile;
178
179 /* Register as a Provider */
180 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
181
182 }
183 else if (BootPhase == 1)
184 {
185 /* Allocate a buffer for debug log */
186 KdpDebugBuffer = ExAllocatePool(NonPagedPool, KdpBufferSize);
187 KdpFreeBytes = KdpBufferSize;
188
189 /* Initialize spinlock */
190 KeInitializeSpinLock(&KdpDebugLogSpinLock);
191
192 /* Display separator + ReactOS version at start of the debug log */
193 DPRINT1("---------------------------------------------------------------\n");
194 DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
195 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
196 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
197 }
198 else if (BootPhase == 2)
199 {
200 HalDisplayString("\n File log debugging enabled\n\n");
201 }
202 else if (BootPhase == 3)
203 {
204 /* Setup the log name */
205 Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE);
206 if (!NT_SUCCESS(Status)) return;
207
208 InitializeObjectAttributes(&ObjectAttributes,
209 &FileName,
210 0,
211 NULL,
212 NULL);
213
214 /* Create the log file */
215 Status = NtCreateFile(&KdpLogFileHandle,
216 FILE_APPEND_DATA | SYNCHRONIZE,
217 &ObjectAttributes,
218 &Iosb,
219 NULL,
220 FILE_ATTRIBUTE_NORMAL,
221 FILE_SHARE_READ,
222 FILE_SUPERSEDE,
223 FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
224 NULL,
225 0);
226
227 RtlFreeUnicodeString(&FileName);
228
229 if (!NT_SUCCESS(Status)) return;
230
231 KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE);
232
233 /* Create the logger thread */
234 Status = PsCreateSystemThread(&ThreadHandle,
235 THREAD_ALL_ACCESS,
236 NULL,
237 NULL,
238 NULL,
239 KdpLoggerThread,
240 NULL);
241
242 if (!NT_SUCCESS(Status)) return;
243
244 Priority = 7;
245 NtSetInformationThread(ThreadHandle,
246 ThreadPriority,
247 &Priority,
248 sizeof(Priority));
249 }
250 }
251
252 /* SERIAL FUNCTIONS **********************************************************/
253
254 VOID
255 NTAPI
256 KdpSerialDebugPrint(LPSTR Message,
257 ULONG Length)
258 {
259 KIRQL OldIrql;
260 PCHAR pch = (PCHAR) Message;
261
262 /* Acquire the printing spinlock without waiting at raised IRQL */
263 while (TRUE)
264 {
265 /* Wait when the spinlock becomes available */
266 while (!KeTestSpinLock(&KdpSerialSpinLock));
267
268 /* Spinlock was free, raise IRQL */
269 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
270
271 /* Try to get the spinlock */
272 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpSerialSpinLock))
273 break;
274
275 /* Someone else got the spinlock, lower IRQL back */
276 KeLowerIrql(OldIrql);
277 }
278
279 /* Output the message */
280 while (*pch != 0)
281 {
282 if (*pch == '\n')
283 {
284 KdPortPutByteEx(&SerialPortInfo, '\r');
285 }
286 KdPortPutByteEx(&SerialPortInfo, *pch);
287 pch++;
288 }
289
290 /* Release spinlock */
291 KiReleaseSpinLock(&KdpSerialSpinLock);
292
293 /* Lower IRQL */
294 KeLowerIrql(OldIrql);
295 }
296
297 VOID
298 NTAPI
299 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
300 ULONG BootPhase)
301 {
302 SIZE_T MemSizeMBs;
303 if (!KdpDebugMode.Serial) return;
304
305 if (BootPhase == 0)
306 {
307 /* Write out the functions that we support for now */
308 DispatchTable->KdpInitRoutine = KdpSerialInit;
309 DispatchTable->KdpPrintRoutine = KdpSerialDebugPrint;
310
311 /* Initialize the Port */
312 if (!KdPortInitializeEx(&SerialPortInfo, 0, 0))
313 {
314 KdpDebugMode.Serial = FALSE;
315 return;
316 }
317 KdComPortInUse = (PUCHAR)(ULONG_PTR)SerialPortInfo.BaseAddress;
318
319 /* Initialize spinlock */
320 KeInitializeSpinLock(&KdpSerialSpinLock);
321
322 /* Register as a Provider */
323 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
324
325 /* Display separator + ReactOS version at start of the debug log */
326 DPRINT1("-----------------------------------------------------\n");
327 DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
328 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
329 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
330 DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
331 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
332 KeLoaderBlock->NtHalPathName,
333 KeLoaderBlock->ArcHalDeviceName,
334 KeLoaderBlock->NtBootPathName);
335 }
336 else if (BootPhase == 2)
337 {
338 HalDisplayString("\n Serial debugging enabled\n\n");
339 }
340 }
341
342 /* SCREEN FUNCTIONS **********************************************************/
343
344 /*
345 * Screen debug logger function KdpScreenPrint() writes text messages into
346 * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
347 * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
348 * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
349 */
350 VOID
351 NTAPI
352 KdpScreenPrint(LPSTR Message,
353 ULONG Length)
354 {
355 ULONG beg, end, num;
356 KIRQL OldIrql;
357 PCHAR pch = (PCHAR) Message;
358
359 while (*pch)
360 {
361 if(*pch == '\b')
362 {
363 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */
364 if(KdpScreenLineLength > 0)
365 {
366 /* Remove last character from buffer */
367 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0';
368 KdpScreenLineBufferPos = KdpScreenLineLength;
369
370 /* Clear row and print line again */
371 HalDisplayString("\r");
372 HalDisplayString(KdpScreenLineBuffer);
373 }
374 }
375 else
376 {
377 KdpScreenLineBuffer[KdpScreenLineLength++] = *pch;
378 KdpScreenLineBuffer[KdpScreenLineLength] = '\0';
379 }
380
381 if(*pch == '\n' || KdpScreenLineLength == KdpScreenLineLenght)
382 {
383 /* Print buffered characters */
384 if(KdpScreenLineBufferPos != KdpScreenLineLength)
385 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
386
387 /* Clear line buffer */
388 KdpScreenLineBuffer[0] = '\0';
389 KdpScreenLineLength = KdpScreenLineBufferPos = 0;
390 }
391
392 ++pch;
393 }
394
395 /* Print buffered characters */
396 if(KdpScreenLineBufferPos != KdpScreenLineLength)
397 {
398 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
399 KdpScreenLineBufferPos = KdpScreenLineLength;
400 }
401
402 /* Dmesg: store Message in the buffer to show it later */
403 if (KdbpIsInDmesgMode)
404 return;
405
406 if (KdpDmesgBuffer == NULL)
407 return;
408
409 /* Acquire the printing spinlock without waiting at raised IRQL */
410 while (TRUE)
411 {
412 /* Wait when the spinlock becomes available */
413 while (!KeTestSpinLock(&KdpDmesgLogSpinLock));
414
415 /* Spinlock was free, raise IRQL */
416 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
417
418 /* Try to get the spinlock */
419 if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock))
420 break;
421
422 /* Someone else got the spinlock, lower IRQL back */
423 KeLowerIrql(OldIrql);
424 }
425
426 /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
427 * set num to min(KdpDmesgFreeBytes, Length).
428 */
429 num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes;
430 beg = KdpDmesgCurrentPosition;
431 if (num != 0)
432 {
433 end = (beg + num) % KdpDmesgBufferSize;
434 if (end > beg)
435 {
436 RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length);
437 }
438 else
439 {
440 RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg);
441 RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end);
442 }
443 KdpDmesgCurrentPosition = end;
444
445 /* Counting the total bytes written */
446 KdbDmesgTotalWritten += num;
447 }
448
449 /* Release spinlock */
450 KiReleaseSpinLock(&KdpDmesgLogSpinLock);
451
452 /* Lower IRQL */
453 KeLowerIrql(OldIrql);
454
455 /* Optional step(?): find out a way to notify about buffer exhaustion,
456 * and possibly fall into kbd to use dmesg command: user will read
457 * debug messages before they will be wiped over by next writes.
458 */
459 }
460
461 VOID
462 NTAPI
463 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
464 ULONG BootPhase)
465 {
466 SIZE_T MemSizeMBs;
467 if (!KdpDebugMode.Screen) return;
468
469 if (BootPhase == 0)
470 {
471 /* Write out the functions that we support for now */
472 DispatchTable->KdpInitRoutine = KdpScreenInit;
473 DispatchTable->KdpPrintRoutine = KdpScreenPrint;
474
475 /* Register as a Provider */
476 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
477 }
478 else if (BootPhase == 1)
479 {
480 /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
481 * see kdbp_cli.c:KdbpCmdDmesg()/2
482 */
483 KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1);
484 RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1);
485 KdpDmesgFreeBytes = KdpDmesgBufferSize;
486 KdbDmesgTotalWritten = 0;
487
488 /* Take control of the display */
489 InbvAcquireDisplayOwnership();
490 InbvResetDisplay();
491 InbvSolidColorFill(0, 0, 639, 479, 0);
492 InbvSetTextColor(15);
493 InbvSetScrollRegion(0, 0, 639, 479);
494 InbvInstallDisplayStringFilter(NULL);
495 InbvEnableDisplayString(TRUE);
496
497 /* Initialize spinlock */
498 KeInitializeSpinLock(&KdpDmesgLogSpinLock);
499
500 /* Display separator + ReactOS version at start of the debug log */
501 DPRINT1("-----------------------------------------------------\n");
502 DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
503 MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
504 DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
505 DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
506 DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
507 KeLoaderBlock->NtHalPathName,
508 KeLoaderBlock->ArcHalDeviceName,
509 KeLoaderBlock->NtBootPathName);
510 }
511 else if (BootPhase == 2)
512 {
513 HalDisplayString("\n Screen debugging enabled\n\n");
514 }
515 }
516
517 /* GENERAL FUNCTIONS *********************************************************/
518
519 ULONG
520 NTAPI
521 KdpPrintString(LPSTR String,
522 ULONG Length)
523 {
524 PLIST_ENTRY CurrentEntry;
525 PKD_DISPATCH_TABLE CurrentTable;
526
527 if (!KdpDebugMode.Value) return 0;
528
529 /* Call the registered handlers */
530 CurrentEntry = KdProviders.Flink;
531 while (CurrentEntry != &KdProviders)
532 {
533 /* Get the current table */
534 CurrentTable = CONTAINING_RECORD(CurrentEntry,
535 KD_DISPATCH_TABLE,
536 KdProvidersList);
537
538 /* Call it */
539 CurrentTable->KdpPrintRoutine(String, Length);
540
541 /* Next Table */
542 CurrentEntry = CurrentEntry->Flink;
543 }
544
545 /* Call the Wrapper Routine */
546 if (WrapperTable.KdpPrintRoutine)
547 WrapperTable.KdpPrintRoutine(String, Length);
548
549 /* Return the Length */
550 return Length;
551 }
552
553 /* EOF */