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