1 /** @file
2   The application to show the Boot Manager Menu.
3 
4 Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "BootManagerMenu.h"
16 
17 EFI_HII_HANDLE gStringPackHandle;
18 
19 BOOLEAN   mModeInitialized = FALSE;
20 
21 //
22 // Boot video resolution and text mode.
23 //
24 UINT32    mBootHorizontalResolution    = 0;
25 UINT32    mBootVerticalResolution      = 0;
26 UINT32    mBootTextModeColumn          = 0;
27 UINT32    mBootTextModeRow             = 0;
28 //
29 // BIOS setup video resolution and text mode.
30 //
31 UINT32    mSetupTextModeColumn         = 0;
32 UINT32    mSetupTextModeRow            = 0;
33 UINT32    mSetupHorizontalResolution   = 0;
34 UINT32    mSetupVerticalResolution     = 0;
35 
36 /**
37   Prints a unicode string to the default console, at
38   the supplied cursor position, using L"%s" format.
39 
40   @param  Column     The cursor position to print the string at.
41   @param  Row        The cursor position to print the string at
42   @param  String     String pointer.
43 
44   @return Length of string printed to the console
45 
46 **/
47 UINTN
PrintStringAt(IN UINTN Column,IN UINTN Row,IN CHAR16 * String)48 PrintStringAt (
49   IN UINTN     Column,
50   IN UINTN     Row,
51   IN CHAR16    *String
52   )
53 {
54 
55   gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
56   return Print (L"%s", String);
57 }
58 
59 /**
60   Prints a character to the default console, at
61   the supplied cursor position, using L"%c" format.
62 
63   @param  Column     The cursor position to print the string at.
64   @param  Row        The cursor position to print the string at.
65   @param  Character  Character to print.
66 
67   @return Length of string printed to the console.
68 
69 **/
70 UINTN
PrintCharAt(IN UINTN Column,IN UINTN Row,CHAR16 Character)71 PrintCharAt (
72   IN UINTN     Column,
73   IN UINTN     Row,
74   CHAR16       Character
75   )
76 {
77   gST->ConOut->SetCursorPosition (gST->ConOut, Column, Row);
78   return Print (L"%c", Character);
79 }
80 
81 /**
82   Count the storage space of a Unicode string which uses current language to get
83   from input string ID.
84 
85   @param StringId          The input string to be counted.
86 
87   @return Storage space for the input string.
88 
89 **/
90 UINTN
GetLineWidth(IN EFI_STRING_ID StringId)91 GetLineWidth (
92   IN EFI_STRING_ID       StringId
93   )
94 {
95   UINTN        Index;
96   UINTN        IncrementValue;
97   EFI_STRING   String;
98   UINTN        LineWidth;
99 
100   LineWidth = 0;
101   String = HiiGetString (gStringPackHandle, StringId, NULL);
102 
103   if (String != NULL) {
104     Index           = 0;
105     IncrementValue  = 1;
106 
107     do {
108       //
109       // Advance to the null-terminator or to the first width directive
110       //
111       for (;
112            (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0);
113            Index++, LineWidth = LineWidth + IncrementValue
114           )
115         ;
116 
117       //
118       // We hit the null-terminator, we now have a count
119       //
120       if (String[Index] == 0) {
121         break;
122       }
123       //
124       // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed
125       // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2)
126       //
127       if (String[Index] == NARROW_CHAR) {
128         //
129         // Skip to the next character
130         //
131         Index++;
132         IncrementValue = 1;
133       } else {
134         //
135         // Skip to the next character
136         //
137         Index++;
138         IncrementValue = 2;
139       }
140     } while (String[Index] != 0);
141     FreePool (String);
142   }
143 
144   return LineWidth;
145 }
146 
147 /**
148   This function uses calculate the boot menu location, size and scroll bar information.
149 
150   @param  BootMenuData            The boot menu data to be processed.
151 
152   @return EFI_SUCCESS             calculate boot menu information successful.
153   @retval EFI_INVALID_PARAMETER   Input parameter is invalid
154 
155 **/
156 EFI_STATUS
InitializeBootMenuScreen(IN OUT BOOT_MENU_POPUP_DATA * BootMenuData)157 InitializeBootMenuScreen (
158   IN OUT  BOOT_MENU_POPUP_DATA  *BootMenuData
159   )
160 {
161   UINTN         MaxStrWidth;
162   UINTN         StrWidth;
163   UINTN         Index;
164   UINTN         Column;
165   UINTN         Row;
166   UINTN         MaxPrintRows;
167   UINTN         UnSelectableItmes;
168 
169   if (BootMenuData == NULL) {
170     return EFI_INVALID_PARAMETER;
171   }
172   //
173   // Get maximum string width
174   //
175   MaxStrWidth = 0;
176   for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++) {
177     StrWidth = GetLineWidth (BootMenuData->TitleToken[Index]);
178     MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth;
179   }
180 
181   for (Index = 0; Index < BootMenuData->ItemCount; Index++) {
182     StrWidth = GetLineWidth (BootMenuData->PtrTokens[Index]);
183     MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth;
184   }
185 
186   for (Index = 0; Index < HELP_TOKEN_COUNT; Index++) {
187     StrWidth = GetLineWidth (BootMenuData->HelpToken[Index]);
188     MaxStrWidth = MaxStrWidth > StrWidth ? MaxStrWidth : StrWidth;
189   }
190   //
191   // query current row and column to calculate boot menu location
192   //
193   gST->ConOut->QueryMode (
194                  gST->ConOut,
195                  gST->ConOut->Mode->Mode,
196                  &Column,
197                  &Row
198                  );
199 
200   MaxPrintRows = Row - 6;
201   UnSelectableItmes = TITLE_TOKEN_COUNT + 2 + HELP_TOKEN_COUNT + 2;
202   BootMenuData->MenuScreen.Width = MaxStrWidth + 8;
203   if (BootMenuData->ItemCount + UnSelectableItmes > MaxPrintRows) {
204     BootMenuData->MenuScreen.Height = MaxPrintRows;
205     BootMenuData->ScrollBarControl.HasScrollBar = TRUE;
206     BootMenuData->ScrollBarControl.ItemCountPerScreen = MaxPrintRows - UnSelectableItmes;
207     BootMenuData->ScrollBarControl.FirstItem = 0;
208     BootMenuData->ScrollBarControl.LastItem = MaxPrintRows - UnSelectableItmes - 1;
209   } else {
210     BootMenuData->MenuScreen.Height = BootMenuData->ItemCount + UnSelectableItmes;
211     BootMenuData->ScrollBarControl.HasScrollBar = FALSE;
212     BootMenuData->ScrollBarControl.ItemCountPerScreen = BootMenuData->ItemCount;
213     BootMenuData->ScrollBarControl.FirstItem = 0;
214     BootMenuData->ScrollBarControl.LastItem = BootMenuData->ItemCount - 1;
215   }
216   BootMenuData->MenuScreen.StartCol = (Column -  BootMenuData->MenuScreen.Width) / 2;
217   BootMenuData->MenuScreen.StartRow = (Row -  BootMenuData->MenuScreen.Height) / 2;
218 
219   return EFI_SUCCESS;
220 }
221 /**
222   This function uses check boot option is wheher setup application or no
223 
224   @param   BootOption   Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array.
225 
226   @retval  TRUE         This boot option is setup application.
227   @retval  FALSE        This boot options isn't setup application
228 
229 **/
230 BOOLEAN
IsBootManagerMenu(IN EFI_BOOT_MANAGER_LOAD_OPTION * BootOption)231 IsBootManagerMenu (
232   IN  EFI_BOOT_MANAGER_LOAD_OPTION    *BootOption
233   )
234 {
235   EFI_STATUS                          Status;
236   EFI_BOOT_MANAGER_LOAD_OPTION        BootManagerMenu;
237 
238   Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);
239   if (!EFI_ERROR (Status)) {
240     EfiBootManagerFreeLoadOption (&BootManagerMenu);
241   }
242 
243   return (BOOLEAN) (!EFI_ERROR (Status) && (BootOption->OptionNumber == BootManagerMenu.OptionNumber));
244 }
245 
246 /**
247   Return whether to ignore the boot option.
248 
249   @param BootOption  Pointer to EFI_BOOT_MANAGER_LOAD_OPTION to check.
250 
251   @retval TRUE  Ignore the boot option.
252   @retval FALSE Do not ignore the boot option.
253 **/
254 BOOLEAN
IgnoreBootOption(IN EFI_BOOT_MANAGER_LOAD_OPTION * BootOption)255 IgnoreBootOption (
256   IN   EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption
257   )
258 {
259   EFI_STATUS                    Status;
260   EFI_DEVICE_PATH_PROTOCOL      *ImageDevicePath;
261 
262   //
263   // Ignore myself.
264   //
265   Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageDevicePathProtocolGuid, (VOID **) &ImageDevicePath);
266   ASSERT_EFI_ERROR (Status);
267   if (CompareMem (BootOption->FilePath, ImageDevicePath, GetDevicePathSize (ImageDevicePath)) == 0) {
268     return TRUE;
269   }
270 
271   //
272   // Do not ignore Boot Manager Menu.
273   //
274   if (IsBootManagerMenu (BootOption)) {
275     return FALSE;
276   }
277 
278   //
279   // Ignore the hidden/inactive boot option.
280   //
281   if (((BootOption->Attributes & LOAD_OPTION_HIDDEN) != 0) || ((BootOption->Attributes & LOAD_OPTION_ACTIVE) == 0)) {
282     return TRUE;
283   }
284 
285   return FALSE;
286 }
287 
288 /**
289   This function uses to initialize boot menu data
290 
291   @param   BootOption             Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array.
292   @param   BootOptionCount        Number of boot option.
293   @param   BootMenuData           The Input BootMenuData to be initialized.
294 
295   @retval  EFI_SUCCESS            Initialize boot menu data successful.
296   @retval  EFI_INVALID_PARAMETER  Input parameter is invalid.
297 
298 **/
299 EFI_STATUS
InitializeBootMenuData(IN EFI_BOOT_MANAGER_LOAD_OPTION * BootOption,IN UINTN BootOptionCount,OUT BOOT_MENU_POPUP_DATA * BootMenuData)300 InitializeBootMenuData (
301   IN   EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption,
302   IN   UINTN                         BootOptionCount,
303   OUT  BOOT_MENU_POPUP_DATA          *BootMenuData
304   )
305 {
306   UINTN                         Index;
307   UINTN                         StrIndex;
308 
309   if (BootOption == NULL || BootMenuData == NULL) {
310     return EFI_INVALID_PARAMETER;
311   }
312 
313   BootMenuData->TitleToken[0] = STRING_TOKEN (STR_BOOT_POPUP_MENU_TITLE_STRING);
314   BootMenuData->PtrTokens     = AllocateZeroPool (BootOptionCount * sizeof (EFI_STRING_ID));
315   ASSERT (BootMenuData->PtrTokens != NULL);
316 
317   //
318   // Skip boot option which created by BootNext Variable
319   //
320   for (StrIndex = 0, Index = 0; Index < BootOptionCount; Index++) {
321     if (IgnoreBootOption (&BootOption[Index])) {
322       continue;
323     }
324 
325     ASSERT (BootOption[Index].Description != NULL);
326     BootMenuData->PtrTokens[StrIndex++] = HiiSetString (
327                                             gStringPackHandle,
328                                             0,
329                                             BootOption[Index].Description,
330                                             NULL
331                                             );
332   }
333 
334   BootMenuData->ItemCount           = StrIndex;
335   BootMenuData->HelpToken[0] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP1_STRING);
336   BootMenuData->HelpToken[1] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP2_STRING);
337   BootMenuData->HelpToken[2] = STRING_TOKEN (STR_BOOT_POPUP_MENU_HELP3_STRING);
338   InitializeBootMenuScreen (BootMenuData);
339   BootMenuData->SelectItem = 0;
340   return EFI_SUCCESS;
341 }
342 
343 /**
344   This function uses input select item to highlight selected item
345   and set current selected item in BootMenuData
346 
347   @param  WantSelectItem          The user wants to select item.
348   @param  BootMenuData            The boot menu data to be processed
349 
350   @return EFI_SUCCESS             Highlight selected item and update current selected
351                                   item successful
352   @retval EFI_INVALID_PARAMETER   Input parameter is invalid
353 **/
354 EFI_STATUS
BootMenuSelectItem(IN UINTN WantSelectItem,IN OUT BOOT_MENU_POPUP_DATA * BootMenuData)355 BootMenuSelectItem (
356   IN     UINTN                 WantSelectItem,
357   IN OUT BOOT_MENU_POPUP_DATA  *BootMenuData
358   )
359 {
360   INT32                 SavedAttribute;
361   EFI_STRING            String;
362   UINTN                 StartCol;
363   UINTN                 StartRow;
364   UINTN                 PrintCol;
365   UINTN                 PrintRow;
366   UINTN                 TopShadeNum;
367   UINTN                 LowShadeNum;
368   UINTN                 FirstItem;
369   UINTN                 LastItem;
370   UINTN                 ItemCountPerScreen;
371   UINTN                 Index;
372   BOOLEAN               RePaintItems;
373 
374   if (BootMenuData == NULL || WantSelectItem >= BootMenuData->ItemCount) {
375     return EFI_INVALID_PARAMETER;
376   }
377   SavedAttribute = gST->ConOut->Mode->Attribute;
378   RePaintItems = FALSE;
379   StartCol = BootMenuData->MenuScreen.StartCol;
380   StartRow = BootMenuData->MenuScreen.StartRow;
381   //
382   // print selectable items again and adjust scroll bar if need
383   //
384   if (BootMenuData->ScrollBarControl.HasScrollBar &&
385       (WantSelectItem < BootMenuData->ScrollBarControl.FirstItem ||
386       WantSelectItem > BootMenuData->ScrollBarControl.LastItem ||
387       WantSelectItem == BootMenuData->SelectItem)) {
388     ItemCountPerScreen   = BootMenuData->ScrollBarControl.ItemCountPerScreen;
389     //
390     // Set first item and last item
391     //
392     if (WantSelectItem < BootMenuData->ScrollBarControl.FirstItem) {
393       BootMenuData->ScrollBarControl.FirstItem = WantSelectItem;
394       BootMenuData->ScrollBarControl.LastItem = WantSelectItem + ItemCountPerScreen - 1;
395     } else if (WantSelectItem > BootMenuData->ScrollBarControl.LastItem) {
396       BootMenuData->ScrollBarControl.FirstItem = WantSelectItem - ItemCountPerScreen + 1;
397       BootMenuData->ScrollBarControl.LastItem = WantSelectItem;
398     }
399     gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE);
400     FirstItem = BootMenuData->ScrollBarControl.FirstItem;
401     LastItem  = BootMenuData->ScrollBarControl.LastItem;
402     TopShadeNum = 0;
403     if (FirstItem != 0) {
404       TopShadeNum = (FirstItem * ItemCountPerScreen) / BootMenuData->ItemCount;
405       if ((FirstItem * ItemCountPerScreen) % BootMenuData->ItemCount != 0) {
406         TopShadeNum++;
407       }
408       PrintCol = StartCol  + BootMenuData->MenuScreen.Width - 2;
409       PrintRow = StartRow + TITLE_TOKEN_COUNT + 2;
410       for (Index = 0; Index < TopShadeNum; Index++, PrintRow++) {
411         PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_LIGHT_SHADE);
412       }
413     }
414     LowShadeNum = 0;
415     if (LastItem != BootMenuData->ItemCount - 1) {
416       LowShadeNum = ((BootMenuData->ItemCount - 1 - LastItem) * ItemCountPerScreen) / BootMenuData->ItemCount;
417       if (((BootMenuData->ItemCount - 1 - LastItem) * ItemCountPerScreen) % BootMenuData->ItemCount != 0) {
418         LowShadeNum++;
419       }
420       PrintCol = StartCol  + BootMenuData->MenuScreen.Width - 2;
421       PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + ItemCountPerScreen - LowShadeNum;
422       for (Index = 0; Index < LowShadeNum; Index++, PrintRow++) {
423         PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_LIGHT_SHADE);
424       }
425     }
426     PrintCol = StartCol  + BootMenuData->MenuScreen.Width - 2;
427     PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + TopShadeNum;
428     for (Index = TopShadeNum; Index < ItemCountPerScreen - LowShadeNum; Index++, PrintRow++) {
429       PrintCharAt (PrintCol, PrintRow, BLOCKELEMENT_FULL_BLOCK);
430     }
431 
432 
433     //
434     // Clear selectable items first
435     //
436     PrintCol = StartCol  + 1;
437     PrintRow = StartRow + TITLE_TOKEN_COUNT + 2;
438     String = AllocateZeroPool ((BootMenuData->MenuScreen.Width - 2) * sizeof (CHAR16));
439     ASSERT (String != NULL);
440     for (Index = 0; Index < BootMenuData->MenuScreen.Width - 3; Index++) {
441       String[Index] = 0x20;
442     }
443     for (Index = 0; Index < ItemCountPerScreen; Index++) {
444       PrintStringAt (PrintCol, PrintRow + Index, String);
445     }
446     FreePool (String);
447     //
448     // print selectable items
449     //
450     for (Index = 0; Index < ItemCountPerScreen; Index++, PrintRow++) {
451       String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[Index + FirstItem], NULL);
452       PrintStringAt (PrintCol, PrintRow, String);
453       FreePool (String);
454     }
455     RePaintItems = TRUE;
456   }
457 
458   //
459   // Print want to select item
460   //
461   FirstItem = BootMenuData->ScrollBarControl.FirstItem;
462   gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLACK);
463   String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[WantSelectItem], NULL);
464   PrintCol = StartCol  + 1;
465   PrintRow = StartRow + TITLE_TOKEN_COUNT + 2 + WantSelectItem - FirstItem;
466   PrintStringAt (PrintCol, PrintRow, String);
467   FreePool (String);
468 
469   //
470   // if Want Select and selected item isn't the same and doesn't re-draw selectable
471   // items, clear select item
472   //
473   if (WantSelectItem != BootMenuData->SelectItem && !RePaintItems) {
474     gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE);
475     String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[BootMenuData->SelectItem], NULL);
476     PrintCol = StartCol  + 1;
477     PrintRow = StartRow + 3 + BootMenuData->SelectItem - FirstItem;
478     PrintStringAt (PrintCol, PrintRow, String);
479     FreePool (String);
480   }
481 
482   gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
483   BootMenuData->SelectItem = WantSelectItem;
484   return EFI_SUCCESS;
485 }
486 
487 /**
488   This function uses to draw boot popup menu
489 
490   @param   BootMenuData           The Input BootMenuData to be processed.
491 
492   @retval  EFI_SUCCESS            Draw boot popup menu successful.
493 
494 **/
495 EFI_STATUS
DrawBootPopupMenu(IN BOOT_MENU_POPUP_DATA * BootMenuData)496 DrawBootPopupMenu (
497   IN  BOOT_MENU_POPUP_DATA  *BootMenuData
498   )
499 {
500   EFI_STRING            String;
501   UINTN                 Index;
502   UINTN                 Width;
503   UINTN                 StartCol;
504   UINTN                 StartRow;
505   UINTN                 PrintRow;
506   UINTN                 PrintCol;
507   UINTN                 LineWidth;
508   INT32                 SavedAttribute;
509   UINTN                 ItemCountPerScreen;
510 
511   gST->ConOut->ClearScreen (gST->ConOut);
512 
513   SavedAttribute = gST->ConOut->Mode->Attribute;
514   gST->ConOut->SetAttribute (gST->ConOut, EFI_WHITE | EFI_BACKGROUND_BLUE);
515   Width    = BootMenuData->MenuScreen.Width;
516   StartCol = BootMenuData->MenuScreen.StartCol;
517   StartRow = BootMenuData->MenuScreen.StartRow;
518   ItemCountPerScreen = BootMenuData->ScrollBarControl.ItemCountPerScreen;
519   PrintRow = StartRow;
520 
521   gST->ConOut->EnableCursor (gST->ConOut, FALSE);
522   //
523   // Draw Boot popup menu screen
524   //
525   PrintCharAt (StartCol, PrintRow, BOXDRAW_DOWN_RIGHT);
526   for (Index = 1; Index < Width - 1; Index++) {
527     PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
528   }
529   PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_DOWN_LEFT);
530 
531   //
532   // Draw the screen for title
533   //
534   String = AllocateZeroPool ((Width - 1) * sizeof (CHAR16));
535   ASSERT (String != NULL);
536   for (Index = 0; Index < Width - 2; Index++) {
537     String[Index] = 0x20;
538   }
539 
540   for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++) {
541     PrintRow++;
542     PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL);
543     PrintStringAt (StartCol + 1, PrintRow, String);
544     PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL);
545   }
546 
547   PrintRow++;
548   PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL_RIGHT);
549   for (Index = 1; Index < Width - 1; Index++) {
550     PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
551   }
552   PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL_LEFT);
553 
554   //
555   // Draw screen for selectable items
556   //
557   for (Index = 0; Index < ItemCountPerScreen; Index++) {
558     PrintRow++;
559     PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL);
560     PrintStringAt (StartCol + 1, PrintRow, String);
561     PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL);
562   }
563 
564   PrintRow++;
565   PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL_RIGHT);
566   for (Index = 1; Index < Width - 1; Index++) {
567     PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
568   }
569   PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL_LEFT);
570 
571   //
572   // Draw screen for Help
573   //
574   for (Index = 0; Index < HELP_TOKEN_COUNT; Index++) {
575     PrintRow++;
576     PrintCharAt (StartCol, PrintRow, BOXDRAW_VERTICAL);
577     PrintStringAt (StartCol + 1, PrintRow, String);
578     PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_VERTICAL);
579   }
580   FreePool (String);
581 
582   PrintRow++;
583   PrintCharAt (StartCol, PrintRow, BOXDRAW_UP_RIGHT);
584   for (Index = 1; Index < Width - 1; Index++) {
585     PrintCharAt (StartCol + Index, PrintRow, BOXDRAW_HORIZONTAL);
586   }
587   PrintCharAt (StartCol + Width - 1, PrintRow, BOXDRAW_UP_LEFT);
588 
589 
590   //
591   // print title strings
592   //
593   PrintRow = StartRow + 1;
594   for (Index = 0; Index < TITLE_TOKEN_COUNT; Index++, PrintRow++) {
595     String = HiiGetString (gStringPackHandle, BootMenuData->TitleToken[Index], NULL);
596     LineWidth = GetLineWidth (BootMenuData->TitleToken[Index]);
597     PrintCol = StartCol + (Width - LineWidth) / 2;
598     PrintStringAt (PrintCol, PrintRow, String);
599     FreePool (String);
600   }
601 
602   //
603   // print selectable items
604   //
605   PrintCol = StartCol + 1;
606   PrintRow = StartRow + TITLE_TOKEN_COUNT + 2;
607   for (Index = 0; Index < ItemCountPerScreen; Index++, PrintRow++) {
608     String = HiiGetString (gStringPackHandle, BootMenuData->PtrTokens[Index], NULL);
609     PrintStringAt (PrintCol, PrintRow, String);
610     FreePool (String);
611   }
612 
613   //
614   // Print Help strings
615   //
616   PrintRow++;
617   for (Index = 0; Index < HELP_TOKEN_COUNT; Index++, PrintRow++) {
618     String = HiiGetString (gStringPackHandle, BootMenuData->HelpToken[Index], NULL);
619     LineWidth = GetLineWidth (BootMenuData->HelpToken[Index]);
620     PrintCol = StartCol + (Width - LineWidth) / 2;
621     PrintStringAt (PrintCol, PrintRow, String);
622     FreePool (String);
623   }
624 
625   //
626   // Print scroll bar if has scroll bar
627   //
628   if (BootMenuData->ScrollBarControl.HasScrollBar) {
629     PrintCol = StartCol + Width - 2;
630     PrintRow = StartRow + 2;
631     PrintCharAt (PrintCol, PrintRow, GEOMETRICSHAPE_UP_TRIANGLE);
632     PrintCharAt (PrintCol + 1, PrintRow, BOXDRAW_VERTICAL);
633     PrintRow += (ItemCountPerScreen + 1);
634     PrintCharAt (PrintCol, PrintRow, GEOMETRICSHAPE_DOWN_TRIANGLE);
635     PrintCharAt (PrintCol + 1, PrintRow, BOXDRAW_VERTICAL);
636   }
637 
638   gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
639   //
640   // Print Selected item
641   //
642   BootMenuSelectItem (BootMenuData->SelectItem, BootMenuData);
643   return EFI_SUCCESS;
644 }
645 
646 /**
647   This function uses to boot from selected item
648 
649   @param   BootOptions            Pointer to EFI_BOOT_MANAGER_LOAD_OPTION array.
650   @param   BootOptionCount        Number of boot option.
651   @param   SelectItem             Current selected item.
652 **/
653 VOID
BootFromSelectOption(IN EFI_BOOT_MANAGER_LOAD_OPTION * BootOptions,IN UINTN BootOptionCount,IN UINTN SelectItem)654 BootFromSelectOption (
655   IN   EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions,
656   IN   UINTN                         BootOptionCount,
657   IN   UINTN                         SelectItem
658   )
659 {
660   UINTN                 ItemNum;
661   UINTN                 Index;
662 
663   ASSERT (BootOptions != NULL);
664 
665   for (ItemNum = 0, Index = 0; Index < BootOptionCount; Index++) {
666     if (IgnoreBootOption (&BootOptions[Index])) {
667       continue;
668     }
669 
670     if (ItemNum++ == SelectItem) {
671       EfiBootManagerBoot (&BootOptions[Index]);
672       break;
673     }
674   }
675 }
676 
677 /**
678   This function will change video resolution and text mode
679   according to defined setup mode or defined boot mode
680 
681   @param  IsSetupMode   Indicate mode is changed to setup mode or boot mode.
682 
683   @retval  EFI_SUCCESS  Mode is changed successfully.
684   @retval  Others             Mode failed to be changed.
685 
686 **/
687 EFI_STATUS
688 EFIAPI
BdsSetConsoleMode(BOOLEAN IsSetupMode)689 BdsSetConsoleMode (
690   BOOLEAN  IsSetupMode
691   )
692 {
693   EFI_GRAPHICS_OUTPUT_PROTOCOL          *GraphicsOutput;
694   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL       *SimpleTextOut;
695   UINTN                                 SizeOfInfo;
696   EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  *Info;
697   UINT32                                MaxGopMode;
698   UINT32                                MaxTextMode;
699   UINT32                                ModeNumber;
700   UINT32                                NewHorizontalResolution;
701   UINT32                                NewVerticalResolution;
702   UINT32                                NewColumns;
703   UINT32                                NewRows;
704   UINTN                                 HandleCount;
705   EFI_HANDLE                            *HandleBuffer;
706   EFI_STATUS                            Status;
707   UINTN                                 Index;
708   UINTN                                 CurrentColumn;
709   UINTN                                 CurrentRow;
710 
711   MaxGopMode  = 0;
712   MaxTextMode = 0;
713 
714   //
715   // Get current video resolution and text mode
716   //
717   Status = gBS->HandleProtocol (
718                   gST->ConsoleOutHandle,
719                   &gEfiGraphicsOutputProtocolGuid,
720                   (VOID**)&GraphicsOutput
721                   );
722   if (EFI_ERROR (Status)) {
723     GraphicsOutput = NULL;
724   }
725 
726   Status = gBS->HandleProtocol (
727                   gST->ConsoleOutHandle,
728                   &gEfiSimpleTextOutProtocolGuid,
729                   (VOID**)&SimpleTextOut
730                   );
731   if (EFI_ERROR (Status)) {
732     SimpleTextOut = NULL;
733   }
734 
735   if ((GraphicsOutput == NULL) || (SimpleTextOut == NULL)) {
736     return EFI_UNSUPPORTED;
737   }
738 
739   if (IsSetupMode) {
740     //
741     // The required resolution and text mode is setup mode.
742     //
743     NewHorizontalResolution = mSetupHorizontalResolution;
744     NewVerticalResolution   = mSetupVerticalResolution;
745     NewColumns              = mSetupTextModeColumn;
746     NewRows                 = mSetupTextModeRow;
747   } else {
748     //
749     // The required resolution and text mode is boot mode.
750     //
751     NewHorizontalResolution = mBootHorizontalResolution;
752     NewVerticalResolution   = mBootVerticalResolution;
753     NewColumns              = mBootTextModeColumn;
754     NewRows                 = mBootTextModeRow;
755   }
756 
757   if (GraphicsOutput != NULL) {
758     MaxGopMode  = GraphicsOutput->Mode->MaxMode;
759   }
760 
761   if (SimpleTextOut != NULL) {
762     MaxTextMode = SimpleTextOut->Mode->MaxMode;
763   }
764 
765   //
766   // 1. If current video resolution is same with required video resolution,
767   //    video resolution need not be changed.
768   //    1.1. If current text mode is same with required text mode, text mode need not be changed.
769   //    1.2. If current text mode is different from required text mode, text mode need be changed.
770   // 2. If current video resolution is different from required video resolution, we need restart whole console drivers.
771   //
772   for (ModeNumber = 0; ModeNumber < MaxGopMode; ModeNumber++) {
773     Status = GraphicsOutput->QueryMode (
774                        GraphicsOutput,
775                        ModeNumber,
776                        &SizeOfInfo,
777                        &Info
778                        );
779     if (!EFI_ERROR (Status)) {
780       if ((Info->HorizontalResolution == NewHorizontalResolution) &&
781           (Info->VerticalResolution == NewVerticalResolution)) {
782         if ((GraphicsOutput->Mode->Info->HorizontalResolution == NewHorizontalResolution) &&
783             (GraphicsOutput->Mode->Info->VerticalResolution == NewVerticalResolution)) {
784           //
785           // Current resolution is same with required resolution, check if text mode need be set
786           //
787           Status = SimpleTextOut->QueryMode (SimpleTextOut, SimpleTextOut->Mode->Mode, &CurrentColumn, &CurrentRow);
788           ASSERT_EFI_ERROR (Status);
789           if (CurrentColumn == NewColumns && CurrentRow == NewRows) {
790             //
791             // If current text mode is same with required text mode. Do nothing
792             //
793             FreePool (Info);
794             return EFI_SUCCESS;
795           } else {
796             //
797             // If current text mode is different from required text mode.  Set new video mode
798             //
799             for (Index = 0; Index < MaxTextMode; Index++) {
800               Status = SimpleTextOut->QueryMode (SimpleTextOut, Index, &CurrentColumn, &CurrentRow);
801               if (!EFI_ERROR(Status)) {
802                 if ((CurrentColumn == NewColumns) && (CurrentRow == NewRows)) {
803                   //
804                   // Required text mode is supported, set it.
805                   //
806                   Status = SimpleTextOut->SetMode (SimpleTextOut, Index);
807                   ASSERT_EFI_ERROR (Status);
808                   //
809                   // Update text mode PCD.
810                   //
811                   Status = PcdSet32S (PcdConOutColumn, mSetupTextModeColumn);
812                   ASSERT_EFI_ERROR (Status);
813                   Status = PcdSet32S (PcdConOutRow, mSetupTextModeRow);
814                   ASSERT_EFI_ERROR (Status);
815                   FreePool (Info);
816                   return EFI_SUCCESS;
817                 }
818               }
819             }
820             if (Index == MaxTextMode) {
821               //
822               // If required text mode is not supported, return error.
823               //
824               FreePool (Info);
825               return EFI_UNSUPPORTED;
826             }
827           }
828         } else {
829           //
830           // If current video resolution is not same with the new one, set new video resolution.
831           // In this case, the driver which produces simple text out need be restarted.
832           //
833           Status = GraphicsOutput->SetMode (GraphicsOutput, ModeNumber);
834           if (!EFI_ERROR (Status)) {
835             FreePool (Info);
836             break;
837           }
838         }
839       }
840       FreePool (Info);
841     }
842   }
843 
844   if (ModeNumber == MaxGopMode) {
845     //
846     // If the resolution is not supported, return error.
847     //
848     return EFI_UNSUPPORTED;
849   }
850 
851   //
852   // Set PCD to Inform GraphicsConsole to change video resolution.
853   // Set PCD to Inform Consplitter to change text mode.
854   //
855   Status = PcdSet32S (PcdVideoHorizontalResolution, NewHorizontalResolution);
856   ASSERT_EFI_ERROR (Status);
857   Status = PcdSet32S (PcdVideoVerticalResolution, NewVerticalResolution);
858   ASSERT_EFI_ERROR (Status);
859   Status = PcdSet32S (PcdConOutColumn, NewColumns);
860   ASSERT_EFI_ERROR (Status);
861   Status = PcdSet32S (PcdConOutRow, NewRows);
862   ASSERT_EFI_ERROR (Status);
863 
864   //
865   // Video mode is changed, so restart graphics console driver and higher level driver.
866   // Reconnect graphics console driver and higher level driver.
867   // Locate all the handles with GOP protocol and reconnect it.
868   //
869   Status = gBS->LocateHandleBuffer (
870                    ByProtocol,
871                    &gEfiSimpleTextOutProtocolGuid,
872                    NULL,
873                    &HandleCount,
874                    &HandleBuffer
875                    );
876   if (!EFI_ERROR (Status)) {
877     for (Index = 0; Index < HandleCount; Index++) {
878       gBS->DisconnectController (HandleBuffer[Index], NULL, NULL);
879     }
880     for (Index = 0; Index < HandleCount; Index++) {
881       gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
882     }
883     if (HandleBuffer != NULL) {
884       FreePool (HandleBuffer);
885     }
886   }
887 
888   return EFI_SUCCESS;
889 }
890 
891 /**
892   Display the boot popup menu and allow user select boot item.
893 
894   @param   ImageHandle     The image handle.
895   @param   SystemTable     The system table.
896 
897   @retval  EFI_SUCCESS          Boot from selected boot option, and return success from boot option
898   @retval  EFI_NOT_FOUND        User select to enter setup or can not find boot option
899 
900 **/
901 EFI_STATUS
902 EFIAPI
BootManagerMenuEntry(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)903 BootManagerMenuEntry (
904   IN EFI_HANDLE                            ImageHandle,
905   IN EFI_SYSTEM_TABLE                      *SystemTable
906   )
907 {
908   EFI_BOOT_MANAGER_LOAD_OPTION    *BootOption;
909   UINTN                           BootOptionCount;
910   EFI_STATUS                      Status;
911   BOOT_MENU_POPUP_DATA            BootMenuData;
912   UINTN                           Index;
913   EFI_INPUT_KEY                   Key;
914   BOOLEAN                         ExitApplication;
915   UINTN                           SelectItem;
916   EFI_BOOT_LOGO_PROTOCOL          *BootLogo;
917   EFI_GRAPHICS_OUTPUT_PROTOCOL    *GraphicsOutput;
918   EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *SimpleTextOut;
919   UINTN                           BootTextColumn;
920   UINTN                           BootTextRow;
921 
922   //
923   // Set Logo status invalid when boot manager menu is launched
924   //
925   BootLogo = NULL;
926   Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
927   if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
928     Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
929     ASSERT_EFI_ERROR (Status);
930   }
931 
932   gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
933 
934   gStringPackHandle = HiiAddPackages (
935                          &gEfiCallerIdGuid,
936                          gImageHandle,
937                          BootManagerMenuAppStrings,
938                          NULL
939                          );
940   ASSERT (gStringPackHandle != NULL);
941 
942   //
943   // Connect all prior to entering the platform setup menu.
944   //
945   EfiBootManagerConnectAll ();
946   EfiBootManagerRefreshAllBootOption ();
947 
948   BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
949 
950   if (!mModeInitialized) {
951     //
952     // After the console is ready, get current video resolution
953     // and text mode before launching setup at first time.
954     //
955     Status = gBS->HandleProtocol (
956                     gST->ConsoleOutHandle,
957                     &gEfiGraphicsOutputProtocolGuid,
958                     (VOID**)&GraphicsOutput
959                     );
960     if (EFI_ERROR (Status)) {
961       GraphicsOutput = NULL;
962     }
963 
964     Status = gBS->HandleProtocol (
965                     gST->ConsoleOutHandle,
966                     &gEfiSimpleTextOutProtocolGuid,
967                     (VOID**)&SimpleTextOut
968                     );
969     if (EFI_ERROR (Status)) {
970       SimpleTextOut = NULL;
971     }
972 
973     if (GraphicsOutput != NULL) {
974       //
975       // Get current video resolution and text mode.
976       //
977       mBootHorizontalResolution = GraphicsOutput->Mode->Info->HorizontalResolution;
978       mBootVerticalResolution   = GraphicsOutput->Mode->Info->VerticalResolution;
979     }
980 
981     if (SimpleTextOut != NULL) {
982       Status = SimpleTextOut->QueryMode (
983                                 SimpleTextOut,
984                                 SimpleTextOut->Mode->Mode,
985                                 &BootTextColumn,
986                                 &BootTextRow
987                                 );
988       mBootTextModeColumn = (UINT32)BootTextColumn;
989       mBootTextModeRow    = (UINT32)BootTextRow;
990     }
991 
992     //
993     // Get user defined text mode for setup.
994     //
995     mSetupHorizontalResolution = PcdGet32 (PcdSetupVideoHorizontalResolution);
996     mSetupVerticalResolution   = PcdGet32 (PcdSetupVideoVerticalResolution);
997     mSetupTextModeColumn       = PcdGet32 (PcdSetupConOutColumn);
998     mSetupTextModeRow          = PcdGet32 (PcdSetupConOutRow);
999     mModeInitialized           = TRUE;
1000   }
1001 
1002   //
1003   // Set back to conventional setup resolution
1004   //
1005   BdsSetConsoleMode (TRUE);
1006 
1007   //
1008   // Initialize Boot menu data
1009   //
1010   Status = InitializeBootMenuData (BootOption, BootOptionCount, &BootMenuData);
1011   //
1012   // According to boot menu data to draw boot popup menu
1013   //
1014   DrawBootPopupMenu (&BootMenuData);
1015 
1016   //
1017   // check user input to determine want to re-draw or boot from user selected item
1018   //
1019   ExitApplication = FALSE;
1020   while (!ExitApplication) {
1021     gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
1022     Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
1023     if (!EFI_ERROR (Status)) {
1024       switch (Key.UnicodeChar) {
1025 
1026       case CHAR_NULL:
1027         switch (Key.ScanCode) {
1028 
1029         case SCAN_UP:
1030           SelectItem = BootMenuData.SelectItem == 0 ? BootMenuData.ItemCount - 1 : BootMenuData.SelectItem - 1;
1031           BootMenuSelectItem (SelectItem, &BootMenuData);
1032           break;
1033 
1034         case SCAN_DOWN:
1035           SelectItem = BootMenuData.SelectItem == BootMenuData.ItemCount - 1 ? 0 : BootMenuData.SelectItem + 1;
1036           BootMenuSelectItem (SelectItem, &BootMenuData);
1037           break;
1038 
1039         case SCAN_ESC:
1040           gST->ConOut->ClearScreen (gST->ConOut);
1041           ExitApplication = TRUE;
1042           //
1043           // Set boot resolution for normal boot
1044           //
1045           BdsSetConsoleMode (FALSE);
1046           break;
1047 
1048         default:
1049           break;
1050         }
1051         break;
1052 
1053       case CHAR_CARRIAGE_RETURN:
1054         gST->ConOut->ClearScreen (gST->ConOut);
1055         //
1056         // Set boot resolution for normal boot
1057         //
1058         BdsSetConsoleMode (FALSE);
1059         BootFromSelectOption (BootOption, BootOptionCount, BootMenuData.SelectItem);
1060         //
1061         // Back to boot manager menu again, set back to setup resolution
1062         //
1063         BdsSetConsoleMode (TRUE);
1064         DrawBootPopupMenu (&BootMenuData);
1065         break;
1066 
1067       default:
1068         break;
1069       }
1070     }
1071   }
1072   EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount);
1073   FreePool (BootMenuData.PtrTokens);
1074 
1075   HiiRemovePackages (gStringPackHandle);
1076 
1077   return Status;
1078 
1079 }
1080