1 /* 2 * COPYRIGHT: See COPYING.ARM in the top level directory 3 * PROJECT: ReactOS UEFI Boot Library 4 * FILE: boot/environ/lib/io/display/efi/textcons.c 5 * PURPOSE: Boot Library EFI Text Console Routines 6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include "bl.h" 12 13 /* DATA VARIABLES ************************************************************/ 14 15 /* FUNCTIONS *****************************************************************/ 16 17 BL_COLOR 18 ConsoleEfiTextGetColorForeground ( 19 _In_ UINT32 Attributes 20 ) 21 { 22 /* Read the foreground color attribute and convert to CGA color index */ 23 switch (Attributes & 0x0F) 24 { 25 case EFI_BLACK: 26 return Black; 27 case EFI_BLUE: 28 return Blue; 29 case EFI_GREEN: 30 return Green; 31 case EFI_RED: 32 return Red; 33 case EFI_CYAN: 34 return Cyan; 35 case EFI_MAGENTA: 36 return Magenta; 37 case EFI_BROWN: 38 return Brown; 39 case EFI_LIGHTGRAY: 40 return LtGray; 41 case EFI_DARKGRAY: 42 return Gray; 43 case EFI_LIGHTBLUE: 44 return LtBlue; 45 case EFI_LIGHTGREEN: 46 return LtGreen; 47 case EFI_LIGHTCYAN: 48 return LtCyan; 49 case EFI_LIGHTRED: 50 return LtRed; 51 case EFI_LIGHTMAGENTA: 52 return LtMagenta; 53 case EFI_YELLOW: 54 return Yellow; 55 case EFI_WHITE: 56 default: 57 return White; 58 } 59 } 60 61 BL_COLOR 62 ConsoleEfiTextGetColorBackground ( 63 _In_ UINT32 Attributes 64 ) 65 { 66 /* Read the background color attribute and convert to CGA color index */ 67 switch (Attributes & 0xF0) 68 { 69 case EFI_BACKGROUND_MAGENTA: 70 return Magenta; 71 case EFI_BACKGROUND_BROWN: 72 return Brown; 73 case EFI_BACKGROUND_LIGHTGRAY: 74 return White; 75 case EFI_BACKGROUND_BLACK: 76 default: 77 return Black; 78 case EFI_BACKGROUND_RED: 79 return Red; 80 case EFI_BACKGROUND_GREEN: 81 return Green; 82 case EFI_BACKGROUND_CYAN: 83 return Cyan; 84 case EFI_BACKGROUND_BLUE: 85 return Blue; 86 } 87 } 88 89 ULONG 90 ConsoleEfiTextGetEfiColorBackground ( 91 _In_ BL_COLOR Color 92 ) 93 { 94 /* Convert the CGA color index into an EFI background attribute */ 95 switch (Color) 96 { 97 case Blue: 98 case LtBlue: 99 return EFI_BACKGROUND_BLUE; 100 case Green: 101 case LtGreen: 102 return EFI_BACKGROUND_GREEN; 103 case Cyan: 104 case LtCyan: 105 return EFI_BACKGROUND_CYAN; 106 case Red: 107 case LtRed: 108 return EFI_BACKGROUND_RED; 109 case Magenta: 110 case LtMagenta: 111 return EFI_BACKGROUND_MAGENTA; 112 case Brown: 113 case Yellow: 114 return EFI_BACKGROUND_BROWN; 115 case LtGray: 116 case White: 117 return EFI_BACKGROUND_LIGHTGRAY; 118 case Black: 119 case Gray: 120 default: 121 return EFI_BACKGROUND_BLACK; 122 } 123 } 124 125 ULONG 126 ConsoleEfiTextGetEfiColorForeground ( 127 _In_ BL_COLOR Color 128 ) 129 { 130 /* Convert the CGA color index into an EFI foreground attribute */ 131 switch (Color) 132 { 133 case Black: 134 return EFI_BLACK; 135 case Blue: 136 return EFI_BLUE; 137 case Green: 138 return EFI_GREEN; 139 case Cyan: 140 return EFI_CYAN; 141 case Red: 142 return EFI_RED; 143 case Magenta: 144 return EFI_MAGENTA; 145 case Brown: 146 return EFI_BROWN; 147 case LtGray: 148 return EFI_LIGHTGRAY; 149 case Gray: 150 return EFI_DARKGRAY; 151 case LtBlue: 152 return EFI_LIGHTBLUE; 153 case LtGreen: 154 return EFI_LIGHTGREEN; 155 case LtCyan: 156 return EFI_LIGHTCYAN; 157 case LtRed: 158 return EFI_LIGHTRED; 159 case LtMagenta: 160 return EFI_LIGHTMAGENTA; 161 case Yellow: 162 return EFI_YELLOW; 163 case White: 164 default: 165 return EFI_WHITE; 166 } 167 } 168 169 ULONG 170 ConsoleEfiTextGetAttribute ( 171 BL_COLOR BgColor, 172 BL_COLOR FgColor 173 ) 174 { 175 /* Convert each part and OR into a single attribute */ 176 return ConsoleEfiTextGetEfiColorBackground(BgColor) | 177 ConsoleEfiTextGetEfiColorForeground(FgColor); 178 } 179 180 VOID 181 ConsoleEfiTextGetStateFromMode ( 182 _In_ EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode, 183 _Out_ PBL_DISPLAY_STATE State 184 ) 185 { 186 ULONG TextWidth, TextHeight; 187 188 /* Get all the EFI data and convert it into our own structure */ 189 BlDisplayGetTextCellResolution(&TextWidth, &TextHeight); 190 State->FgColor = ConsoleEfiTextGetColorForeground(Mode->Attribute); 191 State->BgColor = ConsoleEfiTextGetColorBackground(Mode->Attribute); 192 State->XPos = Mode->CursorColumn * TextWidth; 193 State->YPos = Mode->CursorRow * TextHeight; 194 State->CursorVisible = Mode->CursorVisible != FALSE; 195 } 196 197 NTSTATUS 198 ConsoleFirmwareTextSetState ( 199 _In_ PBL_TEXT_CONSOLE TextConsole, 200 _In_ UCHAR Mask, 201 _In_ PBL_DISPLAY_STATE State 202 ) 203 { 204 NTSTATUS Status; 205 ULONG FgColor, BgColor, Attribute, XPos, YPos, TextHeight, TextWidth; 206 BOOLEAN Visible; 207 208 /* Check if foreground state is being set */ 209 if (Mask & 1) 210 { 211 /* Check if there's a difference from current */ 212 FgColor = State->FgColor; 213 if (TextConsole->State.FgColor != FgColor) 214 { 215 /* Ignore invalid color */ 216 if (FgColor > White) 217 { 218 return STATUS_INVALID_PARAMETER; 219 } 220 221 /* Convert from NT/CGA format to EFI, and then set the attribute */ 222 Attribute = ConsoleEfiTextGetAttribute(TextConsole->State.BgColor, 223 FgColor); 224 Status = EfiConOutSetAttribute(TextConsole->Protocol, Attribute); 225 if (!NT_SUCCESS(Status)) 226 { 227 return Status; 228 } 229 230 /* Update cached state */ 231 TextConsole->State.FgColor = FgColor; 232 } 233 } 234 235 /* Check if background state is being set */ 236 if (Mask & 2) 237 { 238 /* Check if there's a difference from current */ 239 BgColor = State->BgColor; 240 if (TextConsole->State.BgColor != BgColor) 241 { 242 /* Ignore invalid color */ 243 if (BgColor > White) 244 { 245 return STATUS_INVALID_PARAMETER; 246 } 247 248 /* Convert from NT/CGA format to EFI, and then set the attribute */ 249 Attribute = ConsoleEfiTextGetAttribute(BgColor, 250 TextConsole->State.FgColor); 251 Status = EfiConOutSetAttribute(TextConsole->Protocol, Attribute); 252 253 if (!NT_SUCCESS(Status)) 254 { 255 return Status; 256 } 257 258 /* Update cached state */ 259 TextConsole->State.BgColor = BgColor; 260 } 261 } 262 263 /* Check if position state is being set */ 264 if (Mask & 4) 265 { 266 /* Check if there's a difference from current */ 267 XPos = State->XPos; 268 YPos = State->YPos; 269 if ((TextConsole->State.XPos != XPos) || 270 (TextConsole->State.YPos != YPos)) 271 { 272 /* Set the new cursor position */ 273 BlDisplayGetTextCellResolution(&TextWidth, &TextHeight); 274 Status = EfiConOutSetCursorPosition(TextConsole->Protocol, 275 XPos/ TextWidth, 276 YPos / TextHeight); 277 if (!NT_SUCCESS(Status)) 278 { 279 return Status; 280 } 281 282 /* Update cached state */ 283 TextConsole->State.XPos = XPos; 284 TextConsole->State.YPos = YPos; 285 } 286 } 287 288 /* Check if cursor state is being set */ 289 if (Mask & 8) 290 { 291 /* Check if there's a difference from current */ 292 Visible = State->CursorVisible; 293 if (TextConsole->State.CursorVisible != Visible) 294 { 295 /* Ignore invalid state */ 296 if (Visible >= 3) 297 { 298 return STATUS_INVALID_PARAMETER; 299 } 300 301 /* Set the new cursor state */ 302 Status = EfiConOutEnableCursor(TextConsole->Protocol, Visible); 303 if (!NT_SUCCESS(Status)) 304 { 305 return Status; 306 } 307 308 /* Update cached status */ 309 TextConsole->State.CursorVisible = Visible; 310 } 311 } 312 313 /* Return success */ 314 return STATUS_SUCCESS; 315 } 316 317 NTSTATUS 318 ConsoleEfiTextFindModeFromAllowed ( 319 _In_ SIMPLE_TEXT_OUTPUT_INTERFACE *TextProtocol, 320 _In_ PBL_DISPLAY_MODE SupportedModes, 321 _In_ ULONG MaxIndex, 322 _Out_ PULONG SupportedMode 323 ) 324 { 325 EFI_SIMPLE_TEXT_OUTPUT_MODE ModeInfo; 326 ULONG MaxMode, MaxQueriedMode, Mode, i, MatchingMode; 327 UINTN HRes, VRes; 328 ULONGLONG ModeListSize; 329 PBL_DISPLAY_MODE ModeEntry, ModeList, SupportedModeEntry; 330 NTSTATUS Status; 331 332 /* Read information on the current mode */ 333 EfiConOutReadCurrentMode(TextProtocol, &ModeInfo); 334 335 /* Figure out the max mode, and how many modes we'll have to read */ 336 MaxMode = ModeInfo.MaxMode; 337 ModeListSize = sizeof(*ModeEntry) * ModeInfo.MaxMode; 338 if (ModeListSize > MAXULONG) 339 { 340 return STATUS_INTEGER_OVERFLOW; 341 } 342 343 /* Allocate a list for all the supported EFI modes */ 344 ModeList = BlMmAllocateHeap(ModeListSize); 345 if (!ModeList) 346 { 347 return STATUS_INSUFFICIENT_RESOURCES; 348 } 349 350 /* Scan all the EFI modes */ 351 EfiPrintf(L"Scanning through %d modes\r\n", MaxMode); 352 for (MaxQueriedMode = 0, Mode = 0; Mode < MaxMode; Mode++) 353 { 354 /* Query information on this mode */ 355 ModeEntry = &ModeList[MaxQueriedMode]; 356 if (NT_SUCCESS(EfiConOutQueryMode(TextProtocol, 357 Mode, 358 &HRes, 359 &VRes))) 360 { 361 /* This mode was successfully queried. Save the data */ 362 EfiPrintf(L"EFI Firmware Supported Mode %d is H: %d V: %d\r\n", Mode, HRes, VRes); 363 ModeEntry->HRes = HRes; 364 ModeEntry->VRes = VRes; 365 ModeEntry->HRes2 = HRes; 366 MaxQueriedMode = Mode + 1; 367 } 368 } 369 370 /* Loop all the supported mode entries */ 371 for (i = 0; i < MaxIndex; i++) 372 { 373 /* Loop all the UEFI queried modes */ 374 SupportedModeEntry = &SupportedModes[i]; 375 for (MatchingMode = 0; MatchingMode < MaxQueriedMode; MatchingMode++) 376 { 377 /* Check if the UEFI mode is compatible with our supported mode */ 378 ModeEntry = &ModeList[MatchingMode]; 379 EfiPrintf(L"H1: %d V1: %d - H2: %d - V2: %d\r\n", ModeEntry->HRes, ModeEntry->VRes, SupportedModeEntry->HRes, SupportedModeEntry->VRes); 380 if ((ModeEntry->HRes == SupportedModeEntry->HRes) && 381 (ModeEntry->VRes == SupportedModeEntry->VRes)) 382 { 383 /* Yep -- free the mode list and return this mode */ 384 BlMmFreeHeap(ModeList); 385 *SupportedMode = MatchingMode; 386 return STATUS_SUCCESS; 387 } 388 } 389 } 390 391 /* We can't do anything -- there are no matching modes */ 392 Status = STATUS_UNSUCCESSFUL; 393 BlMmFreeHeap(ModeList); 394 return Status; 395 } 396 397 VOID 398 ConsoleFirmwareTextClose ( 399 _In_ PBL_TEXT_CONSOLE TextConsole 400 ) 401 { 402 ULONG Mode; 403 BL_DISPLAY_STATE DisplayState; 404 405 /* Read the original mode, and see if it's different than the one now */ 406 Mode = TextConsole->OldMode.Mode; 407 if (Mode != TextConsole->Mode) 408 { 409 /* Restore to the original mode */ 410 EfiConOutSetMode(TextConsole->Protocol, Mode); 411 } 412 413 /* Read the EFI settings for the original mode */ 414 ConsoleEfiTextGetStateFromMode(&TextConsole->OldMode, &DisplayState); 415 416 /* Set the original settings */ 417 ConsoleFirmwareTextSetState(TextConsole, 0xF, &DisplayState); 418 } 419 420 NTSTATUS 421 ConsoleFirmwareTextOpen ( 422 _In_ PBL_TEXT_CONSOLE TextConsole 423 ) 424 { 425 BL_DISPLAY_MODE DisplayMode; 426 EFI_SIMPLE_TEXT_OUTPUT_MODE CurrentMode, NewMode; 427 UINTN HRes, VRes; 428 ULONG Mode; 429 NTSTATUS Status; 430 431 /* Read the current mode and its settings */ 432 EfiConOutReadCurrentMode(EfiConOut, &CurrentMode); 433 Status = EfiConOutQueryMode(EfiConOut, CurrentMode.Mode, &HRes, &VRes); 434 if (!NT_SUCCESS(Status)) 435 { 436 return Status; 437 } 438 439 /* Save the current mode and its settings */ 440 NewMode = CurrentMode; 441 DisplayMode.VRes = VRes; 442 DisplayMode.HRes = HRes; 443 DisplayMode.HRes2 = HRes; 444 445 /* Check if the current mode is compatible with one of our modes */ 446 if (!ConsolepFindResolution(&DisplayMode, ConsoleTextResolutionList, 1)) 447 { 448 /* It isn't -- find a matching EFI mode for what we need */ 449 EfiPrintf(L"In incorrect mode, scanning for right one\r\n"); 450 Status = ConsoleEfiTextFindModeFromAllowed(EfiConOut, 451 ConsoleTextResolutionList, 452 1, 453 &Mode); 454 if (!NT_SUCCESS(Status)) 455 { 456 EfiPrintf(L"Failed to find mode: %lx\r\n", Status); 457 return Status; 458 } 459 460 /* Set the new EFI mode */ 461 EfiPrintf(L"Setting new mode: %d\r\n", Mode); 462 Status = EfiConOutSetMode(EfiConOut, Mode); 463 if (!NT_SUCCESS(Status)) 464 { 465 return Status; 466 } 467 468 /* Read the current mode and its settings */ 469 EfiConOutReadCurrentMode(EfiConOut, &NewMode); 470 Status = EfiConOutQueryMode(EfiConOut, Mode, &HRes, &VRes); 471 if (!NT_SUCCESS(Status)) 472 { 473 EfiConOutSetMode(EfiConOut, CurrentMode.Mode); 474 return Status; 475 } 476 477 /* Save the current mode and its settings */ 478 DisplayMode.HRes = HRes; 479 DisplayMode.VRes = VRes; 480 DisplayMode.HRes2 = HRes; 481 } 482 483 /* Capture all the current settings */ 484 ConsoleEfiTextGetStateFromMode(&NewMode, &TextConsole->State); 485 TextConsole->Mode = NewMode.Mode; 486 TextConsole->DisplayMode = DisplayMode; 487 TextConsole->Protocol = EfiConOut; 488 TextConsole->OldMode = CurrentMode; 489 return STATUS_SUCCESS; 490 } 491 492 NTSTATUS 493 ConsoleInputBaseEraseBuffer ( 494 _In_ PBL_INPUT_CONSOLE Console, 495 _In_opt_ PULONG FillValue 496 ) 497 { 498 ULONG ValueToFill; 499 PULONG i; 500 501 /* Check if we should fill with a particular value */ 502 if (FillValue) 503 { 504 /* Use it */ 505 ValueToFill = *FillValue; 506 } 507 else 508 { 509 /* Otherwise, use default */ 510 ValueToFill = 0x10020; 511 } 512 513 /* Set the input buffer to its last location */ 514 Console->DataStart = Console->DataEnd; 515 516 /* Fill the buffer with the value */ 517 for (i = Console->Buffer; i < Console->EndBuffer; i++) 518 { 519 *i = ValueToFill; 520 } 521 522 /* All done */ 523 return STATUS_SUCCESS; 524 } 525 526 NTSTATUS 527 ConsoleInputLocalEraseBuffer ( 528 _In_ PBL_INPUT_CONSOLE Console, 529 _In_opt_ PULONG FillValue 530 ) 531 { 532 NTSTATUS Status, EfiStatus; 533 534 /* Erase the software buffer */ 535 Status = ConsoleInputBaseEraseBuffer(Console, FillValue); 536 537 /* Reset the hardware console */ 538 EfiStatus = EfiConInEx ? EfiConInExReset() : EfiConInReset(); 539 if (!NT_SUCCESS(EfiStatus)) 540 { 541 /* Normalize the failure code */ 542 EfiStatus = STATUS_UNSUCCESSFUL; 543 } 544 545 /* Check if software reset worked */ 546 if (NT_SUCCESS(Status)) 547 { 548 /* Then return the firmware code */ 549 Status = EfiStatus; 550 } 551 552 /* All done */ 553 return Status; 554 } 555 556 NTSTATUS 557 ConsoleFirmwareTextClear ( 558 _In_ PBL_TEXT_CONSOLE Console, 559 _In_ BOOLEAN LineOnly 560 ) 561 { 562 BL_ARCH_MODE OldMode; 563 EFI_STATUS EfiStatus; 564 NTSTATUS Status; 565 ULONG i, Column, Row, TextWidth, TextHeight; 566 567 /* Get the text resolution */ 568 BlDisplayGetTextCellResolution(&TextWidth, &TextHeight); 569 570 /* Are we just clearing a line? */ 571 if (LineOnly) 572 { 573 /* Get the current column and row */ 574 Column = Console->State.XPos / TextWidth; 575 Row = Console->State.YPos / TextHeight; 576 577 /* Loop over every remaining character */ 578 for (i = 0; i < Console->DisplayMode.HRes - Column - 1; i++) 579 { 580 /* Write a space on top of it */ 581 Status = EfiConOutOutputString(Console->Protocol, L" "); 582 if (!NT_SUCCESS(Status)) 583 { 584 break; 585 } 586 } 587 588 /* And reset the cursor back at the initial position */ 589 Status = EfiConOutSetCursorPosition(Console->Protocol, 590 Column, 591 Row); 592 } 593 else 594 { 595 /* Are we in protected mode? */ 596 OldMode = CurrentExecutionContext->Mode; 597 if (OldMode != BlRealMode) 598 { 599 /* FIXME: Not yet implemented */ 600 return STATUS_NOT_IMPLEMENTED; 601 } 602 603 /* Clear the screen */ 604 EfiStatus = Console->Protocol->ClearScreen(Console->Protocol); 605 606 /* Switch back to protected mode if we came from there */ 607 if (OldMode != BlRealMode) 608 { 609 BlpArchSwitchContext(OldMode); 610 } 611 612 /* Conver to NT status -- did that work? */ 613 Status = EfiGetNtStatusCode(EfiStatus); 614 if (NT_SUCCESS(Status)) 615 { 616 /* Reset current positions */ 617 Console->State.XPos = 0; 618 Console->State.YPos = 0; 619 } 620 } 621 622 /* All done */ 623 return Status; 624 } 625 626