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