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