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