1 /** @file
2   This module produce main entry for BDS phase - BdsEntry.
3   When this module was dispatched by DxeCore, gEfiBdsArchProtocolGuid will be installed
4   which contains interface of BdsEntry.
5   After DxeCore finish DXE phase, gEfiBdsArchProtocolGuid->BdsEntry will be invoked
6   to enter BDS phase.
7 
8 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10 
11 **/
12 
13 #include "Bds.h"
14 #include "Language.h"
15 #include "FrontPage.h"
16 #include "Hotkey.h"
17 #include "HwErrRecSupport.h"
18 
19 ///
20 /// BDS arch protocol instance initial value.
21 ///
22 /// Note: Current BDS not directly get the BootMode, DefaultBoot,
23 /// TimeoutDefault, MemoryTestLevel value from the BDS arch protocol.
24 /// Please refer to the library useage of BdsLibGetBootMode, BdsLibGetTimeout
25 /// and PlatformBdsDiagnostics in BdsPlatform.c
26 ///
27 EFI_HANDLE  gBdsHandle = NULL;
28 
29 EFI_BDS_ARCH_PROTOCOL  gBds = {
30   BdsEntry
31 };
32 
33 UINT16                          *mBootNext = NULL;
34 
35 ///
36 /// The read-only variables defined in UEFI Spec.
37 ///
38 CHAR16  *mReadOnlyVariables[] = {
39   L"PlatformLangCodes",
40   L"LangCodes",
41   L"BootOptionSupport",
42   L"HwErrRecSupport",
43   L"OsIndicationsSupported"
44   };
45 
46 /**
47 
48   Install Boot Device Selection Protocol
49 
50   @param ImageHandle     The image handle.
51   @param SystemTable     The system table.
52 
53   @retval  EFI_SUCEESS  BDS has finished initializing.
54                         Return the dispatcher and recall BDS.Entry
55   @retval  Other        Return status from AllocatePool() or gBS->InstallProtocolInterface
56 
57 **/
58 EFI_STATUS
59 EFIAPI
BdsInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)60 BdsInitialize (
61   IN EFI_HANDLE                            ImageHandle,
62   IN EFI_SYSTEM_TABLE                      *SystemTable
63   )
64 {
65   EFI_STATUS  Status;
66 
67   //
68   // Install protocol interface
69   //
70   Status = gBS->InstallMultipleProtocolInterfaces (
71                   &gBdsHandle,
72                   &gEfiBdsArchProtocolGuid, &gBds,
73                   NULL
74                   );
75   ASSERT_EFI_ERROR (Status);
76 
77   return Status;
78 }
79 
80 /**
81 
82   This function attempts to boot for the boot order specified
83   by platform policy.
84 
85 **/
86 VOID
BdsBootDeviceSelect(VOID)87 BdsBootDeviceSelect (
88   VOID
89   )
90 {
91   EFI_STATUS        Status;
92   LIST_ENTRY        *Link;
93   BDS_COMMON_OPTION *BootOption;
94   UINTN             ExitDataSize;
95   CHAR16            *ExitData;
96   UINT16            Timeout;
97   LIST_ENTRY        BootLists;
98   CHAR16            Buffer[20];
99   BOOLEAN           BootNextExist;
100   LIST_ENTRY        *LinkBootNext;
101   EFI_EVENT         ConnectConInEvent;
102 
103   //
104   // Got the latest boot option
105   //
106   BootNextExist = FALSE;
107   LinkBootNext  = NULL;
108   ConnectConInEvent = NULL;
109   InitializeListHead (&BootLists);
110 
111   //
112   // First check the boot next option
113   //
114   ZeroMem (Buffer, sizeof (Buffer));
115 
116   //
117   // Create Event to signal ConIn connection request
118   //
119   if (PcdGetBool (PcdConInConnectOnDemand)) {
120     Status = gBS->CreateEventEx (
121                     EVT_NOTIFY_SIGNAL,
122                     TPL_CALLBACK,
123                     EfiEventEmptyFunction,
124                     NULL,
125                     &gConnectConInEventGuid,
126                     &ConnectConInEvent
127                     );
128     if (EFI_ERROR(Status)) {
129       ConnectConInEvent = NULL;
130     }
131   }
132 
133   if (mBootNext != NULL) {
134     //
135     // Indicate we have the boot next variable, so this time
136     // boot will always have this boot option
137     //
138     BootNextExist = TRUE;
139 
140     //
141     // Clear the this variable so it's only exist in this time boot
142     //
143     Status = gRT->SetVariable (
144                     L"BootNext",
145                     &gEfiGlobalVariableGuid,
146                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
147                     0,
148                     NULL
149                     );
150     //
151     // Deleting variable with current variable implementation shouldn't fail.
152     //
153     ASSERT_EFI_ERROR (Status);
154 
155     //
156     // Add the boot next boot option
157     //
158     UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", *mBootNext);
159     BootOption = BdsLibVariableToOption (&BootLists, Buffer);
160 
161     //
162     // If fail to get boot option from variable, just return and do nothing.
163     //
164     if (BootOption == NULL) {
165       return;
166     }
167 
168     BootOption->BootCurrent = *mBootNext;
169   }
170   //
171   // Parse the boot order to get boot option
172   //
173   BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");
174 
175   //
176   // When we didn't have chance to build boot option variables in the first
177   // full configuration boot (e.g.: Reset in the first page or in Device Manager),
178   // we have no boot options in the following mini configuration boot.
179   // Give the last chance to enumerate the boot options.
180   //
181   if (IsListEmpty (&BootLists)) {
182     BdsLibEnumerateAllBootOption (&BootLists);
183   }
184 
185   Link = BootLists.ForwardLink;
186 
187   //
188   // Parameter check, make sure the loop will be valid
189   //
190   if (Link == NULL) {
191     return ;
192   }
193   //
194   // Here we make the boot in a loop, every boot success will
195   // return to the front page
196   //
197   for (;;) {
198     //
199     // Check the boot option list first
200     //
201     if (Link == &BootLists) {
202       //
203       // When LazyConIn enabled, signal connect ConIn event before enter UI
204       //
205       if (PcdGetBool (PcdConInConnectOnDemand) && ConnectConInEvent != NULL) {
206         gBS->SignalEvent (ConnectConInEvent);
207       }
208 
209       //
210       // There are two ways to enter here:
211       // 1. There is no active boot option, give user chance to
212       //    add new boot option
213       // 2. All the active boot option processed, and there is no
214       //    one is success to boot, then we back here to allow user
215       //    add new active boot option
216       //
217       Timeout = 0xffff;
218       PlatformBdsEnterFrontPage (Timeout, FALSE);
219       InitializeListHead (&BootLists);
220       BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");
221       Link = BootLists.ForwardLink;
222       continue;
223     }
224     //
225     // Get the boot option from the link list
226     //
227     BootOption = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
228 
229     //
230     // According to EFI Specification, if a load option is not marked
231     // as LOAD_OPTION_ACTIVE, the boot manager will not automatically
232     // load the option.
233     //
234     if (!IS_LOAD_OPTION_TYPE (BootOption->Attribute, LOAD_OPTION_ACTIVE)) {
235       //
236       // skip the header of the link list, because it has no boot option
237       //
238       Link = Link->ForwardLink;
239       continue;
240     }
241     //
242     // Make sure the boot option device path connected,
243     // but ignore the BBS device path
244     //
245     if (DevicePathType (BootOption->DevicePath) != BBS_DEVICE_PATH) {
246       //
247       // Notes: the internal shell can not been connected with device path
248       // so we do not check the status here
249       //
250       BdsLibConnectDevicePath (BootOption->DevicePath);
251     }
252 
253     //
254     // Restore to original mode before launching boot option.
255     //
256     BdsSetConsoleMode (FALSE);
257 
258     //
259     // All the driver options should have been processed since
260     // now boot will be performed.
261     //
262     Status = BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);
263     if (Status != EFI_SUCCESS) {
264       //
265       // Call platform action to indicate the boot fail
266       //
267       BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));
268       PlatformBdsBootFail (BootOption, Status, ExitData, ExitDataSize);
269 
270       //
271       // Check the next boot option
272       //
273       Link = Link->ForwardLink;
274 
275     } else {
276       //
277       // Call platform action to indicate the boot success
278       //
279       BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED));
280       PlatformBdsBootSuccess (BootOption);
281 
282       //
283       // Boot success, then stop process the boot order, and
284       // present the boot manager menu, front page
285       //
286 
287       //
288       // When LazyConIn enabled, signal connect ConIn Event before enter UI
289       //
290       if (PcdGetBool (PcdConInConnectOnDemand) && ConnectConInEvent != NULL) {
291         gBS->SignalEvent (ConnectConInEvent);
292       }
293 
294       Timeout = 0xffff;
295       PlatformBdsEnterFrontPage (Timeout, FALSE);
296 
297       //
298       // Rescan the boot option list, avoid potential risk of the boot
299       // option change in front page
300       //
301       if (BootNextExist) {
302         LinkBootNext = BootLists.ForwardLink;
303       }
304 
305       InitializeListHead (&BootLists);
306       if (LinkBootNext != NULL) {
307         //
308         // Reserve the boot next option
309         //
310         InsertTailList (&BootLists, LinkBootNext);
311       }
312 
313       BdsLibBuildOptionFromVar (&BootLists, L"BootOrder");
314       Link = BootLists.ForwardLink;
315     }
316   }
317 
318 }
319 
320 /**
321 
322   Validate input console variable data.
323 
324   If found the device path is not a valid device path, remove the variable.
325 
326   @param VariableName             Input console variable name.
327 
328 **/
329 VOID
BdsFormalizeConsoleVariable(IN CHAR16 * VariableName)330 BdsFormalizeConsoleVariable (
331   IN  CHAR16          *VariableName
332   )
333 {
334   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
335   UINTN                     VariableSize;
336   EFI_STATUS                Status;
337 
338   DevicePath = BdsLibGetVariableAndSize (
339                       VariableName,
340                       &gEfiGlobalVariableGuid,
341                       &VariableSize
342                       );
343   if ((DevicePath != NULL) && !IsDevicePathValid (DevicePath, VariableSize)) {
344     Status = gRT->SetVariable (
345                     VariableName,
346                     &gEfiGlobalVariableGuid,
347                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
348                     0,
349                     NULL
350                     );
351     //
352     // Deleting variable with current variable implementation shouldn't fail.
353     //
354     ASSERT_EFI_ERROR (Status);
355   }
356 }
357 
358 /**
359 
360   Formalize Bds global variables.
361 
362  1. For ConIn/ConOut/ConErr, if found the device path is not a valid device path, remove the variable.
363  2. For OsIndicationsSupported, Create a BS/RT/UINT64 variable to report caps
364  3. Delete OsIndications variable if it is not NV/BS/RT UINT64
365  Item 3 is used to solve case when OS corrupts OsIndications. Here simply delete this NV variable.
366 
367 **/
368 VOID
BdsFormalizeEfiGlobalVariable(VOID)369 BdsFormalizeEfiGlobalVariable (
370   VOID
371   )
372 {
373   EFI_STATUS Status;
374   UINT64     OsIndicationSupport;
375   UINT64     OsIndication;
376   UINTN      DataSize;
377   UINT32     Attributes;
378 
379   //
380   // Validate Console variable.
381   //
382   BdsFormalizeConsoleVariable (L"ConIn");
383   BdsFormalizeConsoleVariable (L"ConOut");
384   BdsFormalizeConsoleVariable (L"ErrOut");
385 
386   //
387   // OS indicater support variable
388   //
389   OsIndicationSupport = EFI_OS_INDICATIONS_BOOT_TO_FW_UI \
390                       | EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED;
391 
392   BdsDxeSetVariableAndReportStatusCodeOnError (
393     L"OsIndicationsSupported",
394     &gEfiGlobalVariableGuid,
395     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
396     sizeof(UINT64),
397     &OsIndicationSupport
398     );
399 
400   //
401   // If OsIndications is invalid, remove it.
402   // Invalid case
403   //   1. Data size != UINT64
404   //   2. OsIndication value inconsistence
405   //   3. OsIndication attribute inconsistence
406   //
407   OsIndication = 0;
408   Attributes = 0;
409   DataSize = sizeof(UINT64);
410   Status = gRT->GetVariable (
411                   L"OsIndications",
412                   &gEfiGlobalVariableGuid,
413                   &Attributes,
414                   &DataSize,
415                   &OsIndication
416                   );
417 
418   if (!EFI_ERROR(Status)) {
419     if (DataSize != sizeof(UINT64) ||
420         (OsIndication & ~OsIndicationSupport) != 0 ||
421         Attributes != (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE)){
422 
423       DEBUG ((EFI_D_ERROR, "Unformalized OsIndications variable exists. Delete it\n"));
424       Status = gRT->SetVariable (
425                       L"OsIndications",
426                       &gEfiGlobalVariableGuid,
427                       0,
428                       0,
429                       NULL
430                       );
431       //
432       // Deleting variable with current variable implementation shouldn't fail.
433       //
434       ASSERT_EFI_ERROR (Status);
435     }
436   }
437 
438 }
439 
440 /**
441 
442   Service routine for BdsInstance->Entry(). Devices are connected, the
443   consoles are initialized, and the boot options are tried.
444 
445   @param This             Protocol Instance structure.
446 
447 **/
448 VOID
449 EFIAPI
BdsEntry(IN EFI_BDS_ARCH_PROTOCOL * This)450 BdsEntry (
451   IN EFI_BDS_ARCH_PROTOCOL  *This
452   )
453 {
454   LIST_ENTRY                      DriverOptionList;
455   LIST_ENTRY                      BootOptionList;
456   UINTN                           BootNextSize;
457   CHAR16                          *FirmwareVendor;
458   EFI_STATUS                      Status;
459   UINT16                          BootTimeOut;
460   UINTN                           Index;
461   EDKII_VARIABLE_LOCK_PROTOCOL    *VariableLock;
462 
463   //
464   // Insert the performance probe
465   //
466   PERF_END (NULL, "DXE", NULL, 0);
467   PERF_START (NULL, "BDS", NULL, 0);
468 
469   //
470   // Initialize the global system boot option and driver option
471   //
472   InitializeListHead (&DriverOptionList);
473   InitializeListHead (&BootOptionList);
474 
475   //
476   // Initialize hotkey service
477   //
478   InitializeHotkeyService ();
479 
480   //
481   // Fill in FirmwareVendor and FirmwareRevision from PCDs
482   //
483   FirmwareVendor = (CHAR16 *)PcdGetPtr (PcdFirmwareVendor);
484   gST->FirmwareVendor = AllocateRuntimeCopyPool (StrSize (FirmwareVendor), FirmwareVendor);
485   ASSERT (gST->FirmwareVendor != NULL);
486   gST->FirmwareRevision = PcdGet32 (PcdFirmwareRevision);
487 
488   //
489   // Fixup Tasble CRC after we updated Firmware Vendor and Revision
490   //
491   gST->Hdr.CRC32 = 0;
492   gBS->CalculateCrc32 ((VOID *)gST, sizeof(EFI_SYSTEM_TABLE), &gST->Hdr.CRC32);
493 
494   //
495   // Validate Variable.
496   //
497   BdsFormalizeEfiGlobalVariable();
498 
499   //
500   // Mark the read-only variables if the Variable Lock protocol exists
501   //
502   Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
503   DEBUG ((EFI_D_INFO, "[BdsDxe] Locate Variable Lock protocol - %r\n", Status));
504   if (!EFI_ERROR (Status)) {
505     for (Index = 0; Index < ARRAY_SIZE (mReadOnlyVariables); Index++) {
506       Status = VariableLock->RequestToLock (VariableLock, mReadOnlyVariables[Index], &gEfiGlobalVariableGuid);
507       ASSERT_EFI_ERROR (Status);
508     }
509   }
510 
511   //
512   // Report Status Code to indicate connecting drivers will happen
513   //
514   REPORT_STATUS_CODE (
515     EFI_PROGRESS_CODE,
516     (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_BEGIN_CONNECTING_DRIVERS)
517     );
518 
519   InitializeHwErrRecSupport();
520 
521   //
522   // Initialize L"Timeout" EFI global variable.
523   //
524   BootTimeOut = PcdGet16 (PcdPlatformBootTimeOut);
525   if (BootTimeOut != 0xFFFF) {
526     //
527     // If time out value equal 0xFFFF, no need set to 0xFFFF to variable area because UEFI specification
528     // define same behavior between no value or 0xFFFF value for L"Timeout".
529     //
530     BdsDxeSetVariableAndReportStatusCodeOnError (
531                     L"Timeout",
532                     &gEfiGlobalVariableGuid,
533                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
534                     sizeof (UINT16),
535                     &BootTimeOut
536                     );
537   }
538 
539   //
540   // bugbug: platform specific code
541   // Initialize the platform specific string and language
542   //
543   InitializeStringSupport ();
544   InitializeLanguage (TRUE);
545   InitializeFrontPage (TRUE);
546 
547   //
548   // Do the platform init, can be customized by OEM/IBV
549   //
550   PERF_START (NULL, "PlatformBds", "BDS", 0);
551   PlatformBdsInit ();
552 
553   //
554   // Set up the device list based on EFI 1.1 variables
555   // process Driver#### and Load the driver's in the
556   // driver option list
557   //
558   BdsLibBuildOptionFromVar (&DriverOptionList, L"DriverOrder");
559   if (!IsListEmpty (&DriverOptionList)) {
560     BdsLibLoadDrivers (&DriverOptionList);
561   }
562   //
563   // Check if we have the boot next option
564   //
565   mBootNext = BdsLibGetVariableAndSize (
566                 L"BootNext",
567                 &gEfiGlobalVariableGuid,
568                 &BootNextSize
569                 );
570 
571   //
572   // Setup some platform policy here
573   //
574   PlatformBdsPolicyBehavior (&DriverOptionList, &BootOptionList, BdsProcessCapsules, BdsMemoryTest);
575   PERF_END (NULL, "PlatformBds", "BDS", 0);
576 
577   //
578   // BDS select the boot device to load OS
579   //
580   BdsBootDeviceSelect ();
581 
582   //
583   // Only assert here since this is the right behavior, we should never
584   // return back to DxeCore.
585   //
586   ASSERT (FALSE);
587 
588   return ;
589 }
590 
591 
592 /**
593   Set the variable and report the error through status code upon failure.
594 
595   @param  VariableName           A Null-terminated string that is the name of the vendor's variable.
596                                  Each VariableName is unique for each VendorGuid. VariableName must
597                                  contain 1 or more characters. If VariableName is an empty string,
598                                  then EFI_INVALID_PARAMETER is returned.
599   @param  VendorGuid             A unique identifier for the vendor.
600   @param  Attributes             Attributes bitmask to set for the variable.
601   @param  DataSize               The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
602                                  EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or
603                                  EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
604                                  causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
605                                  set, then a SetVariable() call with a DataSize of zero will not cause any change to
606                                  the variable value (the timestamp associated with the variable may be updated however
607                                  even if no new data value is provided,see the description of the
608                                  EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
609                                  be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
610   @param  Data                   The contents for the variable.
611 
612   @retval EFI_SUCCESS            The firmware has successfully stored the variable and its data as
613                                  defined by the Attributes.
614   @retval EFI_INVALID_PARAMETER  An invalid combination of attribute bits, name, and GUID was supplied, or the
615                                  DataSize exceeds the maximum allowed.
616   @retval EFI_INVALID_PARAMETER  VariableName is an empty string.
617   @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
618   @retval EFI_DEVICE_ERROR       The variable could not be retrieved due to a hardware error.
619   @retval EFI_WRITE_PROTECTED    The variable in question is read-only.
620   @retval EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
621   @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
622                                  or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo
623                                  does NOT pass the validation check carried out by the firmware.
624 
625   @retval EFI_NOT_FOUND          The variable trying to be updated or deleted was not found.
626 **/
627 EFI_STATUS
BdsDxeSetVariableAndReportStatusCodeOnError(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID * Data)628 BdsDxeSetVariableAndReportStatusCodeOnError (
629   IN CHAR16     *VariableName,
630   IN EFI_GUID   *VendorGuid,
631   IN UINT32     Attributes,
632   IN UINTN      DataSize,
633   IN VOID       *Data
634   )
635 {
636   EFI_STATUS                 Status;
637   EDKII_SET_VARIABLE_STATUS  *SetVariableStatus;
638   UINTN                      NameSize;
639 
640   Status = gRT->SetVariable (
641                   VariableName,
642                   VendorGuid,
643                   Attributes,
644                   DataSize,
645                   Data
646                   );
647   if (EFI_ERROR (Status)) {
648     NameSize = StrSize (VariableName);
649     SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
650     if (SetVariableStatus != NULL) {
651       CopyGuid (&SetVariableStatus->Guid, VendorGuid);
652       SetVariableStatus->NameSize   = NameSize;
653       SetVariableStatus->DataSize   = DataSize;
654       SetVariableStatus->SetStatus  = Status;
655       SetVariableStatus->Attributes = Attributes;
656       CopyMem (SetVariableStatus + 1,                          VariableName, NameSize);
657       CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data,         DataSize);
658 
659       REPORT_STATUS_CODE_EX (
660         EFI_ERROR_CODE,
661         PcdGet32 (PcdErrorCodeSetVariable),
662         0,
663         NULL,
664         &gEdkiiStatusCodeDataTypeVariableGuid,
665         SetVariableStatus,
666         sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
667         );
668 
669       FreePool (SetVariableStatus);
670     }
671   }
672 
673   return Status;
674 }
675 
676