1 /** @file
2 
3 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
5 
6 
7 **/
8 
9 #include "Edb.h"
10 
11 /**
12   Set the current coordinates of the cursor position.
13 
14   @param  ConOut        Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
15   @param  Column        The position to set the cursor to.
16   @param  Row           The position to set the cursor to.
17   @param  LineLength    Length of a line.
18   @param  TotalRow      Total row of a screen.
19   @param  Str           Point to the string.
20   @param  StrPos        The position of the string.
21   @param  Len           The length of the string.
22 
23 **/
24 VOID
25 EFIAPI
26 SetCursorPosition (
27   IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut,
28   IN  UINTN                           Column,
29   IN  INTN                            Row,
30   IN  UINTN                           LineLength,
31   IN  UINTN                           TotalRow,
32   IN  CHAR16                          *Str,
33   IN  UINTN                           StrPos,
34   IN  UINTN                           Len
35   );
36 
37 /**
38 
39   Function waits for a given event to fire, or for an optional timeout to expire.
40 
41   @param  Event            - The event to wait for
42   @param  Timeout          - An optional timeout value in 100 ns units.
43 
44   @retval EFI_SUCCESS       - Event fired before Timeout expired.
45   @retval EFI_TIME_OUT     - Timout expired before Event fired..
46 
47 **/
48 EFI_STATUS
49 EFIAPI
WaitForSingleEvent(IN EFI_EVENT Event,IN UINT64 Timeout OPTIONAL)50 WaitForSingleEvent (
51   IN EFI_EVENT                  Event,
52   IN UINT64                     Timeout OPTIONAL
53   )
54 {
55   EFI_STATUS  Status;
56   UINTN       Index;
57   EFI_EVENT   TimerEvent;
58   EFI_EVENT   WaitList[2];
59 
60   if (Timeout != 0) {
61     //
62     // Create a timer event
63     //
64     Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
65     if (!EFI_ERROR (Status)) {
66       //
67       // Set the timer event
68       //
69       gBS->SetTimer (
70             TimerEvent,
71             TimerRelative,
72             Timeout
73             );
74 
75       //
76       // Wait for the original event or the timer
77       //
78       WaitList[0] = Event;
79       WaitList[1] = TimerEvent;
80       Status      = gBS->WaitForEvent (2, WaitList, &Index);
81       gBS->CloseEvent (TimerEvent);
82 
83       //
84       // If the timer expired, change the return to timed out
85       //
86       if (!EFI_ERROR (Status) && Index == 1) {
87         Status = EFI_TIMEOUT;
88       }
89     }
90   } else {
91     //
92     // No timeout... just wait on the event
93     //
94     Status = gBS->WaitForEvent (1, &Event, &Index);
95     ASSERT (!EFI_ERROR (Status));
96     ASSERT (Index == 0);
97   }
98 
99   return Status;
100 }
101 
102 /**
103 
104   Move the cursor position one character backward.
105 
106   @param  LineLength       Length of a line. Get it by calling QueryMode
107   @param  Column           Current column of the cursor position
108   @param  Row              Current row of the cursor position
109 
110 **/
111 VOID
112 EFIAPI
ConMoveCursorBackward(IN UINTN LineLength,IN OUT UINTN * Column,IN OUT UINTN * Row)113 ConMoveCursorBackward (
114   IN     UINTN                   LineLength,
115   IN OUT UINTN                   *Column,
116   IN OUT UINTN                   *Row
117   )
118 {
119   ASSERT (Column != NULL);
120   ASSERT (Row != NULL);
121   //
122   // If current column is 0, move to the last column of the previous line,
123   // otherwise, just decrement column.
124   //
125   if (*Column == 0) {
126     (*Column) = LineLength - 1;
127     //
128     //   if (*Row > 0) {
129     //
130     (*Row)--;
131     //
132     // }
133     //
134   } else {
135     (*Column)--;
136   }
137 }
138 
139 /**
140 
141   Move the cursor position one character backward.
142 
143   @param  LineLength       Length of a line. Get it by calling QueryMode
144   @param  TotalRow         Total row of a screen, get by calling QueryMode
145   @param  Column           Current column of the cursor position
146   @param  Row              Current row of the cursor position
147 
148 **/
149 VOID
150 EFIAPI
ConMoveCursorForward(IN UINTN LineLength,IN UINTN TotalRow,IN OUT UINTN * Column,IN OUT UINTN * Row)151 ConMoveCursorForward (
152   IN     UINTN                   LineLength,
153   IN     UINTN                   TotalRow,
154   IN OUT UINTN                   *Column,
155   IN OUT UINTN                   *Row
156   )
157 {
158   ASSERT (Column != NULL);
159   ASSERT (Row != NULL);
160   //
161   // If current column is at line end, move to the first column of the nest
162   // line, otherwise, just increment column.
163   //
164   (*Column)++;
165   if (*Column >= LineLength) {
166     (*Column) = 0;
167     if ((*Row) < TotalRow - 1) {
168       (*Row)++;
169     }
170   }
171 }
172 
173 CHAR16 mBackupSpace[EFI_DEBUG_INPUS_BUFFER_SIZE];
174 CHAR16 mInputBufferHistory[EFI_DEBUG_INPUS_BUFFER_SIZE];
175 
176 /**
177 
178   Get user input.
179 
180   @param  Prompt       The prompt string.
181   @param  InStr        Point to the input string.
182   @param  StrLength    The max length of string user can input.
183 
184 **/
185 VOID
186 EFIAPI
Input(IN CHAR16 * Prompt OPTIONAL,OUT CHAR16 * InStr,IN UINTN StrLength)187 Input (
188   IN CHAR16    *Prompt OPTIONAL,
189   OUT CHAR16   *InStr,
190   IN UINTN     StrLength
191   )
192 {
193   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL     *ConOut;
194   EFI_SIMPLE_TEXT_INPUT_PROTOCOL      *ConIn;
195   BOOLEAN       Done;
196   UINTN         Column;
197   UINTN         Row;
198   UINTN         StartColumn;
199   UINTN         Update;
200   UINTN         Delete;
201   UINTN         Len;
202   UINTN         StrPos;
203   UINTN         Index;
204   UINTN         LineLength;
205   UINTN         TotalRow;
206   UINTN         SkipLength;
207   UINTN         OutputLength;
208   UINTN         TailRow;
209   UINTN         TailColumn;
210   EFI_INPUT_KEY Key;
211   BOOLEAN       InsertMode;
212   BOOLEAN       NeedAdjust;
213   UINTN         SubIndex;
214   CHAR16        *CommandStr;
215 
216   ConOut = gST->ConOut;
217   ConIn = gST->ConIn;
218 
219   ASSERT (ConOut != NULL);
220   ASSERT (ConIn != NULL);
221   ASSERT (InStr != NULL);
222 
223   if (Prompt != NULL) {
224     ConOut->OutputString (ConOut, Prompt);
225   }
226   //
227   // Read a line from the console
228   //
229   Len           = 0;
230   StrPos        = 0;
231   OutputLength  = 0;
232   Update        = 0;
233   Delete        = 0;
234   InsertMode    = TRUE;
235   NeedAdjust    = FALSE;
236 
237   //
238   // If buffer is not large enough to hold a CHAR16, do nothing.
239   //
240   if (StrLength < 1) {
241     return ;
242   }
243   //
244   // Get the screen setting and the current cursor location
245   //
246   StartColumn = ConOut->Mode->CursorColumn;
247   Column      = StartColumn;
248   Row         = ConOut->Mode->CursorRow;
249   ConOut->QueryMode (ConOut, ConOut->Mode->Mode, &LineLength, &TotalRow);
250   if (LineLength == 0) {
251     return ;
252   }
253 
254   SetMem (InStr, StrLength * sizeof (CHAR16), 0);
255   Done = FALSE;
256   do {
257     //
258     // Read a key
259     //
260     WaitForSingleEvent (ConIn->WaitForKey, 0);
261     ConIn->ReadKeyStroke (ConIn, &Key);
262 
263     switch (Key.UnicodeChar) {
264     case CHAR_CARRIAGE_RETURN:
265       //
266       // All done, print a newline at the end of the string
267       //
268       TailRow     = Row + (Len - StrPos + Column) / LineLength;
269       TailColumn  = (Len - StrPos + Column) % LineLength;
270       Done        = TRUE;
271       break;
272 
273     case CHAR_BACKSPACE:
274       if (StrPos != 0) {
275         //
276         // If not move back beyond string beginning, move all characters behind
277         // the current position one character forward
278         //
279         StrPos -= 1;
280         Update  = StrPos;
281         Delete  = 1;
282         CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos));
283 
284         //
285         // Adjust the current column and row
286         //
287         ConMoveCursorBackward (LineLength, &Column, &Row);
288 
289         NeedAdjust = TRUE;
290       }
291       break;
292 
293     default:
294       if (Key.UnicodeChar >= ' ') {
295         //
296         // If we are at the buffer's end, drop the key
297         //
298         if (Len == StrLength - 1 && (InsertMode || StrPos == Len)) {
299           break;
300         }
301         //
302         // If in insert mode, move all characters behind the current position
303         // one character backward to make space for this character. Then store
304         // the character.
305         //
306         if (InsertMode) {
307           for (Index = Len; Index > StrPos; Index -= 1) {
308             InStr[Index] = InStr[Index - 1];
309           }
310         }
311 
312         InStr[StrPos] = Key.UnicodeChar;
313         Update        = StrPos;
314 
315         StrPos += 1;
316         OutputLength = 1;
317       }
318       break;
319 
320     case 0:
321       switch (Key.ScanCode) {
322       case SCAN_DELETE:
323         //
324         // Move characters behind current position one character forward
325         //
326         if (Len != 0) {
327           Update  = StrPos;
328           Delete  = 1;
329           CopyMem (InStr + StrPos, InStr + StrPos + 1, sizeof (CHAR16) * (Len - StrPos));
330 
331           NeedAdjust = TRUE;
332         }
333         break;
334 
335       case SCAN_LEFT:
336         //
337         // Adjust current cursor position
338         //
339         if (StrPos != 0) {
340           StrPos -= 1;
341           ConMoveCursorBackward (LineLength, &Column, &Row);
342         }
343         break;
344 
345       case SCAN_RIGHT:
346         //
347         // Adjust current cursor position
348         //
349         if (StrPos < Len) {
350           StrPos += 1;
351           ConMoveCursorForward (LineLength, TotalRow, &Column, &Row);
352         }
353         break;
354 
355       case SCAN_HOME:
356         //
357         // Move current cursor position to the beginning of the command line
358         //
359         Row -= (StrPos + StartColumn) / LineLength;
360         Column  = StartColumn;
361         StrPos  = 0;
362         break;
363 
364       case SCAN_END:
365         //
366         // Move current cursor position to the end of the command line
367         //
368         TailRow     = Row + (Len - StrPos + Column) / LineLength;
369         TailColumn  = (Len - StrPos + Column) % LineLength;
370         Row         = TailRow;
371         Column      = TailColumn;
372         StrPos      = Len;
373         break;
374 
375       case SCAN_ESC:
376         //
377         // Prepare to clear the current command line
378         //
379         InStr[0]  = 0;
380         Update    = 0;
381         Delete    = Len;
382         Row -= (StrPos + StartColumn) / LineLength;
383         Column        = StartColumn;
384         OutputLength  = 0;
385 
386         NeedAdjust = TRUE;
387         break;
388 
389       case SCAN_INSERT:
390         //
391         // Toggle the SEnvInsertMode flag
392         //
393         InsertMode = (BOOLEAN)!InsertMode;
394         break;
395 
396       case SCAN_UP:
397       case SCAN_DOWN:
398         //
399         // show history
400         //
401         CopyMem (InStr, mInputBufferHistory, StrLength * sizeof(CHAR16));
402         StrPos       = StrLen (mInputBufferHistory);
403         Update       = 0;
404         Delete       = 0;
405         OutputLength = 0;
406 
407         TailRow      = Row + (StrPos + StartColumn) / LineLength;
408         TailColumn   = (StrPos + StartColumn) % LineLength;
409         Row          = TailRow;
410         Column       = TailColumn;
411         NeedAdjust   = FALSE;
412 
413         ConOut->SetCursorPosition (ConOut, StartColumn, Row);
414         for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) {
415           mBackupSpace[SubIndex] = L' ';
416         }
417         EDBPrint (mBackupSpace);
418         SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (StartColumn - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0);
419 
420         ConOut->SetCursorPosition (ConOut, StartColumn, Row);
421         Len = StrPos;
422 
423         break;
424 
425       case SCAN_F1:
426       case SCAN_F2:
427       case SCAN_F3:
428       case SCAN_F4:
429       case SCAN_F5:
430       case SCAN_F6:
431       case SCAN_F7:
432       case SCAN_F8:
433       case SCAN_F9:
434       case SCAN_F10:
435       case SCAN_F11:
436       case SCAN_F12:
437         CommandStr = GetCommandNameByKey (Key);
438         if (CommandStr != NULL) {
439           StrnCpyS (InStr, StrLength, CommandStr, StrLength - 1);
440           return ;
441         }
442         break;
443       }
444     }
445 
446     if (Done) {
447       break;
448     }
449     //
450     // If we need to update the output do so now
451     //
452     if (Update != -1) {
453       if (NeedAdjust) {
454         ConOut->SetCursorPosition (ConOut, Column, Row);
455         for (SubIndex = 0; SubIndex < EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN); SubIndex++) {
456           mBackupSpace[SubIndex] = L' ';
457         }
458         EDBPrint (mBackupSpace);
459         SetMem (mBackupSpace, (EFI_DEBUG_INPUS_BUFFER_SIZE - (Column - EFI_DEBUG_PROMPT_COLUMN)) * sizeof(CHAR16), 0);
460         ConOut->SetCursorPosition (ConOut, Column, Row);
461         NeedAdjust = FALSE;
462       }
463       EDBPrint (InStr + Update);
464       Len = StrLen (InStr);
465 
466       if (Delete != 0) {
467         SetMem (InStr + Len, Delete * sizeof (CHAR16), 0x00);
468       }
469 
470       if (StrPos > Len) {
471         StrPos = Len;
472       }
473 
474       Update = (UINTN) -1;
475 
476       //
477       // After using print to reflect newly updates, if we're not using
478       // BACKSPACE and DELETE, we need to move the cursor position forward,
479       // so adjust row and column here.
480       //
481       if (Key.UnicodeChar != CHAR_BACKSPACE && !(Key.UnicodeChar == 0 && Key.ScanCode == SCAN_DELETE)) {
482         //
483         // Calulate row and column of the tail of current string
484         //
485         TailRow     = Row + (Len - StrPos + Column + OutputLength) / LineLength;
486         TailColumn  = (Len - StrPos + Column + OutputLength) % LineLength;
487 
488         //
489         // If the tail of string reaches screen end, screen rolls up, so if
490         // Row does not equal TailRow, Row should be decremented
491         //
492         // (if we are recalling commands using UPPER and DOWN key, and if the
493         // old command is too long to fit the screen, TailColumn must be 79.
494         //
495         if (TailColumn == 0 && TailRow >= TotalRow && (UINTN) Row != TailRow) {
496           Row--;
497         }
498         //
499         // Calculate the cursor position after current operation. If cursor
500         // reaches line end, update both row and column, otherwise, only
501         // column will be changed.
502         //
503         if (Column + OutputLength >= LineLength) {
504           SkipLength = OutputLength - (LineLength - Column);
505 
506           Row += SkipLength / LineLength + 1;
507           if ((UINTN) Row > TotalRow - 1) {
508             Row = TotalRow - 1;
509           }
510 
511           Column = SkipLength % LineLength;
512         } else {
513           Column += OutputLength;
514         }
515       }
516 
517       Delete = 0;
518     }
519     //
520     // Set the cursor position for this key
521     //
522     SetCursorPosition (ConOut, Column, Row, LineLength, TotalRow, InStr, StrPos, Len);
523   } while (!Done);
524 
525   CopyMem (mInputBufferHistory, InStr, StrLength * sizeof(CHAR16));
526 
527   //
528   // Return the data to the caller
529   //
530   return ;
531 }
532 
533 /**
534   Set the current coordinates of the cursor position.
535 
536   @param  ConOut        Point to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
537   @param  Column        The position to set the cursor to.
538   @param  Row           The position to set the cursor to.
539   @param  LineLength    Length of a line.
540   @param  TotalRow      Total row of a screen.
541   @param  Str           Point to the string.
542   @param  StrPos        The position of the string.
543   @param  Len           The length of the string.
544 
545 **/
546 VOID
547 EFIAPI
SetCursorPosition(IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL * ConOut,IN UINTN Column,IN INTN Row,IN UINTN LineLength,IN UINTN TotalRow,IN CHAR16 * Str,IN UINTN StrPos,IN UINTN Len)548 SetCursorPosition (
549   IN  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut,
550   IN  UINTN                           Column,
551   IN  INTN                            Row,
552   IN  UINTN                           LineLength,
553   IN  UINTN                           TotalRow,
554   IN  CHAR16                          *Str,
555   IN  UINTN                           StrPos,
556   IN  UINTN                           Len
557   )
558 {
559   CHAR16  Backup;
560 
561   ASSERT (ConOut != NULL);
562   ASSERT (Str != NULL);
563 
564   Backup = 0;
565   if (Row >= 0) {
566     ConOut->SetCursorPosition (ConOut, Column, Row);
567     return ;
568   }
569 
570   if (Len - StrPos > Column * Row) {
571     Backup                          = *(Str + StrPos + Column * Row);
572     *(Str + StrPos + Column * Row)  = 0;
573   }
574 
575   EDBPrint (L"%s", Str + StrPos);
576   if (Len - StrPos > Column * Row) {
577     *(Str + StrPos + Column * Row) = Backup;
578   }
579 
580   ConOut->SetCursorPosition (ConOut, 0, 0);
581 }
582 
583 /**
584 
585   SetPageBreak.
586 
587 **/
588 BOOLEAN
589 EFIAPI
SetPageBreak(VOID)590 SetPageBreak (
591   VOID
592   )
593 {
594   EFI_INPUT_KEY Key;
595   CHAR16        Str[3];
596   BOOLEAN       OmitPrint;
597 
598   //
599   // Check
600   //
601   if (!mDebuggerPrivate.EnablePageBreak) {
602     return FALSE;
603   }
604 
605   gST->ConOut->OutputString (gST->ConOut, L"Press ENTER to continue, 'q' to exit:");
606 
607   OmitPrint = FALSE;
608   //
609   // Wait for user input
610   //
611   Str[0]  = ' ';
612   Str[1]  = 0;
613   Str[2]  = 0;
614   for (;;) {
615     WaitForSingleEvent (gST->ConIn->WaitForKey, 0);
616     gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
617 
618     //
619     // handle control keys
620     //
621     if (Key.UnicodeChar == CHAR_NULL) {
622       if (Key.ScanCode == SCAN_ESC) {
623         gST->ConOut->OutputString (gST->ConOut, L"\r\n");
624         OmitPrint = TRUE;
625         break;
626       }
627 
628       continue;
629     }
630 
631     if (Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
632       gST->ConOut->OutputString (gST->ConOut, L"\r\n");
633       break;
634     }
635     //
636     // Echo input
637     //
638     Str[1] = Key.UnicodeChar;
639     if (Str[1] == CHAR_BACKSPACE) {
640       continue;
641     }
642 
643     gST->ConOut->OutputString (gST->ConOut, Str);
644 
645     if ((Str[1] == L'q') || (Str[1] == L'Q')) {
646       OmitPrint = TRUE;
647     } else {
648       OmitPrint = FALSE;
649     }
650 
651     Str[0] = CHAR_BACKSPACE;
652   }
653 
654   return OmitPrint;
655 }
656 
657 /**
658   Print a Unicode string to the output device.
659 
660   @param  Format    A Null-terminated Unicode format string.
661   @param  ...       The variable argument list that contains pointers to Null-
662                     terminated Unicode strings to be printed
663 
664 **/
665 UINTN
666 EFIAPI
EDBPrint(IN CONST CHAR16 * Format,...)667 EDBPrint (
668   IN CONST CHAR16  *Format,
669   ...
670   )
671 {
672   UINTN   Return;
673   VA_LIST Marker;
674   CHAR16  Buffer[EFI_DEBUG_MAX_PRINT_BUFFER];
675 
676   VA_START (Marker, Format);
677   Return = UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker);
678   VA_END (Marker);
679 
680   if (gST->ConOut != NULL) {
681     //
682     // To be extra safe make sure ConOut has been initialized
683     //
684     gST->ConOut->OutputString (gST->ConOut, Buffer);
685   }
686 
687   return Return;
688 }
689 
690 /**
691   Print a Unicode string to the output buffer.
692 
693   @param  Buffer          A pointer to the output buffer for the produced Null-terminated
694                           Unicode string.
695   @param  BufferSize      The size, in bytes, of the output buffer specified by StartOfBuffer.
696   @param  Format          A Null-terminated Unicode format string.
697   @param  ...             The variable argument list that contains pointers to Null-
698                           terminated Unicode strings to be printed
699 
700 **/
701 UINTN
702 EFIAPI
EDBSPrint(OUT CHAR16 * Buffer,IN INTN BufferSize,IN CONST CHAR16 * Format,...)703 EDBSPrint (
704   OUT CHAR16        *Buffer,
705   IN  INTN          BufferSize,
706   IN  CONST CHAR16  *Format,
707   ...
708   )
709 {
710   UINTN   Return;
711   VA_LIST Marker;
712 
713   ASSERT (BufferSize > 0);
714 
715   VA_START (Marker, Format);
716   Return = UnicodeVSPrint (Buffer, (UINTN)BufferSize, Format, Marker);
717   VA_END (Marker);
718 
719   return Return;
720 }
721 
722 /**
723   Print a Unicode string to the output buffer with specified offset..
724 
725   @param  Buffer          A pointer to the output buffer for the produced Null-terminated
726                           Unicode string.
727   @param  BufferSize      The size, in bytes, of the output buffer specified by StartOfBuffer.
728   @param  Offset          The offset of the buffer.
729   @param  Format          A Null-terminated Unicode format string.
730   @param  ...             The variable argument list that contains pointers to Null-
731                           terminated Unicode strings to be printed
732 
733 **/
734 UINTN
735 EFIAPI
EDBSPrintWithOffset(OUT CHAR16 * Buffer,IN INTN BufferSize,IN UINTN Offset,IN CONST CHAR16 * Format,...)736 EDBSPrintWithOffset (
737   OUT CHAR16        *Buffer,
738   IN  INTN          BufferSize,
739   IN  UINTN         Offset,
740   IN  CONST CHAR16  *Format,
741   ...
742   )
743 {
744   UINTN   Return;
745   VA_LIST Marker;
746 
747   ASSERT (BufferSize - (Offset * sizeof(CHAR16)) > 0);
748 
749   VA_START (Marker, Format);
750   Return = UnicodeVSPrint (Buffer + Offset, (UINTN)(BufferSize - (Offset * sizeof(CHAR16))), Format, Marker);
751   VA_END (Marker);
752 
753   return Return;
754 }
755