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