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
ConsoleEfiTextGetColorForeground(_In_ UINT32 Attributes)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
ConsoleEfiTextGetColorBackground(_In_ UINT32 Attributes)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
ConsoleEfiTextGetEfiColorBackground(_In_ BL_COLOR Color)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
ConsoleEfiTextGetEfiColorForeground(_In_ BL_COLOR Color)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
ConsoleEfiTextGetAttribute(BL_COLOR BgColor,BL_COLOR FgColor)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
ConsoleEfiTextGetStateFromMode(_In_ EFI_SIMPLE_TEXT_OUTPUT_MODE * Mode,_Out_ PBL_DISPLAY_STATE State)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
ConsoleFirmwareTextSetState(_In_ PBL_TEXT_CONSOLE TextConsole,_In_ UCHAR Mask,_In_ PBL_DISPLAY_STATE State)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
ConsoleEfiTextFindModeFromAllowed(_In_ SIMPLE_TEXT_OUTPUT_INTERFACE * TextProtocol,_In_ PBL_DISPLAY_MODE SupportedModes,_In_ ULONG MaxIndex,_Out_ PULONG SupportedMode)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
ConsoleFirmwareTextClose(_In_ PBL_TEXT_CONSOLE TextConsole)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
ConsoleFirmwareTextOpen(_In_ PBL_TEXT_CONSOLE TextConsole)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
ConsoleInputBaseEraseBuffer(_In_ PBL_INPUT_CONSOLE Console,_In_opt_ PULONG FillValue)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
ConsoleInputLocalEraseBuffer(_In_ PBL_INPUT_CONSOLE Console,_In_opt_ PULONG FillValue)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
ConsoleFirmwareTextClear(_In_ PBL_TEXT_CONSOLE Console,_In_ BOOLEAN LineOnly)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