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