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 "kd.h" 15 #include "kdterminal.h" 16 #ifdef KDBG 17 #include "../kdbg/kdb.h" 18 #endif 19 20 #define NDEBUG 21 #include <debug.h> 22 23 #undef KdSendPacket 24 #undef KdReceivePacket 25 26 /* GLOBALS *******************************************************************/ 27 28 #define KdpBufferSize (1024 * 512) 29 static BOOLEAN KdpLoggingEnabled = FALSE; 30 static PCHAR KdpDebugBuffer = NULL; 31 static volatile ULONG KdpCurrentPosition = 0; 32 static volatile ULONG KdpFreeBytes = 0; 33 static KSPIN_LOCK KdpDebugLogSpinLock; 34 static KEVENT KdpLoggerThreadEvent; 35 static HANDLE KdpLogFileHandle; 36 ANSI_STRING KdpLogFileName = RTL_CONSTANT_STRING("\\SystemRoot\\debug.log"); 37 38 static KSPIN_LOCK KdpSerialSpinLock; 39 ULONG SerialPortNumber = DEFAULT_DEBUG_PORT; 40 CPPORT SerialPortInfo = {0, DEFAULT_DEBUG_BAUD_RATE, 0}; 41 42 #define KdpScreenLineLengthDefault 80 43 static CHAR KdpScreenLineBuffer[KdpScreenLineLengthDefault + 1] = ""; 44 static ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0; 45 46 KDP_DEBUG_MODE KdpDebugMode; 47 LIST_ENTRY KdProviders = {&KdProviders, &KdProviders}; 48 KD_DISPATCH_TABLE DispatchTable[KdMax] = {0}; 49 50 PKDP_INIT_ROUTINE InitRoutines[KdMax] = 51 { 52 KdpScreenInit, 53 KdpSerialInit, 54 KdpDebugLogInit, 55 #ifdef KDBG // See kdb_cli.c 56 KdpKdbgInit 57 #endif 58 }; 59 60 /* LOCKING FUNCTIONS *********************************************************/ 61 62 KIRQL 63 NTAPI 64 KdbpAcquireLock( 65 _In_ PKSPIN_LOCK SpinLock) 66 { 67 KIRQL OldIrql; 68 69 /* Acquire the spinlock without waiting at raised IRQL */ 70 while (TRUE) 71 { 72 /* Loop until the spinlock becomes available */ 73 while (!KeTestSpinLock(SpinLock)); 74 75 /* Spinlock is free, raise IRQL to high level */ 76 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 77 78 /* Try to get the spinlock */ 79 if (KeTryToAcquireSpinLockAtDpcLevel(SpinLock)) 80 break; 81 82 /* Someone else got the spinlock, lower IRQL back */ 83 KeLowerIrql(OldIrql); 84 } 85 86 return OldIrql; 87 } 88 89 VOID 90 NTAPI 91 KdbpReleaseLock( 92 _In_ PKSPIN_LOCK SpinLock, 93 _In_ KIRQL OldIrql) 94 { 95 /* Release the spinlock */ 96 KiReleaseSpinLock(SpinLock); 97 // KeReleaseSpinLockFromDpcLevel(SpinLock); 98 99 /* Restore the old IRQL */ 100 KeLowerIrql(OldIrql); 101 } 102 103 /* FILE DEBUG LOG FUNCTIONS **************************************************/ 104 105 static VOID 106 NTAPI 107 KdpLoggerThread(PVOID Context) 108 { 109 ULONG beg, end, num; 110 IO_STATUS_BLOCK Iosb; 111 112 ASSERT(ExGetPreviousMode() == KernelMode); 113 114 KdpLoggingEnabled = TRUE; 115 116 while (TRUE) 117 { 118 KeWaitForSingleObject(&KdpLoggerThreadEvent, Executive, KernelMode, FALSE, NULL); 119 120 /* Bug */ 121 /* Keep KdpCurrentPosition and KdpFreeBytes values in local 122 * variables to avoid their possible change from Producer part, 123 * KdpPrintToLogFile function 124 */ 125 end = KdpCurrentPosition; 126 num = KdpFreeBytes; 127 128 /* Now securely calculate values, based on local variables */ 129 beg = (end + num) % KdpBufferSize; 130 num = KdpBufferSize - num; 131 132 /* Nothing to do? */ 133 if (num == 0) 134 continue; 135 136 if (end > beg) 137 { 138 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb, 139 KdpDebugBuffer + beg, num, NULL, NULL); 140 } 141 else 142 { 143 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb, 144 KdpDebugBuffer + beg, KdpBufferSize - beg, NULL, NULL); 145 146 NtWriteFile(KdpLogFileHandle, NULL, NULL, NULL, &Iosb, 147 KdpDebugBuffer, end, NULL, NULL); 148 } 149 150 (VOID)InterlockedExchangeAddUL(&KdpFreeBytes, num); 151 } 152 } 153 154 static VOID 155 NTAPI 156 KdpPrintToLogFile( 157 _In_ PCCH String, 158 _In_ ULONG Length) 159 { 160 KIRQL OldIrql; 161 ULONG beg, end, num; 162 163 if (KdpDebugBuffer == NULL) return; 164 165 /* Acquire the printing spinlock without waiting at raised IRQL */ 166 OldIrql = KdbpAcquireLock(&KdpDebugLogSpinLock); 167 168 beg = KdpCurrentPosition; 169 num = min(Length, KdpFreeBytes); 170 if (num != 0) 171 { 172 end = (beg + num) % KdpBufferSize; 173 KdpCurrentPosition = end; 174 KdpFreeBytes -= num; 175 176 if (end > beg) 177 { 178 RtlCopyMemory(KdpDebugBuffer + beg, String, num); 179 } 180 else 181 { 182 RtlCopyMemory(KdpDebugBuffer + beg, String, KdpBufferSize - beg); 183 RtlCopyMemory(KdpDebugBuffer, String + KdpBufferSize - beg, end); 184 } 185 } 186 187 /* Release the spinlock */ 188 KdbpReleaseLock(&KdpDebugLogSpinLock, OldIrql); 189 190 /* Signal the logger thread */ 191 if (OldIrql <= DISPATCH_LEVEL && KdpLoggingEnabled) 192 KeSetEvent(&KdpLoggerThreadEvent, IO_NO_INCREMENT, FALSE); 193 } 194 195 NTSTATUS 196 NTAPI 197 KdpDebugLogInit( 198 _In_ PKD_DISPATCH_TABLE DispatchTable, 199 _In_ ULONG BootPhase) 200 { 201 NTSTATUS Status = STATUS_SUCCESS; 202 203 if (!KdpDebugMode.File) 204 return STATUS_PORT_DISCONNECTED; 205 206 if (BootPhase == 0) 207 { 208 /* Write out the functions that we support for now */ 209 DispatchTable->KdpPrintRoutine = KdpPrintToLogFile; 210 211 /* Register for BootPhase 1 initialization and as a Provider */ 212 DispatchTable->KdpInitRoutine = KdpDebugLogInit; 213 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 214 } 215 else if (BootPhase == 1) 216 { 217 /* Allocate a buffer for debug log */ 218 KdpDebugBuffer = ExAllocatePoolZero(NonPagedPool, 219 KdpBufferSize, 220 TAG_KDBG); 221 if (!KdpDebugBuffer) 222 { 223 KdpDebugMode.File = FALSE; 224 RemoveEntryList(&DispatchTable->KdProvidersList); 225 return STATUS_NO_MEMORY; 226 } 227 KdpFreeBytes = KdpBufferSize; 228 229 /* Initialize spinlock */ 230 KeInitializeSpinLock(&KdpDebugLogSpinLock); 231 232 /* Register for later BootPhase 2 reinitialization */ 233 DispatchTable->KdpInitRoutine = KdpDebugLogInit; 234 235 /* Announce ourselves */ 236 HalDisplayString(" File log debugging enabled\r\n"); 237 } 238 else if (BootPhase >= 2) 239 { 240 UNICODE_STRING FileName; 241 OBJECT_ATTRIBUTES ObjectAttributes; 242 IO_STATUS_BLOCK Iosb; 243 HANDLE ThreadHandle; 244 KPRIORITY Priority; 245 246 /* If we have already successfully opened the log file, bail out */ 247 if (KdpLogFileHandle != NULL) 248 return STATUS_SUCCESS; 249 250 /* Setup the log name */ 251 Status = RtlAnsiStringToUnicodeString(&FileName, &KdpLogFileName, TRUE); 252 if (!NT_SUCCESS(Status)) 253 goto Failure; 254 255 InitializeObjectAttributes(&ObjectAttributes, 256 &FileName, 257 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 258 NULL, 259 NULL); 260 261 /* Create the log file */ 262 Status = ZwCreateFile(&KdpLogFileHandle, 263 FILE_APPEND_DATA | SYNCHRONIZE, 264 &ObjectAttributes, 265 &Iosb, 266 NULL, 267 FILE_ATTRIBUTE_NORMAL, 268 FILE_SHARE_READ, 269 FILE_OPEN_IF, 270 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | 271 FILE_SEQUENTIAL_ONLY | FILE_WRITE_THROUGH, 272 NULL, 273 0); 274 275 RtlFreeUnicodeString(&FileName); 276 277 if (!NT_SUCCESS(Status)) 278 { 279 DPRINT1("Failed to open log file: 0x%08lx\n", Status); 280 281 /* Schedule an I/O reinitialization if needed */ 282 if (Status == STATUS_OBJECT_NAME_NOT_FOUND || 283 Status == STATUS_OBJECT_PATH_NOT_FOUND) 284 { 285 DispatchTable->KdpInitRoutine = KdpDebugLogInit; 286 return Status; 287 } 288 goto Failure; 289 } 290 291 /** HACK for FILE_APPEND_DATA ** 292 ** Remove once CORE-18789 is fixed. ** 293 ** Enforce to go to the end of file **/ 294 { 295 FILE_STANDARD_INFORMATION FileInfo; 296 FILE_POSITION_INFORMATION FilePosInfo; 297 298 Status = ZwQueryInformationFile(KdpLogFileHandle, 299 &Iosb, 300 &FileInfo, 301 sizeof(FileInfo), 302 FileStandardInformation); 303 DPRINT("Status: 0x%08lx - EOF offset: %I64d\n", 304 Status, FileInfo.EndOfFile.QuadPart); 305 306 Status = ZwQueryInformationFile(KdpLogFileHandle, 307 &Iosb, 308 &FilePosInfo, 309 sizeof(FilePosInfo), 310 FilePositionInformation); 311 DPRINT("Status: 0x%08lx - Position: %I64d\n", 312 Status, FilePosInfo.CurrentByteOffset.QuadPart); 313 314 FilePosInfo.CurrentByteOffset.QuadPart = FileInfo.EndOfFile.QuadPart; 315 Status = ZwSetInformationFile(KdpLogFileHandle, 316 &Iosb, 317 &FilePosInfo, 318 sizeof(FilePosInfo), 319 FilePositionInformation); 320 DPRINT("ZwSetInformationFile(FilePositionInfo) returned: 0x%08lx\n", Status); 321 } 322 /** END OF HACK **/ 323 324 KeInitializeEvent(&KdpLoggerThreadEvent, SynchronizationEvent, TRUE); 325 326 /* Create the logger thread */ 327 Status = PsCreateSystemThread(&ThreadHandle, 328 THREAD_ALL_ACCESS, 329 NULL, 330 NULL, 331 NULL, 332 KdpLoggerThread, 333 NULL); 334 if (!NT_SUCCESS(Status)) 335 { 336 DPRINT1("Failed to create log file thread: 0x%08lx\n", Status); 337 ZwClose(KdpLogFileHandle); 338 goto Failure; 339 } 340 341 Priority = HIGH_PRIORITY; 342 ZwSetInformationThread(ThreadHandle, 343 ThreadPriority, 344 &Priority, 345 sizeof(Priority)); 346 347 ZwClose(ThreadHandle); 348 return Status; 349 350 Failure: 351 KdpFreeBytes = 0; 352 ExFreePoolWithTag(KdpDebugBuffer, TAG_KDBG); 353 KdpDebugBuffer = NULL; 354 KdpDebugMode.File = FALSE; 355 RemoveEntryList(&DispatchTable->KdProvidersList); 356 } 357 358 return Status; 359 } 360 361 /* SERIAL FUNCTIONS **********************************************************/ 362 363 static VOID 364 NTAPI 365 KdpSerialPrint( 366 _In_ PCCH String, 367 _In_ ULONG Length) 368 { 369 PCCH pch = String; 370 KIRQL OldIrql; 371 372 /* Acquire the printing spinlock without waiting at raised IRQL */ 373 OldIrql = KdbpAcquireLock(&KdpSerialSpinLock); 374 375 /* Output the string */ 376 while (pch < String + Length && *pch) 377 { 378 if (*pch == '\n') 379 { 380 KdPortPutByteEx(&SerialPortInfo, '\r'); 381 } 382 KdPortPutByteEx(&SerialPortInfo, *pch); 383 ++pch; 384 } 385 386 /* Release the spinlock */ 387 KdbpReleaseLock(&KdpSerialSpinLock, OldIrql); 388 } 389 390 NTSTATUS 391 NTAPI 392 KdpSerialInit( 393 _In_ PKD_DISPATCH_TABLE DispatchTable, 394 _In_ ULONG BootPhase) 395 { 396 if (!KdpDebugMode.Serial) 397 return STATUS_PORT_DISCONNECTED; 398 399 if (BootPhase == 0) 400 { 401 /* Write out the functions that we support for now */ 402 DispatchTable->KdpPrintRoutine = KdpSerialPrint; 403 404 /* Initialize the Port */ 405 if (!KdPortInitializeEx(&SerialPortInfo, SerialPortNumber)) 406 { 407 KdpDebugMode.Serial = FALSE; 408 return STATUS_DEVICE_DOES_NOT_EXIST; 409 } 410 KdComPortInUse = SerialPortInfo.Address; 411 412 /* Initialize spinlock */ 413 KeInitializeSpinLock(&KdpSerialSpinLock); 414 415 /* Register for BootPhase 1 initialization and as a Provider */ 416 DispatchTable->KdpInitRoutine = KdpSerialInit; 417 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 418 } 419 else if (BootPhase == 1) 420 { 421 /* Announce ourselves */ 422 HalDisplayString(" Serial debugging enabled\r\n"); 423 } 424 425 return STATUS_SUCCESS; 426 } 427 428 /* SCREEN FUNCTIONS **********************************************************/ 429 430 VOID 431 KdpScreenAcquire(VOID) 432 { 433 if (InbvIsBootDriverInstalled() /* && 434 !InbvCheckDisplayOwnership() */) 435 { 436 /* Acquire ownership and reset the display */ 437 InbvAcquireDisplayOwnership(); 438 InbvResetDisplay(); 439 InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK); 440 InbvSetTextColor(BV_COLOR_WHITE); 441 InbvInstallDisplayStringFilter(NULL); 442 InbvEnableDisplayString(TRUE); 443 InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); 444 } 445 } 446 447 // extern VOID NTAPI InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned); 448 449 VOID 450 KdpScreenRelease(VOID) 451 { 452 if (InbvIsBootDriverInstalled()&& 453 InbvCheckDisplayOwnership()) 454 { 455 /* Release the display */ 456 // InbvSetDisplayOwnership(FALSE); 457 InbvNotifyDisplayOwnershipLost(NULL); 458 } 459 } 460 461 static VOID 462 NTAPI 463 KdpScreenPrint( 464 _In_ PCCH String, 465 _In_ ULONG Length) 466 { 467 PCCH pch = String; 468 469 while (pch < String + Length && *pch) 470 { 471 if (*pch == '\b') 472 { 473 /* HalDisplayString does not support '\b'. Workaround it and use '\r' */ 474 if (KdpScreenLineLength > 0) 475 { 476 /* Remove last character from buffer */ 477 KdpScreenLineBuffer[--KdpScreenLineLength] = '\0'; 478 KdpScreenLineBufferPos = KdpScreenLineLength; 479 480 /* Clear row and print line again */ 481 HalDisplayString("\r"); 482 HalDisplayString(KdpScreenLineBuffer); 483 } 484 } 485 else 486 { 487 KdpScreenLineBuffer[KdpScreenLineLength++] = *pch; 488 KdpScreenLineBuffer[KdpScreenLineLength] = '\0'; 489 } 490 491 if (*pch == '\n' || KdpScreenLineLength == KdpScreenLineLengthDefault) 492 { 493 /* Print buffered characters */ 494 if (KdpScreenLineBufferPos != KdpScreenLineLength) 495 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos); 496 497 /* Clear line buffer */ 498 KdpScreenLineBuffer[0] = '\0'; 499 KdpScreenLineLength = KdpScreenLineBufferPos = 0; 500 } 501 502 ++pch; 503 } 504 505 /* Print buffered characters */ 506 if (KdpScreenLineBufferPos != KdpScreenLineLength) 507 { 508 HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos); 509 KdpScreenLineBufferPos = KdpScreenLineLength; 510 } 511 } 512 513 NTSTATUS 514 NTAPI 515 KdpScreenInit( 516 _In_ PKD_DISPATCH_TABLE DispatchTable, 517 _In_ ULONG BootPhase) 518 { 519 if (!KdpDebugMode.Screen) 520 return STATUS_PORT_DISCONNECTED; 521 522 if (BootPhase == 0) 523 { 524 /* Write out the functions that we support for now */ 525 DispatchTable->KdpPrintRoutine = KdpScreenPrint; 526 527 /* Register for BootPhase 1 initialization and as a Provider */ 528 DispatchTable->KdpInitRoutine = KdpScreenInit; 529 InsertTailList(&KdProviders, &DispatchTable->KdProvidersList); 530 } 531 else if (BootPhase == 1) 532 { 533 /* Take control of the display */ 534 KdpScreenAcquire(); 535 536 /* Announce ourselves */ 537 HalDisplayString(" Screen debugging enabled\r\n"); 538 } 539 540 return STATUS_SUCCESS; 541 } 542 543 544 /* GENERAL FUNCTIONS *********************************************************/ 545 546 static VOID 547 KdIoPrintString( 548 _In_ PCCH String, 549 _In_ ULONG Length) 550 { 551 PLIST_ENTRY CurrentEntry; 552 PKD_DISPATCH_TABLE CurrentTable; 553 554 /* Call the registered providers */ 555 for (CurrentEntry = KdProviders.Flink; 556 CurrentEntry != &KdProviders; 557 CurrentEntry = CurrentEntry->Flink) 558 { 559 CurrentTable = CONTAINING_RECORD(CurrentEntry, 560 KD_DISPATCH_TABLE, 561 KdProvidersList); 562 563 CurrentTable->KdpPrintRoutine(String, Length); 564 } 565 } 566 567 VOID 568 KdIoPuts( 569 _In_ PCSTR String) 570 { 571 KdIoPrintString(String, (ULONG)strlen(String)); 572 } 573 574 VOID 575 __cdecl 576 KdIoPrintf( 577 _In_ PCSTR Format, 578 ...) 579 { 580 va_list ap; 581 ULONG Length; 582 CHAR Buffer[512]; 583 584 /* Format the string */ 585 va_start(ap, Format); 586 Length = (ULONG)_vsnprintf(Buffer, 587 sizeof(Buffer), 588 Format, 589 ap); 590 va_end(ap); 591 592 /* Send it to the display providers */ 593 KdIoPrintString(Buffer, Length); 594 } 595 596 #ifdef KDBG 597 extern const CSTRING KdbPromptStr; 598 #endif 599 600 VOID 601 NTAPI 602 KdSendPacket( 603 _In_ ULONG PacketType, 604 _In_ PSTRING MessageHeader, 605 _In_opt_ PSTRING MessageData, 606 _Inout_ PKD_CONTEXT Context) 607 { 608 PDBGKD_DEBUG_IO DebugIo; 609 610 if (PacketType != PACKET_TYPE_KD_DEBUG_IO) 611 { 612 KdIoPrintf("%s: PacketType %d is UNIMPLEMENTED\n", __FUNCTION__, PacketType); 613 return; 614 } 615 616 DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; 617 618 /* Validate API call */ 619 if (MessageHeader->Length != sizeof(DBGKD_DEBUG_IO)) 620 return; 621 if ((DebugIo->ApiNumber != DbgKdPrintStringApi) && 622 (DebugIo->ApiNumber != DbgKdGetStringApi)) 623 { 624 return; 625 } 626 if (!MessageData) 627 return; 628 629 /* NOTE: MessageData->Length should be equal to 630 * DebugIo.u.PrintString.LengthOfString, or to 631 * DebugIo.u.GetString.LengthOfPromptString */ 632 633 if (!KdpDebugMode.Value) 634 return; 635 636 /* Print the string proper */ 637 KdIoPrintString(MessageData->Buffer, MessageData->Length); 638 } 639 640 KDSTATUS 641 NTAPI 642 KdReceivePacket( 643 _In_ ULONG PacketType, 644 _Out_ PSTRING MessageHeader, 645 _Out_ PSTRING MessageData, 646 _Out_ PULONG DataLength, 647 _Inout_ PKD_CONTEXT Context) 648 { 649 #ifdef KDBG 650 PDBGKD_DEBUG_IO DebugIo; 651 STRING ResponseString; 652 CHAR MessageBuffer[512]; 653 #endif 654 655 if (PacketType != PACKET_TYPE_KD_DEBUG_IO) 656 { 657 KdIoPrintf("%s: PacketType %d is UNIMPLEMENTED\n", __FUNCTION__, PacketType); 658 return KdPacketTimedOut; 659 } 660 661 #ifdef KDBG 662 DebugIo = (PDBGKD_DEBUG_IO)MessageHeader->Buffer; 663 664 /* Validate API call */ 665 if (MessageHeader->MaximumLength != sizeof(DBGKD_DEBUG_IO)) 666 return KdPacketNeedsResend; 667 if (DebugIo->ApiNumber != DbgKdGetStringApi) 668 return KdPacketNeedsResend; 669 670 /* NOTE: We cannot use directly MessageData->Buffer here as it points 671 * to the temporary KdpMessageBuffer scratch buffer that is being 672 * shared with all the possible I/O KD operations that may happen. */ 673 ResponseString.Buffer = MessageBuffer; 674 ResponseString.Length = 0; 675 ResponseString.MaximumLength = min(sizeof(MessageBuffer), 676 MessageData->MaximumLength); 677 ResponseString.MaximumLength = min(ResponseString.MaximumLength, 678 DebugIo->u.GetString.LengthOfStringRead); 679 680 /* The prompt string has been printed by KdSendPacket; go to 681 * new line and print the kdb prompt -- for SYSREG2 support. */ 682 KdIoPrintString("\n", 1); 683 KdIoPuts(KdbPromptStr.Buffer); // Alternatively, use "Input> " 684 685 if (!(KdbDebugState & KD_DEBUG_KDSERIAL)) 686 KbdDisableMouse(); 687 688 /* 689 * Read a NULL-terminated line of user input and retrieve its length. 690 * Official documentation states that DbgPrompt() includes a terminating 691 * newline character but does not NULL-terminate. However, experiments 692 * show that this behaviour is left at the discretion of WinDbg itself. 693 * WinDbg NULL-terminates the string unless its buffer is too short, 694 * in which case the string is simply truncated without NULL-termination. 695 */ 696 ResponseString.Length = 697 (USHORT)KdIoReadLine(ResponseString.Buffer, 698 ResponseString.MaximumLength); 699 700 if (!(KdbDebugState & KD_DEBUG_KDSERIAL)) 701 KbdEnableMouse(); 702 703 /* Adjust and return the string length */ 704 *DataLength = min(ResponseString.Length + sizeof(ANSI_NULL), 705 DebugIo->u.GetString.LengthOfStringRead); 706 MessageData->Length = DebugIo->u.GetString.LengthOfStringRead = *DataLength; 707 708 /* Only now we can copy back the data into MessageData->Buffer */ 709 RtlCopyMemory(MessageData->Buffer, ResponseString.Buffer, *DataLength); 710 #endif 711 712 return KdPacketReceived; 713 } 714 715 /* EOF */ 716