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