xref: /reactos/boot/environ/lib/io/display/efi/textcons.c (revision 8a978a17)
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