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