1/** @file
2  Simple Console that sits on a SerialLib.
3
4  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5
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
16/*
17  Symbols used in table below
18===========================
19  ESC = 0x1B
20  CSI = 0x9B
21  DEL = 0x7f
22  ^   = CTRL
23
24+=========+======+===========+==========+==========+
25|         | EFI  | UEFI 2.0  |          |          |
26|         | Scan |           |  VT100+  |          |
27|   KEY   | Code |  PC ANSI  |  VTUTF8  |   VT100  |
28+=========+======+===========+==========+==========+
29| NULL    | 0x00 |           |          |          |
30| UP      | 0x01 | ESC [ A   | ESC [ A  | ESC [ A  |
31| DOWN    | 0x02 | ESC [ B   | ESC [ B  | ESC [ B  |
32| RIGHT   | 0x03 | ESC [ C   | ESC [ C  | ESC [ C  |
33| LEFT    | 0x04 | ESC [ D   | ESC [ D  | ESC [ D  |
34| HOME    | 0x05 | ESC [ H   | ESC h    | ESC [ H  |
35| END     | 0x06 | ESC [ F   | ESC k    | ESC [ K  |
36| INSERT  | 0x07 | ESC [ @   | ESC +    | ESC [ @  |
37|         |      | ESC [ L   |          | ESC [ L  |
38| DELETE  | 0x08 | ESC [ X   | ESC -    | ESC [ P  |
39| PG UP   | 0x09 | ESC [ I   | ESC ?    | ESC [ V  |
40|         |      |           |          | ESC [ ?  |
41| PG DOWN | 0x0A | ESC [ G   | ESC /    | ESC [ U  |
42|         |      |           |          | ESC [ /  |
43| F1      | 0x0B | ESC [ M   | ESC 1    | ESC O P  |
44| F2      | 0x0C | ESC [ N   | ESC 2    | ESC O Q  |
45| F3      | 0x0D | ESC [ O   | ESC 3    | ESC O w  |
46| F4      | 0x0E | ESC [ P   | ESC 4    | ESC O x  |
47| F5      | 0x0F | ESC [ Q   | ESC 5    | ESC O t  |
48| F6      | 0x10 | ESC [ R   | ESC 6    | ESC O u  |
49| F7      | 0x11 | ESC [ S   | ESC 7    | ESC O q  |
50| F8      | 0x12 | ESC [ T   | ESC 8    | ESC O r  |
51| F9      | 0x13 | ESC [ U   | ESC 9    | ESC O p  |
52| F10     | 0x14 | ESC [ V   | ESC 0    | ESC O M  |
53| Escape  | 0x17 | ESC       | ESC      | ESC      |
54| F11     | 0x15 |           | ESC !    |          |
55| F12     | 0x16 |           | ESC @    |          |
56+=========+======+===========+==========+==========+
57
58*/
59
60#include <PiDxe.h>
61#include <Library/UefiLib.h>
62#include <Library/UefiBootServicesTableLib.h>
63#include <Library/BaseLib.h>
64#include <Library/MemoryAllocationLib.h>
65#include <Library/DebugLib.h>
66#include <Library/SerialPortLib.h>
67#include <Library/PcdLib.h>
68
69#include <Protocol/SerialIo.h>
70#include <Protocol/SimpleTextIn.h>
71#include <Protocol/SimpleTextOut.h>
72#include <Protocol/DevicePath.h>
73
74
75#define MODE0_COLUMN_COUNT        80
76#define MODE0_ROW_COUNT           25
77
78
79EFI_STATUS
80EFIAPI
81TextInReset(
82  IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
83  IN BOOLEAN                        ExtendedVerification
84  );
85
86
87EFI_STATUS
88EFIAPI
89ReadKeyStroke(
90  IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
91  OUT EFI_INPUT_KEY                 *Key
92  );
93
94
95EFI_STATUS
96EFIAPI
97TextOutReset(
98  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
99  IN BOOLEAN                          ExtendedVerification
100  );
101
102CHAR8 *
103EFIAPI
104SafeUnicodeStrToAsciiStr (
105  IN      CONST CHAR16                *Source,
106  OUT     CHAR8                       *Destination
107  );
108
109EFI_STATUS
110EFIAPI
111OutputString (
112  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
113  IN CHAR16                           *String
114  );
115
116
117EFI_STATUS
118EFIAPI
119TestString (
120  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
121  IN CHAR16                           *String
122  );
123
124
125EFI_STATUS
126EFIAPI
127QueryMode (
128  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
129  IN UINTN                            ModeNumber,
130  OUT UINTN                           *Columns,
131  OUT UINTN                           *Rows
132  );
133
134
135EFI_STATUS
136EFIAPI
137SetMode(
138  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
139  IN UINTN                            ModeNumber
140  );
141
142
143EFI_STATUS
144EFIAPI
145SetAttribute(
146  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
147  IN UINTN                            Attribute
148  );
149
150
151EFI_STATUS
152EFIAPI
153ClearScreen (
154  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This
155  );
156
157
158EFI_STATUS
159EFIAPI
160SetCursorPosition (
161  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
162  IN UINTN                            Column,
163  IN UINTN                            Row
164  );
165
166
167EFI_STATUS
168EFIAPI
169EnableCursor (
170  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
171  IN BOOLEAN                          Enable
172  );
173
174
175 EFI_SIMPLE_TEXT_INPUT_PROTOCOL mSimpleTextIn = {
176  TextInReset,
177  ReadKeyStroke,
178  NULL
179};
180
181 EFI_SIMPLE_TEXT_OUTPUT_MODE mSimpleTextOutMode = {
182  1,
183  0,
184  EFI_TEXT_ATTR( EFI_LIGHTGRAY, EFI_BLACK ),
185  0,
186  0,
187  TRUE
188};
189
190EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL mSimpleTextOut = {
191  TextOutReset,
192  OutputString,
193  TestString,
194  QueryMode,
195  SetMode,
196  SetAttribute,
197  ClearScreen,
198  SetCursorPosition,
199  EnableCursor,
200  &mSimpleTextOutMode
201};
202
203EFI_HANDLE           mInstallHandle = NULL;
204
205typedef struct {
206  VENDOR_DEVICE_PATH        Guid;
207  UART_DEVICE_PATH          Uart;
208  EFI_DEVICE_PATH_PROTOCOL  End;
209} SIMPLE_TEXT_OUT_DEVICE_PATH;
210
211SIMPLE_TEXT_OUT_DEVICE_PATH mDevicePath = {
212  {
213    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0} },
214    EFI_CALLER_ID_GUID
215  },
216  {
217    { MESSAGING_DEVICE_PATH, MSG_UART_DP, { sizeof (UART_DEVICE_PATH), 0} },
218    0,        // Reserved
219    FixedPcdGet64 (PcdUartDefaultBaudRate),   // BaudRate
220    FixedPcdGet8 (PcdUartDefaultDataBits),    // DataBits
221    FixedPcdGet8 (PcdUartDefaultParity),      // Parity (N)
222    FixedPcdGet8 (PcdUartDefaultStopBits)     // StopBits
223  },
224  { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0} }
225};
226
227
228
229
230BOOLEAN
231TextOutIsValidAscii (
232  IN CHAR16       Ascii
233  )
234{
235  //
236  // valid ASCII code lies in the extent of 0x20 - 0x7F
237  //
238  if ((Ascii >= 0x20) && (Ascii <= 0x7F)) {
239    return TRUE;
240  }
241
242  return FALSE;
243}
244
245
246BOOLEAN
247TextOutIsValidEfiCntlChar (
248  IN CHAR16       Char
249  )
250{
251  //
252  // only support four control characters.
253  //
254  if (Char == CHAR_NULL ||
255      Char == CHAR_BACKSPACE ||
256      Char == CHAR_LINEFEED ||
257      Char == CHAR_CARRIAGE_RETURN ||
258      Char == CHAR_TAB ) {
259    return TRUE;
260  }
261
262  return FALSE;
263}
264
265
266VOID
267EFIAPI
268WaitForKeyEvent (
269  IN EFI_EVENT          Event,
270  IN VOID               *Context
271  )
272{
273  if (SerialPortPoll ())  {
274    gBS->SignalEvent (Event);
275  }
276}
277
278
279EFI_STATUS
280EFIAPI
281TextInReset (
282  IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
283  IN BOOLEAN                        ExtendedVerification
284  )
285{
286  return EFI_SUCCESS;
287}
288
289
290EFI_STATUS
291EFIAPI
292ReadKeyStroke (
293  IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
294  OUT EFI_INPUT_KEY                 *Key
295  )
296{
297  CHAR8             Char;
298
299  if (!SerialPortPoll ()) {
300    return EFI_NOT_READY;
301  }
302
303  SerialPortRead ((UINT8 *)&Char, 1);
304
305  //
306  // Check for ESC sequence. This code is not techincally correct VT100 code.
307  // An illegal ESC sequence represents an ESC and the characters that follow.
308  // This code will eat one or two chars after an escape. This is done to
309  // prevent some complex FIFOing of the data. It is good enough to get
310  // the arrow and delete keys working
311  //
312  Key->UnicodeChar = 0;
313  Key->ScanCode    = SCAN_NULL;
314  if (Char == 0x1b) {
315    SerialPortRead ((UINT8 *)&Char, 1);
316    if (Char == '[') {
317      SerialPortRead ((UINT8 *)&Char, 1);
318      switch (Char) {
319      case 'A':
320        Key->ScanCode = SCAN_UP;
321        break;
322      case 'B':
323        Key->ScanCode = SCAN_DOWN;
324        break;
325      case 'C':
326        Key->ScanCode = SCAN_RIGHT;
327        break;
328      case 'D':
329        Key->ScanCode = SCAN_LEFT;
330        break;
331      case 'H':
332        Key->ScanCode = SCAN_HOME;
333        break;
334      case 'K':
335      case 'F': // PC ANSI
336        Key->ScanCode = SCAN_END;
337        break;
338      case '@':
339      case 'L':
340        Key->ScanCode = SCAN_INSERT;
341        break;
342      case 'P':
343      case 'X': // PC ANSI
344        Key->ScanCode = SCAN_DELETE;
345        break;
346      case 'U':
347      case '/':
348      case 'G': // PC ANSI
349        Key->ScanCode = SCAN_PAGE_DOWN;
350        break;
351      case 'V':
352      case '?':
353      case 'I': // PC ANSI
354        Key->ScanCode = SCAN_PAGE_UP;
355        break;
356
357      // PCANSI that does not conflict with VT100
358      case 'M':
359        Key->ScanCode = SCAN_F1;
360        break;
361      case 'N':
362        Key->ScanCode = SCAN_F2;
363        break;
364      case 'O':
365        Key->ScanCode = SCAN_F3;
366        break;
367      case 'Q':
368        Key->ScanCode = SCAN_F5;
369        break;
370      case 'R':
371        Key->ScanCode = SCAN_F6;
372        break;
373      case 'S':
374        Key->ScanCode = SCAN_F7;
375        break;
376      case 'T':
377        Key->ScanCode = SCAN_F8;
378        break;
379
380      default:
381        Key->UnicodeChar = Char;
382        break;
383      }
384    } else if (Char == '0') {
385      SerialPortRead ((UINT8 *)&Char, 1);
386      switch (Char) {
387      case 'P':
388        Key->ScanCode = SCAN_F1;
389        break;
390      case 'Q':
391        Key->ScanCode = SCAN_F2;
392        break;
393      case 'w':
394        Key->ScanCode = SCAN_F3;
395        break;
396      case 'x':
397        Key->ScanCode = SCAN_F4;
398        break;
399      case 't':
400        Key->ScanCode = SCAN_F5;
401        break;
402      case 'u':
403        Key->ScanCode = SCAN_F6;
404        break;
405      case 'q':
406        Key->ScanCode = SCAN_F7;
407        break;
408      case 'r':
409        Key->ScanCode = SCAN_F8;
410        break;
411      case 'p':
412        Key->ScanCode = SCAN_F9;
413        break;
414      case 'm':
415        Key->ScanCode = SCAN_F10;
416        break;
417      default :
418        break;
419      }
420    }
421  } else if (Char < ' ') {
422    if ((Char == CHAR_BACKSPACE) ||
423        (Char == CHAR_TAB)       ||
424        (Char == CHAR_LINEFEED)  ||
425        (Char == CHAR_CARRIAGE_RETURN)) {
426      // Only let through EFI required control characters
427      Key->UnicodeChar = (CHAR16)Char;
428    }
429  } else if (Char == 0x7f) {
430    Key->ScanCode = SCAN_DELETE;
431  } else {
432    Key->UnicodeChar = (CHAR16)Char;
433  }
434
435  return EFI_SUCCESS;
436}
437
438
439EFI_STATUS
440EFIAPI
441TextOutReset (
442  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
443  IN BOOLEAN                          ExtendedVerification
444  )
445{
446  EFI_STATUS            Status;
447
448  This->SetAttribute(
449        This,
450        EFI_TEXT_ATTR(This->Mode->Attribute & 0x0F, EFI_BACKGROUND_BLACK)
451        );
452
453  Status = This->SetMode (This, 0);
454
455  return Status;
456}
457
458CHAR8 *
459EFIAPI
460SafeUnicodeStrToAsciiStr (
461  IN      CONST CHAR16                *Source,
462  OUT     CHAR8                       *Destination
463  )
464{
465  CHAR8                               *ReturnValue;
466
467  ASSERT (Destination != NULL);
468
469  //
470  // ASSERT if Source is long than PcdMaximumUnicodeStringLength.
471  // Length tests are performed inside StrLen().
472  //
473  ASSERT (StrSize (Source) != 0);
474
475  //
476  // Source and Destination should not overlap
477  //
478  ASSERT ((UINTN) ((CHAR16 *) Destination -  Source) > StrLen (Source));
479  ASSERT ((UINTN) ((CHAR8 *) Source - Destination) > StrLen (Source));
480
481
482  ReturnValue = Destination;
483  while (*Source != '\0') {
484    //
485    // If any non-ascii characters in Source then replace it with '?'.
486    //
487    if (*Source < 0x80) {
488      *Destination = (CHAR8) *Source;
489    } else {
490      *Destination = '?';
491
492      //Surrogate pair check.
493      if ((*Source >= 0xD800) && (*Source <= 0xDFFF)) {
494        Source++;
495      }
496    }
497
498    Destination++;
499    Source++;
500  }
501
502  *Destination = '\0';
503
504  //
505  // ASSERT Original Destination is less long than PcdMaximumAsciiStringLength.
506  // Length tests are performed inside AsciiStrLen().
507  //
508  ASSERT (AsciiStrSize (ReturnValue) != 0);
509
510  return ReturnValue;
511}
512
513EFI_STATUS
514EFIAPI
515OutputString (
516  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
517  IN CHAR16                           *String
518  )
519{
520  UINTN                       Size;
521  CHAR8*                      OutputString;
522  EFI_STATUS                  Status;
523  EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
524  UINTN                       MaxColumn;
525  UINTN                       MaxRow;
526
527  Size = StrLen(String) + 1;
528  OutputString = AllocatePool(Size);
529
530  //If there is any non-ascii characters in String buffer then replace it with '?'
531  //Eventually, UnicodeStrToAsciiStr API should be fixed.
532  SafeUnicodeStrToAsciiStr(String, OutputString);
533  SerialPortWrite ((UINT8 *)OutputString, Size - 1);
534
535  //
536  // Parse each character of the string to output
537  // to update the cursor position information
538  //
539  Mode = This->Mode;
540
541  Status = This->QueryMode (
542                   This,
543                   Mode->Mode,
544                   &MaxColumn,
545                   &MaxRow
546                   );
547  if (EFI_ERROR (Status)) {
548    return Status;
549  }
550
551  for (; *String != CHAR_NULL; String++) {
552
553    switch (*String) {
554    case CHAR_BACKSPACE:
555      if (Mode->CursorColumn > 0) {
556        Mode->CursorColumn--;
557      }
558      break;
559
560    case CHAR_LINEFEED:
561      if (Mode->CursorRow < (INT32) (MaxRow - 1)) {
562        Mode->CursorRow++;
563      }
564      break;
565
566    case CHAR_CARRIAGE_RETURN:
567      Mode->CursorColumn = 0;
568      break;
569
570    default:
571      if (Mode->CursorColumn >= (INT32) (MaxColumn - 1)) {
572        // Move the cursor as if we print CHAR_CARRIAGE_RETURN & CHAR_LINE_FEED
573        // CHAR_LINEFEED
574        if (Mode->CursorRow < (INT32) (MaxRow - 1)) {
575          Mode->CursorRow++;
576        }
577        // CHAR_CARIAGE_RETURN
578        Mode->CursorColumn = 0;
579      } else {
580        Mode->CursorColumn++;
581      }
582      break;
583    }
584  }
585
586  FreePool(OutputString);
587
588  return EFI_SUCCESS;
589}
590
591
592EFI_STATUS
593EFIAPI
594TestString (
595  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
596  IN CHAR16                           *String
597  )
598{
599  CHAR8           Character;
600
601  for ( ; *String != CHAR_NULL; String++) {
602    Character = (CHAR8)*String;
603    if (!(TextOutIsValidAscii (Character) || TextOutIsValidEfiCntlChar (Character))) {
604      return EFI_UNSUPPORTED;
605    }
606  }
607
608  return EFI_SUCCESS;
609}
610
611
612EFI_STATUS
613EFIAPI
614QueryMode (
615  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
616  IN UINTN                            ModeNumber,
617  OUT UINTN                          *Columns,
618  OUT UINTN                          *Rows
619  )
620{
621  if (This->Mode->MaxMode > 1) {
622    return EFI_DEVICE_ERROR;
623  }
624
625  if (ModeNumber == 0) {
626    *Columns  = MODE0_COLUMN_COUNT;
627    *Rows     = MODE0_ROW_COUNT;
628    return EFI_SUCCESS;
629  }
630
631  return EFI_UNSUPPORTED;
632}
633
634
635EFI_STATUS
636EFIAPI
637SetMode (
638  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *This,
639  IN UINTN                              ModeNumber
640  )
641{
642  if (ModeNumber != 0) {
643    return EFI_UNSUPPORTED;
644  }
645
646  This->Mode->Mode = 0;
647  This->ClearScreen (This);
648  return EFI_SUCCESS;
649}
650
651
652EFI_STATUS
653EFIAPI
654SetAttribute(
655  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *This,
656  IN UINTN                              Attribute
657  )
658{
659  This->Mode->Attribute = (INT32)Attribute;
660  return EFI_SUCCESS;
661}
662
663
664EFI_STATUS
665EFIAPI
666ClearScreen (
667  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *This
668  )
669{
670  EFI_STATUS    Status;
671
672  Status = This->SetCursorPosition (This, 0, 0);
673  return Status;
674}
675
676
677EFI_STATUS
678EFIAPI
679SetCursorPosition (
680  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL    *This,
681  IN UINTN                              Column,
682  IN UINTN                              Row
683  )
684{
685  EFI_SIMPLE_TEXT_OUTPUT_MODE       *Mode;
686  EFI_STATUS                        Status;
687  UINTN                             MaxColumn;
688  UINTN                             MaxRow;
689
690  Mode = This->Mode;
691
692  Status = This->QueryMode(
693                  This,
694                  Mode->Mode,
695                  &MaxColumn,
696                  &MaxRow
697                  );
698  if (EFI_ERROR(Status)) {
699    return EFI_UNSUPPORTED;
700  }
701
702  if ((Column >= MaxColumn) || (Row >= MaxRow)) {
703    return EFI_UNSUPPORTED;
704  }
705
706  Mode->CursorColumn = (INT32)Column;
707  Mode->CursorRow = (INT32)Row;
708
709  return EFI_SUCCESS;
710}
711
712
713EFI_STATUS
714EFIAPI
715EnableCursor (
716  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *This,
717  IN BOOLEAN                          Enable
718  )
719{
720  if (!Enable) {
721    return EFI_UNSUPPORTED;
722  }
723
724  return EFI_SUCCESS;
725}
726
727
728EFI_STATUS
729EFIAPI
730SimpleTextInOutEntryPoint (
731  IN EFI_HANDLE         ImageHandle,
732  IN EFI_SYSTEM_TABLE   *SystemTable
733  )
734{
735  EFI_STATUS            Status;
736
737  Status = gBS->CreateEvent (
738                  EVT_NOTIFY_WAIT,
739                  TPL_NOTIFY,
740                  WaitForKeyEvent,
741                  NULL,
742                  &mSimpleTextIn.WaitForKey
743                  );
744  ASSERT_EFI_ERROR (Status);
745
746  Status = gBS->InstallMultipleProtocolInterfaces(
747                  &mInstallHandle,
748                  &gEfiSimpleTextInProtocolGuid,   &mSimpleTextIn,
749                  &gEfiSimpleTextOutProtocolGuid,  &mSimpleTextOut,
750                  &gEfiDevicePathProtocolGuid,     &mDevicePath,
751                  NULL
752                  );
753  if (!EFI_ERROR (Status)) {
754    gST->ConOut = &mSimpleTextOut;
755    gST->ConIn = &mSimpleTextIn;
756  }
757
758  return Status;
759}
760