]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c
ArmVirtPkg/PlatformBDS: Implement PlatformBootManagerUnableToBoot
[mirror_edk2.git] / ArmVirtPkg / Library / PlatformBootManagerLib / PlatformBm.c
... / ...
CommitLineData
1/** @file\r
2 Implementation for PlatformBootManagerLib library class interfaces.\r
3\r
4 Copyright (C) 2015-2016, Red Hat, Inc.\r
5 Copyright (c) 2014, ARM Ltd. All rights reserved.<BR>\r
6 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>\r
7\r
8 This program and the accompanying materials are licensed and made available\r
9 under the terms and conditions of the BSD License which accompanies this\r
10 distribution. The full text of the license may be found at\r
11 http://opensource.org/licenses/bsd-license.php\r
12\r
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
18#include <IndustryStandard/Pci22.h>\r
19#include <IndustryStandard/Virtio095.h>\r
20#include <Library/BootLogoLib.h>\r
21#include <Library/DevicePathLib.h>\r
22#include <Library/PcdLib.h>\r
23#include <Library/QemuBootOrderLib.h>\r
24#include <Library/UefiBootManagerLib.h>\r
25#include <Protocol/DevicePath.h>\r
26#include <Protocol/FirmwareVolume2.h>\r
27#include <Protocol/GraphicsOutput.h>\r
28#include <Protocol/LoadedImage.h>\r
29#include <Protocol/PciIo.h>\r
30#include <Protocol/PciRootBridgeIo.h>\r
31#include <Protocol/VirtioDevice.h>\r
32#include <Guid/EventGroup.h>\r
33#include <Guid/RootBridgesConnectedEventGroup.h>\r
34\r
35#include "PlatformBm.h"\r
36\r
37#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) }\r
38\r
39\r
40#pragma pack (1)\r
41typedef struct {\r
42 VENDOR_DEVICE_PATH SerialDxe;\r
43 UART_DEVICE_PATH Uart;\r
44 VENDOR_DEFINED_DEVICE_PATH TermType;\r
45 EFI_DEVICE_PATH_PROTOCOL End;\r
46} PLATFORM_SERIAL_CONSOLE;\r
47#pragma pack ()\r
48\r
49#define SERIAL_DXE_FILE_GUID { \\r
50 0xD3987D4B, 0x971A, 0x435F, \\r
51 { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \\r
52 }\r
53\r
54STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = {\r
55 //\r
56 // VENDOR_DEVICE_PATH SerialDxe\r
57 //\r
58 {\r
59 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) },\r
60 SERIAL_DXE_FILE_GUID\r
61 },\r
62\r
63 //\r
64 // UART_DEVICE_PATH Uart\r
65 //\r
66 {\r
67 { MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) },\r
68 0, // Reserved\r
69 FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate\r
70 FixedPcdGet8 (PcdUartDefaultDataBits), // DataBits\r
71 FixedPcdGet8 (PcdUartDefaultParity), // Parity\r
72 FixedPcdGet8 (PcdUartDefaultStopBits) // StopBits\r
73 },\r
74\r
75 //\r
76 // VENDOR_DEFINED_DEVICE_PATH TermType\r
77 //\r
78 {\r
79 {\r
80 MESSAGING_DEVICE_PATH, MSG_VENDOR_DP,\r
81 DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH)\r
82 }\r
83 //\r
84 // Guid to be filled in dynamically\r
85 //\r
86 },\r
87\r
88 //\r
89 // EFI_DEVICE_PATH_PROTOCOL End\r
90 //\r
91 {\r
92 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
93 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)\r
94 }\r
95};\r
96\r
97\r
98#pragma pack (1)\r
99typedef struct {\r
100 USB_CLASS_DEVICE_PATH Keyboard;\r
101 EFI_DEVICE_PATH_PROTOCOL End;\r
102} PLATFORM_USB_KEYBOARD;\r
103#pragma pack ()\r
104\r
105STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = {\r
106 //\r
107 // USB_CLASS_DEVICE_PATH Keyboard\r
108 //\r
109 {\r
110 {\r
111 MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP,\r
112 DP_NODE_LEN (USB_CLASS_DEVICE_PATH)\r
113 },\r
114 0xFFFF, // VendorId: any\r
115 0xFFFF, // ProductId: any\r
116 3, // DeviceClass: HID\r
117 1, // DeviceSubClass: boot\r
118 1 // DeviceProtocol: keyboard\r
119 },\r
120\r
121 //\r
122 // EFI_DEVICE_PATH_PROTOCOL End\r
123 //\r
124 {\r
125 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
126 DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL)\r
127 }\r
128};\r
129\r
130\r
131/**\r
132 Check if the handle satisfies a particular condition.\r
133\r
134 @param[in] Handle The handle to check.\r
135 @param[in] ReportText A caller-allocated string passed in for reporting\r
136 purposes. It must never be NULL.\r
137\r
138 @retval TRUE The condition is satisfied.\r
139 @retval FALSE Otherwise. This includes the case when the condition could not\r
140 be fully evaluated due to an error.\r
141**/\r
142typedef\r
143BOOLEAN\r
144(EFIAPI *FILTER_FUNCTION) (\r
145 IN EFI_HANDLE Handle,\r
146 IN CONST CHAR16 *ReportText\r
147 );\r
148\r
149\r
150/**\r
151 Process a handle.\r
152\r
153 @param[in] Handle The handle to process.\r
154 @param[in] ReportText A caller-allocated string passed in for reporting\r
155 purposes. It must never be NULL.\r
156**/\r
157typedef\r
158VOID\r
159(EFIAPI *CALLBACK_FUNCTION) (\r
160 IN EFI_HANDLE Handle,\r
161 IN CONST CHAR16 *ReportText\r
162 );\r
163\r
164/**\r
165 Locate all handles that carry the specified protocol, filter them with a\r
166 callback function, and pass each handle that passes the filter to another\r
167 callback.\r
168\r
169 @param[in] ProtocolGuid The protocol to look for.\r
170\r
171 @param[in] Filter The filter function to pass each handle to. If this\r
172 parameter is NULL, then all handles are processed.\r
173\r
174 @param[in] Process The callback function to pass each handle to that\r
175 clears the filter.\r
176**/\r
177STATIC\r
178VOID\r
179FilterAndProcess (\r
180 IN EFI_GUID *ProtocolGuid,\r
181 IN FILTER_FUNCTION Filter OPTIONAL,\r
182 IN CALLBACK_FUNCTION Process\r
183 )\r
184{\r
185 EFI_STATUS Status;\r
186 EFI_HANDLE *Handles;\r
187 UINTN NoHandles;\r
188 UINTN Idx;\r
189\r
190 Status = gBS->LocateHandleBuffer (ByProtocol, ProtocolGuid,\r
191 NULL /* SearchKey */, &NoHandles, &Handles);\r
192 if (EFI_ERROR (Status)) {\r
193 //\r
194 // This is not an error, just an informative condition.\r
195 //\r
196 DEBUG ((EFI_D_VERBOSE, "%a: %g: %r\n", __FUNCTION__, ProtocolGuid,\r
197 Status));\r
198 return;\r
199 }\r
200\r
201 ASSERT (NoHandles > 0);\r
202 for (Idx = 0; Idx < NoHandles; ++Idx) {\r
203 CHAR16 *DevicePathText;\r
204 STATIC CHAR16 Fallback[] = L"<device path unavailable>";\r
205\r
206 //\r
207 // The ConvertDevicePathToText() function handles NULL input transparently.\r
208 //\r
209 DevicePathText = ConvertDevicePathToText (\r
210 DevicePathFromHandle (Handles[Idx]),\r
211 FALSE, // DisplayOnly\r
212 FALSE // AllowShortcuts\r
213 );\r
214 if (DevicePathText == NULL) {\r
215 DevicePathText = Fallback;\r
216 }\r
217\r
218 if (Filter == NULL || Filter (Handles[Idx], DevicePathText)) {\r
219 Process (Handles[Idx], DevicePathText);\r
220 }\r
221\r
222 if (DevicePathText != Fallback) {\r
223 FreePool (DevicePathText);\r
224 }\r
225 }\r
226 gBS->FreePool (Handles);\r
227}\r
228\r
229\r
230/**\r
231 This FILTER_FUNCTION checks if a handle corresponds to a PCI display device.\r
232**/\r
233STATIC\r
234BOOLEAN\r
235EFIAPI\r
236IsPciDisplay (\r
237 IN EFI_HANDLE Handle,\r
238 IN CONST CHAR16 *ReportText\r
239 )\r
240{\r
241 EFI_STATUS Status;\r
242 EFI_PCI_IO_PROTOCOL *PciIo;\r
243 PCI_TYPE00 Pci;\r
244\r
245 Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid,\r
246 (VOID**)&PciIo);\r
247 if (EFI_ERROR (Status)) {\r
248 //\r
249 // This is not an error worth reporting.\r
250 //\r
251 return FALSE;\r
252 }\r
253\r
254 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0 /* Offset */,\r
255 sizeof Pci / sizeof (UINT32), &Pci);\r
256 if (EFI_ERROR (Status)) {\r
257 DEBUG ((EFI_D_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status));\r
258 return FALSE;\r
259 }\r
260\r
261 return IS_PCI_DISPLAY (&Pci);\r
262}\r
263\r
264\r
265/**\r
266 This FILTER_FUNCTION checks if a handle corresponds to a Virtio RNG device at\r
267 the VIRTIO_DEVICE_PROTOCOL level.\r
268**/\r
269STATIC\r
270BOOLEAN\r
271EFIAPI\r
272IsVirtioRng (\r
273 IN EFI_HANDLE Handle,\r
274 IN CONST CHAR16 *ReportText\r
275 )\r
276{\r
277 EFI_STATUS Status;\r
278 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
279\r
280 Status = gBS->HandleProtocol (Handle, &gVirtioDeviceProtocolGuid,\r
281 (VOID**)&VirtIo);\r
282 if (EFI_ERROR (Status)) {\r
283 return FALSE;\r
284 }\r
285 return (BOOLEAN)(VirtIo->SubSystemDeviceId ==\r
286 VIRTIO_SUBSYSTEM_ENTROPY_SOURCE);\r
287}\r
288\r
289\r
290/**\r
291 This FILTER_FUNCTION checks if a handle corresponds to a Virtio RNG device at\r
292 the EFI_PCI_IO_PROTOCOL level.\r
293**/\r
294STATIC\r
295BOOLEAN\r
296EFIAPI\r
297IsVirtioPciRng (\r
298 IN EFI_HANDLE Handle,\r
299 IN CONST CHAR16 *ReportText\r
300 )\r
301{\r
302 EFI_STATUS Status;\r
303 EFI_PCI_IO_PROTOCOL *PciIo;\r
304 UINT16 VendorId;\r
305 UINT16 DeviceId;\r
306 UINT8 RevisionId;\r
307 BOOLEAN Virtio10;\r
308 UINT16 SubsystemId;\r
309\r
310 Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid,\r
311 (VOID**)&PciIo);\r
312 if (EFI_ERROR (Status)) {\r
313 return FALSE;\r
314 }\r
315\r
316 //\r
317 // Read and check VendorId.\r
318 //\r
319 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET,\r
320 1, &VendorId);\r
321 if (EFI_ERROR (Status)) {\r
322 goto PciError;\r
323 }\r
324 if (VendorId != VIRTIO_VENDOR_ID) {\r
325 return FALSE;\r
326 }\r
327\r
328 //\r
329 // Read DeviceId and RevisionId.\r
330 //\r
331 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_DEVICE_ID_OFFSET,\r
332 1, &DeviceId);\r
333 if (EFI_ERROR (Status)) {\r
334 goto PciError;\r
335 }\r
336 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_REVISION_ID_OFFSET,\r
337 1, &RevisionId);\r
338 if (EFI_ERROR (Status)) {\r
339 goto PciError;\r
340 }\r
341\r
342 //\r
343 // From DeviceId and RevisionId, determine whether the device is a\r
344 // modern-only Virtio 1.0 device. In case of Virtio 1.0, DeviceId can\r
345 // immediately be restricted to VIRTIO_SUBSYSTEM_ENTROPY_SOURCE, and\r
346 // SubsystemId will only play a sanity-check role. Otherwise, DeviceId can\r
347 // only be sanity-checked, and SubsystemId will decide.\r
348 //\r
349 if (DeviceId == 0x1040 + VIRTIO_SUBSYSTEM_ENTROPY_SOURCE &&\r
350 RevisionId >= 0x01) {\r
351 Virtio10 = TRUE;\r
352 } else if (DeviceId >= 0x1000 && DeviceId <= 0x103F && RevisionId == 0x00) {\r
353 Virtio10 = FALSE;\r
354 } else {\r
355 return FALSE;\r
356 }\r
357\r
358 //\r
359 // Read and check SubsystemId as dictated by Virtio10.\r
360 //\r
361 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16,\r
362 PCI_SUBSYSTEM_ID_OFFSET, 1, &SubsystemId);\r
363 if (EFI_ERROR (Status)) {\r
364 goto PciError;\r
365 }\r
366 if (Virtio10 && SubsystemId >= 0x40) {\r
367 return TRUE;\r
368 }\r
369 if (!Virtio10 && SubsystemId == VIRTIO_SUBSYSTEM_ENTROPY_SOURCE) {\r
370 return TRUE;\r
371 }\r
372 return FALSE;\r
373\r
374PciError:\r
375 DEBUG ((DEBUG_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status));\r
376 return FALSE;\r
377}\r
378\r
379\r
380/**\r
381 This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking\r
382 the matching driver to produce all first-level child handles.\r
383**/\r
384STATIC\r
385VOID\r
386EFIAPI\r
387Connect (\r
388 IN EFI_HANDLE Handle,\r
389 IN CONST CHAR16 *ReportText\r
390 )\r
391{\r
392 EFI_STATUS Status;\r
393\r
394 Status = gBS->ConnectController (\r
395 Handle, // ControllerHandle\r
396 NULL, // DriverImageHandle\r
397 NULL, // RemainingDevicePath -- produce all children\r
398 FALSE // Recursive\r
399 );\r
400 DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, "%a: %s: %r\n",\r
401 __FUNCTION__, ReportText, Status));\r
402}\r
403\r
404\r
405/**\r
406 This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the\r
407 handle, and adds it to ConOut and ErrOut.\r
408**/\r
409STATIC\r
410VOID\r
411EFIAPI\r
412AddOutput (\r
413 IN EFI_HANDLE Handle,\r
414 IN CONST CHAR16 *ReportText\r
415 )\r
416{\r
417 EFI_STATUS Status;\r
418 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
419\r
420 DevicePath = DevicePathFromHandle (Handle);\r
421 if (DevicePath == NULL) {\r
422 DEBUG ((EFI_D_ERROR, "%a: %s: handle %p: device path not found\n",\r
423 __FUNCTION__, ReportText, Handle));\r
424 return;\r
425 }\r
426\r
427 Status = EfiBootManagerUpdateConsoleVariable (ConOut, DevicePath, NULL);\r
428 if (EFI_ERROR (Status)) {\r
429 DEBUG ((EFI_D_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__,\r
430 ReportText, Status));\r
431 return;\r
432 }\r
433\r
434 Status = EfiBootManagerUpdateConsoleVariable (ErrOut, DevicePath, NULL);\r
435 if (EFI_ERROR (Status)) {\r
436 DEBUG ((EFI_D_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__,\r
437 ReportText, Status));\r
438 return;\r
439 }\r
440\r
441 DEBUG ((EFI_D_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__,\r
442 ReportText));\r
443}\r
444\r
445STATIC\r
446VOID\r
447PlatformRegisterFvBootOption (\r
448 EFI_GUID *FileGuid,\r
449 CHAR16 *Description,\r
450 UINT32 Attributes\r
451 )\r
452{\r
453 EFI_STATUS Status;\r
454 INTN OptionIndex;\r
455 EFI_BOOT_MANAGER_LOAD_OPTION NewOption;\r
456 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
457 UINTN BootOptionCount;\r
458 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
459 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
460 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
461\r
462 Status = gBS->HandleProtocol (\r
463 gImageHandle,\r
464 &gEfiLoadedImageProtocolGuid,\r
465 (VOID **) &LoadedImage\r
466 );\r
467 ASSERT_EFI_ERROR (Status);\r
468\r
469 EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);\r
470 DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);\r
471 ASSERT (DevicePath != NULL);\r
472 DevicePath = AppendDevicePathNode (\r
473 DevicePath,\r
474 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
475 );\r
476 ASSERT (DevicePath != NULL);\r
477\r
478 Status = EfiBootManagerInitializeLoadOption (\r
479 &NewOption,\r
480 LoadOptionNumberUnassigned,\r
481 LoadOptionTypeBoot,\r
482 Attributes,\r
483 Description,\r
484 DevicePath,\r
485 NULL,\r
486 0\r
487 );\r
488 ASSERT_EFI_ERROR (Status);\r
489 FreePool (DevicePath);\r
490\r
491 BootOptions = EfiBootManagerGetLoadOptions (\r
492 &BootOptionCount, LoadOptionTypeBoot\r
493 );\r
494\r
495 OptionIndex = EfiBootManagerFindLoadOption (\r
496 &NewOption, BootOptions, BootOptionCount\r
497 );\r
498\r
499 if (OptionIndex == -1) {\r
500 Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);\r
501 ASSERT_EFI_ERROR (Status);\r
502 }\r
503 EfiBootManagerFreeLoadOption (&NewOption);\r
504 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
505}\r
506\r
507\r
508/**\r
509 Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options\r
510 whose device paths do not resolve exactly to an FvFile in the system.\r
511\r
512 This removes any boot options that point to binaries built into the firmware\r
513 and have become stale due to any of the following:\r
514 - FvMain's base address or size changed (historical),\r
515 - FvMain's FvNameGuid changed,\r
516 - the FILE_GUID of the pointed-to binary changed,\r
517 - the referenced binary is no longer built into the firmware.\r
518\r
519 EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only\r
520 avoids exact duplicates.\r
521**/\r
522STATIC\r
523VOID\r
524RemoveStaleFvFileOptions (\r
525 VOID\r
526 )\r
527{\r
528 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
529 UINTN BootOptionCount;\r
530 UINTN Index;\r
531\r
532 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount,\r
533 LoadOptionTypeBoot);\r
534\r
535 for (Index = 0; Index < BootOptionCount; ++Index) {\r
536 EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2, *SearchNode;\r
537 EFI_STATUS Status;\r
538 EFI_HANDLE FvHandle;\r
539\r
540 //\r
541 // If the device path starts with neither MemoryMapped(...) nor Fv(...),\r
542 // then keep the boot option.\r
543 //\r
544 Node1 = BootOptions[Index].FilePath;\r
545 if (!(DevicePathType (Node1) == HARDWARE_DEVICE_PATH &&\r
546 DevicePathSubType (Node1) == HW_MEMMAP_DP) &&\r
547 !(DevicePathType (Node1) == MEDIA_DEVICE_PATH &&\r
548 DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)) {\r
549 continue;\r
550 }\r
551\r
552 //\r
553 // If the second device path node is not FvFile(...), then keep the boot\r
554 // option.\r
555 //\r
556 Node2 = NextDevicePathNode (Node1);\r
557 if (DevicePathType (Node2) != MEDIA_DEVICE_PATH ||\r
558 DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP) {\r
559 continue;\r
560 }\r
561\r
562 //\r
563 // Locate the Firmware Volume2 protocol instance that is denoted by the\r
564 // boot option. If this lookup fails (i.e., the boot option references a\r
565 // firmware volume that doesn't exist), then we'll proceed to delete the\r
566 // boot option.\r
567 //\r
568 SearchNode = Node1;\r
569 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid,\r
570 &SearchNode, &FvHandle);\r
571\r
572 if (!EFI_ERROR (Status)) {\r
573 //\r
574 // The firmware volume was found; now let's see if it contains the FvFile\r
575 // identified by GUID.\r
576 //\r
577 EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;\r
578 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;\r
579 UINTN BufferSize;\r
580 EFI_FV_FILETYPE FoundType;\r
581 EFI_FV_FILE_ATTRIBUTES FileAttributes;\r
582 UINT32 AuthenticationStatus;\r
583\r
584 Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid,\r
585 (VOID **)&FvProtocol);\r
586 ASSERT_EFI_ERROR (Status);\r
587\r
588 FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)Node2;\r
589 //\r
590 // Buffer==NULL means we request metadata only: BufferSize, FoundType,\r
591 // FileAttributes.\r
592 //\r
593 Status = FvProtocol->ReadFile (\r
594 FvProtocol,\r
595 &FvFileNode->FvFileName, // NameGuid\r
596 NULL, // Buffer\r
597 &BufferSize,\r
598 &FoundType,\r
599 &FileAttributes,\r
600 &AuthenticationStatus\r
601 );\r
602 if (!EFI_ERROR (Status)) {\r
603 //\r
604 // The FvFile was found. Keep the boot option.\r
605 //\r
606 continue;\r
607 }\r
608 }\r
609\r
610 //\r
611 // Delete the boot option.\r
612 //\r
613 Status = EfiBootManagerDeleteLoadOptionVariable (\r
614 BootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
615 DEBUG_CODE (\r
616 CHAR16 *DevicePathString;\r
617\r
618 DevicePathString = ConvertDevicePathToText(BootOptions[Index].FilePath,\r
619 FALSE, FALSE);\r
620 DEBUG ((\r
621 EFI_ERROR (Status) ? EFI_D_WARN : EFI_D_VERBOSE,\r
622 "%a: removing stale Boot#%04x %s: %r\n",\r
623 __FUNCTION__,\r
624 (UINT32)BootOptions[Index].OptionNumber,\r
625 DevicePathString == NULL ? L"<unavailable>" : DevicePathString,\r
626 Status\r
627 ));\r
628 if (DevicePathString != NULL) {\r
629 FreePool (DevicePathString);\r
630 }\r
631 );\r
632 }\r
633\r
634 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
635}\r
636\r
637\r
638STATIC\r
639VOID\r
640PlatformRegisterOptionsAndKeys (\r
641 VOID\r
642 )\r
643{\r
644 EFI_STATUS Status;\r
645 EFI_INPUT_KEY Enter;\r
646 EFI_INPUT_KEY F2;\r
647 EFI_INPUT_KEY Esc;\r
648 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
649\r
650 //\r
651 // Register ENTER as CONTINUE key\r
652 //\r
653 Enter.ScanCode = SCAN_NULL;\r
654 Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;\r
655 Status = EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);\r
656 ASSERT_EFI_ERROR (Status);\r
657\r
658 //\r
659 // Map F2 and ESC to Boot Manager Menu\r
660 //\r
661 F2.ScanCode = SCAN_F2;\r
662 F2.UnicodeChar = CHAR_NULL;\r
663 Esc.ScanCode = SCAN_ESC;\r
664 Esc.UnicodeChar = CHAR_NULL;\r
665 Status = EfiBootManagerGetBootManagerMenu (&BootOption);\r
666 ASSERT_EFI_ERROR (Status);\r
667 Status = EfiBootManagerAddKeyOptionVariable (\r
668 NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL\r
669 );\r
670 ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
671 Status = EfiBootManagerAddKeyOptionVariable (\r
672 NULL, (UINT16) BootOption.OptionNumber, 0, &Esc, NULL\r
673 );\r
674 ASSERT (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED);\r
675}\r
676\r
677\r
678//\r
679// BDS Platform Functions\r
680//\r
681/**\r
682 Do the platform init, can be customized by OEM/IBV\r
683 Possible things that can be done in PlatformBootManagerBeforeConsole:\r
684 > Update console variable: 1. include hot-plug devices;\r
685 > 2. Clear ConIn and add SOL for AMT\r
686 > Register new Driver#### or Boot####\r
687 > Register new Key####: e.g.: F12\r
688 > Signal ReadyToLock event\r
689 > Authentication action: 1. connect Auth devices;\r
690 > 2. Identify auto logon user.\r
691**/\r
692VOID\r
693EFIAPI\r
694PlatformBootManagerBeforeConsole (\r
695 VOID\r
696 )\r
697{\r
698 RETURN_STATUS PcdStatus;\r
699\r
700 //\r
701 // Signal EndOfDxe PI Event\r
702 //\r
703 EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
704\r
705 //\r
706 // Dispatch deferred images after EndOfDxe event.\r
707 //\r
708 EfiBootManagerDispatchDeferredImages ();\r
709\r
710 //\r
711 // Locate the PCI root bridges and make the PCI bus driver connect each,\r
712 // non-recursively. This will produce a number of child handles with PciIo on\r
713 // them.\r
714 //\r
715 FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid, NULL, Connect);\r
716\r
717 //\r
718 // Signal the ACPI platform driver that it can download QEMU ACPI tables.\r
719 //\r
720 EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid);\r
721\r
722 //\r
723 // Find all display class PCI devices (using the handles from the previous\r
724 // step), and connect them non-recursively. This should produce a number of\r
725 // child handles with GOPs on them.\r
726 //\r
727 FilterAndProcess (&gEfiPciIoProtocolGuid, IsPciDisplay, Connect);\r
728\r
729 //\r
730 // Now add the device path of all handles with GOP on them to ConOut and\r
731 // ErrOut.\r
732 //\r
733 FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput);\r
734\r
735 //\r
736 // Add the hardcoded short-form USB keyboard device path to ConIn.\r
737 //\r
738 EfiBootManagerUpdateConsoleVariable (ConIn,\r
739 (EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, NULL);\r
740\r
741 //\r
742 // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut.\r
743 //\r
744 CopyGuid (&mSerialConsole.TermType.Guid,\r
745 PcdGetPtr (PcdTerminalTypeGuidBuffer));\r
746 EfiBootManagerUpdateConsoleVariable (ConIn,\r
747 (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);\r
748 EfiBootManagerUpdateConsoleVariable (ConOut,\r
749 (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);\r
750 EfiBootManagerUpdateConsoleVariable (ErrOut,\r
751 (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL);\r
752\r
753 //\r
754 // Set the front page timeout from the QEMU configuration.\r
755 //\r
756 PcdStatus = PcdSet16S (PcdPlatformBootTimeOut,\r
757 GetFrontPageTimeoutFromQemu ());\r
758 ASSERT_RETURN_ERROR (PcdStatus);\r
759\r
760 //\r
761 // Register platform-specific boot options and keyboard shortcuts.\r
762 //\r
763 PlatformRegisterOptionsAndKeys ();\r
764\r
765 //\r
766 // At this point, VIRTIO_DEVICE_PROTOCOL instances exist only for Virtio MMIO\r
767 // transports. Install EFI_RNG_PROTOCOL instances on Virtio MMIO RNG devices.\r
768 //\r
769 FilterAndProcess (&gVirtioDeviceProtocolGuid, IsVirtioRng, Connect);\r
770\r
771 //\r
772 // Install both VIRTIO_DEVICE_PROTOCOL and (dependent) EFI_RNG_PROTOCOL\r
773 // instances on Virtio PCI RNG devices.\r
774 //\r
775 FilterAndProcess (&gEfiPciIoProtocolGuid, IsVirtioPciRng, Connect);\r
776}\r
777\r
778/**\r
779 Do the platform specific action after the console is ready\r
780 Possible things that can be done in PlatformBootManagerAfterConsole:\r
781 > Console post action:\r
782 > Dynamically switch output mode from 100x31 to 80x25 for certain senarino\r
783 > Signal console ready platform customized event\r
784 > Run diagnostics like memory testing\r
785 > Connect certain devices\r
786 > Dispatch aditional option roms\r
787 > Special boot: e.g.: USB boot, enter UI\r
788**/\r
789VOID\r
790EFIAPI\r
791PlatformBootManagerAfterConsole (\r
792 VOID\r
793 )\r
794{\r
795 RETURN_STATUS Status;\r
796\r
797 //\r
798 // Show the splash screen.\r
799 //\r
800 BootLogoEnableLogo ();\r
801\r
802 //\r
803 // Process QEMU's -kernel command line option. The kernel booted this way\r
804 // will receive ACPI tables: in PlatformBootManagerBeforeConsole(), we\r
805 // connected any and all PCI root bridges, and then signaled the ACPI\r
806 // platform driver.\r
807 //\r
808 TryRunningQemuKernel ();\r
809\r
810 //\r
811 // Connect the purported boot devices.\r
812 //\r
813 Status = ConnectDevicesFromQemu ();\r
814 if (RETURN_ERROR (Status)) {\r
815 //\r
816 // Connect the rest of the devices.\r
817 //\r
818 EfiBootManagerConnectAll ();\r
819 }\r
820\r
821 //\r
822 // Enumerate all possible boot options, then filter and reorder them based on\r
823 // the QEMU configuration.\r
824 //\r
825 EfiBootManagerRefreshAllBootOption ();\r
826\r
827 //\r
828 // Register UEFI Shell\r
829 //\r
830 PlatformRegisterFvBootOption (\r
831 &gUefiShellFileGuid, L"EFI Internal Shell", LOAD_OPTION_ACTIVE\r
832 );\r
833\r
834 RemoveStaleFvFileOptions ();\r
835 SetBootOrderFromQemu ();\r
836}\r
837\r
838/**\r
839 This function is called each second during the boot manager waits the\r
840 timeout.\r
841\r
842 @param TimeoutRemain The remaining timeout.\r
843**/\r
844VOID\r
845EFIAPI\r
846PlatformBootManagerWaitCallback (\r
847 UINT16 TimeoutRemain\r
848 )\r
849{\r
850 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
851 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
852 UINT16 Timeout;\r
853\r
854 Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
855\r
856 Black.Raw = 0x00000000;\r
857 White.Raw = 0x00FFFFFF;\r
858\r
859 BootLogoUpdateProgress (\r
860 White.Pixel,\r
861 Black.Pixel,\r
862 L"Start boot option",\r
863 White.Pixel,\r
864 (Timeout - TimeoutRemain) * 100 / Timeout,\r
865 0\r
866 );\r
867}\r
868\r
869/**\r
870 The function is called when no boot option could be launched,\r
871 including platform recovery options and options pointing to applications\r
872 built into firmware volumes.\r
873\r
874 If this function returns, BDS attempts to enter an infinite loop.\r
875**/\r
876VOID\r
877EFIAPI\r
878PlatformBootManagerUnableToBoot (\r
879 VOID\r
880 )\r
881{\r
882 EFI_STATUS Status;\r
883 EFI_INPUT_KEY Key;\r
884 EFI_BOOT_MANAGER_LOAD_OPTION BootManagerMenu;\r
885 UINTN Index;\r
886\r
887 //\r
888 // BootManagerMenu doesn't contain the correct information when return status\r
889 // is EFI_NOT_FOUND.\r
890 //\r
891 Status = EfiBootManagerGetBootManagerMenu (&BootManagerMenu);\r
892 if (EFI_ERROR (Status)) {\r
893 return;\r
894 }\r
895 //\r
896 // Normally BdsDxe does not print anything to the system console, but this is\r
897 // a last resort -- the end-user will likely not see any DEBUG messages\r
898 // logged in this situation.\r
899 //\r
900 // AsciiPrint() will NULL-check gST->ConOut internally. We check gST->ConIn\r
901 // here to see if it makes sense to request and wait for a keypress.\r
902 //\r
903 if (gST->ConIn != NULL) {\r
904 AsciiPrint (\r
905 "%a: No bootable option or device was found.\n"\r
906 "%a: Press any key to enter the Boot Manager Menu.\n",\r
907 gEfiCallerBaseName,\r
908 gEfiCallerBaseName\r
909 );\r
910 Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);\r
911 ASSERT_EFI_ERROR (Status);\r
912 ASSERT (Index == 0);\r
913\r
914 //\r
915 // Drain any queued keys.\r
916 //\r
917 while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {\r
918 //\r
919 // just throw away Key\r
920 //\r
921 }\r
922 }\r
923\r
924 for (;;) {\r
925 EfiBootManagerBoot (&BootManagerMenu);\r
926 }\r
927}\r