1/** @file
2  The application to show the Boot Manager Menu.
3
4Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "BootManagerMenu.h"
16
17EFI_HII_HANDLE gStringPackHandle;
18
19BOOLEAN   mModeInitialized = FALSE;
20
21//
22// Boot video resolution and text mode.
23//
24UINT32    mBootHorizontalResolution    = 0;
25UINT32    mBootVerticalResolution      = 0;
26UINT32    mBootTextModeColumn          = 0;
27UINT32    mBootTextModeRow             = 0;
28//
29// BIOS setup video resolution and text mode.
30//
31UINT32    mSetupTextModeColumn         = 0;
32UINT32    mSetupTextModeRow            = 0;
33UINT32    mSetupHorizontalResolution   = 0;
34UINT32    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**/
47UINTN
48PrintStringAt (
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**/
70UINTN
71PrintCharAt (
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**/
90UINTN
91GetLineWidth (
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**/
156EFI_STATUS
157InitializeBootMenuScreen (
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**/
230BOOLEAN
231IsBootManagerMenu (
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**/
254BOOLEAN
255IgnoreBootOption (
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**/
299EFI_STATUS
300InitializeBootMenuData (
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**/
354EFI_STATUS
355BootMenuSelectItem (
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**/
495EFI_STATUS
496DrawBootPopupMenu (
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**/
653VOID
654BootFromSelectOption (
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**/
687EFI_STATUS
688EFIAPI
689BdsSetConsoleMode (
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**/
901EFI_STATUS
902EFIAPI
903BootManagerMenuEntry (
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