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 79 EFI_STATUS 80 EFIAPI 81 TextInReset( 82 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, 83 IN BOOLEAN ExtendedVerification 84 ); 85 86 87 EFI_STATUS 88 EFIAPI 89 ReadKeyStroke( 90 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, 91 OUT EFI_INPUT_KEY *Key 92 ); 93 94 95 EFI_STATUS 96 EFIAPI 97 TextOutReset( 98 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 99 IN BOOLEAN ExtendedVerification 100 ); 101 102 CHAR8 * 103 EFIAPI 104 SafeUnicodeStrToAsciiStr ( 105 IN CONST CHAR16 *Source, 106 OUT CHAR8 *Destination 107 ); 108 109 EFI_STATUS 110 EFIAPI 111 OutputString ( 112 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 113 IN CHAR16 *String 114 ); 115 116 117 EFI_STATUS 118 EFIAPI 119 TestString ( 120 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 121 IN CHAR16 *String 122 ); 123 124 125 EFI_STATUS 126 EFIAPI 127 QueryMode ( 128 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 129 IN UINTN ModeNumber, 130 OUT UINTN *Columns, 131 OUT UINTN *Rows 132 ); 133 134 135 EFI_STATUS 136 EFIAPI 137 SetMode( 138 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 139 IN UINTN ModeNumber 140 ); 141 142 143 EFI_STATUS 144 EFIAPI 145 SetAttribute( 146 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 147 IN UINTN Attribute 148 ); 149 150 151 EFI_STATUS 152 EFIAPI 153 ClearScreen ( 154 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 155 ); 156 157 158 EFI_STATUS 159 EFIAPI 160 SetCursorPosition ( 161 IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, 162 IN UINTN Column, 163 IN UINTN Row 164 ); 165 166 167 EFI_STATUS 168 EFIAPI 169 EnableCursor ( 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 190 EFI_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 203 EFI_HANDLE mInstallHandle = NULL; 204 205 typedef struct { 206 VENDOR_DEVICE_PATH Guid; 207 UART_DEVICE_PATH Uart; 208 EFI_DEVICE_PATH_PROTOCOL End; 209 } SIMPLE_TEXT_OUT_DEVICE_PATH; 210 211 SIMPLE_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 230 BOOLEAN 231 TextOutIsValidAscii ( 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 246 BOOLEAN 247 TextOutIsValidEfiCntlChar ( 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 266 VOID 267 EFIAPI 268 WaitForKeyEvent ( 269 IN EFI_EVENT Event, 270 IN VOID *Context 271 ) 272 { 273 if (SerialPortPoll ()) { 274 gBS->SignalEvent (Event); 275 } 276 } 277 278 279 EFI_STATUS 280 EFIAPI 281 TextInReset ( 282 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, 283 IN BOOLEAN ExtendedVerification 284 ) 285 { 286 return EFI_SUCCESS; 287 } 288 289 290 EFI_STATUS 291 EFIAPI 292 ReadKeyStroke ( 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 439 EFI_STATUS 440 EFIAPI 441 TextOutReset ( 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 458 CHAR8 * 459 EFIAPI 460 SafeUnicodeStrToAsciiStr ( 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 513 EFI_STATUS 514 EFIAPI 515 OutputString ( 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 592 EFI_STATUS 593 EFIAPI 594 TestString ( 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 612 EFI_STATUS 613 EFIAPI 614 QueryMode ( 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 635 EFI_STATUS 636 EFIAPI 637 SetMode ( 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 652 EFI_STATUS 653 EFIAPI 654 SetAttribute( 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 664 EFI_STATUS 665 EFIAPI 666 ClearScreen ( 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 677 EFI_STATUS 678 EFIAPI 679 SetCursorPosition ( 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 713 EFI_STATUS 714 EFIAPI 715 EnableCursor ( 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 728 EFI_STATUS 729 EFIAPI 730 SimpleTextInOutEntryPoint ( 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