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