1 /** @file 2 Provides interface to shell console logger. 3 4 (C) Copyright 2013 Hewlett-Packard Development Company, L.P.<BR> 5 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 **/ 14 15 #include "Shell.h" 16 17 /** 18 Install our intermediate ConOut into the system table to 19 keep a log of all the info that is displayed to the user. 20 21 @param[in] ScreensToSave Sets how many screen-worths of data to save. 22 @param[out] ConsoleInfo The object to pass into later functions. 23 24 @retval EFI_SUCCESS The operation was successful. 25 @return other The operation failed. 26 27 @sa ConsoleLoggerResetBuffers 28 @sa InstallProtocolInterface 29 **/ 30 EFI_STATUS 31 EFIAPI 32 ConsoleLoggerInstall( 33 IN CONST UINTN ScreensToSave, 34 OUT CONSOLE_LOGGER_PRIVATE_DATA **ConsoleInfo 35 ) 36 { 37 EFI_STATUS Status; 38 ASSERT(ConsoleInfo != NULL); 39 40 (*ConsoleInfo) = AllocateZeroPool(sizeof(CONSOLE_LOGGER_PRIVATE_DATA)); 41 if ((*ConsoleInfo) == NULL) { 42 return (EFI_OUT_OF_RESOURCES); 43 } 44 45 (*ConsoleInfo)->Signature = CONSOLE_LOGGER_PRIVATE_DATA_SIGNATURE; 46 (*ConsoleInfo)->OldConOut = gST->ConOut; 47 (*ConsoleInfo)->OldConHandle = gST->ConsoleOutHandle; 48 (*ConsoleInfo)->Buffer = NULL; 49 (*ConsoleInfo)->BufferSize = 0; 50 (*ConsoleInfo)->OriginalStartRow = 0; 51 (*ConsoleInfo)->CurrentStartRow = 0; 52 (*ConsoleInfo)->RowsPerScreen = 0; 53 (*ConsoleInfo)->ColsPerScreen = 0; 54 (*ConsoleInfo)->Attributes = NULL; 55 (*ConsoleInfo)->AttribSize = 0; 56 (*ConsoleInfo)->ScreenCount = ScreensToSave; 57 (*ConsoleInfo)->HistoryMode.MaxMode = 1; 58 (*ConsoleInfo)->HistoryMode.Mode = 0; 59 (*ConsoleInfo)->HistoryMode.Attribute = 0; 60 (*ConsoleInfo)->HistoryMode.CursorColumn = 0; 61 (*ConsoleInfo)->HistoryMode.CursorRow = 0; 62 (*ConsoleInfo)->HistoryMode.CursorVisible = FALSE; 63 (*ConsoleInfo)->OurConOut.Reset = ConsoleLoggerReset; 64 (*ConsoleInfo)->OurConOut.OutputString = ConsoleLoggerOutputString; 65 (*ConsoleInfo)->OurConOut.TestString = ConsoleLoggerTestString; 66 (*ConsoleInfo)->OurConOut.QueryMode = ConsoleLoggerQueryMode; 67 (*ConsoleInfo)->OurConOut.SetMode = ConsoleLoggerSetMode; 68 (*ConsoleInfo)->OurConOut.SetAttribute = ConsoleLoggerSetAttribute; 69 (*ConsoleInfo)->OurConOut.ClearScreen = ConsoleLoggerClearScreen; 70 (*ConsoleInfo)->OurConOut.SetCursorPosition = ConsoleLoggerSetCursorPosition; 71 (*ConsoleInfo)->OurConOut.EnableCursor = ConsoleLoggerEnableCursor; 72 (*ConsoleInfo)->OurConOut.Mode = gST->ConOut->Mode; 73 (*ConsoleInfo)->Enabled = TRUE; 74 75 Status = ConsoleLoggerResetBuffers(*ConsoleInfo); 76 if (EFI_ERROR(Status)) { 77 SHELL_FREE_NON_NULL((*ConsoleInfo)); 78 *ConsoleInfo = NULL; 79 return (Status); 80 } 81 82 Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut)); 83 if (EFI_ERROR(Status)) { 84 SHELL_FREE_NON_NULL((*ConsoleInfo)->Buffer); 85 SHELL_FREE_NON_NULL((*ConsoleInfo)->Attributes); 86 SHELL_FREE_NON_NULL((*ConsoleInfo)); 87 *ConsoleInfo = NULL; 88 return (Status); 89 } 90 91 gST->ConsoleOutHandle = gImageHandle; 92 gST->ConOut = &(*ConsoleInfo)->OurConOut; 93 94 return (Status); 95 } 96 97 /** 98 Return the system to the state it was before InstallConsoleLogger 99 was installed. 100 101 @param[in] ConsoleInfo The object from the install function. 102 103 @retval EFI_SUCCESS The operation was successful 104 @return other The operation failed. This was from UninstallProtocolInterface. 105 **/ 106 EFI_STATUS 107 EFIAPI 108 ConsoleLoggerUninstall( 109 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 110 ) 111 { 112 ASSERT(ConsoleInfo != NULL); 113 ASSERT(ConsoleInfo->OldConOut != NULL); 114 115 if (ConsoleInfo->Buffer != NULL) { 116 FreePool(ConsoleInfo->Buffer); 117 DEBUG_CODE(ConsoleInfo->Buffer = NULL;); 118 DEBUG_CODE(ConsoleInfo->BufferSize = 0;); 119 } 120 if (ConsoleInfo->Attributes != NULL) { 121 FreePool(ConsoleInfo->Attributes); 122 DEBUG_CODE(ConsoleInfo->Attributes = NULL;); 123 DEBUG_CODE(ConsoleInfo->AttribSize = 0;); 124 } 125 126 gST->ConsoleOutHandle = ConsoleInfo->OldConHandle; 127 gST->ConOut = ConsoleInfo->OldConOut; 128 129 return (gBS->UninstallProtocolInterface(gImageHandle, &gEfiSimpleTextOutProtocolGuid, (VOID*)&ConsoleInfo->OurConOut)); 130 } 131 132 /** 133 Displays previously logged output back to the screen. 134 135 This will scroll the screen forwards and backwards through the log of previous 136 output. If Rows is 0 then the size of 1/2 the screen will be scrolled. If Rows 137 is (UINTN)(-1) then the size of the screen will be scrolled. 138 139 @param[in] Forward If TRUE then the log will be displayed forwards (scroll to newer). 140 If FALSE then the log will be displayed backwards (scroll to older). 141 @param[in] Rows Determines how many rows the log should scroll. 142 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 143 **/ 144 EFI_STATUS 145 EFIAPI 146 ConsoleLoggerDisplayHistory( 147 IN CONST BOOLEAN Forward, 148 IN CONST UINTN Rows, 149 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 150 ) 151 { 152 UINTN RowChange; 153 154 ASSERT(ConsoleInfo != NULL); 155 156 // 157 // Calculate the row number change 158 // 159 switch (Rows) { 160 case ((UINTN)(-1)): 161 RowChange = ConsoleInfo->RowsPerScreen; 162 break; 163 case (0): 164 RowChange = ConsoleInfo->RowsPerScreen / 2; 165 break; 166 default: 167 RowChange = Rows; 168 break; 169 } 170 171 // 172 // Do the math for direction 173 // 174 if (Forward) { 175 if ((ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow) < RowChange) { 176 RowChange = ConsoleInfo->OriginalStartRow - ConsoleInfo->CurrentStartRow; 177 } 178 } else { 179 if (ConsoleInfo->CurrentStartRow < RowChange) { 180 RowChange = ConsoleInfo->CurrentStartRow; 181 } 182 } 183 184 // 185 // If we are already at one end or the other 186 // 187 if (RowChange == 0) { 188 return (EFI_SUCCESS); 189 } 190 191 // 192 // Clear the screen 193 // 194 ConsoleInfo->OldConOut->ClearScreen(ConsoleInfo->OldConOut); 195 196 // 197 // Set the new start row 198 // 199 if (Forward) { 200 ConsoleInfo->CurrentStartRow += RowChange; 201 } else { 202 ConsoleInfo->CurrentStartRow -= RowChange; 203 } 204 205 // 206 // Change the screen 207 // 208 return (UpdateDisplayFromHistory(ConsoleInfo)); 209 } 210 211 /** 212 Function to return to normal output whent he scrolling is complete. 213 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 214 215 @retval EFI_SUCCESS The operation was successful. 216 @return other The operation failed. See UpdateDisplayFromHistory. 217 218 @sa UpdateDisplayFromHistory 219 **/ 220 EFI_STATUS 221 EFIAPI 222 ConsoleLoggerStopHistory( 223 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 224 ) 225 { 226 ASSERT(ConsoleInfo != NULL); 227 if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) { 228 return (EFI_SUCCESS); 229 } 230 231 // 232 // Clear the screen 233 // 234 ConsoleInfo->OldConOut->ClearScreen(ConsoleInfo->OldConOut); 235 236 ConsoleInfo->CurrentStartRow = ConsoleInfo->OriginalStartRow; 237 return (UpdateDisplayFromHistory(ConsoleInfo)); 238 } 239 240 /** 241 Updates the hidden ConOut to be displaying the correct stuff. 242 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 243 244 @retval EFI_SUCCESS The operation was successful. 245 @return other The operation failed. 246 **/ 247 EFI_STATUS 248 EFIAPI 249 UpdateDisplayFromHistory( 250 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 251 ) 252 { 253 EFI_STATUS Status; 254 EFI_STATUS RetVal; 255 CHAR16 *Screen; 256 INT32 *Attributes; 257 UINTN CurrentRow; 258 CHAR16 TempCharHolder; 259 UINTN Column; 260 INT32 CurrentAttrib; 261 UINTN CurrentColumn; 262 CHAR16 *StringSegment; 263 CHAR16 *StringSegmentEnd; 264 CHAR16 StringSegmentEndChar; 265 INT32 OrigAttribute; 266 267 ASSERT(ConsoleInfo != NULL); 268 TempCharHolder = CHAR_NULL; 269 RetVal = EFI_SUCCESS; 270 OrigAttribute = ConsoleInfo->OldConOut->Mode->Attribute; 271 272 // 273 // Disable cursor visibility and move it to the top left corner 274 // 275 ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, FALSE); 276 ConsoleInfo->OldConOut->SetCursorPosition (ConsoleInfo->OldConOut, 0, 0); 277 278 Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow]; 279 Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow]; 280 for ( CurrentRow = 0 281 ; CurrentRow < ConsoleInfo->RowsPerScreen 282 ; CurrentRow++ 283 , Screen += (ConsoleInfo->ColsPerScreen + 2) 284 , Attributes += ConsoleInfo->ColsPerScreen 285 ){ 286 // 287 // dont use the last char - prevents screen scroll 288 // 289 if (CurrentRow == (ConsoleInfo->RowsPerScreen-1)){ 290 TempCharHolder = Screen[ConsoleInfo->ColsPerScreen - 1]; 291 Screen[ConsoleInfo->ColsPerScreen - 1] = CHAR_NULL; 292 } 293 294 for ( Column = 0 295 ; Column < ConsoleInfo->ColsPerScreen 296 ; Column++ 297 ){ 298 if (Screen[Column] != CHAR_NULL) { 299 CurrentAttrib = Attributes[Column]; 300 CurrentColumn = Column; 301 StringSegment = &Screen[Column]; 302 303 // 304 // Find the first char with a different arrribute and make that temporarily NULL 305 // so we can do fewer printout statements. (later) restore that one and we will 306 // start at that collumn on the next loop. 307 // 308 StringSegmentEndChar = CHAR_NULL; 309 for ( StringSegmentEnd = StringSegment 310 ; StringSegmentEnd != CHAR_NULL 311 ; StringSegmentEnd++ 312 , Column++ 313 ){ 314 if (Attributes[Column] != CurrentAttrib) { 315 StringSegmentEndChar = *StringSegmentEnd; 316 *StringSegmentEnd = CHAR_NULL; 317 break; 318 } 319 } // StringSegmentEnd loop 320 321 // 322 // Now write out as much as had the same Attributes 323 // 324 325 ConsoleInfo->OldConOut->SetAttribute(ConsoleInfo->OldConOut, CurrentAttrib); 326 ConsoleInfo->OldConOut->SetCursorPosition(ConsoleInfo->OldConOut, CurrentColumn, CurrentRow); 327 Status = ConsoleInfo->OldConOut->OutputString(ConsoleInfo->OldConOut, StringSegment); 328 329 if (EFI_ERROR(Status)) { 330 ASSERT(FALSE); 331 RetVal = Status; 332 } 333 334 // 335 // If we found a change in attribute put the character back and decrement the column 336 // so when it increments it will point at that character and we will start printing 337 // a segment with that new attribute 338 // 339 if (StringSegmentEndChar != CHAR_NULL) { 340 *StringSegmentEnd = StringSegmentEndChar; 341 StringSegmentEndChar = CHAR_NULL; 342 Column--; 343 } 344 } 345 } // column for loop 346 347 // 348 // If we removed the last char and this was the last row put it back 349 // 350 if (TempCharHolder != CHAR_NULL) { 351 Screen[ConsoleInfo->ColsPerScreen - 1] = TempCharHolder; 352 TempCharHolder = CHAR_NULL; 353 } 354 } // row for loop 355 356 // 357 // If we are setting the screen back to original turn on the cursor and make it visible 358 // and set the attributes back to what they were 359 // 360 if (ConsoleInfo->CurrentStartRow == ConsoleInfo->OriginalStartRow) { 361 ConsoleInfo->OldConOut->SetAttribute ( 362 ConsoleInfo->OldConOut, 363 ConsoleInfo->HistoryMode.Attribute 364 ); 365 ConsoleInfo->OldConOut->SetCursorPosition ( 366 ConsoleInfo->OldConOut, 367 ConsoleInfo->HistoryMode.CursorColumn, 368 ConsoleInfo->HistoryMode.CursorRow - ConsoleInfo->OriginalStartRow 369 ); 370 371 Status = ConsoleInfo->OldConOut->EnableCursor ( 372 ConsoleInfo->OldConOut, 373 ConsoleInfo->HistoryMode.CursorVisible 374 ); 375 if (EFI_ERROR (Status)) { 376 RetVal = Status; 377 } 378 } else { 379 ConsoleInfo->OldConOut->SetAttribute ( 380 ConsoleInfo->OldConOut, 381 OrigAttribute 382 ); 383 } 384 385 return (RetVal); 386 } 387 388 /** 389 Reset the text output device hardware and optionaly run diagnostics 390 391 @param This pointer to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 392 @param ExtendedVerification Indicates that a more extensive test may be performed 393 394 @retval EFI_SUCCESS The text output device was reset. 395 @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and 396 could not be reset. 397 **/ 398 EFI_STATUS 399 EFIAPI 400 ConsoleLoggerReset ( 401 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 402 IN BOOLEAN ExtendedVerification 403 ) 404 { 405 EFI_STATUS Status; 406 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 407 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 408 409 // 410 // Forward the request to the original ConOut 411 // 412 Status = ConsoleInfo->OldConOut->Reset (ConsoleInfo->OldConOut, ExtendedVerification); 413 414 // 415 // Check that the buffers are still correct for logging 416 // 417 if (!EFI_ERROR (Status)) { 418 ConsoleLoggerResetBuffers(ConsoleInfo); 419 if (ExtendedVerification) { 420 ConsoleInfo->OriginalStartRow = 0; 421 ConsoleInfo->CurrentStartRow = 0; 422 } 423 } 424 425 return Status; 426 } 427 428 /** 429 Appends a string to the history buffer. If the buffer is full then the oldest 430 information in the buffer will be dropped. Information is added in a line by 431 line manner such that an empty line takes up just as much space as a full line. 432 433 @param[in] String String pointer to add. 434 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 435 **/ 436 EFI_STATUS 437 EFIAPI 438 AppendStringToHistory( 439 IN CONST CHAR16 *String, 440 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 441 ) 442 { 443 CONST CHAR16 *Walker; 444 UINTN CopySize; 445 UINTN PrintIndex; 446 UINTN Index; 447 448 ASSERT(ConsoleInfo != NULL); 449 450 for ( Walker = String 451 ; Walker != NULL && *Walker != CHAR_NULL 452 ; Walker++ 453 ){ 454 switch (*Walker) { 455 case (CHAR_BACKSPACE): 456 if (ConsoleInfo->HistoryMode.CursorColumn > 0) { 457 ConsoleInfo->HistoryMode.CursorColumn--; 458 } 459 break; 460 case (CHAR_LINEFEED): 461 if (ConsoleInfo->HistoryMode.CursorRow >= (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)) { 462 // 463 // Should never be bigger 464 // 465 ASSERT(ConsoleInfo->HistoryMode.CursorRow == (INT32)((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount)-1)); 466 467 // 468 // scroll history attributes 'up' 1 row and set the last row to default attribute 469 // 470 CopySize = ConsoleInfo->ColsPerScreen 471 * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1) 472 * sizeof(ConsoleInfo->Attributes[0]); 473 ASSERT(CopySize < ConsoleInfo->AttribSize); 474 CopyMem( 475 ConsoleInfo->Attributes, 476 ConsoleInfo->Attributes + ConsoleInfo->ColsPerScreen, 477 CopySize 478 ); 479 480 for ( Index = 0 481 ; Index < ConsoleInfo->ColsPerScreen 482 ; Index++ 483 ){ 484 *(ConsoleInfo->Attributes + (CopySize/sizeof(ConsoleInfo->Attributes[0])) + Index) = ConsoleInfo->HistoryMode.Attribute; 485 } 486 487 // 488 // scroll history buffer 'up' 1 row and set the last row to spaces (L' ') 489 // 490 CopySize = (ConsoleInfo->ColsPerScreen + 2) 491 * ((ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) - 1) 492 * sizeof(ConsoleInfo->Buffer[0]); 493 ASSERT(CopySize < ConsoleInfo->BufferSize); 494 CopyMem( 495 ConsoleInfo->Buffer, 496 ConsoleInfo->Buffer + (ConsoleInfo->ColsPerScreen + 2), 497 CopySize 498 ); 499 500 // 501 // Set that last row of chars to spaces 502 // 503 SetMem16(((UINT8*)ConsoleInfo->Buffer)+CopySize, ConsoleInfo->ColsPerScreen*sizeof(CHAR16), L' '); 504 } else { 505 // 506 // we are not on the last row 507 // 508 509 // 510 // We should not be scrolling history 511 // 512 ASSERT (ConsoleInfo->OriginalStartRow == ConsoleInfo->CurrentStartRow); 513 // 514 // are we at the end of a row? 515 // 516 if (ConsoleInfo->HistoryMode.CursorRow == (INT32) (ConsoleInfo->OriginalStartRow + ConsoleInfo->RowsPerScreen - 1)) { 517 ConsoleInfo->OriginalStartRow++; 518 ConsoleInfo->CurrentStartRow++; 519 } 520 ConsoleInfo->HistoryMode.CursorRow++; 521 } 522 break; 523 case (CHAR_CARRIAGE_RETURN): 524 // 525 // Move the cursor to the beginning of the current row. 526 // 527 ConsoleInfo->HistoryMode.CursorColumn = 0; 528 break; 529 default: 530 // 531 // Acrtually print characters into the history buffer 532 // 533 534 PrintIndex = ConsoleInfo->HistoryMode.CursorRow * ConsoleInfo->ColsPerScreen + ConsoleInfo->HistoryMode.CursorColumn; 535 536 for ( // no initializer needed 537 ; ConsoleInfo->HistoryMode.CursorColumn < (INT32) ConsoleInfo->ColsPerScreen 538 ; ConsoleInfo->HistoryMode.CursorColumn++ 539 , PrintIndex++ 540 , Walker++ 541 ){ 542 if (*Walker == CHAR_NULL 543 ||*Walker == CHAR_BACKSPACE 544 ||*Walker == CHAR_LINEFEED 545 ||*Walker == CHAR_CARRIAGE_RETURN 546 ){ 547 Walker--; 548 break; 549 } 550 // 551 // The buffer is 2*CursorRow more since it has that many \r\n characters at the end of each row. 552 // 553 554 ASSERT(PrintIndex + ConsoleInfo->HistoryMode.CursorRow < ConsoleInfo->BufferSize); 555 ConsoleInfo->Buffer[PrintIndex + (2*ConsoleInfo->HistoryMode.CursorRow)] = *Walker; 556 ASSERT(PrintIndex < ConsoleInfo->AttribSize); 557 ConsoleInfo->Attributes[PrintIndex] = ConsoleInfo->HistoryMode.Attribute; 558 } // for loop 559 560 // 561 // Add the carriage return and line feed at the end of the lines 562 // 563 if (ConsoleInfo->HistoryMode.CursorColumn >= (INT32)ConsoleInfo->ColsPerScreen) { 564 AppendStringToHistory(L"\r\n", ConsoleInfo); 565 Walker--; 566 } 567 568 break; 569 } // switch for character 570 } // for loop 571 572 return (EFI_SUCCESS); 573 } 574 575 /** 576 Worker function to handle printing the output to the screen 577 and the history buffer 578 579 @param[in] String The string to output 580 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 581 582 @retval EFI_SUCCESS The string was printed 583 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output 584 the text. 585 @retval EFI_UNSUPPORTED The output device's mode is not currently in a 586 defined text mode. 587 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the 588 characters in the Unicode string could not be 589 rendered and were skipped. 590 **/ 591 EFI_STATUS 592 EFIAPI 593 ConsoleLoggerOutputStringSplit( 594 IN CONST CHAR16 *String, 595 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 596 ) 597 { 598 EFI_STATUS Status; 599 600 // 601 // Forward the request to the original ConOut 602 // 603 Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String); 604 605 if (EFI_ERROR(Status)) { 606 return (Status); 607 } 608 609 return (AppendStringToHistory(String, ConsoleInfo)); 610 } 611 612 /** 613 Function to handle page break mode. 614 615 This function will prompt for continue or break. 616 617 @retval EFI_SUCCESS Continue was choosen 618 @return other Break was choosen 619 **/ 620 EFI_STATUS 621 EFIAPI 622 ConsoleLoggerDoPageBreak( 623 VOID 624 ) 625 { 626 SHELL_PROMPT_RESPONSE *Resp; 627 EFI_STATUS Status; 628 629 Resp = NULL; 630 ASSERT(ShellInfoObject.PageBreakEnabled); 631 ShellInfoObject.PageBreakEnabled = FALSE; 632 Status = ShellPromptForResponseHii(ShellPromptResponseTypeQuitContinue, STRING_TOKEN(STR_SHELL_QUIT_CONT), ShellInfoObject.HiiHandle, (VOID**)&Resp); 633 ShellInfoObject.PageBreakEnabled = TRUE; 634 ASSERT(Resp != NULL); 635 if (Resp == NULL) { 636 return (EFI_NOT_FOUND); 637 } 638 if (EFI_ERROR(Status)) { 639 if (Resp != NULL) { 640 FreePool(Resp); 641 } 642 return (Status); 643 } 644 if (*Resp == ShellPromptResponseContinue) { 645 FreePool(Resp); 646 ShellInfoObject.ConsoleInfo->RowCounter = 0; 647 // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorRow = 0; 648 // ShellInfoObject.ConsoleInfo->OurConOut.Mode->CursorColumn = 0; 649 650 return (EFI_SUCCESS); 651 } else if (*Resp == ShellPromptResponseQuit) { 652 FreePool(Resp); 653 ShellInfoObject.ConsoleInfo->Enabled = FALSE; 654 // 655 // When user wants to quit, the shell should stop running the command. 656 // 657 gBS->SignalEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak); 658 return (EFI_DEVICE_ERROR); 659 } else { 660 ASSERT(FALSE); 661 } 662 return (EFI_SUCCESS); 663 } 664 /** 665 Worker function to handle printing the output with page breaks. 666 667 @param[in] String The string to output 668 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 669 670 @retval EFI_SUCCESS The string was printed 671 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output 672 the text. 673 @retval EFI_UNSUPPORTED The output device's mode is not currently in a 674 defined text mode. 675 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the 676 characters in the Unicode string could not be 677 rendered and were skipped. 678 **/ 679 EFI_STATUS 680 EFIAPI 681 ConsoleLoggerPrintWithPageBreak( 682 IN CONST CHAR16 *String, 683 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 684 ) 685 { 686 CONST CHAR16 *Walker; 687 CONST CHAR16 *LineStart; 688 CHAR16 *StringCopy; 689 CHAR16 TempChar; 690 691 StringCopy = NULL; 692 StringCopy = StrnCatGrow(&StringCopy, NULL, String, 0); 693 if (StringCopy == NULL) { 694 return (EFI_OUT_OF_RESOURCES); 695 } 696 697 for ( Walker = StringCopy 698 , LineStart = StringCopy 699 ; Walker != NULL && *Walker != CHAR_NULL 700 ; Walker++ 701 ){ 702 switch (*Walker) { 703 case (CHAR_BACKSPACE): 704 if (ConsoleInfo->OurConOut.Mode->CursorColumn > 0) { 705 ConsoleInfo->OurConOut.Mode->CursorColumn--; 706 } 707 break; 708 case (CHAR_LINEFEED): 709 // 710 // add a temp NULL terminator 711 // 712 TempChar = *(Walker + 1); 713 *((CHAR16*)(Walker+1)) = CHAR_NULL; 714 715 // 716 // output the string 717 // 718 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); 719 720 // 721 // restore the temp NULL terminator to it's original character 722 // 723 *((CHAR16*)(Walker+1)) = TempChar; 724 725 // 726 // Update LineStart Variable 727 // 728 LineStart = Walker + 1; 729 730 // 731 // increment row count 732 // 733 ShellInfoObject.ConsoleInfo->RowCounter++; 734 ConsoleInfo->OurConOut.Mode->CursorRow++; 735 736 break; 737 case (CHAR_CARRIAGE_RETURN): 738 // 739 // Move the cursor to the beginning of the current row. 740 // 741 ConsoleInfo->OurConOut.Mode->CursorColumn = 0; 742 break; 743 default: 744 // 745 // increment column count 746 // 747 ConsoleInfo->OurConOut.Mode->CursorColumn++; 748 // 749 // check if that is the last column 750 // 751 if ((INTN)ConsoleInfo->ColsPerScreen == ConsoleInfo->OurConOut.Mode->CursorColumn + 1) { 752 // 753 // output a line similar to the linefeed character. 754 // 755 756 // 757 // add a temp NULL terminator 758 // 759 TempChar = *(Walker + 1); 760 *((CHAR16*)(Walker+1)) = CHAR_NULL; 761 762 // 763 // output the string 764 // 765 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); 766 767 // 768 // restore the temp NULL terminator to it's original character 769 // 770 *((CHAR16*)(Walker+1)) = TempChar; 771 772 // 773 // Update LineStart Variable 774 // 775 LineStart = Walker + 1; 776 777 // 778 // increment row count and zero the column 779 // 780 ShellInfoObject.ConsoleInfo->RowCounter++; 781 ConsoleInfo->OurConOut.Mode->CursorRow++; 782 ConsoleInfo->OurConOut.Mode->CursorColumn = 0; 783 } // last column on line 784 break; 785 } // switch for character 786 787 // 788 // check if that was the last printable row. If yes handle PageBreak mode 789 // 790 if ((ConsoleInfo->RowsPerScreen) -1 == ShellInfoObject.ConsoleInfo->RowCounter) { 791 if (EFI_ERROR(ConsoleLoggerDoPageBreak())) { 792 // 793 // We got an error which means 'break' and halt the printing 794 // 795 SHELL_FREE_NON_NULL(StringCopy); 796 return (EFI_DEVICE_ERROR); 797 } 798 } 799 } // for loop 800 801 if (LineStart != NULL && *LineStart != CHAR_NULL) { 802 ConsoleLoggerOutputStringSplit (LineStart, ConsoleInfo); 803 } 804 805 SHELL_FREE_NON_NULL(StringCopy); 806 return (EFI_SUCCESS); 807 } 808 809 /** 810 Write a Unicode string to the output device. 811 812 @param[in] This Protocol instance pointer. 813 @param[in] WString The NULL-terminated Unicode string to be displayed on the output 814 device(s). All output devices must also support the Unicode 815 drawing defined in this file. 816 @retval EFI_SUCCESS The string was output to the device. 817 @retval EFI_DEVICE_ERROR The device reported an error while attempting to output 818 the text. 819 @retval EFI_UNSUPPORTED The output device's mode is not currently in a 820 defined text mode. 821 @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the 822 characters in the Unicode string could not be 823 rendered and were skipped. 824 **/ 825 EFI_STATUS 826 EFIAPI 827 ConsoleLoggerOutputString ( 828 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 829 IN CHAR16 *WString 830 ) 831 { 832 EFI_STATUS Status; 833 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; 834 EFI_KEY_DATA KeyData; 835 UINTN EventIndex; 836 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 837 838 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 839 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { 840 return (EFI_UNSUPPORTED); 841 } 842 ASSERT(ShellInfoObject.ConsoleInfo == ConsoleInfo); 843 844 Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx); 845 if (!EFI_ERROR (Status)) { 846 while (ShellInfoObject.HaltOutput) { 847 848 ShellInfoObject.HaltOutput = FALSE; 849 // 850 // just get some key 851 // 852 Status = gBS->WaitForEvent (1, &TxtInEx->WaitForKeyEx, &EventIndex); 853 ASSERT_EFI_ERROR (Status); 854 Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData); 855 if (EFI_ERROR(Status)) { 856 break; 857 } 858 859 if ((KeyData.Key.UnicodeChar == L's') && (KeyData.Key.ScanCode == SCAN_NULL) && 860 ((KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED)) || 861 (KeyData.KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID | EFI_RIGHT_CONTROL_PRESSED)) 862 ) 863 ) { 864 ShellInfoObject.HaltOutput = TRUE; 865 } 866 } 867 } 868 869 if (!ShellInfoObject.ConsoleInfo->Enabled) { 870 return (EFI_DEVICE_ERROR); 871 } else if (ShellInfoObject.PageBreakEnabled) { 872 return (ConsoleLoggerPrintWithPageBreak(WString, ConsoleInfo)); 873 } else { 874 return (ConsoleLoggerOutputStringSplit(WString, ConsoleInfo)); 875 } 876 } 877 878 /** 879 Verifies that all characters in a Unicode string can be output to the 880 target device. 881 882 @param[in] This Protocol instance pointer. 883 @param[in] WString The NULL-terminated Unicode string to be examined for the output 884 device(s). 885 886 @retval EFI_SUCCESS The device(s) are capable of rendering the output string. 887 @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be 888 rendered by one or more of the output devices mapped 889 by the EFI handle. 890 891 **/ 892 EFI_STATUS 893 EFIAPI 894 ConsoleLoggerTestString ( 895 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 896 IN CHAR16 *WString 897 ) 898 { 899 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 900 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 901 // 902 // Forward the request to the original ConOut 903 // 904 return (ConsoleInfo->OldConOut->TestString (ConsoleInfo->OldConOut, WString)); 905 } 906 907 /** 908 Returns information for an available text mode that the output device(s) 909 supports. 910 911 @param[in] This Protocol instance pointer. 912 @param[in] ModeNumber The mode number to return information on. 913 @param[out] Columns Upon return, the number of columns in the selected geometry 914 @param[out] Rows Upon return, the number of rows in the selected geometry 915 916 @retval EFI_SUCCESS The requested mode information was returned. 917 @retval EFI_DEVICE_ERROR The device had an error and could not 918 complete the request. 919 @retval EFI_UNSUPPORTED The mode number was not valid. 920 **/ 921 EFI_STATUS 922 EFIAPI 923 ConsoleLoggerQueryMode ( 924 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 925 IN UINTN ModeNumber, 926 OUT UINTN *Columns, 927 OUT UINTN *Rows 928 ) 929 { 930 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 931 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 932 // 933 // Forward the request to the original ConOut 934 // 935 return (ConsoleInfo->OldConOut->QueryMode ( 936 ConsoleInfo->OldConOut, 937 ModeNumber, 938 Columns, 939 Rows 940 )); 941 } 942 943 /** 944 Sets the output device(s) to a specified mode. 945 946 @param[in] This Protocol instance pointer. 947 @param[in] ModeNumber The mode number to set. 948 949 950 @retval EFI_SUCCESS The requested text mode was set. 951 @retval EFI_DEVICE_ERROR The device had an error and 952 could not complete the request. 953 @retval EFI_UNSUPPORTED The mode number was not valid. 954 **/ 955 EFI_STATUS 956 EFIAPI 957 ConsoleLoggerSetMode ( 958 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 959 IN UINTN ModeNumber 960 ) 961 { 962 EFI_STATUS Status; 963 964 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 965 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 966 967 // 968 // Forward the request to the original ConOut 969 // 970 Status = ConsoleInfo->OldConOut->SetMode (ConsoleInfo->OldConOut, ModeNumber); 971 972 // 973 // Check that the buffers are still correct for logging 974 // 975 if (!EFI_ERROR (Status)) { 976 ConsoleInfo->OurConOut.Mode = ConsoleInfo->OldConOut->Mode; 977 ConsoleLoggerResetBuffers(ConsoleInfo); 978 ConsoleInfo->OriginalStartRow = 0; 979 ConsoleInfo->CurrentStartRow = 0; 980 ConsoleInfo->OurConOut.ClearScreen (&ConsoleInfo->OurConOut); 981 } 982 983 return Status; 984 } 985 986 /** 987 Sets the background and foreground colors for the OutputString () and 988 ClearScreen () functions. 989 990 @param[in] This Protocol instance pointer. 991 @param[in] Attribute The attribute to set. Bits 0..3 are the foreground color, and 992 bits 4..6 are the background color. All other bits are undefined 993 and must be zero. The valid Attributes are defined in this file. 994 995 @retval EFI_SUCCESS The attribute was set. 996 @retval EFI_DEVICE_ERROR The device had an error and 997 could not complete the request. 998 @retval EFI_UNSUPPORTED The attribute requested is not defined. 999 1000 **/ 1001 EFI_STATUS 1002 EFIAPI 1003 ConsoleLoggerSetAttribute ( 1004 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 1005 IN UINTN Attribute 1006 ) 1007 { 1008 EFI_STATUS Status; 1009 1010 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 1011 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 1012 1013 // 1014 // Forward the request to the original ConOut 1015 // 1016 Status = ConsoleInfo->OldConOut->SetAttribute (ConsoleInfo->OldConOut, Attribute); 1017 1018 // 1019 // Record console output history 1020 // 1021 if (!EFI_ERROR (Status)) { 1022 ConsoleInfo->HistoryMode.Attribute = (INT32) Attribute; 1023 } 1024 1025 return Status; 1026 } 1027 1028 /** 1029 Clears the output device(s) display to the currently selected background 1030 color. 1031 1032 @param[in] This Protocol instance pointer. 1033 1034 @retval EFI_SUCCESS The operation completed successfully. 1035 @retval EFI_DEVICE_ERROR The device had an error and 1036 could not complete the request. 1037 @retval EFI_UNSUPPORTED The output device is not in a valid text mode. 1038 **/ 1039 EFI_STATUS 1040 EFIAPI 1041 ConsoleLoggerClearScreen ( 1042 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 1043 ) 1044 { 1045 EFI_STATUS Status; 1046 CHAR16 *Screen; 1047 INT32 *Attributes; 1048 UINTN Row; 1049 UINTN Column; 1050 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 1051 1052 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { 1053 return (EFI_UNSUPPORTED); 1054 } 1055 1056 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 1057 1058 // 1059 // Forward the request to the original ConOut 1060 // 1061 Status = ConsoleInfo->OldConOut->ClearScreen (ConsoleInfo->OldConOut); 1062 1063 // 1064 // Record console output history 1065 // 1066 if (!EFI_ERROR (Status)) { 1067 Screen = &ConsoleInfo->Buffer[(ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->CurrentStartRow]; 1068 Attributes = &ConsoleInfo->Attributes[ConsoleInfo->ColsPerScreen * ConsoleInfo->CurrentStartRow]; 1069 for ( Row = ConsoleInfo->OriginalStartRow 1070 ; Row < (ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount) 1071 ; Row++ 1072 ){ 1073 for ( Column = 0 1074 ; Column < ConsoleInfo->ColsPerScreen 1075 ; Column++ 1076 , Screen++ 1077 , Attributes++ 1078 ){ 1079 *Screen = L' '; 1080 *Attributes = ConsoleInfo->OldConOut->Mode->Attribute; 1081 } 1082 // 1083 // Skip the NULL on each column end in text buffer only 1084 // 1085 Screen += 2; 1086 } 1087 ConsoleInfo->HistoryMode.CursorColumn = 0; 1088 ConsoleInfo->HistoryMode.CursorRow = 0; 1089 } 1090 1091 return Status; 1092 } 1093 1094 /** 1095 Sets the current coordinates of the cursor position 1096 1097 @param[in] This Protocol instance pointer. 1098 @param[in] Column Column to put the cursor in. Must be between zero and Column returned from QueryMode 1099 @param[in] Row Row to put the cursor in. Must be between zero and Row returned from QueryMode 1100 1101 @retval EFI_SUCCESS The operation completed successfully. 1102 @retval EFI_DEVICE_ERROR The device had an error and 1103 could not complete the request. 1104 @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the 1105 cursor position is invalid for the current mode. 1106 **/ 1107 EFI_STATUS 1108 EFIAPI 1109 ConsoleLoggerSetCursorPosition ( 1110 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 1111 IN UINTN Column, 1112 IN UINTN Row 1113 ) 1114 { 1115 EFI_STATUS Status; 1116 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 1117 1118 if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut) { 1119 return (EFI_UNSUPPORTED); 1120 } 1121 1122 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 1123 // 1124 // Forward the request to the original ConOut 1125 // 1126 Status = ConsoleInfo->OldConOut->SetCursorPosition ( 1127 ConsoleInfo->OldConOut, 1128 Column, 1129 Row 1130 ); 1131 1132 // 1133 // Record console output history 1134 // 1135 if (!EFI_ERROR (Status)) { 1136 ConsoleInfo->HistoryMode.CursorColumn = (INT32)Column; 1137 ConsoleInfo->HistoryMode.CursorRow = (INT32)(ConsoleInfo->OriginalStartRow + Row); 1138 } 1139 1140 return Status; 1141 } 1142 1143 /** 1144 Makes the cursor visible or invisible 1145 1146 @param[in] This Protocol instance pointer. 1147 @param[in] Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is 1148 set to be invisible. 1149 1150 @retval EFI_SUCCESS The operation completed successfully. 1151 @retval EFI_DEVICE_ERROR The device had an error and could not complete the 1152 request, or the device does not support changing 1153 the cursor mode. 1154 @retval EFI_UNSUPPORTED The output device is not in a valid text mode. 1155 **/ 1156 EFI_STATUS 1157 EFIAPI 1158 ConsoleLoggerEnableCursor ( 1159 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 1160 IN BOOLEAN Visible 1161 ) 1162 { 1163 EFI_STATUS Status; 1164 1165 CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo; 1166 ConsoleInfo = CONSOLE_LOGGER_PRIVATE_DATA_FROM_THIS(This); 1167 // 1168 // Forward the request to the original ConOut 1169 // 1170 Status = ConsoleInfo->OldConOut->EnableCursor (ConsoleInfo->OldConOut, Visible); 1171 1172 // 1173 // Record console output history 1174 // 1175 if (!EFI_ERROR (Status)) { 1176 ConsoleInfo->HistoryMode.CursorVisible = Visible; 1177 } 1178 1179 return Status; 1180 } 1181 1182 /** 1183 Function to update and verify that the current buffers are correct. 1184 1185 @param[in] ConsoleInfo The pointer to the instance of the console logger information. 1186 1187 This will be used when a mode has changed or a reset ocurred to verify all 1188 history buffers. 1189 **/ 1190 EFI_STATUS 1191 EFIAPI 1192 ConsoleLoggerResetBuffers( 1193 IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo 1194 ) 1195 { 1196 EFI_STATUS Status; 1197 1198 if (ConsoleInfo->Buffer != NULL) { 1199 FreePool(ConsoleInfo->Buffer); 1200 ConsoleInfo->Buffer = NULL; 1201 ConsoleInfo->BufferSize = 0; 1202 } 1203 if (ConsoleInfo->Attributes != NULL) { 1204 FreePool(ConsoleInfo->Attributes); 1205 ConsoleInfo->Attributes = NULL; 1206 ConsoleInfo->AttribSize = 0; 1207 } 1208 1209 Status = gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &ConsoleInfo->ColsPerScreen, &ConsoleInfo->RowsPerScreen); 1210 if (EFI_ERROR(Status)){ 1211 return (Status); 1212 } 1213 1214 ConsoleInfo->BufferSize = (ConsoleInfo->ColsPerScreen + 2) * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Buffer[0]); 1215 ConsoleInfo->AttribSize = ConsoleInfo->ColsPerScreen * ConsoleInfo->RowsPerScreen * ConsoleInfo->ScreenCount * sizeof(ConsoleInfo->Attributes[0]); 1216 1217 ConsoleInfo->Buffer = (CHAR16*)AllocateZeroPool(ConsoleInfo->BufferSize); 1218 1219 if (ConsoleInfo->Buffer == NULL) { 1220 return (EFI_OUT_OF_RESOURCES); 1221 } 1222 1223 ConsoleInfo->Attributes = (INT32*)AllocateZeroPool(ConsoleInfo->AttribSize); 1224 if (ConsoleInfo->Attributes == NULL) { 1225 FreePool(ConsoleInfo->Buffer); 1226 ConsoleInfo->Buffer = NULL; 1227 return (EFI_OUT_OF_RESOURCES); 1228 } 1229 1230 CopyMem (&ConsoleInfo->HistoryMode, ConsoleInfo->OldConOut->Mode, sizeof (EFI_SIMPLE_TEXT_OUTPUT_MODE)); 1231 1232 return (EFI_SUCCESS); 1233 } 1234