• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Implementation for PlatformBootManagerLib library class interfaces.
3 
4   Copyright (C) 2015-2016, Red Hat, Inc.
5   Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>
6   Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
7   Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
8 
9   This program and the accompanying materials are licensed and made available
10   under the terms and conditions of the BSD License which accompanies this
11   distribution. The full text of the license may be found at
12   http://opensource.org/licenses/bsd-license.php
13 
14   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
15   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 
17 **/
18 
19 #include <IndustryStandard/Pci22.h>
20 #include <Library/DevicePathLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/UefiBootManagerLib.h>
23 #include <Library/UefiLib.h>
24 #include <Protocol/DevicePath.h>
25 #include <Protocol/GraphicsOutput.h>
26 #include <Protocol/LoadedImage.h>
27 #include <Protocol/PciIo.h>
28 #include <Protocol/PciRootBridgeIo.h>
29 #include <Guid/EventGroup.h>
30 #include <Guid/TtyTerm.h>
31 
32 #include "PlatformBm.h"
33 
34 #define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }
35 
36 
37 #pragma pack (1)
38 typedef struct {
39   VENDOR_DEVICE_PATH         SerialDxe;
40   UART_DEVICE_PATH           Uart;
41   VENDOR_DEFINED_DEVICE_PATH TermType;
42   EFI_DEVICE_PATH_PROTOCOL   End;
43 } PLATFORM_SERIAL_CONSOLE;
44 #pragma pack ()
45 
46 #define SERIAL_DXE_FILE_GUID { \
47           0xD3987D4B, 0x971A, 0x435F, \
48           { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \
49           }
50 
51 STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = {
52   //
53   // VENDOR_DEVICE_PATH SerialDxe
54   //
55   {
56     { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },
57     SERIAL_DXE_FILE_GUID
58   },
59 
60   //
61   // UART_DEVICE_PATH Uart
62   //
63   {
64     { MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) },
65     0,                                      // Reserved
66     FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate
67     FixedPcdGet8 (PcdUartDefaultDataBits),  // DataBits
68     FixedPcdGet8 (PcdUartDefaultParity),    // Parity
69     FixedPcdGet8 (PcdUartDefaultStopBits)   // StopBits
70   },
71 
72   //
73   // VENDOR_DEFINED_DEVICE_PATH TermType
74   //
75   {
76     {
77       MESSAGING_DEVICE_PATH, MSG_VENDOR_DP,
78       DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH)
79     }
80     //
81     // Guid to be filled in dynamically
82     //
83   },
84 
85   //
86   // EFI_DEVICE_PATH_PROTOCOL End
87   //
88   {
89     END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
90     DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
91   }
92 };
93 
94 
95 #pragma pack (1)
96 typedef struct {
97   USB_CLASS_DEVICE_PATH    Keyboard;
98   EFI_DEVICE_PATH_PROTOCOL End;
99 } PLATFORM_USB_KEYBOARD;
100 #pragma pack ()
101 
102 STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = {
103   //
104   // USB_CLASS_DEVICE_PATH Keyboard
105   //
106   {
107     {
108       MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP,
109       DP_NODE_LEN (USB_CLASS_DEVICE_PATH)
110     },
111     0xFFFF, // VendorId: any
112     0xFFFF, // ProductId: any
113     3,      // DeviceClass: HID
114     1,      // DeviceSubClass: boot
115     1       // DeviceProtocol: keyboard
116   },
117 
118   //
119   // EFI_DEVICE_PATH_PROTOCOL End
120   //
121   {
122     END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
123     DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)
124   }
125 };
126 
127 
128 /**
129   Check if the handle satisfies a particular condition.
130 
131   @param[in] Handle      The handle to check.
132   @param[in] ReportText  A caller-allocated string passed in for reporting
133                          purposes. It must never be NULL.
134 
135   @retval TRUE   The condition is satisfied.
136   @retval FALSE  Otherwise. This includes the case when the condition could not
137                  be fully evaluated due to an error.
138 **/
139 typedef
140 BOOLEAN
141 (EFIAPI *FILTER_FUNCTION) (
142   IN EFI_HANDLE   Handle,
143   IN CONST CHAR16 *ReportText
144   );
145 
146 
147 /**
148   Process a handle.
149 
150   @param[in] Handle      The handle to process.
151   @param[in] ReportText  A caller-allocated string passed in for reporting
152                          purposes. It must never be NULL.
153 **/
154 typedef
155 VOID
156 (EFIAPI *CALLBACK_FUNCTION)  (
157   IN EFI_HANDLE   Handle,
158   IN CONST CHAR16 *ReportText
159   );
160 
161 /**
162   Locate all handles that carry the specified protocol, filter them with a
163   callback function, and pass each handle that passes the filter to another
164   callback.
165 
166   @param[in] ProtocolGuid  The protocol to look for.
167 
168   @param[in] Filter        The filter function to pass each handle to. If this
169                            parameter is NULL, then all handles are processed.
170 
171   @param[in] Process       The callback function to pass each handle to that
172                            clears the filter.
173 **/
174 STATIC
175 VOID
FilterAndProcess(IN EFI_GUID * ProtocolGuid,IN FILTER_FUNCTION Filter OPTIONAL,IN CALLBACK_FUNCTION Process)176 FilterAndProcess (
177   IN EFI_GUID          *ProtocolGuid,
178   IN FILTER_FUNCTION   Filter         OPTIONAL,
179   IN CALLBACK_FUNCTION Process
180   )
181 {
182   EFI_STATUS Status;
183   EFI_HANDLE *Handles;
184   UINTN      NoHandles;
185   UINTN      Idx;
186 
187   Status = gBS->LocateHandleBuffer (ByProtocol, ProtocolGuid,
188                   NULL /* SearchKey */, &NoHandles, &Handles);
189   if (EFI_ERROR (Status)) {
190     //
191     // This is not an error, just an informative condition.
192     //
193     DEBUG ((EFI_D_VERBOSE, "%a: %g: %r\n", __FUNCTION__, ProtocolGuid,
194       Status));
195     return;
196   }
197 
198   ASSERT (NoHandles > 0);
199   for (Idx = 0; Idx < NoHandles; ++Idx) {
200     CHAR16        *DevicePathText;
201     STATIC CHAR16 Fallback[] = L"<device path unavailable>";
202 
203     //
204     // The ConvertDevicePathToText() function handles NULL input transparently.
205     //
206     DevicePathText = ConvertDevicePathToText (
207                        DevicePathFromHandle (Handles[Idx]),
208                        FALSE, // DisplayOnly
209                        FALSE  // AllowShortcuts
210                        );
211     if (DevicePathText == NULL) {
212       DevicePathText = Fallback;
213     }
214 
215     if (Filter == NULL || Filter (Handles[Idx], DevicePathText)) {
216       Process (Handles[Idx], DevicePathText);
217     }
218 
219     if (DevicePathText != Fallback) {
220       FreePool (DevicePathText);
221     }
222   }
223   gBS->FreePool (Handles);
224 }
225 
226 
227 /**
228   This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.
229 **/
230 STATIC
231 BOOLEAN
232 EFIAPI
IsPciDisplay(IN EFI_HANDLE Handle,IN CONST CHAR16 * ReportText)233 IsPciDisplay (
234   IN EFI_HANDLE   Handle,
235   IN CONST CHAR16 *ReportText
236   )
237 {
238   EFI_STATUS          Status;
239   EFI_PCI_IO_PROTOCOL *PciIo;
240   PCI_TYPE00          Pci;
241 
242   Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid,
243                   (VOID**)&PciIo);
244   if (EFI_ERROR (Status)) {
245     //
246     // This is not an error worth reporting.
247     //
248     return FALSE;
249   }
250 
251   Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0 /* Offset */,
252                         sizeof Pci / sizeof (UINT32), &Pci);
253   if (EFI_ERROR (Status)) {
254     DEBUG ((EFI_D_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status));
255     return FALSE;
256   }
257 
258   return IS_PCI_DISPLAY (&Pci);
259 }
260 
261 
262 /**
263   This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking
264   the matching driver to produce all first-level child handles.
265 **/
266 STATIC
267 VOID
268 EFIAPI
Connect(IN EFI_HANDLE Handle,IN CONST CHAR16 * ReportText)269 Connect (
270   IN EFI_HANDLE   Handle,
271   IN CONST CHAR16 *ReportText
272   )
273 {
274   EFI_STATUS Status;
275 
276   Status = gBS->ConnectController (
277                   Handle, // ControllerHandle
278                   NULL,   // DriverImageHandle
279                   NULL,   // RemainingDevicePath -- produce all children
280                   FALSE   // Recursive
281                   );
282   DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, "%a: %s: %r\n",
283     __FUNCTION__, ReportText, Status));
284 }
285 
286 
287 /**
288   This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the
289   handle, and adds it to ConOut and ErrOut.
290 **/
291 STATIC
292 VOID
293 EFIAPI
AddOutput(IN EFI_HANDLE Handle,IN CONST CHAR16 * ReportText)294 AddOutput (
295   IN EFI_HANDLE   Handle,
296   IN CONST CHAR16 *ReportText
297   )
298 {
299   EFI_STATUS               Status;
300   EFI_DEVICE_PATH_PROTOCOL *DevicePath;
301 
302   DevicePath = DevicePathFromHandle (Handle);
303   if (DevicePath == NULL) {
304     DEBUG ((EFI_D_ERROR, "%a: %s: handle %p: device path not found\n",
305       __FUNCTION__, ReportText, Handle));
306     return;
307   }
308 
309   Status = EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);
310   if (EFI_ERROR (Status)) {
311     DEBUG ((EFI_D_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__,
312       ReportText, Status));
313     return;
314   }
315 
316   Status = EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);
317   if (EFI_ERROR (Status)) {
318     DEBUG ((EFI_D_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__,
319       ReportText, Status));
320     return;
321   }
322 
323   DEBUG ((EFI_D_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__,
324     ReportText));
325 }
326 
327 STATIC
328 VOID
PlatformRegisterFvBootOption(EFI_GUID * FileGuid,CHAR16 * Description,UINT32 Attributes)329 PlatformRegisterFvBootOption (
330   EFI_GUID                         *FileGuid,
331   CHAR16                           *Description,
332   UINT32                           Attributes
333   )
334 {
335   EFI_STATUS                        Status;
336   INTN                              OptionIndex;
337   EFI_BOOT_MANAGER_LOAD_OPTION      NewOption;
338   EFI_BOOT_MANAGER_LOAD_OPTION      *BootOptions;
339   UINTN                             BootOptionCount;
340   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
341   EFI_LOADED_IMAGE_PROTOCOL         *LoadedImage;
342   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
343 
344   Status = gBS->HandleProtocol (
345                   gImageHandle,
346                   &gEfiLoadedImageProtocolGuid,
347                   (VOID **) &LoadedImage
348                   );
349   ASSERT_EFI_ERROR (Status);
350 
351   EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
352   DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);
353   ASSERT (DevicePath != NULL);
354   DevicePath = AppendDevicePathNode (
355                  DevicePath,
356                  (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
357                  );
358   ASSERT (DevicePath != NULL);
359 
360   Status = EfiBootManagerInitializeLoadOption (
361              &NewOption,
362              LoadOptionNumberUnassigned,
363              LoadOptionTypeBoot,
364              Attributes,
365              Description,
366              DevicePath,
367              NULL,
368              0
369              );
370   ASSERT_EFI_ERROR (Status);
371   FreePool (DevicePath);
372 
373   BootOptions = EfiBootManagerGetLoadOptions (
374                   &BootOptionCount, LoadOptionTypeBoot
375                   );
376 
377   OptionIndex = EfiBootManagerFindLoadOption (
378                   &NewOption, BootOptions, BootOptionCount
379                   );
380 
381   if (OptionIndex == -1) {
382     Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
383     ASSERT_EFI_ERROR (Status);
384   }
385   EfiBootManagerFreeLoadOption (&NewOption);
386   EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
387 }
388 
389 
390 STATIC
391 VOID
PlatformRegisterOptionsAndKeys(VOID)392 PlatformRegisterOptionsAndKeys (
393   VOID
394   )
395 {
396   EFI_STATUS                   Status;
397   EFI_INPUT_KEY                Enter;
398   EFI_INPUT_KEY                F2;
399   EFI_INPUT_KEY                Esc;
400   EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
401 
402   //
403   // Register ENTER as CONTINUE key
404   //
405   Enter.ScanCode    = SCAN_NULL;
406   Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;
407   Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);
408   ASSERT_EFI_ERROR (Status);
409 
410   //
411   // Map F2 and ESC to Boot Manager Menu
412   //
413   F2.ScanCode     = SCAN_F2;
414   F2.UnicodeChar  = CHAR_NULL;
415   Esc.ScanCode    = SCAN_ESC;
416   Esc.UnicodeChar = CHAR_NULL;
417   Status = EfiBootManagerGetBootManagerMenu (&BootOption);
418   ASSERT_EFI_ERROR (Status);
419   Status = EfiBootManagerAddKeyOptionVariable (
420              NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL
421              );
422   ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
423   Status = EfiBootManagerAddKeyOptionVariable (
424              NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL
425              );
426   ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);
427 }
428 
429 
430 //
431 // BDS Platform Functions
432 //
433 /**
434   Do the platform init, can be customized by OEM/IBV
435   Possible things that can be done in PlatformBootManagerBeforeConsole:
436   > Update console variable: 1. include hot-plug devices;
437   >                          2. Clear ConIn and add SOL for AMT
438   > Register new Driver#### or Boot####
439   > Register new Key####: e.g.: F12
440   > Signal ReadyToLock event
441   > Authentication action: 1. connect Auth devices;
442   >                        2. Identify auto logon user.
443 **/
444 VOID
445 EFIAPI
PlatformBootManagerBeforeConsole(VOID)446 PlatformBootManagerBeforeConsole (
447   VOID
448   )
449 {
450   //
451   // Signal EndOfDxe PI Event
452   //
453   EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);
454 
455   //
456   // Locate the PCI root bridges and make the PCI bus driver connect each,
457   // non-recursively. This will produce a number of child handles with PciIo on
458   // them.
459   //
460   FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid, NULL, Connect);
461 
462   //
463   // Find all display class PCI devices (using the handles from the previous
464   // step), and connect them non-recursively. This should produce a number of
465   // child handles with GOPs on them.
466   //
467   FilterAndProcess (&gEfiPciIoProtocolGuid, IsPciDisplay, Connect);
468 
469   //
470   // Now add the device path of all handles with GOP on them to ConOut and
471   // ErrOut.
472   //
473   FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput);
474 
475   //
476   // Add the hardcoded short-form USB keyboard device path to ConIn.
477   //
478   EfiBootManagerUpdateConsoleVariable (ConIn,
479     (EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, NULL);
480 
481   //
482   // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.
483   //
484   ASSERT (FixedPcdGet8 (PcdDefaultTerminalType) == 4);
485   CopyGuid (&mSerialConsole.TermType.Guid, &gEfiTtyTermGuid);
486 
487   EfiBootManagerUpdateConsoleVariable (ConIn,
488     (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
489   EfiBootManagerUpdateConsoleVariable (ConOut,
490     (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
491   EfiBootManagerUpdateConsoleVariable (ErrOut,
492     (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);
493 
494   //
495   // Register platform-specific boot options and keyboard shortcuts.
496   //
497   PlatformRegisterOptionsAndKeys ();
498 }
499 
500 /**
501   Do the platform specific action after the console is ready
502   Possible things that can be done in PlatformBootManagerAfterConsole:
503   > Console post action:
504     > Dynamically switch output mode from 100x31 to 80x25 for certain senarino
505     > Signal console ready platform customized event
506   > Run diagnostics like memory testing
507   > Connect certain devices
508   > Dispatch aditional option roms
509   > Special boot: e.g.: USB boot, enter UI
510 **/
511 VOID
512 EFIAPI
PlatformBootManagerAfterConsole(VOID)513 PlatformBootManagerAfterConsole (
514   VOID
515   )
516 {
517   Print (L"Press ESCAPE for boot options ");
518 
519   //
520   // Show the splash screen.
521   //
522   EnableQuietBoot (PcdGetPtr (PcdLogoFile));
523 
524   //
525   // Connect the rest of the devices.
526   //
527   EfiBootManagerConnectAll ();
528 
529   //
530   // Enumerate all possible boot options.
531   //
532   EfiBootManagerRefreshAllBootOption ();
533 
534   //
535   // Register UEFI Shell
536   //
537   PlatformRegisterFvBootOption (
538     PcdGetPtr (PcdShellFile), L"UEFI Shell", LOAD_OPTION_ACTIVE
539     );
540 }
541 
542 /**
543   This function is called each second during the boot manager waits the
544   timeout.
545 
546   @param TimeoutRemain  The remaining timeout.
547 **/
548 VOID
549 EFIAPI
PlatformBootManagerWaitCallback(UINT16 TimeoutRemain)550 PlatformBootManagerWaitCallback (
551   UINT16          TimeoutRemain
552   )
553 {
554   Print (L".");
555 }
556