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