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