Home | History | Annotate | Download | only in BdsDxe
      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
     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
     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
    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
    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
    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
    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
    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 < ARRAY_SIZE (mReadOnlyVariables); 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
    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