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