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